📄 roamsimple.cpp
字号:
//
// ROAM Simplistic Implementation
// All code copyright Bryan Turner (Jan, 2000)
// brturn@bellsouth.net
//
// Based on the Tread Marks engine by Longbow Digital Arts
// (www.LongbowDigitalArts.com)
// Much help and hints provided by Seumas McNally, LDA.
//
#include <windows.h>
#include <math.h>
#include <gl/gl.h> // OpenGL
#include "landscape.h"
// -------------------------------------------------------------------------------------------------
// PATCH CLASS
// -------------------------------------------------------------------------------------------------
// ---------------------------------------------------------------------
// Split a single Triangle and link it into the mesh.
// Will correctly force-split diamonds.
//
void Patch::Split(TriTreeNode *tri)
{
// We are already split, no need to do it again.
if (tri->LeftChild)
return;
// If this triangle is not in a proper diamond, force split our base neighbor
if ( tri->BaseNeighbor && (tri->BaseNeighbor->BaseNeighbor != tri) )
Split(tri->BaseNeighbor);
// Create children and link into mesh
tri->LeftChild = Landscape::AllocateTri();
tri->RightChild = Landscape::AllocateTri();
// If creation failed, just exit.
if ( !tri->LeftChild )
return;
// Fill in the information we can get from the parent (neighbor pointers)
tri->LeftChild->BaseNeighbor = tri->LeftNeighbor;
tri->LeftChild->LeftNeighbor = tri->RightChild;
tri->RightChild->BaseNeighbor = tri->RightNeighbor;
tri->RightChild->RightNeighbor = tri->LeftChild;
// Link our Left Neighbor to the new children
if (tri->LeftNeighbor != NULL)
{
if (tri->LeftNeighbor->BaseNeighbor == tri)
tri->LeftNeighbor->BaseNeighbor = tri->LeftChild;
else if (tri->LeftNeighbor->LeftNeighbor == tri)
tri->LeftNeighbor->LeftNeighbor = tri->LeftChild;
else if (tri->LeftNeighbor->RightNeighbor == tri)
tri->LeftNeighbor->RightNeighbor = tri->LeftChild;
else
;// Illegal Left Neighbor!
}
// Link our Right Neighbor to the new children
if (tri->RightNeighbor != NULL)
{
if (tri->RightNeighbor->BaseNeighbor == tri)
tri->RightNeighbor->BaseNeighbor = tri->RightChild;
else if (tri->RightNeighbor->RightNeighbor == tri)
tri->RightNeighbor->RightNeighbor = tri->RightChild;
else if (tri->RightNeighbor->LeftNeighbor == tri)
tri->RightNeighbor->LeftNeighbor = tri->RightChild;
else
;// Illegal Right Neighbor!
}
// Link our Base Neighbor to the new children
if (tri->BaseNeighbor != NULL)
{
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( tri->BaseNeighbor); // Base Neighbor (in a diamond with us) was not split yet, so do that now.
}
else
{
// An edge triangle, trivial case.
tri->LeftChild->RightNeighbor = NULL;
tri->RightChild->LeftNeighbor = NULL;
}
}
// ---------------------------------------------------------------------
// Tessellate a Patch.
// Will continue to split until the variance metric is met.
//
void Patch::RecursTessellate( TriTreeNode *tri,
int leftX, int leftY,
int rightX, int rightY,
int apexX, int apexY,
int node )
{
float TriVariance;
int centerX = (leftX + rightX)>>1; // Compute X coordinate of center of Hypotenuse
int centerY = (leftY + rightY)>>1; // Compute Y coord...
if ( node < (1<<VARIANCE_DEPTH) )
{
// Extremely slow distance metric (sqrt is used).
// Replace this with a faster one!
float distance = 1.0f + sqrtf( SQR((float)centerX - gViewPosition[0]) +
SQR((float)centerY - gViewPosition[2]) );
// Egads! A division too? What's this world coming to!
// This should also be replaced with a faster operation.
TriVariance = ((float)m_CurrentVariance[node] * MAP_SIZE * 2)/distance; // Take both distance and variance into consideration
}
if ( (node >= (1<<VARIANCE_DEPTH)) || // IF we do not have variance info for this node, then we must have gotten here by splitting, so continue down to the lowest level.
(TriVariance > gFrameVariance)) // OR if we are not below the variance tree, test for variance.
{
Split(tri); // Split this triangle.
if (tri->LeftChild && // If this triangle was split, try to split it's children as well.
((abs(leftX - rightX) >= 3) || (abs(leftY - rightY) >= 3))) // Tessellate all the way down to one vertex per height field entry
{
RecursTessellate( tri->LeftChild, apexX, apexY, leftX, leftY, centerX, centerY, node<<1 );
RecursTessellate( tri->RightChild, rightX, rightY, apexX, apexY, centerX, centerY, 1+(node<<1) );
}
}
}
// ---------------------------------------------------------------------
// Render the tree. Simple no-fan method.
//
void Patch::RecursRender( TriTreeNode *tri, int leftX, int leftY, int rightX, int rightY, int apexX, int apexY )
{
if ( tri->LeftChild ) // All non-leaf nodes have both children, so just check for one
{
int centerX = (leftX + rightX)>>1; // Compute X coordinate of center of Hypotenuse
int centerY = (leftY + rightY)>>1; // Compute Y coord...
RecursRender( tri->LeftChild, apexX, apexY, leftX, leftY, centerX, centerY );
RecursRender( tri->RightChild, rightX, rightY, apexX, apexY, centerX, centerY );
}
else // A leaf node! Output a triangle to be rendered.
{
// Actual number of rendered triangles...
gNumTrisRendered++;
GLfloat leftZ = m_HeightMap[(leftY *MAP_SIZE)+leftX ];
GLfloat rightZ = m_HeightMap[(rightY*MAP_SIZE)+rightX];
GLfloat apexZ = m_HeightMap[(apexY *MAP_SIZE)+apexX ];
// Perform lighting calculations if requested.
if (gDrawMode == DRAW_USE_LIGHTING)
{
float v[3][3];
float out[3];
// Create a vertex normal for this triangle.
// NOTE: This is an extremely slow operation for illustration purposes only.
// You should use a texture map with the lighting pre-applied to the texture.
v[0][0] = (GLfloat) leftX;
v[0][1] = (GLfloat) leftZ;
v[0][2] = (GLfloat) leftY;
v[1][0] = (GLfloat) rightX;
v[1][1] = (GLfloat) rightZ ;
v[1][2] = (GLfloat) rightY;
v[2][0] = (GLfloat) apexX;
v[2][1] = (GLfloat) apexZ ;
v[2][2] = (GLfloat) apexY;
calcNormal( v, out );
glNormal3fv( out );
}
// Perform polygon coloring based on a height sample
float fColor = (60.0f + leftZ) / 256.0f;
if ( fColor > 1.0f ) fColor = 1.0f;
glColor3f( fColor, fColor, fColor );
// Output the LEFT VERTEX for the triangle
glVertex3f( (GLfloat) leftX,
(GLfloat) leftZ,
(GLfloat) leftY );
if ( gDrawMode == DRAW_USE_TEXTURE || // Gaurad shading based on height samples instead of light normal
gDrawMode == DRAW_USE_FILL_ONLY )
{
float fColor = (60.0f + rightZ) / 256.0f;
if ( fColor > 1.0f ) fColor = 1.0f;
glColor3f( fColor, fColor, fColor );
}
// Output the RIGHT VERTEX for the triangle
glVertex3f( (GLfloat) rightX,
(GLfloat) rightZ,
(GLfloat) rightY );
if ( gDrawMode == DRAW_USE_TEXTURE || // Gaurad shading based on height samples instead of light normal
gDrawMode == DRAW_USE_FILL_ONLY )
{
float fColor = (60.0f + apexZ) / 256.0f;
if ( fColor > 1.0f ) fColor = 1.0f;
glColor3f( fColor, fColor, fColor );
}
// Output the APEX VERTEX for the triangle
glVertex3f( (GLfloat) apexX,
(GLfloat) apexZ,
(GLfloat) apexY );
}
}
// ---------------------------------------------------------------------
// Computes Variance over the entire tree. Does not examine node relationships.
//
unsigned char Patch::RecursComputeVariance( int leftX, int leftY, unsigned char leftZ,
int rightX, int rightY, unsigned char rightZ,
int apexX, int apexY, unsigned char apexZ,
int node)
{
// /|\
// / | \
// / | \
// / | \
// ~~~~~~~*~~~~~~~ <-- Compute the X and Y coordinates of '*'
//
int centerX = (leftX + rightX) >>1; // Compute X coordinate of center of Hypotenuse
int centerY = (leftY + rightY) >>1; // Compute Y coord...
unsigned char myVariance;
// Get the height value at the middle of the Hypotenuse
unsigned char centerZ = m_HeightMap[(centerY * MAP_SIZE) + centerX];
// Variance of this triangle is the actual height at it's hypotenuse midpoint minus the interpolated height.
// Use values passed on the stack instead of re-accessing the Height Field.
myVariance = abs((int)centerZ - (((int)leftZ + (int)rightZ)>>1));
// Since we're after speed and not perfect representations,
// only calculate variance down to an 8x8 block
if ( (abs(leftX - rightX) >= 8) ||
(abs(leftY - rightY) >= 8) )
{
// Final Variance for this node is the max of it's own variance and that of it's children.
myVariance = MAX( myVariance, RecursComputeVariance( apexX, apexY, apexZ, leftX, leftY, leftZ, centerX, centerY, centerZ, node<<1 ) );
myVariance = MAX( myVariance, RecursComputeVariance( rightX, rightY, rightZ, apexX, apexY, apexZ, centerX, centerY, centerZ, 1+(node<<1)) );
}
// Store the final variance for this node. Note Variance is never zero.
if (node < (1<<VARIANCE_DEPTH))
m_CurrentVariance[node] = 1 + myVariance;
return myVariance;
}
// -------------------------------------------------------------------------------------------------
// PATCH CLASS
// -------------------------------------------------------------------------------------------------
// ---------------------------------------------------------------------
// Initialize a patch.
//
void Patch::Init( int heightX, int heightY, int worldX, int worldY, unsigned char *hMap )
{
// Clear all the relationships
m_BaseLeft.RightNeighbor = m_BaseLeft.LeftNeighbor = m_BaseRight.RightNeighbor = m_BaseRight.LeftNeighbor =
m_BaseLeft.LeftChild = m_BaseLeft.RightChild = m_BaseRight.LeftChild = m_BaseLeft.LeftChild = NULL;
// Attach the two m_Base triangles together
m_BaseLeft.BaseNeighbor = &m_BaseRight;
m_BaseRight.BaseNeighbor = &m_BaseLeft;
// Store Patch offsets for the world and heightmap.
m_WorldX = worldX;
m_WorldY = worldY;
// Store pointer to first byte of the height data for this patch.
m_HeightMap = &hMap[heightY * MAP_SIZE + heightX];
// Initialize flags
m_VarianceDirty = 1;
m_isVisible = 0;
}
// ---------------------------------------------------------------------
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -