/* OpenMW - The completely unofficial reimplementation of Morrowind Copyright (C) 2009 Nicolay Korslund WWW: http://openmw.sourceforge.net/ This file (cpp_archive.cpp) is part of the OpenMW package. OpenMW is distributed as free software: you can redistribute it and/or modify it under the terms of the GNU General Public License version 3, as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License version 3 along with this program. If not, see http://www.gnu.org/licenses/ . */ // Info about the entire quad. TODO: Some of this (such as the texture // scale and probably the width and radius) can be generated at // loadtime and is common for all quads on the same level. struct QuadInfo { // Basic info int cellX, cellY; int level; // Bounding box info float minHeight, maxHeight; float worldWidth; float boundingRadius; // Texture scale for this quad float texScale; // True if we should make the given child bool hasChild[4]; // Number of mesh segments in this quad int meshNum; // Location of this quad in the main archive file. The size includes // everything related to this quad, including mesh data, alpha maps, // etc. size_t offset, size; }; struct MeshInfo; const static int CACHE_MAGIC = 0x345AF815; struct ArchiveHeader { // "Magic" number to make sure we're actually reading an archive // file int magic; // Total number of quads in the archive int quads; // Level of the 'root' quad. There will only be one quad on this // level. int rootLevel; // Size of the alpha maps, in pixels along one side. int alphaSize; // Number of strings in the string table int stringNum; // Size of the string buffer size_t stringSize; }; // This class handles the cached terrain data. class TerrainArchive { public: MeshInfo *curMesh; QuadInfo *curQuad; QuadInfo *rootQuad; TerrainArchive() : mappedPtr(0), mappedSize(0), mmf(0), curMesh(0), curQuad(0), rootQuad(0) {} void openFile(const std::string &name) { mmf = new MmFile(name); // Read the index file first std::ifstream ifile((name+".index").c_str(), std::ios::binary); ArchiveHeader head; ifile.read((char*)&head, sizeof(head)); // Sanity check assert(head.magic == CACHE_MAGIC); assert(head.quads > 0 && head.quads < 8192); // Store header info alphaSize = head.alphaSize; // Read all the quads quadList = new QuadInfo[head.quads]; ifile.read((char*)quadList, sizeof(QuadInfo)*head.quads); // Create an index of all the quads for(int qn = 0; qn < head.quads; qn++) { int x = quadList[qn].cellX; int y = quadList[qn].cellY; int l = quadList[qn].level; assert(l >= 1); quadMap[l][x][y] = qn; // Store the root quad if(l == head.rootLevel) { assert(rootQuad == NULL); rootQuad = &quadList[qn]; } else assert(l < head.rootLevel); } // Make sure the root was set assert(rootQuad != NULL); // Next read the string table stringBuf = new char[head.stringSize]; stringOffsets = new int[head.stringNum]; stringNum = head.stringNum; // First read the main string buffer ifile.read(stringBuf, head.stringSize); // Then read the string offsets ifile.read((char*)stringOffsets, stringNum*sizeof(int)); // Read the vertex buffer data int bufNum = head.rootLevel; assert(bufNum == 7); vertBufData.resize(bufNum); indexBufData.resize(bufNum); // Fill the buffers. Start at level 1. for(int i=1;ioffset, info->size); } // Get the info struct for a given segment. Remembers the MeshInfo // for all later calls. MeshInfo *getMeshInfo(int segNum); float *getVertexBuffer(int level) { assert(level>=1 && level=1 && level > > quadMap; // These contain pregenerated mesh data that is common for all // meshes on a given level. std::vector vertBufData; std::vector indexBufData; // Used for the mmapped file MmFile *mmf; uint8_t *mappedPtr; size_t mappedSize; // Stores the string table char *stringBuf; int *stringOffsets; int stringNum; // Texture size of the alpha maps int alphaSize; const char *getString(int index) { assert(index >= 0); assert(index < stringNum); return stringBuf + stringOffsets[index]; } void doMap(size_t offset, size_t size) { assert(mmf != NULL); mappedPtr = (uint8_t*)mmf->map(offset, size); mappedSize = size; assert(mappedPtr != NULL); } // Get the pointer to a given buffer in the mapped window. The // offset is relative to the start of the window, and the size must // fit inside the window. void *getRelPtr(size_t offset, size_t size) { assert(mappedPtr != NULL); // Don't overstep the mapped data assert(offset + size <= mappedSize); return mappedPtr + offset; } // Copy a given buffer from the file. The buffer might be a // compressed stream, so it's important that the buffers are written // the same as they are read. (Ie. you can't write a buffer as one // operation and read it as two, or vice versa. Also, buffers cannot // overlap.) The offset is relative to the current mapped file // window. void copy(void *dest, size_t offset, size_t inSize) { const void *source = getRelPtr(offset, inSize); // Just copy it for now memcpy(dest, source, inSize); } friend struct MeshInfo; friend struct AlphaInfo; } g_archive; // Info about an alpha map belonging to a mesh struct AlphaInfo { size_t bufSize, bufOffset; // The texture name for this layer. The actual string is stored in // the archive's string buffer. int texName; int alphaName; // Fill the alpha texture buffer void fillAlphaBuffer(uint8_t *abuf) const { g_archive.copy(abuf, bufOffset, bufSize); } // Get the texture for this alpha layer const char *getTexName() const { return g_archive.getString(texName); } // Get the material name to give the alpha texture const char *getAlphaName() const { return g_archive.getString(alphaName); } }; // Info about each submesh struct MeshInfo { // Bounding box info float minHeight, maxHeight; float worldWidth; // Vertex and index numbers int vertRows, vertCols; int indexCount; // Scene node position (relative to the parent node) float x, y; // Height offset to apply to all vertices float heightOffset; // Size and offset of the vertex buffer size_t vertBufSize, vertBufOffset; // Number and offset of AlphaInfo blocks int alphaNum; size_t alphaOffset; // Texture name. Index to the string table. int texName; // Fill the given vertex buffer void fillVertexBuffer(float *vbuf) const { //g_archive.copy(vbuf, vertBufOffset, vertBufSize); // The height map and normals from the archive char *hmap = (char*)g_archive.getRelPtr(vertBufOffset, vertBufSize); int level = getLevel(); // The generic part, containing the x,y coordinates and the uv // maps. float *gmap = g_archive.getVertexBuffer(level); // Calculate the factor to multiply each height value with. The // heights are very limited in range as they are stored in a // single byte. Normal MW data uses a factor of 8, but we have to // double this for each successive level since we're splicing // several vertices together and need to allow larger differences // for each vertex. The formula is 8*2^(level-1). float scale = 4.0 * (1<level; } // Get an alpha map belonging to this mesh AlphaInfo *getAlphaInfo(int num) const { assert(num < alphaNum && num >= 0); assert(getLevel() == 1); AlphaInfo *res = (AlphaInfo*)g_archive.getRelPtr (alphaOffset, alphaNum*sizeof(AlphaInfo)); res += num; return res; } // Get the size of the alpha textures (in pixels). int getAlphaSize() const { return g_archive.alphaSize; } // Get the texture and material name to use for this mesh. const char *getTexName() const { return g_archive.getString(texName); } float getTexScale() const { return g_archive.curQuad->texScale; } const char *getBackgroundTex() const { return "_land_default.dds"; } }; MeshInfo *TerrainArchive::getMeshInfo(int segNum) { assert(curQuad); assert(segNum < curQuad->meshNum); // The mesh headers are at the beginning of the mapped segment. curMesh = (MeshInfo*) getRelPtr(0, sizeof(MeshInfo)*curQuad->meshNum); curMesh += segNum; return curMesh; }