/// <summary> /// Combines an array of GeometryPrimitives into a single primitive. /// The order and all characteristics of the vertices are preserved. /// </summary> /// <param name="primitives"></param> /// <returns></returns> public static GeometryPrimitive Combine(GeometryPrimitive[] primitives) { int numVertices = 0; int numIndices = 0; for (int i = 0; i < primitives.Length; i++) { numVertices += primitives[i].Vertices.Length; numIndices += primitives[i].Indices.Length; } GeometryPrimitive output = new GeometryPrimitive(); output.Vertices = new VertexPositionNormalTexture[numVertices]; output.Indices = new int[numIndices]; int vertIndex = 0; int indIndex = 0; for (int i = 0; i < primitives.Length; i++) { for (int j = 0; j < primitives[i].Vertices.Length; j++) output.Vertices[j + vertIndex] = primitives[i].Vertices[j]; for (int j = 0; j < primitives[i].Indices.Length; j++) output.Indices[j + indIndex] = primitives[i].Indices[j] + vertIndex; vertIndex += primitives[i].Vertices.Length; indIndex += primitives[i].Indices.Length; } return output; }
public static void LoadContent(Game game) { entityPrimitive = GeometryPrimitive.Make3DRectangle( Vector3.One * buffer, Vector3.One * size, Vector2.Zero, new Vector2(1.0f, 1.0f), true, true, true, true, true, true); texture = game.Content.Load<Texture2D>("Textures/Cubes/Dirt"); }
/// <summary> /// Recalculates the Visual geometry for the Chunk. The supplied chunks can be null; /// if not, will be used for visual occlusion (if null, will be assumed see-through). /// </summary> public void RecalculateVisualGeometry() { Chunk leftChunk, rightChunk, forwardChunk, backwardChunk; LoadNeighborChunks(out leftChunk, out rightChunk, out forwardChunk, out backwardChunk); GeometryPrimitive[] newCombinedPrimitives = new GeometryPrimitive[BlockHandler.TotalNumberOfTextures]; int[] newCombinedVerticesCount = new int[BlockHandler.TotalNumberOfTextures]; int[] newCombinedTrianglesCount = new int[BlockHandler.TotalNumberOfTextures]; bool[] newUsesTextureIndex = new bool[BlockHandler.TotalNumberOfTextures]; float visualXMin = GameConstants.CHUNK_X_WIDTH; float visualXMax = 0; float visualYMin = GameConstants.CHUNK_Y_HEIGHT; float visualYMax = 0; float visualZMin = GameConstants.CHUNK_Z_LENGTH; float visualZMax = 0; for (int textureIndex = 0; textureIndex < BlockHandler.TotalNumberOfTextures; textureIndex++) { List<GeometryPrimitive> buildingBlocks = new List<GeometryPrimitive>(); for (int x = 0; x < GameConstants.CHUNK_X_WIDTH; x++) { for (int y = 0; y < GameConstants.CHUNK_Y_HEIGHT; y++) { for (int z = 0; z < GameConstants.CHUNK_Z_LENGTH; z++) { Block relevantBlock = this[x, y, z]; if (!BlockHandler.IsVisible(relevantBlock) || BlockHandler.TextureIndex(relevantBlock) != textureIndex) continue; bool includeFrontFace, includeBackFace; bool includeTopFace, includeBottomFace; bool includeLeftFace, includeRightFace; MakeOcclusionTags(x, y, z, out includeFrontFace, out includeBackFace, out includeTopFace, out includeBottomFace, out includeLeftFace, out includeRightFace, leftChunk, rightChunk, forwardChunk, backwardChunk); bool hasVisibleFace = (includeTopFace || includeBottomFace || includeBackFace || includeFrontFace || includeRightFace || includeLeftFace); if (!hasVisibleFace) continue; visualXMin = MathHelper.Min(visualXMin, x); visualXMax = MathHelper.Max(visualXMax, x + 1); visualYMin = MathHelper.Min(visualYMin, y); visualYMax = MathHelper.Max(visualYMax, y + 1); visualZMin = MathHelper.Min(visualZMin, z); visualZMax = MathHelper.Max(visualZMax, z + 1); GeometryPrimitive drawingPrimitive = BlockHandler.DrawingPrimitive(relevantBlock, includeFrontFace, includeBackFace, includeTopFace, includeBottomFace, includeLeftFace, includeRightFace); if (drawingPrimitive.Vertices.Length > 0) buildingBlocks.Add(drawingPrimitive.Translate(new Vector3(x, y, z))); } } } GeometryPrimitive[] primitivesArray = new GeometryPrimitive[buildingBlocks.Count]; buildingBlocks.CopyTo(primitivesArray); newUsesTextureIndex[textureIndex] = (primitivesArray.Length > 0); if (newUsesTextureIndex[textureIndex]) { newCombinedPrimitives[textureIndex] = GeometryPrimitive.Combine(primitivesArray); } } lock (this) { combinedPrimitives = newCombinedPrimitives; usesTextureIndex = newUsesTextureIndex; visualBoundingBox = new BoundingBox( new Vector3(visualXMin, visualYMin, visualZMin), new Vector3(visualXMax, visualYMax, visualZMax)); } }
/// <summary> /// This constructs a 3D rectangle (interval in 3-space if you prefer) with the /// specified "minimal corner" and size vector. Minimal corner means the point where /// x, y, and z are all lowest, and size should be a strictly positive vector in all /// components. /// /// There are a big pile of optional bools, indicating which (if any) faces to include. /// </summary> /// <param name="minimalCorner">Corner where x, y, and z are minimal.</param> /// <param name="size">Positive vector expressing the x, y, and z size.</param> /// <param name="textureMinimalCorner">Where the relevant part of the texture starts, in UV coordinates.</param> /// <param name="textureSize">How big the relevant part of the texture is, in UV coordinates.</param> /// <returns></returns> public static GeometryPrimitive Make3DRectangle(Vector3 minimalCorner, Vector3 size, Vector2 textureMinimalCorner, Vector2 textureSize, bool includeFrontFace, bool includeBackFace, bool includeTopFace, bool includeBottomFace, bool includeLeftFace, bool includeRightFace) { int faceCount = 0; if (includeFrontFace) faceCount++; if (includeBackFace) faceCount++; if (includeTopFace) faceCount++; if (includeBottomFace) faceCount++; if (includeLeftFace) faceCount++; if (includeRightFace) faceCount++; //physical dimensions float xmin = minimalCorner.X; float xmid = minimalCorner.X + (size.X / 2.0f); float xmax = minimalCorner.X + size.X; float xSize = size.X; float ymin = minimalCorner.Y; float ymid = minimalCorner.Y + (size.Y / 2.0f); float ymax = minimalCorner.Y + size.Y; float ySize = size.Y; float zmin = minimalCorner.Z; float zmid = minimalCorner.Z + (size.Z / 2.0f); float zmax = minimalCorner.Z + size.Z; float zSize = size.Z; //texture dimensions float texIncX = textureSize.X * 0.25f; //texture increment x float texIncY = textureSize.Y * 0.25f; //texture increment y Vector2 faceTextureSize = new Vector2(texIncX, texIncY); //and now for the geometry! GeometryPrimitive[] faces = new GeometryPrimitive[faceCount]; int faceIndex = 0; //Front face if (includeFrontFace) { faces[faceIndex++] = GeometryPrimitive.MakeRectangle( new Vector3(xmid, ymid, zmin), Vector3.Forward, Vector3.Up, new Vector2(texIncX, 2.0f * texIncY) + textureMinimalCorner, faceTextureSize, xSize, ySize); } //Back face if (includeBackFace) { faces[faceIndex++] = GeometryPrimitive.MakeRectangle( new Vector3(xmid, ymid, zmax), Vector3.Backward, Vector3.Down, new Vector2(texIncX, 0) + textureMinimalCorner, faceTextureSize, xSize, ySize); } //top face if (includeTopFace) { faces[faceIndex++] = GeometryPrimitive.MakeRectangle( new Vector3(xmid, ymax, zmid), Vector3.Up, Vector3.Forward, new Vector2(texIncX, texIncY) + textureMinimalCorner, faceTextureSize, xSize, zSize); } //bottom face if (includeBottomFace) { faces[faceIndex++] = GeometryPrimitive.MakeRectangle( new Vector3(xmid, ymin, zmid), Vector3.Down, Vector3.Forward, new Vector2(texIncX, 3.0f * texIncY) + textureMinimalCorner, faceTextureSize, xSize, zSize); } //left face if (includeLeftFace) { faces[faceIndex++] = GeometryPrimitive.MakeRectangle( new Vector3(xmin, ymid, zmid), Vector3.Left, Vector3.Forward, new Vector2(0, texIncY) + textureMinimalCorner, faceTextureSize, ySize, zSize); } //right face if (includeRightFace) { faces[faceIndex++] = GeometryPrimitive.MakeRectangle( new Vector3(xmax, ymid, zmid), Vector3.Right, Vector3.Forward, new Vector2(2.0f * texIncX, texIncY) + textureMinimalCorner, faceTextureSize, ySize, zSize); } return GeometryPrimitive.Combine(faces); }
/// <summary> /// Translates the given GeometryPrimitive by the specified vector. /// </summary> /// <param name="original"></param> /// <param name="translation"></param> /// <returns></returns> public GeometryPrimitive Translate(Vector3 translation) { GeometryPrimitive output = new GeometryPrimitive(); output.Indices = new int[Indices.Length]; output.Vertices = new VertexPositionNormalTexture[Vertices.Length]; for (int i = 0; i < Indices.Length; i++) output.Indices[i] = Indices[i]; for (int i = 0; i < Vertices.Length; i++) { output.Vertices[i] = Vertices[i]; output.Vertices[i].Position += translation; } return output; }
/// <summary> /// Constructs a new GeometryPrimitive from this original and a scaling factor. /// </summary> /// <param name="scalingFactor"></param> /// <returns></returns> public GeometryPrimitive Scale(float scalingFactor) { GeometryPrimitive output = new GeometryPrimitive(); output.Indices = new int[Indices.Length]; output.Vertices = new VertexPositionNormalTexture[Vertices.Length]; for (int i = 0; i < Indices.Length; i++) output.Indices[i] = Indices[i]; for (int i = 0; i < Vertices.Length; i++) { output.Vertices[i] = Vertices[i]; output.Vertices[i].Position *= scalingFactor; } return output; }
/// <summary> /// This is exactly like the Make3DRectangle method, including texture usage, /// except that all the faces are looking IN (as would be expected). So you /// can hang this around the camera and that'll look fine. /// </summary> /// <param name="minimalCorner"></param> /// <param name="size"></param> /// <param name="textureMinimalCorner"></param> /// <param name="textureSize"></param> /// <returns></returns> public static GeometryPrimitive MakeSkybox(Vector3 minimalCorner, Vector3 size, Vector2 textureMinimalCorner, Vector2 textureSize) { //physical dimensions float xmin = minimalCorner.X; float xmid = minimalCorner.X + (size.X / 2.0f); float xmax = minimalCorner.X + size.X; float xSize = size.X; float ymin = minimalCorner.Y; float ymid = minimalCorner.Y + (size.Y / 2.0f); float ymax = minimalCorner.Y + size.Y; float ySize = size.Y; float zmin = minimalCorner.Z; float zmid = minimalCorner.Z + (size.Z / 2.0f); float zmax = minimalCorner.Z + size.Z; float zSize = size.Z; //texture dimensions float texIncX = textureSize.X * 0.25f; //texture increment x float texIncY = textureSize.Y * 0.25f; //texture increment y Vector2 faceTextureSize = new Vector2(texIncX, texIncY); //and now for the geometry! GeometryPrimitive[] faces = new GeometryPrimitive[6]; //Front face faces[0] = GeometryPrimitive.MakeRectangle( new Vector3(xmid, ymid, zmin), Vector3.Backward, Vector3.Up, new Vector2(texIncX, 2.0f * texIncY) + textureMinimalCorner, faceTextureSize, xSize, ySize); //Back face faces[1] = GeometryPrimitive.MakeRectangle( new Vector3(xmid, ymid, zmax), Vector3.Forward, Vector3.Down, new Vector2(texIncX, 0) + textureMinimalCorner, faceTextureSize, xSize, ySize); //top face faces[2] = GeometryPrimitive.MakeRectangle( new Vector3(xmid, ymax, zmid), Vector3.Down, Vector3.Backward, new Vector2(texIncX, texIncY) + textureMinimalCorner, faceTextureSize, xSize, zSize); //bottom face faces[3] = GeometryPrimitive.MakeRectangle( new Vector3(xmid, ymin, zmid), Vector3.Up, Vector3.Forward, new Vector2(texIncX, 3.0f * texIncY) + textureMinimalCorner, faceTextureSize, xSize, zSize); //left face faces[4] = GeometryPrimitive.MakeRectangle( new Vector3(xmin, ymid, zmid), Vector3.Right, Vector3.Backward, new Vector2(0, texIncY) + textureMinimalCorner, faceTextureSize, zSize, ySize); //right face faces[5] = GeometryPrimitive.MakeRectangle( new Vector3(xmax, ymid, zmid), Vector3.Left, Vector3.Backward, new Vector2(2.0f * texIncX, texIncY) + textureMinimalCorner, faceTextureSize, zSize, ySize); return GeometryPrimitive.Combine(faces); }
/// <summary> /// Construct a rectangle at the specified location, which displays a /// portion of a texture. /// </summary> /// <param name="center">The center of the rectangle.</param> /// <param name="normal">UNIT VECTOR facing directly out of the rectangle. /// If you're facing the rectangle (and you want it to be visible) /// then this should be pointing at you.</param> /// <param name="up">UNIT VECTOR pointing in the direction that should be /// "up" on the texture. Messing this up may flip the texture.</param> /// <param name="textureCornerUL">The "upper left corner" of the relevant /// part of the texture in UV coordinates.</param> /// <param name="textureSize">The "size" of the relevant part of the /// texture in UV coordinates. For clarity, note that the lower left /// corner should be textureCornerUL + size.</param> /// <param name="width">The width of the rectangle.</param> /// <param name="height">The height of the rectangle.</param> /// <returns></returns> public static GeometryPrimitive MakeRectangle( Vector3 center, Vector3 normal, Vector3 up, Vector2 textureCornerUL, Vector2 textureSize, float width, float height) { GeometryPrimitive quad = new GeometryPrimitive(); Vector3 left = Vector3.Cross(normal, up); Vector3 upperCenter = center + up * (height / 2.0f); Vector3 upperLeft = upperCenter + left * (width / 2.0f); Vector3 upperRight = upperCenter - left * (width / 2.0f); Vector3 lowerLeft = upperLeft - up * height; Vector3 lowerRight = upperRight - up * height; Vector2 textureUL = new Vector2(textureCornerUL.X, textureCornerUL.Y); Vector2 textureUR = new Vector2(textureCornerUL.X + textureSize.X, textureCornerUL.Y); Vector2 textureDL = new Vector2(textureCornerUL.X, textureCornerUL.Y + textureSize.Y); Vector2 textureDR = new Vector2(textureCornerUL.X + textureSize.X, textureCornerUL.Y + textureSize.Y); //with all those preliminaries set up, just define the corners! quad.Vertices = new VertexPositionNormalTexture[4]; quad.Vertices[0] = new VertexPositionNormalTexture(lowerLeft, normal, textureDL); quad.Vertices[1] = new VertexPositionNormalTexture(upperLeft, normal, textureUL); quad.Vertices[2] = new VertexPositionNormalTexture(lowerRight, normal, textureDR); quad.Vertices[3] = new VertexPositionNormalTexture(upperRight, normal, textureUR); quad.Indices = new int[] { 0, 1, 2, 2, 1, 3 }; return quad; }