Exemplo n.º 1
0
        public override Matrix[] GetBoneTransforms()
        {
            Matrix[] child1Transforms = child1.GetBoneTransforms();
            Matrix[] child2Transforms = child2.GetBoneTransforms();

            return(SpaceUtils.LerpSkeletalPose(child1Transforms, child2Transforms, BlendFactor));
        }
Exemplo n.º 2
0
        public Matrix[] GetSkinTransforms()
        {
            Matrix[] blendStartPose;

            if (blendFromCancelledTransitionPose)
            {
                blendStartPose = cancelledPose;
            }
            else
            {
                blendStartPose = CurrentState.GetBoneTransforms();
            }

            if (ActiveTransition == null)
            {
                return(CreateSkinTransforms(blendStartPose));
            }
            else
            {
                Matrix[] nextStatePose = nextState.GetBoneTransforms();

                float pctComplete = (float)(transitionTime.TotalSeconds / ActiveTransition.DurationInSeconds);
                float blendFactor = ActiveTransition.UseSmoothStep ? MathHelper.SmoothStep(0.0f, 1.0f, pctComplete) : pctComplete;

                Matrix[] blendedPose = SpaceUtils.LerpSkeletalPose(blendStartPose, nextStatePose, blendFactor);

                // Store the blended pose in case we have to cancel the transition next frame.
                if (!blendFromCancelledTransitionPose)
                {
                    cancelledPose = blendedPose;
                }

                return(CreateSkinTransforms(blendedPose));
            }
        }
Exemplo n.º 3
0
        public override Matrix[] GetBoneTransforms()
        {
            // Pick an arbitrary node to 'prime' the refinement loop
            float lowerBoundPosition = ChildrenByPosition.Keys.Min();
            float upperBoundPosition = ChildrenByPosition.Keys.Max();

            foreach (float position in ChildrenByPosition.Keys)
            {
                if (position > lowerBoundPosition && position <= BlendPosition)
                {
                    lowerBoundPosition = position;
                }

                if (position < upperBoundPosition && position >= BlendPosition)
                {
                    upperBoundPosition = position;
                }
            }

            Matrix[] lowerBoundTransforms = ChildrenByPosition[lowerBoundPosition].GetBoneTransforms();

            if (lowerBoundPosition == upperBoundPosition)
            {
                return(lowerBoundTransforms);
            }

            Matrix[] upperBoundTransforms = ChildrenByPosition[upperBoundPosition].GetBoneTransforms();
            float    blendFactor          = (BlendPosition - lowerBoundPosition) /
                                            (upperBoundPosition - lowerBoundPosition);

            return(SpaceUtils.LerpSkeletalPose(lowerBoundTransforms, upperBoundTransforms, blendFactor));
        }
Exemplo n.º 4
0
        public override Matrix[] GetBoneTransforms()
        {
            Matrix[] child1Transforms = child1.GetBoneTransforms();
            Matrix[] child2Transforms = child2.GetBoneTransforms();
            Matrix[] child3Transforms = child2.GetBoneTransforms();

            Matrix[] blendedTransforms = new Matrix[child1Transforms.Length];

            Vector3    scale1;
            Quaternion rot1;
            Vector3    pos1;
            Vector3    scale2;
            Quaternion rot2;
            Vector3    pos2;
            Vector3    scale3;
            Quaternion rot3;
            Vector3    pos3;

            Vector3 bc = SpaceUtils.GetBarycentricCoords(Child1Position, Child2Position, Child3Position, BlendPosition);

            for (int p = 0; p < child1Transforms.Length; p++)
            {
                child1Transforms[p].Decompose(out scale1, out rot1, out pos1);
                child2Transforms[p].Decompose(out scale2, out rot2, out pos2);
                child3Transforms[p].Decompose(out scale3, out rot3, out pos3);
                blendedTransforms[p] = Matrix.CreateScale(scale1 * bc.X + scale2 * bc.Y + scale3 * bc.Z) *
                                       Matrix.CreateFromQuaternion(Quaternion.Normalize(rot1 * bc.X + rot2 * bc.Y + rot3 * bc.Z)) *
                                       Matrix.CreateTranslation(pos1 * bc.X + pos2 * bc.Y + pos3 * bc.Z);
            }

            return(blendedTransforms);
        }
Exemplo n.º 5
0
        private void ControlBiped(InputManager input, float dTimeMs)
        {
            // If input is in line with avatar orientation, you get all movement, otherwise, some of the movement is dulled to get reorientation

            Vector3 moveInput = Vector3.Zero;

            if (ActiveInputMap.FullIntervalMap.ContainsKey(FullIntervalControlActions.MoveLeftRightRate))
            {
                moveInput.X += input.GetFullIntervalControlValue(ActiveInputMap.FullIntervalMap[FullIntervalControlActions.MoveLeftRightRate],
                                                                 InputIndex);
            }

            if (ActiveInputMap.FullIntervalMap.ContainsKey(FullIntervalControlActions.MoveDownUpRate))
            {
                moveInput.Z -= input.GetFullIntervalControlValue(ActiveInputMap.FullIntervalMap[FullIntervalControlActions.MoveDownUpRate],
                                                                 InputIndex);
            }

            float moveAmount = moveInput.Length();

            if (moveAmount > 0.0f)
            {
                // Transform (rotation only) the input from view space into world space
                Matrix cameraRotation = mReferenceCam.Transform;
                cameraRotation.Translation = Vector3.Zero;
                moveInput = Vector3.Transform(moveInput, cameraRotation);

                if (moveInput.X != 0.0f || moveInput.Z != 0.0f)
                {
                    moveInput.Y = 0.0f;
                    Quaternion directionDiff = SpaceUtils.GetSweptQuaternion(BepuConverter.Convert(mBipedControl.Controller.HorizontalViewDirection), moveInput);

                    mBipedControl.OrientationChange = directionDiff;
                }
            }

            mBipedControl.HorizontalMovement = Vector2.UnitY * moveAmount;

            // Crouching:
            if (input.CheckForBinaryInput(ActiveInputMap, BinaryControlActions.Crouch, InputIndex))
            {
                mBipedControl.DesiredMovementActions |= BipedControllerComponent.MovementActions.Crouching;
            }
            else
            {
                mBipedControl.DesiredMovementActions &= ~BipedControllerComponent.MovementActions.Crouching;
            }

            // Jumping:
            if (input.CheckForNewBinaryInput(ActiveInputMap, BinaryControlActions.Jump, InputIndex))
            {
                mBipedControl.DesiredMovementActions |= BipedControllerComponent.MovementActions.Jumping;
            }
            else
            {
                mBipedControl.DesiredMovementActions &= ~BipedControllerComponent.MovementActions.Jumping;
            }
        }
Exemplo n.º 6
0
        public override Matrix[] GetBoneTransforms()
        {
            foreach (TernaryLerpBlendNode tri in Triangulation)
            {
                Vector3 bc = SpaceUtils.GetBarycentricCoords(tri.Child1Position, tri.Child2Position, tri.Child3Position, BlendPosition);
                if (bc.X >= 0.0f && bc.Y >= 0.0f && bc.Z >= 0.0f)
                {
                    return(tri.GetBoneTransforms());
                }
            }

            throw new InvalidOperationException("The triangulation does not contain the BlendPosition point");
        }
Exemplo n.º 7
0
        public override void Fire()
        {
            const float ATTACK_RADIUS = 3.0f;
            const float ATTACK_LENGTH = 4.0f;
            // Play 'thwack' sound
            Actor owner = GameResources.ActorManager.GetActorById(OwnerActorId);
            BipedControllerComponent bipedControl = owner.GetComponent <BipedControllerComponent>(ActorComponent.ComponentType.Control);
            RigidTransform           alignCapsule = new RigidTransform(BepuVec3.Forward * ATTACK_LENGTH * 0.5f + BepuConverter.Convert(MuzzleOffset),
                                                                       BepuQuaternion.CreateFromAxisAngle(BepuVec3.Right, MathHelper.PiOver2));

            Vector3 aim = (bipedControl.WorldAim.HasValue ? bipedControl.WorldAim.Value :
                           BepuConverter.Convert(bipedControl.Controller.ViewDirection));
            RigidTransform positionAndAim = new RigidTransform(bipedControl.Controller.Body.Position, BepuConverter.Convert(
                                                                   SpaceUtils.GetOrientation(aim, Vector3.Up)));

            RigidTransform attackTransform;

            RigidTransform.Transform(ref alignCapsule, ref positionAndAim, out attackTransform);

            ConvexShape          bashShape   = new CapsuleShape(ATTACK_LENGTH, ATTACK_RADIUS);
            BepuVec3             noSweep     = BepuVec3.Zero;
            List <RayCastResult> dudesBashed = new List <RayCastResult>();
            AttackFilter         filter      = new AttackFilter(GameResources.ActorManager.IsMob(OwnerActorId));

            GameResources.ActorManager.SimSpace.ConvexCast(bashShape, ref attackTransform, ref noSweep, filter.Test, dudesBashed);

            foreach (RayCastResult dude in dudesBashed)
            {
                EntityCollidable otherEntityCollidable = dude.HitObject as EntityCollidable;
                Terrain          otherTerrain          = dude.HitObject as Terrain;
                if (otherEntityCollidable != null &&
                    otherEntityCollidable.Entity != null &&
                    otherEntityCollidable.Entity.Tag != null)
                {
                    Actor      actorHit = GameResources.ActorManager.GetActorById((int)(otherEntityCollidable.Entity.Tag));
                    IDamagable damage   = actorHit.GetBehaviorThatImplementsType <IDamagable>();
                    if (damage != null)
                    {
                        damage.TakeDamage(Damage);
                        // TODO: P2: Query hit actor for appropiate damage effect e.g. blood and create it;
                    }
                    BashDust(dude.HitData.Location);
                }
                else if (otherTerrain != null)
                {
                    BashDust(dude.HitData.Location);
                }
            }
        }
Exemplo n.º 8
0
        private void CommitOrientationAndMovement()
        {
            if (mState == ControllerState.InputDisabled)
            {
                Controller.HorizontalMotionConstraint.SpeedScale        = 0.0f;
                Controller.HorizontalMotionConstraint.MovementDirection = BEPUutilities.Vector2.UnitY;
                return;
            }

            float diffAngle = (float)(SpaceUtils.GetQuaternionAngle(OrientationChange));

            if (diffAngle > MaxTurnAnglePerTick)
            {
                OrientationChange  = Quaternion.Slerp(Quaternion.Identity, OrientationChange, MaxTurnAnglePerTick / diffAngle);
                HorizontalMovement = Vector2.Zero; // We spent our movement turning (but this won't affect boosting.)
            }

            Controller.ViewDirection = BEPUutilities.Quaternion.Transform(Controller.ViewDirection, BepuConverter.Convert(OrientationChange));
            OrientationChange        = Quaternion.Identity;

            if (mState == ControllerState.Boosting)
            {
                Controller.HorizontalMotionConstraint.SpeedScale        = 1.0f;
                Controller.HorizontalMotionConstraint.MovementDirection = BEPUutilities.Vector2.UnitY;
            }
            else
            {
                if (AimCheck())
                {
                    Controller.HorizontalMotionConstraint.SpeedScale        = 0.375f * Math.Min(1.0f, HorizontalMovement.Length());
                    Controller.HorizontalMotionConstraint.MovementDirection = BepuConverter.Convert(HorizontalMovement);
                }
                else
                {
                    SetHorizontalMovementAloof();
                }
            }
        }
Exemplo n.º 9
0
        private void PreAnimationUpdateHandler(object sender, UpdateStepEventArgs e)
        {
            float elapsedTime = (float)(e.GameTime.ElapsedGameTime.TotalSeconds);

            Actor avatar = GameResources.ActorManager.GetActorById(ActorId);
            BipedControllerComponent bipedControl = avatar.GetComponent <BipedControllerComponent>(ActorComponent.ComponentType.Control);

            // Update the camera.
            Vector3 desiredCameraPosition;

            if (mInputMode == InputMode.Aloof)
            {
                Matrix        cameraRotation = Matrix.CreateFromYawPitchRoll(mMmoCameraDesc.Yaw, mMmoCameraDesc.Pitch, 0.0f);
                BepuRay       boomRay        = new BepuRay(mAvatarBepuEntity.Position, BepuConverter.Convert(cameraRotation.Backward));
                RayCastResult result;

                GameResources.ActorManager.SimSpace.RayCast(boomRay, mMmoCameraDesc.Distance, CameraClipFilter, out result);

                desiredCameraPosition = result.HitObject != null?
                                        BepuConverter.Convert(BEPUutilities.Vector3.Lerp(result.HitData.Location, mAvatarBepuEntity.Position, 0.05f)) :
                                            BepuConverter.Convert(mAvatarBepuEntity.Position) + mMmoCameraDesc.Distance * cameraRotation.Backward;
            }
            else if (mInputMode == InputMode.Aiming)
            {
                Matrix viewRotation = Matrix.CreateWorld(Vector3.Zero, BepuConverter.Convert(
                                                             bipedControl.Controller.ViewDirection), Vector3.Up);
                desiredCameraPosition = BepuConverter.Convert(mAvatarBepuEntity.Position) + Vector3.Transform(
                    mAimingCameraOffset, viewRotation);
            }
            else
            {
                desiredCameraPosition = mCamera.Transform.Translation;
            }

            Vector3 newCameraPosition = desiredCameraPosition;

            Vector3 desiredCameraDirection;

            if (mInputMode == InputMode.Aloof)
            {
                desiredCameraDirection = BepuConverter.Convert(mAvatarBepuEntity.Position) - newCameraPosition;
            }
            else if (mInputMode == InputMode.Aiming)
            {
                desiredCameraDirection = BepuConverter.Convert(bipedControl.Controller.ViewDirection);
            }
            else
            {
                desiredCameraDirection = mCamera.Transform.Forward;
            }
            desiredCameraDirection.Normalize();

            Vector3 newCameraDirection = desiredCameraDirection;

            if (mCameraSmoothingEngaged)
            {
                Vector3    positionDelta  = desiredCameraPosition - mCamera.Transform.Translation;
                Quaternion directionDelta = SpaceUtils.GetSweptQuaternion(mCamera.Transform.Forward, desiredCameraDirection);

                const float POSITION_DELTA_THRESHHOLD  = 4.0f;
                const float DIRECTION_DELTA_THRESHHOLD = MathHelper.Pi / 16.0f;

                float positionDeltaLength = positionDelta.Length();
                float directionDeltaAngle = (float)(SpaceUtils.GetQuaternionAngle(directionDelta));

                float fractionComplete = Math.Min(POSITION_DELTA_THRESHHOLD / positionDeltaLength,
                                                  DIRECTION_DELTA_THRESHHOLD / directionDeltaAngle);

                if (fractionComplete < 1.0f)
                {
                    newCameraPosition = Vector3.Lerp(mCamera.Transform.Translation, desiredCameraPosition, fractionComplete);
                    Quaternion smoothedCamRotation = Quaternion.Slerp(Quaternion.Identity, directionDelta, fractionComplete);
                    newCameraDirection = Vector3.Transform(mCamera.Transform.Forward, smoothedCamRotation);
                }
            }
            else
            {
                mCameraSmoothingEngaged = true;
            }

            mCamera.Transform = Matrix.CreateWorld(newCameraPosition, newCameraDirection, Vector3.Up);
        }
Exemplo n.º 10
0
        private void ProcessAIStepHandler(object sender, UpdateStepEventArgs e)
        {
            // Check FOV, add any new foes to memory. And update existing ones. We may also have gained new memories by other means.

            // Get players and mobs in field of vision:
            List <RayCastResult> actorsInView = new List <RayCastResult>();

            ConeShape visionCone                 = new ConeShape(VisionDistance, VisionDistance);
            BipedControllerComponent bcc         = Owner.GetComponent <BipedControllerComponent>(ActorComponent.ComponentType.Control);
            RigidTransform           tipOverCone = new RigidTransform(BepuVec3.Forward * VisionDistance * 0.75f,
                                                                      BepuQuaternion.CreateFromAxisAngle(BepuVec3.Right, MathHelper.PiOver2));
            RigidTransform eyeLevelAndFacing = new RigidTransform(bcc.Controller.Body.Position - bcc.Controller.Down * bcc.Controller.Body.Height * 0.45f,
                                                                  BepuConverter.Convert(SpaceUtils.GetOrientation(BepuConverter.Convert(bcc.Controller.ViewDirection), Vector3.Up)));
            RigidTransform visionConeTransform;

            RigidTransform.Transform(ref tipOverCone, ref eyeLevelAndFacing, out visionConeTransform);
            BepuVec3           noSweep = BepuVec3.Zero;
            ViewInterestFilter filter  = new ViewInterestFilter(bcc.Controller.Body.CollisionInformation);

            GameResources.ActorManager.SimSpace.ConvexCast(visionCone, ref visionConeTransform, ref noSweep, filter.Test, actorsInView);

            for (int a = 0; a < actorsInView.Count; ++a)
            {
                // Does this actor warrant an addition to be made to our memory?
                // If so, check for LOS and recheck range. If those tests pass, modify the memory.
                EntityCollidable otherEntityCollidable = actorsInView[a].HitObject as EntityCollidable;
                // We can jump to the Id in the Tag property because we know the filter has validated this.
                int   actorId     = (int)(otherEntityCollidable.Entity.Tag);
                Actor viewedActor = GameResources.ActorManager.GetActorById(actorId);
                BipedControllerComponent viewedActorBcc = viewedActor.GetComponent <BipedControllerComponent>(ActorComponent.ComponentType.Control);
                BepuVec3 toSubject = viewedActorBcc.Controller.Body.Position - eyeLevelAndFacing.Position;

                // Check range:
                if (toSubject.LengthSquared() <= VisionDistance * VisionDistance)
                {
                    BepuRay losRay = new BepuRay(eyeLevelAndFacing.Position, toSubject);

                    RayCastResult losResult;
                    LOSFilter     losFilter = new LOSFilter(bcc.Controller.Body.CollisionInformation, otherEntityCollidable);
                    GameResources.ActorManager.SimSpace.RayCast(losRay, VisionDistance, losFilter.Test, out losResult);
                    EntityCollidable losEC = losResult.HitObject as EntityCollidable;

                    // Test for LOS:
                    if (losEC != null &&
                        losEC.Entity != null &&
                        losEC.Entity.Tag != null &&
                        (int)(losEC.Entity.Tag) == actorId)
                    {
                        // The viewed actor is either a player(foe) or a mob(ally).
                        if (GameResources.ActorManager.IsPlayer(actorId))
                        {
                            mMemory.SpotFoe(actorId);
                        }
                        else
                        {
                            IAgentStateManager agent = viewedActor.GetBehaviorThatImplementsType <IAgentStateManager>();
                            if (agent != null &&
                                agent.HasProperty(AgentPropertyName.ActiveOpponent))
                            {
                                int mobFoe = agent.GetProperty <int>(AgentPropertyName.ActiveOpponent);
                                mMemory.SenseFoe(mobFoe);
                            }
                        }
                    }
                }
            }

            // Evaluate current threats and select one to engage:
            int enemyId = mMemory.GetLargestThreat();

            if (enemyId != Actor.INVALID_ACTOR_ID)
            {
                if (mAgentProperties.ContainsKey(AgentPropertyName.ActiveOpponent))
                {
                    if ((int)(mAgentProperties[AgentPropertyName.ActiveOpponent]) != enemyId)
                    {
                        mAgentProperties[AgentPropertyName.ActiveOpponent] = enemyId;
                    }
                }
                else
                {
                    mAgentProperties.Add(AgentPropertyName.ActiveOpponent, enemyId);
                }
            }
            else
            {
                if (mAgentProperties.ContainsKey(AgentPropertyName.ActiveOpponent))
                {
                    mAgentProperties.Remove(AgentPropertyName.ActiveOpponent);
                }
            }

            TimeInState += e.GameTime.ElapsedGameTime;
            CurrentState.Update(mSteering, Owner, this);
            Vector2 locomotion = mSteering.ComputeForce(Owner);

            if (locomotion.LengthSquared() == 0.0f)
            {
                bcc.OrientationChange  = Quaternion.Identity;
                bcc.HorizontalMovement = Vector2.Zero;
            }
            else
            {
                bcc.OrientationChange  = Quaternion.CreateFromAxisAngle(Vector3.Up, (float)(Math.Atan2(-locomotion.X, locomotion.Y)));
                bcc.HorizontalMovement = locomotion.Length() * Vector2.UnitY;
            }

            mMemory.Fade(e.GameTime);
        }
Exemplo n.º 11
0
        // Steer away from obstacles in the way. This method returns a zero vector if no correction is required.
        // It should be high priority and the steering from other behaviors should blend into the remaining space.
        // So if this returns a length 1.0f vector, avoiding the obstacle is most urgent and there is no room for other
        // steering.
        private Vector2 AvoidObstacles(Actor owner)
        {
            BipedControllerComponent bcc = owner.GetComponent <BipedControllerComponent>(ActorComponent.ComponentType.Control);

            // Conditions where we do not want to use this steering force.
            if (GetAngleFromVertical(bcc.Controller.Body.LinearVelocity) < MathHelper.PiOver4 ||    // We're probably falling...
                !bcc.Controller.SupportFinder.HasSupport ||
                !bcc.Controller.SupportFinder.HasTraction)
            {
                return(Vector2.Zero);
            }

            // Sphere cast ahead along facing.
            List <RayCastResult> obstacles          = new List <RayCastResult>();
            SphereShape          probe              = new SphereShape(bcc.Controller.BodyRadius * 1.1f);
            RigidTransform       probeStartPosition = new RigidTransform(bcc.Controller.Body.Position);
            // Add a small constant to the probe length because we want a minimum amount of forward probing, even if we are not moving.
            float          probeLength = Math.Max(BepuVec3.Dot(bcc.Controller.Body.LinearVelocity, bcc.Controller.ViewDirection), 0.0f) + 1.0f;
            BepuVec3       probeSweep  = bcc.Controller.ViewDirection * probeLength;
            ObstacleFilter filter      = new ObstacleFilter(bcc.Controller.Body.CollisionInformation);

            GameResources.ActorManager.SimSpace.ConvexCast(probe, ref probeStartPosition, ref probeSweep, filter.Test, obstacles);

            RayCastDistanceComparer rcdc = new RayCastDistanceComparer();

            obstacles.Sort(rcdc);

            BEPUutilities.Vector3 cross = BEPUutilities.Vector3.Zero;
            int obstacleIndex           = 0;

            do
            {
                if (obstacles.Count == obstacleIndex)
                {
                    return(Vector2.Zero);
                }

                cross = BEPUutilities.Vector3.Cross(bcc.Controller.ViewDirection, -obstacles[obstacleIndex++].HitData.Normal);
            }while (cross.X > 0.7f); // if cross.X > 0.7f, the obstacle is some kind of gentle ramp; ignore it.

            // dot will typically be negative and magnitude indicates how directly ahead the obstacle is.
            float dot = BEPUutilities.Vector3.Dot(bcc.Controller.ViewDirection, -obstacles[0].HitData.Normal);

            if (dot >= 0.0f) // The obstacle won't hinder us if we touch it.
            {
                return(Vector2.Zero);
            }

            // When cross.Y is positive, the object is generally to the right, so veer left (and vice versa).
            float directionSign = cross.Y >= 0.0f ? -1.0f : 1.0f;

            BEPUutilities.Vector2 result = BEPUutilities.Vector2.UnitX * directionSign * -dot;

            // Also scale response by how close the obstacle is.
            float distance = (obstacles[0].HitData.Location - bcc.Controller.Body.Position).Length();

            result *= MathHelper.Clamp((1.0f - distance / probeLength), 0.0f, 1.0f); // / Math.Abs(dot);


            // So far the result is in terms of 'velocity space'. Rotate it to align with the controller facing.
            float velocityTheta = (float)(Math.Atan2(-probeSweep.X, -probeSweep.Z));

            BEPUutilities.Matrix2x2 velocityWorld = SpaceUtils.Create2x2RotationMatrix(velocityTheta);
            float facingTheta = (float)(Math.Atan2(-bcc.Controller.HorizontalViewDirection.X, -bcc.Controller.HorizontalViewDirection.Z));

            BEPUutilities.Matrix2x2 facingWorldInv = SpaceUtils.Create2x2RotationMatrix(facingTheta);
            facingWorldInv.Transpose(); // We want the transpose/inverse of the facing transform because we want to transform the movement into 'facing space'.

            return(BepuConverter.Convert(SpaceUtils.TransformVec2(SpaceUtils.TransformVec2(result, velocityWorld), facingWorldInv)));
        }
Exemplo n.º 12
0
        private void DrawShadowMap(ICamera camera, Actor castingActor)
        {
            TransformComponent shadowCasterTransform = castingActor.GetComponent <TransformComponent>(ActorComponent.ComponentType.Transform);
            Matrix             casterView            = Matrix.Invert(shadowCasterTransform.Transform);

            // Find the front half of the frustum corners in world space.
            Matrix invCamFrustum = Matrix.Invert(camera.Frustum.Matrix);

            Vector3[] halfCamCorners = new Vector3[] {
                new Vector3(-1.0f, -1.0f, 0.0f),
                new Vector3(1.0f, -1.0f, 0.0f),
                new Vector3(1.0f, 1.0f, 0.0f),
                new Vector3(-1.0f, 1.0f, 0.0f),
                new Vector3(-1.0f, -1.0f, 1.0f),
                new Vector3(1.0f, -1.0f, 1.0f),
                new Vector3(1.0f, 1.0f, 1.0f),
                new Vector3(-1.0f, 1.0f, 1.0f),
            };

            for (int c = 0; c < 8; ++c)
            {
                Vector4 transformedCorner = Vector4.Transform(halfCamCorners[c], invCamFrustum);
                transformedCorner  /= transformedCorner.W;
                halfCamCorners[c].X = transformedCorner.X;
                halfCamCorners[c].Y = transformedCorner.Y;
                halfCamCorners[c].Z = transformedCorner.Z;
            }

            for (int c = 0; c < 4; ++c)
            {
                halfCamCorners[c + 4] = halfCamCorners[c] + 0.05f * (halfCamCorners[c + 4] - halfCamCorners[c]);
            }

            Vector3[] camCorners = camera.Frustum.GetCorners();

            // Transform those corners into to caster space, and form a bounding box around them, which will become our caster projection.
            Vector3 shadowMapFrustumMinHalfCorner = Vector3.Transform(halfCamCorners[0], casterView);
            Vector3 shadowMapFrustumMaxHalfCorner = shadowMapFrustumMinHalfCorner;
            Vector3 shadowMapFrustumMinCorner     = Vector3.Transform(camCorners[0], casterView);
            Vector3 shadowMapFrustumMaxCorner     = shadowMapFrustumMinHalfCorner;

            for (int fc = 1; fc < 8; ++fc)
            {
                Vector3 currTranslatedCorner = Vector3.Transform(halfCamCorners[fc], casterView);
                shadowMapFrustumMinHalfCorner = Vector3.Min(currTranslatedCorner, shadowMapFrustumMinHalfCorner);
                shadowMapFrustumMaxHalfCorner = Vector3.Max(currTranslatedCorner, shadowMapFrustumMaxHalfCorner);
                currTranslatedCorner          = Vector3.Transform(camCorners[fc], casterView);
                shadowMapFrustumMinCorner     = Vector3.Min(currTranslatedCorner, shadowMapFrustumMinCorner);
                shadowMapFrustumMaxCorner     = Vector3.Max(currTranslatedCorner, shadowMapFrustumMaxCorner);
            }

            Matrix shadowMapProj = SpaceUtils.CreateOrthographicOffCenter(
                shadowMapFrustumMinHalfCorner.X,
                shadowMapFrustumMaxHalfCorner.X,
                shadowMapFrustumMinHalfCorner.Y,
                shadowMapFrustumMaxHalfCorner.Y,
                shadowMapFrustumMaxHalfCorner.Z + 1000.0f,  // Extra room to include shadow casting objects.
                shadowMapFrustumMinHalfCorner.Z);

            SceneGraph.ResetTraversal();
            SceneGraph.VisibilityFrustum = new BoundingFrustum(casterView * shadowMapProj);

            Resources.ShadowTransform        = SceneGraph.VisibilityFrustum.Matrix * sShadowTextureShift;
            SceneGraph.ExternalMaterialFlags = TraversalContext.MaterialFlags.ShadowMap;

            SharedResources.Game.GraphicsDevice.SetRenderTarget(Resources.ShadowMap);
            SharedResources.Game.GraphicsDevice.Clear(Color.White);

            SceneGraph.Draw();
        }