void IntermediateValues::generateRealObj(uint32 mapID, uint32 tileX, uint32 tileY, MeshData meshData) { char objFileName[255]; sprintf(objFileName, "meshes/map%03u.obj", mapID); FILE* objFile = fopen(objFileName, "wb"); if (!objFile) { char message[1024]; sprintf(message, "Failed to open %s for writing!\n", objFileName); perror(message); return; } G3D::Array<float> allVerts; G3D::Array<int> allTris; allTris.append(meshData.liquidTris); allVerts.append(meshData.liquidVerts); TerrainBuilder::copyIndices(allTris, meshData.solidTris, allVerts.size() / 3); allVerts.append(meshData.solidVerts); float* verts = allVerts.getCArray(); int* tris = allTris.getCArray(); for (int i = 0; i < allVerts.size() / 3; i++) fprintf(objFile, "v %f %f %f\n", verts[i*3], verts[i*3 + 1], verts[i*3 + 2]); for (int i = 0; i < allTris.size() / 3; i++) fprintf(objFile, "f %i %i %i\n", tris[i*3] + 1, tris[i*3 + 1] + 1, tris[i*3 + 2] + 1); fclose(objFile); }
void IntermediateValues::generateObjFile(uint32 mapID, uint32 tileX, uint32 tileY, MeshData meshData) { generateRealObj(mapID, tileX, tileY, meshData); char tileString[25]; sprintf(tileString, "[%02u,%02u]: ", tileY, tileX); printf("%sWriting debug output... \r", tileString); char objFileName[255]; sprintf(objFileName, "meshes/%03u.map", mapID); FILE* objFile = fopen(objFileName, "wb"); if (!objFile) { char message[1024]; sprintf(message, "Failed to open %s for writing!\n", objFileName); perror(message); return; } char b = '\0'; fwrite(&b, sizeof(char), 1, objFile); fclose(objFile); sprintf(objFileName, "meshes/%03u%02u%02u.mesh", mapID, tileY, tileX); objFile = fopen(objFileName, "wb"); if (!objFile) { char message[1024]; sprintf(message, "Failed to open %s for writing!\n", objFileName); perror(message); return; } G3D::Array<float> allVerts; G3D::Array<int> allTris; allTris.append(meshData.liquidTris); allVerts.append(meshData.liquidVerts); TerrainBuilder::copyIndices(allTris, meshData.solidTris, allVerts.size() / 3); allVerts.append(meshData.solidVerts); float* verts = allVerts.getCArray(); int vertCount = allVerts.size() / 3; int* tris = allTris.getCArray(); int triCount = allTris.size() / 3; fwrite(&vertCount, sizeof(int), 1, objFile); fwrite(verts, sizeof(float), vertCount*3, objFile); fflush(objFile); fwrite(&triCount, sizeof(int), 1, objFile); fwrite(tris, sizeof(int), triCount*3, objFile); fflush(objFile); fclose(objFile); }
void MapBuilder::buildTile(int mapID, int tileX, int tileY, dtNavMesh* navMesh) { MeshData meshData; // get heightmap data m_terrainBuilder->loadMap(mapID, tileX, tileY, meshData, m_magic); // get model data m_terrainBuilder->loadVMap(mapID, tileY, tileX, meshData); // if there is no data, give up now if (!meshData.solidVerts.size() && !meshData.liquidVerts.size()) { return; } // remove unused vertices TerrainBuilder::cleanVertices(meshData.solidVerts, meshData.solidTris); TerrainBuilder::cleanVertices(meshData.liquidVerts, meshData.liquidTris); // gather all mesh data for final data check, and bounds calculation G3D::Array<float> allVerts; allVerts.append(meshData.liquidVerts); allVerts.append(meshData.solidVerts); if (!allVerts.size()) { return; } // get bounds of current tile float bmin[3], bmax[3]; getTileBounds(tileX, tileY, allVerts.getCArray(), allVerts.size() / 3, bmin, bmax); m_terrainBuilder->loadOffMeshConnections(mapID, tileX, tileY, meshData, m_offMeshFilePath); printf(" Building map %03u - Tile [%02u,%02u]\n", mapID, tileX, tileY); buildMoveMapTile(mapID, tileX, tileY, meshData, bmin, bmax, navMesh); }
bool TileBuilder::loadMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData, Spot portion) { char mapFileName[255]; sprintf(mapFileName, "maps/%03u%02u%02u.map", mapID, tileY, tileX); FILE* mapFile = fopen(mapFileName, "rb"); if(!mapFile) return true; GridMapFileHeader fheader; fread(&fheader, sizeof(GridMapFileHeader), 1, mapFile); if(fheader.versionMagic != *((uint32 const*)(MAP_VERSION_MAGIC))) { fclose(mapFile); printf("%s is the wrong version, please extract new .map files\n", mapFileName); return false; } GridMapHeightHeader hheader; fseek(mapFile, fheader.heightMapOffset, SEEK_SET); fread(&hheader, sizeof(GridMapHeightHeader), 1, mapFile); bool haveTerrain = !(hheader.flags & MAP_HEIGHT_NO_HEIGHT); bool haveLiquid = fheader.liquidMapOffset && !m_skipLiquid; // no data in this map file if(!haveTerrain && !haveLiquid) { fclose(mapFile); return true; } // data used later uint16 holes[16][16]; uint8* liquid_type = 0; G3D::Array<int> ltriangles; G3D::Array<int> ttriangles; // terrain data if(haveTerrain) { int i; float heightMultiplier; float V9[V9_SIZE_SQ], V8[V8_SIZE_SQ]; if(hheader.flags & MAP_HEIGHT_AS_INT8) { uint8 v9[V9_SIZE_SQ]; uint8 v8[V8_SIZE_SQ]; fread(v9, sizeof(uint8), V9_SIZE_SQ, mapFile); fread(v8, sizeof(uint8), V8_SIZE_SQ, mapFile); heightMultiplier = (hheader.gridMaxHeight - hheader.gridHeight) / 255; for(i = 0; i < V9_SIZE_SQ; ++i) V9[i] = (float)v9[i]*heightMultiplier + hheader.gridHeight; if(m_hiResHeightMaps) for(i = 0; i < V8_SIZE_SQ; ++i) V8[i] = (float)v8[i]*heightMultiplier + hheader.gridHeight; } else if(hheader.flags & MAP_HEIGHT_AS_INT16) { uint16 v9[V9_SIZE_SQ]; uint16 v8[V8_SIZE_SQ]; fread(v9, sizeof(uint16), V9_SIZE_SQ, mapFile); fread(v8, sizeof(uint16), V8_SIZE_SQ, mapFile); heightMultiplier = (hheader.gridMaxHeight - hheader.gridHeight) / 65535; for(i = 0; i < V9_SIZE_SQ; ++i) V9[i] = (float)v9[i]*heightMultiplier + hheader.gridHeight; if(m_hiResHeightMaps) for(i = 0; i < V8_SIZE_SQ; ++i) V8[i] = (float)v8[i]*heightMultiplier + hheader.gridHeight; } else { fread(V9, sizeof(float), V9_SIZE_SQ, mapFile); if(m_hiResHeightMaps) fread(V8, sizeof(float), V8_SIZE_SQ, mapFile); } // hole data memset(holes, 0, fheader.holesSize); fseek(mapFile, fheader.holesOffset, SEEK_SET); fread(holes, fheader.holesSize, 1, mapFile); int count = meshData.solidVerts.size() / 3; float xoffset = (float(tileX)-32)*GRID_SIZE; float yoffset = (float(tileY)-32)*GRID_SIZE; float coord[3]; for(i = 0; i < V9_SIZE_SQ; ++i) { getHeightCoord(i, GRID_V9, xoffset, yoffset, coord, V9); meshData.solidVerts.append(coord[0]); meshData.solidVerts.append(coord[2]); meshData.solidVerts.append(coord[1]); } if(m_hiResHeightMaps) for(i = 0; i < V8_SIZE_SQ; ++i) { getHeightCoord(i, GRID_V8, xoffset, yoffset, coord, V8); meshData.solidVerts.append(coord[0]); meshData.solidVerts.append(coord[2]); meshData.solidVerts.append(coord[1]); } int triInc, j, indices[3], loopStart, loopEnd, loopInc; getLoopVars(portion, loopStart, loopEnd, loopInc); triInc = m_hiResHeightMaps ? 1 : BOTTOM-TOP; for(i = loopStart; i < loopEnd; i+=loopInc) for(j = TOP; j <= BOTTOM; j+=triInc) { getHeightTriangle(i, Spot(j), indices); ttriangles.append(indices[2] + count); ttriangles.append(indices[1] + count); ttriangles.append(indices[0] + count); } } // liquid data if(haveLiquid) { do { GridMapLiquidHeader lheader; fseek(mapFile, fheader.liquidMapOffset, SEEK_SET); fread(&lheader, sizeof(GridMapLiquidHeader), 1, mapFile); float* liquid_map = 0; if (!(lheader.flags & MAP_LIQUID_NO_TYPE)) { liquid_type = new uint8 [16*16]; fread(liquid_type, sizeof(uint8), 16*16, mapFile); } if (!(lheader.flags & MAP_LIQUID_NO_HEIGHT)) { liquid_map = new float [lheader.width*lheader.height]; fread(liquid_map, sizeof(float), lheader.width*lheader.height, mapFile); } if(!liquid_type && !liquid_map) break; int count = meshData.liquidVerts.size() / 3; float xoffset = (float(tileX)-32)*GRID_SIZE; float yoffset = (float(tileY)-32)*GRID_SIZE; float coord[3]; int row, col; // generate coordinates if (!(lheader.flags & MAP_LIQUID_NO_HEIGHT)) { int j = 0; for(int i = 0; i < V9_SIZE_SQ; ++i) { row = i / V9_SIZE; col = i % V9_SIZE; if((row < (lheader.offsetY) || row >= (lheader.offsetY + lheader.height)) || (col < (lheader.offsetX) || col >= (lheader.offsetX + lheader.width))) { // dummy vert using invalid height meshData.liquidVerts.append((xoffset+col*GRID_PART_SIZE)*-1, -500.f, (yoffset+row*GRID_PART_SIZE)*-1); continue; } getLiquidCoord(i, j, xoffset, yoffset, coord, liquid_map); meshData.liquidVerts.append(coord[0]); meshData.liquidVerts.append(coord[2]); meshData.liquidVerts.append(coord[1]); j++; } } else { for(int i = 0; i < V9_SIZE_SQ; ++i) { row = i / V9_SIZE; col = i % V9_SIZE; meshData.liquidVerts.append((xoffset+col*GRID_PART_SIZE)*-1, lheader.liquidLevel, (yoffset+row*GRID_PART_SIZE)*-1); } } delete [] liquid_map; int indices[3], loopStart, loopEnd, loopInc, triInc; getLoopVars(portion, loopStart, loopEnd, loopInc); triInc = BOTTOM-TOP; // generate triangles for(int i = loopStart; i < loopEnd; i+=loopInc) for(int j = TOP; j <= BOTTOM; j+= triInc) { getHeightTriangle(i, Spot(j), indices, true); ltriangles.append(indices[2] + count); ltriangles.append(indices[1] + count); ltriangles.append(indices[0] + count); } }while(0); } fclose(mapFile); // now that we have gathered the data, we can figure out which parts to keep: // liquid above ground // ground above any liquid type // ground below <1.5 yard of water int loopStart, loopEnd, loopInc, tTriCount; bool useTerrain, useLiquid; float* lverts = meshData.liquidVerts.getCArray(); float* tverts = meshData.solidVerts.getCArray(); int* ltris = ltriangles.getCArray(); int* ttris = ttriangles.getCArray(); getLoopVars(portion, loopStart, loopEnd, loopInc); tTriCount = m_hiResHeightMaps ? 4 : 2; if(ltriangles.size() || ttriangles.size()) { for(int i = loopStart; i < loopEnd; i+=loopInc) { for(int j = 0; j < 2; ++j) { // default is true, will change to false if needed useTerrain = true; useLiquid = true; uint8 liquidType; // if there is no liquid, don't use liquid if(!liquid_type || !meshData.liquidVerts.size() || !ltriangles.size()) useLiquid = false; else { liquidType = getLiquidType(i, (const uint8 (*)[16])liquid_type); switch(liquidType) { case 0: // unknown liquid gets no terrain type liquidType = 0; break; case MAP_LIQUID_TYPE_WATER: case MAP_LIQUID_TYPE_OCEAN: // merge different types of water liquidType = NAV_WATER; break; case MAP_LIQUID_TYPE_MAGMA: liquidType = NAV_MAGMA; break; case MAP_LIQUID_TYPE_SLIME: liquidType = NAV_SLIME; break; case MAP_LIQUID_TYPE_DARK_WATER: // players should not be here, so logically neither should creatures useTerrain = false; useLiquid = false; break; } } // if there is no terrain, don't use terrain if(!ttriangles.size()) useTerrain = false; // liquid is rendered as quads. If any triangle has invalid height, // don't render any of the triangles in that quad // contrib/extractor uses -500.0 for invalid height if(useLiquid) if((&lverts[ltris[0]*3])[1] == -500.f || (&lverts[ltris[1]*3])[1] == -500.f || (&lverts[ltris[2]*3])[1] == -500.f) { useLiquid = false; } // if there is a hole here, don't use the terrain if(useTerrain) useTerrain = !isHole(i, holes); if(useTerrain && useLiquid) { // get the indexes of the corners of the quad int idx1, idx2, idx3; if(j == 0) { idx1 = 0; idx2 = 1; idx3 = tTriCount; // accesses index from next triangle on low res } else { idx1 = 0; idx2 = 3*tTriCount/2-2; idx3 = 3*tTriCount/2-1; } if(useTerrain && (&lverts[ltris[0]*3])[1] - 1.5f > (&tverts[ttris[idx1]*3])[1] && (&lverts[ltris[1]*3])[1] - 1.5f > (&tverts[ttris[idx2]*3])[1] && (&lverts[ltris[2]*3])[1] - 1.5f > (&tverts[ttris[idx3]*3])[1]) useTerrain = false; // if the whole terrain triangle is 1.5yds under liquid, don't use it else if(useLiquid && (&lverts[ltris[0]*3])[1] < (&tverts[ttris[idx1]*3])[1] && (&lverts[ltris[1]*3])[1] < (&tverts[ttris[idx2]*3])[1] && (&lverts[ltris[2]*3])[1] < (&tverts[ttris[idx3]*3])[1]) useLiquid = false; // if the whole liquid triangle is under terrain, don't use it } if(useLiquid) { meshData.liquidType.append(liquidType); for(int k = 0; k < 3; ++k) meshData.liquidTris.append(ltris[k]); } if(useTerrain) for(int k = 0; k < 3*tTriCount/2; ++k) meshData.solidTris.append(ttris[k]); // advance to next set of triangles ltris += 3; ttris += 3*tTriCount/2; } } } delete [] liquid_type; return true; }