/// <summary> /// Checks if a ray intersects the bounding sphere of a model. /// </summary> private bool RayIntersectsModel(Ray ray, Meteor.Resources.Model instancedModel, out Matrix instanceMatrix, out BoundingSphere bSphere) { foreach (MeshInstanceGroup instanceGroup in instancedModel.MeshInstanceGroups.Values) { foreach (MeshInstance meshInstance in instanceGroup.visibleInstances) { if (meshInstance == null) { continue; } // Sphere collision test in world space. if (meshInstance.boundingSphere.Intersects(ray) != null) { instanceMatrix = meshInstance.Transform; bSphere = meshInstance.boundingSphere; return(true); } } } // No collision was found instanceMatrix = Matrix.Identity; bSphere = new BoundingSphere(); return(false); }
/// <summary> /// Draw instanced model with a custom effect without camera parameters /// </summary> private int DrawModel(Meteor.Resources.Model model, Effect effect, String technique) { if (model.animationPlayer != null) { String animatedTechnique = technique += "Animated"; UseTechnique(animatedTechnique); TrimBoneTransforms(model); // Set bones if the model is animated currentEffect.Parameters["bones"].SetValue(tempBones); } else { UseTechnique(technique); } int meshIndex = 0; int visibleInstances = 0; foreach (MeshInstanceGroup instanceGroup in model.MeshInstanceGroups.Values) { // Create vertex buffer if necessary model.BuildMeshData(graphicsDevice, instanceGroup); // Retrieve the current mesh from the mesh list ModelMesh mesh = model.modelMeshes[meshIndex]; Matrix world = model.World * model.boneMatrices[mesh.ParentBone.Index]; // Set world matrix for all mesh parts currentEffect.Parameters["World"].SetValue(world); foreach (ModelMeshPart meshPart in mesh.MeshParts) { Material meshMaterial = model.materials[meshIndex]; Texture2D textureValue = (meshMaterial.textures["Texture"] != null) ? meshMaterial.textures["Texture"] : blankTexture; // Assign all proper textures to the effect foreach (EffectParameter effectParam in currentEffect.Parameters) { if (meshMaterial.textures.ContainsKey(effectParam.Name)) { effectParam.SetValue(meshMaterial.textures[effectParam.Name]); } } DrawInstancedMeshPart(meshPart, instanceGroup); meshIndex++; } // Finished drawing mesh parts visibleInstances += instanceGroup.totalVisible; } // Finished model rendering return(visibleInstances); }
/// <summary> /// A bit hacky way to limit bone transforms. Will fix soon. /// </summary> private void TrimBoneTransforms(Meteor.Resources.Model instancedModel) { if (instancedModel.animationPlayer != null) { Matrix[] bones = instancedModel.animationPlayer.GetSkinTransforms(); int maxBones = (bones.Count() > 60) ? 60 : bones.Count(); // Not very efficient to do, need a way to avoid copying arrays Array.Copy(bones, tempBones, maxBones); } }
private void DrawBoundingBoxes(Meteor.Resources.Model model, Camera camera) { int meshIndex = 0; Viewport v = graphicsDevice.Viewport; float farDistance = camera.farPlaneDistance; // Matrices to project into screen space Matrix worldViewProjection = camera.view * camera.projection; Matrix invClient = Matrix.Invert(Matrix.CreateOrthographicOffCenter(0, v.Width, v.Height, 0, -1, 1)); foreach (MeshInstanceGroup instancedGroup in model.MeshInstanceGroups.Values) { BoundingBox box = instancedGroup.boundingBox; // Assign the box corners boxCorners[0] = new Vector3(box.Min.X, box.Max.Y, box.Max.Z); boxCorners[1] = new Vector3(box.Max.X, box.Max.Y, box.Max.Z); // maximum boxCorners[2] = new Vector3(box.Max.X, box.Min.Y, box.Max.Z); boxCorners[3] = new Vector3(box.Min.X, box.Min.Y, box.Max.Z); boxCorners[4] = new Vector3(box.Min.X, box.Max.Y, box.Min.Z); boxCorners[5] = new Vector3(box.Max.X, box.Max.Y, box.Min.Z); boxCorners[6] = new Vector3(box.Max.X, box.Min.Y, box.Min.Z); boxCorners[7] = new Vector3(box.Min.X, box.Min.Y, box.Min.Z); // minimum //Color[] colors = { Color.Cyan, Color.White, Color.Magenta, Color.Blue, // Color.Green, Color.Yellow, Color.Red, Color.Black };XZ spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend); int visible = 0; foreach (MeshInstance meshInstance in instancedGroup.visibleInstances) { if (meshInstance == null) { continue; } // If we have exceeded the visible count, we have already rendered // all visible instances if (visible >= instancedGroup.totalVisible) { break; } Matrix modelTransform = meshInstance.Transform; Vector2 rectMin = new Vector2(v.Width, v.Height); Vector2 rectMax = Vector2.Zero; float minDistance = camera.farPlaneDistance; for (int i = 0; i < boxCorners.Length; i++) { model.boxVertices[i].Position = Vector3.Transform(boxCorners[i], modelTransform); model.boxVertices[i].Color = Color.Cyan; // Determin the closest distance point in the bounding box float camDistance = Vector3.Distance(model.boxVertices[i].Position, camera.position); minDistance = Math.Min(camDistance, minDistance); // Project the corners of the bounding box onto screen space Vector4 position = new Vector4(model.boxVertices[i].Position, 1); Vector4.Transform(ref position, ref worldViewProjection, out position); position /= position.W; Vector2 clientResult = Vector2.Transform(new Vector2(position.X, position.Y), invClient); rectMin.X = (int)Math.Min((float)clientResult.X, (float)rectMin.X); rectMin.Y = (int)Math.Min((float)clientResult.Y, (float)rectMin.Y); rectMax.X = (int)Math.Max((float)clientResult.X, (float)rectMax.X); rectMax.Y = (int)Math.Max((float)clientResult.Y, (float)rectMax.Y); } Vector3 topLeft = v.Unproject(new Vector3(rectMin, minDistance), camera.projection, camera.view, camera.worldMatrix); Vector3 bottomRight = v.Unproject(new Vector3(rectMax, minDistance), camera.projection, camera.view, camera.worldMatrix); // Transform the temporary bounding boxes with the model instance's world matrix // TODO: Update these boxes only when intances are updated // Render the bounding box for this instance if (camera.frustum.Contains(meshInstance.boundingSphere) != ContainmentType.Disjoint) { // Add a bounding sphere to the list of shapes to draw //ShapeRenderer.AddBoundingSphere(meshInstance.BSphere, Color.Red); for (int i = 0; i < basicEffect.CurrentTechnique.Passes.Count; i++) { basicEffect.CurrentTechnique.Passes[i].Apply(); graphicsDevice.DrawUserIndexedPrimitives <VertexPositionColor>( PrimitiveType.LineList, model.boxVertices, 0, 8, Meteor.Resources.Model.bBoxIndices, 0, 12); } } // Render our shapes now //ShapeRenderer.Draw(camera.View, camera.Projection); // Add to the total visible visible++; } spriteBatch.End(); meshIndex++; } // End box rendering }