public void DrawMeshDebugLines(BaseMesh mesh, Transform transform, List<Vector3> overrideVertices, List<Vector3> overrideNormals, Render render) { if(render.drawNormals) { SetColor(0.8f, 0.0f, 0.9f); if(overrideVertices.Any()) { for (var i = 0; i < mesh.Vertices.Count; i++) { var ov = overrideVertices[i]; var on = overrideNormals[i]; var v = transform * ov; buffer.Add(v.ToArray()); buffer.Add(color); v += transform.Basis.MultiplyByVector(on) * 128; buffer.Add(v.ToArray()); buffer.Add(color); } } else { foreach (var mv in mesh.Vertices) { var v = transform * mv.Position; buffer.Add(v.ToArray()); buffer.Add(color); v += transform.Basis.MultiplyByVector(mv.Normal) * 128; buffer.Add(v.ToArray()); buffer.Add(color); } } } }
public static void tr_setupTexturedFace(Mesh trMesh, BaseMesh mesh, ushort[] vertexIndices, Polygon p) { if (trMesh.Lights.Length == trMesh.Vertices.Length) { for (var i = 0; i < p.Vertices.Count; i++) { p.Vertices[i].Color[0] = 1.0f - trMesh.Lights[vertexIndices[i]] / 8192.0f; p.Vertices[i].Color[1] = 1.0f - trMesh.Lights[vertexIndices[i]] / 8192.0f; p.Vertices[i].Color[2] = 1.0f - trMesh.Lights[vertexIndices[i]] / 8192.0f; p.Vertices[i].Color[3] = 1.0f; } } else { foreach (var v in p.Vertices) { v.Color = new float[] {1, 1, 1, 1}; } } }
public void RenderMesh(BaseMesh mesh) { if(mesh.AllAnimatedElements.Any()) { // Respecify the tex coord buffer GL.BindBuffer(BufferTarget.ArrayBuffer, mesh.AnimatedVBOTexCoordArray); // Tell OpenGL to discard the old values GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(mesh.AnimatedVertices.Count * 2 * sizeof(float)), IntPtr.Zero, BufferUsageHint.StreamDraw); unsafe { // Get writable data (to avoid copy) var data = (float*) GL.MapBuffer(BufferTarget.ArrayBuffer, BufferAccess.WriteOnly); var offset = 0; foreach (var p in mesh.Polygons) { if(p.AnimID == 0 || p.IsBroken) { continue; } var seq = EngineWorld.AnimSequences[p.AnimID - 1]; var frame = (seq.CurrentFrame + p.FrameOffset) % seq.Frames.Count; var tf = seq.Frames[frame]; foreach (var vert in p.Vertices) { var v = vert.TexCoord; data[offset + 0] = tf.Mat[0 + 0 * 2] * v[0] + tf.Mat[0 + 1 * 2] * v[1] + tf.Move[0]; data[offset + 1] = tf.Mat[1 + 0 * 2] * v[0] + tf.Mat[1 + 1 * 2] * v[1] + tf.Move[1]; offset += 2; } } GL.UnmapBuffer(BufferTarget.ArrayBuffer); } if(mesh.AnimatedElementCount > 0) { mesh.AnimatedVertexArray.Bind(); GL.BindTexture(TextureTarget.Texture2D, World.Textures[0]); GL.DrawElements(PrimitiveType.Triangles, (int)mesh.AnimatedElementCount, DrawElementsType.UnsignedInt, IntPtr.Zero); } } if(mesh.Vertices.Any()) { mesh.MainVertexArray.Bind(); var elementsbase = IntPtr.Zero; var offset = 0; for (var texture = 0; texture < mesh.TexturePageCount; texture++) { if(mesh.ElementsPerTexture[texture] == 0) { continue; } GL.BindTexture(TextureTarget.Texture2D, World.Textures[texture]); mesh.MainVertexArray.Bind(); GL.DrawElements(PrimitiveType.Triangles, (int)mesh.ElementsPerTexture[texture], DrawElementsType.UnsignedInt, elementsbase + offset); offset += (int)mesh.ElementsPerTexture[texture]; } } }
public static void tr_accumulateNormals(Mesh trMesh, BaseMesh mesh, int numCorners, ushort[] vertexIndices, Polygon p) { p.Vertices.Resize(numCorners, () => new Vertex()); for (var i = 0; i < numCorners; i++) { p.Vertices[i].Position = trMesh.Vertices[vertexIndices[i]].ToVector3(); } p.FindNormal(); for (var i = 0; i < numCorners; i++) { mesh.Vertices[vertexIndices[i]].Normal += p.Plane.Normal; } }
public static void tr_setupColoredFace(Mesh trMesh, Level tr, BaseMesh mesh, ushort[] vertexIndices, int color, Polygon p) { var tmp = trMesh.Lights.Length == trMesh.Vertices.Length; for (var i = 0; i < p.Vertices.Count; i++) { p.Vertices[i].Color[0] = tr.Palette.Colour[color].R / 255.0f; p.Vertices[i].Color[1] = tr.Palette.Colour[color].G / 255.0f; p.Vertices[i].Color[2] = tr.Palette.Colour[color].B / 255.0f; if(tmp) { p.Vertices[i].Color[0] = p.Vertices[i].Color[0] * 1.0f - trMesh.Lights[vertexIndices[i]] / 8192.0f; p.Vertices[i].Color[1] = p.Vertices[i].Color[1] * 1.0f - trMesh.Lights[vertexIndices[i]] / 8192.0f; p.Vertices[i].Color[2] = p.Vertices[i].Color[2] * 1.0f - trMesh.Lights[vertexIndices[i]] / 8192.0f; } p.Vertices[i].Color[3] = 1.0f; p.Vertices[i].TexCoord[0] = (i & 2) == 2 ? 1.0f : 0.0f; p.Vertices[i].TexCoord[1] = i >= 2 ? 1.0f : 0.0f; } mesh.UsesVertexColors = true; }
public static void tr_setupRoomVertices(World world, Level tr, Loader.Room tr_room, BaseMesh mesh, int numCorners, ushort[] vertices, ushort masked_texture, Polygon p) { p.Vertices.Resize(numCorners, () => new Vertex()); for (var i = 0; i < numCorners; i++) { p.Vertices[i].Position = tr_room.Vertices[vertices[i]].Vertex.ToVector3(); } p.FindNormal(); for (var i = 0; i < numCorners; i++) { mesh.Vertices[vertices[i]].Normal += p.Plane.Normal; p.Vertices[i].Normal = p.Plane.Normal; p.Vertices[i].Color = TR_color_to_arr(tr_room.Vertices[vertices[i]].Color); } var tex = tr.ObjectTextures[masked_texture]; SetAnimTexture(p, masked_texture, world); p.BlendMode = tex.TransparencyFlags; world.TextureAtlas.GetCoordinates(masked_texture, false, p); }
public static void tr_copyNormals(Polygon polygon, BaseMesh mesh, ushort[] mesh_vertex_indices) { for (var i = 0; i < polygon.Vertices.Count; i++) { polygon.Vertices[i].Normal = mesh.Vertices[mesh_vertex_indices[i]].Normal; } }
public static void TR_GenMesh(World world, int mesh_index, BaseMesh mesh, Level tr) { var texMask = world.EngineVersion == Loader.Engine.TR4 ? TextureIndexMaskTr4 : TextureIndexMask; /* TR WAD FORMAT DOCUMENTATION! * tr4_face[3,4]_t: * flipped texture & 0x8000 (1 bit ) - horizontal flipping. * shape texture & 0x7000 (3 bits ) - texture sample shape. * index texture & $0FFF (12 bits) - texture sample index. * * if bit [15] is set, as in ( texture and $8000 ), it indicates that the texture * sample must be flipped horizontally prior to be used. * Bits [14..12] as in ( texture and $7000 ), are used to store the texture * shape, given by: ( texture and $7000 ) shr 12. * The valid values are: 0, 2, 4, 6, 7, as assigned to a square starting from * the top-left corner and going clockwise: 0, 2, 4, 6 represent the positions * of the square angle of the triangles, 7 represents a quad. */ var trMesh = tr.Meshes[mesh_index]; mesh.ID = (uint)mesh_index; mesh.Center.X = trMesh.Centre.X; mesh.Center.Y = trMesh.Centre.Z; mesh.Center.Z = trMesh.Centre.Y; mesh.Radius = trMesh.CollisionSize; mesh.TexturePageCount = world.TextureAtlas.NumAtlasPages + 1; mesh.Vertices.Resize(trMesh.Vertices.Length, () => new Vertex()); for (var i = 0; i < mesh.Vertices.Count; i++) { mesh.Vertices[i].Position = trMesh.Vertices[i].ToVector3(); mesh.Vertices[i].Normal = Vector3.Zero; // paranoid } mesh.FindBB(); mesh.Polygons.Clear(); // textured triangles foreach (var face3 in trMesh.TexturedTriangles) { var p = new Polygon(); var tex = tr.ObjectTextures[face3.Texture & texMask]; p.DoubleSide = face3.Texture >> 15 != 0; // CORRECT, BUT WRONG IN TR3-5 SetAnimTexture(p, (uint)face3.Texture & texMask, world); p.BlendMode = face3.Lighting.HasFlagUns(0x01) ? BlendingMode.Multiply : tex.TransparencyFlags; tr_accumulateNormals(trMesh, mesh, 3, face3.Vertices, p); tr_setupTexturedFace(trMesh, mesh, face3.Vertices, p); world.TextureAtlas.GetCoordinates((uint) face3.Texture & texMask, false, p); mesh.Polygons.Add(p); } // coloured triangles foreach (var face3 in trMesh.ColouredTriangles) { var p = new Polygon(); var col = face3.Texture & 0xff; p.TexIndex = (ushort)world.TextureAtlas.NumAtlasPages; p.BlendMode = BlendingMode.Opaque; p.AnimID = 0; tr_accumulateNormals(trMesh, mesh, 3, face3.Vertices, p); tr_setupColoredFace(trMesh, tr, mesh, face3.Vertices, col, p); mesh.Polygons.Add(p); } // textured rectangles foreach (var face4 in trMesh.TexturedRectangles) { var p = new Polygon(); var tex = tr.ObjectTextures[face4.Texture & texMask]; p.DoubleSide = face4.Texture >> 15 != 0; // CORRECT, BUT WRONG IN TR3-5 SetAnimTexture(p, (uint)face4.Texture & texMask, world); p.BlendMode = face4.Lighting.HasFlagUns(0x01) ? BlendingMode.Multiply : tex.TransparencyFlags; tr_accumulateNormals(trMesh, mesh, 4, face4.Vertices, p); tr_setupTexturedFace(trMesh, mesh, face4.Vertices, p); world.TextureAtlas.GetCoordinates((uint)face4.Texture & texMask, false, p); mesh.Polygons.Add(p); } // coloured rectangles foreach (var face4 in trMesh.ColouredRectangles) { var p = new Polygon(); var col = face4.Texture & 0xff; p.TexIndex = (ushort)world.TextureAtlas.NumAtlasPages; p.BlendMode = BlendingMode.Opaque; p.AnimID = 0; tr_accumulateNormals(trMesh, mesh, 4, face4.Vertices, p); tr_setupColoredFace(trMesh, tr, mesh, face4.Vertices, col, p); mesh.Polygons.Add(p); } // let us normalise normals %) foreach (var v in mesh.Vertices) { v.Normal = v.Normal.SafeNormalize(); } // triangles var j = 0; for (var i = 0; i < trMesh.TexturedTriangles.Length; i++, j++) { tr_copyNormals(mesh.Polygons[j], mesh, trMesh.TexturedTriangles[i].Vertices); } for (var i = 0; i < trMesh.ColouredTriangles.Length; i++, j++) { tr_copyNormals(mesh.Polygons[j], mesh, trMesh.ColouredTriangles[i].Vertices); } // triangles for (var i = 0; i < trMesh.TexturedRectangles.Length; i++, j++) { tr_copyNormals(mesh.Polygons[j], mesh, trMesh.TexturedRectangles[i].Vertices); } for (var i = 0; i < trMesh.ColouredRectangles.Length; i++, j++) { tr_copyNormals(mesh.Polygons[j], mesh, trMesh.ColouredRectangles[i].Vertices); } mesh.Vertices.Clear(); mesh.GenFaces(); mesh.PolySortInMesh(); }
private void createHairMesh(SkeletalModel model) { Mesh = new BaseMesh(); Mesh.ElementsPerTexture.Resize(EngineWorld.Textures.Count); var totalElements = 0; // Gather size information for (var i = 0; i < model.MeshCount; i++) { var original = model.MeshTree[i].MeshBase; Mesh.TexturePageCount = Math.Max(Mesh.TexturePageCount, original.TexturePageCount); for (var j = 0; j < original.TexturePageCount; j++) { Mesh.ElementsPerTexture[j] += original.ElementsPerTexture[j]; totalElements += (int) original.ElementsPerTexture[j]; } } // Create arrays Mesh.Elements.Resize(totalElements); // - with matrix index information Mesh.MatrixIndices.Resize(Mesh.Vertices.Count, () => new BaseMesh.MatrixIndex()); // Copy information var elementsStartPerTexture = new List<uint>(); elementsStartPerTexture.Resize((int)Mesh.TexturePageCount); Mesh.Vertices.Clear(); for (var i = 0; i < model.MeshCount; i++) { var original = model.MeshTree[i].MeshBase; // Copy vertices var verticesStart = Mesh.Vertices.Count; // TODO: Wut... size == 0 (cf. L328) Mesh.Vertices.AddRange(original.Vertices); // Copy elements var originalElementsStart = 0; for (var page = 0; page < original.TexturePageCount; page++) { if (original.ElementsPerTexture[page] == 0) continue; Assert(originalElementsStart < original.Elements.Count); Assert(originalElementsStart + original.ElementsPerTexture[page] <= original.Elements.Count); Assert(elementsStartPerTexture[page] < Mesh.Elements.Count); Assert(elementsStartPerTexture[page] + original.ElementsPerTexture[page] <= Mesh.Elements.Count); Helper.ListCopy(original.Elements, originalElementsStart, Mesh.Elements, (int) elementsStartPerTexture[page], (int) original.ElementsPerTexture[page]); for (var j = 0; j < original.ElementsPerTexture[page]; j++) { Mesh.Elements[(int) elementsStartPerTexture[page]] = (uint) (verticesStart + original.Elements[originalElementsStart]); originalElementsStart++; elementsStartPerTexture[page]++; } } // Apply total offset from parent. // The resulting mesh will have all the hair in default position // (i.e. as one big rope). The shader and matrix then transform it // correctly. Elements[i].Position = model.MeshTree[i].Offset; if(i > 0) { // TODO: This assumes the parent is always the preceding mesh. // True for hair, obviously wrong for everything else. Can stay // here, but must go when we start generalizing the whole thing. Elements[i].Position += Elements[i - 1].Position; } // And create vertex data (including matrix indices) for (var j = 0; j < original.Vertices.Count; j++) { Mesh.MatrixIndices.Add(new BaseMesh.MatrixIndex()); Assert(Mesh.MatrixIndices.Count > verticesStart + j); if (original.Vertices[j].Position[1] <= 0) { Mesh.MatrixIndices[verticesStart + j].I = (sbyte) i; Mesh.MatrixIndices[verticesStart + j].J = (sbyte) (i + 1); } else { Mesh.MatrixIndices[verticesStart + j].I = (sbyte) (i + 1); Mesh.MatrixIndices[verticesStart + j].J = Math.Min((sbyte) (i + 2), (sbyte) model.MeshCount); } // Now move all the hair vertices Mesh.Vertices[verticesStart + j].Position += Elements[i].Position; // If the normal isn't fully in y direction, cancel its y component // This is perhaps a bit dubious. if (Mesh.Vertices[verticesStart + j].Normal.X != 0 || Mesh.Vertices[verticesStart + j].Normal.Z != 0) { Mesh.Vertices[verticesStart + j].Normal.Y = 0; Mesh.Vertices[verticesStart + j].Normal.Normalize(); } } } Mesh.GenVBO(Renderer); }
public void GenMesh(World world, uint roomID, Level tr) { var texMask = world.EngineVersion == Loader.Engine.TR4 ? TextureIndexMaskTr4 : TextureIndexMask; var trRoom = tr.Rooms[roomID]; if(trRoom.Triangles.Length == 0 && trRoom.Rectangles.Length == 0) { Mesh = null; return; } Mesh = new BaseMesh { ID = roomID, TexturePageCount = world.TextureAtlas.NumAtlasPages + 1, UsesVertexColors = true // This is implicitly true on room meshes }; Mesh.Vertices.Resize(trRoom.Vertices.Length, () => new Vertex()); for (var i = 0; i < Mesh.Vertices.Count; i++) { Mesh.Vertices[i].Position = trRoom.Vertices[i].Vertex.ToVector3(); Mesh.Vertices[i].Normal = Vector3.Zero; // paranoid } Mesh.FindBB(); Mesh.Polygons.Resize(trRoom.Triangles.Length + trRoom.Rectangles.Length, () => new Polygon()); var p = 0; // triangles for (var i = 0; i < trRoom.Triangles.Length; i++, p++) { tr_setupRoomVertices(world, tr, trRoom, Mesh, 3, trRoom.Triangles[i].Vertices, (ushort)(trRoom.Triangles[i].Texture & texMask), Mesh.Polygons[p]); Mesh.Polygons[p].DoubleSide = (trRoom.Triangles[i].Texture & 0x8000) != 0; } // rectangles for (var i = 0; i < trRoom.Rectangles.Length; i++, p++) { tr_setupRoomVertices(world, tr, trRoom, Mesh, 4, trRoom.Rectangles[i].Vertices, (ushort)(trRoom.Rectangles[i].Texture & texMask), Mesh.Polygons[p]); Mesh.Polygons[p].DoubleSide = (trRoom.Rectangles[i].Texture & 0x8000) != 0; } // let us normalise normals %) foreach (var v in Mesh.Vertices) { v.Normal = v.Normal.SafeNormalize(); } p = 0; // triangles for (var i = 0; i < trRoom.Triangles.Length; i++, p++) { tr_copyNormals(Mesh.Polygons[p], Mesh, trRoom.Triangles[i].Vertices); } // rectangles for (var i = 0; i < trRoom.Rectangles.Length; i++, p++) { tr_copyNormals(Mesh.Polygons[p], Mesh, trRoom.Rectangles[i].Vertices); } Mesh.Vertices.Clear(); Mesh.GenFaces(); Mesh.PolySortInMesh(); }
public void Empty() { Containers.Clear(); NearRoomList.Clear(); Portals.Clear(); Frustum.Clear(); Mesh = null; if (StaticMesh.Count > 0) { foreach(var sm in StaticMesh) { RigidBody body = null; if((body = sm.BtBody) != null) { if(body.MotionState != null) { body.MotionState.Dispose(); body.MotionState = null; } body.CollisionShape = null; BtEngineDynamicsWorld.RemoveRigidBody(body); body.Dispose(); body = null; sm.BtBody = null; } if(sm.Self != null) { sm.Self.Room = null; sm.Self = null; } } StaticMesh.Clear(); } if(BtBody != null) { BtBody.UserObject = null; if(BtBody.MotionState != null) { BtBody.MotionState.Dispose(); BtBody.MotionState = null; } if(BtBody.CollisionShape != null) { BtBody.CollisionShape.Dispose(); //BtBody.CollisionShape = null; } BtEngineDynamicsWorld.RemoveRigidBody(BtBody); BtBody = null; } Sectors.Clear(); SectorsX = 0; SectorsY = 0; Sprites.Clear(); Lights.Clear(); Self = null; }
public static Vertex FindVertexInMesh(BaseMesh mesh, Vector3 v) { return mesh.Vertices.FirstOrDefault(mv => (v - mv.Position).LengthSquared < 4.0f); }