Started converting terrain code to D.

git-svn-id: https://openmw.svn.sourceforge.net/svnroot/openmw/trunk@119 ea6a568a-9f4f-0410-981a-c910a81bb256
actorid
nkorslund 16 years ago
parent 5c523995bb
commit 61030fc382

@ -1,7 +1,7 @@
# Designed for GNU Make
# Compiler settings
CXXFLAGS?= -g -Iutil/
CXXFLAGS?= -g
DMD=gdmd -version=Posix
# Some extra flags for niftool and bsatool
@ -32,8 +32,7 @@ ogre_cpp=ogre framelistener interface bsaarchive
mygui_cpp=mygui console
# Ditto for the landscape engine, in terrain/cpp_X.cpp
terrain_cpp=baseland esm generator landdata quad terrain terrainmesh \
archive cachewriter
terrain_cpp=baseland terrain mesh
# FFmpeg files, in the form sound/cpp_X.cpp.
avcodec_cpp=avcodec
@ -46,7 +45,7 @@ bullet_cpp=bullet player scale
ogre_cpp_files=\
$(ogre_cpp:%=ogre/cpp_%.cpp) \
$(mygui_cpp:%=gui/cpp_%.cpp) \
$(terrain_cpp:%=terrain/cpp_%.cpp) util/outbuffer.h util/mmfile.h
$(terrain_cpp:%=terrain/cpp_%.cpp)
avcodec_cpp_files=$(avcodec_cpp:%=sound/cpp_%.cpp)
bullet_cpp_files=$(bullet_cpp:%=bullet/cpp_%.cpp)

@ -25,7 +25,7 @@
#include <iostream>
#include "dbg.h"
#include "../util/dbg.h"
using namespace std;

@ -35,7 +35,7 @@
#include <MyGUI.h>
#include "dbg.h"
#include "../util/dbg.h"
using namespace Ogre;

@ -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);
}
};

@ -67,6 +67,7 @@ std::string g_cacheFile;
#define RTRACE(x)
//#define RTRACE TRACE
/*
// Prerequisites
#include <vector>
#include <map>
@ -94,7 +95,7 @@ BaseLand *g_baseLand;
SceneNode *g_rootTerrainNode;
#include "cpp_baseland.cpp"
#include "cpp_terrainmesh.cpp"
#include "cpp_mesh.cpp"
#include "cpp_quad.cpp"
class TerrainFrameListener : public FrameListener
@ -109,6 +110,7 @@ protected:
};
extern "C" void d_superman();
*/
extern "C" void terr_setCacheDir(char *cacheDir)
{
@ -119,6 +121,7 @@ extern "C" void terr_setCacheDir(char *cacheDir)
// Set up the rendering system
extern "C" void terr_setupRendering()
{
/*
// Add the terrain directory
ResourceGroupManager::getSingleton().
addResourceLocation(g_cacheDir, "FileSystem", "General");
@ -142,8 +145,10 @@ extern "C" void terr_setupRendering()
mCamera->setFarClipDistance(32*CELL_WIDTH);
//ogre_setFog(0.7, 0.7, 0.7, 200, 32*CELL_WIDTH);
d_superman();
*/
}
/*
// Generate all cached data.
extern "C" void terr_genData()
{
@ -170,3 +175,4 @@ extern "C" void terr_genData()
mhm.generate(g_cacheFile);
}
*/

@ -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; }
}

@ -23,49 +23,16 @@
module terrain.terrain;
import std.stdio;
import std.file;
import monster.util.string;
char[] cacheDir = "cache/terrain/";
import terrain.generator;
void initTerrain(bool doGen)
{
if(doGen)
terr_genData();
terr_setupRendering();
}
// 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]);
generate();
if(!pt.ends("/"))
makeDir(pt);
//terr_setupRendering();
}
extern(C):
void terr_setCacheDir(char *dir);
void terr_genData();
void terr_setupRendering();

@ -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;
}

@ -72,7 +72,7 @@ class RegionBuffer(T)
// Check if the buffer can hold 'size' more elements. If not,
// increase it. NOTE: This works even if data = null, and inUse
// is not. This allows us to make copy-on-resize slices.
private void alloc(uint size)
private void alloc(ulong size)
{
if(inUse.length + size <= buffer.length)
{
@ -137,9 +137,9 @@ class RegionBuffer(T)
return b;
}
uint length() { return inUse.length; }
ulong length() { return inUse.length; }
void length(uint size)
void length(ulong size)
{
// Grow array
if(size > inUse.length) alloc(size - inUse.length);
@ -169,15 +169,15 @@ class RegionManager
char[] name;
// Use a default buffer size of one meg. Might change later.
const uint defaultBufferSize = 1024*1024;
const ulong defaultBufferSize = 1024*1024;
// The size to use for new buffers.
uint bufferSize;
ulong bufferSize;
// Current amount of space that is 'lost' in unused end-of-buffer
// areas. Since we have proceeded to other buffers, this space will
// remain unused until freeAll is called.
uint lost;
ulong lost;
ubyte[][] buffers; // Actual memory buffers
void *gcRanges[]; // List of ranges added to gc
@ -222,7 +222,7 @@ class RegionManager
public:
this(char[] name = "", uint bufferSize = defaultBufferSize)
this(char[] name = "", ulong bufferSize = defaultBufferSize)
{
this.name = name;
this.bufferSize = bufferSize;
@ -249,8 +249,10 @@ class RegionManager
delete gcRanges;
}
ulong getBufferSize() { return bufferSize; }
// Allocates an array from the region.
ubyte[] allocate(uint size)
ubyte[] allocate(ulong size)
{
if(size > bufferSize)
fail(format("Tried to allocate %d, but maximum allowed allocation size is %d",
@ -271,7 +273,7 @@ class RegionManager
// Allocate an array and add it to the GC as a root region. This
// should be used for classes and other data that might contain
// pointers / class references to GC-managed data.
ubyte[] allocateGC(uint size)
ubyte[] allocateGC(ulong size)
{
if(currentRange >= gcRanges.length)
fail("No more available GC ranges");
@ -290,7 +292,7 @@ class RegionManager
// int[] array = allocateT!(int)(4);
template allocateT(T)
{
T[] allocateT(uint number)
T[] allocateT(ulong number)
{
return cast(T[])allocate(number * T.sizeof);
}
@ -309,7 +311,7 @@ class RegionManager
template allocateGCT(T)
{
T[] allocateGCT(uint number)
T[] allocateGCT(ulong number)
{
return cast(T[])allocateGC(number * T.sizeof);
}
@ -359,12 +361,12 @@ class RegionManager
}
// Number of used buffers, including the current one
uint usedBuffers() { return currentBuffer; }
ulong usedBuffers() { return currentBuffer; }
// Total number of allocated buffers
uint totalBuffers()
ulong totalBuffers()
{
uint i;
ulong i;
// Count number of allocated buffers
while(i < buffers.length && buffers[i].length) i++;
@ -373,7 +375,7 @@ class RegionManager
}
// Total number of allocated bytes
uint poolSize()
ulong poolSize()
{
return bufferSize * totalBuffers();
}
@ -381,25 +383,25 @@ class RegionManager
// Total number of bytes that are unavailable for use. (They might
// not be used, as such, if they are at the end of a buffer but the
// next buffer is in use.)
uint usedSize()
ulong usedSize()
{
return currentBuffer*bufferSize - left.length;
}
// The total size of data that the user has requested.
uint dataSize()
ulong dataSize()
{
return usedSize() - lostSize();
}
// Number of lost bytes
uint lostSize()
ulong lostSize()
{
return lost;
}
// Total amount of allocated space that is not used
uint wastedSize()
ulong wastedSize()
{
return poolSize() - dataSize();
}

Loading…
Cancel
Save