forked from 0ad/0ad
New terrain files from ScEd 0.0.5.2
This was SVN commit r201.
This commit is contained in:
parent
6b7eb9b9d8
commit
6abf0e5b5d
308
source/terrain/AABBTree.h
Executable file
308
source/terrain/AABBTree.h
Executable file
@ -0,0 +1,308 @@
|
|||||||
|
#ifndef _AABBTREE_H
|
||||||
|
#define _AABBTREE_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <algorithm>
|
||||||
|
#include "Bound.h"
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class CAABBTree
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
struct Leaf;
|
||||||
|
struct Branch;
|
||||||
|
struct InsertionData;
|
||||||
|
|
||||||
|
struct Node {
|
||||||
|
Node() : m_Parent(0) {}
|
||||||
|
virtual ~Node() {}
|
||||||
|
|
||||||
|
virtual bool RayIntersect(CVector3D& origin,CVector3D& dir,float& dist,T* selection) = 0;
|
||||||
|
virtual void FindInsertionPoint(Leaf* _leaf,InsertionData& _inserter) = 0;
|
||||||
|
|
||||||
|
virtual void AddLeaf(Leaf* _leaf) = 0;
|
||||||
|
virtual void AddLeafAsChild(Leaf* _leaf) {};
|
||||||
|
void AddLeafAsSibling(Leaf* _leaf);
|
||||||
|
|
||||||
|
CBound m_Bounds;
|
||||||
|
Branch* m_Parent;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Leaf : public Node {
|
||||||
|
|
||||||
|
T m_Element;
|
||||||
|
void AddLeaf(Leaf* _leaf);
|
||||||
|
bool RayIntersect(CVector3D& origin,CVector3D& dir,float& dist,T* selection);
|
||||||
|
void FindInsertionPoint(Leaf* _leaf,InsertionData& _inserter);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Branch : public Node {
|
||||||
|
|
||||||
|
~Branch() {
|
||||||
|
for (int i=0;i<m_Children.size();i++) {
|
||||||
|
delete m_Children[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddChild(Node* _node);
|
||||||
|
void RemoveChild(Node* _node);
|
||||||
|
|
||||||
|
void AddLeaf(Leaf* _leaf);
|
||||||
|
void AddLeafAsChild(Leaf* leaf);
|
||||||
|
|
||||||
|
bool RayIntersect(CVector3D& origin,CVector3D& dir,float& dist,T* selection);
|
||||||
|
void FindInsertionPoint(Leaf* _leaf,InsertionData& _inserter);
|
||||||
|
|
||||||
|
std::vector<Node*> m_Children;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct InsertionData
|
||||||
|
{
|
||||||
|
enum Method { ADDINVALID, ADDASSIBLING, ADDASCHILD };
|
||||||
|
|
||||||
|
InsertionData() : cost((float) 1.0e38), inheritedCost(0), method(ADDINVALID), insertionPt(0) {}
|
||||||
|
void setInsertion(Node* pt,float c,enum Method m) {
|
||||||
|
insertionPt=pt;
|
||||||
|
cost=c;
|
||||||
|
method=m;
|
||||||
|
}
|
||||||
|
|
||||||
|
float cost;
|
||||||
|
float inheritedCost;
|
||||||
|
enum Method method;
|
||||||
|
Node* insertionPt;
|
||||||
|
};
|
||||||
|
|
||||||
|
// root of AABBTree
|
||||||
|
Node* m_Root;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CAABBTree();
|
||||||
|
~CAABBTree();
|
||||||
|
|
||||||
|
void AddElement(const T& element);
|
||||||
|
bool RayIntersect(CVector3D& origin,CVector3D& dir,float& dist,T* selection);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
CAABBTree<T>::CAABBTree() : m_Root(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
CAABBTree<T>::~CAABBTree()
|
||||||
|
{
|
||||||
|
delete m_Root;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void CAABBTree<T>::AddElement(const T& element)
|
||||||
|
{
|
||||||
|
Leaf* leaf=new Leaf;
|
||||||
|
leaf->m_Element=element;
|
||||||
|
leaf->m_Element->GetBounds(leaf->m_Bounds);
|
||||||
|
|
||||||
|
if (m_Root) {
|
||||||
|
m_Root->AddLeaf(leaf);
|
||||||
|
|
||||||
|
// set root to topmost node
|
||||||
|
while (m_Root->m_Parent) {
|
||||||
|
m_Root=m_Root->m_Parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
m_Root=leaf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
bool CAABBTree<T>::RayIntersect(CVector3D& origin,CVector3D& dir,float& dist,T* selection)
|
||||||
|
{
|
||||||
|
if (m_Root) {
|
||||||
|
return m_Root->RayIntersect(origin,dir,dist,selection);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void CAABBTree<T>::Node::AddLeafAsSibling(CAABBTree<T>::Leaf* leaf)
|
||||||
|
{
|
||||||
|
CBound& leafBound=leaf->m_Bounds;
|
||||||
|
|
||||||
|
if (m_Parent) {
|
||||||
|
// recursively extend the bounding volumes of the parent nodes to allow the leaf
|
||||||
|
// to fit within them
|
||||||
|
Branch* p=m_Parent;
|
||||||
|
while (p) {
|
||||||
|
p->m_Bounds+=leafBound;
|
||||||
|
p=p->m_Parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a new parent, whose children are the current node (this) and
|
||||||
|
// the leaf being added
|
||||||
|
Branch* newParent=new Branch;
|
||||||
|
newParent->m_Parent=m_Parent;
|
||||||
|
newParent->m_Bounds=m_Bounds;
|
||||||
|
newParent->m_Bounds+=leafBound;
|
||||||
|
|
||||||
|
if (m_Parent) {
|
||||||
|
// remove the current node from it's original parent ..
|
||||||
|
m_Parent->RemoveChild(this);
|
||||||
|
|
||||||
|
// .. and add new parent as a child of the original parent
|
||||||
|
m_Parent->AddChild(newParent);
|
||||||
|
}
|
||||||
|
|
||||||
|
// create new parent/child relationships
|
||||||
|
newParent->AddChild(leaf);
|
||||||
|
leaf->m_Parent=newParent;
|
||||||
|
|
||||||
|
newParent->AddChild(this);
|
||||||
|
this->m_Parent=newParent;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
bool CAABBTree<T>::Branch::RayIntersect(CVector3D& origin,CVector3D& dir,float& dist,T* selection)
|
||||||
|
{
|
||||||
|
// assume nothing hit
|
||||||
|
bool result=false;
|
||||||
|
|
||||||
|
// do bounds check first
|
||||||
|
float tmin,tmax;
|
||||||
|
if (m_Bounds.RayIntersect(origin,dir,tmin,tmax) && tmin<=dist) {
|
||||||
|
// test all children of this branch
|
||||||
|
for (int i=0;i<m_Children.size();i++) {
|
||||||
|
bool hit=m_Children[i]->RayIntersect(origin,dir,dist,selection);
|
||||||
|
if (hit) {
|
||||||
|
// hit a triangle; can quit out ..
|
||||||
|
result=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
bool CAABBTree<T>::Leaf::RayIntersect(CVector3D& origin,CVector3D& dir,float& dist,T* selection)
|
||||||
|
{
|
||||||
|
if (m_Element->RayIntersect(origin,dir,dist)) {
|
||||||
|
*selection=m_Element;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//**********************************************************************************************
|
||||||
|
// FindInsertionPoint : check if the estimated cost of adding given leaf to this leaf
|
||||||
|
// is less than the current best cost; if so, update insertion data accordingly
|
||||||
|
//**********************************************************************************************
|
||||||
|
template <class T>
|
||||||
|
void CAABBTree<T>::Leaf::FindInsertionPoint(CAABBTree<T>::Leaf* leaf,CAABBTree<T>::InsertionData& inserter)
|
||||||
|
{
|
||||||
|
CBound thisbound=m_Bounds;
|
||||||
|
thisbound+=leaf->m_Bounds;
|
||||||
|
float m1=2*thisbound.GetVolume();
|
||||||
|
if (m1<inserter.cost)
|
||||||
|
inserter.setInsertion(this,m1,InsertionData::ADDASSIBLING);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void CAABBTree<T>::Branch::AddLeaf(CAABBTree<T>::Leaf* leaf)
|
||||||
|
{
|
||||||
|
// first find the node to attach the leaf to, and the method with which to attach it
|
||||||
|
InsertionData inserter;
|
||||||
|
FindInsertionPoint(leaf,inserter);
|
||||||
|
|
||||||
|
// now add the leaf to the descendent found above, using the required method
|
||||||
|
if (inserter.method==InsertionData::ADDASCHILD) {
|
||||||
|
inserter.insertionPt->AddLeafAsChild(leaf);
|
||||||
|
} else {
|
||||||
|
inserter.insertionPt->AddLeafAsSibling(leaf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void CAABBTree<T>::Leaf::AddLeaf(CAABBTree<T>::Leaf* leaf)
|
||||||
|
{
|
||||||
|
AddLeafAsSibling(leaf);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void CAABBTree<T>::Branch::AddChild(CAABBTree<T>::Node* node)
|
||||||
|
{
|
||||||
|
m_Children.push_back(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void CAABBTree<T>::Branch::RemoveChild(CAABBTree<T>::Node* node)
|
||||||
|
{
|
||||||
|
m_Children.erase(std::find(m_Children.begin(),m_Children.end(),node));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void CAABBTree<T>::Branch::FindInsertionPoint(CAABBTree<T>::Leaf* leaf,CAABBTree<T>::InsertionData& inserter)
|
||||||
|
{
|
||||||
|
// first count the children
|
||||||
|
int childCt=m_Children.size();
|
||||||
|
|
||||||
|
// get volume of union between this node and leaf
|
||||||
|
CBound& leafBounds=leaf->m_Bounds;
|
||||||
|
CBound uBounds=m_Bounds;
|
||||||
|
uBounds+=leafBounds;
|
||||||
|
float unionVol=uBounds.GetVolume();
|
||||||
|
// get volume of this node
|
||||||
|
float thisVol=m_Bounds.GetVolume();
|
||||||
|
|
||||||
|
// estimate cost of adding the leaf as a sibling of this node
|
||||||
|
float m1=2*unionVol;
|
||||||
|
if (m1<inserter.cost) {
|
||||||
|
// best cost so far; use this insertion point
|
||||||
|
inserter.setInsertion(this,m1,InsertionData::ADDASSIBLING);
|
||||||
|
}
|
||||||
|
|
||||||
|
// estimate cost of adding the leaf as a child of this node
|
||||||
|
float m2=(unionVol-thisVol)*childCt+thisVol;
|
||||||
|
if (m2<inserter.cost) {
|
||||||
|
// best cost so far; use this insertion point
|
||||||
|
inserter.setInsertion(this,m2,InsertionData::ADDASCHILD);
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate cost children inherit from parent
|
||||||
|
float inheritanceCost=inserter.inheritedCost+(unionVol-thisVol)*childCt;
|
||||||
|
if (inheritanceCost>inserter.cost) {
|
||||||
|
// child inheritance cost is greater than lowest cost insertion found so
|
||||||
|
// far, so no improvement can be found by searching children; terminate now
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// now traverse through children to try and find a better insertion point
|
||||||
|
for (int i=0;i<m_Children.size();i++) {
|
||||||
|
inserter.inheritedCost=inheritanceCost;
|
||||||
|
m_Children[i]->FindInsertionPoint(leaf,inserter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void CAABBTree<T>::Branch::AddLeafAsChild(CAABBTree<T>::Leaf* leaf)
|
||||||
|
{
|
||||||
|
Node* p=this;
|
||||||
|
|
||||||
|
// stretch bounds of all ancestors to include the bounds of the leaf
|
||||||
|
while (p) {
|
||||||
|
p->m_Bounds+=leaf->m_Bounds;
|
||||||
|
p=p->m_Parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add leaf as child
|
||||||
|
AddChild(leaf);
|
||||||
|
leaf->m_Parent=this;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
272
source/terrain/AlphaMapCalculator.cpp
Executable file
272
source/terrain/AlphaMapCalculator.cpp
Executable file
@ -0,0 +1,272 @@
|
|||||||
|
#include "AlphaMapCalculator.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
namespace CAlphaMapCalculator {
|
||||||
|
|
||||||
|
struct Blend4 {
|
||||||
|
Blend4(BlendShape4 shape,int alphamap) : m_Shape(shape), m_AlphaMap(alphamap) {}
|
||||||
|
|
||||||
|
BlendShape4 m_Shape;
|
||||||
|
int m_AlphaMap;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Blend8 {
|
||||||
|
Blend8(BlendShape8 shape,int alphamap) : m_Shape(shape), m_AlphaMap(alphamap) {}
|
||||||
|
|
||||||
|
BlendShape8 m_Shape;
|
||||||
|
int m_AlphaMap;
|
||||||
|
};
|
||||||
|
|
||||||
|
Blend4 Blends1Neighbour[] =
|
||||||
|
{
|
||||||
|
Blend4(BlendShape4(1,0,0,0), 12) // u shape blend
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Blend4 Blends2Neighbour[] =
|
||||||
|
{
|
||||||
|
Blend4(BlendShape4(0,1,1,0), 7), // l shaped corner blend
|
||||||
|
Blend4(BlendShape4(1,0,1,0), 10) // two edges blend
|
||||||
|
};
|
||||||
|
|
||||||
|
Blend8 Blends2Neighbour8[] =
|
||||||
|
{
|
||||||
|
Blend8(BlendShape8(1,1,0,0,0,0,0,0), 12), // u shaped blend
|
||||||
|
Blend8(BlendShape8(1,0,0,0,0,1,0,0), 12), // u shaped blend
|
||||||
|
Blend8(BlendShape8(0,1,0,1,0,0,0,0), 0) ,
|
||||||
|
Blend8(BlendShape8(0,1,0,0,0,1,0,0), 0)
|
||||||
|
};
|
||||||
|
|
||||||
|
Blend4 Blends3Neighbour[] =
|
||||||
|
{
|
||||||
|
Blend4(BlendShape4(1,1,1,0), 4) // l shaped corner blend
|
||||||
|
};
|
||||||
|
|
||||||
|
Blend8 Blends3Neighbour8[] =
|
||||||
|
{
|
||||||
|
Blend8(BlendShape8(1,1,0,0,1,0,0,0), 10),
|
||||||
|
Blend8(BlendShape8(1,1,0,0,0,0,0,1), 12),
|
||||||
|
Blend8(BlendShape8(1,1,1,0,0,0,0,0), 1),
|
||||||
|
Blend8(BlendShape8(0,1,1,0,1,0,0,0), 7),
|
||||||
|
Blend8(BlendShape8(0,0,1,0,1,0,1,0), 4),
|
||||||
|
Blend8(BlendShape8(1,1,0,0,0,1,0,0), 12),
|
||||||
|
Blend8(BlendShape8(1,1,0,1,0,0,0,0), 12),
|
||||||
|
Blend8(BlendShape8(0,0,1,0,1,0,0,1), 7),
|
||||||
|
Blend8(BlendShape8(1,0,0,1,0,1,0,0), 12),
|
||||||
|
Blend8(BlendShape8(0,1,0,1,0,1,0,0), 0)
|
||||||
|
};
|
||||||
|
|
||||||
|
Blend8 Blends4Neighbour8[] =
|
||||||
|
{
|
||||||
|
Blend8(BlendShape8(1,1,0,0,1,0,0,1), 10),
|
||||||
|
Blend8(BlendShape8(1,1,0,1,1,0,0,0), 10),
|
||||||
|
Blend8(BlendShape8(1,1,0,0,1,1,0,0), 10),
|
||||||
|
Blend8(BlendShape8(1,1,0,1,0,0,0,1), 12),
|
||||||
|
Blend8(BlendShape8(0,1,1,0,1,1,0,0), 7),
|
||||||
|
Blend8(BlendShape8(1,1,1,1,0,0,0,0), 1),
|
||||||
|
Blend8(BlendShape8(1,1,1,0,1,0,0,0), 3),
|
||||||
|
Blend8(BlendShape8(0,0,1,0,1,1,0,1), 7),
|
||||||
|
Blend8(BlendShape8(1,0,1,0,1,1,0,0), 4),
|
||||||
|
Blend8(BlendShape8(1,1,1,0,0,1,0,0), 1),
|
||||||
|
Blend8(BlendShape8(1,1,0,1,0,1,0,0), 12),
|
||||||
|
Blend8(BlendShape8(0,1,0,1,0,1,0,1), 0)
|
||||||
|
};
|
||||||
|
|
||||||
|
Blend8 Blends5Neighbour8[] =
|
||||||
|
{
|
||||||
|
Blend8(BlendShape8(1,1,1,1,1,0,0,0), 2),
|
||||||
|
Blend8(BlendShape8(1,1,1,1,0,0,0,1), 1),
|
||||||
|
Blend8(BlendShape8(1,1,1,0,1,0,0,1), 3),
|
||||||
|
Blend8(BlendShape8(1,1,1,0,1,0,1,0), 11),
|
||||||
|
Blend8(BlendShape8(1,1,1,0,0,1,0,1), 1),
|
||||||
|
Blend8(BlendShape8(1,1,0,1,1,1,0,0), 10),
|
||||||
|
Blend8(BlendShape8(1,1,1,0,1,1,0,0), 3),
|
||||||
|
Blend8(BlendShape8(1,0,1,0,1,1,0,1), 4),
|
||||||
|
Blend8(BlendShape8(1,1,0,1,0,1,0,1), 12),
|
||||||
|
Blend8(BlendShape8(0,1,1,0,1,1,0,1), 7)
|
||||||
|
};
|
||||||
|
|
||||||
|
Blend8 Blends6Neighbour8[] =
|
||||||
|
{
|
||||||
|
Blend8(BlendShape8(1,1,1,1,1,1,0,0), 2),
|
||||||
|
Blend8(BlendShape8(1,1,1,1,1,0,1,0), 8),
|
||||||
|
Blend8(BlendShape8(1,1,1,1,0,1,0,1), 1),
|
||||||
|
Blend8(BlendShape8(1,1,1,0,1,1,1,0), 6),
|
||||||
|
Blend8(BlendShape8(1,1,1,0,1,1,0,1), 3),
|
||||||
|
Blend8(BlendShape8(1,1,0,1,1,1,0,1), 10)
|
||||||
|
};
|
||||||
|
|
||||||
|
Blend8 Blends7Neighbour8[] =
|
||||||
|
{
|
||||||
|
Blend8(BlendShape8(1,1,1,1,1,1,0,1), 2),
|
||||||
|
Blend8(BlendShape8(1,1,1,1,1,1,1,0), 9)
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
bool MatchBlendShapeRotated(const T& templateshape,const T& shape,unsigned int& flipflags)
|
||||||
|
{
|
||||||
|
// try to match shapes by testing the template shape in normal, and flipped u and v configurations
|
||||||
|
// test unrotated shape
|
||||||
|
if (shape==templateshape) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
T tstShape;
|
||||||
|
templateshape.FlipU(tstShape);
|
||||||
|
if (shape==tstShape) {
|
||||||
|
flipflags|=0x02;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
templateshape.FlipV(tstShape);
|
||||||
|
if (shape==tstShape) {
|
||||||
|
flipflags|=0x01;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
int MatchBlendShape(const T& templateshape,const T& shape,unsigned int& flipflags)
|
||||||
|
{
|
||||||
|
// try matching unrotated shape first
|
||||||
|
if (MatchBlendShapeRotated(templateshape,shape,flipflags)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// now try iterating through rotations of 90,180,270 degrees
|
||||||
|
T tstShape;
|
||||||
|
templateshape.Rotate90(tstShape);
|
||||||
|
if (MatchBlendShapeRotated(tstShape,shape,flipflags)) {
|
||||||
|
flipflags|=flipflags ? 0x10 : 0x04;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
templateshape.Rotate180(tstShape);
|
||||||
|
if (MatchBlendShapeRotated(tstShape,shape,flipflags)) {
|
||||||
|
flipflags|=0x08;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
templateshape.Rotate270(tstShape);
|
||||||
|
if (MatchBlendShapeRotated(tstShape,shape,flipflags)) {
|
||||||
|
flipflags|=flipflags ? 0x04 : 0x10;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
FlipBlendU(templateshape,tstShape);
|
||||||
|
if (shape==tstShape) {
|
||||||
|
flipflags|=0x01;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
FlipBlendV(templateshape,tstShape);
|
||||||
|
if (shape==tstShape) {
|
||||||
|
flipflags|=0x02;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class S,class T>
|
||||||
|
int LookupBlend(int tableSize,const S* table,const T& shape,unsigned int& flipflags)
|
||||||
|
{
|
||||||
|
// iterate through known blend shapes
|
||||||
|
for (int b=0;b<tableSize;b++) {
|
||||||
|
const S& blend=table[b];
|
||||||
|
if (MatchBlendShape(blend.m_Shape,shape,flipflags)) {
|
||||||
|
return blend.m_AlphaMap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// eh? shouldn't get here if we've correctly considered all possible cases; keep the compiler happy, and, while
|
||||||
|
// we're still debugging possible shapes, return bad blend to highlight suspect alphamap logic
|
||||||
|
return 13;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int Calculate(BlendShape8 shape,unsigned int& flipflags)
|
||||||
|
{
|
||||||
|
// assume we're not going to require flipping
|
||||||
|
flipflags=0;
|
||||||
|
|
||||||
|
// count number of neighbours
|
||||||
|
int count=0;
|
||||||
|
for (int i=0;i<8;i++) {
|
||||||
|
if (shape[i]) count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count==0) {
|
||||||
|
// no neighbours, just the centre tile has the given texture; use blend circle
|
||||||
|
return 0;
|
||||||
|
} else if (count==8) {
|
||||||
|
// all neighbours have same texture; return code to signal no alphamap required
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
if (count<=4) {
|
||||||
|
// check if we can consider this a BlendShape4 - ie are any of the diagonals (NE,SE,SW,NW) set?
|
||||||
|
if (!shape[1] && !shape[3] && !shape[5] && !shape[7]) {
|
||||||
|
// ok, build a BlendShape4 and use that
|
||||||
|
BlendShape4 shape4;
|
||||||
|
shape4[0]=shape[0];
|
||||||
|
shape4[1]=shape[2];
|
||||||
|
shape4[2]=shape[4];
|
||||||
|
shape4[3]=shape[6];
|
||||||
|
|
||||||
|
switch (count) {
|
||||||
|
case 1:
|
||||||
|
return LookupBlend(sizeof(Blends1Neighbour)/sizeof(Blend4),Blends1Neighbour,shape4,flipflags);
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
return LookupBlend(sizeof(Blends2Neighbour)/sizeof(Blend4),Blends2Neighbour,shape4,flipflags);
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
return LookupBlend(sizeof(Blends3Neighbour)/sizeof(Blend4),Blends3Neighbour,shape4,flipflags);
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
// N,S,E,W have same texture, NE,SE,SW,NW don't; use a blend 4 corners
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// we've got this far, so now we've got to consider the remaining choices, all containing diagonal elements
|
||||||
|
switch (count) {
|
||||||
|
case 1:
|
||||||
|
// trivial case - just return a circle blend
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
return LookupBlend(sizeof(Blends2Neighbour8)/sizeof(Blend8),Blends2Neighbour8,shape,flipflags);
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
return LookupBlend(sizeof(Blends3Neighbour8)/sizeof(Blend8),Blends3Neighbour8,shape,flipflags);
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
return LookupBlend(sizeof(Blends4Neighbour8)/sizeof(Blend8),Blends4Neighbour8,shape,flipflags);
|
||||||
|
|
||||||
|
case 5:
|
||||||
|
return LookupBlend(sizeof(Blends5Neighbour8)/sizeof(Blend8),Blends5Neighbour8,shape,flipflags);
|
||||||
|
|
||||||
|
case 6:
|
||||||
|
return LookupBlend(sizeof(Blends6Neighbour8)/sizeof(Blend8),Blends6Neighbour8,shape,flipflags);
|
||||||
|
|
||||||
|
case 7:
|
||||||
|
return LookupBlend(sizeof(Blends7Neighbour8)/sizeof(Blend8),Blends7Neighbour8,shape,flipflags);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shouldn't get here if we've correctly considered all possible cases; keep the compiler happy, and, while
|
||||||
|
// we're still debugging possible shapes, return bad blend to highlight suspect alphamap logic
|
||||||
|
return 13;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end of namespace
|
11
source/terrain/AlphaMapCalculator.h
Executable file
11
source/terrain/AlphaMapCalculator.h
Executable file
@ -0,0 +1,11 @@
|
|||||||
|
#ifndef _ALPHAMAPCALCULATOR_H
|
||||||
|
#define _ALPHAMAPCALCULATOR_H
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include "BlendShapes.h"
|
||||||
|
|
||||||
|
namespace CAlphaMapCalculator {
|
||||||
|
int Calculate(BlendShape8 shape,unsigned int& flipflags);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
134
source/terrain/BlendShapes.h
Executable file
134
source/terrain/BlendShapes.h
Executable file
@ -0,0 +1,134 @@
|
|||||||
|
#ifndef _BLENDSHAPES_H
|
||||||
|
#define _BLENDSHAPES_H
|
||||||
|
|
||||||
|
struct BlendShape4
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BlendShape4() {}
|
||||||
|
BlendShape4(int a,int b,int c,int d) {
|
||||||
|
m_Data[0]=a; m_Data[1]=b; m_Data[2]=c; m_Data[3]=d;
|
||||||
|
}
|
||||||
|
|
||||||
|
int& operator[](int index) { return m_Data[index]; }
|
||||||
|
const int& operator[](int index) const { return m_Data[index]; }
|
||||||
|
|
||||||
|
bool operator==(const BlendShape4& lhs) const {
|
||||||
|
return memcmp(m_Data,lhs.m_Data,sizeof(BlendShape4))==0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Rotate90(BlendShape4& dst) const {
|
||||||
|
dst[0]=m_Data[3];
|
||||||
|
dst[1]=m_Data[0];
|
||||||
|
dst[2]=m_Data[1];
|
||||||
|
dst[3]=m_Data[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
void Rotate180(BlendShape4& dst) const {
|
||||||
|
dst[0]=m_Data[2];
|
||||||
|
dst[1]=m_Data[3];
|
||||||
|
dst[2]=m_Data[0];
|
||||||
|
dst[3]=m_Data[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
void Rotate270(BlendShape4& dst) const {
|
||||||
|
dst[0]=m_Data[1];
|
||||||
|
dst[1]=m_Data[2];
|
||||||
|
dst[2]=m_Data[3];
|
||||||
|
dst[3]=m_Data[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlipU(BlendShape4& dst) const {
|
||||||
|
dst[0]=m_Data[0];
|
||||||
|
dst[1]=m_Data[3];
|
||||||
|
dst[2]=m_Data[2];
|
||||||
|
dst[3]=m_Data[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlipV(BlendShape4& dst) const {
|
||||||
|
dst[0]=m_Data[2];
|
||||||
|
dst[1]=m_Data[1];
|
||||||
|
dst[2]=m_Data[0];
|
||||||
|
dst[3]=m_Data[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_Data[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct BlendShape8
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BlendShape8() {}
|
||||||
|
BlendShape8(int a,int b,int c,int d,int e,int f,int g,int h) {
|
||||||
|
m_Data[0]=a; m_Data[1]=b; m_Data[2]=c; m_Data[3]=d;
|
||||||
|
m_Data[4]=e; m_Data[5]=f; m_Data[6]=g; m_Data[7]=h;
|
||||||
|
}
|
||||||
|
|
||||||
|
int& operator[](int index) { return m_Data[index]; }
|
||||||
|
const int& operator[](int index) const { return m_Data[index]; }
|
||||||
|
|
||||||
|
bool operator==(const BlendShape8& lhs) const {
|
||||||
|
return memcmp(m_Data,lhs.m_Data,sizeof(BlendShape8))==0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Rotate90(BlendShape8& dst) const {
|
||||||
|
dst[0]=m_Data[6];
|
||||||
|
dst[1]=m_Data[7];
|
||||||
|
dst[2]=m_Data[0];
|
||||||
|
dst[3]=m_Data[1];
|
||||||
|
dst[4]=m_Data[2];
|
||||||
|
dst[5]=m_Data[3];
|
||||||
|
dst[6]=m_Data[4];
|
||||||
|
dst[7]=m_Data[5];
|
||||||
|
}
|
||||||
|
|
||||||
|
void Rotate180(BlendShape8& dst) const {
|
||||||
|
dst[0]=m_Data[4];
|
||||||
|
dst[1]=m_Data[5];
|
||||||
|
dst[2]=m_Data[6];
|
||||||
|
dst[3]=m_Data[7];
|
||||||
|
dst[4]=m_Data[0];
|
||||||
|
dst[5]=m_Data[1];
|
||||||
|
dst[6]=m_Data[2];
|
||||||
|
dst[7]=m_Data[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
void Rotate270(BlendShape8& dst) const {
|
||||||
|
dst[0]=m_Data[2];
|
||||||
|
dst[1]=m_Data[3];
|
||||||
|
dst[2]=m_Data[4];
|
||||||
|
dst[3]=m_Data[5];
|
||||||
|
dst[4]=m_Data[6];
|
||||||
|
dst[5]=m_Data[7];
|
||||||
|
dst[6]=m_Data[0];
|
||||||
|
dst[7]=m_Data[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlipU(BlendShape8& dst) const {
|
||||||
|
dst[0]=m_Data[0];
|
||||||
|
dst[1]=m_Data[7];
|
||||||
|
dst[2]=m_Data[6];
|
||||||
|
dst[3]=m_Data[5];
|
||||||
|
dst[4]=m_Data[4];
|
||||||
|
dst[5]=m_Data[3];
|
||||||
|
dst[6]=m_Data[2];
|
||||||
|
dst[7]=m_Data[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlipV(BlendShape8& dst) const {
|
||||||
|
dst[0]=m_Data[4];
|
||||||
|
dst[1]=m_Data[3];
|
||||||
|
dst[2]=m_Data[2];
|
||||||
|
dst[3]=m_Data[1];
|
||||||
|
dst[4]=m_Data[0];
|
||||||
|
dst[5]=m_Data[7];
|
||||||
|
dst[6]=m_Data[6];
|
||||||
|
dst[7]=m_Data[5];
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_Data[8];
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
156
source/terrain/HBV.h
Executable file
156
source/terrain/HBV.h
Executable file
@ -0,0 +1,156 @@
|
|||||||
|
// HBV.h
|
||||||
|
//
|
||||||
|
// (c) Rich Cross, 2000
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef __HBV_H
|
||||||
|
#define __HBV_H
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <vector>
|
||||||
|
#include "HBVNode.h"
|
||||||
|
#include "Aggregate.h"
|
||||||
|
|
||||||
|
class Point3;
|
||||||
|
class Vector3;
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class HBV
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HBV();
|
||||||
|
~HBV();
|
||||||
|
|
||||||
|
Geometry* clone() const;
|
||||||
|
|
||||||
|
void open();
|
||||||
|
void add(const T& element);
|
||||||
|
void addMany(const std::vector<T>& v);
|
||||||
|
void close();
|
||||||
|
|
||||||
|
bool vectorIntersect(const Point3& origin,const Vector3& dir,float& dist) const;
|
||||||
|
void getBounds(BoundingBox& result) const;
|
||||||
|
void getNormal(const Point3& pt,Vector3& result) const;
|
||||||
|
void getUV(const Point3& pt,float& u,float& v) const;
|
||||||
|
|
||||||
|
const T& getIntersectedElement() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable T _cachedElement;
|
||||||
|
HBVNode<T>* _root;
|
||||||
|
std::vector<T>* _elementList;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "HBVLeaf.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
HBV<T>::HBV() : _root(0), _elementList(0), _cachedElement(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
HBV<T>::~HBV()
|
||||||
|
{
|
||||||
|
assert(_elementList==0);
|
||||||
|
delete _root;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
Geometry* HBV<T>::clone() const
|
||||||
|
{
|
||||||
|
assert(0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void HBV<T>::open()
|
||||||
|
{
|
||||||
|
_elementList=new std::vector<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void HBV<T>::add(const T& element)
|
||||||
|
{
|
||||||
|
assert(_elementList!=0);
|
||||||
|
_elementList->push_back(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void HBV<T>::addMany(const std::vector<T>& v)
|
||||||
|
{
|
||||||
|
assert(_elementList!=0);
|
||||||
|
for (int i=0;i<v.size();i++)
|
||||||
|
_elementList->push_back(v.at(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void HBV<T>::close()
|
||||||
|
{
|
||||||
|
std::random_shuffle(_elementList->begin(),_elementList->end());
|
||||||
|
|
||||||
|
for (int i=0;i<_elementList->size();i++) {
|
||||||
|
T element=_elementList->at(i);
|
||||||
|
HBVLeaf<T> *leaf=new HBVLeaf<T>(element);
|
||||||
|
if (_root) {
|
||||||
|
_root->addLeaf(leaf);
|
||||||
|
while (_root->getParent())
|
||||||
|
_root=_root->getParent();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
_root=leaf;
|
||||||
|
}
|
||||||
|
|
||||||
|
_elementList->clear();
|
||||||
|
delete _elementList;
|
||||||
|
_elementList=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
bool HBV<T>::vectorIntersect(const Point3& origin,const Vector3& dir,float& dist) const
|
||||||
|
{
|
||||||
|
T element;
|
||||||
|
|
||||||
|
_cachedElement=0;
|
||||||
|
|
||||||
|
int result=_root->vectorIntersect(origin,dir,dist,element);
|
||||||
|
if (result) {
|
||||||
|
_cachedElement=element;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void HBV<T>::getBounds(BoundingBox& result) const
|
||||||
|
{
|
||||||
|
result=_root->getBounds();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void HBV<T>::getNormal(const Point3& pt,Vector3& result) const
|
||||||
|
{
|
||||||
|
assert(_cachedElement!=0);
|
||||||
|
_cachedElement->getNormal(pt,result);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void HBV<T>::getUV(const Point3& pt,float& u,float& v) const
|
||||||
|
{
|
||||||
|
assert(_cachedElement!=0);
|
||||||
|
_cachedElement->getUV(pt,u,v);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
const T& HBV<T>::getIntersectedElement() const
|
||||||
|
{
|
||||||
|
assert(_cachedElement!=0);
|
||||||
|
return _cachedElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
271
source/terrain/ModelRData.cpp
Executable file
271
source/terrain/ModelRData.cpp
Executable file
@ -0,0 +1,271 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include "res/tex.h"
|
||||||
|
#include "Renderer.h"
|
||||||
|
#include "TransparencyRenderer.h"
|
||||||
|
#include "ModelRData.h"
|
||||||
|
#include "terrain/Model.h"
|
||||||
|
|
||||||
|
extern CRenderer g_Renderer;
|
||||||
|
|
||||||
|
CModelRData::CModelRData(CModel* model) : m_Model(model), m_Vertices(0), m_Indices(0), m_VB(0)
|
||||||
|
{
|
||||||
|
assert(model);
|
||||||
|
// set models renderdata pointer to point to this object
|
||||||
|
m_Model->m_RenderData=this;
|
||||||
|
// build all data now
|
||||||
|
Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
CModelRData::~CModelRData()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CModelRData::Build()
|
||||||
|
{
|
||||||
|
BuildVertices();
|
||||||
|
BuildIndices();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CModelRData::BuildIndices()
|
||||||
|
{
|
||||||
|
CModelDef* mdef=m_Model->GetModelDef();
|
||||||
|
|
||||||
|
// allocate indices if we haven't got any already
|
||||||
|
if (!m_Indices) {
|
||||||
|
m_Indices=new u16[mdef->GetNumFaces()*3];
|
||||||
|
}
|
||||||
|
|
||||||
|
// build indices
|
||||||
|
u32 indices=0;
|
||||||
|
SModelFace* faces=mdef->GetFaces();
|
||||||
|
for (int j=0; j<mdef->GetNumFaces(); j++) {
|
||||||
|
SModelFace& face=faces[j];
|
||||||
|
m_Indices[indices++]=face.m_Verts[0];
|
||||||
|
m_Indices[indices++]=face.m_Verts[1];
|
||||||
|
m_Indices[indices++]=face.m_Verts[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int clamp(int x,int min,int max)
|
||||||
|
{
|
||||||
|
if (x<min) return min;
|
||||||
|
else if (x>max) return max;
|
||||||
|
else return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SColor4ub ConvertColor(const RGBColor& src)
|
||||||
|
{
|
||||||
|
SColor4ub result;
|
||||||
|
result.R=clamp(int(src.X*255),0,255);
|
||||||
|
result.G=clamp(int(src.Y*255),0,255);
|
||||||
|
result.B=clamp(int(src.Z*255),0,255);
|
||||||
|
result.A=0xff;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CModelRData::BuildVertices()
|
||||||
|
{
|
||||||
|
CModelDef* mdef=m_Model->GetModelDef();
|
||||||
|
|
||||||
|
// allocate vertices if we haven't got any already
|
||||||
|
if (!m_Vertices) {
|
||||||
|
m_Vertices=new SVertex[mdef->GetNumVertices()];
|
||||||
|
}
|
||||||
|
|
||||||
|
// build vertices
|
||||||
|
SModelVertex* vertices=mdef->GetVertices();
|
||||||
|
for (int j=0; j<mdef->GetNumVertices(); j++) {
|
||||||
|
if (vertices[j].m_Bone!=-1) {
|
||||||
|
m_Vertices[j].m_Position=m_Model->GetBonePoses()[vertices[j].m_Bone].Transform(vertices[j].m_Coords);
|
||||||
|
} else {
|
||||||
|
m_Vertices[j].m_Position=vertices[j].m_Coords;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_Vertices[j].m_UVs[0]=vertices[j].m_U;
|
||||||
|
m_Vertices[j].m_UVs[1]=1-vertices[j].m_V;
|
||||||
|
|
||||||
|
RGBColor c;
|
||||||
|
g_Renderer.m_SHCoeffsUnits.Evaluate(vertices[j].m_Norm,c);
|
||||||
|
|
||||||
|
m_Vertices[j].m_Color=ConvertColor(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_Renderer.m_Caps.m_VBO) {
|
||||||
|
if (!m_VB) {
|
||||||
|
glGenBuffersARB(1,(GLuint*) &m_VB);
|
||||||
|
glBindBufferARB(GL_ARRAY_BUFFER_ARB,m_VB);
|
||||||
|
glBufferDataARB(GL_ARRAY_BUFFER_ARB,mdef->GetNumVertices()*sizeof(SVertex),0,GL_STATIC_DRAW_ARB);
|
||||||
|
} else {
|
||||||
|
glBindBufferARB(GL_ARRAY_BUFFER_ARB,m_VB);
|
||||||
|
}
|
||||||
|
|
||||||
|
u8* vertices=(u8*) glMapBufferARB(GL_ARRAY_BUFFER_ARB,GL_WRITE_ONLY_ARB);
|
||||||
|
memcpy(vertices,m_Vertices,mdef->GetNumVertices()*sizeof(SVertex));
|
||||||
|
glUnmapBufferARB(GL_ARRAY_BUFFER_ARB);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CModelRData::RenderWireframe(const CMatrix3D& transform,bool transparentPass)
|
||||||
|
{
|
||||||
|
// ignore transparent passes
|
||||||
|
if (!transparentPass && g_Renderer.IsTextureTransparent(m_Model->GetTexture())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(m_UpdateFlags==0);
|
||||||
|
|
||||||
|
u8* base;
|
||||||
|
if (g_Renderer.m_Caps.m_VBO) {
|
||||||
|
glBindBufferARB(GL_ARRAY_BUFFER_ARB,m_VB);
|
||||||
|
base=0;
|
||||||
|
} else {
|
||||||
|
base=(u8*) &m_Vertices[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
glMatrixMode(GL_MODELVIEW);
|
||||||
|
glPushMatrix();
|
||||||
|
|
||||||
|
CMatrix3D tmp;
|
||||||
|
transform.GetTranspose(tmp);
|
||||||
|
glMultMatrixf(&tmp._11);
|
||||||
|
|
||||||
|
|
||||||
|
// set vertex pointers
|
||||||
|
glVertexPointer(3,GL_FLOAT,sizeof(SVertex),base+offsetof(SVertex,m_Position));
|
||||||
|
|
||||||
|
// render the lot
|
||||||
|
u32 numFaces=m_Model->GetModelDef()->GetNumFaces();
|
||||||
|
glDrawElements(GL_TRIANGLES,numFaces*3,GL_UNSIGNED_SHORT,m_Indices);
|
||||||
|
|
||||||
|
// bump stats
|
||||||
|
g_Renderer.m_Stats.m_DrawCalls++;
|
||||||
|
if (transparentPass) {
|
||||||
|
g_Renderer.m_Stats.m_TransparentTris+=numFaces;
|
||||||
|
} else {
|
||||||
|
g_Renderer.m_Stats.m_ModelTris+=numFaces;
|
||||||
|
}
|
||||||
|
|
||||||
|
glPopMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CModelRData::Render(const CMatrix3D& transform,bool transparentPass)
|
||||||
|
{
|
||||||
|
// ignore transparent passes
|
||||||
|
if (!transparentPass && g_Renderer.IsTextureTransparent(m_Model->GetTexture())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CModelDef* mdldef=(CModelDef*) m_Model->GetModelDef();
|
||||||
|
|
||||||
|
glMatrixMode(GL_MODELVIEW);
|
||||||
|
glPushMatrix();
|
||||||
|
|
||||||
|
CMatrix3D tmp;
|
||||||
|
transform.GetTranspose(tmp);
|
||||||
|
glMultMatrixf(&tmp._11);
|
||||||
|
|
||||||
|
g_Renderer.SetTexture(0,m_Model->GetTexture());
|
||||||
|
|
||||||
|
u8* base;
|
||||||
|
if (g_Renderer.m_Caps.m_VBO) {
|
||||||
|
glBindBufferARB(GL_ARRAY_BUFFER_ARB,m_VB);
|
||||||
|
base=0;
|
||||||
|
} else {
|
||||||
|
base=(u8*) &m_Vertices[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// set vertex pointers
|
||||||
|
u32 stride=sizeof(SVertex);
|
||||||
|
glVertexPointer(3,GL_FLOAT,stride,base+offsetof(SVertex,m_Position));
|
||||||
|
glColorPointer(4,GL_UNSIGNED_BYTE,stride,base+offsetof(SVertex,m_Color));
|
||||||
|
glTexCoordPointer(2,GL_FLOAT,stride,base+offsetof(SVertex,m_UVs));
|
||||||
|
|
||||||
|
// render the lot
|
||||||
|
u32 numFaces=mdldef->GetNumFaces();
|
||||||
|
glDrawElements(GL_TRIANGLES,numFaces*3,GL_UNSIGNED_SHORT,m_Indices);
|
||||||
|
|
||||||
|
// bump stats
|
||||||
|
g_Renderer.m_Stats.m_DrawCalls++;
|
||||||
|
if (transparentPass) {
|
||||||
|
g_Renderer.m_Stats.m_TransparentTris+=numFaces;
|
||||||
|
} else {
|
||||||
|
g_Renderer.m_Stats.m_ModelTris+=numFaces;
|
||||||
|
}
|
||||||
|
|
||||||
|
glPopMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CModelRData::Update()
|
||||||
|
{
|
||||||
|
if (m_UpdateFlags!=0) {
|
||||||
|
// renderdata changed : rebuild necessary portions
|
||||||
|
if (m_UpdateFlags & RENDERDATA_UPDATE_VERTICES) {
|
||||||
|
BuildVertices();
|
||||||
|
}
|
||||||
|
if (m_UpdateFlags & RENDERDATA_UPDATE_INDICES) {
|
||||||
|
BuildIndices();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_UpdateFlags=0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef std::pair<int,float> IntFloatPair;
|
||||||
|
static std::vector<IntFloatPair> IndexSorter;
|
||||||
|
|
||||||
|
struct SortFacesByDist {
|
||||||
|
bool operator()(const IntFloatPair& lhs,const IntFloatPair& rhs) {
|
||||||
|
return lhs.second>rhs.second ? true : false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
float CModelRData::BackToFrontIndexSort(CMatrix3D& objToCam)
|
||||||
|
{
|
||||||
|
float mindist=1.0e30f;
|
||||||
|
CVector3D osvtx,csvtx;
|
||||||
|
|
||||||
|
CModelDef* mdldef=(CModelDef*) m_Model->GetModelDef();
|
||||||
|
|
||||||
|
SModelVertex* vtxs=mdldef->GetVertices();
|
||||||
|
|
||||||
|
u32 numFaces=mdldef->GetNumFaces();
|
||||||
|
SModelFace* faces=mdldef->GetFaces();
|
||||||
|
|
||||||
|
IndexSorter.reserve(numFaces);
|
||||||
|
|
||||||
|
SModelFace* facePtr=faces;
|
||||||
|
uint i;
|
||||||
|
for (i=0;i<numFaces;i++)
|
||||||
|
{
|
||||||
|
osvtx=vtxs[facePtr->m_Verts[0]].m_Coords;
|
||||||
|
osvtx+=vtxs[facePtr->m_Verts[1]].m_Coords;
|
||||||
|
osvtx+=vtxs[facePtr->m_Verts[2]].m_Coords;
|
||||||
|
osvtx*=1.0f/3.0f;
|
||||||
|
|
||||||
|
csvtx=objToCam.Transform(osvtx);
|
||||||
|
float distsqrd=SQR(csvtx.X)+SQR(csvtx.Y)+SQR(csvtx.Z);
|
||||||
|
if (distsqrd<mindist) mindist=distsqrd;
|
||||||
|
|
||||||
|
IndexSorter.push_back(IntFloatPair(i,distsqrd));
|
||||||
|
facePtr++;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(IndexSorter.begin(),IndexSorter.end(),SortFacesByDist());
|
||||||
|
|
||||||
|
// now build index list
|
||||||
|
u32 indices=0;
|
||||||
|
for (i=0;i<numFaces;i++) {
|
||||||
|
SModelFace& face=faces[IndexSorter[i].first];
|
||||||
|
m_Indices[indices++]=face.m_Verts[0];
|
||||||
|
m_Indices[indices++]=face.m_Verts[1];
|
||||||
|
m_Indices[indices++]=face.m_Verts[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear list for next call
|
||||||
|
IndexSorter.clear();
|
||||||
|
|
||||||
|
return mindist;
|
||||||
|
}
|
53
source/terrain/ModelRData.h
Executable file
53
source/terrain/ModelRData.h
Executable file
@ -0,0 +1,53 @@
|
|||||||
|
#ifndef _MODELRDATA_H
|
||||||
|
#define _MODELRDATA_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include "res/res.h"
|
||||||
|
#include "Vector3D.h"
|
||||||
|
#include "RenderableObject.h"
|
||||||
|
|
||||||
|
class CModel;
|
||||||
|
|
||||||
|
class CModelRData : public CRenderData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CModelRData(CModel* model);
|
||||||
|
~CModelRData();
|
||||||
|
|
||||||
|
void Update();
|
||||||
|
void Render(const CMatrix3D& transform,bool transparentPass=false);
|
||||||
|
void RenderWireframe(const CMatrix3D& transform,bool transparentPass=false);
|
||||||
|
|
||||||
|
// sort indices of this object from back to front according to given
|
||||||
|
// object to camera space transform; return sqrd distance to centre of nearest triangle
|
||||||
|
float BackToFrontIndexSort(CMatrix3D& objToCam);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// build this renderdata object
|
||||||
|
void Build();
|
||||||
|
|
||||||
|
void BuildVertices();
|
||||||
|
void BuildIndices();
|
||||||
|
|
||||||
|
|
||||||
|
struct SVertex {
|
||||||
|
// vertex position
|
||||||
|
CVector3D m_Position;
|
||||||
|
// vertex uvs for base texture
|
||||||
|
float m_UVs[2];
|
||||||
|
// vertex color
|
||||||
|
SColor4ub m_Color;
|
||||||
|
};
|
||||||
|
|
||||||
|
// owner model
|
||||||
|
CModel* m_Model;
|
||||||
|
// handle to models vertex buffer
|
||||||
|
u32 m_VB;
|
||||||
|
// model render vertices
|
||||||
|
SVertex* m_Vertices;
|
||||||
|
// model render indices
|
||||||
|
u16* m_Indices;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
579
source/terrain/PatchRData.cpp
Executable file
579
source/terrain/PatchRData.cpp
Executable file
@ -0,0 +1,579 @@
|
|||||||
|
#pragma warning(disable:4786)
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <set>
|
||||||
|
#include <algorithm>
|
||||||
|
#include "res/tex.h"
|
||||||
|
#include "Renderer.h"
|
||||||
|
#include "PatchRData.h"
|
||||||
|
#include "AlphaMapCalculator.h"
|
||||||
|
|
||||||
|
extern CRenderer g_Renderer;
|
||||||
|
|
||||||
|
const int BlendOffsets[8][2] = {
|
||||||
|
{ 0, -1 },
|
||||||
|
{ -1, -1 },
|
||||||
|
{ -1, 0 },
|
||||||
|
{ -1, 1 },
|
||||||
|
{ 0, 1 },
|
||||||
|
{ 1, 1 },
|
||||||
|
{ 1, 0 },
|
||||||
|
{ 1, -1 }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
CPatchRData::CPatchRData(CPatch* patch) : m_Patch(patch), m_Vertices(0), m_VBBase(0), m_VBBlends(0)
|
||||||
|
{
|
||||||
|
assert(patch);
|
||||||
|
// set patches renderdata pointer to point to this object
|
||||||
|
m_Patch->m_RenderData=this;
|
||||||
|
// build all data now
|
||||||
|
Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
CPatchRData::~CPatchRData()
|
||||||
|
{
|
||||||
|
delete[] m_Vertices;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static Handle GetTerrainTileTexture(CTerrain* terrain,int gx,int gz)
|
||||||
|
{
|
||||||
|
CMiniPatch* mp=terrain->GetTile(gx,gz);
|
||||||
|
return mp ? mp->Tex1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QueryAdjacency(int x,int y,Handle h,Handle* texgrid)
|
||||||
|
{
|
||||||
|
for (int j=y-1;j<=y+1;j++) {
|
||||||
|
for (int i=x-1;i<=x+1;i++) {
|
||||||
|
if (i<0 || i>PATCH_SIZE+1 || j<0 || j>PATCH_SIZE+1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (texgrid[j*(PATCH_SIZE+2)+i]==h) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct STmpSplat {
|
||||||
|
Handle m_Texture;
|
||||||
|
u16 m_Indices[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
void CPatchRData::BuildBlends()
|
||||||
|
{
|
||||||
|
m_BlendIndices.clear();
|
||||||
|
m_BlendSplats.clear();
|
||||||
|
m_BlendVertices.clear();
|
||||||
|
|
||||||
|
// get index of this patch
|
||||||
|
int px=m_Patch->m_X;
|
||||||
|
int pz=m_Patch->m_Z;
|
||||||
|
|
||||||
|
CTerrain* terrain=m_Patch->m_Parent;
|
||||||
|
|
||||||
|
// temporary list of splats
|
||||||
|
std::vector<STmpSplat> splats;
|
||||||
|
// set of textures used for splats
|
||||||
|
std::set<Handle> splatTextures;
|
||||||
|
|
||||||
|
// for each tile in patch ..
|
||||||
|
for (int j=0;j<PATCH_SIZE;j++) {
|
||||||
|
for (int i=0;i<PATCH_SIZE;i++) {
|
||||||
|
u32 gx,gz;
|
||||||
|
CMiniPatch* mp=&m_Patch->m_MiniPatches[j][i];
|
||||||
|
mp->GetTileIndex(gx,gz);
|
||||||
|
|
||||||
|
// build list of textures of higher priority than current tile that are used by neighbouring tiles
|
||||||
|
std::vector<STex> neighbourTextures;
|
||||||
|
for (int m=-1;m<=1;m++) {
|
||||||
|
for (int k=-1;k<=1;k++) {
|
||||||
|
CMiniPatch* nmp=terrain->GetTile(gx+k,gz+m);
|
||||||
|
if (nmp) {
|
||||||
|
if (nmp->Tex1Priority>mp->Tex1Priority || (nmp->Tex1Priority==mp->Tex1Priority && nmp->Tex1>mp->Tex1)) {
|
||||||
|
STex tex;
|
||||||
|
tex.m_Handle=nmp->Tex1;
|
||||||
|
tex.m_Priority=nmp->Tex1Priority;
|
||||||
|
if (std::find(neighbourTextures.begin(),neighbourTextures.end(),tex)==neighbourTextures.end()) {
|
||||||
|
neighbourTextures.push_back(tex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (neighbourTextures.size()>0) {
|
||||||
|
u32 count=neighbourTextures.size();
|
||||||
|
// sort textures from lowest to highest priority
|
||||||
|
std::sort(neighbourTextures.begin(),neighbourTextures.end());
|
||||||
|
|
||||||
|
// for each of the neighbouring textures ..
|
||||||
|
for (uint k=0;k<neighbourTextures.size();++k) {
|
||||||
|
|
||||||
|
// now build the grid of blends dependent on whether the tile adjacent to the current tile
|
||||||
|
// uses the current neighbour texture
|
||||||
|
BlendShape8 shape;
|
||||||
|
for (int m=0;m<8;m++) {
|
||||||
|
int ox=gx+BlendOffsets[m][1];
|
||||||
|
int oz=gz+BlendOffsets[m][0];
|
||||||
|
|
||||||
|
// get texture on adjacent tile
|
||||||
|
Handle atex=GetTerrainTileTexture(terrain,ox,oz);
|
||||||
|
// fill 0/1 into shape array
|
||||||
|
shape[m]=(atex==neighbourTextures[k].m_Handle) ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate the required alphamap and the required rotation of the alphamap from blendshape
|
||||||
|
unsigned int alphamapflags;
|
||||||
|
int alphamap=CAlphaMapCalculator::Calculate(shape,alphamapflags);
|
||||||
|
|
||||||
|
// now actually render the blend tile (if we need one)
|
||||||
|
if (alphamap!=-1) {
|
||||||
|
float u0=g_Renderer.m_AlphaMapCoords[alphamap].u0;
|
||||||
|
float u1=g_Renderer.m_AlphaMapCoords[alphamap].u1;
|
||||||
|
float v0=g_Renderer.m_AlphaMapCoords[alphamap].v0;
|
||||||
|
float v1=g_Renderer.m_AlphaMapCoords[alphamap].v1;
|
||||||
|
if (alphamapflags & 0x01) {
|
||||||
|
// flip u
|
||||||
|
float t=u0;
|
||||||
|
u0=u1;
|
||||||
|
u1=t;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (alphamapflags & 0x02) {
|
||||||
|
// flip v
|
||||||
|
float t=v0;
|
||||||
|
v0=v1;
|
||||||
|
v1=t;
|
||||||
|
}
|
||||||
|
|
||||||
|
int base=0;
|
||||||
|
if (alphamapflags & 0x04) {
|
||||||
|
// rotate 1
|
||||||
|
base=1;
|
||||||
|
} else if (alphamapflags & 0x08) {
|
||||||
|
// rotate 2
|
||||||
|
base=2;
|
||||||
|
} else if (alphamapflags & 0x10) {
|
||||||
|
// rotate 3
|
||||||
|
base=3;
|
||||||
|
}
|
||||||
|
|
||||||
|
SBlendVertex vtx[4];
|
||||||
|
vtx[(base+0)%4].m_AlphaUVs[0]=u0;
|
||||||
|
vtx[(base+0)%4].m_AlphaUVs[1]=v0;
|
||||||
|
vtx[(base+1)%4].m_AlphaUVs[0]=u1;
|
||||||
|
vtx[(base+1)%4].m_AlphaUVs[1]=v0;
|
||||||
|
vtx[(base+2)%4].m_AlphaUVs[0]=u1;
|
||||||
|
vtx[(base+2)%4].m_AlphaUVs[1]=v1;
|
||||||
|
vtx[(base+3)%4].m_AlphaUVs[0]=u0;
|
||||||
|
vtx[(base+3)%4].m_AlphaUVs[1]=v1;
|
||||||
|
|
||||||
|
int vsize=PATCH_SIZE+1;
|
||||||
|
|
||||||
|
SBlendVertex dst;
|
||||||
|
int vindex=m_BlendVertices.size();
|
||||||
|
|
||||||
|
const SBaseVertex& vtx0=m_Vertices[(j*vsize)+i];
|
||||||
|
dst.m_UVs[0]=i*0.125f;
|
||||||
|
dst.m_UVs[1]=j*0.125f;
|
||||||
|
dst.m_AlphaUVs[0]=vtx[0].m_AlphaUVs[0];
|
||||||
|
dst.m_AlphaUVs[1]=vtx[0].m_AlphaUVs[1];
|
||||||
|
dst.m_Color=vtx0.m_Color;
|
||||||
|
dst.m_Position=vtx0.m_Position;
|
||||||
|
m_BlendVertices.push_back(dst);
|
||||||
|
|
||||||
|
const SBaseVertex& vtx1=m_Vertices[(j*vsize)+i+1];
|
||||||
|
dst.m_UVs[0]=(i+1)*0.125f;
|
||||||
|
dst.m_UVs[1]=j*0.125f;
|
||||||
|
dst.m_AlphaUVs[0]=vtx[1].m_AlphaUVs[0];
|
||||||
|
dst.m_AlphaUVs[1]=vtx[1].m_AlphaUVs[1];
|
||||||
|
dst.m_Color=vtx1.m_Color;
|
||||||
|
dst.m_Position=vtx1.m_Position;
|
||||||
|
m_BlendVertices.push_back(dst);
|
||||||
|
|
||||||
|
const SBaseVertex& vtx2=m_Vertices[((j+1)*vsize)+i+1];
|
||||||
|
dst.m_UVs[0]=(i+1)*0.125f;
|
||||||
|
dst.m_UVs[1]=(j+1)*0.125f;
|
||||||
|
dst.m_AlphaUVs[0]=vtx[2].m_AlphaUVs[0];
|
||||||
|
dst.m_AlphaUVs[1]=vtx[2].m_AlphaUVs[1];
|
||||||
|
dst.m_Color=vtx2.m_Color;
|
||||||
|
dst.m_Position=vtx2.m_Position;
|
||||||
|
m_BlendVertices.push_back(dst);
|
||||||
|
|
||||||
|
const SBaseVertex& vtx3=m_Vertices[((j+1)*vsize)+i];
|
||||||
|
dst.m_UVs[0]=i*0.125f;
|
||||||
|
dst.m_UVs[1]=(j+1)*0.125f;
|
||||||
|
dst.m_AlphaUVs[0]=vtx[3].m_AlphaUVs[0];
|
||||||
|
dst.m_AlphaUVs[1]=vtx[3].m_AlphaUVs[1];
|
||||||
|
dst.m_Color=vtx3.m_Color;
|
||||||
|
dst.m_Position=vtx3.m_Position;
|
||||||
|
m_BlendVertices.push_back(dst);
|
||||||
|
|
||||||
|
// build a splat for this quad
|
||||||
|
STmpSplat splat;
|
||||||
|
splat.m_Texture=neighbourTextures[k].m_Handle;
|
||||||
|
splat.m_Indices[0]=vindex;
|
||||||
|
splat.m_Indices[1]=vindex+1;
|
||||||
|
splat.m_Indices[2]=vindex+2;
|
||||||
|
splat.m_Indices[3]=vindex+3;
|
||||||
|
splats.push_back(splat);
|
||||||
|
|
||||||
|
// add this texture to set of unique splat textures
|
||||||
|
splatTextures.insert(splat.m_Texture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// now build outgoing splats
|
||||||
|
m_BlendSplats.resize(splatTextures.size());
|
||||||
|
int splatCount=0;
|
||||||
|
|
||||||
|
std::set<Handle>::iterator iter=splatTextures.begin();
|
||||||
|
for (;iter!=splatTextures.end();++iter) {
|
||||||
|
Handle tex=*iter;
|
||||||
|
|
||||||
|
SSplat& splat=m_BlendSplats[splatCount];
|
||||||
|
splat.m_IndexStart=m_BlendIndices.size();
|
||||||
|
splat.m_Texture=tex;
|
||||||
|
|
||||||
|
for (uint k=0;k<splats.size();k++) {
|
||||||
|
if (splats[k].m_Texture==tex) {
|
||||||
|
m_BlendIndices.push_back(splats[k].m_Indices[0]);
|
||||||
|
m_BlendIndices.push_back(splats[k].m_Indices[1]);
|
||||||
|
m_BlendIndices.push_back(splats[k].m_Indices[2]);
|
||||||
|
m_BlendIndices.push_back(splats[k].m_Indices[3]);
|
||||||
|
splat.m_IndexCount+=4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
splatCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_Renderer.m_Caps.m_VBO) {
|
||||||
|
if (m_VBBlends) {
|
||||||
|
// destroy old buffer
|
||||||
|
glDeleteBuffersARB(1,(GLuint*) &m_VBBlends);
|
||||||
|
} else {
|
||||||
|
// generate buffer index
|
||||||
|
glGenBuffersARB(1,(GLuint*) &m_VBBlends);
|
||||||
|
}
|
||||||
|
|
||||||
|
// create new buffer
|
||||||
|
glBindBufferARB(GL_ARRAY_BUFFER_ARB,m_VBBlends);
|
||||||
|
glBufferDataARB(GL_ARRAY_BUFFER_ARB,m_BlendVertices.size()*sizeof(SBlendVertex),0,GL_STATIC_DRAW_ARB);
|
||||||
|
|
||||||
|
u8* vertices=(u8*) glMapBufferARB(GL_ARRAY_BUFFER_ARB,GL_WRITE_ONLY_ARB);
|
||||||
|
memcpy(vertices,&m_BlendVertices[0],sizeof(SBlendVertex)*m_BlendVertices.size());
|
||||||
|
glUnmapBufferARB(GL_ARRAY_BUFFER_ARB);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPatchRData::BuildIndices()
|
||||||
|
{
|
||||||
|
// number of vertices in each direction in each patch
|
||||||
|
int vsize=PATCH_SIZE+1;
|
||||||
|
|
||||||
|
// release existing indices and bins
|
||||||
|
m_Indices.clear();
|
||||||
|
m_Splats.clear();
|
||||||
|
|
||||||
|
// build grid of textures on this patch and boundaries of adjacent patches
|
||||||
|
std::vector<Handle> textures;
|
||||||
|
Handle texgrid[PATCH_SIZE][PATCH_SIZE];
|
||||||
|
for (int j=0;j<PATCH_SIZE;j++) {
|
||||||
|
for (int i=0;i<PATCH_SIZE;i++) {
|
||||||
|
Handle h=m_Patch->m_MiniPatches[j][i].Tex1;
|
||||||
|
texgrid[j][i]=h;
|
||||||
|
if (std::find(textures.begin(),textures.end(),h)==textures.end()) {
|
||||||
|
textures.push_back(h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// now build base splats from interior textures
|
||||||
|
m_Splats.resize(textures.size());
|
||||||
|
|
||||||
|
for (uint i=0;i<m_Splats.size();i++) {
|
||||||
|
Handle h=textures[i];
|
||||||
|
|
||||||
|
SSplat& splat=m_Splats[i];
|
||||||
|
splat.m_Texture=h;
|
||||||
|
splat.m_IndexStart=m_Indices.size();
|
||||||
|
|
||||||
|
for (int j=0;j<PATCH_SIZE;j++) {
|
||||||
|
for (int i=0;i<PATCH_SIZE;i++) {
|
||||||
|
if (texgrid[j][i]==h){
|
||||||
|
m_Indices.push_back(((j+0)*vsize+(i+0)));
|
||||||
|
m_Indices.push_back(((j+0)*vsize+(i+1)));
|
||||||
|
m_Indices.push_back(((j+1)*vsize+(i+1)));
|
||||||
|
m_Indices.push_back(((j+1)*vsize+(i+0)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
splat.m_IndexCount=m_Indices.size()-splat.m_IndexStart;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int clamp(int x,int min,int max)
|
||||||
|
{
|
||||||
|
if (x<min) return min;
|
||||||
|
else if (x>max) return max;
|
||||||
|
else return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SColor4ub ConvertColor(const RGBColor& src)
|
||||||
|
{
|
||||||
|
SColor4ub result;
|
||||||
|
result.R=clamp(int(src.X*255),0,255);
|
||||||
|
result.G=clamp(int(src.Y*255),0,255);
|
||||||
|
result.B=clamp(int(src.Z*255),0,255);
|
||||||
|
result.A=0xff;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void BuildHeightmapNormals(int size,u16 *heightmap,CVector3D* normals)
|
||||||
|
{
|
||||||
|
int x, y;
|
||||||
|
int sm=size-1;
|
||||||
|
|
||||||
|
for(y = 0;y < size; y++)
|
||||||
|
for(x = 0; x < size; x++) {
|
||||||
|
|
||||||
|
// Access current normalmap grid point
|
||||||
|
CVector3D* N = &normals[y*size+x];
|
||||||
|
|
||||||
|
// Compute normal by using the height differential
|
||||||
|
u16 h1=(x==sm) ? heightmap[y*size+x] : heightmap[y*size+x+1];
|
||||||
|
u16 h2=(y==sm) ? heightmap[y*size+x] : heightmap[(y+1)*size+x];
|
||||||
|
u16 h3=(x==0) ? heightmap[y*size+x] : heightmap[y*size+x-1];
|
||||||
|
u16 h4=(y==0) ? heightmap[y*size+x] : heightmap[(y-1)*size+x+1];
|
||||||
|
N->X = (h3-h1)*HEIGHT_SCALE;
|
||||||
|
N->Y = CELL_SIZE;
|
||||||
|
N->Z = (h4-h2)*HEIGHT_SCALE;
|
||||||
|
|
||||||
|
// Normalize it
|
||||||
|
float len=N->GetLength();
|
||||||
|
if (len>0) {
|
||||||
|
(*N)*=1.0f/len;
|
||||||
|
} else {
|
||||||
|
*N=CVector3D(0,0,0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPatchRData::BuildVertices()
|
||||||
|
{
|
||||||
|
// number of vertices in each direction in each patch
|
||||||
|
int vsize=PATCH_SIZE+1;
|
||||||
|
|
||||||
|
if (!m_Vertices) {
|
||||||
|
m_Vertices=new SBaseVertex[vsize*vsize];
|
||||||
|
}
|
||||||
|
SBaseVertex* vertices=m_Vertices;
|
||||||
|
|
||||||
|
|
||||||
|
// get index of this patch
|
||||||
|
u32 px=m_Patch->m_X;
|
||||||
|
u32 pz=m_Patch->m_Z;
|
||||||
|
|
||||||
|
CTerrain* terrain=m_Patch->m_Parent;
|
||||||
|
u32 mapSize=terrain->GetVerticesPerSide();
|
||||||
|
|
||||||
|
// CVector3D* normals=new CVector3D[mapSize*mapSize];
|
||||||
|
// BuildHeightmapNormals(mapSize,terrain->GetHeightMap(),normals);
|
||||||
|
|
||||||
|
// build vertices
|
||||||
|
for (int j=0; j<vsize; j++)
|
||||||
|
{
|
||||||
|
for (int i=0; i<vsize; i++)
|
||||||
|
{
|
||||||
|
int ix=px*16+i;
|
||||||
|
int iz=pz*16+j;
|
||||||
|
|
||||||
|
CVector3D pos,normal;
|
||||||
|
terrain->CalcPosition(ix,iz,pos);
|
||||||
|
|
||||||
|
// normal=normals[iz*mapSize+ix];
|
||||||
|
terrain->CalcNormal(ix,iz,normal);
|
||||||
|
|
||||||
|
RGBColor c;
|
||||||
|
g_Renderer.m_SHCoeffsTerrain.Evaluate(normal,c);
|
||||||
|
|
||||||
|
int v=(j*vsize)+i;
|
||||||
|
vertices[v].m_UVs[0]=i*0.125f;
|
||||||
|
vertices[v].m_UVs[1]=j*0.125f;
|
||||||
|
vertices[v].m_Color=ConvertColor(c);
|
||||||
|
vertices[v].m_Position=pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_Renderer.m_Caps.m_VBO) {
|
||||||
|
if (!m_VBBase) {
|
||||||
|
glGenBuffersARB(1,(GLuint*) &m_VBBase);
|
||||||
|
glBindBufferARB(GL_ARRAY_BUFFER_ARB,m_VBBase);
|
||||||
|
glBufferDataARB(GL_ARRAY_BUFFER_ARB,vsize*vsize*sizeof(SBaseVertex),0,GL_STATIC_DRAW_ARB);
|
||||||
|
} else {
|
||||||
|
glBindBufferARB(GL_ARRAY_BUFFER_ARB,m_VBBase);
|
||||||
|
}
|
||||||
|
|
||||||
|
u8* vertices=(u8*) glMapBufferARB(GL_ARRAY_BUFFER_ARB,GL_WRITE_ONLY_ARB);
|
||||||
|
memcpy(vertices,m_Vertices,sizeof(SBaseVertex)*vsize*vsize);
|
||||||
|
glUnmapBufferARB(GL_ARRAY_BUFFER_ARB);
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete[] normals;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPatchRData::Build()
|
||||||
|
{
|
||||||
|
BuildVertices();
|
||||||
|
BuildIndices();
|
||||||
|
BuildBlends();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPatchRData::Update()
|
||||||
|
{
|
||||||
|
if (m_UpdateFlags!=0) {
|
||||||
|
// renderdata changed : rebuild necessary portions
|
||||||
|
/*
|
||||||
|
if (data->m_UpdateFlags & RENDERDATA_UPDATE_VERTICES) {
|
||||||
|
BuildPatchVertices(patch,data);
|
||||||
|
}
|
||||||
|
if (data->m_UpdateFlags & RENDERDATA_UPDATE_INDICES) {
|
||||||
|
BuildPatchIndices(patch,data);
|
||||||
|
BuildPatchBlends(patch,data);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
BuildVertices();
|
||||||
|
BuildIndices();
|
||||||
|
BuildBlends();
|
||||||
|
|
||||||
|
m_UpdateFlags=0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPatchRData::RenderBase()
|
||||||
|
{
|
||||||
|
assert(m_UpdateFlags==0);
|
||||||
|
|
||||||
|
u8* base;
|
||||||
|
if (g_Renderer.m_Caps.m_VBO) {
|
||||||
|
glBindBufferARB(GL_ARRAY_BUFFER_ARB,m_VBBase);
|
||||||
|
base=0;
|
||||||
|
} else {
|
||||||
|
base=(u8*) &m_Vertices[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup data pointers
|
||||||
|
u32 stride=sizeof(SBaseVertex);
|
||||||
|
glVertexPointer(3,GL_FLOAT,stride,base+offsetof(SBaseVertex,m_Position));
|
||||||
|
glColorPointer(4,GL_UNSIGNED_BYTE,stride,base+offsetof(SBaseVertex,m_Color));
|
||||||
|
glTexCoordPointer(2,GL_FLOAT,stride,base+offsetof(SBaseVertex,m_UVs[0]));
|
||||||
|
|
||||||
|
// render each splat
|
||||||
|
for (uint i=0;i<m_Splats.size();i++) {
|
||||||
|
SSplat& splat=m_Splats[i];
|
||||||
|
tex_bind(splat.m_Texture);
|
||||||
|
glDrawElements(GL_QUADS,splat.m_IndexCount,GL_UNSIGNED_SHORT,&m_Indices[splat.m_IndexStart]);
|
||||||
|
// bump stats
|
||||||
|
g_Renderer.m_Stats.m_DrawCalls++;
|
||||||
|
g_Renderer.m_Stats.m_TerrainTris+=splat.m_IndexCount/2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPatchRData::RenderWireframe()
|
||||||
|
{
|
||||||
|
assert(m_UpdateFlags==0);
|
||||||
|
|
||||||
|
u8* base;
|
||||||
|
if (g_Renderer.m_Caps.m_VBO) {
|
||||||
|
glBindBufferARB(GL_ARRAY_BUFFER_ARB,m_VBBase);
|
||||||
|
base=0;
|
||||||
|
} else {
|
||||||
|
base=(u8*) &m_Vertices[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup data pointers
|
||||||
|
glVertexPointer(3,GL_FLOAT,sizeof(SBaseVertex),base+offsetof(SBaseVertex,m_Position));
|
||||||
|
|
||||||
|
// render all base splats at once
|
||||||
|
glDrawElements(GL_QUADS,m_Indices.size(),GL_UNSIGNED_SHORT,&m_Indices[0]);
|
||||||
|
|
||||||
|
// bump stats
|
||||||
|
g_Renderer.m_Stats.m_DrawCalls++;
|
||||||
|
g_Renderer.m_Stats.m_TerrainTris+=m_Indices.size()/2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CPatchRData::RenderBlends()
|
||||||
|
{
|
||||||
|
assert(m_UpdateFlags==0);
|
||||||
|
|
||||||
|
if (m_BlendVertices.size()==0) return;
|
||||||
|
|
||||||
|
u8* base;
|
||||||
|
if (g_Renderer.m_Caps.m_VBO) {
|
||||||
|
glBindBufferARB(GL_ARRAY_BUFFER_ARB,m_VBBlends);
|
||||||
|
base=0;
|
||||||
|
} else {
|
||||||
|
base=(u8*) &m_BlendVertices[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// setup data pointers
|
||||||
|
u32 stride=sizeof(SBlendVertex);
|
||||||
|
glVertexPointer(3,GL_FLOAT,stride,base+offsetof(SBlendVertex,m_Position));
|
||||||
|
glColorPointer(4,GL_UNSIGNED_BYTE,stride,base+offsetof(SBlendVertex,m_Color));
|
||||||
|
|
||||||
|
glClientActiveTexture(GL_TEXTURE0_ARB);
|
||||||
|
glTexCoordPointer(2,GL_FLOAT,stride,base+offsetof(SBlendVertex,m_UVs[0]));
|
||||||
|
|
||||||
|
glClientActiveTexture(GL_TEXTURE1_ARB);
|
||||||
|
glTexCoordPointer(2,GL_FLOAT,stride,base+offsetof(SBlendVertex,m_AlphaUVs[0]));
|
||||||
|
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
glEnable(GL_TEXTURE_2D);
|
||||||
|
|
||||||
|
for (uint i=0;i<m_BlendSplats.size();i++) {
|
||||||
|
SSplat& splat=m_BlendSplats[i];
|
||||||
|
tex_bind(splat.m_Texture);
|
||||||
|
glDrawElements(GL_QUADS,splat.m_IndexCount,GL_UNSIGNED_SHORT,&m_BlendIndices[splat.m_IndexStart]);
|
||||||
|
|
||||||
|
// bump stats
|
||||||
|
g_Renderer.m_Stats.m_DrawCalls++;
|
||||||
|
g_Renderer.m_Stats.m_BlendSplats++;
|
||||||
|
g_Renderer.m_Stats.m_TerrainTris+=splat.m_IndexCount/2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPatchRData::RenderOutline()
|
||||||
|
{
|
||||||
|
const u16 EdgeIndices[PATCH_SIZE*4] = {
|
||||||
|
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
|
||||||
|
33, 50, 67, 84, 101, 118, 135, 152, 169, 186, 203, 220, 237, 254, 271, 288,
|
||||||
|
287, 286, 285, 284, 283, 282, 281, 280, 279, 278, 277, 276, 275, 274, 273, 272,
|
||||||
|
255, 238, 221, 204, 187, 170, 153, 136, 119, 102, 85, 68, 51, 34, 17, 0
|
||||||
|
};
|
||||||
|
|
||||||
|
u8* base;
|
||||||
|
if (g_Renderer.m_Caps.m_VBO) {
|
||||||
|
glBindBufferARB(GL_ARRAY_BUFFER_ARB,m_VBBase);
|
||||||
|
base=0;
|
||||||
|
} else {
|
||||||
|
base=(u8*) &m_Vertices[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup data pointers
|
||||||
|
glVertexPointer(3,GL_FLOAT,sizeof(SBaseVertex),base+offsetof(SBaseVertex,m_Position));
|
||||||
|
// render outline as line loop
|
||||||
|
u32 numIndices=sizeof(EdgeIndices)/sizeof(u16);
|
||||||
|
glDrawElements(GL_LINE_LOOP,numIndices,GL_UNSIGNED_SHORT,EdgeIndices);
|
||||||
|
|
||||||
|
g_Renderer.m_Stats.m_DrawCalls++;
|
||||||
|
g_Renderer.m_Stats.m_TerrainTris+=numIndices/2;
|
||||||
|
}
|
89
source/terrain/PatchRData.h
Executable file
89
source/terrain/PatchRData.h
Executable file
@ -0,0 +1,89 @@
|
|||||||
|
#ifndef _PATCHRDATA_H
|
||||||
|
#define _PATCHRDATA_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include "res/res.h"
|
||||||
|
#include "Vector3D.h"
|
||||||
|
#include "RenderableObject.h"
|
||||||
|
|
||||||
|
|
||||||
|
class CPatchRData : public CRenderData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CPatchRData(CPatch* patch);
|
||||||
|
~CPatchRData();
|
||||||
|
|
||||||
|
void Update();
|
||||||
|
void RenderBase();
|
||||||
|
void RenderBlends();
|
||||||
|
void RenderOutline();
|
||||||
|
void RenderWireframe();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// build this renderdata object
|
||||||
|
void Build();
|
||||||
|
|
||||||
|
void BuildBlends();
|
||||||
|
void BuildIndices();
|
||||||
|
void BuildVertices();
|
||||||
|
|
||||||
|
struct SSplat {
|
||||||
|
SSplat() : m_Texture(0), m_IndexCount(0) {}
|
||||||
|
|
||||||
|
// handle of texture to apply during splat
|
||||||
|
Handle m_Texture;
|
||||||
|
// offset into the index array for this patch where splat starts
|
||||||
|
u32 m_IndexStart;
|
||||||
|
// number of indices used by splat
|
||||||
|
u32 m_IndexCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SBaseVertex {
|
||||||
|
// vertex position
|
||||||
|
CVector3D m_Position;
|
||||||
|
// vertex color
|
||||||
|
SColor4ub m_Color;
|
||||||
|
// vertex uvs for base texture
|
||||||
|
float m_UVs[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SBlendVertex {
|
||||||
|
// vertex position
|
||||||
|
CVector3D m_Position;
|
||||||
|
// vertex color
|
||||||
|
SColor4ub m_Color;
|
||||||
|
// vertex uvs for base texture
|
||||||
|
float m_UVs[2];
|
||||||
|
// vertex uvs for alpha texture
|
||||||
|
float m_AlphaUVs[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct STex {
|
||||||
|
bool operator==(const STex& rhs) const { return m_Handle==rhs.m_Handle; }
|
||||||
|
bool operator<(const STex& rhs) const { return m_Priority<rhs.m_Priority; }
|
||||||
|
Handle m_Handle;
|
||||||
|
int m_Priority;
|
||||||
|
};
|
||||||
|
|
||||||
|
// owner patch
|
||||||
|
CPatch* m_Patch;
|
||||||
|
// vertex buffer handle for base vertices
|
||||||
|
u32 m_VBBase;
|
||||||
|
// vertex buffer handle for blend vertices
|
||||||
|
u32 m_VBBlends;
|
||||||
|
// patch render vertices
|
||||||
|
SBaseVertex* m_Vertices;
|
||||||
|
// patch index list
|
||||||
|
std::vector<unsigned short> m_Indices;
|
||||||
|
// list of base splats to apply to this patch
|
||||||
|
std::vector<SSplat> m_Splats;
|
||||||
|
// vertices to use for blending transition texture passes
|
||||||
|
std::vector<SBlendVertex> m_BlendVertices;
|
||||||
|
// indices into blend vertices for the blend splats
|
||||||
|
std::vector<unsigned short> m_BlendIndices;
|
||||||
|
// splats used in blend pass
|
||||||
|
std::vector<SSplat> m_BlendSplats;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
52
source/terrain/RenderableObject.h
Executable file
52
source/terrain/RenderableObject.h
Executable file
@ -0,0 +1,52 @@
|
|||||||
|
#ifndef _RENDERABLEOBJECT_H
|
||||||
|
#define _RENDERABLEOBJECT_H
|
||||||
|
|
||||||
|
#include "res/res.h"
|
||||||
|
#include "types.h"
|
||||||
|
#include "terrain/Bound.h"
|
||||||
|
#include "terrain/Matrix3D.h"
|
||||||
|
|
||||||
|
|
||||||
|
// dirty flags
|
||||||
|
#define RENDERDATA_UPDATE_VERTICES (1<<1)
|
||||||
|
#define RENDERDATA_UPDATE_INDICES (1<<2)
|
||||||
|
|
||||||
|
class CRenderData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CRenderData() : m_UpdateFlags(0) {}
|
||||||
|
virtual ~CRenderData() {}
|
||||||
|
|
||||||
|
u32 m_UpdateFlags;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CRenderableObject
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CRenderableObject() : m_RenderData(0) {
|
||||||
|
m_Transform.SetIdentity();
|
||||||
|
}
|
||||||
|
virtual ~CRenderableObject() { delete m_RenderData; }
|
||||||
|
|
||||||
|
void SetTransform(const CMatrix3D& transform) {
|
||||||
|
m_Transform=transform;
|
||||||
|
CalcBounds();
|
||||||
|
}
|
||||||
|
const CMatrix3D& GetTransform() const { return m_Transform; }
|
||||||
|
|
||||||
|
|
||||||
|
// CalcBounds: calculate (and store in m_Bounds) the world space bounds of this object
|
||||||
|
virtual void CalcBounds() = 0;
|
||||||
|
const CBound& GetBounds() const { return m_Bounds; }
|
||||||
|
|
||||||
|
// object renderdata
|
||||||
|
CRenderData* m_RenderData;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// object bounds
|
||||||
|
CBound m_Bounds;
|
||||||
|
// local->world space transform
|
||||||
|
CMatrix3D m_Transform;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
24
source/terrain/TextureEntry.h
Executable file
24
source/terrain/TextureEntry.h
Executable file
@ -0,0 +1,24 @@
|
|||||||
|
#ifndef _TEXTUREENTRY_H
|
||||||
|
#define _TEXTUREENTRY_H
|
||||||
|
|
||||||
|
#include "res/res.h"
|
||||||
|
#include "CStr.h"
|
||||||
|
|
||||||
|
class CTextureEntry
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CTextureEntry() : m_Bitmap(0) {}
|
||||||
|
|
||||||
|
// filename
|
||||||
|
CStr m_Name;
|
||||||
|
// UI bitmap object
|
||||||
|
void* m_Bitmap;
|
||||||
|
// handle to GL texture data
|
||||||
|
Handle m_Handle;
|
||||||
|
// BGRA color of topmost mipmap level, for coloring minimap
|
||||||
|
unsigned int m_BaseColor;
|
||||||
|
// "type" of texture - index into TextureManager texturetypes array
|
||||||
|
int m_Type;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
214
source/terrain/TextureManager.cpp
Executable file
214
source/terrain/TextureManager.cpp
Executable file
@ -0,0 +1,214 @@
|
|||||||
|
#include "TextureManager.h"
|
||||||
|
#include "lib.h"
|
||||||
|
#include "ogl.h"
|
||||||
|
#include "res/tex.h"
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <io.h>
|
||||||
|
#endif
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
const char* SupportedTextureFormats[] = { "png", "dds", "tga", "bmp" };
|
||||||
|
|
||||||
|
|
||||||
|
CTextureManager g_TexMan;
|
||||||
|
|
||||||
|
int GetNumMipmaps(int w,int h)
|
||||||
|
{
|
||||||
|
int mip=0;
|
||||||
|
int dim=(w > h) ? w : h;
|
||||||
|
while(dim) {
|
||||||
|
dim>>=1;
|
||||||
|
mip++;
|
||||||
|
}
|
||||||
|
return mip;
|
||||||
|
}
|
||||||
|
|
||||||
|
CTextureManager::CTextureManager()
|
||||||
|
{
|
||||||
|
m_TerrainTextures.reserve(32);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTextureManager::AddTextureType(const char* name)
|
||||||
|
{
|
||||||
|
m_TerrainTextures.resize(m_TerrainTextures.size()+1);
|
||||||
|
STextureType& ttype=m_TerrainTextures.back();
|
||||||
|
ttype.m_Name=name;
|
||||||
|
ttype.m_Index=m_TerrainTextures.size()-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
CTextureEntry* CTextureManager::FindTexture(const char* filename)
|
||||||
|
{
|
||||||
|
// check if file already loaded
|
||||||
|
for (uint k=0;k<m_TerrainTextures.size();k++) {
|
||||||
|
STextureType& ttype=m_TerrainTextures[k];
|
||||||
|
for (uint i=0;i<ttype.m_Textures.size();i++) {
|
||||||
|
if (strcmp((const char*) ttype.m_Textures[i]->m_Name,filename)==0) {
|
||||||
|
return ttype.m_Textures[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
CTextureEntry* CTextureManager::FindTexture(Handle handle)
|
||||||
|
{
|
||||||
|
for (uint k=0;k<m_TerrainTextures.size();k++) {
|
||||||
|
STextureType& ttype=m_TerrainTextures[k];
|
||||||
|
for (uint i=0;i<ttype.m_Textures.size();i++) {
|
||||||
|
if (handle==ttype.m_Textures[i]->m_Handle) {
|
||||||
|
return ttype.m_Textures[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool IsCompressed(Handle h)
|
||||||
|
{
|
||||||
|
int fmt;
|
||||||
|
tex_info(h, NULL, NULL, &fmt, NULL, NULL);
|
||||||
|
if (fmt==GL_COMPRESSED_RGB_S3TC_DXT1_EXT) return true;
|
||||||
|
if (fmt==GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) return true;
|
||||||
|
if (fmt==GL_COMPRESSED_RGBA_S3TC_DXT3_EXT) return true;
|
||||||
|
if (fmt==GL_COMPRESSED_RGBA_S3TC_DXT5_EXT) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CTextureEntry* CTextureManager::AddTexture(const char* filename,int type)
|
||||||
|
{
|
||||||
|
assert(type<m_TerrainTextures.size());
|
||||||
|
|
||||||
|
CStr pathname("terrains/textures/");
|
||||||
|
pathname+=m_TerrainTextures[type].m_Name;
|
||||||
|
pathname+='/';
|
||||||
|
pathname+=filename;
|
||||||
|
|
||||||
|
Handle h=tex_load((const char*) pathname);
|
||||||
|
if (!h) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
int tw;
|
||||||
|
int th;
|
||||||
|
|
||||||
|
tex_info(h, &tw, &th, NULL, NULL, NULL);
|
||||||
|
|
||||||
|
tw &= (tw-1);
|
||||||
|
th &= (th-1);
|
||||||
|
if (tw || th) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create new texture entry
|
||||||
|
CTextureEntry* texentry=new CTextureEntry;
|
||||||
|
texentry->m_Name=filename;
|
||||||
|
texentry->m_Handle=h;
|
||||||
|
texentry->m_Type=type;
|
||||||
|
|
||||||
|
// upload texture for future GL use
|
||||||
|
if (IsCompressed(h)) {
|
||||||
|
tex_upload(h,GL_LINEAR);
|
||||||
|
} else {
|
||||||
|
tex_upload(h,GL_LINEAR_MIPMAP_LINEAR);
|
||||||
|
}
|
||||||
|
// setup texture to repeat
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||||
|
|
||||||
|
// get root color for coloring minimap
|
||||||
|
int width,height;
|
||||||
|
glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&width);
|
||||||
|
glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&height);
|
||||||
|
int mip=GetNumMipmaps(width,height);
|
||||||
|
glGetTexImage(GL_TEXTURE_2D,mip-1,GL_BGRA_EXT,GL_UNSIGNED_BYTE,&texentry->m_BaseColor);
|
||||||
|
|
||||||
|
// add entry to list ..
|
||||||
|
m_TerrainTextures[type].m_Textures.push_back(texentry);
|
||||||
|
|
||||||
|
// .. and return it
|
||||||
|
return texentry;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTextureManager::DeleteTexture(CTextureEntry* entry)
|
||||||
|
{
|
||||||
|
// find entry in list
|
||||||
|
std::vector<CTextureEntry*>& textures=m_TerrainTextures[entry->m_Type].m_Textures;
|
||||||
|
|
||||||
|
typedef std::vector<CTextureEntry*>::iterator Iter;
|
||||||
|
Iter i=std::find(textures.begin(),textures.end(),entry);
|
||||||
|
if (i!=textures.end()) {
|
||||||
|
textures.erase(i);
|
||||||
|
}
|
||||||
|
delete entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTextureManager::LoadTerrainTextures(int terraintype,const char* fileext)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
struct _finddata_t file;
|
||||||
|
long handle;
|
||||||
|
|
||||||
|
// build pathname
|
||||||
|
CStr pathname("terrains\\textures\\");
|
||||||
|
pathname+=m_TerrainTextures[terraintype].m_Name;
|
||||||
|
pathname+="\\";
|
||||||
|
|
||||||
|
CStr findname(pathname);
|
||||||
|
findname+="*.";
|
||||||
|
findname+=fileext;
|
||||||
|
|
||||||
|
// Find first matching file in directory for this terrain type
|
||||||
|
if ((handle=_findfirst((const char*) findname,&file))!=-1) {
|
||||||
|
|
||||||
|
AddTexture(file.name,terraintype);
|
||||||
|
|
||||||
|
// Find the rest of the matching files
|
||||||
|
while( _findnext(handle,&file)==0) {
|
||||||
|
AddTexture((const char*) file.name,terraintype);
|
||||||
|
}
|
||||||
|
|
||||||
|
_findclose(handle);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTextureManager::BuildTerrainTypes()
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
struct _finddata_t file;
|
||||||
|
long handle;
|
||||||
|
|
||||||
|
// Find first matching directory in terrain\textures
|
||||||
|
if ((handle=_findfirst("terrains\\textures\\*",&file))!=-1) {
|
||||||
|
|
||||||
|
if ((file.attrib & _A_SUBDIR) && file.name[0]!='.' && file.name[0]!='_') {
|
||||||
|
AddTextureType(file.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the rest of the matching files
|
||||||
|
while( _findnext(handle,&file)==0) {
|
||||||
|
if ((file.attrib & _A_SUBDIR) && file.name[0]!='.' && file.name[0]!='_') {
|
||||||
|
AddTextureType(file.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_findclose(handle);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTextureManager::LoadTerrainTextures()
|
||||||
|
{
|
||||||
|
// find all the terrain types by directory name
|
||||||
|
BuildTerrainTypes();
|
||||||
|
|
||||||
|
// now iterate through terrain types loading all textures of that type
|
||||||
|
for (uint i=0;i<m_TerrainTextures.size();i++) {
|
||||||
|
for (uint j=0;j<sizeof(SupportedTextureFormats)/sizeof(const char*);j++) {
|
||||||
|
LoadTerrainTextures(i,SupportedTextureFormats[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
45
source/terrain/TextureManager.h
Executable file
45
source/terrain/TextureManager.h
Executable file
@ -0,0 +1,45 @@
|
|||||||
|
#ifndef _TEXTUREMANAGER_H
|
||||||
|
#define _TEXTUREMANAGER_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include "TextureEntry.h"
|
||||||
|
#include "CStr.h"
|
||||||
|
|
||||||
|
class CTextureManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct STextureType
|
||||||
|
{
|
||||||
|
// name of this texture type (derived from directory name)
|
||||||
|
CStr m_Name;
|
||||||
|
// index in parent array
|
||||||
|
int m_Index;
|
||||||
|
// list of textures of this type (found from the texture directory)
|
||||||
|
std::vector<CTextureEntry*> m_Textures;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
CTextureManager();
|
||||||
|
|
||||||
|
void LoadTerrainTextures();
|
||||||
|
|
||||||
|
|
||||||
|
void AddTextureType(const char* name);
|
||||||
|
|
||||||
|
CTextureEntry* FindTexture(const char* filename);
|
||||||
|
CTextureEntry* FindTexture(Handle handle);
|
||||||
|
CTextureEntry* AddTexture(const char* filename,int type);
|
||||||
|
void DeleteTexture(CTextureEntry* entry);
|
||||||
|
|
||||||
|
std::vector<STextureType> m_TerrainTextures;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void LoadTerrainTextures(int terraintype,const char* fileext);
|
||||||
|
void BuildTerrainTypes();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
extern CTextureManager g_TexMan;
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
138
source/terrain/TransparencyRenderer.cpp
Executable file
138
source/terrain/TransparencyRenderer.cpp
Executable file
@ -0,0 +1,138 @@
|
|||||||
|
#include <algorithm>
|
||||||
|
#include "Renderer.h"
|
||||||
|
#include "TransparencyRenderer.h"
|
||||||
|
#include "terrain/Model.h"
|
||||||
|
#include "terrain/Visual.h"
|
||||||
|
|
||||||
|
extern CRenderer g_Renderer;
|
||||||
|
|
||||||
|
CTransparencyRenderer g_TransparencyRenderer;
|
||||||
|
|
||||||
|
|
||||||
|
struct SortObjectsByDist {
|
||||||
|
typedef CTransparencyRenderer::SObject SortObj;
|
||||||
|
|
||||||
|
bool operator()(const SortObj& lhs,const SortObj& rhs) {
|
||||||
|
return lhs.m_Dist>rhs.m_Dist? true : false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void CTransparencyRenderer::Render()
|
||||||
|
{
|
||||||
|
// coarsely sort submitted objects in back to front manner
|
||||||
|
std::sort(m_Objects.begin(),m_Objects.end(),SortObjectsByDist());
|
||||||
|
|
||||||
|
// switch on wireframe if we need it
|
||||||
|
if (g_Renderer.m_ModelRenderMode==WIREFRAME) {
|
||||||
|
glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// switch on client states
|
||||||
|
glEnableClientState(GL_VERTEX_ARRAY);
|
||||||
|
glEnableClientState(GL_COLOR_ARRAY);
|
||||||
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||||
|
|
||||||
|
// setup texture environment to modulate diffuse color with texture color
|
||||||
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
|
||||||
|
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
|
||||||
|
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
|
||||||
|
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
|
||||||
|
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR);
|
||||||
|
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
|
||||||
|
|
||||||
|
// just pass through texture's alpha
|
||||||
|
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
|
||||||
|
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
|
||||||
|
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
|
||||||
|
|
||||||
|
glEnable(GL_ALPHA_TEST);
|
||||||
|
glAlphaFunc(GL_GREATER,0.975f);
|
||||||
|
|
||||||
|
uint i;
|
||||||
|
for (i=0;i<m_Objects.size();++i) {
|
||||||
|
CVisual* visual=m_Objects[i].m_Visual;
|
||||||
|
CModelRData* modeldata=(CModelRData*) visual->m_Model->m_RenderData;
|
||||||
|
modeldata->Render(visual->GetTransform(),true);
|
||||||
|
}
|
||||||
|
|
||||||
|
glDepthMask(0);
|
||||||
|
glAlphaFunc(GL_LEQUAL,0.975f);
|
||||||
|
|
||||||
|
glEnable(GL_BLEND);
|
||||||
|
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
|
for (i=0;i<m_Objects.size();++i) {
|
||||||
|
CVisual* visual=m_Objects[i].m_Visual;
|
||||||
|
CModelRData* modeldata=(CModelRData*) visual->m_Model->m_RenderData;
|
||||||
|
modeldata->Render(visual->GetTransform(),true);
|
||||||
|
}
|
||||||
|
glDisable(GL_BLEND);
|
||||||
|
glDisable(GL_ALPHA_TEST);
|
||||||
|
glDepthMask(1);
|
||||||
|
|
||||||
|
// switch off client states
|
||||||
|
glDisableClientState(GL_VERTEX_ARRAY);
|
||||||
|
glDisableClientState(GL_COLOR_ARRAY);
|
||||||
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||||
|
|
||||||
|
if (g_Renderer.m_ModelRenderMode==WIREFRAME) {
|
||||||
|
// switch wireframe off again
|
||||||
|
glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
|
||||||
|
} else if (g_Renderer.m_ModelRenderMode==EDGED_FACES) {
|
||||||
|
// edged faces: need to make a second pass over the data:
|
||||||
|
// first switch on wireframe
|
||||||
|
glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
|
||||||
|
|
||||||
|
// setup some renderstate ..
|
||||||
|
glDepthMask(0);
|
||||||
|
g_Renderer.SetTexture(0,0);
|
||||||
|
glColor4f(1,1,1,0.75f);
|
||||||
|
glLineWidth(1.0f);
|
||||||
|
|
||||||
|
glEnable(GL_BLEND);
|
||||||
|
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
|
// .. and some client states
|
||||||
|
glEnableClientState(GL_VERTEX_ARRAY);
|
||||||
|
|
||||||
|
// render each model
|
||||||
|
for (uint i=0;i<m_Objects.size();++i) {
|
||||||
|
CVisual* visual=m_Objects[i].m_Visual;
|
||||||
|
CModelRData* modeldata=(CModelRData*) visual->m_Model->m_RenderData;
|
||||||
|
modeldata->RenderWireframe(visual->GetTransform(),true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// .. and switch off the client states
|
||||||
|
glDisableClientState(GL_VERTEX_ARRAY);
|
||||||
|
|
||||||
|
// .. and restore the renderstates
|
||||||
|
glDisable(GL_BLEND);
|
||||||
|
glDepthMask(1);
|
||||||
|
|
||||||
|
// restore fill mode, and we're done
|
||||||
|
glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// all transparent objects rendered; release them
|
||||||
|
m_Objects.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTransparencyRenderer::Add(CVisual* visual)
|
||||||
|
{
|
||||||
|
// resize array, get last object in list
|
||||||
|
m_Objects.resize(m_Objects.size()+1);
|
||||||
|
|
||||||
|
SObject& obj=m_Objects.back();
|
||||||
|
obj.m_Visual=visual;
|
||||||
|
|
||||||
|
// build transform from object to camera space
|
||||||
|
CMatrix3D objToCam,invcam;
|
||||||
|
g_Renderer.m_Camera.m_Orientation.Invert(objToCam);
|
||||||
|
objToCam*=visual->GetTransform();
|
||||||
|
|
||||||
|
// resort model indices from back to front, according to camera position - and store
|
||||||
|
// the returned sqrd distance to the centre of the nearest triangle
|
||||||
|
CModelRData* modeldata=(CModelRData*) visual->m_Model->m_RenderData;
|
||||||
|
obj.m_Dist=modeldata->BackToFrontIndexSort(objToCam);
|
||||||
|
}
|
||||||
|
|
31
source/terrain/TransparencyRenderer.h
Executable file
31
source/terrain/TransparencyRenderer.h
Executable file
@ -0,0 +1,31 @@
|
|||||||
|
#ifndef __TRANSPARENCYRENDERER_H
|
||||||
|
#define __TRANSPARENCYRENDERER_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class CVisual;
|
||||||
|
|
||||||
|
class CTransparencyRenderer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct SObject {
|
||||||
|
// visual representation of object
|
||||||
|
CVisual* m_Visual;
|
||||||
|
// sqrd distance from camera to centre of nearest triangle
|
||||||
|
float m_Dist;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
// add object to render in deferred transparency pass
|
||||||
|
void Add(CVisual* visual);
|
||||||
|
// render all deferred objects
|
||||||
|
void Render();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// list of transparent objects to render
|
||||||
|
std::vector<SObject> m_Objects;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern CTransparencyRenderer g_TransparencyRenderer;
|
||||||
|
|
||||||
|
#endif
|
60
source/terrain/Triangle.cpp
Executable file
60
source/terrain/Triangle.cpp
Executable file
@ -0,0 +1,60 @@
|
|||||||
|
#include "Triangle.h"
|
||||||
|
#include "Plane.h"
|
||||||
|
|
||||||
|
#define EPSILON 0.00001f
|
||||||
|
|
||||||
|
Triangle::Triangle()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Triangle::Triangle(const CVector3D& p0,const CVector3D& p1,const CVector3D& p2)
|
||||||
|
{
|
||||||
|
_vertices[0]=p0;
|
||||||
|
_vertices[1]=p1;
|
||||||
|
_vertices[2]=p2;
|
||||||
|
|
||||||
|
// calculate edge vectors
|
||||||
|
_edge[0]=_vertices[1]-_vertices[0];
|
||||||
|
_edge[1]=_vertices[2]-_vertices[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Triangle::RayIntersect(const CVector3D& origin,const CVector3D& dir,float& dist) const
|
||||||
|
{
|
||||||
|
// begin calculating determinant - also used to calculate U parameter
|
||||||
|
CVector3D pvec=dir.Cross(_edge[1]);
|
||||||
|
|
||||||
|
// if determinant is near zero, ray lies in plane of triangle
|
||||||
|
float det = _edge[0].Dot(pvec);
|
||||||
|
if (fabs(det)<EPSILON)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
float inv_det = 1.0f/det;
|
||||||
|
|
||||||
|
// calculate vector from vert0 to ray origin
|
||||||
|
CVector3D tvec;
|
||||||
|
for (int i=0;i<3;++i) {
|
||||||
|
tvec[i]=origin[i]-_vertices[0][i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate U parameter, test bounds
|
||||||
|
float u=tvec.Dot(pvec)*inv_det;
|
||||||
|
if (u<-0.01f || u>1.01f)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// prepare to test V parameter
|
||||||
|
CVector3D qvec=tvec.Cross(_edge[0]);
|
||||||
|
|
||||||
|
// calculate V parameter and test bounds
|
||||||
|
float v=dir.Dot(qvec)*inv_det;
|
||||||
|
if (v<-0.01f || u+v>1.01f)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// calculate distance to intersection point from ray origin
|
||||||
|
float d=_edge[1].Dot(qvec)*inv_det;
|
||||||
|
if (d>=0 && d<dist) {
|
||||||
|
dist=d;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
24
source/terrain/Triangle.h
Executable file
24
source/terrain/Triangle.h
Executable file
@ -0,0 +1,24 @@
|
|||||||
|
#ifndef _TRIANGLE_H
|
||||||
|
#define _TRIANGLE_H
|
||||||
|
|
||||||
|
// necessary includes
|
||||||
|
#include "Vector3D.h"
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
class Triangle
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Triangle();
|
||||||
|
Triangle(const CVector3D& p0,const CVector3D& p1,const CVector3D& p2);
|
||||||
|
|
||||||
|
bool RayIntersect(const CVector3D& origin,const CVector3D& dir,float& maxdist) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
CVector3D _vertices[3];
|
||||||
|
CVector3D _edge[2];
|
||||||
|
};
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
19
source/terrain/Visual.cpp
Executable file
19
source/terrain/Visual.cpp
Executable file
@ -0,0 +1,19 @@
|
|||||||
|
#include "Visual.h"
|
||||||
|
#include "Model.h"
|
||||||
|
|
||||||
|
void CVisual::CalcBounds()
|
||||||
|
{
|
||||||
|
m_Bounds.SetEmpty();
|
||||||
|
|
||||||
|
for (int i=0; i<m_Model->GetModelDef()->GetNumVertices(); i++)
|
||||||
|
{
|
||||||
|
SModelVertex *pVertex = &m_Model->GetModelDef()->GetVertices()[i];
|
||||||
|
CVector3D coord1,coord2;
|
||||||
|
if (pVertex->m_Bone!=-1) {
|
||||||
|
coord1=m_Model->GetBonePoses()[pVertex->m_Bone].Transform(pVertex->m_Coords);
|
||||||
|
} else {
|
||||||
|
coord1=pVertex->m_Coords;
|
||||||
|
}
|
||||||
|
m_Bounds+=m_Transform.Transform(coord1);
|
||||||
|
}
|
||||||
|
}
|
18
source/terrain/Visual.h
Executable file
18
source/terrain/Visual.h
Executable file
@ -0,0 +1,18 @@
|
|||||||
|
#ifndef _VISUAL_H
|
||||||
|
#define _VISUAL_H
|
||||||
|
|
||||||
|
#include "RenderableObject.h"
|
||||||
|
|
||||||
|
class CModel;
|
||||||
|
|
||||||
|
class CVisual : public CRenderableObject
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CVisual() : m_Model(0) {}
|
||||||
|
|
||||||
|
void CalcBounds();
|
||||||
|
|
||||||
|
CModel* m_Model;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user