protected override JobHandle OnUpdate(JobHandle inputDeps) { if (m_MouseGroup.CalculateEntityCount() == 0) { return(inputDeps); } ComponentDataFromEntity <Translation> Positions = GetComponentDataFromEntity <Translation>(true); ComponentDataFromEntity <Rotation> Rotations = GetComponentDataFromEntity <Rotation>(true); ComponentDataFromEntity <PhysicsVelocity> Velocities = GetComponentDataFromEntity <PhysicsVelocity>(); ComponentDataFromEntity <PhysicsMass> Masses = GetComponentDataFromEntity <PhysicsMass>(true); // If there's a pick job, wait for it to finish if (m_PickSystem.PickJobHandle != null) { JobHandle.CombineDependencies(inputDeps, m_PickSystem.PickJobHandle.Value).Complete(); } // If there's a picked entity, drag it MousePickSystem.SpringData springData = m_PickSystem.SpringDatas[0]; if (springData.Dragging != 0) { Entity entity = m_PickSystem.SpringDatas[0].Entity; if (!EntityManager.HasComponent <PhysicsMass>(entity)) { return(inputDeps); } PhysicsMass massComponent = Masses[entity]; PhysicsVelocity velocityComponent = Velocities[entity]; if (massComponent.InverseMass == 0) { return(inputDeps); } var worldFromBody = new MTransform(Rotations[entity].Value, Positions[entity].Value); // Body to motion transform var bodyFromMotion = new MTransform(Masses[entity].InertiaOrientation, Masses[entity].CenterOfMass); MTransform worldFromMotion = Mul(worldFromBody, bodyFromMotion); // Damp the current velocity const float gain = 0.95f; velocityComponent.Linear *= gain; velocityComponent.Angular *= gain; // Get the body and mouse points in world space float3 pointBodyWs = Mul(worldFromBody, springData.PointOnBody); float3 pointSpringWs = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, springData.MouseDepth)); // Calculate the required change in velocity float3 pointBodyLs = Mul(Inverse(bodyFromMotion), springData.PointOnBody); float3 deltaVelocity; { float3 pointDiff = pointBodyWs - pointSpringWs; float3 relativeVelocityInWorld = velocityComponent.Linear + math.mul(worldFromMotion.Rotation, math.cross(velocityComponent.Angular, pointBodyLs)); const float elasticity = 0.1f; const float damping = 0.5f; deltaVelocity = -pointDiff * (elasticity / Time.fixedDeltaTime) - damping * relativeVelocityInWorld; } // Build effective mass matrix in world space // TODO how are bodies with inf inertia and finite mass represented // TODO the aggressive damping is hiding something wrong in this code if dragging non-uniform shapes float3x3 effectiveMassMatrix; { float3 arm = pointBodyWs - worldFromMotion.Translation; var skew = new float3x3( new float3(0.0f, arm.z, -arm.y), new float3(-arm.z, 0.0f, arm.x), new float3(arm.y, -arm.x, 0.0f) ); // world space inertia = worldFromMotion * inertiaInMotionSpace * motionFromWorld var invInertiaWs = new float3x3( massComponent.InverseInertia.x * worldFromMotion.Rotation.c0, massComponent.InverseInertia.y * worldFromMotion.Rotation.c1, massComponent.InverseInertia.z * worldFromMotion.Rotation.c2 ); invInertiaWs = math.mul(invInertiaWs, math.transpose(worldFromMotion.Rotation)); float3x3 invEffMassMatrix = math.mul(math.mul(skew, invInertiaWs), skew); invEffMassMatrix.c0 = new float3(massComponent.InverseMass, 0.0f, 0.0f) - invEffMassMatrix.c0; invEffMassMatrix.c1 = new float3(0.0f, massComponent.InverseMass, 0.0f) - invEffMassMatrix.c1; invEffMassMatrix.c2 = new float3(0.0f, 0.0f, massComponent.InverseMass) - invEffMassMatrix.c2; effectiveMassMatrix = math.inverse(invEffMassMatrix); } // Calculate impulse to cause the desired change in velocity float3 impulse = math.mul(effectiveMassMatrix, deltaVelocity); // Clip the impulse const float maxAcceleration = 250.0f; float maxImpulse = math.rcp(massComponent.InverseMass) * Time.fixedDeltaTime * maxAcceleration; impulse *= math.min(1.0f, math.sqrt((maxImpulse * maxImpulse) / math.lengthsq(impulse))); // Apply the impulse { velocityComponent.Linear += impulse * massComponent.InverseMass; float3 impulseLs = math.mul(math.transpose(worldFromMotion.Rotation), impulse); float3 angularImpulseLs = math.cross(pointBodyLs, impulseLs); velocityComponent.Angular += angularImpulseLs * massComponent.InverseInertia; } // Write back velocity Velocities[entity] = velocityComponent; } return(inputDeps); }
protected override JobHandle OnUpdate(JobHandle inputDeps) { if (m_MouseGroup.CalculateLength() == 0) { return(inputDeps); } var Positions = GetComponentDataFromEntity <Translation>(); // If there's a pick job, wait for it to finish if (m_PickSystem.PickJobHandle != null) { JobHandle.CombineDependencies(inputDeps, m_PickSystem.PickJobHandle.Value).Complete(); } // If there's a picked entity, drag it MousePickSystem.SpringData springData = m_PickSystem.SpringDatas[0]; var physicsWorld = m_PickSystem.m_BuildPhysicsWorldSystem.PhysicsWorld; if (springData.Dragging != 0) { Entity entity = m_PickSystem.SpringDatas[0].Entity; if (!m_WasDragging) { GameMgr.g.PlayPickMonsterSoundEffect(entity); } m_WasDragging = true; Translation posComponent = Positions[entity]; m_SelectedEntity = entity; var hits = new NativeList <RaycastHit>(Allocator.TempJob); physicsWorld.CollisionWorld.CastRay(MousePickBehaviour.CreateRayCastFromMouse(), ref hits); var terrainEntities = m_TerrainEntityQuery.ToEntityArray(Allocator.TempJob); foreach (var terrainEntity in terrainEntities) { var index = physicsWorld.GetRigidBodyIndex(terrainEntity); foreach (var hit in hits.ToArray()) { if (hit.RigidBodyIndex == index) { posComponent.Value.x = hit.Position.x; posComponent.Value.z = hit.Position.z; Positions[entity] = posComponent; break; } } } hits.Dispose(); terrainEntities.Dispose(); } else if (m_WasDragging) { m_WasDragging = false; var hits = new NativeList <RaycastHit>(Allocator.TempJob); physicsWorld.CollisionWorld.CastRay(MousePickBehaviour.CreateRayCastFromMouse(), ref hits); var roomEntities = m_RoomEntityQuery.ToEntityArray(Allocator.TempJob); foreach (var room in roomEntities) { var index = physicsWorld.GetRigidBodyIndex(room); foreach (var hit in hits.ToArray()) { if (hit.RigidBodyIndex == index) { GameMgr.MoveMonsterToRoom(m_SelectedEntity, room, 0); } } } hits.Dispose(); roomEntities.Dispose(); } return(inputDeps); }