📄 roam_naturepatch.cpp
字号:
/*****************************************************************************
File: NaturePatch.h
Desc:
Date: 2003/02/06
Author: Martin Persson
*****************************************************************************/
#include "NatureSceneManager.h"
#include <OgreMaterialManager.h>
#include <asm_math.h>
#include <iostream>
namespace Ogre
{
/* Welcome to MACRO heaven! :) (blame vc++'s inlining...) */
#define GET_HEIGHT(x, z) \
static_cast<int>(mHeightMap[(z << PATCH_LOG2) + z + x])
// Returns the vertex for x,z, 0xffff if no record exists
#define GET_VERTEX(x, z) \
mVertexLookup[(z << PATCH_LOG2) + z + x]
// Allocates a new vertex for x,z
#define ALLOC_VERTEX(vbi, x, z) \
vbi = (mVertexCount++) * 3; \
mVertexLookup[(z << PATCH_LOG2) + z + x] = vbi;
// Stores a vertex in the vertexbuffer
#define STORE_VERTEX(vbi, xp, yp, zp) \
mVertexBuffer[vbi + 0] = (xp) * mScale.x + mWorld.x; \
mVertexBuffer[vbi + 1] = (yp) * mScale.y + mWorld.y; \
mVertexBuffer[vbi + 2] = (zp) * mScale.z + mWorld.z;
// Returns the y coord of a vertex
#define GET_VERTEX_YPOS(vbi) \
((mVertexBuffer[vbi + 1] - mWorld.y) / mScale.y)
// Allocates a vertex and stores its xyz coords
#define STORE_VERTEX_NEW(xp, yp, zp) \
{ \
register int vbi = GET_VERTEX(xp, zp); \
if (vbi == 0xffff) { \
ALLOC_VERTEX(vbi, xp, zp); \
} \
STORE_VERTEX(vbi, xp, yp, zp); \
}
// Stores a triangle in the indexbuffer
#define ADD_TRIANGLE(li, ri, ai) \
mIndexBuffer[mIndexCount++] = (li / 3); \
mIndexBuffer[mIndexCount++] = (ri / 3); \
mIndexBuffer[mIndexCount++] = (ai / 3);
// Stores texture-coordinates
#define STORE_TEXCOORD(vbi, x, y) \
mTexCoordBuffer[0][(vbi*2)/3 + 0] = 1.0 - (x/(Real)PATCH_SIZE); \
mTexCoordBuffer[0][(vbi*2)/3 + 1] = (y/(Real)PATCH_SIZE);
#if STITCH_EDGES
// Stores a pair of edge vertices
#define STORE_EDGE_VERTICES(x1, z1, x2, z2, index1, index2) \
{ \
register int off1 = 0; \
register int off2 = 0; \
if (z1 == 0 && z2 == 0) /* NORTH */ \
{ off1 = x1; off2 = off1 + (x2 - x1); } \
else if (z1 == PATCH_SIZE && z2 == PATCH_SIZE) /* SOUTH */ \
{ off1 = (PATCH_SIZE+1) + x1; off2 = off1 + (x2 - x1); } \
else if (x1 == 0 && x2 == 0) /* WEST */ \
{ off1 = ((PATCH_SIZE+1)*2) + z1; off2 = off1 + (z2 - z1);} \
else /* EAST */ \
{ off1 = ((PATCH_SIZE+1)*3) + z1; off2 = off1 + (z2 - z1);} \
mEdgeVertices[off1] = index1; \
mEdgeVertices[off2] = index2; \
}
#endif
// Allocates a new BinTriNode and clears its childpointers
#define ALLOC_TRI(tri) \
tri = mBinTriBuffer++; \
tri->leftChild = tri->rightChild = 0; \
tri->byEdge = false;
unsigned int NaturePatch::mGenNameCount = 0;
//----------------------------------------------------------------------------
NaturePatch::NaturePatch()
{
mVariance = 0;
mCenter = Vector3::ZERO;
mVertexCache = 0;
mIndexCache = 0;
mTexCoordCache[0] = 0;
mTexCoordCache[1] = 0;
mMeshQuality = DEFAULT_MESH_QUALITY;
mQualityModifier = QUALITY_MODIFIER;
mNeedsRebuild = false;
mNeedsStitching = 0;
// Get default material
mMaterial = reinterpret_cast<Material*>
(MaterialManager::getSingleton().getByName("BaseWhite"));
// Generate name for this NaturePatch instance
mName = "NaturePatch" + toString(mGenNameCount++);
}
//----------------------------------------------------------------------------
NaturePatch::~NaturePatch()
{
if (mVertexCache != 0)
delete[] mVertexCache;
if (mIndexCache != 0)
delete[] mIndexCache;
if (mTexCoordCache[0] != 0)
delete[] mTexCoordCache[0];
// if (mTexCoordCache[1] != 0)
// delete[] mTexCoordCache[1];
if (mVariance != 0)
delete[] mVariance;
}
//----------------------------------------------------------------------------
bool NaturePatch::init(const Vector3& worldPos, const Vector3& scale,
unsigned char *heightMap)
{
mWorld = worldPos;
mScale = scale;
mHeightMap = heightMap;
// Allocate memory for the variance buffer
if (mVariance != 0) delete[] mVariance;
if ((mVariance = new unsigned char[VARBUF_SIZE]) == 0)
return false;
// Pre-calculate the variance buffer
computeVariance(0, PATCH_SIZE, PATCH_SIZE, 0, 0, 0, 2, 2);
computeVariance(PATCH_SIZE, 0, 0, PATCH_SIZE, PATCH_SIZE, PATCH_SIZE,3,2);
// Calculate max height (min == 0)
int maxHeight = 0;
for (int y = 0; y <= PATCH_SIZE; y++)
{
for (int x = 0; x <= PATCH_SIZE; x++)
{
register int h = GET_HEIGHT(x, y);
if (maxHeight < h) maxHeight = h;
}
}
// Set the extents of the bounding box
mBounds.setExtents(mWorld.x,
mWorld.y,
mWorld.z,
mWorld.x + (PATCH_SIZE * mScale.x),
mWorld.y + (maxHeight * mScale.y),
mWorld.z + (PATCH_SIZE * mScale.z));
initMesh();
mCenter = Vector3(mWorld.x + ((PATCH_SIZE/2) * mScale.x),
mWorld.y + ((maxHeight/2) * mScale.y),
mWorld.z + ((PATCH_SIZE/2) * mScale.z));
return true;
}
//----------------------------------------------------------------------------
void NaturePatch::initMesh()
{
mBaseVertex[0].x = 0;
mBaseVertex[0].z = PATCH_SIZE;
mBaseVertex[0].y = GET_HEIGHT(static_cast<int>(mBaseVertex[0].x),
static_cast<int>(mBaseVertex[0].z));
mBaseVertex[1].x = PATCH_SIZE;
mBaseVertex[1].z = 0;
mBaseVertex[1].y = GET_HEIGHT(static_cast<int>(mBaseVertex[1].x),
static_cast<int>(mBaseVertex[1].z));
mBaseVertex[2].x = 0;
mBaseVertex[2].z = 0;
mBaseVertex[2].y = GET_HEIGHT(static_cast<int>(mBaseVertex[2].x),
static_cast<int>(mBaseVertex[2].z));
mBaseVertex[3].x = PATCH_SIZE;
mBaseVertex[3].z = PATCH_SIZE;
mBaseVertex[3].y = GET_HEIGHT(static_cast<int>(mBaseVertex[3].x),
static_cast<int>(mBaseVertex[3].z));
resetMesh();
}
//----------------------------------------------------------------------------
void NaturePatch::resetMesh()
{
// Setup initial neighborhood
mBaseLeft.baseNeighbor = &mBaseRight;
mBaseRight.baseNeighbor = &mBaseLeft;
// Clear the unlinked neighbors
mBaseLeft.leftNeighbor = mBaseLeft.rightNeighbor = 0;
mBaseRight.leftNeighbor = mBaseRight.rightNeighbor = 0;
// Kill the children!
mBaseLeft.leftChild = mBaseLeft.rightChild = 0;
mBaseRight.leftChild = mBaseRight.rightChild = 0;
mBaseLeft.byEdge = mBaseRight.byEdge = true;
}
//----------------------------------------------------------------------------
int NaturePatch::computeVariance(int lx, int lz, // left vertex coords
int rx, int rz, // right vertex coords
int ax, int az, // apex vertex coords
int node, int lvl) // node index & level
{
// Get the center of the hypotenusus
int cx = (lx + rx) >> 1;
int cz = (lz + rz) >> 1;
// Calculate local variance (shift for slightly increased accuracy)
int v = abs((GET_HEIGHT(cx, cz) << 1) -
(GET_HEIGHT(lx, lz) + GET_HEIGHT(rx, rz)));
// Calculate variance of children
if (lvl < (BINTRI_DEPTH - 2))
{
int cv;
// Calculate variance of left child
cv = computeVariance(ax, az, lx, lz, cx, cz, (node<<1), lvl+1);
if (cv > v) v = cv;
// Calculate variance of right child
cv = computeVariance(rx, rz, ax, az, cx, cz, (node<<1)+1, lvl+1);
if (cv > v) v = cv;
}
// Assign and return the max variance
mVariance[node] = (v >> 1);
return v;
}
//----------------------------------------------------------------------------
void NaturePatch::tesselate(BinTriNode *tri,
int lx, int lz, // left vertex coords
int rx, int rz, // right vertex coords
int ax, int az, // apex vertex coords
int node, int lvl) // nodeindex & level
{
int cx = (lx + rx) >> 1;
int cz = (lz + rz) >> 1;
if (tri->leftChild == 0)
{
/* if (tri->byEdge == true || (tri->baseNeighbor != 0 && tri->baseNeighbor->byEdge == true))
{
if (lvl < (BINTRI_DEPTH - 4))
{
split(tri);
}
}
else
*/ {
// We have the VARBUF_SIZE check because we only store variance info
// down to the second highest level of the bintree
if ((node >= VARBUF_SIZE && mMeshQuality >= 2.0) ||
(node < VARBUF_SIZE && (mVariance[node] * mMeshQuality) >= 1.0))
{
split(tri);
}
}
}
// If the tri has been split descend into its children
if (tri->leftChild && lvl < (BINTRI_DEPTH - 1))
{
tesselate(tri->leftChild, ax, az, lx, lz, cx, cz, node<<1, lvl+1);
tesselate(tri->rightChild, rx, rz, ax, az, cx, cz, (node<<1)+1,lvl+1);
}
}
//----------------------------------------------------------------------------
void NaturePatch::split(BinTriNode *tri)
{
/* If base neighbor exists but isn't in diamond with tri,
force it to split first */
if (tri->baseNeighbor && tri->baseNeighbor->baseNeighbor != tri)
{
split(tri->baseNeighbor);
}
// Allocate memory for both childs
ALLOC_TRI(tri->leftChild);
ALLOC_TRI(tri->rightChild);
// Setup neighbor pointers from the parent
tri->leftChild->baseNeighbor = tri->leftNeighbor;
tri->leftChild->leftNeighbor = tri->rightChild;
tri->rightChild->rightNeighbor = tri->leftChild;
// Make sure parents' left-neighbor points to the new child
if (tri->leftNeighbor != 0)
{
if (tri->leftNeighbor->baseNeighbor == tri)
tri->leftNeighbor->baseNeighbor = tri->leftChild;
else
tri->leftNeighbor->rightNeighbor = tri->leftChild;
}
else tri->leftChild->byEdge = true;
// Make sure parents' right-neighbor points to the new child
tri->rightChild->baseNeighbor = tri->rightNeighbor;
if (tri->rightNeighbor != 0)
{
if (tri->rightNeighbor->baseNeighbor == tri)
tri->rightNeighbor->baseNeighbor = tri->rightChild;
else
tri->rightNeighbor->leftNeighbor = tri->rightChild;
}
else tri->rightChild->byEdge = true;
// Setup base neighbor pointers
if (tri->baseNeighbor != 0)
{
if (tri->baseNeighbor->leftChild)
{
tri->baseNeighbor->leftChild->rightNeighbor = tri->rightChild;
tri->baseNeighbor->rightChild->leftNeighbor = tri->leftChild;
tri->leftChild->rightNeighbor = tri->baseNeighbor->rightChild;
tri->rightChild->leftNeighbor = tri->baseNeighbor->leftChild;
}
else
// Split base neighbor in diamond with tri
split(tri->baseNeighbor);
}
else
{
// Tri is by the edge, just reset neighbors
tri->leftChild->rightNeighbor = 0;
tri->rightChild->leftNeighbor = 0;
tri->leftChild->byEdge = tri->rightChild->byEdge = true;
}
}
//----------------------------------------------------------------------------
int NaturePatch::render(BinTriNode *tri,
int lx, int lz, // left vertex coords
int rx, int rz, // right vertex coords
int ax, int az, // apex vertex coords
int li, int ri, int ai, // indices into vertex buffer
int lvl) // bintri level
{
int maxlvl = lvl;
bool avgHeight = false;
// If tri has any childs, store the center vertex and descend
if (tri->leftChild)
{
// Calc center vertex and its index into vertex buffer
int cx = (lx + rx) >> 1;
int cz = (lz + rz) >> 1;
int ci = GET_VERTEX(cx, cz);
if (ci == 0xffff)
{
ALLOC_VERTEX(ci, cx, cz);
}
else
{
avgHeight = true;
}
#ifdef LAME_GEOMORPH_TEST
// Descend into children
int clvl = render(tri->leftChild, ax, az, lx, lz, cx, cz, ai, li, ci, lvl+1);
if (maxlvl < clvl) maxlvl = clvl;
clvl = render(tri->rightChild, rx, rz, ax, az, cx, cz, ri, ai, ci, lvl+1);
if (maxlvl < clvl) maxlvl = clvl;
// Calculate the amount of morphing needed
Real morph = 1.0f - ((Real)(maxlvl-lvl) / (Real)(BINTRI_DEPTH-lvl));
Real realHeight = GET_HEIGHT(cx, cz);
Real diffHeight = realHeight - (((GET_HEIGHT(lx,lz) + GET_HEIGHT(rx,rz))/2.0f));
Real newHeight = realHeight - (diffHeight * morph);
if (!avgHeight)
{
STORE_VERTEX(ci, cx, newHeight, cz);
STORE_TEXCOORD(ci, cx, cz);
}
else
{
// STORE_VERTEX(ci, cx, (newHeight + GET_VERTEX_YPOS(ci)) / 2.0f, cz);
}
#else
// Descend into children
render(tri->leftChild, ax, az, lx, lz, cx, cz, ai, li, ci, lvl + 1);
render(tri->rightChild, rx, rz, ax, az, cx, cz, ri, ai, ci, lvl + 1);
if (!avgHeight)
{
STORE_VERTEX(ci, cx, GET_HEIGHT(cx, cz), cz);
STORE_TEXCOORD(ci, cx, cz);
}
#endif
}
else
{
// Add triangle to the indexbuffer
ADD_TRIANGLE(li, ri, ai);
#if STITCH_EDGES
if (!tri->baseNeighbor)
{
STORE_EDGE_VERTICES(lx, lz, rx, rz, li, ri);
}
if (!tri->leftNeighbor)
{
STORE_EDGE_VERTICES(ax, az, lx, lz, ai, li);
}
if (!tri->rightNeighbor)
{
STORE_EDGE_VERTICES(rx, rz, ax, az, ri, ai);
}
#endif
}
return maxlvl;
}
//----------------------------------------------------------------------------
unsigned int NaturePatch::buildMesh(BinTriNode *binTriBuffer,
Real *vertexBuffer,
ushort *indexBuffer,
ushort *vertexLookup,
Real *texCoordBuffer[2])
{
/* TODO
// Setup buffer pointers
mBinTriBuffer = binTriBuffer;
mIndexBuffer = indexBuffer;
mVertexBuffer = vertexBuffer;
mVertexLookup = vertexLookup;
mTexCoordBuffer[0] = texCoordBuffer[0];
// mTexCoordBuffer[1] = texCoordBuffer[1];
// Reset and tesselate the mesh
resetMesh();
tesselate(&mBaseLeft, static_cast<int>(mBaseVertex[0].x),
static_cast<int>(mBaseVertex[0].z),
static_cast<int>(mBaseVertex[1].x),
static_cast<int>(mBaseVertex[1].z),
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -