/// <summary> /// Update node. /// This method requires some clean-up and optimisation. /// Currently rebuilding matrices on every update whether node /// has changed or not. This is done for simplicity and will /// be improved later. /// </summary> /// <param name="node">SceneNode.</param> /// <param name="matrix">Cumulative Matrix.</param> /// <returns>Transformed and merged BoundingSphere.</returns> /// <param name="elapsedTime">Elapsed time since last frame.</param> private BoundingSphere UpdateNode(SceneNode node, Matrix matrix, TimeSpan elapsedTime) { Matrix cumulativeMatrix = Matrix.Identity; // Create node transforms Matrix rotationX = Matrix.CreateRotationX(node.Rotation.X); Matrix rotationY = Matrix.CreateRotationY(node.Rotation.Y); Matrix rotationZ = Matrix.CreateRotationZ(node.Rotation.Z); Matrix translation = Matrix.CreateTranslation(node.Position); // Create action transforms Matrix actionTranslation = Matrix.Identity; if (node.Action.Enabled) { // Progress actions if (node.Action.ActionState == SceneNode.ActionState.RunningForwards) { // Progress action node.Action.RunTime += elapsedTime.Milliseconds; if (node.Action.RunTime >= node.Action.Duration) { node.Action.RunTime = node.Action.Duration; node.Action.ActionState = SceneNode.ActionState.End; } } else if (node.Action.ActionState == SceneNode.ActionState.RunningBackwards) { // Progress action node.Action.RunTime -= elapsedTime.Milliseconds; if (node.Action.RunTime <= 0) { node.Action.RunTime = 0; node.Action.ActionState = SceneNode.ActionState.Start; } } float scale = (float)node.Action.RunTime / (float)node.Action.Duration; float xrot = node.Action.Rotation.X * scale; float yrot = node.Action.Rotation.Y * scale; float zrot = node.Action.Rotation.Z * scale; float xtrn = node.Action.Translation.X * scale; float ytrn = node.Action.Translation.Y * scale; float ztrn = node.Action.Translation.Z * scale; // Create action transforms Matrix actionRotationX = Matrix.CreateRotationX(xrot); Matrix actionRotationY = Matrix.CreateRotationY(yrot); Matrix actionRotationZ = Matrix.CreateRotationZ(zrot); actionTranslation = Matrix.CreateTranslation(xtrn, ytrn, ztrn); // Apply action transforms Matrix.Multiply(ref cumulativeMatrix, ref actionRotationY, out cumulativeMatrix); Matrix.Multiply(ref cumulativeMatrix, ref actionRotationX, out cumulativeMatrix); Matrix.Multiply(ref cumulativeMatrix, ref actionRotationZ, out cumulativeMatrix); } // Apply node transforms. // Rotation order is Y*X*Z which seems to be correct in all observed cases. Matrix.Multiply(ref cumulativeMatrix, ref rotationY, out cumulativeMatrix); Matrix.Multiply(ref cumulativeMatrix, ref rotationX, out cumulativeMatrix); Matrix.Multiply(ref cumulativeMatrix, ref rotationZ, out cumulativeMatrix); Matrix.Multiply(ref cumulativeMatrix, ref actionTranslation, out cumulativeMatrix); Matrix.Multiply(ref cumulativeMatrix, ref translation, out cumulativeMatrix); Matrix.Multiply(ref cumulativeMatrix, ref matrix, out cumulativeMatrix); // Transform bounds BoundingSphere bounds = node.LocalBounds; Vector3.Transform(ref bounds.Center, ref cumulativeMatrix, out bounds.Center); // Update child nodes foreach (SceneNode child in node.Children) { bounds = BoundingSphere.CreateMerged( bounds, UpdateNode(child, cumulativeMatrix, elapsedTime)); } // Animate a light node if (node is PointLightNode) { PointLightNode pointLightNode = (PointLightNode)node; pointLightNode.AnimTimer += elapsedTime.Milliseconds; if (pointLightNode.AnimTimer > 60) { pointLightNode.AnimTimer = 0; pointLightNode.AnimScale = MathHelper.Clamp( pointLightNode.AnimScale + (float)(rnd.NextDouble() - 0.5f) * 0.15f, 0.70f, 1.0f); } } // Calculate point lighting on a billboard node. // The light and billboard must have the same parent or // they will not be compared. // TODO: Find a better way to do this. if (node is BillboardNode) { BillboardNode billboardNode = (BillboardNode)node; billboardNode.LightIntensity = 0f; foreach (SceneNode child in node.Parent.Children) { if (child is PointLightNode) { PointLightNode pointLightNode = (PointLightNode)child; if (pointLightNode.TransformedBounds.Intersects(billboardNode.TransformedBounds)) { float distance = Vector3.Distance( pointLightNode.TransformedBounds.Center, billboardNode.TransformedBounds.Center); float attenuation = MathHelper.Clamp( 1.0f - distance / pointLightNode.Radius, 0, 1); billboardNode.LightIntensity = MathHelper.Clamp( billboardNode.LightIntensity + attenuation, 0, 1); } } } } // Store transformed bounds node.TransformedBounds = bounds; // Store cumulative matrix node.Matrix = cumulativeMatrix; return(bounds); }
/// <summary> /// Reset entire scene back to empty root node. /// </summary> public void ResetScene() { root = new SceneNode(); root.DrawBoundsColor = defaultRootBoundsColor; }
// Constructors public NodeIntersection() { this.distance = null; this.node = null; }