555 lines
16 KiB
C++
555 lines
16 KiB
C++
/* ************************
|
|
Copyright Terrain Experts Inc.
|
|
Terrain Experts Inc (TERREX) reserves all rights to this source code
|
|
unless otherwise specified in writing by the President of TERREX.
|
|
This copyright may be updated in the future, in which case that version
|
|
supercedes this one.
|
|
-------------------
|
|
Terrex Experts Inc.
|
|
4400 East Broadway #314
|
|
Tucson, AZ 85711
|
|
info@terrex.com
|
|
Tel: (520) 323-7990
|
|
************************
|
|
*/
|
|
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
/* trpage_scene.cpp
|
|
This file implements a bunch of stuff, all of it optional. See trpage_scene.h
|
|
for more information.
|
|
Scene Graph nodes -
|
|
All the methods for the simple scene graph are here.
|
|
trpgSceneGraphParser -
|
|
This is a subclass of trpgSceneParser. It uses that utility class to keep track
|
|
of pushes and pops. It also registers an interest in all the node types it
|
|
knows about (Geometry,Group,LOD,ModelRef). When one of those is encountered
|
|
by the trpgr_Parser (which it's also a subclass of) it reads it into the
|
|
appropriate trpgRead* type.
|
|
Unless you're reading into the scene graph defined in trpage_scene.h, you won't
|
|
use this class directly. Instead, copy it and use it as a template for how
|
|
to read into a scene graph. You'll need to replace the helpers, primarily.
|
|
*/
|
|
|
|
#include <trpage_read.h>
|
|
#include <trpage_scene.h>
|
|
|
|
/* ****************
|
|
MBR Calculation and handling
|
|
****************
|
|
*/
|
|
trpgMBR::trpgMBR()
|
|
{
|
|
valid = false;
|
|
}
|
|
bool trpgMBR::isValid() const
|
|
{
|
|
return valid;
|
|
}
|
|
void trpgMBR::Reset()
|
|
{
|
|
valid = false;
|
|
}
|
|
trpg3dPoint trpgMBR::GetLL() const
|
|
{
|
|
return ll;
|
|
}
|
|
trpg3dPoint trpgMBR::GetUR() const
|
|
{
|
|
return ur;
|
|
}
|
|
void trpgMBR::AddPoint(const trpg3dPoint &pt)
|
|
{
|
|
if (valid) {
|
|
ll.x = MIN(pt.x,ll.x);
|
|
ll.y = MIN(pt.y,ll.y);
|
|
ll.z = MIN(pt.z,ll.z);
|
|
ur.x = MAX(pt.x,ur.x);
|
|
ur.y = MAX(pt.y,ur.y);
|
|
ur.z = MAX(pt.z,ur.z);
|
|
} else {
|
|
valid = true;
|
|
ll = ur = pt;
|
|
}
|
|
}
|
|
void trpgMBR::AddPoint(double x,double y,double z)
|
|
{
|
|
AddPoint(trpg3dPoint(x,y,z));
|
|
}
|
|
void trpgMBR::GetMBR(trpg3dPoint &oll,trpg3dPoint &our) const
|
|
{
|
|
oll = ll;
|
|
our = ur;
|
|
}
|
|
// Add the input MBR to this one
|
|
void trpgMBR::Union(const trpgMBR &in)
|
|
{
|
|
if (valid) {
|
|
if (in.isValid()) {
|
|
AddPoint(in.GetLL());
|
|
AddPoint(in.GetUR());
|
|
}
|
|
} else {
|
|
valid = true;
|
|
*this = in;
|
|
}
|
|
}
|
|
// See if there's any overlap between the two MBRs
|
|
bool trpgMBR::Overlap(const trpg2dPoint &ill, const trpg2dPoint &iur) const
|
|
{
|
|
if (!isValid()) return false;
|
|
|
|
trpg2dPoint ilr = trpg2dPoint(iur.x,ill.y);
|
|
trpg2dPoint iul = trpg2dPoint(ill.x,iur.y);
|
|
|
|
// B MBR falls within A
|
|
if (Within(ill) || Within(iur) || Within(ilr) || Within(iul))
|
|
return true;
|
|
|
|
// A MBR falls within B
|
|
if ((inRange(ill.x,iur.x,ll.x) && inRange(ill.y,iur.y,ll.y)) ||
|
|
(inRange(ill.x,iur.x,ur.x) && inRange(ill.y,iur.y,ll.y)) ||
|
|
(inRange(ill.x,iur.x,ur.x) && inRange(ill.y,iur.y,ur.y)) ||
|
|
(inRange(ill.x,iur.x,ll.x) && inRange(ill.y,iur.y,ur.y)))
|
|
return true;
|
|
|
|
if ((inRange(ll.x,ur.x,ill.x) && ill.y < ll.y && iur.y > ur.y) ||
|
|
(inRange(ll.y,ur.y,ill.y) && ill.x < ll.x && iur.x > ur.x))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
// Check if a given 2d point is within the MBR
|
|
bool trpgMBR::Within(const trpg2dPoint &pt) const
|
|
{
|
|
if (inRange(ll.x,ur.x,pt.x) && inRange(ll.y,ur.y,pt.y))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
/* ****************
|
|
Read Group Base
|
|
Base class for all group structures.
|
|
****************
|
|
*/
|
|
|
|
// Destructor
|
|
trpgReadGroupBase::~trpgReadGroupBase()
|
|
{
|
|
DeleteChildren();
|
|
}
|
|
|
|
// Delete all children
|
|
void trpgReadGroupBase::DeleteChildren()
|
|
{
|
|
for (unsigned int i=0;i<children.size();i++)
|
|
if (children[i])
|
|
delete children[i];
|
|
}
|
|
|
|
// Add a child to the list
|
|
void trpgReadGroupBase::AddChild(trpgReadNode *n)
|
|
{
|
|
children.push_back(n);
|
|
}
|
|
|
|
// Unref a child (but don't delete it)
|
|
void trpgReadGroupBase::unRefChild(int id)
|
|
{
|
|
if (id < 0 || id >= (int)children.size())
|
|
return;
|
|
children[id] = NULL;
|
|
}
|
|
|
|
// Unref all the children (they've probably been moved elsewhere)
|
|
void trpgReadGroupBase::unRefChildren()
|
|
{
|
|
for (unsigned int i=0;i<children.size();i++)
|
|
unRefChild(i);
|
|
}
|
|
|
|
// Calculate an MBR
|
|
trpgMBR trpgReadGroupBase::GetMBR() const
|
|
{
|
|
if (mbr.isValid())
|
|
return mbr;
|
|
else {
|
|
// Calculate and cache a new MBR
|
|
trpgMBR *cmbr = const_cast<trpgMBR *>(&mbr);
|
|
trpgMBR kmbr;
|
|
// Ask the kids
|
|
for (unsigned int i=0;i<children.size();i++) {
|
|
kmbr = children[i]->GetMBR();
|
|
cmbr->Union(kmbr);
|
|
}
|
|
return *cmbr;
|
|
}
|
|
}
|
|
|
|
/* ****************
|
|
Read Geometry
|
|
****************
|
|
*/
|
|
// Calculate an MBR
|
|
trpgMBR trpgReadGeometry::GetMBR() const
|
|
{
|
|
if (mbr.isValid())
|
|
return mbr;
|
|
|
|
trpgMBR *pmbr = const_cast<trpgMBR *>(&mbr);
|
|
|
|
int numVert,i;
|
|
trpg3dPoint pt;
|
|
data.GetNumVertex(numVert);
|
|
numVert /= 3;
|
|
for (i=0;i<numVert;i++) {
|
|
data.GetVertex(i,pt);
|
|
pmbr->AddPoint(pt);
|
|
}
|
|
|
|
return mbr;
|
|
}
|
|
|
|
/* ****************
|
|
Scene Graph Parser
|
|
****************
|
|
*/
|
|
|
|
/* Scene Graph Parser Helpers
|
|
Each of these classes reads a certain kind of data (e.g. a group)
|
|
and creates the appropriate trpgrRead* form and returns that.
|
|
*/
|
|
|
|
/* This is a helper registered by trpgSceneGraphParser that readers trpgGeometry
|
|
nodes and adds them to the current scene graph. trpgGeometry nodes are
|
|
always leaves so there should be no pushes after this node. The Parse method
|
|
also adds the new node as a child to any existing (e.g. top) group.
|
|
{group:Demonstration Scene Graph}
|
|
*/
|
|
class trpgReadGeometryHelper : public trpgr_Callback {
|
|
public:
|
|
trpgReadGeometryHelper(trpgSceneGraphParser *in_parse) { parse = in_parse;}
|
|
void *Parse(trpgToken /*tok*/,trpgReadBuffer &buf) {
|
|
trpgReadGeometry *geom = new trpgReadGeometry();
|
|
trpgGeometry *data = geom->GetData();
|
|
if (!data->Read(buf)) {
|
|
delete geom;
|
|
return NULL;
|
|
}
|
|
trpgReadGroupBase *top = parse->GetCurrTop();
|
|
if (top)
|
|
top->AddChild(geom);
|
|
else
|
|
delete geom;
|
|
|
|
return geom;
|
|
}
|
|
protected:
|
|
trpgSceneGraphParser *parse;
|
|
};
|
|
|
|
/* This helper is registered by trpgSceneGraphParser. It reads a trpgGroup
|
|
from the trpgReadBuffer. It then adds it to our current scene graph.
|
|
It also adds an index corresponding to the group's group ID in our group
|
|
mapping in trpgSceneGraphParser. The new group becomes the top one
|
|
after returning from the Parse call.
|
|
{group:Demonstration Scene Graph}
|
|
*/
|
|
class trpgReadGroupHelper : public trpgr_Callback {
|
|
public:
|
|
trpgReadGroupHelper(trpgSceneGraphParser *in_parse) { parse = in_parse; }
|
|
void *Parse(trpgToken /*tok*/,trpgReadBuffer &buf) {
|
|
trpgReadGroup *group = new trpgReadGroup();
|
|
trpgGroup *data = group->GetData();
|
|
if (!data->Read(buf)) {
|
|
delete group;
|
|
return NULL;
|
|
}
|
|
trpgReadGroupBase *top = parse->GetCurrTop();
|
|
if (top)
|
|
top->AddChild(group);
|
|
else
|
|
delete group;
|
|
// Add to the group map
|
|
int id;
|
|
data->GetID(id);
|
|
trpgSceneGraphParser::GroupMap *gmap = parse->GetGroupMap();
|
|
(*gmap)[id] = group;
|
|
return group;
|
|
}
|
|
protected:
|
|
trpgSceneGraphParser *parse;
|
|
};
|
|
class trpgReadBillboardHelper : public trpgr_Callback {
|
|
public:
|
|
trpgReadBillboardHelper(trpgSceneGraphParser *in_parse) { parse = in_parse; }
|
|
void *Parse(trpgToken /*tok*/,trpgReadBuffer &buf) {
|
|
trpgReadBillboard *group = new trpgReadBillboard();
|
|
trpgBillboard *data = group->GetData();
|
|
if (!data->Read(buf)) {
|
|
delete group;
|
|
return NULL;
|
|
}
|
|
trpgReadGroupBase *top = parse->GetCurrTop();
|
|
if (top)
|
|
top->AddChild(group);
|
|
else
|
|
delete group;
|
|
// Add to the group map
|
|
int id;
|
|
data->GetID(id);
|
|
trpgSceneGraphParser::GroupMap *gmap = parse->GetGroupMap();
|
|
(*gmap)[id] = group;
|
|
return group;
|
|
}
|
|
protected:
|
|
trpgSceneGraphParser *parse;
|
|
};
|
|
class trpgReadAttachHelper : public trpgr_Callback {
|
|
public:
|
|
trpgReadAttachHelper(trpgSceneGraphParser *in_parse) { parse = in_parse; }
|
|
void *Parse(trpgToken /*tok*/,trpgReadBuffer &buf) {
|
|
trpgReadAttach *attach = new trpgReadAttach();
|
|
trpgAttach *data = attach->GetData();
|
|
if (!data->Read(buf)) {
|
|
delete attach;
|
|
return NULL;
|
|
}
|
|
trpgReadGroupBase *top = parse->GetCurrTop();
|
|
if (top)
|
|
top->AddChild(attach);
|
|
else
|
|
delete attach;
|
|
// Add to the group map
|
|
int id;
|
|
data->GetID(id);
|
|
trpgSceneGraphParser::GroupMap *gmap = parse->GetGroupMap();
|
|
(*gmap)[id] = attach;
|
|
return attach;
|
|
}
|
|
protected:
|
|
trpgSceneGraphParser *parse;
|
|
};
|
|
class trpgReadLodHelper : public trpgr_Callback {
|
|
public:
|
|
trpgReadLodHelper(trpgSceneGraphParser *in_parse) { parse = in_parse; }
|
|
void *Parse(trpgToken /*tok*/,trpgReadBuffer &buf) {
|
|
trpgReadLod *lod = new trpgReadLod();
|
|
trpgLod *data = lod->GetData();
|
|
if (!data->Read(buf)) {
|
|
delete lod;
|
|
return NULL;
|
|
}
|
|
trpgReadGroupBase *top = parse->GetCurrTop();
|
|
if (top)
|
|
top->AddChild(lod);
|
|
else
|
|
delete lod;
|
|
// Add to the group map
|
|
int id;
|
|
data->GetID(id);
|
|
trpgSceneGraphParser::GroupMap *gmap = parse->GetGroupMap();
|
|
(*gmap)[id] = lod;
|
|
return lod;
|
|
}
|
|
protected:
|
|
trpgSceneGraphParser *parse;
|
|
};
|
|
class trpgReadModelRefHelper : public trpgr_Callback {
|
|
public:
|
|
trpgReadModelRefHelper(trpgSceneGraphParser *in_parse) { parse = in_parse; }
|
|
void *Parse(trpgToken /*tok*/,trpgReadBuffer &buf) {
|
|
trpgReadModelRef *mod = new trpgReadModelRef();
|
|
trpgModelRef *data = mod->GetData();
|
|
if (!data->Read(buf)) {
|
|
delete mod;
|
|
return NULL;
|
|
}
|
|
trpgReadGroupBase *top = parse->GetCurrTop();
|
|
if (top)
|
|
top->AddChild(mod);
|
|
else
|
|
delete mod;
|
|
return mod;
|
|
}
|
|
protected:
|
|
trpgSceneGraphParser *parse;
|
|
};
|
|
class trpgReadTileHeaderHelper : public trpgr_Callback {
|
|
public:
|
|
trpgReadTileHeaderHelper(trpgSceneGraphParser *in_parse) { parse = in_parse; }
|
|
void *Parse(trpgToken /*tok*/,trpgReadBuffer &buf) {
|
|
trpgReadTileHeader *th = parse->GetTileHeaderRef();
|
|
trpgTileHeader *data = th->GetData();
|
|
if (!data->Read(buf))
|
|
return NULL;
|
|
return th;
|
|
}
|
|
protected:
|
|
trpgSceneGraphParser *parse;
|
|
};
|
|
|
|
/* The Scene Graph Parser constructor does two things. First, it sets
|
|
up any internal variables like a normal constructor. Then it registers
|
|
an interest in all the node types it knows how to parse. It does this
|
|
by calling AddCallback, which is a method of its parent. It passes in
|
|
a token representing the node type (see trpg_io.h) and an object that
|
|
is capable of parsing the given type.
|
|
|
|
The objects we pass in here are called helpers. They parse specific
|
|
objects and add them to the user defined scene graph. Examples include
|
|
trpgReadGeometryHelper, trpgReadGroupHelper, trpgReadAttachHelper,
|
|
trpgReadBillboardHelper, trpgReadLodHelper, trpgReadModelRefHelper,
|
|
trpgReadTileHeaderHelper. These are all derived from trpgr_Callback.
|
|
You should not use any of these yourself. Instead look at these classes
|
|
as examples of how to implement your own subclass of trpgSceneParser.
|
|
*/
|
|
trpgSceneGraphParser::trpgSceneGraphParser()
|
|
{
|
|
top = currTop = NULL;
|
|
|
|
// Register the readers
|
|
AddCallback(TRPG_GEOMETRY,new trpgReadGeometryHelper(this));
|
|
AddCallback(TRPG_GROUP,new trpgReadGroupHelper(this));
|
|
AddCallback(TRPG_ATTACH,new trpgReadAttachHelper(this));
|
|
AddCallback(TRPG_BILLBOARD,new trpgReadBillboardHelper(this));
|
|
AddCallback(TRPG_LOD,new trpgReadLodHelper(this));
|
|
// AddCallback(TRPG_TRANSFORM,new trpgReadTransformHelper(this));
|
|
AddCallback(TRPG_MODELREF,new trpgReadModelRefHelper(this));
|
|
// AddCallback(TRPG_LAYER,new trpgReadLayerHelper(this));
|
|
AddCallback(TRPGTILEHEADER,new trpgReadTileHeaderHelper(this));
|
|
}
|
|
|
|
// Get Current Top node
|
|
trpgReadGroupBase *trpgSceneGraphParser::GetCurrTop()
|
|
{
|
|
if (!currTop)
|
|
return NULL;
|
|
if (currTop->isGroupType())
|
|
return (trpgReadGroupBase *)currTop;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// Return a pointer to the tile header record
|
|
trpgReadTileHeader *trpgSceneGraphParser::GetTileHeaderRef()
|
|
{
|
|
return &tileHead;
|
|
}
|
|
|
|
// Parse Scene
|
|
// Parse a buffer and return the resulting scene graph
|
|
trpgReadNode *trpgSceneGraphParser::ParseScene(trpgReadBuffer &buf,GroupMap &inGmap)
|
|
{
|
|
gmap = &inGmap;
|
|
trpgTileHeader *data = tileHead.GetData();
|
|
data->Reset();
|
|
|
|
// Always put a group up top, since there might be more than
|
|
// one node at the top level in the file.
|
|
top = currTop = new trpgReadGroup();
|
|
|
|
// All the setup for tokens is handled in the constructor
|
|
// Just call parse
|
|
if (!Parse(buf)) {
|
|
// Failed to parse correctly. Give up.
|
|
delete top;
|
|
return NULL;
|
|
}
|
|
|
|
return top;
|
|
}
|
|
|
|
// Start Children
|
|
// This is called when the parser hits a push.
|
|
// We'll want to make the node it's handing us the "top" node
|
|
bool trpgSceneGraphParser::StartChildren(void *in_node)
|
|
{
|
|
trpgReadNode *node = (trpgReadNode *)in_node;
|
|
|
|
if (!node || !node->isGroupType()) {
|
|
// Looks like there's a push in the wrong place
|
|
// Make the current "top" NULL.
|
|
// This will drop all node until we pop back above
|
|
currTop = NULL;
|
|
} else {
|
|
// This node is our new "top"
|
|
currTop = node;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/* This is called whent he parser hits a pop.
|
|
We'll want to look on the parent list (in trpgSceneParser)
|
|
for the parent above the current one.
|
|
If there isn't one, we'll just stick things in our top group.
|
|
*/
|
|
bool trpgSceneGraphParser::EndChildren(void * /*in_node*/)
|
|
{
|
|
// We don't need it here, but this is the node we just
|
|
// finished putting children under. If you need to close
|
|
// it out in some way, do that here
|
|
//trpgReadNode *node = (trpgReadNode *)in_node;
|
|
|
|
// Get the parent above the current one
|
|
int pos = parents.size()-2;
|
|
if (pos < 0)
|
|
// Nothing above the current one. Fall back on our top group
|
|
currTop = top;
|
|
else
|
|
currTop = (trpgReadNode *)parents[pos];
|
|
|
|
return true;
|
|
}
|
|
|
|
// Return group map (for use by helpers)
|
|
trpgSceneGraphParser::GroupMap *trpgSceneGraphParser::GetGroupMap()
|
|
{
|
|
return gmap;
|
|
}
|
|
|
|
/* ***********
|
|
Test functions
|
|
***********
|
|
*/
|
|
|
|
// Test all the tiles in an archive
|
|
bool trpgTestArchive(trpgr_Archive &archive)
|
|
{
|
|
int numLod;
|
|
trpg2iPoint tileSize;
|
|
trpgSceneGraphParser parse;
|
|
trpgReadNode *scene;
|
|
trpgSceneGraphParser::GroupMap gmap;
|
|
|
|
if (!archive.isValid()) return false;
|
|
|
|
const trpgHeader *head = archive.GetHeader();
|
|
head->GetNumLods(numLod);
|
|
|
|
// Iterate over the lods
|
|
int nl,x,y;
|
|
trpgMemReadBuffer buf(archive.GetEndian());
|
|
trpg3dPoint ll,ur;
|
|
for (nl = 0;nl < numLod;nl++) {
|
|
head->GetLodSize(nl,tileSize);
|
|
// Iterate over the tiles within those
|
|
for (x = 0; x < tileSize.x; x++)
|
|
for (y = 0; y < tileSize.y; y++) {
|
|
archive.trpgGetTileMBR(x,y,nl,ll,ur);
|
|
if (archive.ReadTile(x,y,nl,buf)) {
|
|
// Parse it
|
|
scene = parse.ParseScene(buf,gmap);
|
|
if (scene)
|
|
delete scene;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|