forked from mirror/openmw-tes3mp
Started converting terrain code to D.
git-svn-id: https://openmw.svn.sourceforge.net/svnroot/openmw/trunk@119 ea6a568a-9f4f-0410-981a-c910a81bb256actorid
parent
5c523995bb
commit
61030fc382
@ -0,0 +1,463 @@
|
|||||||
|
/*
|
||||||
|
OpenMW - The completely unofficial reimplementation of Morrowind
|
||||||
|
Copyright (C) 2009 Nicolay Korslund
|
||||||
|
WWW: http://openmw.sourceforge.net/
|
||||||
|
|
||||||
|
This file (archive.d) 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/ .
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This should also be part of the generic cache system.
|
||||||
|
const int CACHE_MAGIC = 0x345AF815;
|
||||||
|
|
||||||
|
import std.mmfile;
|
||||||
|
import std.stream;
|
||||||
|
import std.string;
|
||||||
|
|
||||||
|
version(Windows)
|
||||||
|
static int pageSize = 64*1024;
|
||||||
|
else
|
||||||
|
static int pageSize = 4*1024;
|
||||||
|
|
||||||
|
// 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. We could
|
||||||
|
// just make a QuadLevelInfo struct.
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 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(ubyte *abuf)
|
||||||
|
{
|
||||||
|
g_archive.copy(abuf, bufOffset, bufSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the texture for this alpha layer
|
||||||
|
char[] getTexName()
|
||||||
|
{
|
||||||
|
return g_archive.getString(texName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the material name to give the alpha texture
|
||||||
|
char[] getAlphaName()
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
//g_archive.copy(vbuf, vertBufOffset, vertBufSize);
|
||||||
|
|
||||||
|
// The height map and normals from the archive
|
||||||
|
char *hmap = cast(char*)g_archive.getRelSlice(vertBufOffset, vertBufSize).ptr;
|
||||||
|
|
||||||
|
int level = getLevel();
|
||||||
|
|
||||||
|
// The generic part, containing the x,y coordinates and the uv
|
||||||
|
// maps.
|
||||||
|
float *gmap = g_archive.getVertexBuffer(level).ptr;
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// Merge the two data sets together into the output buffer.
|
||||||
|
float offset = heightOffset;
|
||||||
|
for(int y=0; y<vertRows; y++)
|
||||||
|
{
|
||||||
|
// The offset for the entire row is determined by the first
|
||||||
|
// height value. All the values in a row gives the height
|
||||||
|
// relative to the previous value, and the first value in each
|
||||||
|
// row is relative to the first value in the previous row.
|
||||||
|
offset += *hmap;
|
||||||
|
|
||||||
|
// This is the 'sliding offset' for this row. It's adjusted
|
||||||
|
// for each vertex that's added, but only affects this row.
|
||||||
|
float rowofs = offset;
|
||||||
|
for(int x=0; x<vertCols; x++)
|
||||||
|
{
|
||||||
|
hmap++; // Skip the byte we just read
|
||||||
|
|
||||||
|
// X and Y from the pregenerated buffer
|
||||||
|
*vbuf++ = *gmap++;
|
||||||
|
*vbuf++ = *gmap++;
|
||||||
|
|
||||||
|
// The height is calculated from the current offset
|
||||||
|
*vbuf++ = rowofs * scale;
|
||||||
|
|
||||||
|
// Normal vector.
|
||||||
|
// TODO: Normalize?
|
||||||
|
*vbuf++ = *hmap++;
|
||||||
|
*vbuf++ = *hmap++;
|
||||||
|
*vbuf++ = *hmap++;
|
||||||
|
|
||||||
|
// UV
|
||||||
|
*vbuf++ = *gmap++;
|
||||||
|
*vbuf++ = *gmap++;
|
||||||
|
|
||||||
|
// Adjust the offset for the next vertex. On the last
|
||||||
|
// iteration this will read past the current row, but
|
||||||
|
// that's OK since rowofs is discarded afterwards.
|
||||||
|
rowofs += *hmap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill the index buffer
|
||||||
|
void fillIndexBuffer(ushort *ibuf)
|
||||||
|
{
|
||||||
|
// The index buffer is pregenerated. It is identical for all
|
||||||
|
// meshes on the same level, so just copy it over.
|
||||||
|
ushort generic[] = g_archive.getIndexBuffer(getLevel());
|
||||||
|
ibuf[0..generic.length] = generic[];
|
||||||
|
}
|
||||||
|
|
||||||
|
int getLevel()
|
||||||
|
{
|
||||||
|
assert(g_archive.curQuad);
|
||||||
|
return g_archive.curQuad.level;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get an alpha map belonging to this mesh
|
||||||
|
AlphaInfo *getAlphaInfo(int num)
|
||||||
|
{
|
||||||
|
assert(num < alphaNum && num >= 0);
|
||||||
|
assert(getLevel() == 1);
|
||||||
|
AlphaInfo *res = cast(AlphaInfo*)g_archive.getRelSlice
|
||||||
|
(alphaOffset, alphaNum*AlphaInfo.sizeof);
|
||||||
|
res += num;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the size of the alpha textures (in pixels).
|
||||||
|
int getAlphaSize()
|
||||||
|
{ return g_archive.alphaSize; }
|
||||||
|
|
||||||
|
// Get the texture and material name to use for this mesh.
|
||||||
|
char[] getTexName()
|
||||||
|
{ return g_archive.getString(texName); }
|
||||||
|
|
||||||
|
float getTexScale()
|
||||||
|
{ return g_archive.curQuad.texScale; }
|
||||||
|
|
||||||
|
char[] getBackgroundTex()
|
||||||
|
{ return "_land_default.dds"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
TerrainArchive g_archive;
|
||||||
|
|
||||||
|
// This class handles the cached terrain data.
|
||||||
|
struct TerrainArchive
|
||||||
|
{
|
||||||
|
MeshInfo *curMesh;
|
||||||
|
QuadInfo *curQuad;
|
||||||
|
QuadInfo *rootQuad;
|
||||||
|
|
||||||
|
void openFile(char[] name)
|
||||||
|
{
|
||||||
|
mmf = new MmFile(name,
|
||||||
|
MmFile.Mode.Read,
|
||||||
|
0, null, pageSize);
|
||||||
|
|
||||||
|
// Read the index file first
|
||||||
|
File ifile = new File(name ~ ".index");
|
||||||
|
|
||||||
|
ArchiveHeader head;
|
||||||
|
ifile.readExact(&head, head.sizeof);
|
||||||
|
|
||||||
|
// 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.readExact(quadList.ptr, head.quads*QuadInfo.sizeof);
|
||||||
|
|
||||||
|
// Create an index of all the quads
|
||||||
|
foreach(int index, qn; quadList)
|
||||||
|
{
|
||||||
|
int x = qn.cellX;
|
||||||
|
int y = qn.cellY;
|
||||||
|
int l = qn.level;
|
||||||
|
|
||||||
|
assert(l >= 1);
|
||||||
|
|
||||||
|
quadMap[l][x][y] = index;
|
||||||
|
|
||||||
|
// Store the root quad
|
||||||
|
if(l == head.rootLevel)
|
||||||
|
{
|
||||||
|
assert(rootQuad == null);
|
||||||
|
rootQuad = &quadList[index];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
assert(l < head.rootLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the root was set
|
||||||
|
assert(rootQuad !is null);
|
||||||
|
|
||||||
|
// Next read the string table
|
||||||
|
stringBuf = new char[head.stringSize];
|
||||||
|
strings.length = head.stringNum;
|
||||||
|
|
||||||
|
// First read the main string buffer
|
||||||
|
ifile.readExact(stringBuf.ptr, head.stringSize);
|
||||||
|
|
||||||
|
// Then read the string offsets
|
||||||
|
int[] offsets = new int[head.stringNum];
|
||||||
|
ifile.readExact(offsets.ptr, offsets.length*int.sizeof);
|
||||||
|
|
||||||
|
// Set up the string table
|
||||||
|
char *strptr = stringBuf.ptr;
|
||||||
|
foreach(int i, ref str; strings)
|
||||||
|
{
|
||||||
|
// toString(char*) returns the string up to the zero
|
||||||
|
// terminator byte
|
||||||
|
str = toString(strptr + offsets[i]);
|
||||||
|
}
|
||||||
|
delete offsets;
|
||||||
|
|
||||||
|
// Read the vertex buffer data
|
||||||
|
int bufNum = head.rootLevel;
|
||||||
|
assert(bufNum == 7);
|
||||||
|
vertBufData.length = bufNum;
|
||||||
|
indexBufData.length = bufNum;
|
||||||
|
|
||||||
|
// Fill the buffers. Start at level 1.
|
||||||
|
for(int i=1;i<bufNum;i++)
|
||||||
|
{
|
||||||
|
int size;
|
||||||
|
|
||||||
|
// Vertex buffer
|
||||||
|
ifile.read(size);
|
||||||
|
vertBufData[i].length = size;
|
||||||
|
ifile.readExact(vertBufData[i].ptr, size);
|
||||||
|
|
||||||
|
// Index buffer
|
||||||
|
ifile.read(size);
|
||||||
|
indexBufData[i].length = size;
|
||||||
|
ifile.readExact(indexBufData[i].ptr, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get info about a given quad from the index.
|
||||||
|
QuadInfo *getQuad(int X, int Y, int level)
|
||||||
|
{
|
||||||
|
int ind = quadMap[level][X][Y];
|
||||||
|
QuadInfo *res = &quadList[ind];
|
||||||
|
assert(res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maps the terrain and material info for a given quad into
|
||||||
|
// memory. This is typically called right before the meshes are
|
||||||
|
// created.
|
||||||
|
void mapQuad(QuadInfo *info)
|
||||||
|
{
|
||||||
|
assert(info);
|
||||||
|
|
||||||
|
// Store the quad for later
|
||||||
|
curQuad = info;
|
||||||
|
|
||||||
|
doMap(info.offset, info.size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the info struct for a given segment. Remembers the MeshInfo
|
||||||
|
// for all later calls.
|
||||||
|
MeshInfo *getMeshInfo(int segNum)
|
||||||
|
{
|
||||||
|
assert(curQuad);
|
||||||
|
assert(segNum < curQuad.meshNum);
|
||||||
|
|
||||||
|
// The mesh headers are at the beginning of the mapped segment.
|
||||||
|
curMesh = cast(MeshInfo*) getRelSlice(0, MeshInfo.sizeof*curQuad.meshNum);
|
||||||
|
curMesh += segNum;
|
||||||
|
|
||||||
|
return curMesh;
|
||||||
|
}
|
||||||
|
|
||||||
|
float[] getVertexBuffer(int level)
|
||||||
|
{
|
||||||
|
assert(level>=1 && level<vertBufData.length);
|
||||||
|
return vertBufData[level];
|
||||||
|
}
|
||||||
|
|
||||||
|
ushort[] getIndexBuffer(int level)
|
||||||
|
{
|
||||||
|
assert(level>=1 && level<indexBufData.length);
|
||||||
|
return indexBufData[level];
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// All quad headers (from the index) are stored in this array
|
||||||
|
QuadInfo quadList[];
|
||||||
|
|
||||||
|
// A map of all quads. Contain indices to the above array. Indexed
|
||||||
|
// by [level][X][Y].
|
||||||
|
int[int][int][int] quadMap;
|
||||||
|
|
||||||
|
// These contain pregenerated mesh data that is common for all
|
||||||
|
// meshes on a given level.
|
||||||
|
float[][] vertBufData;
|
||||||
|
ushort[][] indexBufData;
|
||||||
|
|
||||||
|
// Used for the mmapped file
|
||||||
|
MmFile mmf;
|
||||||
|
|
||||||
|
ubyte mapped[];
|
||||||
|
|
||||||
|
// Stores the string table
|
||||||
|
char[] stringBuf;
|
||||||
|
char[][] strings;
|
||||||
|
|
||||||
|
// Texture size of the alpha maps.
|
||||||
|
int alphaSize;
|
||||||
|
|
||||||
|
char[] getString(int index)
|
||||||
|
{
|
||||||
|
assert(index >= 0);
|
||||||
|
assert(index < strings.length);
|
||||||
|
|
||||||
|
return strings[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
void doMap(size_t offset, size_t size)
|
||||||
|
{
|
||||||
|
assert(mmf !is null);
|
||||||
|
assert(size);
|
||||||
|
mapped = cast(ubyte[])mmf[offset..offset+size];
|
||||||
|
assert(mapped.length == size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a slice of a given buffer within the mapped window. The
|
||||||
|
// offset is relative to the start of the window, and the size must
|
||||||
|
// fit inside the window.
|
||||||
|
ubyte[] getRelSlice(size_t offset, size_t size)
|
||||||
|
{
|
||||||
|
assert(mapped.length);
|
||||||
|
|
||||||
|
return mapped[offset..offset+size];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 *dst, size_t offset, size_t inSize)
|
||||||
|
{
|
||||||
|
ubyte source[] = getRelSlice(offset, inSize);
|
||||||
|
|
||||||
|
// Just copy it for now
|
||||||
|
ubyte* dest = cast(ubyte*)dst;
|
||||||
|
dest[0..source.length] = source[];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,331 @@
|
|||||||
|
/*
|
||||||
|
OpenMW - The completely unofficial reimplementation of Morrowind
|
||||||
|
Copyright (C) 2009 Nicolay Korslund
|
||||||
|
WWW: http://openmw.sourceforge.net/
|
||||||
|
|
||||||
|
This file (cachewriter.d) 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/ .
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
import terrain.archive;
|
||||||
|
|
||||||
|
import terrain.outbuffer;
|
||||||
|
import std.stream;
|
||||||
|
import monster.util.string;
|
||||||
|
|
||||||
|
// Helper structs
|
||||||
|
struct AlphaHolder
|
||||||
|
{
|
||||||
|
AlphaInfo info;
|
||||||
|
|
||||||
|
// Actual pixel buffer
|
||||||
|
ubyte[] buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MeshHolder
|
||||||
|
{
|
||||||
|
MeshInfo info;
|
||||||
|
|
||||||
|
// Actual buffers
|
||||||
|
byte[] vertexBuffer;
|
||||||
|
|
||||||
|
// Texture name
|
||||||
|
char[] texName;
|
||||||
|
|
||||||
|
// Alpha maps (if any)
|
||||||
|
AlphaHolder alphas[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// A struct that gathers all the relevant quad data in one place.
|
||||||
|
struct QuadHolder
|
||||||
|
{
|
||||||
|
QuadInfo info;
|
||||||
|
|
||||||
|
MeshHolder meshes[];
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CacheWriter
|
||||||
|
{
|
||||||
|
// Opens the main archive file for output
|
||||||
|
void openFile(char[] fname)
|
||||||
|
{
|
||||||
|
mainFile = new File(fname, FileMode.OutNew);
|
||||||
|
iname = fname ~ ".index";
|
||||||
|
|
||||||
|
buf = new OutBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setParams(int mxLev, int alphSize)
|
||||||
|
{
|
||||||
|
maxLevel = mxLev;
|
||||||
|
alphaSize = alphSize;
|
||||||
|
|
||||||
|
vertBuf.length = maxLevel;
|
||||||
|
indexBuf.length = maxLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Closes the main archive file and writes the index.
|
||||||
|
void finish()
|
||||||
|
{
|
||||||
|
mainFile.close();
|
||||||
|
|
||||||
|
// Write the index file
|
||||||
|
scope File ofile = new File(iname, FileMode.OutNew);
|
||||||
|
|
||||||
|
// Header first
|
||||||
|
ArchiveHeader head;
|
||||||
|
head.magic = CACHE_MAGIC;
|
||||||
|
head.quads = quadList.length;
|
||||||
|
head.rootLevel = maxLevel;
|
||||||
|
head.alphaSize = alphaSize;
|
||||||
|
head.stringNum = stringList.length;
|
||||||
|
head.stringSize = totalStringLength;
|
||||||
|
ofile.writeExact(&head, head.sizeof);
|
||||||
|
|
||||||
|
// Write the quads
|
||||||
|
foreach(qi; quadList)
|
||||||
|
ofile.writeExact(&qi, qi.sizeof);
|
||||||
|
|
||||||
|
// String table next. We need to sort it in order of the indices
|
||||||
|
// first.
|
||||||
|
char[][] strVector;
|
||||||
|
strVector.length = head.stringNum;
|
||||||
|
|
||||||
|
foreach(char[] key, int value; stringList)
|
||||||
|
strVector[value] = key;
|
||||||
|
|
||||||
|
// Next, write the strings to file while we fill in the offset
|
||||||
|
// list
|
||||||
|
int[] offsets = new int[head.stringNum];
|
||||||
|
|
||||||
|
size_t curOffs = 0;
|
||||||
|
for(int i=0; i<head.stringNum; i++)
|
||||||
|
{
|
||||||
|
// Add one byte for the zero terminator
|
||||||
|
int len = strVector[i].length + 1;
|
||||||
|
char *ptr = strVector[i].ptr;
|
||||||
|
assert(ptr[len-1] == 0);
|
||||||
|
|
||||||
|
ofile.writeExact(ptr, len);
|
||||||
|
|
||||||
|
// Store the offset
|
||||||
|
offsets[i] = curOffs;
|
||||||
|
curOffs += len;
|
||||||
|
}
|
||||||
|
// At the end the offset should match the buffer size we set in
|
||||||
|
// the header.
|
||||||
|
assert(curOffs == head.stringSize);
|
||||||
|
|
||||||
|
// Finally, write the offset table itself
|
||||||
|
ofile.writeExact(offsets.ptr, offsets.length * int.sizeof);
|
||||||
|
|
||||||
|
// Write the common vertex and index buffers
|
||||||
|
for(int i=1;i<maxLevel;i++)
|
||||||
|
{
|
||||||
|
int size;
|
||||||
|
void *ptr;
|
||||||
|
|
||||||
|
// Write vertex buffer
|
||||||
|
ptr = vertBuf[i].ptr;
|
||||||
|
size = vertBuf[i].length;
|
||||||
|
ofile.write(size);
|
||||||
|
ofile.writeExact(ptr, size);
|
||||||
|
|
||||||
|
// Then the index buffer
|
||||||
|
ptr = indexBuf[i].ptr;
|
||||||
|
size = indexBuf[i].length;
|
||||||
|
ofile.write(size);
|
||||||
|
ofile.writeExact(ptr, size);
|
||||||
|
|
||||||
|
delete vertBuf[i];
|
||||||
|
delete indexBuf[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't need these anymore
|
||||||
|
delete offsets;
|
||||||
|
delete strVector;
|
||||||
|
delete quadList;
|
||||||
|
delete vertBuf;
|
||||||
|
delete indexBuf;
|
||||||
|
delete buf;
|
||||||
|
delete mainFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a common vertex buffer for a given level
|
||||||
|
void addVertexBuffer(int level, void[] buf)
|
||||||
|
{
|
||||||
|
assert(vertBuf.length > level);
|
||||||
|
vertBuf[level] = buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a common vertex buffer for a given level
|
||||||
|
void addIndexBuffer(int level, void[] buf)
|
||||||
|
{
|
||||||
|
assert(indexBuf.length > level);
|
||||||
|
indexBuf[level] = buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write a finished quad to the archive file. All the offsets and
|
||||||
|
// numbers in the *Info structs are filled in automatically based on
|
||||||
|
// the additional data in the Holder structs.
|
||||||
|
void writeQuad(ref QuadHolder qh)
|
||||||
|
{
|
||||||
|
// Make outbuffer a simple struct that uses a region and keeps
|
||||||
|
// track of all the slices we allocate.
|
||||||
|
OutBuffer buf;
|
||||||
|
|
||||||
|
// Write the MeshInfo's first
|
||||||
|
int meshNum = qh.meshes.length;
|
||||||
|
|
||||||
|
MeshInfo meshes[] = buf.write!(MeshInfo)(meshNum);
|
||||||
|
|
||||||
|
// Then write the mesh data in approximately the order it's read
|
||||||
|
for(int i=0; i<meshNum; i++)
|
||||||
|
{
|
||||||
|
assert(meshes !is null);
|
||||||
|
|
||||||
|
auto mh = &qh.meshes[i];
|
||||||
|
|
||||||
|
// Copy the basic data first
|
||||||
|
meshes[i] = mh.info;
|
||||||
|
|
||||||
|
// Set everything else except the offsets
|
||||||
|
int alphaNum = mh.alphas.length;
|
||||||
|
meshes[i].alphaNum = alphaNum;
|
||||||
|
//meshes[i].texName = addString(mh.texName);
|
||||||
|
|
||||||
|
// Write the vertex buffer
|
||||||
|
meshes[i].vertBufOffset = buf.size;
|
||||||
|
meshes[i].vertBufSize = mh.vertexBuffer.length;
|
||||||
|
writeBuf(mh.vertexBuffer);
|
||||||
|
|
||||||
|
// Next write the alpha maps, if any
|
||||||
|
meshes[i].alphaOffset = buf.size;
|
||||||
|
AlphaInfo ais[] = buf.write!(AlphaInfo)(alphaNum);
|
||||||
|
|
||||||
|
// Loop through the alpha maps
|
||||||
|
foreach(int k, ref ai; ais)
|
||||||
|
{
|
||||||
|
AlphaHolder ah = mh.alphas[k];
|
||||||
|
ai = ah.info;
|
||||||
|
|
||||||
|
// Write the alpha pixel buffer
|
||||||
|
ai.bufOffset = buf.size;
|
||||||
|
ai.bufSize = ah.buffer.length;
|
||||||
|
writeBuf(ah.buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Finally set up the QuadInfo itself
|
||||||
|
QuadInfo qi;
|
||||||
|
|
||||||
|
// Basic info
|
||||||
|
qi = qh.info;
|
||||||
|
|
||||||
|
// Derived info
|
||||||
|
qi.meshNum = meshNum;
|
||||||
|
qi.offset = fileOffset;
|
||||||
|
qi.size = buf.size;
|
||||||
|
|
||||||
|
// The quad cache is done, write it to file
|
||||||
|
buf.writeTo(mainFile);
|
||||||
|
|
||||||
|
// Update the main offset
|
||||||
|
fileOffset += qi.size;
|
||||||
|
|
||||||
|
// Add the quad to the list. This list isn't written to the main
|
||||||
|
// cache file, but to the index file.
|
||||||
|
quadList ~= qi;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a texture name as a string. Will convert .tga file names to
|
||||||
|
// .dds as a convenience. TODO: Use the resource system to do this,
|
||||||
|
// it automatically searches for the dds variant.
|
||||||
|
int addTexture(char[] orig)
|
||||||
|
{
|
||||||
|
if(orig.iEnds(".tga"))
|
||||||
|
orig = orig[0..$-3] ~ "dds";
|
||||||
|
return addString(orig);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert a string to an index
|
||||||
|
int addString(char[] str)
|
||||||
|
{
|
||||||
|
// Do we already have the string?
|
||||||
|
if(str in stringList)
|
||||||
|
return stringList[str];
|
||||||
|
|
||||||
|
// Nope, insert it
|
||||||
|
int index = stringList.length;
|
||||||
|
stringList[str] = index;
|
||||||
|
stringLookup[index] = str;
|
||||||
|
|
||||||
|
// Sum up the string lengths + 1 byte for the zero
|
||||||
|
totalStringLength += str.length + 1;
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
char[] getString(int index)
|
||||||
|
{
|
||||||
|
char[] res = stringLookup[index];
|
||||||
|
assert(stringList[res] == index);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Write the given block of memory to 'buf', possibly compressing
|
||||||
|
// the data.
|
||||||
|
void writeBuf(void[] ptr)
|
||||||
|
{
|
||||||
|
ulong size = ptr.length;
|
||||||
|
|
||||||
|
// Reserve the maximum bytes needed.
|
||||||
|
void toPtr[] = buf.reserve(size);
|
||||||
|
|
||||||
|
// Store the data
|
||||||
|
toPtr[] = ptr[];
|
||||||
|
|
||||||
|
// Add the result buffer
|
||||||
|
buf.add(toPtr[0..size]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used for 'writing' to a changable memory buffer before writing to
|
||||||
|
// file
|
||||||
|
OutBuffer buf;
|
||||||
|
|
||||||
|
// Common vertex and index buffers for all quads. One buffer per
|
||||||
|
// level.
|
||||||
|
void[][] vertBuf;
|
||||||
|
void[][] indexBuf;
|
||||||
|
|
||||||
|
// Variables that must be set during the gen phase
|
||||||
|
int maxLevel;
|
||||||
|
int alphaSize;
|
||||||
|
|
||||||
|
// Contains a unique index for each string
|
||||||
|
int[char[]] stringList;
|
||||||
|
char[][int] stringLookup;
|
||||||
|
size_t totalStringLength;
|
||||||
|
|
||||||
|
// List of all quads
|
||||||
|
QuadInfo[] quadList;
|
||||||
|
|
||||||
|
// Output file
|
||||||
|
File mainFile;
|
||||||
|
size_t fileOffset;
|
||||||
|
|
||||||
|
// Index file name
|
||||||
|
char[] iname;
|
||||||
|
}
|
@ -1,335 +0,0 @@
|
|||||||
/*
|
|
||||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
|
||||||
Copyright (C) 2009 Nicolay Korslund
|
|
||||||
WWW: http://openmw.sourceforge.net/
|
|
||||||
|
|
||||||
This file (cpp_cachewriter.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/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Helper structs
|
|
||||||
struct AlphaHolder
|
|
||||||
{
|
|
||||||
AlphaInfo info;
|
|
||||||
|
|
||||||
// Actual pixel buffer
|
|
||||||
unsigned char *buffer;
|
|
||||||
|
|
||||||
// Texture name and alpha material name to use
|
|
||||||
//std::string texName, alphaName;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct MeshHolder
|
|
||||||
{
|
|
||||||
MeshInfo info;
|
|
||||||
|
|
||||||
// Actual buffers
|
|
||||||
char *vertexBuffer;
|
|
||||||
|
|
||||||
// Texture name
|
|
||||||
std::string texName;
|
|
||||||
|
|
||||||
// Alpha maps (if any)
|
|
||||||
std::vector<AlphaHolder> alphas;
|
|
||||||
};
|
|
||||||
|
|
||||||
// A struct that gathers all the relevant quad data in one place.
|
|
||||||
struct QuadHolder
|
|
||||||
{
|
|
||||||
QuadInfo info;
|
|
||||||
|
|
||||||
std::vector<MeshHolder> meshes;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CacheWriter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
// Opens the main archive file for output
|
|
||||||
void openFile(const std::string &fname)
|
|
||||||
{
|
|
||||||
mainFile.open(fname.c_str(), std::ios::binary);
|
|
||||||
iname = fname + ".index";
|
|
||||||
fileOffset = 0;
|
|
||||||
totalStringLength = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setParams(int mxLev, int alphSize)
|
|
||||||
{
|
|
||||||
maxLevel = mxLev;
|
|
||||||
alphaSize = alphSize;
|
|
||||||
|
|
||||||
vertBufData.resize(maxLevel);
|
|
||||||
indexBufData.resize(maxLevel);
|
|
||||||
vertBufSize.resize(maxLevel);
|
|
||||||
indexBufSize.resize(maxLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Closes the main archive file and writes the index.
|
|
||||||
void finish()
|
|
||||||
{
|
|
||||||
mainFile.close();
|
|
||||||
|
|
||||||
// Write the index file
|
|
||||||
std::ofstream ofile(iname.c_str(), std::ios::binary);
|
|
||||||
|
|
||||||
// Header first
|
|
||||||
ArchiveHeader head;
|
|
||||||
head.magic = CACHE_MAGIC;
|
|
||||||
head.quads = quadList.size();
|
|
||||||
head.rootLevel = maxLevel;
|
|
||||||
head.alphaSize = alphaSize;
|
|
||||||
head.stringNum = stringList.size();
|
|
||||||
head.stringSize = totalStringLength;
|
|
||||||
ofile.write((char*)&head, sizeof(head));
|
|
||||||
|
|
||||||
// Write the quads
|
|
||||||
for(QuadList::iterator it = quadList.begin();
|
|
||||||
it != quadList.end(); it++)
|
|
||||||
{
|
|
||||||
QuadInfo qi = *it;
|
|
||||||
ofile.write((char*)&qi, sizeof(QuadInfo));
|
|
||||||
}
|
|
||||||
|
|
||||||
// String table next. We need to sort it in order of the indices
|
|
||||||
// first.
|
|
||||||
std::vector<std::string> strVector;
|
|
||||||
strVector.resize(head.stringNum);
|
|
||||||
|
|
||||||
for(StringList::iterator it = stringList.begin();
|
|
||||||
it != stringList.end(); it++)
|
|
||||||
{
|
|
||||||
strVector[it->second] = it->first;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next, write the strings to file while we fill inn the offset
|
|
||||||
// list
|
|
||||||
std::vector<int> offsets;
|
|
||||||
offsets.resize(head.stringNum);
|
|
||||||
size_t curOffs = 0;
|
|
||||||
|
|
||||||
for(int i=0; i<head.stringNum; i++)
|
|
||||||
{
|
|
||||||
// Add one byte for the zero terminator
|
|
||||||
int len = strVector[i].length() + 1;
|
|
||||||
const char *ptr = strVector[i].c_str();
|
|
||||||
assert(ptr[len-1] == 0);
|
|
||||||
|
|
||||||
ofile.write(ptr, len);
|
|
||||||
|
|
||||||
// Store the offset
|
|
||||||
offsets[i] = curOffs;
|
|
||||||
curOffs += len;
|
|
||||||
}
|
|
||||||
// At the end the offset should match the buffer size we set in
|
|
||||||
// the header.
|
|
||||||
assert(curOffs == head.stringSize);
|
|
||||||
|
|
||||||
// Finally, write the offset table itself
|
|
||||||
for(int i=0; i<head.stringNum; i++)
|
|
||||||
{
|
|
||||||
int offs = offsets[i];
|
|
||||||
ofile.write((char*)&offs, sizeof(int));
|
|
||||||
}
|
|
||||||
|
|
||||||
for(int i=1;i<maxLevel;i++)
|
|
||||||
{
|
|
||||||
int size;
|
|
||||||
void *ptr;
|
|
||||||
|
|
||||||
// Write vertex buffer
|
|
||||||
size = vertBufSize[i];
|
|
||||||
ptr = vertBufData[i];
|
|
||||||
ofile.write((char*)&size, sizeof(int));
|
|
||||||
ofile.write((char*)ptr, size);
|
|
||||||
|
|
||||||
// Then the index buffer
|
|
||||||
size = indexBufSize[i];
|
|
||||||
ptr = indexBufData[i];
|
|
||||||
ofile.write((char*)&size, sizeof(int));
|
|
||||||
ofile.write((char*)ptr, size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a common vertex buffer for a given level
|
|
||||||
void addVertexBuffer(int level, void *ptr, int size)
|
|
||||||
{
|
|
||||||
assert(vertBufData.size() > level);
|
|
||||||
|
|
||||||
vertBufData[level] = ptr;
|
|
||||||
vertBufSize[level] = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a common vertex buffer for a given level
|
|
||||||
void addIndexBuffer(int level, void *ptr, int size)
|
|
||||||
{
|
|
||||||
assert(indexBufData.size() > level);
|
|
||||||
|
|
||||||
indexBufData[level] = ptr;
|
|
||||||
indexBufSize[level] = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write a finished quad to the archive file. All the offsets and
|
|
||||||
// numbers in the *Info structs are filled in automatically based on
|
|
||||||
// the additional data in the Holder structs.
|
|
||||||
void writeQuad(const QuadHolder &qh)
|
|
||||||
{
|
|
||||||
TRACE("writeQuad");
|
|
||||||
|
|
||||||
// See util/outbuffer.h
|
|
||||||
OutBuffer buf;
|
|
||||||
|
|
||||||
// Write the MeshInfo's first
|
|
||||||
int meshNum = qh.meshes.size();
|
|
||||||
|
|
||||||
MeshInfo *meshes = buf.write<MeshInfo>(meshNum);
|
|
||||||
|
|
||||||
// Then write the mesh data in approximately the order it's read
|
|
||||||
for(int i=0; i<meshNum; i++)
|
|
||||||
{
|
|
||||||
assert(meshes != NULL);
|
|
||||||
|
|
||||||
const MeshHolder &mh = qh.meshes[i];
|
|
||||||
|
|
||||||
// Copy the basic data first
|
|
||||||
meshes[i] = mh.info;
|
|
||||||
|
|
||||||
// Set everything else except the offsets
|
|
||||||
int alphaNum = mh.alphas.size();
|
|
||||||
meshes[i].alphaNum = alphaNum;
|
|
||||||
//meshes[i].texName = addString(mh.texName);
|
|
||||||
|
|
||||||
// Write the vertex buffer
|
|
||||||
meshes[i].vertBufOffset = buf.size();
|
|
||||||
writeBuf(buf, mh.vertexBuffer, meshes[i].vertBufSize);
|
|
||||||
|
|
||||||
// Next write the alpha maps, if any
|
|
||||||
meshes[i].alphaOffset = buf.size();
|
|
||||||
AlphaInfo *ai = buf.write<AlphaInfo>(alphaNum);
|
|
||||||
|
|
||||||
// Loop through the alpha maps
|
|
||||||
for(int k=0; k<alphaNum; k++)
|
|
||||||
{
|
|
||||||
AlphaHolder ah = mh.alphas[k];
|
|
||||||
ai[k] = ah.info;
|
|
||||||
|
|
||||||
// Convert the strings
|
|
||||||
// KILLME
|
|
||||||
//ai[k].texName = addString(ah.texName);
|
|
||||||
//ai[k].alphaName = addString(ah.alphaName);
|
|
||||||
|
|
||||||
// Write the alpha pixel buffer
|
|
||||||
ai[k].bufOffset = buf.size();
|
|
||||||
writeBuf(buf, ah.buffer, ai[k].bufSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// The quad cache is done, write it to file
|
|
||||||
mainFile << buf;
|
|
||||||
|
|
||||||
// Finally set up the QuadInfo itself
|
|
||||||
QuadInfo qi;
|
|
||||||
|
|
||||||
// Basic info
|
|
||||||
qi = qh.info;
|
|
||||||
|
|
||||||
// Derived info
|
|
||||||
qi.meshNum = meshNum;
|
|
||||||
qi.size = buf.size();
|
|
||||||
qi.offset = fileOffset;
|
|
||||||
|
|
||||||
// Update the main offset
|
|
||||||
fileOffset += qi.size;
|
|
||||||
|
|
||||||
// Add the quad to the index list
|
|
||||||
quadList.push_back(qi);
|
|
||||||
|
|
||||||
std::cout << "end\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a texture name as a string. Will convert .tga file names to
|
|
||||||
// .dds as a convenience
|
|
||||||
int addTexture(const std::string &orig)
|
|
||||||
{
|
|
||||||
size_t d = orig.find_last_of(".") + 1;
|
|
||||||
return addString(orig.substr(0, d) + "dds");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert a string to an index
|
|
||||||
int addString(const std::string &str)
|
|
||||||
{
|
|
||||||
// Do we already have the string?
|
|
||||||
StringList::iterator it = stringList.find(str);
|
|
||||||
if(it != stringList.end())
|
|
||||||
return it->second;
|
|
||||||
|
|
||||||
// Nope, insert it
|
|
||||||
int index = stringList.size();
|
|
||||||
stringList[str] = index;
|
|
||||||
stringLookup[index] = str;
|
|
||||||
|
|
||||||
// Sum up the string lengths + 1 byte for the zero
|
|
||||||
totalStringLength += str.length() + 1;
|
|
||||||
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string &getString(int index)
|
|
||||||
{
|
|
||||||
const std::string &res = stringLookup[index];
|
|
||||||
assert(stringList[res] == index);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Write the given block of memory to 'buf', possibly compressing
|
|
||||||
// the data.
|
|
||||||
void writeBuf(OutBuffer &buf, const void *ptr, size_t size)
|
|
||||||
{
|
|
||||||
// Reserve the maximum bytes needed.
|
|
||||||
void *toPtr = buf.reserve(size);
|
|
||||||
|
|
||||||
// Store the data
|
|
||||||
memcpy(toPtr, ptr, size);
|
|
||||||
|
|
||||||
// Add the actual number of bytes stored
|
|
||||||
buf.add(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<void*> vertBufData;
|
|
||||||
std::vector<void*> indexBufData;
|
|
||||||
std::vector<int> vertBufSize;
|
|
||||||
std::vector<int> indexBufSize;
|
|
||||||
|
|
||||||
// Variables that must be set during the gen phase
|
|
||||||
int maxLevel;
|
|
||||||
int alphaSize;
|
|
||||||
|
|
||||||
// Contains a unique index for each string
|
|
||||||
typedef std::map<std::string, int> StringList;
|
|
||||||
StringList stringList;
|
|
||||||
std::map<int, std::string> stringLookup;
|
|
||||||
size_t totalStringLength;
|
|
||||||
|
|
||||||
// List of all quads
|
|
||||||
typedef std::list<QuadInfo> QuadList;
|
|
||||||
QuadList quadList;
|
|
||||||
|
|
||||||
// Output file
|
|
||||||
std::ofstream mainFile;
|
|
||||||
size_t fileOffset;
|
|
||||||
|
|
||||||
// Index file name
|
|
||||||
std::string iname;
|
|
||||||
};
|
|
@ -1,52 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (c) Jacob Essex 2009
|
|
||||||
|
|
||||||
This file is part of MWLand.
|
|
||||||
|
|
||||||
MWLand is free software: you can redistribute it and/or modify it
|
|
||||||
under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
MWLand 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
|
|
||||||
along with MWLand. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief utility class for holding two values
|
|
||||||
*
|
|
||||||
* This is mainly used for querying the position of the quad.
|
|
||||||
* Each quad has a center position, which we can use as a unique identifier
|
|
||||||
*/
|
|
||||||
template<class T>
|
|
||||||
struct Point2 {
|
|
||||||
T x, y; //held values.
|
|
||||||
|
|
||||||
inline Point2() {}
|
|
||||||
inline Point2(T ix, T iy) {
|
|
||||||
x = ix;
|
|
||||||
y = iy;
|
|
||||||
}
|
|
||||||
inline Point2(const Point2<T>& i) {
|
|
||||||
x = i.x;
|
|
||||||
y = i.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief comparison operator. Although not used directly, this
|
|
||||||
* class is used in std::map a lot, which used the < operator
|
|
||||||
*/
|
|
||||||
|
|
||||||
inline bool operator<(const Point2<T>& rhs) const{
|
|
||||||
return ( x < rhs.x || !( rhs.x < x) && y < rhs.y );
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Point2 operator + (const Point2<T>& rhs) {
|
|
||||||
return Point2(x + rhs.x, y + rhs.y);
|
|
||||||
}
|
|
||||||
};
|
|
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
OpenMW - The completely unofficial reimplementation of Morrowind
|
||||||
|
Copyright (C) 2008-2009 Nicolay Korslund
|
||||||
|
Email: < korslund@gmail.com >
|
||||||
|
WWW: http://openmw.snaptoad.com/
|
||||||
|
|
||||||
|
This file (generator.d) 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/ .
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This module is responsible for generating the cache files.
|
||||||
|
module terrain.generator;
|
||||||
|
|
||||||
|
import std.stdio;
|
||||||
|
import std.file;
|
||||||
|
import monster.util.string;
|
||||||
|
|
||||||
|
char[] cacheDir = "cache/terrain/";
|
||||||
|
|
||||||
|
void generate()
|
||||||
|
{
|
||||||
|
makePath(cacheDir);
|
||||||
|
terr_setCacheDir(cacheDir.ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move elsewhere, make part of the general cache system later
|
||||||
|
void makeDir(char[] pt)
|
||||||
|
{
|
||||||
|
if(exists(pt))
|
||||||
|
{
|
||||||
|
if(!isdir(pt))
|
||||||
|
fail(pt ~ " is not a directory");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
mkdir(pt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fail(char[] msg)
|
||||||
|
{
|
||||||
|
throw new Exception(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void makePath(char[] pt)
|
||||||
|
{
|
||||||
|
assert(!pt.begins("/"));
|
||||||
|
foreach(int i, char c; pt)
|
||||||
|
if(c == '/')
|
||||||
|
makeDir(pt[0..i]);
|
||||||
|
|
||||||
|
if(!pt.ends("/"))
|
||||||
|
makeDir(pt);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern(C):
|
||||||
|
void terr_setCacheDir(char *dir);
|
@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
OpenMW - The completely unofficial reimplementation of Morrowind
|
||||||
|
Copyright (C) 2008-2009 Nicolay Korslund
|
||||||
|
Email: < korslund@gmail.com >
|
||||||
|
WWW: http://openmw.sourceforge.net/
|
||||||
|
|
||||||
|
This file (outbuffer.d) 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/ .
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
This files provides a simple buffer class used for writing the cache
|
||||||
|
files. It lets you 'write' data to a growing memory buffer and
|
||||||
|
allows you to change the written data after the fact (since it's
|
||||||
|
retained in memory.) When you're done, you can write the entire
|
||||||
|
buffer to a stream in one operation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import util.regions;
|
||||||
|
import std.stream;
|
||||||
|
|
||||||
|
class OutBuffer
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
RegionManager reg;
|
||||||
|
long used;
|
||||||
|
void[][] buffers;
|
||||||
|
|
||||||
|
public:
|
||||||
|
this()
|
||||||
|
{
|
||||||
|
reg = new RegionManager("Outbuf", 200*1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
if(buffers.length)
|
||||||
|
delete buffers;
|
||||||
|
|
||||||
|
reg.freeAll();
|
||||||
|
used = 0;
|
||||||
|
buffers = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write everyting to a stream as one buffer
|
||||||
|
void writeTo(Stream str)
|
||||||
|
{
|
||||||
|
foreach(void[] v; buffers)
|
||||||
|
str.writeExact(v.ptr, v.length);
|
||||||
|
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a pointer to a new block at least 'bytes' large, but don't
|
||||||
|
// add it to the list.
|
||||||
|
void[] reserve(size_t bytes)
|
||||||
|
{ return reg.allocate(bytes); }
|
||||||
|
|
||||||
|
// Get a new block which is 'bytes' size large.
|
||||||
|
void[] add(size_t bytes)
|
||||||
|
{
|
||||||
|
void[] p = reserve(bytes);
|
||||||
|
add(p);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add an existing block to the write list
|
||||||
|
void add(void[] p)
|
||||||
|
{
|
||||||
|
buffers ~= p;
|
||||||
|
used += p.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
T[] write(T)(size_t num)
|
||||||
|
{
|
||||||
|
return cast(T[])add(num * T.sizeof);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size() { return used; }
|
||||||
|
}
|
@ -1,69 +0,0 @@
|
|||||||
/*
|
|
||||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
|
||||||
Copyright (C) 2008-2009 Nicolay Korslund
|
|
||||||
Email: < korslund@gmail.com >
|
|
||||||
WWW: http://openmw.sourceforge.net/
|
|
||||||
|
|
||||||
This file (c_mmfile.d) 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/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
This file provides a simple interface to memory mapped files
|
|
||||||
through C functions. Since D's MmFile is superior in terms of
|
|
||||||
usability and platform independence, we use it even for C++ code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
module util.c_mmfile;
|
|
||||||
|
|
||||||
import std.mmfile;
|
|
||||||
import std.string;
|
|
||||||
|
|
||||||
version(Windows)
|
|
||||||
static int pageSize = 64*1024;
|
|
||||||
else
|
|
||||||
static int pageSize = 4*1024;
|
|
||||||
|
|
||||||
// List of all MMFs in existence, to keep the GC from killing them
|
|
||||||
int[MmFile] mf_list;
|
|
||||||
|
|
||||||
extern(C):
|
|
||||||
|
|
||||||
// Open a new memory mapped file
|
|
||||||
MmFile mmf_open(char *fileName)
|
|
||||||
{
|
|
||||||
auto mmf = new MmFile(toString(fileName),
|
|
||||||
MmFile.Mode.Read,
|
|
||||||
0, null, pageSize);
|
|
||||||
mf_list[mmf] = 1;
|
|
||||||
return mmf;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close a file. Do not use the handle after calling this function, as
|
|
||||||
// the object gets deleted
|
|
||||||
void mmf_close(MmFile mmf)
|
|
||||||
{
|
|
||||||
mf_list.remove(mmf);
|
|
||||||
delete mmf;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map a region of the file. Do NOT attempt to access several regions
|
|
||||||
// at once. Map will almost always unmap the current mapping (thus
|
|
||||||
// making all current pointers invalid) when a new map is requested.
|
|
||||||
void* mmf_map(MmFile mmf, ulong offset, ulong size)
|
|
||||||
{
|
|
||||||
return mmf[offset..offset+size].ptr;
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
|
|
||||||
typedef void* D_MmFile;
|
|
||||||
|
|
||||||
// These functions are implemented in util/c_mmfile.d
|
|
||||||
extern "C"
|
|
||||||
{
|
|
||||||
// Open a new memory mapped file
|
|
||||||
D_MmFile mmf_open(const char *fileName);
|
|
||||||
|
|
||||||
// Close a file. Do not use the handle after calling this function,
|
|
||||||
// as the object gets deleted
|
|
||||||
void mmf_close(D_MmFile mmf);
|
|
||||||
|
|
||||||
// Map a region of the file. Do NOT attempt to access several
|
|
||||||
// regions at once. Map will almost always unmap the current mapping
|
|
||||||
// (thus making all current pointers invalid) when a new map is
|
|
||||||
// requested.
|
|
||||||
void* mmf_map(D_MmFile mmf, int64_t offset, int64_t size);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This struct allows you to open, read and close a memory mapped
|
|
||||||
// file. It uses the D MmFile class to achieve platform independence
|
|
||||||
// and an abstract interface.
|
|
||||||
struct MmFile
|
|
||||||
{
|
|
||||||
MmFile(const std::string &file)
|
|
||||||
{
|
|
||||||
mmf = mmf_open(file.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
~MmFile()
|
|
||||||
{
|
|
||||||
mmf_close(mmf);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *map(int64_t offset, int64_t size)
|
|
||||||
{
|
|
||||||
return mmf_map(mmf, offset, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
D_MmFile mmf;
|
|
||||||
};
|
|
@ -1,129 +0,0 @@
|
|||||||
/*
|
|
||||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
|
||||||
Copyright (C) 2008-2009 Nicolay Korslund
|
|
||||||
Email: < korslund@gmail.com >
|
|
||||||
WWW: http://openmw.sourceforge.net/
|
|
||||||
|
|
||||||
This file (outbuffer.h) 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/ .
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
This files provides a simple buffer class used for writing the cache
|
|
||||||
files. It lets you 'write' data to a growing memory buffer and
|
|
||||||
allows you to change the written data after the fact (since it's
|
|
||||||
retained in memory.) When you're done, you can write the entire
|
|
||||||
buffer to a stream in one operation.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// This is sort of like a mini-version of the Region class in
|
|
||||||
// D. FIXME: And it doesn't need to be. Rewrite this to add buffers of
|
|
||||||
// the exact size requested instead of filling a buffer of predefined
|
|
||||||
// size.
|
|
||||||
class OutBuffer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
OutBuffer() : used(0), left(0), buffers(), sizes()
|
|
||||||
{}
|
|
||||||
|
|
||||||
~OutBuffer()
|
|
||||||
{
|
|
||||||
deallocate();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write everyting to a stream as one buffer
|
|
||||||
void writeTo(std::ostream &str)
|
|
||||||
{
|
|
||||||
for(int i=0;i<buffers.size();i++)
|
|
||||||
str.write((char*)buffers[i], sizes[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get a pointer to a new block at least 'bytes' large. Allocate a
|
|
||||||
// new buffer if necessary.
|
|
||||||
void *reserve(size_t bytes)
|
|
||||||
{
|
|
||||||
assert(bytes <= bufSize);
|
|
||||||
|
|
||||||
if(left >= bytes)
|
|
||||||
return curPtr;
|
|
||||||
|
|
||||||
// Not enough space left. Allocate a new buffer.
|
|
||||||
curPtr = (char*)malloc(bufSize);
|
|
||||||
left = bufSize;
|
|
||||||
|
|
||||||
// Store the new buffer in the lists
|
|
||||||
buffers.push_back(curPtr);
|
|
||||||
sizes.push_back(0);
|
|
||||||
|
|
||||||
return curPtr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get a new block which is 'bytes' size large. The block will be
|
|
||||||
// marked as 'used'.
|
|
||||||
void *add(size_t bytes)
|
|
||||||
{
|
|
||||||
void *res = reserve(bytes);
|
|
||||||
|
|
||||||
if(bytes == 0)
|
|
||||||
return res;
|
|
||||||
|
|
||||||
assert(left >= bytes);
|
|
||||||
curPtr += bytes;
|
|
||||||
left -= bytes;
|
|
||||||
|
|
||||||
// We keep a count of the total number of bytes used
|
|
||||||
used += bytes;
|
|
||||||
|
|
||||||
// Keep a count for each buffer as well
|
|
||||||
sizes[sizes.size()-1] += bytes;
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
T* write(size_t num)
|
|
||||||
{
|
|
||||||
return (T*)add(num*sizeof(T));
|
|
||||||
}
|
|
||||||
|
|
||||||
void deallocate()
|
|
||||||
{
|
|
||||||
for(int i=0;i<buffers.size();i++)
|
|
||||||
free(buffers[i]);
|
|
||||||
|
|
||||||
buffers.clear();
|
|
||||||
sizes.clear();
|
|
||||||
|
|
||||||
left = 0;
|
|
||||||
used = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t size() { return used; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<void*> buffers;
|
|
||||||
std::vector<int> sizes;
|
|
||||||
size_t used, left;
|
|
||||||
char *curPtr;
|
|
||||||
|
|
||||||
static const size_t bufSize = 200*1024;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& os, OutBuffer& buf)
|
|
||||||
{
|
|
||||||
buf.writeTo(os);
|
|
||||||
return os;
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue