/// <summary> /// Toma una curva de Quake 3 y la convierte a un TgcMesh, mediante el proceso de Tessellation /// </summary> private void tessellatePatch(BspMap bspMap, int surfaceId) { // los patch de quake 3 estan formados por curvas de bezier // todos los patch tienen 9 puntos que son los puntos de control para la curva // Esos puntos hay que tesselarlos para conseguir los triangulos int L = 5; QSurface surface = bspMap.Data.drawSurfaces[surfaceId]; int offVert = surface.firstVert; // coeficientes de bezier float[,] B = new float[L+1, 3]; //Numero de patches de 3x3 en cada direccion int num1 = (surface.patchHeight - 1) / 2; int num2 = (surface.patchWidth - 1) / 2; int cantVertices = (L * num1 + 1) * (L * num2 + 1); int cantIndices = 2 * (L * num2 + 2) * (L * num1); //aloco el espacio para los vertices y los indices Vector3[] vertices = new Vector3[cantVertices]; Vector3[] normals = new Vector3[cantVertices]; Vector2[] textCords = new Vector2[cantVertices]; Vector2[] textCords2 = new Vector2[cantVertices]; int[] indices = new int[cantIndices]; { //se cargan las constantes de bezier float dt = 1.0f / L; float t = 0; for (int i = 0; i < L + 1; i++) { if (i == L) t = 1.0f; float mt = 1 - t; B[i,0] = mt * mt; B[i,1] = 2 * mt * t; B[i,2] = t * t; t += dt; } } //Calculo de los bicubic bezier patch para cada strip int pointsXStrip = L * num2 + 1; int pointsXPatch = pointsXStrip * L; int indexCount = 0; int vertexNum = 0; int[] controls = new int[3]; // subindices a los vertices de puntos de control // por cada patch for (int i = 0, Li = L; i < num1; i++) { if (i == num1 - 1) Li = L + 1; // seteo los indices for (int j = 0; j < L; j++) { for (int k = 0; k < pointsXStrip; k++) { indices[indexCount++] = i * pointsXPatch + j * pointsXStrip + k; indices[indexCount++] = i * pointsXPatch + (j + 1) * pointsXStrip + k; } //repito primer y ultimo indice indices[indexCount++] = i * pointsXPatch + (j + 2) * pointsXStrip - 1; indices[indexCount++] = i * pointsXPatch + (j + 1) * pointsXStrip; } //Ahora van los puntos de control y los vertices for (int j = 0, Lj = L; j < num2; j++) { if (j == num2 - 1) Lj = L + 1; // calculo de los puntos de control para este patch // controls[fila] = indice del primer vertice de la fila de este patch controls[0] = offVert + (i * 2) * (2 * num2 + 1) + (j * 2); controls[1] = offVert + (i * 2 + 1) * (2 * num2 + 1) + (j * 2); controls[2] = offVert + (i * 2 + 2) * (2 * num2 + 1) + (j * 2); //por cada punto en el patch for (int i2 = 0; i2 < Li; i2++) { vertexNum = i * pointsXPatch + i2 * pointsXStrip + j * L; for (int j2 = 0; j2 < Lj; j2++, vertexNum++) { vertices[vertexNum] = new Vector3(); normals[vertexNum] = new Vector3(); textCords[vertexNum] = new Vector2(); textCords2[vertexNum] = new Vector2(); for (int i3 = 0; i3 < 3; i3++) { for (int j3 = 0; j3 < 3; j3++) { float blendFactor = B[i2, i3] * B[j2, j3]; vertices[vertexNum] += bspMap.Data.drawVerts[controls[i3] + j3].xyz * blendFactor; normals[vertexNum] += bspMap.Data.drawVerts[controls[i3] + j3].normal * blendFactor; textCords[vertexNum] += bspMap.Data.drawVerts[controls[i3] + j3].st * blendFactor; textCords2[vertexNum] += bspMap.Data.drawVerts[controls[i3] + j3].lightmap * blendFactor; } } } } } } short[] indexBuffer = new short[3 * cantIndices - 6]; int cant_ibuffer = 0; for (int i = 2; i < cantIndices; i++) { if (i % 2 == 0) { indexBuffer[cant_ibuffer++] = (short) indices[i - 2]; indexBuffer[cant_ibuffer++] = (short) indices[i - 1]; indexBuffer[cant_ibuffer++] = (short) indices[i]; } else { indexBuffer[cant_ibuffer++] = (short)indices[i]; indexBuffer[cant_ibuffer++] = (short)indices[i - 1]; indexBuffer[cant_ibuffer++] = (short)indices[i - 2]; } } //Mesh de DirectX Mesh mesh = new Mesh(indexBuffer.Length / 3, vertices.Length, MeshFlags.Managed, TgcSceneLoader.DiffuseMapAndLightmapVertexElements, GuiController.Instance.D3dDevice); //Cargar vertexBuffer using (VertexBuffer vb = mesh.VertexBuffer) { TgcSceneLoader.DiffuseMapAndLightmapVertex[] vertex = new TgcSceneLoader.DiffuseMapAndLightmapVertex[vertices.Length]; for (int i = 0; i < vertices.Length; i++) { vertex[i] = new TgcSceneLoader.DiffuseMapAndLightmapVertex(); vertex[i].Position = vertices[i]; vertex[i].Normal = normals[i];//drawVerts[offVert].normal; vertex[i].Tu0 = textCords[i].X;//drawVerts[offVert].st.X; vertex[i].Tv0 = textCords[i].Y;//drawVerts[offVert].st.Y; vertex[i].Tu1 = textCords2[i].X;//drawVerts[offVert].lightmap.X; vertex[i].Tv1 = textCords2[i].Y;//drawVerts[offVert].lightmap.Y; vertex[i].Color = Color.White.ToArgb(); //drawVerts[i].color; vb.SetData(vertex, 0, LockFlags.None); } } //IndexBuffer using (IndexBuffer ib = mesh.IndexBuffer) { ib.SetData(indexBuffer, 0, LockFlags.None); } //Crea el tgcMesh TgcMesh tgcMesh = CreateTgcMesh(bspMap, mesh, surfaceId); bspMap.Meshes.Add(tgcMesh); }
/// <summary> /// Carga todos los meshes /// </summary> private void CreateMeshes(BspMap bspMap) { Device device = GuiController.Instance.D3dDevice; for (int id = 0; id < bspMap.Data.drawSurfaces.Length; id++) { QSurface surface = bspMap.Data.drawSurfaces[id]; //Crear Patch (curva= if (surface.surfaceType == QMapSurfaceType.Patch) { tessellatePatch(bspMap, id); continue; } /* billboards, no soportados actualmente if (surface.surfaceType == QMapSurfaceType.Flare) { } */ //Superficies no soportadas if (surface.surfaceType != QMapSurfaceType.Planar && surface.surfaceType != QMapSurfaceType.TriangleSoup) { //Agregar null para no mover todos los índices bspMap.Meshes.Add(null); continue; } //Cargar superficies soportadas: de tipo QMapSurfaceType.Planar o QMapSurfaceType.TriangleSoup int cant_indices = surface.numIndexes; int cant_vertices = surface.numVerts; if (cant_vertices <= 0) continue; //Mesh de DirectX Mesh mesh = new Mesh(cant_indices / 3, cant_vertices, MeshFlags.Managed, TgcSceneLoader.DiffuseMapAndLightmapVertexElements, device); //Cargar vertexBuffer using (VertexBuffer vb = mesh.VertexBuffer) { TgcSceneLoader.DiffuseMapAndLightmapVertex[] vertices = new TgcSceneLoader.DiffuseMapAndLightmapVertex[cant_vertices]; int j = 0; for (int i = surface.firstVert; i < surface.firstVert + surface.numVerts; i++, j++) { vertices[j] = new TgcSceneLoader.DiffuseMapAndLightmapVertex(); vertices[j].Position = bspMap.Data.drawVerts[i].xyz; vertices[j].Normal = bspMap.Data.drawVerts[i].normal; vertices[j].Tu0 = bspMap.Data.drawVerts[i].st.X; vertices[j].Tv0 = bspMap.Data.drawVerts[i].st.Y; vertices[j].Tu1 = bspMap.Data.drawVerts[i].lightmap.X; vertices[j].Tv1 = bspMap.Data.drawVerts[i].lightmap.Y; vertices[j].Color = Color.White.ToArgb();//drawVerts[i].color; } vb.SetData(vertices, 0, LockFlags.None); } using (IndexBuffer ib = mesh.IndexBuffer) { short[] indices = new short[cant_indices]; int j = 0; for (int i = surface.firstIndex; i < surface.firstIndex + surface.numIndexes; i++, j++) { indices[j] = (short)bspMap.Data.drawIndexes[i]; } ib.SetData(indices, 0, LockFlags.None); } //Crea el tgcMesh TgcMesh tgcMesh = CreateTgcMesh(bspMap, mesh, id); bspMap.Meshes.Add(tgcMesh); } }