/** * Convert the normals back to cartesian coordinates. */ private float[] restoreNormals(int[] intNormals, float[] vertices, int[] indices, float normalPrecision) { // Calculate smooth normals (nominal normals) float[] smoothNormals = CommonAlgorithm.calcSmoothNormals(vertices, indices); float[] normals = new float[vertices.Length]; int vc = vertices.Length / Mesh.CTM_POSITION_ELEMENT_COUNT; int ne = Mesh.CTM_NORMAL_ELEMENT_COUNT; for (int i = 0; i < vc; ++i) { // Get the normal magnitude from the first of the three normal elements float magn = intNormals[i * ne] * normalPrecision; // Get phi and theta (spherical coordinates, relative to the smooth normal). double thetaScale, theta; int intPhi = intNormals[i * ne + 1]; double phi = intPhi * (0.5 * Math.PI) * normalPrecision; if (intPhi == 0) { thetaScale = 0.0f; } else if (intPhi <= 4) { thetaScale = Math.PI / 2.0f; } else { thetaScale = (2.0f * Math.PI) / intPhi; } theta = intNormals[i * ne + 2] * thetaScale - Math.PI; // Convert the normal from the angular representation (phi, theta) back to // cartesian coordinates double[] n2 = new double[3]; n2[0] = Math.Sin(phi) * Math.Cos(theta); n2[1] = Math.Sin(phi) * Math.Sin(theta); n2[2] = Math.Cos(phi); float[] basisAxes = CommonAlgorithm.makeNormalCoordSys(smoothNormals, i * ne); double[] n = new double[3]; for (int j = 0; j < 3; ++j) { n[j] = basisAxes[j] * n2[0] + basisAxes[3 + j] * n2[1] + basisAxes[6 + j] * n2[2]; } // Apply normal magnitude, and output to the normals array for (int j = 0; j < 3; ++j) { normals[i * ne + j] = (float)(n[j] * magn); } } return(normals); }
private float[] readVertices(CtmInputStream input, Grid grid, int vcount, float precision) { checkTag(input.readLittleInt(), VERT); int[] intVertices = input.readPackedInts(vcount, Mesh.CTM_POSITION_ELEMENT_COUNT, false); checkTag(input.readLittleInt(), GIDX); int[] gridIndices = input.readPackedInts(vcount, 1, false); for (int i = 1; i < vcount; i++) { gridIndices[i] += gridIndices[i - 1]; } return(CommonAlgorithm.restoreVertices(intVertices, gridIndices, grid, precision)); }
/** * Calculate various forms of derivatives in order to reduce data entropy. */ private int[] makeVertexDeltas(float[] vertices, SortableVertex[] sortVertices, Grid grid) { int vc = sortVertices.Length; // Vertex scaling factor float scale = 1.0f / vertexPrecision; float prevGridIndex = 0x7fffffff; int prevDeltaX = 0; int[] intVertices = new int[vc * Mesh.CTM_POSITION_ELEMENT_COUNT]; for (int i = 0; i < vc; ++i) { // Get grid box origin int gridIdx = sortVertices [i].gridIndex; float[] gridOrigin = CommonAlgorithm.gridIdxToPoint(grid, gridIdx); // Get old vertex coordinate index (before vertex sorting) int oldIdx = sortVertices [i].originalIndex; // Store delta to the grid box origin in the integer vertex array. For the // X axis (which is sorted) we also do the delta to the previous coordinate // in the box. int deltaX = (int)Math.Floor(scale * (vertices [oldIdx * 3] - gridOrigin [0]) + 0.5f); if (gridIdx == prevGridIndex) { intVertices [i * 3] = deltaX - prevDeltaX; } else { intVertices [i * 3] = deltaX; } intVertices [i * 3 + 1] = (int)Math.Floor(scale * (vertices [oldIdx * 3 + 1] - gridOrigin [1]) + 0.5f); intVertices [i * 3 + 2] = (int)Math.Floor(scale * (vertices [oldIdx * 3 + 2] - gridOrigin [2]) + 0.5f); prevGridIndex = gridIdx; prevDeltaX = deltaX; } return(intVertices); }
/** * Convert the normals to a new coordinate system: magnitude, phi, theta * (relative to predicted smooth normals). */ private int[] makeNormalDeltas(float[] vertices, float[] normals, int[] indices, SortableVertex[] sortVertices) { // Calculate smooth normals (Note: aVertices and aIndices use the sorted // index space, so smoothNormals will too) float[] smoothNormals = CommonAlgorithm.calcSmoothNormals(vertices, indices); // Normal scaling factor float scale = 1.0f / normalPrecision; int vc = vertices.Length / Mesh.CTM_POSITION_ELEMENT_COUNT; int[] intNormals = new int[vc * Mesh.CTM_NORMAL_ELEMENT_COUNT]; for (int i = 0; i < vc; ++i) { // Get old normal index (before vertex sorting) int oldIdx = sortVertices [i].originalIndex; // Calculate normal magnitude (should always be 1.0 for unit length normals) float magn = (float)Math.Sqrt(normals [oldIdx * 3] * normals [oldIdx * 3] + normals [oldIdx * 3 + 1] * normals [oldIdx * 3 + 1] + normals [oldIdx * 3 + 2] * normals [oldIdx * 3 + 2]); if (magn < 1e-10f) { magn = 1.0f; } // Invert magnitude if the normal is negative compared to the predicted // smooth normal if ((smoothNormals [i * 3] * normals [oldIdx * 3] + smoothNormals [i * 3 + 1] * normals [oldIdx * 3 + 1] + smoothNormals [i * 3 + 2] * normals [oldIdx * 3 + 2]) < 0.0f) { magn = -magn; } // Store the magnitude in the first element of the three normal elements intNormals [i * 3] = (int)Math.Floor(scale * magn + 0.5f); // Normalize the normal (1 / magn) - and flip it if magn < 0 magn = 1.0f / magn; float[] n = new float[3]; for (int j = 0; j < 3; ++j) { n [j] = normals [oldIdx * 3 + j] * magn; } // Convert the normal to angular representation (phi, theta) in a coordinate // system where the nominal (smooth) normal is the Z-axis float[] basisAxes = CommonAlgorithm.makeNormalCoordSys(smoothNormals, i * 3); float[] n2 = new float[3]; for (int j = 0; j < 3; ++j) { n2 [j] = basisAxes [j * 3] * n [0] + basisAxes [j * 3 + 1] * n [1] + basisAxes [j * 3 + 2] * n [2]; } double phi, theta, thetaScale; if (n2 [2] >= 1.0f) { phi = 0.0f; } else { phi = Math.Acos(n2 [2]); } theta = Math.Atan2(n2 [1], n2 [0]); // Round phi and theta (spherical coordinates) to integers. Note: We let the // theta resolution vary with the x/y circumference (roughly phi). int intPhi = (int)Math.Floor(phi * (scale / (0.5 * Math.PI)) + 0.5); if (intPhi == 0) { thetaScale = 0.0; } else if (intPhi <= 4) { thetaScale = 2.0 / Math.PI; } else { thetaScale = intPhi / (2.0 * Math.PI); } intNormals [i * 3 + 1] = intPhi; intNormals [i * 3 + 2] = (int)Math.Floor((theta + Math.PI) * thetaScale + 0.5f); } return(intNormals); }
public override void encode(Mesh m, CtmOutputStream output) { Grid grid = setupGrid(m.vertices); SortableVertex[] sorted = sortVertices(grid, m.vertices); int[] vdeltas = makeVertexDeltas(m.vertices, sorted, grid); int[] gridIndicies = new int[m.getVertexCount()]; gridIndicies [0] = sorted [0].gridIndex; for (int i = 1; i < m.getVertexCount(); ++i) { gridIndicies [i] = sorted [i].gridIndex - sorted [i - 1].gridIndex; } output.writeLittleInt(MG2Decoder.MG2_HEADER_TAG); output.writeLittleFloat(vertexPrecision); output.writeLittleFloat(normalPrecision); grid.writeToStream(output); output.writeLittleInt(MeshDecoder.VERT); output.writePackedInts(vdeltas, m.getVertexCount(), Mesh.CTM_POSITION_ELEMENT_COUNT, false); output.writeLittleInt(MG2Decoder.GIDX); output.writePackedInts(gridIndicies, m.getVertexCount(), 1, false); output.writeLittleInt(MeshDecoder.INDX); int[] indices = reIndexIndices(sorted, m.indices); rearrangeTriangles(indices); //write indicies { int[] deltas = new int[indices.Length]; Array.Copy(indices, deltas, indices.Length); makeIndexDeltas(deltas); output.writePackedInts(deltas, m.getTriangleCount(), 3, false); } if (m.hasNormals()) { for (int i = 1; i < m.getVertexCount(); i++) { gridIndicies [i] += gridIndicies [i - 1]; } float[] restoredv = CommonAlgorithm.restoreVertices(vdeltas, gridIndicies, grid, vertexPrecision); output.writeLittleInt(MeshDecoder.NORM); int[] intNormals = makeNormalDeltas(restoredv, m.normals, indices, sorted); output.writePackedInts(intNormals, m.getVertexCount(), Mesh.CTM_NORMAL_ELEMENT_COUNT, false); } foreach (AttributeData ad in m.texcoordinates) { output.writeLittleInt(MeshDecoder.TEXC); output.writeString(ad.name); output.writeString(ad.materialName); output.writeLittleFloat(ad.precision); int[] deltas = makeUVCoordDeltas(ad, sorted); output.writePackedInts(deltas, m.getVertexCount(), Mesh.CTM_UV_ELEMENT_COUNT, true); } foreach (AttributeData ad in m.attributs) { output.writeLittleInt(MeshDecoder.ATTR); output.writeString(ad.name); output.writeLittleFloat(ad.precision); int[] deltas = makeAttribDeltas(ad, sorted); output.writePackedInts(deltas, m.getVertexCount(), Mesh.CTM_ATTR_ELEMENT_COUNT, true); } }