- more work on the terrain. STILL not finished.

git-svn-id: https://openmw.svn.sourceforge.net/svnroot/openmw/trunk@124 ea6a568a-9f4f-0410-981a-c910a81bb256
actorid
nkorslund 16 years ago
parent f739bf90f1
commit 367b9754b2

@ -333,7 +333,7 @@ struct HashTable(Key, Value, Alloc = GCAlloc, Hash = DefHash,
// Get a pointer to value of given key, or insert a new. Useful for // Get a pointer to value of given key, or insert a new. Useful for
// large value types when you want to avoid copying the value around // large value types when you want to avoid copying the value around
// needlessly. Also useful if you want to do in-place // needlessly. Also useful if you want to do in-place
// editing. Returns true if a value was inserted. // editing. Returns true if a new value was inserted.
bool insertEdit(Key k, out Value *ptr) bool insertEdit(Key k, out Value *ptr)
{ {
Node *p; Node *p;

@ -55,7 +55,7 @@ class NiTexturingProperty : Property
bool inUse; bool inUse;
NiSourceTexture texture; NiSourceTexture texture;
/* Clamp mode (i don't understand this) /* Clamp mode
0 - clampS clampT 0 - clampS clampT
1 - clampS wrapT 1 - clampS wrapT
2 - wrapS clampT 2 - wrapS clampT
@ -89,8 +89,11 @@ class NiTexturingProperty : Property
filter = nifFile.getIntIs(0,1,2); filter = nifFile.getIntIs(0,1,2);
set = nifFile.getInt; set = nifFile.getInt;
ps2L = nifFile.getShortIs(0); // The combination 1222, 322, 212 was used in a bump map
ps2K = nifFile.getShortIs(-75,-2); // NIF. Might just be bogus numbers, I should probably allow all
// values here since we ignore them anyway.
ps2L = nifFile.getShortIs(0,1222);
ps2K = nifFile.getShortIs(-75,-2,322);
debug(verbose) debug(verbose)
{ {
@ -101,7 +104,7 @@ class NiTexturingProperty : Property
writefln(" ps2K ", ps2K); writefln(" ps2K ", ps2K);
} }
unknown2 = nifFile.wgetShortIs(0,257); unknown2 = nifFile.wgetShortIs(0,257,212);
} }
} }

@ -54,6 +54,20 @@ import input.events;
import terrain.terrain; import terrain.terrain;
// Set up exit handler
alias void function() c_func;
extern(C) int atexit(c_func);
bool cleanExit = false;
void exitHandler()
{
// If we exit uncleanly, print the function stack.
if(!cleanExit)
writefln(dbg.getTrace());
}
//* //*
import std.gc; import std.gc;
import gcstats; import gcstats;
@ -224,6 +238,10 @@ Try specifying another cell name on the command line, or edit openmw.ini.");
// Set the name for the GUI (temporary hack) // Set the name for the GUI (temporary hack)
gui_setCellName(cd.inCell.id.ptr); gui_setCellName(cd.inCell.id.ptr);
// Set up the exit handler
atexit(&exitHandler);
scope(exit) cleanExit = true;
if(render) if(render)
{ {
// Warm up OGRE // Warm up OGRE

@ -20,12 +20,17 @@
*/ */
// This should also be part of the generic cache system. module terrain.archive;
const float TEX_SCALE = 1.0/16;
// This should be part of the generic cache system.
const int CACHE_MAGIC = 0x345AF815; const int CACHE_MAGIC = 0x345AF815;
import std.mmfile; import std.mmfile;
import std.stream;
import std.string; import std.string;
import std.stdio;
import terrain.myfile;
version(Windows) version(Windows)
static int pageSize = 64*1024; static int pageSize = 64*1024;
@ -39,19 +44,20 @@ extern(C)
{ return g_archive.getString(index).ptr; } { return g_archive.getString(index).ptr; }
// Fill various hardware buffers from cache // Fill various hardware buffers from cache
void d_terr_fillVertexBuffer(MeshInfo *mi, float *buffer) void d_terr_fillVertexBuffer(MeshInfo *mi, float *buffer, ulong size)
{ mi.fillVertexBuffer(buffer); } { mi.fillVertexBuffer(buffer[0..size]); }
void d_terr_fillIndexBuffer(MeshInfo *mi, ushort *buffer) void d_terr_fillIndexBuffer(MeshInfo *mi, ushort *buffer, ulong size)
{ mi.fillIndexBuffer(buffer); } { mi.fillIndexBuffer(buffer[0..size]); }
void d_terr_fillAlphaBuffer(AlphaInfo *mi, ubyte *buffer) void d_terr_fillAlphaBuffer(AlphaInfo *mi, ubyte *buffer, ulong size)
{ mi.fillAlphaBuffer(buffer); } { mi.fillAlphaBuffer(buffer[0..size]); }
// Get a given alpha map struct belonging to a mesh // Get a given alpha map struct belonging to a mesh
AlphaInfo *d_terr_getAlphaInfo(MeshInfo *mi, int index) AlphaInfo *d_terr_getAlphaInfo(MeshInfo *mi, int index)
{ return mi.getAlphaInfo(index); } { return mi.getAlphaInfo(index); }
int d_terr_getAlphaSize() { return g_archive.alphaSize; }
} }
// Info about the entire quad. TODO: Some of this (such as the texture // Info about the entire quad. TODO: Some of this (such as the texture
@ -69,9 +75,6 @@ struct QuadInfo
float worldWidth; float worldWidth;
float boundingRadius; float boundingRadius;
// Texture scale for this quad
float texScale;
// True if we should make the given child // True if we should make the given child
bool hasChild[4]; bool hasChild[4];
@ -92,13 +95,14 @@ struct AlphaInfo
// The texture name for this layer. The actual string is stored in // The texture name for this layer. The actual string is stored in
// the archive's string buffer. // the archive's string buffer.
int texName; int texName = -1;
int alphaName; int alphaName = -1;
// Fill the alpha texture buffer // Fill the alpha texture buffer
void fillAlphaBuffer(ubyte *abuf) void fillAlphaBuffer(ubyte abuf[])
{ {
g_archive.copy(abuf, bufOffset, bufSize); assert(abuf.length == bufSize);
g_archive.copy(abuf.ptr, bufOffset, bufSize);
} }
} }
static assert(AlphaInfo.sizeof == 6*4); static assert(AlphaInfo.sizeof == 6*4);
@ -113,10 +117,6 @@ struct MeshInfo
// Vertex and index numbers // Vertex and index numbers
int vertRows, vertCols; int vertRows, vertCols;
int indexCount;
// Scene node position (relative to the parent node)
float x, y;
// Height offset to apply to all vertices // Height offset to apply to all vertices
float heightOffset; float heightOffset;
@ -129,21 +129,20 @@ struct MeshInfo
ulong alphaOffset; ulong alphaOffset;
// Texture name. Index to the string table. // Texture name. Index to the string table.
int texName; int texName = -1;
// Fill the given vertex buffer // Fill the given vertex buffer
void fillVertexBuffer(float *vbuf) void fillVertexBuffer(float vdest[])
{ {
//g_archive.copy(vbuf, vertBufOffset, vertBufSize);
// The height map and normals from the archive // The height map and normals from the archive
char *hmap = cast(char*)g_archive.getRelSlice(vertBufOffset, vertBufSize).ptr; byte *hmap = cast(byte*)g_archive.getRelSlice(vertBufOffset, vertBufSize).ptr;
int level = getLevel();
// The generic part, containing the x,y coordinates and the uv // The generic part, containing the x,y coordinates and the uv
// maps. // maps.
float *gmap = g_archive.getVertexBuffer(level).ptr; float *gmap = g_archive.getVertexBuffer(getLevel()).ptr;
// Destination pointer
float *vbuf = vdest.ptr;
assert(vdest.length == vertRows*vertCols*8);
// Calculate the factor to multiply each height value with. The // Calculate the factor to multiply each height value with. The
// heights are very limited in range as they are stored in a // heights are very limited in range as they are stored in a
@ -151,7 +150,7 @@ struct MeshInfo
// double this for each successive level since we're splicing // double this for each successive level since we're splicing
// several vertices together and need to allow larger differences // several vertices together and need to allow larger differences
// for each vertex. The formula is 8*2^(level-1). // for each vertex. The formula is 8*2^(level-1).
float scale = 4.0 * (1<<level); float scale = 4.0 * (1<<getLevel());
// Merge the two data sets together into the output buffer. // Merge the two data sets together into the output buffer.
float offset = heightOffset; float offset = heightOffset;
@ -178,7 +177,6 @@ struct MeshInfo
*vbuf++ = rowofs * scale; *vbuf++ = rowofs * scale;
// Normal vector. // Normal vector.
// TODO: Normalize?
*vbuf++ = *hmap++; *vbuf++ = *hmap++;
*vbuf++ = *hmap++; *vbuf++ = *hmap++;
*vbuf++ = *hmap++; *vbuf++ = *hmap++;
@ -196,12 +194,13 @@ struct MeshInfo
} }
// Fill the index buffer // Fill the index buffer
void fillIndexBuffer(ushort *ibuf) void fillIndexBuffer(ushort ibuf[])
{ {
// The index buffer is pregenerated. It is identical for all // The index buffer is pregenerated. It is identical for all
// meshes on the same level, so just copy it over. // meshes on the same level, so just copy it over.
ushort generic[] = g_archive.getIndexBuffer(getLevel()); ushort generic[] = g_archive.getIndexBuffer();
ibuf[0..generic.length] = generic[]; assert(ibuf.length == generic.length);
ibuf[] = generic[];
} }
int getLevel() int getLevel()
@ -221,9 +220,9 @@ struct MeshInfo
return res; return res;
} }
} }
static assert(MeshInfo.sizeof == 17*4); static assert(MeshInfo.sizeof == 14*4);
// The first part of the .index file
struct ArchiveHeader struct ArchiveHeader
{ {
// "Magic" number to make sure we're actually reading an archive // "Magic" number to make sure we're actually reading an archive
@ -263,10 +262,13 @@ struct TerrainArchive
0, null, pageSize); 0, null, pageSize);
// Read the index file first // Read the index file first
File ifile = new File(name ~ ".index"); MyFile ifile = new MyFile(name ~ ".index");
ArchiveHeader head; ArchiveHeader head;
ifile.readExact(&head, head.sizeof); ifile.fill(head);
// Reads data into an array. Would be better if this was part of
// the stream.
// Sanity check // Sanity check
assert(head.magic == CACHE_MAGIC); assert(head.magic == CACHE_MAGIC);
@ -277,7 +279,7 @@ struct TerrainArchive
// Read all the quads // Read all the quads
quadList = new QuadInfo[head.quads]; quadList = new QuadInfo[head.quads];
ifile.readExact(quadList.ptr, head.quads*QuadInfo.sizeof); ifile.fillArray(quadList);
// Create an index of all the quads // Create an index of all the quads
foreach(int index, qn; quadList) foreach(int index, qn; quadList)
@ -289,6 +291,7 @@ struct TerrainArchive
assert(l >= 1); assert(l >= 1);
quadMap[l][x][y] = index; quadMap[l][x][y] = index;
assert(index == quadMap[l][x][y]);
// Store the root quad // Store the root quad
if(l == head.rootLevel) if(l == head.rootLevel)
@ -303,24 +306,24 @@ struct TerrainArchive
// Make sure the root was set // Make sure the root was set
assert(rootQuad !is null); assert(rootQuad !is null);
// Next read the string table // Next read the string table. First read the main string buffer.
stringBuf = new char[head.stringSize]; stringBuf = new char[head.stringSize];
strings.length = head.stringNum; ifile.fillArray(stringBuf);
// First read the main string buffer
ifile.readExact(stringBuf.ptr, head.stringSize);
// Then read the string offsets // Then read the string offsets
int[] offsets = new int[head.stringNum]; int[] offsets = new int[head.stringNum];
ifile.readExact(offsets.ptr, offsets.length*int.sizeof); ifile.fillArray(offsets);
// Set up the string table // Set up the string table
char *strptr = stringBuf.ptr; char *strptr = stringBuf.ptr;
strings.length = head.stringNum;
foreach(int i, ref str; strings) foreach(int i, ref str; strings)
{ {
// toString(char*) returns the string up to the zero // toString(char*) returns the string up to the zero
// terminator byte // terminator byte
str = toString(strptr + offsets[i]); str = toString(strptr + offsets[i]);
assert(str.ptr + str.length <=
stringBuf.ptr + stringBuf.length);
} }
delete offsets; delete offsets;
@ -328,23 +331,16 @@ struct TerrainArchive
int bufNum = head.rootLevel; int bufNum = head.rootLevel;
assert(bufNum == 7); assert(bufNum == 7);
vertBufData.length = bufNum; vertBufData.length = bufNum;
indexBufData.length = bufNum;
// Fill the buffers. Start at level 1. // Fill the vertex buffers. Start at level 1.
for(int i=1;i<bufNum;i++) for(int i=1;i<bufNum;i++)
{ {
int size;
// Vertex buffer // Vertex buffer
ifile.read(size); ifile.readArray(vertBufData[i]);
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);
} }
// Index buffer
ifile.readArray(indexBufData);
} }
// Get info about a given quad from the index. // Get info about a given quad from the index.
@ -389,10 +385,9 @@ struct TerrainArchive
return vertBufData[level]; return vertBufData[level];
} }
ushort[] getIndexBuffer(int level) ushort[] getIndexBuffer()
{ {
assert(level>=1 && level<indexBufData.length); return indexBufData;
return indexBufData[level];
} }
private: private:
@ -406,7 +401,7 @@ private:
// These contain pregenerated mesh data that is common for all // These contain pregenerated mesh data that is common for all
// meshes on a given level. // meshes on a given level.
float[][] vertBufData; float[][] vertBufData;
ushort[][] indexBufData; ushort[] indexBufData;
// Used for the mmapped file // Used for the mmapped file
MmFile mmf; MmFile mmf;
@ -448,10 +443,10 @@ private:
// Copy a given buffer from the file. The buffer might be a // Copy a given buffer from the file. The buffer might be a
// compressed stream, so it's important that the buffers are written // 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 // in the same block sizes as they are read. (Ie. you can't write a
// operation and read it as two, or vice versa. Also, buffers cannot // buffer as one operation and read it as two, or vice versa. Also,
// overlap.) The offset is relative to the current mapped file // buffers cannot overlap.) The offset is relative to the current
// window. // mapped file window.
void copy(void *dst, size_t offset, size_t inSize) void copy(void *dst, size_t offset, size_t inSize)
{ {
ubyte source[] = getRelSlice(offset, inSize); ubyte source[] = getRelSlice(offset, inSize);

@ -4,6 +4,7 @@ alias void *SceneNode;
alias void *Bounds; alias void *Bounds;
alias void *MeshObj; alias void *MeshObj;
// These are all defined in cpp_terrain.cpp:
extern(C): extern(C):
SceneNode terr_createChildNode(float relX, float relY, SceneNode); SceneNode terr_createChildNode(float relX, float relY, SceneNode);
@ -16,3 +17,11 @@ void terr_killMesh(MeshObj);
void terr_genData(); void terr_genData();
void terr_setupRendering(); void terr_setupRendering();
void terr_makeLandMaterial(char*,float);
ubyte *terr_makeAlphaLayer(char*,int);
void terr_closeAlpha(char*,char*,float);
void terr_cleanupAlpha(char*,void*,int);
void terr_resize(void*,void*,int,int);
void terr_saveImage(void*,int,char*);

@ -23,8 +23,11 @@
import terrain.archive; import terrain.archive;
import terrain.outbuffer; import terrain.outbuffer;
import std.stream; import std.stdio, std.stream, std.string;
import terrain.myfile;
import std.math2;
import monster.util.string; import monster.util.string;
import monster.vm.dbg;
// Helper structs // Helper structs
struct AlphaHolder struct AlphaHolder
@ -42,9 +45,6 @@ struct MeshHolder
// Actual buffers // Actual buffers
byte[] vertexBuffer; byte[] vertexBuffer;
// Texture name
char[] texName;
// Alpha maps (if any) // Alpha maps (if any)
AlphaHolder alphas[]; AlphaHolder alphas[];
} }
@ -74,7 +74,6 @@ struct CacheWriter
alphaSize = alphSize; alphaSize = alphSize;
vertBuf.length = maxLevel; vertBuf.length = maxLevel;
indexBuf.length = maxLevel;
} }
// Closes the main archive file and writes the index. // Closes the main archive file and writes the index.
@ -83,7 +82,7 @@ struct CacheWriter
mainFile.close(); mainFile.close();
// Write the index file // Write the index file
scope File ofile = new File(iname, FileMode.OutNew); scope MyFile ofile = new MyFile(iname, FileMode.OutNew);
// Header first // Header first
ArchiveHeader head; ArchiveHeader head;
@ -93,11 +92,10 @@ struct CacheWriter
head.alphaSize = alphaSize; head.alphaSize = alphaSize;
head.stringNum = stringList.length; head.stringNum = stringList.length;
head.stringSize = totalStringLength; head.stringSize = totalStringLength;
ofile.writeExact(&head, head.sizeof); ofile.dump(head);
// Write the quads // Write the quads
foreach(qi; quadList) ofile.dumpArray(quadList);
ofile.writeExact(&qi, qi.sizeof);
// String table next. We need to sort it in order of the indices // String table next. We need to sort it in order of the indices
// first. // first.
@ -117,6 +115,10 @@ struct CacheWriter
// Add one byte for the zero terminator // Add one byte for the zero terminator
int len = strVector[i].length + 1; int len = strVector[i].length + 1;
char *ptr = strVector[i].ptr; char *ptr = strVector[i].ptr;
if(ptr[len-1] != 0)
ptr = toStringz(strVector[i]);
assert(ptr[len-1] == 0); assert(ptr[len-1] == 0);
ofile.writeExact(ptr, len); ofile.writeExact(ptr, len);
@ -130,30 +132,20 @@ struct CacheWriter
assert(curOffs == head.stringSize); assert(curOffs == head.stringSize);
// Finally, write the offset table itself // Finally, write the offset table itself
ofile.writeExact(offsets.ptr, offsets.length * int.sizeof); ofile.dumpArray(offsets);
// Write the common vertex and index buffers // Write the common vertex and index buffers
assert(maxLevel == 7);
for(int i=1;i<maxLevel;i++) for(int i=1;i<maxLevel;i++)
{ {
int size;
void *ptr;
// Write vertex buffer // Write vertex buffer
ptr = vertBuf[i].ptr; ofile.writeArray(vertBuf[i]);
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 vertBuf[i];
delete indexBuf[i];
} }
// Then the index buffer
ofile.writeArray(indexBuf);
// Don't need these anymore // Don't need these anymore
delete offsets; delete offsets;
delete strVector; delete strVector;
@ -165,17 +157,16 @@ struct CacheWriter
} }
// Add a common vertex buffer for a given level // Add a common vertex buffer for a given level
void addVertexBuffer(int level, void[] buf) void addVertexBuffer(int level, float[] buf)
{ {
assert(vertBuf.length > level); assert(vertBuf.length > level);
vertBuf[level] = buf; vertBuf[level] = buf;
} }
// Add a common vertex buffer for a given level // Add a common index buffer
void addIndexBuffer(int level, void[] buf) void setIndexBuffer(ushort[] buf)
{ {
assert(indexBuf.length > level); indexBuf = buf;
indexBuf[level] = buf;
} }
// Write a finished quad to the archive file. All the offsets and // Write a finished quad to the archive file. All the offsets and
@ -183,15 +174,16 @@ struct CacheWriter
// the additional data in the Holder structs. // the additional data in the Holder structs.
void writeQuad(ref QuadHolder qh) void writeQuad(ref QuadHolder qh)
{ {
// Make outbuffer a simple struct that uses a region and keeps scope auto _trc = new MTrace("writeQuad");
// track of all the slices we allocate.
OutBuffer buf;
// Write the MeshInfo's first // Write the MeshInfo's first
int meshNum = qh.meshes.length; int meshNum = qh.meshes.length;
MeshInfo meshes[] = buf.write!(MeshInfo)(meshNum); MeshInfo meshes[] = buf.write!(MeshInfo)(meshNum);
float minh = float.infinity;
float maxh = -float.infinity;
// Then write the mesh data in approximately the order it's read // Then write the mesh data in approximately the order it's read
for(int i=0; i<meshNum; i++) for(int i=0; i<meshNum; i++)
{ {
@ -202,15 +194,18 @@ struct CacheWriter
// Copy the basic data first // Copy the basic data first
meshes[i] = mh.info; meshes[i] = mh.info;
minh = min(minh,mh.info.minHeight);
maxh = max(maxh,mh.info.maxHeight);
// Set everything else except the offsets // Set everything else except the offsets
int alphaNum = mh.alphas.length; int alphaNum = mh.alphas.length;
meshes[i].alphaNum = alphaNum; meshes[i].alphaNum = alphaNum;
//meshes[i].texName = addString(mh.texName);
// Write the vertex buffer // Write the vertex buffer
meshes[i].vertBufOffset = buf.size; meshes[i].vertBufOffset = buf.size;
meshes[i].vertBufSize = mh.vertexBuffer.length; meshes[i].vertBufSize = mh.vertexBuffer.length;
writeBuf(mh.vertexBuffer); writeBuf(mh.vertexBuffer);
assert(buf.size == meshes[i].vertBufOffset + meshes[i].vertBufSize);
// Next write the alpha maps, if any // Next write the alpha maps, if any
meshes[i].alphaOffset = buf.size; meshes[i].alphaOffset = buf.size;
@ -231,13 +226,22 @@ struct CacheWriter
// Finally set up the QuadInfo itself // Finally set up the QuadInfo itself
QuadInfo qi; QuadInfo qi;
// Basic info // Copy basic info
qi = qh.info; qi = qh.info;
// Derived info // Derived info
qi.meshNum = meshNum; qi.meshNum = meshNum;
qi.offset = fileOffset; qi.offset = fileOffset;
qi.size = buf.size; qi.size = buf.size;
qi.minHeight = minh;
qi.maxHeight = maxh;
// Get the side length, or the height difference if that is bigger
qi.boundingRadius = max(maxh-minh,qi.worldWidth);
// Multiply with roughly sqrt(1/2), converts from side length to
// radius with some extra slack
qi.boundingRadius *= 0.8;
// The quad cache is done, write it to file // The quad cache is done, write it to file
buf.writeTo(mainFile); buf.writeTo(mainFile);
@ -307,8 +311,8 @@ private:
// Common vertex and index buffers for all quads. One buffer per // Common vertex and index buffers for all quads. One buffer per
// level. // level.
void[][] vertBuf; float[][] vertBuf;
void[][] indexBuf; ushort[] indexBuf;
// Variables that must be set during the gen phase // Variables that must be set during the gen phase
int maxLevel; int maxLevel;

@ -8,15 +8,19 @@ public:
: Ogre::Renderable(), : Ogre::Renderable(),
Ogre::MovableObject() Ogre::MovableObject()
{ {
TRACE("TerrainMesh()");
mLevel = level;
// This is a bit messy, with everything in one function. We could // This is a bit messy, with everything in one function. We could
// split it up later. // split it up later.
// Use MW coordinates all the way // Use MW coordinates all the way
assert(info.worldWidth > 0);
assert(info.minHeight <= info.maxHeight);
mBounds.setExtents(0,0,info.minHeight, mBounds.setExtents(0,0,info.minHeight,
// was (mWidth-1) * vertexSeparation
info.worldWidth, info.worldWidth, info.worldWidth, info.worldWidth,
info.maxHeight); info.maxHeight);
mCenter = mBounds.getCenter(); mCenter = mBounds.getCenter();
mBoundingRadius = mBounds.getHalfSize().length(); mBoundingRadius = mBounds.getHalfSize().length();
@ -56,16 +60,16 @@ public:
// Fill the buffer // Fill the buffer
float* verts = static_cast<float*> float* verts = static_cast<float*>
(mMainBuffer->lock(HardwareBuffer::HBL_DISCARD)); (mMainBuffer->lock(HardwareBuffer::HBL_DISCARD));
info.fillVertexBuffer(verts); info.fillVertexBuffer(verts,8*mVertices->vertexCount);
mMainBuffer->unlock(); mMainBuffer->unlock();
// Create the index data holder // Create the index data holder
mIndices = new IndexData(); mIndices = new IndexData();
mIndices->indexCount = info.indexCount; mIndices->indexCount = 64*64*6; // TODO: Shouldn't be hard-coded
mIndices->indexBuffer = mIndices->indexBuffer =
HardwareBufferManager::getSingleton().createIndexBuffer HardwareBufferManager::getSingleton().createIndexBuffer
( HardwareIndexBuffer::IT_16BIT, ( HardwareIndexBuffer::IT_16BIT,
info.indexCount, mIndices->indexCount,
HardwareBuffer::HBU_STATIC_WRITE_ONLY, HardwareBuffer::HBU_STATIC_WRITE_ONLY,
false); false);
@ -74,17 +78,16 @@ public:
(mIndices->indexBuffer->lock (mIndices->indexBuffer->lock
(0, mIndices->indexBuffer->getSizeInBytes(), (0, mIndices->indexBuffer->getSizeInBytes(),
HardwareBuffer::HBL_DISCARD)); HardwareBuffer::HBL_DISCARD));
info.fillIndexBuffer(indices); info.fillIndexBuffer(indices,mIndices->indexCount);
mIndices->indexBuffer->unlock(); mIndices->indexBuffer->unlock();
// Finally, create the material // Finally, create the material
const std::string texName = info.getTexName(); const std::string texName = info.getTexName();
// TODO: A better thing to do here is to keep the material loaded // Create or retrieve the material
// and retrieve it if it exists. mMaterial = MaterialManager::getSingleton().createOrRetrieve
assert(!MaterialManager::getSingleton().resourceExists(texName)); (texName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME).first;
mMaterial = MaterialManager::getSingleton().create
(texName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
Pass* pass = mMaterial->getTechnique(0)->getPass(0); Pass* pass = mMaterial->getTechnique(0)->getPass(0);
pass->setLightingEnabled(false); pass->setLightingEnabled(false);
@ -119,29 +122,32 @@ public:
// Name of the texture // Name of the texture
std::string tname = alpha.getTexName(); std::string tname = alpha.getTexName();
// TODO: Need to store the result and either delete it in // Create the alpha texture if it doesn't exist
// the destructor or fetch it again the next time we run. if(!TextureManager::getSingleton().resourceExists(alphaName))
Ogre::TexturePtr texPtr = Ogre::TextureManager:: {
getSingleton().createManual TexturePtr texPtr = Ogre::TextureManager::
(alphaName, getSingleton().createManual
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, (alphaName,
Ogre::TEX_TYPE_2D, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
g_alphaSize,g_alphaSize, Ogre::TEX_TYPE_2D,
1,0, // depth, mipmaps g_alphaSize,g_alphaSize,
Ogre::PF_A8, // One-channel alpha 1,0, // depth, mipmaps
Ogre::TU_STATIC_WRITE_ONLY); Ogre::PF_A8, // One-channel alpha
Ogre::TU_STATIC_WRITE_ONLY);
// Get the pointer
Ogre::HardwarePixelBufferSharedPtr pixelBuffer = texPtr->getBuffer(); // Get the pointer
pixelBuffer->lock(Ogre::HardwareBuffer::HBL_DISCARD); Ogre::HardwarePixelBufferSharedPtr pixelBuffer = texPtr->getBuffer();
const Ogre::PixelBox& pixelBox = pixelBuffer->getCurrentLock(); pixelBuffer->lock(Ogre::HardwareBuffer::HBL_DISCARD);
Ogre::uint8* pDest = static_cast<Ogre::uint8*>(pixelBox.data); const Ogre::PixelBox& pixelBox = pixelBuffer->getCurrentLock();
Ogre::uint8* pDest = static_cast<Ogre::uint8*>(pixelBox.data);
// Copy alpha data from file
alpha.fillAlphaBuffer(pDest); // Copy alpha data from file
alpha.fillAlphaBuffer(pDest,g_alphaSize*g_alphaSize);
// Finish everything up with a lot of Ogre-code
pixelBuffer->unlock(); // Close the buffer
pixelBuffer->unlock();
}
pass = mMaterial->getTechnique(0)->createPass(); pass = mMaterial->getTechnique(0)->createPass();
pass->setSceneBlending(Ogre::SBT_TRANSPARENT_ALPHA); pass->setSceneBlending(Ogre::SBT_TRANSPARENT_ALPHA);
pass->setLightingEnabled(false); pass->setLightingEnabled(false);
@ -169,7 +175,7 @@ public:
} }
// Finally, set up the scene node. // Finally, set up the scene node.
mNode = parent->createChildSceneNode(Vector3(info.x, info.y, 0.0)); mNode = parent->createChildSceneNode();
mNode->attachObject(this); mNode->attachObject(this);
} }
@ -179,8 +185,8 @@ public:
mNode->detachAllObjects(); mNode->detachAllObjects();
mNode->getCreator()->destroySceneNode(mNode); mNode->getCreator()->destroySceneNode(mNode);
// TODO: This used to crash. See what happens now. // TODO: This still crashes on level1 meshes. Find out why!
delete mVertices; if(mLevel!=1)delete mVertices;
delete mIndices; delete mIndices;
} }
@ -237,9 +243,11 @@ public:
mLightListDirty = true; mLightListDirty = true;
queue->addRenderable(this, mRenderQueueID); queue->addRenderable(this, mRenderQueueID);
} }
const Ogre::AxisAlignedBox& getBoundingBox( void ) const { const Ogre::AxisAlignedBox& getBoundingBox( void ) const
{
return mBounds; return mBounds;
}; }
Ogre::Real getBoundingRadius(void) const { Ogre::Real getBoundingRadius(void) const {
return mBoundingRadius; return mBoundingRadius;
} }
@ -251,6 +259,8 @@ public:
private: private:
int mLevel;
Ogre::SceneNode* mNode; Ogre::SceneNode* mNode;
Ogre::MaterialPtr mMaterial; Ogre::MaterialPtr mMaterial;

@ -36,11 +36,13 @@ extern "C"
char *d_terr_getTexName(int32_t); char *d_terr_getTexName(int32_t);
void d_terr_fillVertexBuffer(const MeshInfo*,float*); void d_terr_fillVertexBuffer(const MeshInfo*,float*,uint64_t);
void d_terr_fillIndexBuffer(const MeshInfo*,uint16_t*); void d_terr_fillIndexBuffer(const MeshInfo*,uint16_t*,uint64_t);
AlphaInfo *d_terr_getAlphaInfo(const MeshInfo*,int32_t); AlphaInfo *d_terr_getAlphaInfo(const MeshInfo*,int32_t);
void d_terr_fillAlphaBuffer(const AlphaInfo*,uint8_t*); void d_terr_fillAlphaBuffer(const AlphaInfo*,uint8_t*,uint64_t);
int32_t d_terr_getAlphaSize();
} }
// Info about a submesh. This is a clone of the struct defined in // Info about a submesh. This is a clone of the struct defined in
@ -54,10 +56,6 @@ struct MeshInfo
// Vertex and index numbers // Vertex and index numbers
int32_t vertRows, vertCols; int32_t vertRows, vertCols;
int32_t indexCount;
// Scene node position (relative to the parent node)
float x, y;
// Height offset to apply to all vertices // Height offset to apply to all vertices
float heightOffset; float heightOffset;
@ -72,14 +70,14 @@ struct MeshInfo
// Texture name. Index to the string table. // Texture name. Index to the string table.
int32_t texName; int32_t texName;
inline void fillVertexBuffer(float *buffer) const inline void fillVertexBuffer(float *buffer, uint64_t size) const
{ {
d_terr_fillVertexBuffer(this, buffer); d_terr_fillVertexBuffer(this, buffer, size);
} }
inline void fillIndexBuffer(uint16_t *buffer) const inline void fillIndexBuffer(uint16_t *buffer, uint64_t size) const
{ {
d_terr_fillIndexBuffer(this, buffer); d_terr_fillIndexBuffer(this, buffer, size);
} }
inline char* getTexName() const inline char* getTexName() const
@ -114,9 +112,9 @@ struct AlphaInfo
return d_terr_getTexName(alphaName); return d_terr_getTexName(alphaName);
} }
inline void fillAlphaBuffer(uint8_t *buffer) const inline void fillAlphaBuffer(uint8_t *buffer, uint64_t size) const
{ {
return d_terr_fillAlphaBuffer(this, buffer); return d_terr_fillAlphaBuffer(this, buffer, size);
} }
}; };
@ -130,12 +128,67 @@ class TerrainFrameListener : public FrameListener
protected: protected:
bool frameEnded(const FrameEvent& evt) bool frameEnded(const FrameEvent& evt)
{ {
TRACE("Terrain frame");
d_terr_terrainUpdate(); d_terr_terrainUpdate();
g_baseLand->update(); g_baseLand->update();
return true; return true;
} }
}; };
// Renders a material into a texture
Ogre::TexturePtr getRenderedTexture(Ogre::MaterialPtr mp,
const std::string& name,
int texSize, Ogre::PixelFormat tt)
{
Ogre::CompositorPtr cp = Ogre::CompositorManager::getSingleton().
create("Rtt_Comp",
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
Ogre::CompositionTargetPass* ctp = cp->createTechnique()->getOutputTargetPass();
Ogre::CompositionPass* cpass = ctp->createPass();
cpass->setType(Ogre::CompositionPass::PT_RENDERQUAD);
cpass->setMaterial(mp);
// Create the destination texture
Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton().
createManual(name + "_T",
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
Ogre::TEX_TYPE_2D,
texSize,
texSize,
0,
tt,
Ogre::TU_RENDERTARGET
);
Ogre::RenderTexture* renderTexture = texture->getBuffer()->getRenderTarget();
Ogre::Viewport* vp = renderTexture->addViewport(mCamera);
Ogre::CompositorManager::getSingleton().addCompositor(vp, "Rtt_Comp");
Ogre::CompositorManager::getSingleton().setCompositorEnabled(vp,"Rtt_Comp", true);
renderTexture->update();
// Call the OGRE renderer.
Ogre::Root::getSingleton().renderOneFrame();
Ogre::CompositorManager::getSingleton().removeCompositor(vp, "Rtt_Comp");
Ogre::CompositorManager::getSingleton().remove(cp->getHandle());
renderTexture->removeAllViewports();
return texture;
}
// These are used between some functions below. Kinda messy. Since
// these are GLOBAL instances, they are terminated at program
// exit. However, OGRE itself is terminated before that, so we have to
// make sure we have no 'active' shared pointers after OGRE is
// finished (otherwise we get a segfault at exit.)
std::list<Ogre::ResourcePtr> createdResources;
Ogre::HardwarePixelBuffer *pixelBuffer;
MaterialPtr mat;
// Functions called from D // Functions called from D
extern "C" extern "C"
{ {
@ -163,8 +216,11 @@ extern "C"
Ogre::AxisAlignedBox *terr_makeBounds(float minHeight, float maxHeight, Ogre::AxisAlignedBox *terr_makeBounds(float minHeight, float maxHeight,
float width, SceneNode* node) float width, SceneNode* node)
{ {
TRACE("terr_makeBounds");
AxisAlignedBox *mBounds = new AxisAlignedBox; AxisAlignedBox *mBounds = new AxisAlignedBox;
assert(maxHeight >= minHeight);
mBounds->setExtents(0,0,minHeight, mBounds->setExtents(0,0,minHeight,
width,width,maxHeight); width,width,maxHeight);
@ -177,11 +233,13 @@ extern "C"
void terr_killBounds(AxisAlignedBox *bounds) void terr_killBounds(AxisAlignedBox *bounds)
{ {
TRACE("terr_killBounds");
delete bounds; delete bounds;
} }
float terr_getSqCamDist(AxisAlignedBox *mBounds) float terr_getSqCamDist(AxisAlignedBox *mBounds)
{ {
TRACE("terr_getSqCamDist");
Ogre::Vector3 cpos = mCamera->getDerivedPosition(); Ogre::Vector3 cpos = mCamera->getDerivedPosition();
Ogre::Vector3 diff(0, 0, 0); Ogre::Vector3 diff(0, 0, 0);
diff.makeFloor(cpos - mBounds->getMinimum() ); diff.makeFloor(cpos - mBounds->getMinimum() );
@ -193,19 +251,22 @@ extern "C"
MeshInfo *info, MeshInfo *info,
int level, float scale) int level, float scale)
{ {
return new TerrainMesh(parent, *info, level, scale); return new TerrainMesh(parent, *info, level, scale);
} }
void terr_killMesh(TerrainMesh *mesh) void terr_killMesh(TerrainMesh *mesh)
{ delete mesh; } {
TRACE("terr_killMesh");
delete mesh;
}
// Set up the rendering system // Set up the rendering system
void terr_setupRendering() void terr_setupRendering()
{ {
TRACE("terr_setupRendering()");
// Make sure the C++ sizes match the D sizes, since the structs // Make sure the C++ sizes match the D sizes, since the structs
// will be shared between the two. // are shared between the two.
assert(sizeof(MeshInfo) == 17*4); assert(sizeof(MeshInfo) == 14*4);
assert(sizeof(AlphaInfo) == 6*4); assert(sizeof(AlphaInfo) == 6*4);
// Add the terrain directory as a resource location. TODO: Get the // Add the terrain directory as a resource location. TODO: Get the
@ -226,7 +287,126 @@ extern "C"
// terrain mesh that makes the terrain look infinite. // terrain mesh that makes the terrain look infinite.
g_baseLand = new BaseLand(); g_baseLand = new BaseLand();
g_alphaSize = d_terr_getAlphaSize();
// Add the frame listener // Add the frame listener
mRoot->addFrameListener(new TerrainFrameListener); mRoot->addFrameListener(new TerrainFrameListener);
} }
// The next four functions are called in the function genLevel2Map()
// only. This is very top-down-programming-ish and a bit messy, but
// that's what I get for mixing C++ and D like this.
void terr_makeLandMaterial(const char* name, float scale)
{
// Get a new material
mat = Ogre::MaterialManager::getSingleton().
create(name,
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
// Put the default texture in the bottom 'layer', so that we don't
// end up seeing through the landscape.
Ogre::Pass* np = mat->getTechnique(0)->getPass(0);
np->setLightingEnabled(false);
np->createTextureUnitState("_land_default.dds")
->setTextureScale(scale,scale);
}
uint8_t *terr_makeAlphaLayer(const char* name, int32_t width)
{
// Create alpha map for this texture.
Ogre::TexturePtr texPtr = Ogre::TextureManager::getSingleton().
createManual(name,
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
Ogre::TEX_TYPE_2D,
width, width,
1,0, // depth, mipmaps
Ogre::PF_A8, // One-channel alpha
Ogre::TU_STATIC_WRITE_ONLY);
createdResources.push_back(texPtr);
pixelBuffer = texPtr->getBuffer().get();
pixelBuffer->lock(Ogre::HardwareBuffer::HBL_DISCARD);
const Ogre::PixelBox& pixelBox = pixelBuffer->getCurrentLock();
return static_cast<Ogre::uint8*>(pixelBox.data);
}
void terr_closeAlpha(const char *alphaName,
const char *texName, float scale)
{
// Close the alpha pixel buffer opened in the previous function
pixelBuffer->unlock();
// Create a pass containing the alpha map
Pass *np = mat->getTechnique(0)->createPass();
np->setSceneBlending(Ogre::SBT_TRANSPARENT_ALPHA);
np->setLightingEnabled(false);
np->setDepthFunction(Ogre::CMPF_EQUAL);
Ogre::TextureUnitState* tus = np->createTextureUnitState(alphaName);
tus->setTextureAddressingMode(Ogre::TextureUnitState::TAM_CLAMP);
// Set various blending options
tus->setAlphaOperation(Ogre::LBX_BLEND_TEXTURE_ALPHA,
Ogre::LBS_TEXTURE,
Ogre::LBS_TEXTURE);
tus->setColourOperationEx(Ogre::LBX_BLEND_DIFFUSE_ALPHA,
Ogre::LBS_TEXTURE,
Ogre::LBS_TEXTURE);
tus->setIsAlpha(true);
// Add the terrain texture to the pass and scale it.
tus = np->createTextureUnitState(texName);
tus->setColourOperationEx(Ogre::LBX_BLEND_DIFFUSE_ALPHA,
Ogre::LBS_TEXTURE,
Ogre::LBS_CURRENT);
tus->setTextureScale(scale, scale);
}
// Clean up after the above functions, render the material to
// texture and save the data in outdata and in the file outname.
void terr_cleanupAlpha(const char *outname,
void *outData, int32_t toSize)
{
TexturePtr tex1 = getRenderedTexture(mat,outname,
toSize,Ogre::PF_R8G8B8);
// Blit the texture into the given memory buffer
PixelBox pb = PixelBox(toSize, toSize, 1, PF_R8G8B8);
pb.data = outData;
tex1->getBuffer()->blitToMemory(pb);
// Clean up
TextureManager::getSingleton().remove(tex1->getHandle());
const std::list<Ogre::ResourcePtr>::const_iterator iend = createdResources.end();
for ( std::list<Ogre::ResourcePtr>::const_iterator itr = createdResources.begin();
itr != iend;
++itr)
(*itr)->getCreator()->remove((*itr)->getHandle());
createdResources.clear();
MaterialManager::getSingleton().remove(mat->getHandle());
mat.setNull();
}
void terr_resize(void* srcPtr, void* dstPtr, int32_t fromW, int32_t toW)
{
// Create pixelboxes
PixelBox src = PixelBox(fromW, fromW, 1, PF_R8G8B8);
PixelBox dst = PixelBox(toW, toW, 1, PF_R8G8B8);
src.data = srcPtr;
dst.data = dstPtr;
// Resize the image. The nearest neighbour filter makes sure
// there is no blurring.
Image::scale(src, dst, Ogre::Image::FILTER_NEAREST);
}
void terr_saveImage(void *data, int32_t width, const char* name)
{
Image img;
img.loadDynamicImage((uchar*)data, width, width, PF_R8G8B8);
img.save(name);
}
} }

@ -5,6 +5,8 @@ import esm.loadcell;
import util.regions; import util.regions;
import esm.filereader; import esm.filereader;
import std.stdio;
const int LAND_NUM_VERTS = 65*65; const int LAND_NUM_VERTS = 65*65;
MWLand mwland; MWLand mwland;
@ -49,23 +51,27 @@ struct MWLand
// Texture data for one cell // Texture data for one cell
struct LTEXData struct LTEXData
{ {
// TODO: Store the source file here too, so we can get the list // Global list of land textures from the source ES file
// from the right file. The source file is the same as the one we LandTextureList.TextureList source;
// load the landscape from in loadCell().
// Texture indices for this cell
VTEX vtex; VTEX vtex;
// Get the texture x2,y2 from the sub map x1,x2 // Get the texture x2,y2 from the sub map x1,x2
char[] getTexture(int x1, int y1, int x2, int y2) char[] getTexture(int x1, int y1, int x2, int y2)
{ {
// Get the texture index relative to the current esm/esp file // Get the texture index relative to the current esm/esp file
short texID = vtex[y1][x1][y2][x2]; short texID = vtex[y1][x1][y2][x2] - 1;
// Hack, will only work for Morrowind.esm. Fix this later. if(texID == -1)
auto tl = landTextures.files["Morrowind.esm"]; return null;
// Return the 'new' texture name. This name has automatically // Return the 'new' texture name. This name was automatically
// been converted to .dds if the .tga file was not found. // been converted to .dds at load time if the .tga file was not
return tl[texID].getNewName(); // found.
assert(source !is null);
assert(texID >= 0 && texID < source.length);
return source[texID].getNewName();
} }
// Get a texture from the 16x16 grid in one cell // Get a texture from the 16x16 grid in one cell
@ -127,6 +133,9 @@ struct MWLand
// skip to the right part of the ESM file. // skip to the right part of the ESM file.
auto cont = cells.getExt(x,y).land.context; auto cont = cells.getExt(x,y).land.context;
// Get the land texture list from the file
currentLtex.source = landTextures.files[cont.filename];
// We should use an existing region later, or at least delete this // We should use an existing region later, or at least delete this
// once we're done with the gen process. // once we're done with the gen process.
if(reg is null) if(reg is null)
@ -135,7 +144,7 @@ struct MWLand
// Open the ESM at this cell // Open the ESM at this cell
esFile.restoreContext(cont, reg); esFile.restoreContext(cont, reg);
// Store the data // Store the cell-specific data
esFile.readHNExact(currentLand.normals.ptr, esFile.readHNExact(currentLand.normals.ptr,
currentLand.normals.length, "VNML"); currentLand.normals.length, "VNML");
esFile.readHNExact(&currentLand.vhgt, VHGT.sizeof, "VHGT"); esFile.readHNExact(&currentLand.vhgt, VHGT.sizeof, "VHGT");

File diff suppressed because it is too large Load Diff

@ -2,6 +2,8 @@ module terrain.quad;
import terrain.archive; import terrain.archive;
import terrain.bindings; import terrain.bindings;
import std.stdio;
import monster.vm.dbg;
const int CELL_WIDTH = 8192; const int CELL_WIDTH = 8192;
const float SPLIT_FACTOR = 0.5; const float SPLIT_FACTOR = 0.5;
@ -11,6 +13,8 @@ class Quad
{ {
this(int cellX=0, int cellY=0, Quad parent = null) this(int cellX=0, int cellY=0, Quad parent = null)
{ {
scope auto _trc = new MTrace("Quad.this");
mCellX = cellX; mCellX = cellX;
mCellY = cellY; mCellY = cellY;
@ -19,13 +23,6 @@ class Quad
{ {
mLevel = parent.mLevel-1; mLevel = parent.mLevel-1;
if(mLevel == 1)
{
// Create the terrain and leave it there.
buildTerrain();
isStatic = true;
}
// Coordinates relative to our parent // Coordinates relative to our parent
int relX = cellX - parent.mCellX; int relX = cellX - parent.mCellX;
int relY = cellY - parent.mCellY; int relY = cellY - parent.mCellY;
@ -43,12 +40,36 @@ class Quad
// Get the archive data for this quad. // Get the archive data for this quad.
mInfo = g_archive.getQuad(mCellX,mCellY,mLevel); mInfo = g_archive.getQuad(mCellX,mCellY,mLevel);
// Set up the bounding box. Use MW coordinates all the
// way.
mBounds = terr_makeBounds(mInfo.minHeight,
mInfo.maxHeight,
mInfo.worldWidth,
mNode);
float radius = mInfo.boundingRadius;
mSplitDistance = radius * SPLIT_FACTOR;
mUnsplitDistance = radius * UNSPLIT_FACTOR;
// Square the distances
mSplitDistance *= mSplitDistance;
mUnsplitDistance *= mUnsplitDistance;
if(mLevel == 1)
{
// Create the terrain and leave it there.
buildTerrain();
isStatic = true;
}
} }
else else
{ {
// No parent, this is the top-most quad. Get all the info from // No parent, this is the top-most quad. Get all the info from
// the archive. // the archive.
mInfo = g_archive.rootQuad; mInfo = g_archive.rootQuad;
assert(mInfo);
mLevel = mInfo.level; mLevel = mInfo.level;
cellX = mCellX = mInfo.cellX; cellX = mCellX = mInfo.cellX;
@ -68,21 +89,6 @@ class Quad
assert(mLevel >= 1); assert(mLevel >= 1);
assert(mNode !is null); assert(mNode !is null);
// Set up the bounding box. Use MW coordinates all the way
mBounds = terr_makeBounds(mInfo.minHeight,
mInfo.maxHeight,
mInfo.worldWidth,
mNode);
float radius = mInfo.boundingRadius;
mSplitDistance = radius * SPLIT_FACTOR;
mUnsplitDistance = radius * UNSPLIT_FACTOR;
// Square the distances
mSplitDistance *= mSplitDistance;
mUnsplitDistance *= mUnsplitDistance;
// Update the terrain. This will create the mesh or children if // Update the terrain. This will create the mesh or children if
// necessary. // necessary.
update(); update();
@ -90,6 +96,8 @@ class Quad
~this() ~this()
{ {
scope auto _trc = new MTrace("Quad.~this");
// TODO: We might rewrite the code so that the quads are never // TODO: We might rewrite the code so that the quads are never
// actually destroyed, just 'inactivated' by hiding their scene // actually destroyed, just 'inactivated' by hiding their scene
// node. We only call update on our children if we don't have a // node. We only call update on our children if we don't have a
@ -101,12 +109,14 @@ class Quad
delete mChildren[i]; delete mChildren[i];
terr_destroyNode(mNode); terr_destroyNode(mNode);
terr_killBounds(mBounds); if(mBounds !is null)
terr_killBounds(mBounds);
} }
// Remove the landscape for this quad, and create children. // Remove the landscape for this quad, and create children.
void split() void split()
{ {
scope auto _trc = new MTrace("split");
// Never split a static quad or a quad that already has children. // Never split a static quad or a quad that already has children.
assert(!isStatic); assert(!isStatic);
assert(!hasChildren); assert(!hasChildren);
@ -136,6 +146,7 @@ class Quad
// Removes children and rebuilds terrain // Removes children and rebuilds terrain
void unsplit() void unsplit()
{ {
scope auto _trc = new MTrace("unsplit");
// Never unsplit the root quad // Never unsplit the root quad
assert(mLevel < g_archive.rootQuad.level); assert(mLevel < g_archive.rootQuad.level);
// Never unsplit a static or quad that isn't split. // Never unsplit a static or quad that isn't split.
@ -158,6 +169,8 @@ class Quad
// does it. // does it.
void update() void update()
{ {
scope auto _trc = new MTrace("Quad.update()");
// Static quads don't change // Static quads don't change
if(!isStatic) if(!isStatic)
{ {
@ -165,6 +178,7 @@ class Quad
// Get (squared) camera distance. TODO: shouldn't this just // Get (squared) camera distance. TODO: shouldn't this just
// be a simple vector difference from the mesh center? // be a simple vector difference from the mesh center?
assert(mBounds !is null);
float camDist = terr_getSqCamDist(mBounds); float camDist = terr_getSqCamDist(mBounds);
// No children? // No children?
@ -209,10 +223,13 @@ class Quad
// Build the terrain for this quad // Build the terrain for this quad
void buildTerrain() void buildTerrain()
{ {
scope auto _trc = new MTrace("buildTerrain");
assert(!hasMesh); assert(!hasMesh);
assert(!isStatic); assert(!isStatic);
// Map the terrain data into memory. // Map the terrain data into memory.
assert(mInfo);
g_archive.mapQuad(mInfo); g_archive.mapQuad(mInfo);
// Create one mesh for each segment in the quad. TerrainMesh takes // Create one mesh for each segment in the quad. TerrainMesh takes
@ -221,7 +238,7 @@ class Quad
foreach(i, ref m; meshList) foreach(i, ref m; meshList)
{ {
MeshInfo *mi = g_archive.getMeshInfo(i); MeshInfo *mi = g_archive.getMeshInfo(i);
m = terr_makeMesh(mNode, mi, mInfo.level, mInfo.texScale); m = terr_makeMesh(mNode, mi, mInfo.level, TEX_SCALE);
} }
hasMesh = true; hasMesh = true;
@ -229,6 +246,8 @@ class Quad
void destroyTerrain() void destroyTerrain()
{ {
scope auto _trc = new MTrace("destroyTerrain");
assert(hasMesh); assert(hasMesh);
foreach(m; meshList) foreach(m; meshList)

@ -31,6 +31,9 @@ import std.file, std.stdio;
char[] cacheDir = "cache/terrain/"; char[] cacheDir = "cache/terrain/";
// Enable this to render one single terrain mesh
//debug=singleMesh;
void initTerrain(bool doGen) void initTerrain(bool doGen)
{ {
char[] fname = cacheDir ~ "landscape.cache"; char[] fname = cacheDir ~ "landscape.cache";
@ -50,12 +53,26 @@ void initTerrain(bool doGen)
terr_setupRendering(); terr_setupRendering();
// Create the root quad debug(singleMesh)
rootQuad = new Quad; {
// Used for debugging single terrain meshes
auto node = terr_createChildNode(20000,-60000,null);
auto info = g_archive.getQuad(0,0,1);
g_archive.mapQuad(info);
auto mi = g_archive.getMeshInfo(0);
auto m = terr_makeMesh(node, mi, info.level, TEX_SCALE);
}
else
{
// Create the root quad
rootQuad = new Quad;
}
} }
extern(C) void d_terr_terrainUpdate() extern(C) void d_terr_terrainUpdate()
{ {
debug(singleMesh) return;
// Update the root quad each frame. // Update the root quad each frame.
assert(rootQuad !is null); assert(rootQuad !is null);
rootQuad.update(); rootQuad.update();

Loading…
Cancel
Save