/** * Merges all geometries in the collection into the output mesh. Creates a new material using the * TextureAtlas. * * @param geometries * @param outMesh */ public static void mergeGeometries(Collection<Geometry> geometries, Mesh outMesh) { int[] compsForBuf = new int[VertexBuffer.Type.values().length]; Format[] formatForBuf = new Format[compsForBuf.length]; int totalVerts = 0; int totalTris = 0; int totalLodLevels = 0; int maxWeights = -1; Mode mode = null; for (Geometry geom : geometries) { totalVerts += geom.getVertexCount(); totalTris += geom.getTriangleCount(); totalLodLevels = Math.min(totalLodLevels, geom.getMesh().getNumLodLevels()); Mode listMode; int components; switch (geom.getMesh().getMode()) { case Points: listMode = Mode.Points; components = 1; break; case LineLoop: case LineStrip: case Lines: listMode = Mode.Lines; components = 2; break; case TriangleFan: case TriangleStrip: case Triangles: listMode = Mode.Triangles; components = 3; break; default: throw new UnsupportedOperationException(); } for (VertexBuffer vb : geom.getMesh().getBufferList().getArray()) { int currentCompsForBuf = compsForBuf[vb.getBufferType().ordinal()]; if (vb.getBufferType() != Type.Index && currentCompsForBuf != 0 && currentCompsForBuf != vb.getNumComponents()) { throw new UnsupportedOperationException( "The geometry " + geom + " buffer " + vb.getBufferType() + " has different number of components than the rest of the meshes " + "(this: " + vb.getNumComponents() + ", expected: " + currentCompsForBuf + ")"); } compsForBuf[vb.getBufferType().ordinal()] = vb.getNumComponents(); formatForBuf[vb.getBufferType().ordinal()] = vb.getFormat(); } maxWeights = Math.max(maxWeights, geom.getMesh().getMaxNumWeights()); if (mode != null && mode != listMode) { throw new UnsupportedOperationException( "Cannot combine different" + " primitive types: " + mode + " != " + listMode); } mode = listMode; compsForBuf[Type.Index.ordinal()] = components; } outMesh.setMaxNumWeights(maxWeights); outMesh.setMode(mode); if (totalVerts >= 65536) { // make sure we create an UnsignedInt buffer so // we can fit all of the meshes formatForBuf[Type.Index.ordinal()] = Format.UnsignedInt; } else { formatForBuf[Type.Index.ordinal()] = Format.UnsignedShort; } // generate output buffers based on retrieved info for (int i = 0; i < compsForBuf.length; i++) { if (compsForBuf[i] == 0) { continue; } Buffer data; if (i == Type.Index.ordinal()) { data = VertexBuffer.createBuffer(formatForBuf[i], compsForBuf[i], totalTris); } else { data = VertexBuffer.createBuffer(formatForBuf[i], compsForBuf[i], totalVerts); } VertexBuffer vb = new VertexBuffer(Type.values()[i]); vb.setupData(Usage.Static, compsForBuf[i], formatForBuf[i], data); outMesh.setBuffer(vb); } int globalVertIndex = 0; int globalTriIndex = 0; for (Geometry geom : geometries) { Mesh inMesh = geom.getMesh(); geom.computeWorldMatrix(); Matrix4f worldMatrix = geom.getWorldMatrix(); int geomVertCount = inMesh.getVertexCount(); int geomTriCount = inMesh.getTriangleCount(); for (int bufType = 0; bufType < compsForBuf.length; bufType++) { VertexBuffer inBuf = inMesh.getBuffer(Type.values()[bufType]); VertexBuffer outBuf = outMesh.getBuffer(Type.values()[bufType]); if (inBuf == null || outBuf == null) { continue; } if (Type.Index.ordinal() == bufType) { int components = compsForBuf[bufType]; IndexBuffer inIdx = inMesh.getIndicesAsList(); IndexBuffer outIdx = outMesh.getIndexBuffer(); for (int tri = 0; tri < geomTriCount; tri++) { for (int comp = 0; comp < components; comp++) { int idx = inIdx.get(tri * components + comp) + globalVertIndex; outIdx.put((globalTriIndex + tri) * components + comp, idx); } } } else if (Type.Position.ordinal() == bufType) { FloatBuffer inPos = (FloatBuffer) inBuf.getDataReadOnly(); FloatBuffer outPos = (FloatBuffer) outBuf.getData(); doTransformVerts(inPos, globalVertIndex, outPos, worldMatrix); } else if (Type.Normal.ordinal() == bufType) { FloatBuffer inPos = (FloatBuffer) inBuf.getDataReadOnly(); FloatBuffer outPos = (FloatBuffer) outBuf.getData(); doTransformNorms(inPos, globalVertIndex, outPos, worldMatrix); } else if (Type.Tangent.ordinal() == bufType) { FloatBuffer inPos = (FloatBuffer) inBuf.getDataReadOnly(); FloatBuffer outPos = (FloatBuffer) outBuf.getData(); int components = inBuf.getNumComponents(); doTransformTangents(inPos, globalVertIndex, components, outPos, worldMatrix); } else { inBuf.copyElements(0, outBuf, globalVertIndex, geomVertCount); } } globalVertIndex += geomVertCount; globalTriIndex += geomTriCount; } }
// Don't remove splitmirorred boolean,It's not used right now, but i intend to // make this method also split vertice with rotated tangent space and I'll // add another splitRotated boolean private static List<VertexData> splitVertices( Mesh mesh, List<VertexData> vertexData, boolean splitMirorred) { int nbVertices = mesh.getBuffer(Type.Position).getNumElements(); List<VertexData> newVertices = new ArrayList<VertexData>(); Map<Integer, Integer> indiceMap = new HashMap<Integer, Integer>(); FloatBuffer normalBuffer = mesh.getFloatBuffer(Type.Normal); for (int i = 0; i < vertexData.size(); i++) { ArrayList<TriangleData> triangles = vertexData.get(i).triangles; Vector3f givenNormal = new Vector3f(); populateFromBuffer(givenNormal, normalBuffer, i); ArrayList<TriangleData> trianglesUp = new ArrayList<TriangleData>(); ArrayList<TriangleData> trianglesDown = new ArrayList<TriangleData>(); for (int j = 0; j < triangles.size(); j++) { TriangleData triangleData = triangles.get(j); if (parity(givenNormal, triangleData.normal) > 0) { trianglesUp.add(triangleData); } else { trianglesDown.add(triangleData); } } // if the vertex has triangles with opposite parity it has to be split if (!trianglesUp.isEmpty() && !trianglesDown.isEmpty()) { log.log(Level.FINE, "Splitting vertex {0}", i); // assigning triangle with the same parity to the original vertex vertexData.get(i).triangles.clear(); vertexData.get(i).triangles.addAll(trianglesUp); // creating a new vertex VertexData newVert = new VertexData(); // assigning triangles with opposite parity to it newVert.triangles.addAll(trianglesDown); newVertices.add(newVert); // keep vertex index to fix the index buffers later indiceMap.put(nbVertices, i); for (TriangleData tri : newVert.triangles) { for (int j = 0; j < tri.index.length; j++) { if (tri.index[j] == i) { tri.index[j] = nbVertices; } } } nbVertices++; } } if (!newVertices.isEmpty()) { // we have new vertices, we need to update the mesh's buffers. for (Type type : VertexBuffer.Type.values()) { // skip tangent buffer as we're gonna overwrite it later if (type == Type.Tangent || type == Type.BindPoseTangent) { continue; } VertexBuffer vb = mesh.getBuffer(type); // Some buffer (hardware skinning ones) can be there but not // initialized, they must be skipped. // They'll be initialized when Hardware Skinning is engaged if (vb == null || vb.getNumComponents() == 0) { continue; } Buffer buffer = vb.getData(); // IndexBuffer has special treatement, only swapping the vertex indices is needed if (type == Type.Index) { boolean isShortBuffer = vb.getFormat() == VertexBuffer.Format.UnsignedShort; for (VertexData vertex : newVertices) { for (TriangleData tri : vertex.triangles) { for (int i = 0; i < tri.index.length; i++) { if (isShortBuffer) { ((ShortBuffer) buffer).put(tri.triangleOffset + i, (short) tri.index[i]); } else { ((IntBuffer) buffer).put(tri.triangleOffset + i, tri.index[i]); } } } } vb.setUpdateNeeded(); } else { // copy the buffer in a bigger one and append nex vertices to the end Buffer newVerts = VertexBuffer.createBuffer(vb.getFormat(), vb.getNumComponents(), nbVertices); if (buffer != null) { buffer.rewind(); bulkPut(vb.getFormat(), newVerts, buffer); int index = vertexData.size(); newVerts.position(vertexData.size() * vb.getNumComponents()); for (int j = 0; j < newVertices.size(); j++) { int oldInd = indiceMap.get(index); for (int i = 0; i < vb.getNumComponents(); i++) { putValue(vb.getFormat(), newVerts, buffer, oldInd * vb.getNumComponents() + i); } index++; } vb.updateData(newVerts); // destroy previous buffer as it's no longer needed destroyDirectBuffer(buffer); } } } vertexData.addAll(newVertices); mesh.updateCounts(); } return vertexData; }