commit 8e6c723751131ee99164ce0421413038c163bff2 Author: Valentin Verdier Date: Fri May 15 12:18:51 2020 +0200 Commit initial diff --git a/Game/Camera.cs b/Game/Camera.cs new file mode 100755 index 0000000..a68697e --- /dev/null +++ b/Game/Camera.cs @@ -0,0 +1,135 @@ +using System; +using Microsoft.Xna.Framework; + +namespace KinPortal { + public class Camera : GameComponent { + public Matrix LookAtMatrix { + get {return Matrix.CreateLookAt(Pos, Eye + Pos, Up);} + } + + public Matrix ProjectionMatrix { + get {return Matrix.CreatePerspectiveFieldOfView( + MathHelper.ToRadians(Fov), + Game.GraphicsDevice.Viewport.AspectRatio, + NearDist, + FarDist + ); + } + } + + public float Fov {get; set;} + public float NearDist {get; set;} + public float FarDist {get; set;} + + public Vector3 Pos {get; set;} + public Vector3 Eye {get; set;} + public Vector3 Up {get; set;} + + private float m_tangage; + public float Tangage { + get {return m_tangage;} + set { + m_tangage = value; + Eye = GetEyeFromAngle(Lacet, Tangage); + } + } + + private float m_lacet; + public float Lacet { + get {return m_lacet;} + set { + m_lacet = value; + Eye = GetEyeFromAngle(Lacet, Tangage); + } + } + + private float m_roulis; + public float Roulis { + get {return m_roulis;} + set { + m_roulis = value; + // TODO : compute up vector + Up = new Vector3(0, 0, 1); + } + } + + public Camera(Game game, Vector3 pos, Vector3 eye, Vector3 up, float fov, float near, float far) + : base(game) { + Pos = pos; + Eye = eye; + Up = up; + Fov = fov; + NearDist = near; + FarDist = far; + UpdateOrder = 1; + } + + public Camera(Game game, Vector3 pos, float lacet, float tangage, float roulis, float fov, float near, float far) + : base(game) { + Pos = pos; + Lacet = lacet; + Tangage = tangage; + Roulis = roulis; + Fov = fov; + NearDist = near; + FarDist = far; + } + + public void MoveForward(float step) { + Pos = Eye * step + Pos; + } + + public void MoveBackward(float step) { + Pos = Eye * step * -1.0f + Pos; + } + + public void MoveLeft(float step) { + Pos = GetEyeFromAngle(Lacet + 90.0f, 0) * step + Pos; + } + + public void MoveRight(float step) { + Pos = GetEyeFromAngle(Lacet + 90.0f, 0) * step * -1.0f + Pos; + } + + public void MoveUp(float step) { + Pos = Up * step + Pos; + } + + public void MoveDown(float step) { + Pos = Up * step * -1.0f + Pos; + } + + public void Rotate(float lacet, float tangage, float roulis) { + Lacet = (Lacet + lacet) % 360.0f; + Tangage = (Tangage + tangage) % 360.0f; + Roulis = (Roulis + roulis) % 360.0f; + } + + private Vector3 GetEyeFromAngle(float lacet, float tangage) { + float lacet_rad = MathHelper.ToRadians(lacet); + float tangage_rad = MathHelper.ToRadians(tangage); + + Vector3 eye = new Vector3( + (float) (Math.Cos(tangage_rad) * Math.Cos(lacet_rad)), + (float) (Math.Cos(tangage_rad) * Math.Sin(lacet_rad)), + (float) (Math.Sin(tangage_rad)) + ); + + return Vector3.Normalize(eye); + } + + public Vector3 Unproject(int x, int y) { + Vector3 nearSource = new Vector3(x, y, 0); + Vector3 farSource = new Vector3(x, y, 1); + + Matrix world = Matrix.Identity; + Matrix proj = ProjectionMatrix; + Matrix view = LookAtMatrix; + + Vector3 nearPoint = Game.GraphicsDevice.Viewport.Unproject(nearSource, proj, view, world); + Vector3 farPoint = Game.GraphicsDevice.Viewport.Unproject(farSource, proj, view, world); + + return farPoint - nearPoint; + } + } +} diff --git a/Game/CollisionStrategy.cs b/Game/CollisionStrategy.cs new file mode 100755 index 0000000..0dac0b2 --- /dev/null +++ b/Game/CollisionStrategy.cs @@ -0,0 +1,9 @@ +using System; +using Microsoft.Xna.Framework; + +namespace KinPortal { + public interface CollisionStrategy { + void Update(InterruptorObject obj, GameTime gameTime); + void Collision(InterruptorObject hostObj, DynamicObject obj); + } +} diff --git a/Game/Cursor.cs b/Game/Cursor.cs new file mode 100755 index 0000000..acd37e5 --- /dev/null +++ b/Game/Cursor.cs @@ -0,0 +1,39 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Input; +using Microsoft.Xna.Framework.Graphics; + +namespace KinPortal { + public class Cursor : DrawableGameComponent { + public Point Pos {get; set;} + public Point Size {get; set;} + public Texture2D Texture {get; set;} + public HandsState Hand {get; private set;} + private SpriteBatch m_cursor; + + public Cursor(KinPortal.Game game, SpriteBatch sprite,string filepath, Point size, HandsState hand) + : base(game) { + Pos = new Point(0, 0); + Size = size; + Texture = Game.Content.Load(filepath); + Hand = hand; + m_cursor = sprite; + UpdateOrder = 2; + } + + public override void Update(GameTime gameTime) { + KinPortal.Game game = (KinPortal.Game) Game; + Pos = Hand.Pos; + base.Update(gameTime); + } + + public override void Draw(GameTime gameTime) { + Rectangle place = new Rectangle(Pos.X - Size.X / 2, Pos.Y - Size.Y / 2, Size.X, Size.Y); + + m_cursor.Begin(); + m_cursor.Draw(Texture, place, Color.White); + m_cursor.End(); + + base.Draw(gameTime); + } + } +} diff --git a/Game/DrawPortalStrategy.cs b/Game/DrawPortalStrategy.cs new file mode 100755 index 0000000..3b4827b --- /dev/null +++ b/Game/DrawPortalStrategy.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using Microsoft.Xna.Framework; + + +namespace KinPortal { + public class DrawPortalStrategy : TargetingStrategy { + private TimeSpan m_leftLoadTime; + private TimeSpan m_rightLoadTime; + + private bool m_leftActive = false; + private HandsProjection m_leftProj; + private bool m_rightActive = false; + private HandsProjection m_rightProj; + + + public void Update(DynamicObject obj, GameTime gameTime) { + KinPortal.Game game = (KinPortal.Game) obj.Game; + + if(m_leftActive && game.HandsTracker.LeftHand.IsClosed) { + m_leftLoadTime += gameTime.ElapsedGameTime; + if(m_leftLoadTime.Milliseconds >= 300.0) { + obj.Scene.PortalManager.CreatePortal(m_leftProj.HitPoint + (m_leftProj.HitNormal * 0.01f), Geometry.GetRelativeRotation(Vector3.Right, m_leftProj.HitNormal), PortalManager.PortalType.RED); + m_leftActive = false; + } + } else { + m_leftActive = false; + } + + if(m_rightActive && game.HandsTracker.RightHand.IsClosed) { + m_rightLoadTime += gameTime.ElapsedGameTime; + if(m_rightLoadTime.Milliseconds >= 300.0) { + obj.Scene.PortalManager.CreatePortal(m_rightProj.HitPoint + (m_rightProj.HitNormal * 0.01f), Geometry.GetRelativeRotation(Vector3.Right, m_rightProj.HitNormal), PortalManager.PortalType.BLUE); + m_rightActive = false; + } + } else { + m_rightActive = false; + } + } + + public void Target(DynamicObject obj, HandsProjection leftHand, HandsProjection rightHand) { + KinPortal.Game game = (KinPortal.Game) obj.Game; + Vector3 orgNormal = Vector3.Right; + + if(leftHand.IsProjected && game.HandsTracker.LeftHand.IsClosed == true && game.HandsTracker.LeftHand.PrevIsClosed == false) { + m_leftActive = true; + m_leftProj = leftHand; + m_leftLoadTime = TimeSpan.Zero; + } + + if(rightHand.IsProjected && game.HandsTracker.RightHand.IsClosed == true && game.HandsTracker.RightHand.PrevIsClosed == false) { + m_rightActive = true; + m_rightProj = rightHand; + m_rightLoadTime = TimeSpan.Zero; + } + } + } +} diff --git a/Game/DynamicObject.cs b/Game/DynamicObject.cs new file mode 100755 index 0000000..0d50c0c --- /dev/null +++ b/Game/DynamicObject.cs @@ -0,0 +1,57 @@ +using Microsoft.Xna.Framework; +using Jitter.Dynamics; + +namespace KinPortal { + public class DynamicObject : Object { + public RigidBody PhysicModel {get; private set;} + public TargetingStrategy TargetingStrategy {get; set;} + + public override Vector3 Pos { + get { + return Geometry.GetXnaVector(PhysicModel.Position); + } + set { + PhysicModel.Position = Geometry.GetJitterVector(value); + } + } + + public override Quaternion Rot { + get { + return Quaternion.CreateFromRotationMatrix(Geometry.GetXnaMatrix(PhysicModel.Orientation)); + } + set { + PhysicModel.Orientation = Geometry.GetJitterMatrix(Matrix.CreateFromQuaternion(value)); + } + } + + public bool IsStatic { + get { + return PhysicModel.IsStatic; + } + set { + PhysicModel.IsStatic = value; + } + } + + public DynamicObject(string filepath, Vector3 pos, Quaternion rot, Scene scene, ShapeType shapeType, bool isStatic, TargetingStrategy strategy) + : base(filepath, scene) { + PhysicModel = new RigidBody(ShapeFactory.CreateShape(Model, shapeType)); + Pos = pos; + Rot = rot; + TargetingStrategy = strategy; + IsStatic = isStatic; + } + + public DynamicObject(string filepath, Scene scene, ShapeType shapeType) + : this(filepath, Vector3.Zero, Quaternion.Identity, scene, shapeType, false, new NoTargetingStrategy()) {} + + public void Target(HandsProjection leftHand, HandsProjection rightHand) { + TargetingStrategy.Target(this, leftHand, rightHand); + } + + public override void Update(GameTime gameTime) { + TargetingStrategy.Update(this, gameTime); + base.Update(gameTime); + } + } +} diff --git a/Game/Game.cs b/Game/Game.cs new file mode 100755 index 0000000..8ad5921 --- /dev/null +++ b/Game/Game.cs @@ -0,0 +1,164 @@ +using System; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Audio; +using Microsoft.Xna.Framework.Content; +using Microsoft.Xna.Framework.GamerServices; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; +using Microsoft.Xna.Framework.Media; + +namespace KinPortal { + public class Game : Microsoft.Xna.Framework.Game { + private GraphicsDeviceManager Graphics {get; set;} + private Cursor m_leftCursor; + private Cursor m_rightCursor; + + public Scene Scene {get; private set;} + public HandsTracker HandsTracker {get; private set;} + + public Game() { + Graphics = new GraphicsDeviceManager(this); + Content.RootDirectory = "Resources"; + } + + protected override void Initialize() { + Window.AllowUserResizing = true; + Window.ClientSizeChanged += new EventHandler(Window_ClientSizeChanged); + Graphics.PreferredBackBufferWidth = 900; + Graphics.PreferredBackBufferHeight = 512; + Graphics.ApplyChanges(); + + HandsTracker = new HandsTracker(900, 512); + //HandsTracker.Run(); + + Scene = new Scene(this, new Camera(this, new Vector3(-15, -15, 15), 45, -30, 0, 70, 0.1f, 1000)); + this.Components.Add(Scene); + base.Initialize(); + } + + protected override void LoadContent() { + Scene.AddComponent(new DynamicObject( + "models\\cube", + new Vector3(0, 0, 5f), Quaternion.Identity, + Scene, ShapeType.BOX, false, new GrabStrategy() + )); + Scene.AddComponent(new DynamicObject( + "models\\monkey", + new Vector3(0, 4, 5f), Quaternion.Identity, + Scene, ShapeType.SPHERE, false, new GrabStrategy() + )); + Scene.AddComponent(new DynamicObject( + "models\\cube", + new Vector3(3, 0, 5f), Quaternion.Identity, + Scene, ShapeType.BOX, false, new GrabStrategy() + )); + Scene.AddComponent(new DynamicObject( + "models\\monkey", + new Vector3(0, -4, 5f), Quaternion.Identity, + Scene, ShapeType.SPHERE, false, new GrabStrategy() + )); + Scene.AddComponent(new DynamicObject( + "models\\sphere", + new Vector3(-3, 0, 5f), Quaternion.Identity, + Scene, ShapeType.SPHERE, false, new GrabStrategy() + )); + Scene.AddComponent(new DynamicObject("models\\level", + new Vector3(0, 0, 0), Quaternion.Identity, Scene, ShapeType.TRIANGULATE, true, new DrawPortalStrategy() + )); + Scene.AddComponent(new InterruptorObject( + "models\\target", + new Vector3(19.8f, 0, 10f), Quaternion.CreateFromYawPitchRoll(0, 0, MathHelper.ToRadians(180)), + Scene, ShapeType.TRIANGULATE, true, new NoTargetingStrategy(), new TargetCollisionStrategy() + )); + + m_leftCursor = new Cursor(this, new SpriteBatch(GraphicsDevice), "textures\\tex_cursor_red", new Point(48, 48), HandsTracker.LeftHand); + this.Components.Add(m_leftCursor); + m_rightCursor = new Cursor(this, new SpriteBatch(GraphicsDevice), "textures\\tex_cursor_blue", new Point(48, 48), HandsTracker.RightHand); + this.Components.Add(m_rightCursor); + } + + protected override void UnloadContent() {} + + protected override void Update(GameTime gameTime) { + /* Quit Game */ + if(Keyboard.GetState().IsKeyDown(Keys.Escape)) { + Exit(); + } + + /* Move Forward */ + if(Keyboard.GetState().IsKeyDown(Keys.Z)) { + Scene.Camera.MoveForward(0.08f); + } + + /* Move Backware */ + if(Keyboard.GetState().IsKeyDown(Keys.S)) { + Scene.Camera.MoveBackward(0.08f); + } + + /* Move Left */ + if(Keyboard.GetState().IsKeyDown(Keys.Q)) { + Scene.Camera.MoveLeft(0.08f); + } + + /* Move Right */ + if(Keyboard.GetState().IsKeyDown(Keys.D)) { + Scene.Camera.MoveRight(0.08f); + } + + /* Move Up */ + if(Keyboard.GetState().IsKeyDown(Keys.Space)) { + Scene.Camera.MoveUp(0.08f); + } + + /* Move Down */ + if(Keyboard.GetState().IsKeyDown(Keys.LeftShift)) { + Scene.Camera.MoveDown(0.08f); + } + + /* Rotate Up */ + if(Keyboard.GetState().IsKeyDown(Keys.Up)) { + Scene.Camera.Rotate(0, 1.8f, 0); + } + + /* Rotate Down */ + if(Keyboard.GetState().IsKeyDown(Keys.Down)) { + Scene.Camera.Rotate(0, -1.8f, 0); + } + + /* Rotate Left */ + if(Keyboard.GetState().IsKeyDown(Keys.Left)) { + Scene.Camera.Rotate(1.8f, 0, 0); + } + + /* Rotate Right */ + if(Keyboard.GetState().IsKeyDown(Keys.Right)) { + Scene.Camera.Rotate(-1.8f, 0, 0); + } + + MouseState ms = Mouse.GetState(); + HandsTracker.LeftHand.Pos = new Point(ms.X, ms.Y); + HandsTracker.RightHand.Pos = new Point(ms.X, ms.Y); + HandsTracker.LeftHand.PrevIsClosed = HandsTracker.LeftHand.IsClosed; + HandsTracker.LeftHand.IsClosed = (ms.LeftButton == ButtonState.Pressed); + HandsTracker.RightHand.PrevIsClosed = HandsTracker.RightHand.IsClosed; + HandsTracker.RightHand.IsClosed = (ms.RightButton == ButtonState.Pressed); + HandsTracker.LeftHand.Depth = (double) ms.ScrollWheelValue / 1000.0f; + HandsTracker.RightHand.Depth = (double) ms.ScrollWheelValue / 1000.0f; + + base.Update(gameTime); + } + + protected override void Draw(GameTime gameTime) { + GraphicsDevice.Clear(Color.SkyBlue); + Scene.Draw(gameTime); + m_leftCursor.Draw(gameTime); + m_rightCursor.Draw(gameTime); + base.Draw(gameTime); + } + + public void Window_ClientSizeChanged(object sender, EventArgs e) { + HandsTracker.InteractionRegionWidth = GraphicsDevice.Viewport.Width; + HandsTracker.InteractionRegionHeight = GraphicsDevice.Viewport.Height; + } + } +} diff --git a/Game/Game.csproj b/Game/Game.csproj new file mode 100755 index 0000000..a4505b0 --- /dev/null +++ b/Game/Game.csproj @@ -0,0 +1,152 @@ + + + + {FAA2E885-42FD-49FD-8678-925331BBE9C8} + {6D335F3A-9D43-41b4-9D22-F6F17C4BE596};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + Debug + x86 + WinExe + Properties + KinPortal + KinPortal + v4.0 + Client + v4.0 + Windows + Reach + 400c0308-ad81-481b-9a51-0bf95f95bd5b + Game + + + + + false + publier\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + true + + + true + full + false + bin\x86\Debug + DEBUG;TRACE;WINDOWS + prompt + 4 + true + false + x86 + false + + + pdbonly + true + bin\x86\Release + TRACE;WINDOWS + prompt + 4 + true + false + x86 + true + + + + ..\Jitter\Jitter\bin\Release\Jitter.dll + + + + ..\..\..\..\..\..\Program Files\Microsoft SDKs\Kinect\Developer Toolkit v1.8.0\Assemblies\Microsoft.Kinect.Toolkit.dll + + + ..\..\..\..\..\..\Program Files\Microsoft SDKs\Kinect\Developer Toolkit v1.8.0\Assemblies\Microsoft.Kinect.Toolkit.Interaction.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Resources + Content + {75D6A435-2F46-4192-B2B7-37A17468C066} + + + + + False + Microsoft .NET Framework 4 Client Profile %28x86 et x64%29 + true + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + false + + + False + Windows Installer 4.5 + true + + + + + + \ No newline at end of file diff --git a/Game/Geometry.cs b/Game/Geometry.cs new file mode 100755 index 0000000..b18afaa --- /dev/null +++ b/Game/Geometry.cs @@ -0,0 +1,161 @@ +using System; +using System.Collections.Generic; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Jitter.LinearMath; +using Jitter.Collision; + +namespace KinPortal { + public class Geometry { + public List Vertices {get; private set;} + public List Indices {get; private set;} + + public BoundingBox Box { + get { + return BoundingBox.CreateFromPoints(Vertices); + } + } + + public BoundingSphere Sphere { + get { + return BoundingSphere.CreateFromPoints(Vertices); + } + } + + public List JVertices { + get { + List vertices = new List(); + foreach(Vector3 vert in Vertices) { + vertices.Add(GetJitterVector(vert)); + } + return vertices; + } + } + + public List JTriangles { + get { + List triangles = new List(); + for(int i = 0; i < Indices.Count / 3; i++) { + triangles.Add(new TriangleVertexIndices( + Indices[i * 3 + 0], + Indices[i * 3 + 1], + Indices[i * 3 + 2] + )); + } + return triangles; + } + } + + public Geometry(Model model) { + Vertices = new List(); + Indices = new List(); + + Matrix[] bones = new Matrix[model.Bones.Count]; + model.CopyAbsoluteBoneTransformsTo(bones); + foreach (ModelMesh mm in model.Meshes) { + Matrix bone = bones[mm.ParentBone.Index]; + foreach (ModelMeshPart mmp in mm.MeshParts) { + int offset = Vertices.Count; + Vector3[] verts = new Vector3[mmp.NumVertices]; + mmp.VertexBuffer.GetData(mmp.VertexOffset * mmp.VertexBuffer.VertexDeclaration.VertexStride, + verts, 0, mmp.NumVertices, mmp.VertexBuffer.VertexDeclaration.VertexStride); + Vector3.Transform(verts, ref bone, verts); + Vertices.AddRange(verts); + + ushort[] indices = new ushort[mmp.PrimitiveCount * 3]; + mmp.IndexBuffer.GetData(mmp.StartIndex * 2, indices, 0, mmp.PrimitiveCount * 3); + TriangleVertexIndices[] tvi = new TriangleVertexIndices[mmp.PrimitiveCount]; + for (int i = 0; i != indices.Length; i++) { + indices[i] += (ushort) offset; + } + Indices.AddRange(indices); + } + } + } + + public static Matrix GetXnaMatrix(JMatrix jmat) { + return new Matrix( + jmat.M11, jmat.M12, jmat.M13, 0.0f, + jmat.M21, jmat.M22, jmat.M23, 0.0f, + jmat.M31, jmat.M32, jmat.M33, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + ); + } + + public static JMatrix GetJitterMatrix(Matrix xmat) { + return new JMatrix( + xmat.M11, xmat.M12, xmat.M13, + xmat.M21, xmat.M22, xmat.M23, + xmat.M31, xmat.M32, xmat.M33 + ); + } + + public static Vector3 GetXnaVector(JVector jvec) { + return new Vector3(jvec.X, jvec.Y, jvec.Z); + } + + public static JVector GetJitterVector(Vector3 xvec) { + return new JVector(xvec.X, xvec.Y, xvec.Z); + } + + public static Quaternion GetRelativeRotation(Vector3 u, Vector3 v) { + Quaternion rot; + if(u == v) { + rot = Quaternion.Identity; + } else if(u == (-v)) { + rot = Quaternion.CreateFromAxisAngle(GetOrthogonalVector(u), (float) Math.PI); + } else { + rot = Quaternion.CreateFromAxisAngle(Vector3.Cross(u, v), (float) Math.Acos(Vector3.Dot(u, v))); + } + return rot; + } + + public static Vector3 GetRotationAxis(Quaternion rot) { + Vector3 axis = new Vector3(rot.X, rot.Y, rot.Z); + Vector3 naxis = Vector3.Normalize(axis); + return naxis; + } + + public static Vector3 GetOrthogonalVector(Vector3 vec) { + Vector3 v; + if(vec.X != 0) { + v = new Vector3((-(vec.Y)-(vec.Z))/vec.X, 1, 0); + } else if(vec.Y != 0) { + v = new Vector3(1, (-(vec.Z)-(vec.X))/vec.Y, 0); + } else if(vec.Z != 0) { + v = new Vector3(1, 0, (-(vec.X)-(vec.Y))/vec.Z); + } else { + v = Vector3.Zero; + } + return Vector3.Normalize(v); + } + } +} + +/*public static void GetJitterModelTriangles(Model model, List vertices, List triangles) { + Matrix[] bones = new Matrix[model.Bones.Count]; + model.CopyAbsoluteBoneTransformsTo(bones); + foreach (ModelMesh mm in model.Meshes) { + Matrix bone = bones[mm.ParentBone.Index]; + foreach (ModelMeshPart mmp in mm.MeshParts) { + int offset = vertices.Count; + Vector3[] verts = new Vector3[mmp.NumVertices]; + mmp.VertexBuffer.GetData(mmp.VertexOffset * mmp.VertexBuffer.VertexDeclaration.VertexStride, + verts, 0, mmp.NumVertices, mmp.VertexBuffer.VertexDeclaration.VertexStride); + foreach(Vector3 vert in verts) { + vertices.Add(GetJitterVector(Vector3.Transform(vert, bone))); + } + + short[] indices = new short[mmp.PrimitiveCount * 3]; + mmp.IndexBuffer.GetData(mmp.StartIndex * 2, indices, 0, mmp.PrimitiveCount * 3); + TriangleVertexIndices[] tvi = new TriangleVertexIndices[mmp.PrimitiveCount]; + for (int i = 0; i != tvi.Length; ++i) { + tvi[i].I0 = indices[i * 3 + 0] + offset; + tvi[i].I1 = indices[i * 3 + 1] + offset; + tvi[i].I2 = indices[i * 3 + 2] + offset; + } + triangles.AddRange(tvi); + } + } + } + */ diff --git a/Game/GrapStrategy.cs b/Game/GrapStrategy.cs new file mode 100755 index 0000000..b68d46c --- /dev/null +++ b/Game/GrapStrategy.cs @@ -0,0 +1,102 @@ +using System; +using Microsoft.Xna.Framework; +using Jitter.Collision; +using Jitter.Dynamics.Constraints.SingleBody; +using Jitter.LinearMath; + +namespace KinPortal { + public class GrabStrategy : TargetingStrategy { + private PointOnPoint m_leftGrabConstraint = null; + private PointOnPoint m_rightGrabConstraint = null; + private float m_leftHitDistance; + private float m_rightHitDistance; + private float m_leftHandHitDepth; + private float m_rightHandHitDepth; + + public void Update(DynamicObject obj, GameTime gameTime) { + KinPortal.Game game = (KinPortal.Game) obj.Game; + + if(game.HandsTracker.LeftHand.IsClosed) { + Vector3 ray = obj.Scene.Camera.Unproject(game.HandsTracker.LeftHand.Pos.X, game.HandsTracker.LeftHand.Pos.Y); + ray.Normalize(); + if(m_leftGrabConstraint != null) { + float hitDistance = m_leftHitDistance * (1.0f + (float) game.HandsTracker.LeftHand.Depth - m_leftHandHitDepth); + m_leftGrabConstraint.Anchor = Geometry.GetJitterVector(obj.Scene.Camera.Pos + ray * hitDistance); + obj.PhysicModel.IsActive = true; + obj.PhysicModel.LinearVelocity *= 0.98f; + obj.PhysicModel.AngularVelocity *= 0.98f; + } + } else { + if(m_leftGrabConstraint != null) { + obj.Scene.PhysicWorld.RemoveConstraint(m_leftGrabConstraint); + m_leftGrabConstraint = null; + } + } + + if(game.HandsTracker.RightHand.IsClosed) { + Vector3 ray = obj.Scene.Camera.Unproject(game.HandsTracker.RightHand.Pos.X, game.HandsTracker.RightHand.Pos.Y); + ray.Normalize(); + if(m_rightGrabConstraint != null) { + float hitDistance = m_rightHitDistance * (1.0f + (float) game.HandsTracker.RightHand.Depth - m_rightHandHitDepth); + m_rightGrabConstraint.Anchor = Geometry.GetJitterVector(obj.Scene.Camera.Pos + ray * hitDistance); + obj.PhysicModel.IsActive = true; + obj.PhysicModel.LinearVelocity *= 0.98f; + obj.PhysicModel.AngularVelocity *= 0.98f; + } + } else { + if(m_rightGrabConstraint != null) { + obj.Scene.PhysicWorld.RemoveConstraint(m_rightGrabConstraint); + m_rightGrabConstraint = null; + } + } + } + + public void Target(DynamicObject obj, HandsProjection leftHand, HandsProjection rightHand) { + KinPortal.Game game = (KinPortal.Game) obj.Game; + + if(leftHand.IsProjected && game.HandsTracker.LeftHand.IsClosed && !game.HandsTracker.LeftHand.PrevIsClosed) { + if(m_leftGrabConstraint != null) { + obj.Scene.PhysicWorld.RemoveConstraint(m_leftGrabConstraint); + } + + if(m_rightGrabConstraint != null) { + return; + } + + JVector lanchor = Geometry.GetJitterVector(leftHand.HitPoint - obj.Pos); + lanchor = JVector.Transform(lanchor, JMatrix.Transpose(obj.PhysicModel.Orientation)); + + m_leftGrabConstraint = new PointOnPoint(obj.PhysicModel, lanchor); + m_leftGrabConstraint.Softness = 0.004f; + m_leftGrabConstraint.BiasFactor = 0.1f; + + obj.Scene.PhysicWorld.AddConstraint(m_leftGrabConstraint); + m_leftHitDistance = (leftHand.HitPoint - obj.Scene.Camera.Pos).Length(); + m_leftGrabConstraint.Anchor = Geometry.GetJitterVector(leftHand.HitPoint); + m_leftHandHitDepth = (float) game.HandsTracker.LeftHand.Depth; + } + + if(rightHand.IsProjected && game.HandsTracker.RightHand.IsClosed && !game.HandsTracker.RightHand.PrevIsClosed) { + if(m_rightGrabConstraint != null) { + obj.Scene.PhysicWorld.RemoveConstraint(m_rightGrabConstraint); + } + + if(m_leftGrabConstraint != null) { + return; + } + + JVector lanchor = Geometry.GetJitterVector(rightHand.HitPoint - obj.Pos); + lanchor = JVector.Transform(lanchor, JMatrix.Transpose(obj.PhysicModel.Orientation)); + + m_rightGrabConstraint = new PointOnPoint(obj.PhysicModel, lanchor); + m_rightGrabConstraint.Softness = 0.004f; + m_rightGrabConstraint.BiasFactor = 0.1f; + + obj.Scene.PhysicWorld.AddConstraint(m_rightGrabConstraint); + m_rightHitDistance = (rightHand.HitPoint - obj.Scene.Camera.Pos).Length(); + m_rightGrabConstraint.Anchor = Geometry.GetJitterVector(rightHand.HitPoint); + m_rightHandHitDepth = (float) game.HandsTracker.RightHand.Depth; + } + } + } +} diff --git a/Game/HandsProjection.cs b/Game/HandsProjection.cs new file mode 100755 index 0000000..3349a71 --- /dev/null +++ b/Game/HandsProjection.cs @@ -0,0 +1,15 @@ +using Microsoft.Xna.Framework; + +namespace KinPortal { + public struct HandsProjection { + public bool IsProjected; + public Vector3 HitPoint; + public Vector3 HitNormal; + + public HandsProjection(bool isProjected, Vector3 hitPoint, Vector3 hitNormal) { + IsProjected = isProjected; + HitPoint = hitPoint; + HitNormal = hitNormal; + } + } +} diff --git a/Game/HandsState.cs b/Game/HandsState.cs new file mode 100755 index 0000000..aec770f --- /dev/null +++ b/Game/HandsState.cs @@ -0,0 +1,16 @@ +using Microsoft.Xna.Framework; + +namespace KinPortal { + public class HandsState { + public Point Pos {get; set;} + public double Depth {get; set;} + public bool PrevIsClosed {get; set;} + public bool IsClosed {get; set;} + + public HandsState() { + Pos = Point.Zero; + IsClosed = false; + PrevIsClosed = false; + } + } +} diff --git a/Game/HandsTracker.cs b/Game/HandsTracker.cs new file mode 100755 index 0000000..3cf4f0a --- /dev/null +++ b/Game/HandsTracker.cs @@ -0,0 +1,234 @@ +using System; +using System.Collections.Generic; +using Microsoft.Xna.Framework; +using Microsoft.Kinect; +using Microsoft.Kinect.Toolkit; +using Microsoft.Kinect.Toolkit.Interaction; + +namespace KinPortal { + public class HandsTracker : IInteractionClient { + public double InteractionRegionWidth {get; set;} + public double InteractionRegionHeight {get; set;} + private const int InvalidTrackingId = 0; + private readonly KinectSensorChooser sensorChooser = new KinectSensorChooser(); + private readonly HashSet trackedUsers = new HashSet(); + + private InteractionStream interactionStream; + private KinectSensor kinectSensor; + private Skeleton[] skeletons; + private UserInfo[] userInfos; + + public HandsState LeftHand {get; private set;} + public HandsState RightHand {get; private set;} + + public HandsTracker(double screenWidth, double screenHeight) { + InteractionRegionWidth = screenWidth; + InteractionRegionHeight = screenHeight; + LeftHand = new HandsState(); + RightHand = new HandsState(); + } + + public void Run() { + this.sensorChooser.KinectChanged += this.OnSensorChanged; + this.sensorChooser.Start(); + } + + #region PressAndGripAdjustment + public InteractionInfo GetInteractionInfoAtLocation(int skeletonTrackingId, InteractionHandType handType, double x, double y) { + return new InteractionInfo(); + } + #endregion PressAndGripAdjustment + + + #region Configuration + private void InitializeInteractions(KinectSensor sensor) { + this.skeletons = new Skeleton[sensor.SkeletonStream.FrameSkeletonArrayLength]; + this.userInfos = new UserInfo[InteractionFrame.UserInfoArrayLength]; + + sensor.DepthFrameReady += this.SensorDepthFrameReady; + sensor.SkeletonFrameReady += this.SensorSkeletonFrameReady; + + this.interactionStream = new InteractionStream(sensor, this); + this.interactionStream.InteractionFrameReady += this.InteractionFrameReady; + } + + private void UninitializeInteractions(KinectSensor sensor) { + sensor.DepthFrameReady -= this.SensorDepthFrameReady; + sensor.SkeletonFrameReady -= this.SensorSkeletonFrameReady; + + this.skeletons = null; + this.userInfos = null; + + this.interactionStream.InteractionFrameReady -= this.InteractionFrameReady; + this.interactionStream.Dispose(); + this.interactionStream = null; + } + + private void OnSensorChanged(object sender, KinectChangedEventArgs args) { + if (args.OldSensor != null) { + this.UninitializeInteractions(args.OldSensor); + try { + args.OldSensor.SkeletonStream.AppChoosesSkeletons = false; + args.OldSensor.DepthStream.Range = DepthRange.Default; + args.OldSensor.SkeletonStream.EnableTrackingInNearRange = false; + args.OldSensor.DepthStream.Disable(); + args.OldSensor.SkeletonStream.Disable(); + } catch (InvalidOperationException) { + // KinectSensor might enter an invalid state while enabling/disabling streams or stream features. + // E.g.: sensor might be abruptly unplugged. + } + } + + this.kinectSensor = null; + + if (args.NewSensor != null) { + try { + // InteractionStream needs 640x480 depth data stream + args.NewSensor.DepthStream.Enable(DepthImageFormat.Resolution640x480Fps30); + args.NewSensor.SkeletonStream.Enable(); + + try { + // Interactions work better in near range + args.NewSensor.DepthStream.Range = DepthRange.Near; + args.NewSensor.SkeletonStream.EnableTrackingInNearRange = true; + } catch (InvalidOperationException) { + // Non Kinect for Windows devices do not support Near mode, so reset back to default mode. + args.NewSensor.DepthStream.Range = DepthRange.Default; + args.NewSensor.SkeletonStream.EnableTrackingInNearRange = false; + } + } catch (InvalidOperationException) { + // KinectSensor might enter an invalid state while enabling/disabling streams or stream features. + // E.g.: sensor might be abruptly unplugged. + } + + this.kinectSensor = args.NewSensor; + + this.InitializeInteractions(args.NewSensor); + } + } + + #endregion Configuration + + #region Processing + private void SensorDepthFrameReady(object sender, DepthImageFrameReadyEventArgs depthImageFrameReadyEventArgs) { + if (this.kinectSensor != sender) { + return; + } + + using (DepthImageFrame depthFrame = depthImageFrameReadyEventArgs.OpenDepthImageFrame()) { + if (null != depthFrame) { + try { + // Hand data to Interaction framework to be processed + this.interactionStream.ProcessDepth(depthFrame.GetRawPixelData(), depthFrame.Timestamp); + } catch (InvalidOperationException) { + // DepthFrame functions may throw when the sensor gets + // into a bad state. Ignore the frame in that case. + } + } + } + } + + private void SensorSkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs skeletonFrameReadyEventArgs) { + if (this.kinectSensor != sender) { + return; + } + + using (SkeletonFrame skeletonFrame = skeletonFrameReadyEventArgs.OpenSkeletonFrame()) { + if (null != skeletonFrame) { + try { + skeletonFrame.CopySkeletonDataTo(this.skeletons); + var accelerometerReading = this.kinectSensor.AccelerometerGetCurrentReading(); + this.interactionStream.ProcessSkeleton(this.skeletons, accelerometerReading, skeletonFrame.Timestamp); + } catch (InvalidOperationException) { + // SkeletonFrame functions may throw when the sensor gets + // into a bad state. Ignore the frame in that case. + } + } + } + } + + private void InteractionFrameReady(object sender, InteractionFrameReadyEventArgs e) { + // Check for a null userInfos since we may still get posted events + // from the stream after we have unregistered our event handler and + // deleted our buffers. + if (this.userInfos == null) { + return; + } + + UserInfo[] localUserInfos = null; + long timestamp = 0; + + using (InteractionFrame interactionFrame = e.OpenInteractionFrame()) { + if (interactionFrame != null) { + interactionFrame.CopyInteractionDataTo(this.userInfos); + timestamp = interactionFrame.Timestamp; + localUserInfos = this.userInfos; + } + } + + if (localUserInfos != null) { + var currentUserSet = new HashSet(); + var usersToRemove = new HashSet(); + + foreach (var info in localUserInfos) { + if (info.SkeletonTrackingId == InvalidTrackingId) { + // Only look at user information corresponding to valid users + continue; + } + + /*if (!this.trackedUsers.Contains(info.SkeletonTrackingId)) { + Console.WriteLine("New user '{0}' entered scene at time {1}", info.SkeletonTrackingId, timestamp); + }*/ + + currentUserSet.Add(info.SkeletonTrackingId); + this.trackedUsers.Add(info.SkeletonTrackingId); + + // Perform hit testing and look for Grip and GripRelease events + foreach (var handPointer in info.HandPointers) { + if (handPointer.HandType.ToString() == "Right") { + RightHand.Pos = new Point( + (int) ((handPointer.X / 2) * InteractionRegionWidth), + (int) ((handPointer.Y / 2) * InteractionRegionHeight) + ); + RightHand.Depth = handPointer.RawZ; + RightHand.PrevIsClosed = RightHand.IsClosed; + + if(handPointer.HandEventType != InteractionHandEventType.None) { + if (handPointer.HandEventType.ToString() == "Grip") + RightHand.IsClosed = true; + else + RightHand.IsClosed = false; + } + } else { + LeftHand.Pos = new Point( + (int) ((handPointer.X / 2) * InteractionRegionWidth), + (int) ((handPointer.Y / 2) * InteractionRegionHeight) + ); + LeftHand.Depth = handPointer.RawZ; + LeftHand.PrevIsClosed = LeftHand.IsClosed; + + if (handPointer.HandEventType != InteractionHandEventType.None) { + if (handPointer.HandEventType.ToString() == "Grip") + LeftHand.IsClosed = true; + else + LeftHand.IsClosed = false; + } + } + } + } + + foreach (var id in this.trackedUsers) { + if (!currentUserSet.Contains(id)) { + usersToRemove.Add(id); + } + } + + foreach (var id in usersToRemove) { + this.trackedUsers.Remove(id); + } + } + } + + #endregion Processing + } +} diff --git a/Game/InterruptorObject.cs b/Game/InterruptorObject.cs new file mode 100755 index 0000000..13ce7e8 --- /dev/null +++ b/Game/InterruptorObject.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using Microsoft.Xna.Framework; + +namespace KinPortal { + public class InterruptorObject : DynamicObject { + public CollisionStrategy CollisionStrategy {get; private set;} + + public InterruptorObject(string filepath, Vector3 pos, Quaternion rot, Scene scene, ShapeType shapeType, bool isStatic, TargetingStrategy tstrategy, CollisionStrategy cstrategy) + : base(filepath, pos, rot, scene, shapeType, isStatic, tstrategy) { + CollisionStrategy = cstrategy; + } + + public InterruptorObject(string filepath, Scene scene, ShapeType shapeType) + : this(filepath, Vector3.Zero, Quaternion.Identity, scene, shapeType, false, new NoTargetingStrategy(), new NoCollisionStrategy()) {} + + public void Collision(DynamicObject obj) { + CollisionStrategy.Collision(this, obj); + } + + public override void Update(GameTime gameTime) { + CollisionStrategy.Update(this, gameTime); + base.Update(gameTime); + } + } +} diff --git a/Game/NoCollisionStrategy.cs b/Game/NoCollisionStrategy.cs new file mode 100755 index 0000000..c3cf41b --- /dev/null +++ b/Game/NoCollisionStrategy.cs @@ -0,0 +1,13 @@ +using Microsoft.Xna.Framework; + +namespace KinPortal { + public class NoCollisionStrategy : CollisionStrategy { + public void Update(InterruptorObject obj, GameTime gameTime) { + // NO OP + } + + public void Collision(InterruptorObject hostObj, DynamicObject obj) { + // NO OP + } + } +} diff --git a/Game/NoTargetingStrategy.cs b/Game/NoTargetingStrategy.cs new file mode 100755 index 0000000..b1661f9 --- /dev/null +++ b/Game/NoTargetingStrategy.cs @@ -0,0 +1,13 @@ +using Microsoft.Xna.Framework; + +namespace KinPortal { + public class NoTargetingStrategy : TargetingStrategy { + public void Update(DynamicObject obj, GameTime gameTime) { + // NO OP + } + + public void Target(DynamicObject obj, HandsProjection leftHand, HandsProjection rightHand) { + // NO OP + } + } +} diff --git a/Game/Object.cs b/Game/Object.cs new file mode 100755 index 0000000..e85ac8a --- /dev/null +++ b/Game/Object.cs @@ -0,0 +1,58 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace KinPortal { + public abstract class Object : DrawableGameComponent { + public string FilePath {get; private set;} + public Scene Scene {get; private set;} + public Model Model {get; private set;} + + private Matrix[] m_world; + + public abstract Vector3 Pos {get; set;} + public abstract Quaternion Rot {get; set;} + + public bool EnableLighting {get; set;} + + public Object(string filepath, Scene scene) + : base(scene.Game) { + FilePath = filepath; + Scene = scene; + Model = Game.Content.Load(FilePath); + + m_world = new Matrix[Model.Bones.Count]; + Model.CopyAbsoluteBoneTransformsTo(m_world); + EnableLighting = true; + UpdateOrder = 2; + } + + public override void Draw(GameTime gameTime) { + BlendState bs = new BlendState(); + bs.AlphaBlendFunction = BlendFunction.Add; + bs.AlphaSourceBlend = Blend.SourceAlpha; + bs.ColorSourceBlend = Blend.SourceAlpha; + bs.AlphaDestinationBlend = Blend.InverseSourceAlpha; + bs.ColorDestinationBlend = Blend.InverseSourceAlpha; + Game.GraphicsDevice.BlendState = bs; + Game.GraphicsDevice.DepthStencilState = DepthStencilState.Default; + Game.GraphicsDevice.SamplerStates[0] = SamplerState.LinearWrap; + + foreach(ModelMesh mesh in Model.Meshes) { + foreach(BasicEffect effect in mesh.Effects) { + if(EnableLighting) { + effect.EnableDefaultLighting(); + effect.PreferPerPixelLighting = true; + } else { + effect.LightingEnabled = false; + } + effect.World = m_world[mesh.ParentBone.Index] * + Matrix.CreateFromQuaternion(Rot) * Matrix.CreateTranslation(Pos); + effect.View = Scene.Camera.LookAtMatrix; + effect.Projection = Scene.Camera.ProjectionMatrix; + } + mesh.Draw(); + } + base.Draw(gameTime); + } + } +} diff --git a/Game/Portal.cs b/Game/Portal.cs new file mode 100755 index 0000000..0193ab4 --- /dev/null +++ b/Game/Portal.cs @@ -0,0 +1,24 @@ +using System; +using Microsoft.Xna.Framework; + +namespace KinPortal { + public class Portal : InterruptorObject { + public Vector3 Normal { + get { + return Vector3.Transform(new Vector3(1, 0, 0), Rot); + } + } + + public Vector3 Up { + get { + return Vector3.Transform(new Vector3(0, 0, 1), Rot); + } + } + + public Portal(string filepath, Vector3 pos, Quaternion rot, Scene scene) + : base(filepath, pos, rot, scene, ShapeType.TRIANGULATE, true, new NoTargetingStrategy(), new PortalCollisionStrategy()) {} + + public Portal(string filepath, Scene scene) + : this(filepath, Vector3.Zero, Quaternion.Identity, scene) {} + } +} diff --git a/Game/PortalCollisionStrategy.cs b/Game/PortalCollisionStrategy.cs new file mode 100755 index 0000000..73fdb50 --- /dev/null +++ b/Game/PortalCollisionStrategy.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using Microsoft.Xna.Framework; +using Jitter.LinearMath; + +namespace KinPortal { + public class PortalCollisionStrategy : CollisionStrategy { + public void Update(InterruptorObject obj, GameTime gameTime) { + + } + + public void Collision(InterruptorObject hostObj, DynamicObject obj) { + Portal inPortal = null; + Portal outPortal = null; + + if(obj.IsStatic == true) { + return; + } + + if(hostObj == hostObj.Scene.PortalManager.BluePortal) { + inPortal = (Portal) hostObj; + outPortal = hostObj.Scene.PortalManager.RedPortal; + } + + if(hostObj == hostObj.Scene.PortalManager.RedPortal) { + inPortal = (Portal) hostObj; + outPortal = hostObj.Scene.PortalManager.BluePortal; + } + + if(inPortal != null && outPortal != null) { + Vector3 relPos = obj.Pos - inPortal.Pos; + if(Vector3.Dot(Vector3.Normalize(relPos), inPortal.Normal) < 0.35f) { + return; + } + + Vector3 axis; + Quaternion rot; + if(inPortal.Rot == outPortal.Rot) { + rot = Quaternion.Identity; + axis = outPortal.Up; + } else { + rot = outPortal.Rot * Quaternion.Conjugate(inPortal.Rot); + //axis = Geometry.GetRotationAxis(rot); + axis = outPortal.Up; + } + obj.Pos = outPortal.Pos + Vector3.Transform(relPos, rot) * 1.2f; + Quaternion rot2 = rot * Quaternion.CreateFromAxisAngle(axis, (float) Math.PI); + obj.Rot = obj.Rot * rot2; + Vector3 vel = Geometry.GetXnaVector(obj.PhysicModel.LinearVelocity); + obj.PhysicModel.LinearVelocity = Geometry.GetJitterVector(Vector3.Transform(-vel, rot)); + } + } + } +} diff --git a/Game/PortalManager.cs b/Game/PortalManager.cs new file mode 100755 index 0000000..0957c85 --- /dev/null +++ b/Game/PortalManager.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using Microsoft.Xna.Framework; + +namespace KinPortal { + public class PortalManager { + public enum PortalType {RED, BLUE}; + + public Scene Scene {get; private set;} + public Portal BluePortal {get; private set;} + public Portal RedPortal {get; private set;} + + public PortalManager(Scene scene) { + BluePortal = null; + RedPortal = null; + Scene = scene; + } + + public void CreatePortal(Vector3 pos, Quaternion rot, PortalType type) { + if(type == PortalType.RED) { + if(RedPortal == null) { + RedPortal = new Portal("models\\portal_red", Vector3.Zero, Quaternion.Identity, Scene); + Scene.AddComponent(RedPortal); + } + RedPortal.Pos = pos; + RedPortal.Rot = rot; + } + + if(type == PortalType.BLUE) { + if(BluePortal == null) { + BluePortal = new Portal("models\\portal_blue", Vector3.Zero, Quaternion.Identity, Scene); + Scene.AddComponent(BluePortal); + } + BluePortal.Pos = pos; + BluePortal.Rot = rot; + } + } + } +} diff --git a/Game/Program.cs b/Game/Program.cs new file mode 100755 index 0000000..e669c3c --- /dev/null +++ b/Game/Program.cs @@ -0,0 +1,15 @@ +using System; +using Microsoft.Kinect.Toolkit.Interaction; + +namespace KinPortal { +#if WINDOWS || XBOX + static class Program { + static void Main(string[] args) { + using (Game game = new Game()) { + game.Run(); + } + } + } +#endif +} + diff --git a/Game/Properties/AssemblyInfo.cs b/Game/Properties/AssemblyInfo.cs new file mode 100755 index 0000000..c185cd1 --- /dev/null +++ b/Game/Properties/AssemblyInfo.cs @@ -0,0 +1,34 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("WindowsGame1")] +[assembly: AssemblyProduct("WindowsGame1")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. Only Windows +// assemblies support COM. +[assembly: ComVisible(false)] + +// On Windows, the following GUID is for the ID of the typelib if this +// project is exposed to COM. On other platforms, it unique identifies the +// title storage container when deploying this assembly to the device. +[assembly: Guid("0de3b11f-46e3-4b73-994f-c11a55bf2224")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("1.0.0.0")] \ No newline at end of file diff --git a/Game/Scene.cs b/Game/Scene.cs new file mode 100755 index 0000000..13d6a81 --- /dev/null +++ b/Game/Scene.cs @@ -0,0 +1,161 @@ +using System; +using System.Collections.Generic; +using Microsoft.Xna.Framework; +using Jitter; +using Jitter.LinearMath; +using Jitter.Collision; +using Jitter.Dynamics; + +namespace KinPortal { + public class Scene : DrawableGameComponent { + private Camera m_camera = null; + public Camera Camera { + get { + return m_camera; + } + set { + if(m_camera != null) { + Game.Components.Remove(m_camera); + } + m_camera = value; + Game.Components.Add(m_camera); + } + } + + public List Components {get; private set;} + public World PhysicWorld {get; private set;} + public PortalManager PortalManager {get; private set;} + + public Scene(KinPortal.Game game, Camera camera) + : base(game) { + Camera = camera; + Components = new List(); + PhysicWorld = new World(new CollisionSystemSAP()); + PhysicWorld.Gravity = new JVector(0, 0, -9.81f); + PortalManager = new PortalManager(this); + PhysicWorld.CollisionSystem.CollisionDetected += this.CollisionEvent; + UpdateOrder = 0; + } + + public void AddComponent(DrawableGameComponent obj) { + Components.Add(obj); + Game.Components.Add(obj); + if(obj is DynamicObject) { + PhysicWorld.AddBody(((DynamicObject) obj).PhysicModel); + } + } + + public void RemoveComponent(DrawableGameComponent obj) { + Components.Remove(obj); + Game.Components.Remove(obj); + if(obj is DynamicObject) { + PhysicWorld.RemoveBody(((DynamicObject) obj).PhysicModel); + } + } + + private bool RaycastCallback(RigidBody body, JVector normal, float fraction) { + return true; + } + + public override void Update(GameTime gameTime) { + float phyStep = gameTime.ElapsedGameTime.Milliseconds / 1000.0f; + PhysicWorld.Step(phyStep, true); + + KinPortal.Game game = (KinPortal.Game) Game; + + JVector ray; + float fraction; + JVector normal; + HandsProjection leftHand = new HandsProjection(); + HandsProjection rightHand = new HandsProjection(); + JVector camPos = Geometry.GetJitterVector(Camera.Pos); + RigidBody leftBody = null, rightBody = null; + + /* Left Hand projection */ + ray = Geometry.GetJitterVector(Camera.Unproject(game.HandsTracker.LeftHand.Pos.X, game.HandsTracker.LeftHand.Pos.Y)); + ray = JVector.Normalize(ray) * 100.0f; + + if(PhysicWorld.CollisionSystem.Raycast(camPos, ray, RaycastCallback, out leftBody, out normal, out fraction)) { + leftHand.HitPoint = Geometry.GetXnaVector(camPos + fraction * ray); + leftHand.HitNormal = Geometry.GetXnaVector(JVector.Normalize(normal)); + leftHand.IsProjected = true; + } else { + leftHand.IsProjected = false; + } + + /* Right Hand projection */ + ray = Geometry.GetJitterVector(Camera.Unproject(game.HandsTracker.RightHand.Pos.X, game.HandsTracker.RightHand.Pos.Y)); + ray = JVector.Normalize(ray) * 100.0f; + + if(PhysicWorld.CollisionSystem.Raycast(camPos, ray, RaycastCallback, out rightBody, out normal, out fraction)) { + rightHand.HitPoint = Geometry.GetXnaVector(camPos + fraction * ray); + rightHand.HitNormal = Geometry.GetXnaVector(JVector.Normalize(normal)); + rightHand.IsProjected = true; + } else { + rightHand.IsProjected = false; + } + + DynamicObject leftObj = null, rightObj = null; + + foreach(DrawableGameComponent obj in Components) { + if(obj is DynamicObject) { + DynamicObject dobj = (DynamicObject) obj; + if(dobj.PhysicModel == leftBody) { + leftObj = dobj; + } + if(dobj.PhysicModel == rightBody) { + rightObj = dobj; + } + } + } + + if(leftObj == rightObj && leftObj != null) { + leftObj.Target(leftHand, rightHand); + } else { + if(leftObj != null) { + leftObj.Target(leftHand, new HandsProjection(false, Vector3.Zero, Vector3.Zero)); + } + if(rightObj != null) { + rightObj.Target(new HandsProjection(false, Vector3.Zero, Vector3.Zero), rightHand); + } + } + + base.Update(gameTime); + } + + public void CollisionEvent(RigidBody body1, RigidBody body2, JVector point1, JVector point2, JVector normal, float penetration) { + DynamicObject dobj1 = null; + DynamicObject dobj2 = null; + + foreach(DrawableGameComponent obj in Components) { + if(obj is DynamicObject) { + DynamicObject dobj = (DynamicObject) obj; + if(dobj.PhysicModel == body1) { + dobj1 = dobj; + } + if(dobj.PhysicModel == body2) { + dobj2 = dobj; + } + } + } + + if(dobj1 == null || dobj2 == null) { + return; + } + + if(dobj1 is InterruptorObject) { + ((InterruptorObject) dobj1).Collision(dobj2); + } + if(dobj2 is InterruptorObject) { + ((InterruptorObject) dobj2).Collision(dobj1); + } + } + + public override void Draw(GameTime gameTime) { + foreach(DrawableGameComponent obj in Components) { + obj.Draw(gameTime); + } + base.Draw(gameTime); + } + } +} diff --git a/Game/ShapeFactory.cs b/Game/ShapeFactory.cs new file mode 100755 index 0000000..6dddc08 --- /dev/null +++ b/Game/ShapeFactory.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Jitter.LinearMath; +using Jitter.Collision; +using Jitter.Collision.Shapes; + +namespace KinPortal { + public enum ShapeType {BOX, SPHERE, TRIANGULATE} + + public static class ShapeFactory { + public static Shape CreateShape(Model mdl, ShapeType type) { + Geometry geo = new Geometry(mdl); + + if(type == ShapeType.BOX) { + BoundingBox box = geo.Box; + JVector vsize = Geometry.GetJitterVector(box.Max - box.Min); + return new BoxShape(vsize); + } + + if(type == ShapeType.SPHERE) { + return new SphereShape(geo.Sphere.Radius); + } + + if(type == ShapeType.TRIANGULATE) { + return new TriangleMeshShape(new Octree(geo.JVertices, geo.JTriangles)); + } + + return null; + } + } +} diff --git a/Game/SimpleObject.cs b/Game/SimpleObject.cs new file mode 100755 index 0000000..e15e837 --- /dev/null +++ b/Game/SimpleObject.cs @@ -0,0 +1,17 @@ +using Microsoft.Xna.Framework; + +namespace KinPortal { + public class SimpleObject : Object { + public override Vector3 Pos {get; set;} + public override Quaternion Rot {get; set;} + + public SimpleObject(string filepath, Vector3 pos, Quaternion rot, Scene scene) + : base(filepath, scene) { + Pos = pos; + Rot = rot; + } + + public SimpleObject(string filepath, Scene scene) + : this(filepath, Vector3.Zero, Quaternion.Identity, scene) {} + } +} diff --git a/Game/TargetCollisionStrategy.cs b/Game/TargetCollisionStrategy.cs new file mode 100755 index 0000000..197052a --- /dev/null +++ b/Game/TargetCollisionStrategy.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using Microsoft.Xna.Framework; + +namespace KinPortal { + public class TargetCollisionStrategy : CollisionStrategy { + private TimeSpan m_reloadTime; + private bool m_lock = false; + + + public void Update(InterruptorObject obj, GameTime gameTime) { + if(m_lock) { + m_reloadTime += gameTime.ElapsedGameTime; + if(m_reloadTime.Milliseconds >= 300.0) { + m_lock = false; + } + } + } + + public void Collision(InterruptorObject hostObj, DynamicObject obj) { + if(obj.IsStatic == true || m_lock) { + return; + } + + DynamicObject o = new DynamicObject( + "models\\monkey", + new Vector3(0, 0, 8.0f), Quaternion.Identity, + hostObj.Scene, ShapeType.SPHERE, false, new GrabStrategy() + ); + + //o.PhysicModel.AffectedByGravity = false; + + hostObj.Scene.AddComponent(o); + + m_lock = true; + m_reloadTime = TimeSpan.Zero; + } + } +} diff --git a/Game/TargetingStrategy.cs b/Game/TargetingStrategy.cs new file mode 100755 index 0000000..3a291de --- /dev/null +++ b/Game/TargetingStrategy.cs @@ -0,0 +1,11 @@ +using Jitter.Collision; +using Jitter.LinearMath; +using Jitter.Dynamics; +using Microsoft.Xna.Framework; + +namespace KinPortal{ + public interface TargetingStrategy { + void Update(DynamicObject obj, GameTime gameTime); + void Target(DynamicObject obj, HandsProjection lefthand, HandsProjection righthand); + } +} diff --git a/KinPortal.sln b/KinPortal.sln new file mode 100755 index 0000000..cb029d8 --- /dev/null +++ b/KinPortal.sln @@ -0,0 +1,40 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.31101.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Game", "Game\Game.csproj", "{FAA2E885-42FD-49FD-8678-925331BBE9C8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Resources", "Resources\Resources.contentproj", "{75D6A435-2F46-4192-B2B7-37A17468C066}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|Mixed Platforms = Debug|Mixed Platforms + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|Mixed Platforms = Release|Mixed Platforms + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {FAA2E885-42FD-49FD-8678-925331BBE9C8}.Debug|Any CPU.ActiveCfg = Debug|x86 + {FAA2E885-42FD-49FD-8678-925331BBE9C8}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {FAA2E885-42FD-49FD-8678-925331BBE9C8}.Debug|Mixed Platforms.Build.0 = Debug|x86 + {FAA2E885-42FD-49FD-8678-925331BBE9C8}.Debug|x86.ActiveCfg = Debug|x86 + {FAA2E885-42FD-49FD-8678-925331BBE9C8}.Debug|x86.Build.0 = Debug|x86 + {FAA2E885-42FD-49FD-8678-925331BBE9C8}.Release|Any CPU.ActiveCfg = Release|x86 + {FAA2E885-42FD-49FD-8678-925331BBE9C8}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {FAA2E885-42FD-49FD-8678-925331BBE9C8}.Release|Mixed Platforms.Build.0 = Release|x86 + {FAA2E885-42FD-49FD-8678-925331BBE9C8}.Release|x86.ActiveCfg = Release|x86 + {FAA2E885-42FD-49FD-8678-925331BBE9C8}.Release|x86.Build.0 = Release|x86 + {75D6A435-2F46-4192-B2B7-37A17468C066}.Debug|Any CPU.ActiveCfg = Debug|x86 + {75D6A435-2F46-4192-B2B7-37A17468C066}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {75D6A435-2F46-4192-B2B7-37A17468C066}.Debug|x86.ActiveCfg = Debug|x86 + {75D6A435-2F46-4192-B2B7-37A17468C066}.Release|Any CPU.ActiveCfg = Release|x86 + {75D6A435-2F46-4192-B2B7-37A17468C066}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {75D6A435-2F46-4192-B2B7-37A17468C066}.Release|x86.ActiveCfg = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Resources/Resources.contentproj b/Resources/Resources.contentproj new file mode 100755 index 0000000..3460977 --- /dev/null +++ b/Resources/Resources.contentproj @@ -0,0 +1,139 @@ + + + + {75D6A435-2F46-4192-B2B7-37A17468C066} + {96E2B04D-8817-42c6-938A-82C39BA4D311};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + Debug + x86 + Library + Properties + v4.0 + v4.0 + bin\$(Platform)\$(Configuration) + Resources + + + x86 + + + x86 + + + WindowsGame1Content + + + + + + + + + + + + monkey + FbxImporter + ModelProcessor + + + + + tex_wall + TextureImporter + TextureProcessor + + + + + cube + FbxImporter + ModelProcessor + + + + + sphere + FbxImporter + ModelProcessor + + + + + tex_cursor_blue + TextureImporter + TextureProcessor + + + + + tex_cursor_red + TextureImporter + TextureProcessor + + + + + tex_floor + TextureImporter + TextureProcessor + + + tex_roof + TextureImporter + TextureProcessor + + + + + level + FbxImporter + ModelProcessor + + + + + tex_portal_blue + TextureImporter + TextureProcessor + + + tex_portal_red + TextureImporter + TextureProcessor + + + + + portal_blue + FbxImporter + ModelProcessor + + + portal_red + FbxImporter + ModelProcessor + + + + + tex_target + TextureImporter + TextureProcessor + + + + + target + FbxImporter + ModelProcessor + + + + + \ No newline at end of file diff --git a/Resources/level.blend b/Resources/level.blend new file mode 100755 index 0000000..05a05f8 Binary files /dev/null and b/Resources/level.blend differ diff --git a/Resources/models/cube.fbx b/Resources/models/cube.fbx new file mode 100755 index 0000000..1649fa1 Binary files /dev/null and b/Resources/models/cube.fbx differ diff --git a/Resources/models/level.fbx b/Resources/models/level.fbx new file mode 100755 index 0000000..56f1862 Binary files /dev/null and b/Resources/models/level.fbx differ diff --git a/Resources/models/monkey.fbx b/Resources/models/monkey.fbx new file mode 100755 index 0000000..733ce3f Binary files /dev/null and b/Resources/models/monkey.fbx differ diff --git a/Resources/models/portal_blue.fbx b/Resources/models/portal_blue.fbx new file mode 100755 index 0000000..e87f20d Binary files /dev/null and b/Resources/models/portal_blue.fbx differ diff --git a/Resources/models/portal_red.fbx b/Resources/models/portal_red.fbx new file mode 100755 index 0000000..a35579e Binary files /dev/null and b/Resources/models/portal_red.fbx differ diff --git a/Resources/models/sphere.fbx b/Resources/models/sphere.fbx new file mode 100755 index 0000000..8482e8b Binary files /dev/null and b/Resources/models/sphere.fbx differ diff --git a/Resources/models/target.fbx b/Resources/models/target.fbx new file mode 100755 index 0000000..7531190 Binary files /dev/null and b/Resources/models/target.fbx differ diff --git a/Resources/portal.blend b/Resources/portal.blend new file mode 100755 index 0000000..cb71cc1 Binary files /dev/null and b/Resources/portal.blend differ diff --git a/Resources/target.blend b/Resources/target.blend new file mode 100755 index 0000000..da28163 Binary files /dev/null and b/Resources/target.blend differ diff --git a/Resources/textures/tex_cursor_blue.png b/Resources/textures/tex_cursor_blue.png new file mode 100755 index 0000000..0dbe50d Binary files /dev/null and b/Resources/textures/tex_cursor_blue.png differ diff --git a/Resources/textures/tex_cursor_red.png b/Resources/textures/tex_cursor_red.png new file mode 100755 index 0000000..5a01875 Binary files /dev/null and b/Resources/textures/tex_cursor_red.png differ diff --git a/Resources/textures/tex_floor.png b/Resources/textures/tex_floor.png new file mode 100755 index 0000000..0233640 Binary files /dev/null and b/Resources/textures/tex_floor.png differ diff --git a/Resources/textures/tex_portal_blue.png b/Resources/textures/tex_portal_blue.png new file mode 100755 index 0000000..de717b8 Binary files /dev/null and b/Resources/textures/tex_portal_blue.png differ diff --git a/Resources/textures/tex_portal_red.png b/Resources/textures/tex_portal_red.png new file mode 100755 index 0000000..51c9fa0 Binary files /dev/null and b/Resources/textures/tex_portal_red.png differ diff --git a/Resources/textures/tex_roof.png b/Resources/textures/tex_roof.png new file mode 100755 index 0000000..8f93972 Binary files /dev/null and b/Resources/textures/tex_roof.png differ diff --git a/Resources/textures/tex_target.png b/Resources/textures/tex_target.png new file mode 100755 index 0000000..072fef6 Binary files /dev/null and b/Resources/textures/tex_target.png differ diff --git a/Resources/textures/tex_wall.png b/Resources/textures/tex_wall.png new file mode 100755 index 0000000..51f25a6 Binary files /dev/null and b/Resources/textures/tex_wall.png differ