diff --git a/source/terrain/AABBTree.h b/source/terrain/AABBTree.h new file mode 100755 index 0000000000..39ee99eef6 --- /dev/null +++ b/source/terrain/AABBTree.h @@ -0,0 +1,308 @@ +#ifndef _AABBTREE_H +#define _AABBTREE_H + +#include +#include +#include "Bound.h" + +template +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; + }; + + 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 +CAABBTree::CAABBTree() : m_Root(0) +{ +} + +template +CAABBTree::~CAABBTree() +{ + delete m_Root; +} + +template +void CAABBTree::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 +bool CAABBTree::RayIntersect(CVector3D& origin,CVector3D& dir,float& dist,T* selection) +{ + if (m_Root) { + return m_Root->RayIntersect(origin,dir,dist,selection); + } else { + return false; + } +} + + +template +void CAABBTree::Node::AddLeafAsSibling(CAABBTree::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 +bool CAABBTree::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;iRayIntersect(origin,dir,dist,selection); + if (hit) { + // hit a triangle; can quit out .. + result=true; + } + } + } + + return result; +} + +template +bool CAABBTree::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 +void CAABBTree::Leaf::FindInsertionPoint(CAABBTree::Leaf* leaf,CAABBTree::InsertionData& inserter) +{ + CBound thisbound=m_Bounds; + thisbound+=leaf->m_Bounds; + float m1=2*thisbound.GetVolume(); + if (m1 +void CAABBTree::Branch::AddLeaf(CAABBTree::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 +void CAABBTree::Leaf::AddLeaf(CAABBTree::Leaf* leaf) +{ + AddLeafAsSibling(leaf); +} + +template +void CAABBTree::Branch::AddChild(CAABBTree::Node* node) +{ + m_Children.push_back(node); +} + +template +void CAABBTree::Branch::RemoveChild(CAABBTree::Node* node) +{ + m_Children.erase(std::find(m_Children.begin(),m_Children.end(),node)); +} + +template +void CAABBTree::Branch::FindInsertionPoint(CAABBTree::Leaf* leaf,CAABBTree::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 (m1inserter.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;iFindInsertionPoint(leaf,inserter); + } +} + +template +void CAABBTree::Branch::AddLeafAsChild(CAABBTree::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 diff --git a/source/terrain/AlphaMapCalculator.cpp b/source/terrain/AlphaMapCalculator.cpp new file mode 100755 index 0000000000..3dd92053c6 --- /dev/null +++ b/source/terrain/AlphaMapCalculator.cpp @@ -0,0 +1,272 @@ +#include "AlphaMapCalculator.h" +#include +#include + +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 +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 +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 +int LookupBlend(int tableSize,const S* table,const T& shape,unsigned int& flipflags) +{ + // iterate through known blend shapes + for (int b=0;b +#include "BlendShapes.h" + +namespace CAlphaMapCalculator { + int Calculate(BlendShape8 shape,unsigned int& flipflags); +} + +#endif diff --git a/source/terrain/BlendShapes.h b/source/terrain/BlendShapes.h new file mode 100755 index 0000000000..7673e6c3d0 --- /dev/null +++ b/source/terrain/BlendShapes.h @@ -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 diff --git a/source/terrain/HBV.h b/source/terrain/HBV.h new file mode 100755 index 0000000000..9ed513e025 --- /dev/null +++ b/source/terrain/HBV.h @@ -0,0 +1,156 @@ +// HBV.h +// +// (c) Rich Cross, 2000 + + +#ifndef __HBV_H +#define __HBV_H + +#include +#include +#include "HBVNode.h" +#include "Aggregate.h" + +class Point3; +class Vector3; + +template +class HBV +{ +public: + HBV(); + ~HBV(); + + Geometry* clone() const; + + void open(); + void add(const T& element); + void addMany(const std::vector& 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* _root; + std::vector* _elementList; +}; + + +#include "common.h" +#include "HBVLeaf.h" + + + +template +HBV::HBV() : _root(0), _elementList(0), _cachedElement(0) +{ +} + +template +HBV::~HBV() +{ + assert(_elementList==0); + delete _root; +} + +template +Geometry* HBV::clone() const +{ + assert(0); + return 0; +} + +template +void HBV::open() +{ + _elementList=new std::vector(); +} + +template +void HBV::add(const T& element) +{ + assert(_elementList!=0); + _elementList->push_back(element); +} + +template +void HBV::addMany(const std::vector& v) +{ + assert(_elementList!=0); + for (int i=0;ipush_back(v.at(i)); +} + +template +void HBV::close() +{ + std::random_shuffle(_elementList->begin(),_elementList->end()); + + for (int i=0;i<_elementList->size();i++) { + T element=_elementList->at(i); + HBVLeaf *leaf=new HBVLeaf(element); + if (_root) { + _root->addLeaf(leaf); + while (_root->getParent()) + _root=_root->getParent(); + } + else + _root=leaf; + } + + _elementList->clear(); + delete _elementList; + _elementList=0; +} + +template +bool HBV::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 +void HBV::getBounds(BoundingBox& result) const +{ + result=_root->getBounds(); +} + + +template +void HBV::getNormal(const Point3& pt,Vector3& result) const +{ + assert(_cachedElement!=0); + _cachedElement->getNormal(pt,result); +} + +template +void HBV::getUV(const Point3& pt,float& u,float& v) const +{ + assert(_cachedElement!=0); + _cachedElement->getUV(pt,u,v); +} + +template +const T& HBV::getIntersectedElement() const +{ + assert(_cachedElement!=0); + return _cachedElement; +} + +#endif \ No newline at end of file diff --git a/source/terrain/ModelRData.cpp b/source/terrain/ModelRData.cpp new file mode 100755 index 0000000000..46977da9e7 --- /dev/null +++ b/source/terrain/ModelRData.cpp @@ -0,0 +1,271 @@ +#include +#include +#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; jGetNumFaces(); 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 (xmax) 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; jGetNumVertices(); 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 IntFloatPair; +static std::vector 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;im_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 +#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 diff --git a/source/terrain/PatchRData.cpp b/source/terrain/PatchRData.cpp new file mode 100755 index 0000000000..8689e96573 --- /dev/null +++ b/source/terrain/PatchRData.cpp @@ -0,0 +1,579 @@ +#pragma warning(disable:4786) + +#include +#include +#include +#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 splats; + // set of textures used for splats + std::set splatTextures; + + // for each tile in patch .. + for (int j=0;jm_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 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::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 textures; + Handle texgrid[PATCH_SIZE][PATCH_SIZE]; + for (int j=0;jm_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;imax) 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; jCalcPosition(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 +#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 m_Indices; + // list of base splats to apply to this patch + std::vector m_Splats; + // vertices to use for blending transition texture passes + std::vector m_BlendVertices; + // indices into blend vertices for the blend splats + std::vector m_BlendIndices; + // splats used in blend pass + std::vector m_BlendSplats; +}; + + +#endif diff --git a/source/terrain/RenderableObject.h b/source/terrain/RenderableObject.h new file mode 100755 index 0000000000..9aa9481d35 --- /dev/null +++ b/source/terrain/RenderableObject.h @@ -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 diff --git a/source/terrain/TextureEntry.h b/source/terrain/TextureEntry.h new file mode 100755 index 0000000000..0d855be077 --- /dev/null +++ b/source/terrain/TextureEntry.h @@ -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 diff --git a/source/terrain/TextureManager.cpp b/source/terrain/TextureManager.cpp new file mode 100755 index 0000000000..fff7711d4e --- /dev/null +++ b/source/terrain/TextureManager.cpp @@ -0,0 +1,214 @@ +#include "TextureManager.h" +#include "lib.h" +#include "ogl.h" +#include "res/tex.h" +#ifdef _WIN32 +#include +#endif +#include + +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;km_Name,filename)==0) { + return ttype.m_Textures[i]; + } + } + } + + return 0; +} + +CTextureEntry* CTextureManager::FindTexture(Handle handle) +{ + for (uint k=0;km_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(typem_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& textures=m_TerrainTextures[entry->m_Type].m_Textures; + + typedef std::vector::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 +#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 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 m_TerrainTextures; + +private: + void LoadTerrainTextures(int terraintype,const char* fileext); + void BuildTerrainTypes(); + +}; + +extern CTextureManager g_TexMan; + + +#endif diff --git a/source/terrain/TransparencyRenderer.cpp b/source/terrain/TransparencyRenderer.cpp new file mode 100755 index 0000000000..5cec44d979 --- /dev/null +++ b/source/terrain/TransparencyRenderer.cpp @@ -0,0 +1,138 @@ +#include +#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;im_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;im_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;im_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); +} + diff --git a/source/terrain/TransparencyRenderer.h b/source/terrain/TransparencyRenderer.h new file mode 100755 index 0000000000..3e9a8d474a --- /dev/null +++ b/source/terrain/TransparencyRenderer.h @@ -0,0 +1,31 @@ +#ifndef __TRANSPARENCYRENDERER_H +#define __TRANSPARENCYRENDERER_H + +#include + +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 m_Objects; +}; + +extern CTransparencyRenderer g_TransparencyRenderer; + +#endif diff --git a/source/terrain/Triangle.cpp b/source/terrain/Triangle.cpp new file mode 100755 index 0000000000..37dbc41f60 --- /dev/null +++ b/source/terrain/Triangle.cpp @@ -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)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 && dGetModelDef()->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); + } +} diff --git a/source/terrain/Visual.h b/source/terrain/Visual.h new file mode 100755 index 0000000000..73aac519e9 --- /dev/null +++ b/source/terrain/Visual.h @@ -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 \ No newline at end of file