@Override public ByteBuffer getTile(final int clipmapLevel, final Tile tile) throws Exception { final ByteBuffer data = tileDataPool.get(); final int tileX = tile.getX(); final int tileY = tile.getY(); final int baseClipmapLevel = availableClipmapLevels - clipmapLevel - 1; textureLock.lock(); try { for (int y = 0; y < tileSize; y++) { for (int x = 0; x < tileSize; x++) { if (Thread.interrupted()) { return null; } final int heightX = tileX * tileSize + x; final int heightY = tileY * tileSize + y; final double eval = function.eval(heightX << baseClipmapLevel, heightY << baseClipmapLevel, 0) * 0.4167f + 0.5f; final byte colIndex = (byte) (eval * 255); final ReadOnlyColorRGBA c = terrainColors[colIndex & 0xFF]; final int index = (x + y * tileSize) * 3; data.put(index, (byte) (c.getRed() * 255)); data.put(index + 1, (byte) (c.getGreen() * 255)); data.put(index + 2, (byte) (c.getBlue() * 255)); } } } finally { textureLock.unlock(); } return data; }
/** * Save a mesh to the given files. * * @param mesh mesh to export * @param objFile WaveFront OBJ file * @param mtlFile material file, optional * @param append indicates whether the data are written to the end of the OBJ file * @param firstVertexIndex first vertex index used for this mesh during the export * @param firstFiles indicates whether the couple of files is used for the first time, i.e there * is nothing to append despite the value of <code>append</code> * @param materialList list of materials already exported in this material file * @param customTextureName texture name that overrides the one of the mesh, optional * @throws IOException */ protected void save( final Mesh mesh, final File objFile, final File mtlFile, final boolean append, final int firstVertexIndex, final boolean firstFiles, final List<ObjMaterial> materialList, final String customTextureName) throws IOException { File parentDirectory = objFile.getParentFile(); if (parentDirectory != null && !parentDirectory.exists()) { parentDirectory.mkdirs(); } if (mtlFile != null) { parentDirectory = mtlFile.getParentFile(); if (parentDirectory != null && !parentDirectory.exists()) { parentDirectory.mkdirs(); } } PrintWriter objPw = null, mtlPw = null; try { // fills the MTL file final String mtlName; if (mtlFile != null) { final FileOutputStream mtlOs = new FileOutputStream(mtlFile, append); mtlPw = new PrintWriter(new BufferedOutputStream(mtlOs)); // writes some comments if (firstFiles) { mtlPw.println("# Ardor3D 1.0 MTL file"); } final ObjMaterial currentMtl = new ObjMaterial(null); final MaterialState mtlState = (MaterialState) mesh.getLocalRenderState(StateType.Material); if (mtlState != null) { final ReadOnlyColorRGBA ambientColor = mtlState.getAmbient(); if (ambientColor != null) { currentMtl.d = ambientColor.getAlpha(); currentMtl.Ka = new float[] { ambientColor.getRed(), ambientColor.getGreen(), ambientColor.getBlue(), ambientColor.getAlpha() }; } final ReadOnlyColorRGBA diffuseColor = mtlState.getDiffuse(); if (diffuseColor != null) { currentMtl.Kd = new float[] { diffuseColor.getRed(), diffuseColor.getGreen(), diffuseColor.getBlue(), diffuseColor.getAlpha() }; } final ReadOnlyColorRGBA specularColor = mtlState.getSpecular(); if (specularColor != null) { currentMtl.Ks = new float[] { specularColor.getRed(), specularColor.getGreen(), specularColor.getBlue(), specularColor.getAlpha() }; } currentMtl.Ns = mtlState.getShininess(); } if (customTextureName == null) { currentMtl.textureName = getLocalMeshTextureName(mesh); } else { currentMtl.textureName = customTextureName; } if (mesh.getSceneHints().getLightCombineMode() == LightCombineMode.Off) { // Color on and Ambient off currentMtl.illumType = 0; } else { // Color on and Ambient on currentMtl.illumType = 1; } ObjMaterial sameObjMtl = null; if (materialList != null && !materialList.isEmpty()) { for (final ObjMaterial mtl : materialList) { if (mtl.illumType == currentMtl.illumType && mtl.Ns == currentMtl.Ns && mtl.forceBlend == currentMtl.forceBlend && mtl.d == currentMtl.d && Arrays.equals(mtl.Ka, currentMtl.Ka) && Arrays.equals(mtl.Kd, currentMtl.Kd) && Arrays.equals(mtl.Ks, currentMtl.Ks) // && Objects.equals(mtl.textureName, currentMtl.textureName)) { && mtl.textureName.equals(currentMtl.textureName)) { sameObjMtl = mtl; break; } } } if (sameObjMtl == null) { // writes the new material library mtlName = mtlFile.getName().trim().replaceAll(" ", "") + "_" + (materialList == null ? 1 : materialList.size() + 1); if (materialList != null) { final ObjMaterial mtl = new ObjMaterial(mtlName); mtl.illumType = currentMtl.illumType; mtl.textureName = currentMtl.textureName; materialList.add(mtl); } mtlPw.println("newmtl " + mtlName); if (currentMtl.Ns != -1) { mtlPw.println("Ns " + currentMtl.Ns); } if (currentMtl.Ka != null) { mtlPw.print("Ka"); for (final float KaCoef : currentMtl.Ka) { mtlPw.print(" " + KaCoef); } mtlPw.println(); } if (currentMtl.Kd != null) { mtlPw.print("Kd"); for (final float KdCoef : currentMtl.Kd) { mtlPw.print(" " + KdCoef); } mtlPw.println(); } if (currentMtl.Ks != null) { mtlPw.print("Ks"); for (final float KsCoef : currentMtl.Ks) { mtlPw.print(" " + KsCoef); } mtlPw.println(); } if (currentMtl.d != -1) { mtlPw.println("d " + currentMtl.d); } mtlPw.println("illum " + currentMtl.illumType); if (currentMtl.textureName != null) { mtlPw.println("map_Kd " + currentMtl.textureName); } } else { mtlName = sameObjMtl.getName(); } } else { mtlName = null; } final FileOutputStream objOs = new FileOutputStream(objFile, append); objPw = new PrintWriter(new BufferedOutputStream(objOs)); // writes some comments if (firstFiles) { objPw.println("# Ardor3D 1.0 OBJ file"); objPw.println("# www.ardor3d.com"); // writes the material file name if any if (mtlFile != null) { final String mtlLibFilename = mtlFile.getName(); objPw.println("mtllib " + mtlLibFilename); } } // writes the object name final String objName; String meshName = mesh.getName(); // removes all spaces from the mesh name if (meshName != null && !meshName.isEmpty()) { meshName = meshName.trim().replaceAll(" ", ""); } if (meshName != null && !meshName.isEmpty()) { objName = meshName; } else { objName = "obj_mesh" + mesh.hashCode(); } objPw.println("o " + objName); final MeshData meshData = mesh.getMeshData(); // writes the coordinates final FloatBufferData verticesData = meshData.getVertexCoords(); if (verticesData == null) { throw new IllegalArgumentException("cannot export a mesh with no vertices"); } final int expectedTupleCount = verticesData.getTupleCount(); saveFloatBufferData(verticesData, objPw, "v", expectedTupleCount); final FloatBufferData texCoordsData = meshData.getTextureCoords(0); saveFloatBufferData(texCoordsData, objPw, "vt", expectedTupleCount); final FloatBufferData normalsData = meshData.getNormalCoords(); saveFloatBufferData(normalsData, objPw, "vn", expectedTupleCount); // writes the used material library if (mtlFile != null) { objPw.println("usemtl " + mtlName); } // writes the faces for (int sectionIndex = 0; sectionIndex < meshData.getSectionCount(); sectionIndex++) { final IndexMode indexMode = meshData.getIndexMode(sectionIndex); final int[] indices = new int[indexMode.getVertexCount()]; switch (indexMode) { case TriangleFan: case Triangles: case TriangleStrip: case Quads: for (int primIndex = 0, primCount = meshData.getPrimitiveCount(sectionIndex); primIndex < primCount; primIndex++) { meshData.getPrimitiveIndices(primIndex, sectionIndex, indices); objPw.print("f"); for (int vertexIndex = 0; vertexIndex < indices.length; vertexIndex++) { // indices start at 1 in the WaveFront OBJ format whereas indices start at 0 in // Ardor3D final int shiftedIndex = indices[vertexIndex] + 1 + firstVertexIndex; // vertex index objPw.print(" " + shiftedIndex); // texture coordinate index if (texCoordsData != null) { objPw.print("/" + shiftedIndex); } // normal coordinate index if (normalsData != null) { objPw.print("/" + shiftedIndex); } } objPw.println(); } break; default: throw new IllegalArgumentException("index mode " + indexMode + " not supported"); } } } catch (final Throwable t) { throw new Error("Unable to save the mesh into an obj", t); } finally { if (objPw != null) { objPw.flush(); objPw.close(); } if (mtlPw != null) { mtlPw.flush(); mtlPw.close(); } } }