public ModelNode( ModelManager.ModelData model) : base() { this.model = model; this.LocalBounds = model.BoundingSphere; }
/// <summary> /// Tests if a sphere intersects DFMesh geometry. /// </summary> /// <param name="position">Position in world space.</param> /// <param name="radius">Radius of sphere.</param> /// <param name="modelTransform">World space transform.</param> /// <param name="model">ModelManager.ModelData.</param> /// <returns>CollisionResult.</returns> public CollisionResult SphereIntersectDFMesh(Vector3 position, float radius, Matrix modelTransform, ModelManager.ModelData model) { // Transform sphere position back to object space Matrix inverseTransform = Matrix.Invert(modelTransform); position = Vector3.Transform(position, inverseTransform); // Test each submesh CollisionResult result = CollisionResult.Nothing; Vector3 normal, vertex1, vertex2, vertex3; int subMeshIndex = 0; foreach (var subMesh in model.DFMesh.SubMeshes) { // Test each plane of this submesh int planeIndex = 0; foreach (var plane in subMesh.Planes) { // Get plane normal (all points in plane have same normal) normal.X = plane.Points[0].NX; normal.Y = -plane.Points[0].NY; normal.Z = -plane.Points[0].NZ; // Get shared point (vertex1) vertex1.X = plane.Points[0].X; vertex1.Y = -plane.Points[0].Y; vertex1.Z = -plane.Points[0].Z; // Walk through triangle fan for (int p = 0; p < plane.Points.Length - 2; p++) { // Get second point (vertex2) vertex2.X = plane.Points[p + 1].X; vertex2.Y = -plane.Points[p + 1].Y; vertex2.Z = -plane.Points[p + 1].Z; // Get third point (vertex3) vertex3.X = plane.Points[p + 2].X; vertex3.Y = -plane.Points[p + 2].Y; vertex3.Z = -plane.Points[p + 2].Z; // Test intersection. // Changing winding order to suit algorithm. SetCollisionTriangle(vertex1, vertex3, vertex2, normal); TestSphereCollision(ref position, radius, ref result); } // Increment planeIndex planeIndex++; } // Increment subMeshIndex subMeshIndex++; } return(result); }
/// <summary> /// Tests pointer against model intersections to /// resolve actual model intersection at face level. /// </summary> private void PointerModelIntersectionsTest() { // Nothing to do if no intersections if (pointerNodeIntersections.Count == 0) { return; } // Sort intersections by distance pointerNodeIntersections.Sort(); // Iterate intersections float?intersection = null; float?closestIntersection = null; Intersection.NodeIntersection closestModelIntersection = null; foreach (var ni in pointerNodeIntersections) { // Ensure node is a ModelNode if (false == (ni.Node is ModelNode)) { continue; } // Get model ModelNode node = (ModelNode)ni.Node; ModelManager.ModelData model = node.Model; // Test model bool insideBoundingSphere; int subMeshResult, planeResult; intersection = Intersection.RayIntersectsDFMesh( pointerRay, node.Matrix, ref model, out insideBoundingSphere, out subMeshResult, out planeResult); if (intersection != null) { if (closestIntersection == null || intersection < closestIntersection) { closestIntersection = intersection; closestModelIntersection = ni; } } } // Store closest intersection if (closestModelIntersection != null) { pointerOverModelNode = (ModelNode)closestModelIntersection.Node; } else { pointerOverModelNode = null; } }
/// <summary> /// Highlights an action chain. /// </summary> /// <param name="start">Start node.</param> private void HighlightActionChain(ModelNode start) { // Link back to start node if there is a parent while (start.Action.PreviousNode != null) { start = (ModelNode)start.Action.PreviousNode; } // Link through chain from start int lineCount = 0; ModelNode node = start; while (node != null) { // Highlight model ModelManager.ModelData model = node.Model; DrawNativeMesh(actionHighlightColor, ref model, node.Matrix); // Get line start actionLines[lineCount].Color = Color.Red; actionLines[lineCount++].Position = node.TransformedBounds.Center; // Get next node node = (ModelNode)node.Action.NextNode; // Store end point of line if there is another node in chain if (node != null) { actionLines[lineCount].Color = Color.Red; actionLines[lineCount++].Position = node.TransformedBounds.Center; } else { lineCount--; } } // Draw action connection lines if (lineCount >= 2) { // Set render states graphicsDevice.DepthStencilState = DepthStencilState.None; // Set view and projection matrices lineEffect.View = camera.View; lineEffect.Projection = camera.Projection; lineEffect.World = Matrix.Identity; // Draw lines lineEffect.CurrentTechnique.Passes[0].Apply(); graphicsDevice.DrawUserPrimitives(PrimitiveType.LineList, actionLines, 0, lineCount / 2); } }
/// <summary> /// Highlights model under pointer. /// </summary> private void HighlightModelUnderPointer() { if (pointerOverModelNode != null) { if (pointerOverModelNode.Action.Enabled) { // Handle action-enabled nodes HighlightActionChain(pointerOverModelNode); } else { // Just highlight model ModelManager.ModelData model = pointerOverModelNode.Model; DrawNativeMesh(modelHighlightColor, ref model, pointerOverModelNode.Matrix); } } }
/// <summary> /// Draw native mesh as wireframe lines. /// </summary> /// <param name="color">Line color.</param> /// <param name="model">ModelManager.Model.</param> /// <param name="matrix">Matrix.</param> protected void DrawNativeMesh(Color color, ref ModelManager.ModelData model, Matrix matrix) { // Scale up just a little to make outline visually pop matrix = Matrix.CreateScale(1.015f) * matrix; // Set view and projection matrices lineEffect.View = camera.View; lineEffect.Projection = camera.Projection; lineEffect.World = matrix; // Draw faces foreach (var subMesh in model.DFMesh.SubMeshes) { foreach (var plane in subMesh.Planes) { DrawNativeFace(color, plane.Points, matrix); } } }
/// <summary> /// Loads a Daggerfall model and any textures required. /// Will use whatever climate is currently set in texture manager. /// </summary> /// <param name="id">ID of model.</param> /// <param name="model">ModelManager.ModelData.</param> /// <returns>True if successful.</returns> private bool LoadDaggerfallModel(uint id, out ModelManager.ModelData model) { try { // Load model and textures model = modelManager.GetModelData(id); for (int i = 0; i < model.SubMeshes.Length; i++) { // Set flags TextureManager.TextureCreateFlags flags = TextureManager.TextureCreateFlags.ApplyClimate | TextureManager.TextureCreateFlags.MipMaps | TextureManager.TextureCreateFlags.PowerOfTwo; // Set HiDef flags if (Core.GraphicsProfile == GraphicsProfile.HiDef) { flags |= TextureManager.TextureCreateFlags.ExtendedAlpha; } // Load texture model.SubMeshes[i].TextureKey = textureManager.LoadTexture( model.DFMesh.SubMeshes[i].TextureArchive, model.DFMesh.SubMeshes[i].TextureRecord, flags); } } catch (Exception e) { Console.WriteLine(e.Message); model = null; return(false); } return(true); }
// Constructors public ModelNode() : base() { this.model = null; }
/// <summary> /// Tests if ray intersects DFMesh geometry. Performs bounding sphere /// test first. Use this method for model picking from an /// unprojected ray. /// </summary> /// <param name="ray">Ray in world space.</param> /// <param name="modelTransform">World space transform.</param> /// <param name="model">ModelManager.Model.</param> /// <param name="insideBoundingSphere">True if ray intersects model bounding sphere.</param> /// <param name="subMeshResult">Index of DFSubMesh intersected by ray, or -1 on miss.</param> /// <param name="planeResult">Index of DFPlane interested by ray, or -1 on miss.</param> /// <returns>Distance to intersection, or NULL if miss.</returns> public static float?RayIntersectsDFMesh(Ray ray, Matrix modelTransform, ref ModelManager.ModelData model, out bool insideBoundingSphere, out int subMeshResult, out int planeResult) { // Reset results insideBoundingSphere = false; subMeshResult = -1; planeResult = -1; // Transform ray back to object space Matrix inverseTransform = Matrix.Invert(modelTransform); ray.Position = Vector3.Transform(ray.Position, inverseTransform); ray.Direction = Vector3.TransformNormal(ray.Direction, inverseTransform); // Test against bounding sphere if (ray.Intersects(model.BoundingSphere) == null) { // No intersection possible return(null); } else { // Ray is inside model bounding sphere insideBoundingSphere = true; } // Test each submesh float dot; float? intersection = null; float? closestIntersection = null; Vector3 normal, vertex1, vertex2, vertex3; int subMeshIndex = 0; foreach (var subMesh in model.DFMesh.SubMeshes) { // Test each plane of this submesh int planeIndex = 0; foreach (var plane in subMesh.Planes) { // Get plane normal (all points in plane have same normal) normal.X = plane.Points[0].NX; normal.Y = -plane.Points[0].NY; normal.Z = -plane.Points[0].NZ; // Cull planes facing away from ray dot = Vector3.Dot(normal, ray.Direction); if (dot > 0f) { planeIndex++; continue; } // Get shared point (vertex1) vertex1.X = plane.Points[0].X; vertex1.Y = -plane.Points[0].Y; vertex1.Z = -plane.Points[0].Z; // Walk through triangle fan for (int p = 0; p < plane.Points.Length - 2; p++) { // Get second point (vertex2) vertex2.X = plane.Points[p + 1].X; vertex2.Y = -plane.Points[p + 1].Y; vertex2.Z = -plane.Points[p + 1].Z; // Get third point (vertex3) vertex3.X = plane.Points[p + 2].X; vertex3.Y = -plane.Points[p + 2].Y; vertex3.Z = -plane.Points[p + 2].Z; // Test intersection RayIntersectsTriangle(ref ray, ref vertex1, ref vertex2, ref vertex3, out intersection); if (intersection != null) { // Test for closest intersection so far if (closestIntersection == null || intersection < closestIntersection) { // Update closest intersection closestIntersection = intersection; subMeshResult = subMeshIndex; planeResult = planeIndex; } } } // Increment planeIndex planeIndex++; } // Increment subMeshIndex subMeshIndex++; } return(closestIntersection); }