From Jason Daly, "This is a plugin (two, actually) that will allow OSG to load .bsp map files from Valve's Source Engine games (Half-Life 2, etc.). One plugin (called "vbsp" to distinguish it from the Quake 3 bsp loader) reads the .bsp file itself, and the other ("vtf") reads the texture files.

The set up for this to work is a bit more complex than most files, since the engine expects all files to be in a certain place, and it tends to mix case a lot.  I tried to explain everything in the VBSP_README.txt file."

This plugin has been integrated with the pre-exisiting bsp plugin.
This commit is contained in:
Robert Osfield
2008-11-24 11:39:02 +00:00
parent 38f7fabb10
commit bf8c3cc07e
13 changed files with 3165 additions and 674 deletions

View File

@@ -78,6 +78,7 @@ ADD_SUBDIRECTORY(dds)
ADD_SUBDIRECTORY(tga)
ADD_SUBDIRECTORY(hdr)
ADD_SUBDIRECTORY(dot)
ADD_SUBDIRECTORY(bsp)
IF(JPEG_FOUND)
ADD_SUBDIRECTORY(jpeg)

View File

@@ -0,0 +1,21 @@
SET(TARGET_SRC
ReaderWriterBSP.cpp
BITSET.cpp
Q3BSPReader.cpp
Q3BSPLoad.cpp
VBSPGeometry.cpp
VBSPReader.cpp
)
SET(TARGET_H
ReaderWriterBSP.h
BITSET.h
Q3BSPReader.h
Q3BSPLoad.h
VBSPGeometry.h
VBSPReader.h
)
#### end var setup ###
SETUP_PLUGIN(bsp)

View File

@@ -1,13 +1,11 @@
#include "Q3BSPLoad.h"
using namespace bsp;
#include "BSPLoad.h"
bool BSPLoad::Load(const std::string& filename, int curveTessellation)
bool Q3BSPLoad::Load(const std::string& filename, int curveTessellation)
{
std::ifstream file(filename.c_str(),std::ios::binary);
if(!file.is_open())
@@ -72,7 +70,7 @@ bool BSPLoad::Load(const std::string& filename, int curveTessellation)
void BSPLoad::LoadVertices(std::ifstream& aFile)
void Q3BSPLoad::LoadVertices(std::ifstream& aFile)
{
//calculate number of vertices
int num_vertices=m_header.m_directoryEntries[bspVertices].m_length/sizeof(BSP_LOAD_VERTEX);
@@ -96,7 +94,7 @@ void BSPLoad::LoadVertices(std::ifstream& aFile)
void BSPLoad::LoadFaces(std::ifstream& aFile, int /*curveTessellation*/)
void Q3BSPLoad::LoadFaces(std::ifstream& aFile, int /*curveTessellation*/)
{
//calculate number of load faces
int numTotalFaces=m_header.m_directoryEntries[bspFaces].m_length/sizeof(BSP_LOAD_FACE);
@@ -123,7 +121,7 @@ void BSPLoad::LoadFaces(std::ifstream& aFile, int /*curveTessellation*/)
void BSPLoad::LoadTextures(std::ifstream& aFile)
void Q3BSPLoad::LoadTextures(std::ifstream& aFile)
{
//Calculate number of textures
int num_textures=m_header.m_directoryEntries[bspTextures].m_length/sizeof(BSP_LOAD_TEXTURE);
@@ -146,7 +144,7 @@ void BSPLoad::LoadTextures(std::ifstream& aFile)
void BSPLoad::LoadLightmaps(std::ifstream& aFile)
void Q3BSPLoad::LoadLightmaps(std::ifstream& aFile)
{
//Calculate number of lightmaps
int num_lightmaps=m_header.m_directoryEntries[bspLightmaps].m_length/sizeof(BSP_LOAD_LIGHTMAP);
@@ -206,7 +204,7 @@ void BSPLoad::LoadLightmaps(std::ifstream& aFile)
void BSPLoad::LoadBSPData(std::ifstream& aFile)
void Q3BSPLoad::LoadBSPData(std::ifstream& aFile)
{
//Load leaves
//Calculate number of leaves

View File

@@ -1,7 +1,7 @@
#ifndef BSPLOAD_H
#define BSPLOAD_H
#ifndef Q3BSPLOAD_H
#define Q3BSPLOAD_H
#include <osg/Vec3f>
@@ -12,6 +12,11 @@
#include <fstream>
namespace bsp
{
//Directory entry in header
class BSP_DIRECTORY_ENTRY
{
@@ -170,7 +175,7 @@ public:
class BSPLoad
class Q3BSPLoad
{
public:
@@ -200,6 +205,8 @@ public:
};
}
#endif // BSPLOAD_H
#endif // Q3BSPLOAD_H

View File

@@ -0,0 +1,641 @@
#include <osg/TexEnv>
#include <osg/CullFace>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/Image>
#include <osg/Texture2D>
#include <osg/PolygonMode>
#include <osg/BlendColor>
#include <osgDB/ReadFile>
#include <osgDB/FileNameUtils>
#include <osgDB/FileUtils>
#include <assert.h>
#include <fstream>
#include "Q3BSPReader.h"
#include "Q3BSPLoad.h"
using namespace bsp;
Q3BSPReader::Q3BSPReader()
{
root_node = NULL;
}
bool Q3BSPReader::readFile(const std::string& file,
const osgDB::ReaderWriter::Options* options)
{
std::string ext = osgDB::getLowerCaseFileExtension(file);
Q3BSPLoad load_data;
load_data.Load(file,8);
osg::Geode* geode = convertFromBSP(load_data, options);
if (!geode)
return false;
//osg::StateSet* state_set=geode->getOrCreateStateSet();
//state_set->setMode(osg::CullFace::BACK,osg::StateAttribute::ON);
root_node = geode;
return true;
}
osg::ref_ptr<osg::Node> Q3BSPReader::getRootNode()
{
return root_node;
}
enum BSP_FACE_TYPE
{
bspPolygonFace=1,
bspPatch,
bspMeshFace,
bspBillboard
};
class BSP_VERTEX
{
public:
osg::Vec3f m_position;
float m_decalS, m_decalT;
float m_lightmapS, m_lightmapT;
BSP_VERTEX operator+(const BSP_VERTEX & rhs) const
{
BSP_VERTEX result;
result.m_position=m_position+rhs.m_position;
result.m_decalS=m_decalS+rhs.m_decalS;
result.m_decalT=m_decalT+rhs.m_decalT;
result.m_lightmapS=m_lightmapS+rhs.m_lightmapS;
result.m_lightmapT=m_lightmapT+rhs.m_lightmapT;
return result;
}
BSP_VERTEX operator*(const float rhs) const
{
BSP_VERTEX result;
result.m_position=m_position*rhs;
result.m_decalS=m_decalS*rhs;
result.m_decalT=m_decalT*rhs;
result.m_lightmapS=m_lightmapS*rhs;
result.m_lightmapT=m_lightmapT*rhs;
return result;
}
};
//every patch (curved surface) is split into biquadratic (3x3) patches
class BSP_BIQUADRATIC_PATCH
{
public:
BSP_BIQUADRATIC_PATCH():m_vertices(32),m_indices(32)
{
}
~BSP_BIQUADRATIC_PATCH()
{
}
bool Tessellate(int newTessellation,osg::Geometry* aGeometry);
BSP_VERTEX m_controlPoints[9]; // Se accede a ellos en la carga
protected:
int m_tessellation;
std::vector<BSP_VERTEX> m_vertices;
std::vector<GLuint> m_indices;
//arrays for multi_draw_arrays
std::vector<int> m_trianglesPerRow;
std::vector<GLuint *> m_rowIndexPointers;
};
//curved surface
class BSP_PATCH
{
public:
BSP_PATCH():m_quadraticPatches(32)
{
}
~BSP_PATCH()
{
}
int m_textureIndex;
int m_lightmapIndex;
int m_width, m_height;
int m_numQuadraticPatches;
std::vector<BSP_BIQUADRATIC_PATCH> m_quadraticPatches;
};
osg::Geode* Q3BSPReader::convertFromBSP(
Q3BSPLoad& aLoadData,
const osgDB::ReaderWriter::Options* options) const
{
std::vector<osg::Texture2D*> texture_array;
loadTextures(aLoadData,texture_array);
std::vector<osg::Texture2D*> lightmap_array;
loadLightMaps(aLoadData,lightmap_array);
osg::Geode* map_geode=new osg::Geode;
// Convertir los vertices
unsigned int num_load_vertices=aLoadData.m_loadVertices.size();
osg::Vec3Array* vertex_array = new osg::Vec3Array(num_load_vertices);
osg::Vec2Array* text_decal_array = new osg::Vec2Array(num_load_vertices);
osg::Vec2Array* text_lmap_array = new osg::Vec2Array(num_load_vertices);
float scale = 0.03;
unsigned int i;
for(i=0; i<num_load_vertices; ++i)
{
BSP_LOAD_VERTEX& vtx=aLoadData.m_loadVertices[i];
//swap y and z and negate z
(*vertex_array)[i]=(osg::Vec3d( vtx.m_position[0]*scale,
-vtx.m_position[1]*scale,
vtx.m_position[2]*scale ) );
//Transfer texture coordinates (Invert t)
(*text_decal_array)[i]=(osg::Vec2d(vtx.m_decalS,-vtx.m_decalT) );
//Transfer lightmap coordinates
(*text_lmap_array)[i]=(osg::Vec2d(vtx.m_lightmapS,vtx.m_lightmapT) );
}
unsigned int num_loadfaces=aLoadData.m_loadFaces.size();
//convert loadFaces to faces
for(i=0; i<num_loadfaces; ++i)
{
const BSP_LOAD_FACE& current_load_face=aLoadData.m_loadFaces[i];
if(current_load_face.m_type!=bspMeshFace) //skip this loadFace if it is not a mesh face
continue;
osg::Geometry* mesh_geom=createMeshFace(current_load_face,texture_array,*vertex_array,aLoadData.m_loadMeshIndices,*text_decal_array,*text_lmap_array);
map_geode->addDrawable(mesh_geom);
}
for(i=0; i<num_loadfaces; ++i)
{
const BSP_LOAD_FACE& current_face=aLoadData.m_loadFaces[i];
if(current_face.m_type!=bspPolygonFace) //skip this loadFace if it is not a polygon face
continue;
osg::Geometry* polygon_geom=createPolygonFace(current_face,texture_array,lightmap_array,*vertex_array,*text_decal_array,*text_lmap_array);
map_geode->addDrawable(polygon_geom);
}
for(i=0; i<num_loadfaces; ++i)
{
const BSP_LOAD_FACE& current_face=aLoadData.m_loadFaces[i];
if(current_face.m_type!=bspPatch) //skip this loadFace if it is not a patch face
continue;
//osg::Group* patch_group=new osg::Group;
BSP_PATCH current_patch;
current_patch.m_textureIndex=current_face.m_texture;
current_patch.m_lightmapIndex=current_face.m_lightmapIndex;
current_patch.m_width=current_face.m_patchSize[0];
current_patch.m_height=current_face.m_patchSize[1];
osg::Texture2D *texture=texture_array[current_face.m_texture];
osg::Texture2D *lightmap_texture=NULL;
if(current_face.m_lightmapIndex>=0)
{
lightmap_texture=lightmap_array[current_face.m_lightmapIndex];
}
else
{
lightmap_texture=lightmap_array[lightmap_array.size()-1];
}
//Create space to hold quadratic patches
int numPatchesWide=(current_patch.m_width-1)/2;
int numPatchesHigh=(current_patch.m_height-1)/2;
current_patch.m_numQuadraticPatches = numPatchesWide*numPatchesHigh;
current_patch.m_quadraticPatches.resize(current_patch.m_numQuadraticPatches);
//fill in the quadratic patches
for(int y=0; y<numPatchesHigh; ++y)
{
for(int x=0; x<numPatchesWide; ++x)
{
for(int row=0; row<3; ++row)
{
for(int point=0; point<3; ++point)
{
BSP_BIQUADRATIC_PATCH& curr_quadraticpatch=current_patch.m_quadraticPatches[y*numPatchesWide+x];
osg::Vec3f vtx= (*vertex_array) [aLoadData.m_loadFaces[i].m_firstVertexIndex+(y*2*current_patch.m_width+x*2)+
row*current_patch.m_width+point];
curr_quadraticpatch.m_controlPoints[row*3+point].m_position[0] = vtx.x();
curr_quadraticpatch.m_controlPoints[row*3+point].m_position[1] = vtx.y();
curr_quadraticpatch.m_controlPoints[row*3+point].m_position[2] = vtx.z();
}
}
osg::Geometry* patch_geom = new osg::Geometry;
//tessellate the patch
osg::StateSet* stateset = patch_geom->getOrCreateStateSet();
if(texture)
{
stateset->setTextureAttributeAndModes(0,texture,osg::StateAttribute::ON);
}
if(lightmap_texture)
{
stateset->setTextureAttributeAndModes(1,lightmap_texture,osg::StateAttribute::ON);
}
//patch_group->addChild(map_geode);
current_patch.m_quadraticPatches[y*numPatchesWide+x].Tessellate(8/*aCurveTessellation*/,patch_geom);
map_geode->addDrawable(patch_geom);
}
}
}
//int num_primitive_sets=geom->getNumPrimitiveSets();
//const osg::BoundingSphere& bs=map_geom->getBound();
map_geode->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
return map_geode;
}
osg::Geometry* Q3BSPReader::createMeshFace( const BSP_LOAD_FACE& aLoadFace,const std::vector<osg::Texture2D*>& aTextureArray,
osg::Vec3Array& aVertexArray,std::vector<GLuint>& aIndices,
osg::Vec2Array& aTextureDecalCoords,osg::Vec2Array& aTextureLMapCoords
) const
{
osg::Geometry* obj_geom = new osg::Geometry;
osg::Vec3Array* obj_vertex_array = new osg::Vec3Array(aLoadFace.m_numMeshIndices,
&(aVertexArray)[aLoadFace.m_firstVertexIndex]
);
obj_geom->setVertexArray(obj_vertex_array);
osg::DrawElementsUInt* face_indices = new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLES,
aLoadFace.m_numMeshIndices,
&(aIndices)[0]+aLoadFace.m_firstMeshIndex
);
obj_geom->addPrimitiveSet(face_indices);
osg::Texture2D *texture=aTextureArray[aLoadFace.m_texture];
if(texture)
{
osg::StateSet* stateset = obj_geom->getOrCreateStateSet();
stateset->setTextureAttributeAndModes(0,texture,osg::StateAttribute::ON);
stateset->setTextureAttributeAndModes(1,texture,osg::StateAttribute::ON);
osg::Vec2Array* obj_texcoords_array = new osg::Vec2Array(aLoadFace.m_numMeshIndices,
&(aTextureDecalCoords)[aLoadFace.m_firstVertexIndex]
);
obj_geom->setTexCoordArray(0,obj_texcoords_array);
osg::Vec2Array* obj_lmapcoords_array = new osg::Vec2Array(aLoadFace.m_numMeshIndices,
&(aTextureLMapCoords)[aLoadFace.m_firstVertexIndex]
);
obj_geom->setTexCoordArray(1,obj_lmapcoords_array);
}
return obj_geom;
}
osg::Geometry* Q3BSPReader::createPolygonFace(const BSP_LOAD_FACE& aLoadFace,const std::vector<osg::Texture2D*>& aTextureArray,const std::vector<osg::Texture2D*>& aTextureLMapArray,
osg::Vec3Array& aVertexArray,
osg::Vec2Array& aTextureDecalCoords,osg::Vec2Array& aTextureLMapCoords
) const
{
osg::Texture2D *texture=aTextureArray[aLoadFace.m_texture];
osg::Geometry* polygon_geom = new osg::Geometry;
polygon_geom->setVertexArray(&aVertexArray);
polygon_geom->setTexCoordArray(0, &aTextureDecalCoords);
polygon_geom->setTexCoordArray(1, &aTextureLMapCoords);
osg::DrawArrays* face_indices = new osg::DrawArrays(osg::PrimitiveSet::TRIANGLE_FAN,
aLoadFace.m_firstVertexIndex,
aLoadFace.m_numVertices
);
osg::StateSet* stateset = polygon_geom->getOrCreateStateSet();
if(texture)
{
stateset->setTextureAttributeAndModes(0,texture,osg::StateAttribute::ON);
if(aLoadFace.m_lightmapIndex>=0)
{
texture=aTextureLMapArray[aLoadFace.m_lightmapIndex];
if(texture)
{
stateset->setTextureAttributeAndModes(1,texture,osg::StateAttribute::ON);
}
}
else
{
texture=aTextureLMapArray[aTextureLMapArray.size()-1];
if(texture)
{
stateset->setTextureAttributeAndModes(1,texture,osg::StateAttribute::ON);
}
}
}
else
{
osg::PolygonMode* polygon_mode=new osg::PolygonMode;
polygon_mode->setMode(osg::PolygonMode::FRONT_AND_BACK,osg::PolygonMode::LINE);
stateset->setAttributeAndModes(polygon_mode, osg::StateAttribute::ON);
}
polygon_geom->addPrimitiveSet(face_indices);
return polygon_geom;
}
bool Q3BSPReader::loadTextures(
const Q3BSPLoad& aLoadData,
std::vector<osg::Texture2D*>& aTextureArray) const
{
int num_textures=aLoadData.m_loadTextures.size();
int i;
for(i=0;i<num_textures;i++)
{
//add file extension to the name
std::string tgaExtendedName(aLoadData.m_loadTextures[i].m_name);
tgaExtendedName+=".tga";
std::string jpgExtendedName(aLoadData.m_loadTextures[i].m_name);
jpgExtendedName+=".jpg";
osg::Image* image = osgDB::readImageFile(tgaExtendedName);
if (!image)
{
image = osgDB::readImageFile(jpgExtendedName);
if (!image)
{
aTextureArray.push_back(NULL);
continue; //?
}
}
osg::Texture2D* texture= new osg::Texture2D;
texture->setImage(image);
texture->setDataVariance(osg::Object::DYNAMIC); // protect from being optimized away as static state.
texture->setWrap(osg::Texture2D::WRAP_S, osg::Texture2D::REPEAT);
texture->setWrap(osg::Texture2D::WRAP_T, osg::Texture2D::REPEAT);
aTextureArray.push_back(texture);
}
return true;
}
bool Q3BSPReader::loadLightMaps(
const Q3BSPLoad& aLoadData,
std::vector<osg::Texture2D*>& aTextureArray) const
{
int num_textures=aLoadData.m_loadLightmaps.size();
int i;
for(i=0;i<num_textures;i++)
{
osg::Image* image=new osg::Image;
unsigned char *data=new unsigned char[128*128*3];
memcpy(data,aLoadData.m_loadLightmaps[i].m_lightmapData,128*128*3);
image->setImage(128,128,1,GL_RGBA8,GL_RGB,GL_UNSIGNED_BYTE,data,osg::Image::USE_NEW_DELETE);
osg::Texture2D* texture= new osg::Texture2D;
texture->setImage(image);
texture->setDataVariance(osg::Object::DYNAMIC); // protect from being optimized away as static state.
texture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR_MIPMAP_LINEAR);
texture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR);
texture->setWrap(osg::Texture2D::WRAP_S, osg::Texture2D::REPEAT);
texture->setWrap(osg::Texture2D::WRAP_T, osg::Texture2D::REPEAT);
aTextureArray.push_back(texture);
}
// A continuaci<63>n, a<>ado el blanco
osg::Image* image=new osg::Image;
unsigned char *data=new unsigned char[3];
for(int whiteidx=0;whiteidx<3;whiteidx++)
{
data[whiteidx]=255;
}
image->setImage(1,1,1,GL_RGBA8,GL_RGB,GL_UNSIGNED_BYTE,data,osg::Image::USE_NEW_DELETE);
osg::Texture2D* texture= new osg::Texture2D;
texture->setImage(image);
texture->setDataVariance(osg::Object::DYNAMIC); // protect from being optimized away as static state.
texture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR_MIPMAP_LINEAR);
texture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR);
texture->setWrap(osg::Texture2D::WRAP_S, osg::Texture2D::REPEAT);
texture->setWrap(osg::Texture2D::WRAP_T, osg::Texture2D::REPEAT);
aTextureArray.push_back(texture);
return true;
}
//Tessellate a biquadratic patch
bool BSP_BIQUADRATIC_PATCH::Tessellate(int newTessellation,osg::Geometry* aGeometry)
{
m_tessellation=newTessellation;
float px, py;
BSP_VERTEX temp[3];
m_vertices.resize((m_tessellation+1)*(m_tessellation+1));
for(int v=0; v<=m_tessellation; ++v)
{
px=(float)v/m_tessellation;
m_vertices[v]=m_controlPoints[0]*((1.0f-px)*(1.0f-px))+
m_controlPoints[3]*((1.0f-px)*px*2)+
m_controlPoints[6]*(px*px);
}
for(int u=1; u<=m_tessellation; ++u)
{
py=(float)u/m_tessellation;
temp[0]=m_controlPoints[0]*((1.0f-py)*(1.0f-py))+
m_controlPoints[1]*((1.0f-py)*py*2)+
m_controlPoints[2]*(py*py);
temp[1]=m_controlPoints[3]*((1.0f-py)*(1.0f-py))+
m_controlPoints[4]*((1.0f-py)*py*2)+
m_controlPoints[5]*(py*py);
temp[2]=m_controlPoints[6]*((1.0f-py)*(1.0f-py))+
m_controlPoints[7]*((1.0f-py)*py*2)+
m_controlPoints[8]*(py*py);
for(int v=0; v<=m_tessellation; ++v)
{
px=(float)v/m_tessellation;
m_vertices[u*(m_tessellation+1)+v]=temp[0]*((1.0f-px)*(1.0f-px))+
temp[1]*((1.0f-px)*px*2)+
temp[2]*(px*px);
}
}
//Create indices
m_indices.resize(m_tessellation*(m_tessellation+1)*2);
int row;
for(row=0; row<m_tessellation; ++row)
{
for(int point=0; point<=m_tessellation; ++point)
{
//calculate indices
//reverse them to reverse winding
m_indices[(row*(m_tessellation+1)+point)*2+1]= row*(m_tessellation+1)+point;
m_indices[(row*(m_tessellation+1)+point)*2]= (row+1)*(m_tessellation+1)+point;
}
}
//Fill in the arrays for multi_draw_arrays
m_trianglesPerRow.resize(m_tessellation);
m_rowIndexPointers.resize(m_tessellation);
for(row=0; row<m_tessellation; ++row)
{
m_trianglesPerRow[row]=2*(m_tessellation+1);
m_rowIndexPointers[row]=&m_indices[row*2*(m_tessellation+1)];
}
osg::Vec3Array* patch_vertex_array = new osg::Vec3Array( (m_tessellation+1)*(m_tessellation+1) );
osg::Vec2Array* patch_textcoord_array = new osg::Vec2Array( (m_tessellation+1)*(m_tessellation+1) );
osg::Vec2Array* patch_lmapcoord_array = new osg::Vec2Array( (m_tessellation+1)*(m_tessellation+1) );
for(int i=0;i<(m_tessellation+1)*(m_tessellation+1);i++)
{
(*patch_vertex_array)[i].set( m_vertices[ i ].m_position[0],
m_vertices[ i ].m_position[1],
m_vertices[ i ].m_position[2]
);
(*patch_textcoord_array)[i].set( m_vertices[ i ].m_decalS,
m_vertices[ i ].m_decalT
);
(*patch_lmapcoord_array)[i].set( m_vertices[ i ].m_lightmapS,
m_vertices[ i ].m_lightmapT
);
}
aGeometry->setVertexArray(patch_vertex_array);
aGeometry->setTexCoordArray(0,patch_textcoord_array);
aGeometry->setTexCoordArray(1,patch_lmapcoord_array);
for(row=0; row<m_tessellation; ++row)
{
osg::DrawElementsUInt* face_indices = new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLE_STRIP,
m_tessellation*(m_tessellation+1)*2,
&m_indices[0]
);
aGeometry->addPrimitiveSet(face_indices);
}
return true;
}

View File

@@ -0,0 +1,83 @@
// El siguiente bloque ifdef muestra la forma est<73>ndar de crear macros que facilitan
// la exportaci<63>n de archivos DLL. Todos los archivos de este archivo DLL se compilan con el s<>mbolo Q3BSP_EXPORTS
// definido en la l<>nea de comandos. Este s<>mbolo no se debe definir en ning<6E>n proyecto
// que utilice este archivo DLL. De este modo, otros proyectos cuyos archivos de c<>digo fuente incluyan el archivo
// interpreta que las funciones Q3BSP_API se importan de un archivo DLL, mientras que este archivo DLL interpreta los s<>mbolos
// definidos en esta macro como si fueran exportados.
/*
#ifdef Q3BSP_EXPORTS
#define Q3BSP_API __declspec(dllexport)
#else
#define Q3BSP_API __declspec(dllimport)
#endif
// Clase exportada de q3bsp.dll
class Q3BSP_API Cq3bsp {
public:
Cq3bsp(void);
// TODO: agregar m<>todos aqu<71>.
};
extern Q3BSP_API int nq3bsp;
Q3BSP_API int fnq3bsp(void);
*/
#include <osg/Array>
#include <osg/Node>
#include <osg/Texture2D>
#include <osgDB/ReadFile>
#include "Q3BSPLoad.h"
namespace bsp
{
class Q3BSPReader
{
public:
Q3BSPReader();
bool readFile(const std::string& fileName,
const osgDB::ReaderWriter::Options*);
osg::ref_ptr<osg::Node> getRootNode();
private:
osg::ref_ptr<osg::Node> root_node;
osg::Geode* convertFromBSP(Q3BSPLoad& aLoadData,
const osgDB::ReaderWriter::Options*) const;
osg::Geometry* createMeshFace(
const BSP_LOAD_FACE& aLoadFace,
const std::vector<osg::Texture2D*>& aTextureArray,
osg::Vec3Array& aVertexArray,
std::vector<GLuint>& aIndices,
osg::Vec2Array& aTextureDecalCoords,
osg::Vec2Array& aTextureLMapCoords) const;
osg::Geometry* createPolygonFace(
const BSP_LOAD_FACE& aLoadFace,
const std::vector<osg::Texture2D*>& aTextureArray,
const std::vector<osg::Texture2D*>& aTextureLMapArray,
osg::Vec3Array& aVertexArray,
osg::Vec2Array& aTextureDecalCoords,
osg::Vec2Array& aTextureLMapCoords) const;
bool loadTextures(
const Q3BSPLoad& aLoadData,
std::vector<osg::Texture2D*>& aTextureArray) const;
bool loadLightMaps(
const Q3BSPLoad& aLoadData,
std::vector<osg::Texture2D*>& aTextureArray) const;
};
}

View File

@@ -1,670 +1,120 @@
#include <osg/TexEnv>
#include <osg/CullFace>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/Image>
#include <osg/Texture2D>
#include <osg/PolygonMode>
#include <osg/BlendColor>
#include <osgDB/ReadFile>
#include <osgDB/FileNameUtils>
#include <osg/Node>
#include <osg/Notify>
#include <osgDB/Registry>
#include <osgDB/FileUtils>
#include <osgDB/ReadFile>
#include <assert.h>
#include <fstream>
#include "ReaderWriterBSP.h"
#include "VBSPReader.h"
#include "Q3BSPReader.h"
#include "BSPLoad.h"
using namespace bsp;
using namespace osg;
using namespace osgDB;
class ReaderWriterQ3BSP: public osgDB::ReaderWriter
// "VBSP" for Valve BSP files
const int VBSP_MAGIC_NUMBER = (('P'<<24)+('S'<<16)+('B'<<8)+'V');
// "IBSP" for id (Quake 3) BSP files
const int IBSP_MAGIC_NUMBER = (('P'<<24)+('S'<<16)+('B'<<8)+'I');
const char* ReaderWriterBSP::className() const
{
public:
ReaderWriterQ3BSP()
{
supportsExtension("bsp","Quake3 BSP model format");
}
virtual const char* className() const
{
return "Quake3 BSP Reader";
}
virtual ReadResult readNode(const std::string& fileName, const osgDB::ReaderWriter::Options* options) const;
private:
osg::Geode* convertFromBSP(BSPLoad& aLoadData,const osgDB::ReaderWriter::Options* options) const;
osg::Geometry* createMeshFace(const BSP_LOAD_FACE& aLoadFace,const std::vector<osg::Texture2D*>& aTextureArray,
osg::Vec3Array& aVertexArray,std::vector<GLuint>& aIndices,
osg::Vec2Array& aTextureDecalCoords,osg::Vec2Array& aTextureLMapCoords
) const;
osg::Geometry* createPolygonFace(const BSP_LOAD_FACE& aLoadFace,const std::vector<osg::Texture2D*>& aTextureArray,const std::vector<osg::Texture2D*>& aTextureLMapArray,
osg::Vec3Array& aVertexArray,
osg::Vec2Array& aTextureDecalCoords,osg::Vec2Array& aTextureLMapCoords
) const;
bool loadTextures(const BSPLoad& aLoadData,std::vector<osg::Texture2D*>& aTextureArray) const;
bool loadLightMaps(const BSPLoad& aLoadData,std::vector<osg::Texture2D*>& aTextureArray) const;
};
// Register with Registry to instantiate the above reader/writer.
REGISTER_OSGPLUGIN(bsp, ReaderWriterQ3BSP)
// Read node
osgDB::ReaderWriter::ReadResult ReaderWriterQ3BSP::readNode(const std::string& file, const osgDB::ReaderWriter::Options* options) const
{
std::string ext = osgDB::getLowerCaseFileExtension(file);
if (!acceptsExtension(ext))
return ReadResult::FILE_NOT_HANDLED;
std::string file_name = osgDB::findDataFile( file, options );
if (file_name.empty())
return ReadResult::FILE_NOT_FOUND;
//osg::notify(osg::INFO) << "ReaderWriterQ3BSP::readNode(" << fileName.c_str() << ")\n";
BSPLoad load_data;
load_data.Load(file_name,8);
osg::Geode* geode = convertFromBSP(load_data, options);
if (!geode)
return ReadResult::FILE_NOT_HANDLED;
//osg::StateSet* state_set=geode->getOrCreateStateSet();
//state_set->setMode(osg::CullFace::BACK,osg::StateAttribute::ON);
return geode;
return ReadResult::FILE_NOT_HANDLED;
// Return a description of this class
return "BSP File Reader";
}
enum BSP_FACE_TYPE
bool ReaderWriterBSP::acceptsExtension(const std::string& extension) const
{
bspPolygonFace=1,
bspPatch,
bspMeshFace,
bspBillboard
};
// If the extension is empty or "bsp", we accept it
return osgDB::equalCaseInsensitive(extension, "bsp") || extension.empty();
}
class BSP_VERTEX
ReaderWriter::ReadResult ReaderWriterBSP::readNode(
const std::string& file,
const ReaderWriter::Options* options) const
{
public:
osg::Vec3f m_position;
float m_decalS, m_decalT;
float m_lightmapS, m_lightmapT;
VBSPReader * vbspReader;
Q3BSPReader * q3bspReader;
ref_ptr<Node> result;
osgDB::ifstream stream;
int magicNumber;
int version;
BSP_VERTEX operator+(const BSP_VERTEX & rhs) const
{
BSP_VERTEX result;
result.m_position=m_position+rhs.m_position;
result.m_decalS=m_decalS+rhs.m_decalS;
result.m_decalT=m_decalT+rhs.m_decalT;
result.m_lightmapS=m_lightmapS+rhs.m_lightmapS;
result.m_lightmapT=m_lightmapT+rhs.m_lightmapT;
// See if we handle this kind of file
if (!acceptsExtension(osgDB::getFileExtension(file)))
return ReadResult::FILE_NOT_HANDLED;
return result;
}
// See if we can find the requested file
std::string fileName = osgDB::findDataFile(file, options);
if (fileName.empty())
return ReadResult::FILE_NOT_FOUND;
BSP_VERTEX operator*(const float rhs) const
{
BSP_VERTEX result;
result.m_position=m_position*rhs;
result.m_decalS=m_decalS*rhs;
result.m_decalT=m_decalT*rhs;
result.m_lightmapS=m_lightmapS*rhs;
result.m_lightmapT=m_lightmapT*rhs;
return result;
}
};
//every patch (curved surface) is split into biquadratic (3x3) patches
class BSP_BIQUADRATIC_PATCH
{
public:
BSP_BIQUADRATIC_PATCH():m_vertices(32),m_indices(32)
// Open the file and read the magic number and version
stream.open(file.c_str(), std::ios::binary);
stream.read((char *) &magicNumber, sizeof(int));
stream.read((char *) &version, sizeof(int));
stream.close();
// See which kind of BSP file this is
if ((magicNumber == VBSP_MAGIC_NUMBER) &&
(version >= 19) && (version <= 20))
{
}
~BSP_BIQUADRATIC_PATCH()
{
}
bool Tessellate(int newTessellation,osg::Geometry* aGeometry);
BSP_VERTEX m_controlPoints[9]; // Se accede a ellos en la carga
protected:
int m_tessellation;
std::vector<BSP_VERTEX> m_vertices;
std::vector<GLuint> m_indices;
//arrays for multi_draw_arrays
std::vector<int> m_trianglesPerRow;
std::vector<GLuint *> m_rowIndexPointers;
};
//curved surface
class BSP_PATCH
{
public:
BSP_PATCH():m_quadraticPatches(32)
{
}
~BSP_PATCH()
{
}
int m_textureIndex;
int m_lightmapIndex;
int m_width, m_height;
int m_numQuadraticPatches;
std::vector<BSP_BIQUADRATIC_PATCH> m_quadraticPatches;
};
osg::Geode* ReaderWriterQ3BSP::convertFromBSP(BSPLoad& aLoadData,const osgDB::ReaderWriter::Options*) const
{
std::vector<osg::Texture2D*> texture_array;
loadTextures(aLoadData,texture_array);
std::vector<osg::Texture2D*> lightmap_array;
loadLightMaps(aLoadData,lightmap_array);
osg::Geode* map_geode=new osg::Geode;
// Convertir los vertices
unsigned int num_load_vertices=aLoadData.m_loadVertices.size();
osg::Vec3Array* vertex_array = new osg::Vec3Array(num_load_vertices);
osg::Vec2Array* text_decal_array = new osg::Vec2Array(num_load_vertices);
osg::Vec2Array* text_lmap_array = new osg::Vec2Array(num_load_vertices);
float scale = 0.03;
unsigned int i;
for(i=0; i<num_load_vertices; ++i)
{
BSP_LOAD_VERTEX& vtx=aLoadData.m_loadVertices[i];
//swap y and z and negate z
(*vertex_array)[i]=(osg::Vec3d( vtx.m_position[0]*scale,
-vtx.m_position[1]*scale,
vtx.m_position[2]*scale ) );
//Transfer texture coordinates (Invert t)
(*text_decal_array)[i]=(osg::Vec2d(vtx.m_decalS,-vtx.m_decalT) );
//Transfer lightmap coordinates
(*text_lmap_array)[i]=(osg::Vec2d(vtx.m_lightmapS,vtx.m_lightmapT) );
}
unsigned int num_loadfaces=aLoadData.m_loadFaces.size();
//convert loadFaces to faces
for(i=0; i<num_loadfaces; ++i)
{
const BSP_LOAD_FACE& current_load_face=aLoadData.m_loadFaces[i];
if(current_load_face.m_type!=bspMeshFace) //skip this loadFace if it is not a mesh face
continue;
osg::Geometry* mesh_geom=createMeshFace(current_load_face,texture_array,*vertex_array,aLoadData.m_loadMeshIndices,*text_decal_array,*text_lmap_array);
map_geode->addDrawable(mesh_geom);
}
for(i=0; i<num_loadfaces; ++i)
{
const BSP_LOAD_FACE& current_face=aLoadData.m_loadFaces[i];
if(current_face.m_type!=bspPolygonFace) //skip this loadFace if it is not a polygon face
continue;
osg::Geometry* polygon_geom=createPolygonFace(current_face,texture_array,lightmap_array,*vertex_array,*text_decal_array,*text_lmap_array);
map_geode->addDrawable(polygon_geom);
}
for(i=0; i<num_loadfaces; ++i)
{
const BSP_LOAD_FACE& current_face=aLoadData.m_loadFaces[i];
if(current_face.m_type!=bspPatch) //skip this loadFace if it is not a patch face
continue;
//osg::Group* patch_group=new osg::Group;
BSP_PATCH current_patch;
current_patch.m_textureIndex=current_face.m_texture;
current_patch.m_lightmapIndex=current_face.m_lightmapIndex;
current_patch.m_width=current_face.m_patchSize[0];
current_patch.m_height=current_face.m_patchSize[1];
osg::Texture2D *texture=texture_array[current_face.m_texture];
osg::Texture2D *lightmap_texture=NULL;
if(current_face.m_lightmapIndex>=0)
// Read the Valve file
vbspReader = new VBSPReader();
if (vbspReader->readFile(fileName))
{
lightmap_texture=lightmap_array[current_face.m_lightmapIndex];
// Get the results of our read
result = vbspReader->getRootNode();
// Clean up the reader
delete vbspReader;
// Return the results
return ReadResult(result.get());
}
else
else
{
lightmap_texture=lightmap_array[lightmap_array.size()-1];
// Clean up the reader
delete vbspReader;
// Return the error
return ReadResult::ERROR_IN_READING_FILE;
}
//Create space to hold quadratic patches
int numPatchesWide=(current_patch.m_width-1)/2;
int numPatchesHigh=(current_patch.m_height-1)/2;
current_patch.m_numQuadraticPatches = numPatchesWide*numPatchesHigh;
current_patch.m_quadraticPatches.resize(current_patch.m_numQuadraticPatches);
//fill in the quadratic patches
for(int y=0; y<numPatchesHigh; ++y)
}
else if ((magicNumber == IBSP_MAGIC_NUMBER) && (version == 0x2E))
{
// Read the Quake 3 file
q3bspReader = new Q3BSPReader();
if (q3bspReader->readFile(file, options))
{
for(int x=0; x<numPatchesWide; ++x)
{
for(int row=0; row<3; ++row)
{
for(int point=0; point<3; ++point)
{
BSP_BIQUADRATIC_PATCH& curr_quadraticpatch=current_patch.m_quadraticPatches[y*numPatchesWide+x];
// Get the results of our read
result = q3bspReader->getRootNode();
osg::Vec3f vtx= (*vertex_array) [aLoadData.m_loadFaces[i].m_firstVertexIndex+(y*2*current_patch.m_width+x*2)+
row*current_patch.m_width+point];
// Clean up the reader
delete q3bspReader;
curr_quadraticpatch.m_controlPoints[row*3+point].m_position[0] = vtx.x();
curr_quadraticpatch.m_controlPoints[row*3+point].m_position[1] = vtx.y();
curr_quadraticpatch.m_controlPoints[row*3+point].m_position[2] = vtx.z();
}
}
osg::Geometry* patch_geom = new osg::Geometry;
//tessellate the patch
osg::StateSet* stateset = patch_geom->getOrCreateStateSet();
if(texture)
{
stateset->setTextureAttributeAndModes(0,texture,osg::StateAttribute::ON);
}
if(lightmap_texture)
{
stateset->setTextureAttributeAndModes(1,lightmap_texture,osg::StateAttribute::ON);
}
//patch_group->addChild(map_geode);
current_patch.m_quadraticPatches[y*numPatchesWide+x].Tessellate(8/*aCurveTessellation*/,patch_geom);
map_geode->addDrawable(patch_geom);
}
// Return the results
return ReadResult(result.get());
}
}
//int num_primitive_sets=geom->getNumPrimitiveSets();
//const osg::BoundingSphere& bs=map_geom->getBound();
map_geode->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
return map_geode;
}
osg::Geometry* ReaderWriterQ3BSP::createMeshFace( const BSP_LOAD_FACE& aLoadFace,const std::vector<osg::Texture2D*>& aTextureArray,
osg::Vec3Array& aVertexArray,std::vector<GLuint>& aIndices,
osg::Vec2Array& aTextureDecalCoords,osg::Vec2Array& aTextureLMapCoords
) const
{
osg::Geometry* obj_geom = new osg::Geometry;
osg::Vec3Array* obj_vertex_array = new osg::Vec3Array(aLoadFace.m_numMeshIndices,
&(aVertexArray)[aLoadFace.m_firstVertexIndex]
);
obj_geom->setVertexArray(obj_vertex_array);
osg::DrawElementsUInt* face_indices = new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLES,
aLoadFace.m_numMeshIndices,
&(aIndices)[0]+aLoadFace.m_firstMeshIndex
);
obj_geom->addPrimitiveSet(face_indices);
osg::Texture2D *texture=aTextureArray[aLoadFace.m_texture];
if(texture)
{
osg::StateSet* stateset = obj_geom->getOrCreateStateSet();
stateset->setTextureAttributeAndModes(0,texture,osg::StateAttribute::ON);
stateset->setTextureAttributeAndModes(1,texture,osg::StateAttribute::ON);
osg::Vec2Array* obj_texcoords_array = new osg::Vec2Array(aLoadFace.m_numMeshIndices,
&(aTextureDecalCoords)[aLoadFace.m_firstVertexIndex]
);
obj_geom->setTexCoordArray(0,obj_texcoords_array);
osg::Vec2Array* obj_lmapcoords_array = new osg::Vec2Array(aLoadFace.m_numMeshIndices,
&(aTextureLMapCoords)[aLoadFace.m_firstVertexIndex]
);
obj_geom->setTexCoordArray(1,obj_lmapcoords_array);
}
return obj_geom;
}
osg::Geometry* ReaderWriterQ3BSP::createPolygonFace(const BSP_LOAD_FACE& aLoadFace,const std::vector<osg::Texture2D*>& aTextureArray,const std::vector<osg::Texture2D*>& aTextureLMapArray,
osg::Vec3Array& aVertexArray,
osg::Vec2Array& aTextureDecalCoords,osg::Vec2Array& aTextureLMapCoords
) const
{
osg::Texture2D *texture=aTextureArray[aLoadFace.m_texture];
osg::Geometry* polygon_geom = new osg::Geometry;
polygon_geom->setVertexArray(&aVertexArray);
polygon_geom->setTexCoordArray(0, &aTextureDecalCoords);
polygon_geom->setTexCoordArray(1, &aTextureLMapCoords);
osg::DrawArrays* face_indices = new osg::DrawArrays(osg::PrimitiveSet::TRIANGLE_FAN,
aLoadFace.m_firstVertexIndex,
aLoadFace.m_numVertices
);
osg::StateSet* stateset = polygon_geom->getOrCreateStateSet();
if(texture)
{
stateset->setTextureAttributeAndModes(0,texture,osg::StateAttribute::ON);
if(aLoadFace.m_lightmapIndex>=0)
else
{
texture=aTextureLMapArray[aLoadFace.m_lightmapIndex];
if(texture)
{
stateset->setTextureAttributeAndModes(1,texture,osg::StateAttribute::ON);
}
// Clean up the reader
delete q3bspReader;
// Return the error
return ReadResult::ERROR_IN_READING_FILE;
}
else
{
texture=aTextureLMapArray[aTextureLMapArray.size()-1];
if(texture)
{
stateset->setTextureAttributeAndModes(1,texture,osg::StateAttribute::ON);
}
}
}
else
{
osg::PolygonMode* polygon_mode=new osg::PolygonMode;
polygon_mode->setMode(osg::PolygonMode::FRONT_AND_BACK,osg::PolygonMode::LINE);
stateset->setAttributeAndModes(polygon_mode, osg::StateAttribute::ON);
}
polygon_geom->addPrimitiveSet(face_indices);
return polygon_geom;
else
return ReadResult::FILE_NOT_HANDLED;
}
bool ReaderWriterQ3BSP::loadTextures(const BSPLoad& aLoadData,std::vector<osg::Texture2D*>& aTextureArray) const
{
int num_textures=aLoadData.m_loadTextures.size();
int i;
for(i=0;i<num_textures;i++)
{
//add file extension to the name
std::string tgaExtendedName(aLoadData.m_loadTextures[i].m_name);
tgaExtendedName+=".tga";
std::string jpgExtendedName(aLoadData.m_loadTextures[i].m_name);
jpgExtendedName+=".jpg";
osg::Image* image = osgDB::readImageFile(tgaExtendedName);
if (!image)
{
image = osgDB::readImageFile(jpgExtendedName);
if (!image)
{
aTextureArray.push_back(NULL);
continue; //?
}
}
osg::Texture2D* texture= new osg::Texture2D;
texture->setImage(image);
texture->setDataVariance(osg::Object::DYNAMIC); // protect from being optimized away as static state.
texture->setWrap(osg::Texture2D::WRAP_S, osg::Texture2D::REPEAT);
texture->setWrap(osg::Texture2D::WRAP_T, osg::Texture2D::REPEAT);
aTextureArray.push_back(texture);
}
return true;
}
bool ReaderWriterQ3BSP::loadLightMaps(const BSPLoad& aLoadData,std::vector<osg::Texture2D*>& aTextureArray) const
{
int num_textures=aLoadData.m_loadLightmaps.size();
int i;
for(i=0;i<num_textures;i++)
{
osg::Image* image=new osg::Image;
unsigned char *data=new unsigned char[128*128*3];
memcpy(data,aLoadData.m_loadLightmaps[i].m_lightmapData,128*128*3);
image->setImage(128,128,1,GL_RGBA8,GL_RGB,GL_UNSIGNED_BYTE,data,osg::Image::USE_NEW_DELETE);
osg::Texture2D* texture= new osg::Texture2D;
texture->setImage(image);
texture->setDataVariance(osg::Object::DYNAMIC); // protect from being optimized away as static state.
texture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR_MIPMAP_LINEAR);
texture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR);
texture->setWrap(osg::Texture2D::WRAP_S, osg::Texture2D::REPEAT);
texture->setWrap(osg::Texture2D::WRAP_T, osg::Texture2D::REPEAT);
aTextureArray.push_back(texture);
}
// A continuaci<63>n, a<>ado el blanco
osg::Image* image=new osg::Image;
unsigned char *data=new unsigned char[3];
for(int whiteidx=0;whiteidx<3;whiteidx++)
{
data[whiteidx]=255;
}
image->setImage(1,1,1,GL_RGBA8,GL_RGB,GL_UNSIGNED_BYTE,data,osg::Image::USE_NEW_DELETE);
osg::Texture2D* texture= new osg::Texture2D;
texture->setImage(image);
texture->setDataVariance(osg::Object::DYNAMIC); // protect from being optimized away as static state.
texture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR_MIPMAP_LINEAR);
texture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR);
texture->setWrap(osg::Texture2D::WRAP_S, osg::Texture2D::REPEAT);
texture->setWrap(osg::Texture2D::WRAP_T, osg::Texture2D::REPEAT);
aTextureArray.push_back(texture);
return true;
}
//Tessellate a biquadratic patch
bool BSP_BIQUADRATIC_PATCH::Tessellate(int newTessellation,osg::Geometry* aGeometry)
{
m_tessellation=newTessellation;
float px, py;
BSP_VERTEX temp[3];
m_vertices.resize((m_tessellation+1)*(m_tessellation+1));
for(int v=0; v<=m_tessellation; ++v)
{
px=(float)v/m_tessellation;
m_vertices[v]=m_controlPoints[0]*((1.0f-px)*(1.0f-px))+
m_controlPoints[3]*((1.0f-px)*px*2)+
m_controlPoints[6]*(px*px);
}
for(int u=1; u<=m_tessellation; ++u)
{
py=(float)u/m_tessellation;
temp[0]=m_controlPoints[0]*((1.0f-py)*(1.0f-py))+
m_controlPoints[1]*((1.0f-py)*py*2)+
m_controlPoints[2]*(py*py);
temp[1]=m_controlPoints[3]*((1.0f-py)*(1.0f-py))+
m_controlPoints[4]*((1.0f-py)*py*2)+
m_controlPoints[5]*(py*py);
temp[2]=m_controlPoints[6]*((1.0f-py)*(1.0f-py))+
m_controlPoints[7]*((1.0f-py)*py*2)+
m_controlPoints[8]*(py*py);
for(int v=0; v<=m_tessellation; ++v)
{
px=(float)v/m_tessellation;
m_vertices[u*(m_tessellation+1)+v]=temp[0]*((1.0f-px)*(1.0f-px))+
temp[1]*((1.0f-px)*px*2)+
temp[2]*(px*px);
}
}
//Create indices
m_indices.resize(m_tessellation*(m_tessellation+1)*2);
int row;
for(row=0; row<m_tessellation; ++row)
{
for(int point=0; point<=m_tessellation; ++point)
{
//calculate indices
//reverse them to reverse winding
m_indices[(row*(m_tessellation+1)+point)*2+1]= row*(m_tessellation+1)+point;
m_indices[(row*(m_tessellation+1)+point)*2]= (row+1)*(m_tessellation+1)+point;
}
}
//Fill in the arrays for multi_draw_arrays
m_trianglesPerRow.resize(m_tessellation);
m_rowIndexPointers.resize(m_tessellation);
for(row=0; row<m_tessellation; ++row)
{
m_trianglesPerRow[row]=2*(m_tessellation+1);
m_rowIndexPointers[row]=&m_indices[row*2*(m_tessellation+1)];
}
osg::Vec3Array* patch_vertex_array = new osg::Vec3Array( (m_tessellation+1)*(m_tessellation+1) );
osg::Vec2Array* patch_textcoord_array = new osg::Vec2Array( (m_tessellation+1)*(m_tessellation+1) );
osg::Vec2Array* patch_lmapcoord_array = new osg::Vec2Array( (m_tessellation+1)*(m_tessellation+1) );
for(int i=0;i<(m_tessellation+1)*(m_tessellation+1);i++)
{
(*patch_vertex_array)[i].set( m_vertices[ i ].m_position[0],
m_vertices[ i ].m_position[1],
m_vertices[ i ].m_position[2]
);
(*patch_textcoord_array)[i].set( m_vertices[ i ].m_decalS,
m_vertices[ i ].m_decalT
);
(*patch_lmapcoord_array)[i].set( m_vertices[ i ].m_lightmapS,
m_vertices[ i ].m_lightmapT
);
}
aGeometry->setVertexArray(patch_vertex_array);
aGeometry->setTexCoordArray(0,patch_textcoord_array);
aGeometry->setTexCoordArray(1,patch_lmapcoord_array);
for(row=0; row<m_tessellation; ++row)
{
osg::DrawElementsUInt* face_indices = new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLE_STRIP,
m_tessellation*(m_tessellation+1)*2,
&m_indices[0]
);
aGeometry->addPrimitiveSet(face_indices);
}
return true;
}
REGISTER_OSGPLUGIN(bsp, ReaderWriterBSP)

View File

@@ -1,27 +1,28 @@
// El siguiente bloque ifdef muestra la forma est<73>ndar de crear macros que facilitan
// la exportaci<63>n de archivos DLL. Todos los archivos de este archivo DLL se compilan con el s<>mbolo Q3BSP_EXPORTS
// definido en la l<>nea de comandos. Este s<>mbolo no se debe definir en ning<6E>n proyecto
// que utilice este archivo DLL. De este modo, otros proyectos cuyos archivos de c<>digo fuente incluyan el archivo
// interpreta que las funciones Q3BSP_API se importan de un archivo DLL, mientras que este archivo DLL interpreta los s<>mbolos
// definidos en esta macro como si fueran exportados.
/*
#ifdef Q3BSP_EXPORTS
#define Q3BSP_API __declspec(dllexport)
#else
#define Q3BSP_API __declspec(dllimport)
#endif
#ifndef __READERWRITER_VBSP_H_
#define __READERWRITER_VBSP_H_
// Clase exportada de q3bsp.dll
class Q3BSP_API Cq3bsp {
#include <osgDB/Registry>
#include <osgDB/FileNameUtils>
namespace bsp
{
class ReaderWriterBSP : public osgDB::ReaderWriter
{
public:
Cq3bsp(void);
// TODO: agregar m<>todos aqu<71>.
virtual const char* className() const;
virtual bool acceptsExtension(const std::string& extension) const;
virtual ReadResult readNode(const std::string& file,
const Options* options) const;
};
extern Q3BSP_API int nq3bsp;
Q3BSP_API int fnq3bsp(void);
*/
}
#endif

View File

@@ -0,0 +1,654 @@
#include "VBSPGeometry.h"
using namespace osg;
using namespace osgDB;
using namespace bsp;
VBSPGeometry::VBSPGeometry(VBSPReader * reader)
{
// Keep track of the reader, as it has all of the data lists that we
// need
vbsp_reader = reader;
// Create arrays for the vertex attributes
vertex_array = new Vec3Array();
normal_array = new Vec3Array();
texcoord_array = new Vec2Array();
// Create a primitive set for drawing variable length primitives (VBSP
// primitives are only guaranteed to be convex polygons)
primitive_set = new DrawArrayLengths(PrimitiveSet::POLYGON);
// Create a second set of arrays for displacement surfaces
disp_vertex_array = new Vec3Array();
disp_normal_array = new Vec3Array();
disp_texcoord_array = new Vec2Array();
disp_vertex_attr_array = new FloatArray();
// Create a second primitive set for drawing indexed triangles, which is
// the quickest method for drawing the displacement surfaces
disp_primitive_set = new DrawElementsUInt(PrimitiveSet::TRIANGLES);
}
VBSPGeometry::~VBSPGeometry()
{
}
bool VBSPGeometry::doesEdgeExist(int row, int col, int direction,
int vertsPerEdge)
{
// See if there is an edge on the displacement surface from the given
// vertex in the given direction (we only need to know the vertices
// indices, because all displacement surfaces are tessellated in the
// same way)
switch (direction)
{
case 0:
// False if we're on the left edge, otherwise true
if ((row - 1) < 0)
return false;
else
return true;
case 1:
// False if we're on the top edge, otherwise true
if ((col + 1) >= vertsPerEdge)
return false;
else
return true;
case 2:
// False if we're on the right edge, otherwise true
if ((row + 1) >= vertsPerEdge)
return false;
else
return true;
case 3:
// False if we're on the bottom edge, otherwise true
if ((col - 1) < 0)
return false;
else
return true;
default:
return false;
}
}
osg::Vec3 VBSPGeometry::getNormalFromEdges(int row, int col,
unsigned char edgeBits,
int firstVertex, int vertsPerEdge)
{
osg::Vec3 * vertexData;
osg::Vec3 * surfaceVerts;
osg::Vec3 finalNormal;
osg::Vec3 v1, v2, v3;
osg::Vec3 e1, e2;
osg::Vec3 tempNormal;
int normalCount;
// Constants for direction. If the bit is set in the edgeBits, then
// there is an edge connected to the current vertex in that direction
const unsigned char NEG_X = 1 << 0;
const unsigned char POS_Y = 1 << 1;
const unsigned char POS_X = 1 << 2;
const unsigned char NEG_Y = 1 << 3;
// Constants for quadrants. If both bits are set, then there are
// exactly two triangles in that quadrant
const unsigned char QUAD_1 = POS_X | POS_Y;
const unsigned char QUAD_2 = NEG_X | POS_Y;
const unsigned char QUAD_3 = NEG_X | NEG_Y;
const unsigned char QUAD_4 = POS_X | NEG_Y;
// Grab the vertex data from the displaced vertex array (if there's a
// better way to randomly access the data in this array, I'm all ears)
vertexData = (osg::Vec3 *)disp_vertex_array->getDataPointer();
// Move to the surface we're interested in, and start counting vertices
// from there
surfaceVerts = &vertexData[firstVertex];
// Start with no normals computed
finalNormal.set(0.0, 0.0, 0.0);
normalCount = 0;
// The process is fairly simple. For all four quadrants surrounding
// the vertex, check each quadrant to see if there are triangles there.
// If so, calculate the normals of the two triangles in that quadrant, and
// add them to the final normal. When fininshed, scale the final normal
// based on the number of contributing triangle normals
// Check quadrant 1 (+X,+Y)
if ((edgeBits & QUAD_1) == QUAD_1)
{
// First triangle
v1 = surfaceVerts[(col+1) * vertsPerEdge + row];
v2 = surfaceVerts[col * vertsPerEdge + row];
v3 = surfaceVerts[col * vertsPerEdge + (row+1)];
e1 = v1 - v2;
e2 = v3 - v2;
tempNormal = e2 ^ e1;
tempNormal.normalize();
finalNormal += tempNormal;
normalCount++;
// Second triangle
v1 = surfaceVerts[(col+1) * vertsPerEdge + row];
v2 = surfaceVerts[col * vertsPerEdge + (row+1)];
v3 = surfaceVerts[(col+1) * vertsPerEdge + (row+1)];
e1 = v1 - v2;
e2 = v3 - v2;
tempNormal = e2 ^ e1;
tempNormal.normalize();
finalNormal += tempNormal;
normalCount++;
}
// Check quadrant 2 (-X,+Y)
if ((edgeBits & QUAD_2) == QUAD_2)
{
// First triangle
v1 = surfaceVerts[(col+1) * vertsPerEdge + (row-1)];
v2 = surfaceVerts[col * vertsPerEdge + (row-1)];
v3 = surfaceVerts[col * vertsPerEdge + row];
e1 = v1 - v2;
e2 = v3 - v2;
tempNormal = e2 ^ e1;
tempNormal.normalize();
finalNormal += tempNormal;
normalCount++;
// Second triangle
v1 = surfaceVerts[(col+1) * vertsPerEdge + (row-1)];
v2 = surfaceVerts[col * vertsPerEdge + row];
v3 = surfaceVerts[(col+1) * vertsPerEdge + row];
e1 = v1 - v2;
e2 = v3 - v2;
tempNormal = e2 ^ e1;
tempNormal.normalize();
finalNormal += tempNormal;
normalCount++;
}
// Check quadrant 3 (-X,-Y)
if ((edgeBits & QUAD_3) == QUAD_3)
{
// First triangle
v1 = surfaceVerts[col * vertsPerEdge + (row-1)];
v2 = surfaceVerts[(col-1) * vertsPerEdge + (row-1)];
v3 = surfaceVerts[(col-1) * vertsPerEdge + row];
e1 = v1 - v2;
e2 = v3 - v2;
tempNormal = e2 ^ e1;
tempNormal.normalize();
finalNormal += tempNormal;
normalCount++;
// Second triangle
v1 = surfaceVerts[col * vertsPerEdge + (row-1)];
v2 = surfaceVerts[(col-1) * vertsPerEdge + row];
v3 = surfaceVerts[col * vertsPerEdge + row];
e1 = v1 - v2;
e2 = v3 - v2;
tempNormal = e2 ^ e1;
tempNormal.normalize();
finalNormal += tempNormal;
normalCount++;
}
// Check quadrant 4 (+X,-Y)
if ((edgeBits & QUAD_4) == QUAD_4)
{
// First triangle
v1 = surfaceVerts[col * vertsPerEdge + row];
v2 = surfaceVerts[(col-1) * vertsPerEdge + row];
v3 = surfaceVerts[(col-1) * vertsPerEdge + (row+1)];
e1 = v1 - v2;
e2 = v3 - v2;
tempNormal = e2 ^ e1;
tempNormal.normalize();
finalNormal += tempNormal;
normalCount++;
// Second triangle
v1 = surfaceVerts[col * vertsPerEdge + row];
v2 = surfaceVerts[(col-1) * vertsPerEdge + (row+1)];
v3 = surfaceVerts[col * vertsPerEdge + (row+1)];
e1 = v1 - v2;
e2 = v3 - v2;
tempNormal = e2 ^ e1;
tempNormal.normalize();
finalNormal += tempNormal;
normalCount++;
}
// Scale the final normal according to how many triangle normals are
// contributing
finalNormal *= (1.0f / (float)normalCount);
return finalNormal;
}
void VBSPGeometry::createDispSurface(Face & face, DisplaceInfo & dispInfo)
{
TexInfo currentTexInfo;
TexData currentTexData;
Vec3 texU;
float texUOffset;
float texUScale;
Vec3 texV;
float texVOffset;
float texVScale;
unsigned int i, j, k;
osg::Vec3 temp;
int edgeIndex;
int currentSurfEdge;
Edge currentEdge;
osg::Vec3 currentVertex;
osg::Vec3 vertices[4];
unsigned int firstVertex;
unsigned int numEdgeVertices;
double subdivideScale;
osg::Vec3 leftEdge, rightEdge;
osg::Vec3 leftEdgeStep, rightEdgeStep;
osg::Vec3 leftEnd, rightEnd;
osg::Vec3 leftRightSeg, leftRightStep;
unsigned int dispVertIndex;
DisplacedVertex dispVertInfo;
osg::Vec3 flatVertex, dispVertex;
unsigned int index;
osg::Vec3 normal;
float u, v;
osg::Vec2 texCoord;
unsigned char edgeBits;
// Get the texture info for this face
currentTexInfo = vbsp_reader->getTexInfo(face.texinfo_index);
currentTexData = vbsp_reader->getTexData(currentTexInfo.texdata_index);
// Get the texture vectors and offsets. These are used to calculate
// texture coordinates
texU.set(currentTexInfo.texture_vecs[0][0],
currentTexInfo.texture_vecs[0][1],
currentTexInfo.texture_vecs[0][2]);
texUOffset = currentTexInfo.texture_vecs[0][3];
texV.set(currentTexInfo.texture_vecs[1][0],
currentTexInfo.texture_vecs[1][1],
currentTexInfo.texture_vecs[1][2]);
texVOffset = currentTexInfo.texture_vecs[1][3];
// Get the size of the texture involved, as the planar texture projection
// assumes non-normalized texture coordinates
texUScale = 1.0 / (float)currentTexData.texture_width;
texVScale = 1.0 / (float)currentTexData.texture_height;
// Get the first edge index
edgeIndex = face.first_edge;
// Get the base vertices for this face
for (i = 0; i < face.num_edges; i++)
{
// Look up the edge specified by the surface edge index, the
// index might be negative (see below), so take the absolute
// value
currentSurfEdge = vbsp_reader->getSurfaceEdge(edgeIndex);
currentEdge = vbsp_reader->getEdge(abs(currentSurfEdge));
// The sign of the surface edge index specifies which vertex is
// "first" for this face. A negative index means the edge should
// be flipped, and the second vertex treated as the first
if (currentSurfEdge < 0)
currentVertex = vbsp_reader->getVertex(currentEdge.vertex[1]);
else
currentVertex = vbsp_reader->getVertex(currentEdge.vertex[0]);
// Add the vertex to the array
vertices[i] = currentVertex;
// Move on to the next vertex
edgeIndex++;
}
// Rotate the base coordinates for the surface until the first vertex
// matches the start position
while ((fabs(vertices[0].x() - dispInfo.start_position.x()) > 0.1) ||
(fabs(vertices[0].y() - dispInfo.start_position.y()) > 0.1) ||
(fabs(vertices[0].z() - dispInfo.start_position.z()) > 0.1))
{
temp = vertices[0];
vertices[0] = vertices[1];
vertices[1] = vertices[2];
vertices[2] = vertices[3];
vertices[3] = temp;
}
// Calculate the vectors for the left and right edges of the surface
// (remembering that the surface is wound clockwise)
leftEdge = vertices[1] - vertices[0];
rightEdge = vertices[2] - vertices[3];
// Calculate the number of vertices along each edge of the surface
numEdgeVertices = (1 << dispInfo.power) + 1;
// Calculate the subdivide scale, which will tell us how far apart to
// put each vertex (relative to the length of the surface's edges)
subdivideScale = 1.0 / (double)(numEdgeVertices - 1);
// Calculate the step size between vertices on the left and right edges
leftEdgeStep = leftEdge * subdivideScale;
rightEdgeStep = rightEdge * subdivideScale;
// Remember the first vertex index in the vertex array
firstVertex = disp_vertex_array->size();
// Generate the displaced vertices (this technique comes from the
// Source SDK)
for (i = 0; i < numEdgeVertices; i++)
{
// Calculate the two endpoints for this section of the surface
leftEnd = leftEdgeStep * (double) i;
leftEnd += vertices[0];
rightEnd = rightEdgeStep * (double) i;
rightEnd += vertices[3];
// Now, get the vector from left to right, and subdivide it as well
leftRightSeg = rightEnd - leftEnd;
leftRightStep = leftRightSeg * subdivideScale;
// Generate the vertices for this section
for (j = 0; j < numEdgeVertices; j++)
{
// Get the displacement info for this vertex
dispVertIndex = dispInfo.disp_vert_start;
dispVertIndex += i * numEdgeVertices + j;
dispVertInfo = vbsp_reader->getDispVertex(dispVertIndex);
// Calculate the flat vertex
flatVertex = leftEnd + (leftRightStep * (double) j);
// Calculate the displaced vertex
dispVertex =
dispVertInfo.displace_vec * dispVertInfo.displace_dist;
dispVertex += flatVertex;
// Add the vertex to the displaced vertex array
disp_vertex_array->push_back(dispVertex);
// Calculate the texture coordinates for this vertex. Texture
// coordinates are calculated using a planar projection, so we need
// to use the non-displaced vertex position here
u = texU * flatVertex + texUOffset;
u *= texUScale;
v = texV * flatVertex + texVOffset;
v *= texVScale;
texCoord.set(u, v);
// Add the texture coordinate to the array
disp_texcoord_array->push_back(texCoord);
// Get the texture blend parameter for this vertex as well
disp_vertex_attr_array->
push_back(dispVertInfo.alpha_blend / 255.0);
}
}
// Calculate normals at each vertex (this is adapted from the Source SDK,
// including the two helper functions)
for (i = 0; i < numEdgeVertices; i++)
{
for (j = 0; j < numEdgeVertices; j++)
{
// See which of the 4 possible edges (left, up, right, or down) are
// incident on this vertex
edgeBits = 0;
for (k = 0; k < 4; k++)
{
if (doesEdgeExist(j, i, k, numEdgeVertices))
edgeBits |= 1 << k;
}
// Calculate the normal based on the adjacent edges
normal = getNormalFromEdges(j, i, edgeBits, firstVertex,
numEdgeVertices);
// Add the normal to the normal array
disp_normal_array->push_back(normal);
}
}
// Now, triangulate the surface (this technique comes from the Source SDK)
for (i = 0; i < numEdgeVertices-1; i++)
{
for (j = 0; j < numEdgeVertices-1; j++)
{
// Get the current vertex index (local to this surface)
index = i * numEdgeVertices + j;
// See if this index is odd
if ((index % 2) == 1)
{
// Add the vertex offset (so we reference this surface's
// vertices in the array)
index += firstVertex;
// Create two triangles on this vertex from top-left to
// bottom-right
disp_primitive_set->push_back(index);
disp_primitive_set->push_back(index + 1);
disp_primitive_set->push_back(index + numEdgeVertices);
disp_primitive_set->push_back(index + 1);
disp_primitive_set->push_back(index + numEdgeVertices + 1);
disp_primitive_set->push_back(index + numEdgeVertices);
}
else
{
// Add the vertex offset (so we reference this surface's
// vertices in the array)
index += firstVertex;
// Create two triangles on this vertex from bottom-left to
// top-right
disp_primitive_set->push_back(index);
disp_primitive_set->push_back(index + numEdgeVertices + 1);
disp_primitive_set->push_back(index + numEdgeVertices);
disp_primitive_set->push_back(index);
disp_primitive_set->push_back(index + 1);
disp_primitive_set->push_back(index + numEdgeVertices + 1);
}
}
}
}
void VBSPGeometry::addFace(int faceIndex)
{
Face currentFace;
Edge currentEdge;
DisplaceInfo currentDispInfo;
TexInfo currentTexInfo;
TexData currentTexData;
Vec3 normal;
int edgeIndex;
int i;
int currentSurfEdge;
Vec3 currentVertex;
Vec3 texU;
float texUOffset;
float texUScale;
Vec3 texV;
float texVOffset;
float texVScale;
float u, v;
Vec2f texCoord;
// Make sure this face is not "on node" (an internal node of the BSP tree).
// These faces are not used for visible geometry
currentFace = vbsp_reader->getFace(faceIndex);
// See if this is a displacement surface
if (currentFace.dispinfo_index != -1)
{
// Get the displacement info
currentDispInfo =
vbsp_reader->getDispInfo(currentFace.dispinfo_index);
// Generate the displacement surface
createDispSurface(currentFace, currentDispInfo);
}
else
{
// Get the face normal, using the plane information
normal = vbsp_reader->getPlane(currentFace.plane_index).plane_normal;
if (currentFace.plane_side != 0)
normal = -normal;
// Get the texture info and data structures
currentTexInfo = vbsp_reader->getTexInfo(currentFace.texinfo_index);
currentTexData = vbsp_reader->getTexData(currentTexInfo.texdata_index);
// Get the texture vectors and offsets. These are used to calculate
// texture coordinates
texU.set(currentTexInfo.texture_vecs[0][0],
currentTexInfo.texture_vecs[0][1],
currentTexInfo.texture_vecs[0][2]);
texUOffset = currentTexInfo.texture_vecs[0][3];
texV.set(currentTexInfo.texture_vecs[1][0],
currentTexInfo.texture_vecs[1][1],
currentTexInfo.texture_vecs[1][2]);
texVOffset = currentTexInfo.texture_vecs[1][3];
// Get the texture size, as the planar texture projection results in
// non-normalized texture coordinates
texUScale = 1.0 / (float)currentTexData.texture_width;
texVScale = 1.0 / (float)currentTexData.texture_height;
// Start with the last edge index, because we need to switch from
// clockwise winding (DirectX) to counter-clockwise winding (OpenGL)
edgeIndex = currentFace.first_edge + currentFace.num_edges - 1;
// Set the length of this primitive on the primitive set
primitive_set->push_back(currentFace.num_edges);
// Iterate over the edges in this face, and extract the vertex data
for (i = 0; i < currentFace.num_edges; i++)
{
// Look up the edge specified by the surface edge index, the
// index might be negative (see below), so take the absolute
// value
currentSurfEdge = vbsp_reader->getSurfaceEdge(edgeIndex);
currentEdge = vbsp_reader->getEdge(abs(currentSurfEdge));
// The sign of the surface edge index specifies which vertex is
// "first" for this face. A negative index means the edge should
// be flipped, and the second vertex treated as the first
if (currentSurfEdge < 0)
currentVertex = vbsp_reader->getVertex(currentEdge.vertex[1]);
else
currentVertex = vbsp_reader->getVertex(currentEdge.vertex[0]);
// Add the vertex to the array
vertex_array->push_back(currentVertex);
// Set the normal
normal_array->push_back(normal);
// Calculate the texture coordinates for this vertex
u = texU * currentVertex + texUOffset;
u *= texUScale;
v = texV * currentVertex + texVOffset;
v *= texVScale;
texCoord.set(u, v);
// Add the texture coordinate to the array
texcoord_array->push_back(texCoord);
// Move on to the next (previous?) vertex
edgeIndex--;
}
}
}
ref_ptr<Group> VBSPGeometry::createGeometry()
{
ref_ptr<Group> rootGroup;
ref_ptr<Geode> geode;
ref_ptr<Geometry> geometry;
Vec4f color;
ref_ptr<Vec4Array> colorArray;
// Create the root group (we'll attach everything to this group and
// return it)
rootGroup = new Group();
// Create a geode for the geometries
geode = new Geode();
rootGroup->addChild(geode.get());
// See if there are any regular (non-displaced) faces to render
if (primitive_set->size() > 0)
{
// Create a geometry object for the regular surfaces
geometry = new Geometry();
// Add the vertex attributes
geometry->setVertexArray(vertex_array.get());
geometry->setNormalArray(normal_array.get());
geometry->setNormalBinding(Geometry::BIND_PER_VERTEX);
geometry->setTexCoordArray(0, texcoord_array.get());
// Add an overall color
color.set(1.0, 1.0, 1.0, 1.0);
colorArray = new Vec4Array(1, &color);
geometry->setColorArray(colorArray.get());
geometry->setColorBinding(Geometry::BIND_OVERALL);
// Add our primitive set to the geometry
geometry->addPrimitiveSet(primitive_set.get());
// Add the geometry to the geode
geode->addDrawable(geometry.get());
}
// Now do the same for the displacement surfaces (if any)
if (disp_primitive_set->size() > 0)
{
// Create a geometry object for the regular surfaces
geometry = new Geometry();
// Add the vertex attributes
geometry->setVertexArray(disp_vertex_array.get());
geometry->setNormalArray(disp_normal_array.get());
geometry->setNormalBinding(Geometry::BIND_PER_VERTEX);
geometry->setTexCoordArray(0, disp_texcoord_array.get());
geometry->setVertexAttribArray(1, disp_vertex_attr_array.get());
geometry->setVertexAttribBinding(1, Geometry::BIND_PER_VERTEX);
// Add an overall color
color.set(1.0, 1.0, 1.0, 1.0);
colorArray = new Vec4Array(1, &color);
geometry->setColorArray(colorArray.get());
geometry->setColorBinding(Geometry::BIND_OVERALL);
// Add our primitive set to the geometry
geometry->addPrimitiveSet(disp_primitive_set.get());
// Add the geometry to the geode
geode->addDrawable(geometry.get());
}
// Return the root group
return rootGroup;
}

View File

@@ -0,0 +1,54 @@
#ifndef VBSP_GEOMETRY_H
#define VBSP_GEOMETRY_H
#include <osg/Array>
#include <osg/Geometry>
#include "VBSPReader.h"
namespace bsp
{
class VBSPGeometry
{
protected:
VBSPReader * vbsp_reader;
osg::ref_ptr<osg::Vec3Array> vertex_array;
osg::ref_ptr<osg::Vec3Array> normal_array;
osg::ref_ptr<osg::Vec2Array> texcoord_array;
osg::ref_ptr<osg::DrawArrayLengths> primitive_set;
osg::ref_ptr<osg::Vec3Array> disp_vertex_array;
osg::ref_ptr<osg::Vec3Array> disp_normal_array;
osg::ref_ptr<osg::Vec2Array> disp_texcoord_array;
osg::ref_ptr<osg::FloatArray> disp_vertex_attr_array;
osg::ref_ptr<osg::DrawElementsUInt> disp_primitive_set;
bool doesEdgeExist(int row, int col, int direction,
int vertsPerEdge);
osg::Vec3f getNormalFromEdges(int row, int col,
unsigned char edgeBits,
int firstVertex, int vertsPerEdge);
void createDispSurface(Face & face, DisplaceInfo & dispInfo);
public:
VBSPGeometry(VBSPReader * reader);
virtual ~VBSPGeometry();
void addFace(int faceIndex);
osg::ref_ptr<osg::Group> createGeometry();
};
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,385 @@
#ifndef __VBSP_READER_H_
#define __VBSP_READER_H_
#include <osg/Geometry>
#include <osg/Matrixd>
#include <osg/Node>
#include <osg/Object>
#include <osg/StateSet>
#include <osgDB/Registry>
#include <osgDB/FileNameUtils>
#include <osg/Referenced>
namespace bsp
{
// The magic number for a Valve BSP file is 'VBSP' in little-endian
// order
const int MAGIC_NUMBER = (('P'<<24)+('S'<<16)+('B'<<8)+'V');
enum LumpType
{
ENTITIES_LUMP,
PLANES_LUMP,
TEXDATA_LUMP,
VERTICES_LUMP,
VISIBILITY_LUMP,
NODES_LUMP,
TEXINFO_LUMP,
FACES_LUMP,
LIGHTING_LUMP,
OCCLUSION_LUMP,
LEAFS_LUMP,
UNUSED_LUMP_11,
EDGES_LUMP,
SURFEDGES_LUMP,
MODELS_LUMP,
WORLD_LIGHTS_LUMP,
LEAF_FACES_LUMP,
LEAF_BRUSHES_LUMP,
BRUSHES_LUMP,
BRUSH_SIDES_LUMP,
AREAS_LUMP,
AREA_PORTALS_LUMP,
PORTALS_LUMP,
CLUSTERS_LUMP,
PORTAL_VERTS_LUMP,
CLUSTER_PORTALS_LUMP,
DISPINFO_LUMP,
ORIGINAL_FACES_LUMP,
UNUSED_LUMP_28,
PHYS_COLLIDE_LUMP,
VERT_NORMALS_LUMP,
VERT_NORMAL_INDICES_LUMP,
DISP_LIGHTMAP_ALPHAS_LUMP,
DISP_VERTS_LUMP,
DISP_LIGHTMAP_SAMPLE_POS_LUMP,
GAME_LUMP,
LEAF_WATER_DATA_LUMP,
PRIMITIVES_LUMP,
PRIM_VERTS_LUMP,
PRIM_INDICES_LUMP,
PAK_FILE_LUMP,
CLIP_PORTAL_VERTS_LUMP,
CUBEMAPS_LUMP,
TEXDATA_STRING_DATA_LUMP,
TEXDATA_STRING_TABLE_LUMP,
OVERLAYS_LUMP,
LEAF_MIN_DIST_TO_WATER_LUMP,
FACE_MACRO_TEXTURE_INFO_LUMP,
DISP_TRIS_LUMP,
PHYS_COLLIDE_SURFACE_LUMP,
UNUSED_LUMP_50,
UNUSED_LUMP_51,
UNUSED_LUMP_52,
LIGHTING_HDR_LUMP,
WORLD_LIGHTS_HDR_LUMP,
LEAF_LIGHT_HDR_1_LUMP,
LEAF_LIGHT_HDR_2_LUMP,
UNUSED_LUMP_57,
UNUSED_LUMP_58,
UNUSED_LUMP_59,
UNUSED_LUMP_60,
UNUSED_LUMP_61,
UNUSED_LUMP_62,
UNUSED_LUMP_63,
MAX_LUMPS
};
const char LUMP_DESCRIPTION[][64] =
{
"ENTITIES_LUMP",
"PLANES_LUMP",
"TEXDATA_LUMP",
"VERTICES_LUMP",
"VISIBILITY_LUMP",
"NODES_LUMP",
"TEXINFO_LUMP",
"FACES_LUMP",
"LIGHTING_LUMP",
"OCCLUSION_LUMP",
"LEAFS_LUMP",
"UNUSED_LUMP_11",
"EDGES_LUMP",
"SURFEDGES_LUMP",
"MODELS_LUMP",
"WORLD_LIGHTS_LUMP",
"LEAF_FACES_LUMP",
"LEAF_BRUSHES_LUMP",
"BRUSHES_LUMP",
"BRUSH_SIDES_LUMP",
"AREAS_LUMP",
"AREA_PORTALS_LUMP",
"PORTALS_LUMP",
"CLUSTERS_LUMP",
"PORTAL_VERTS_LUMP",
"CLUSTER_PORTALS_LUMP",
"DISPINFO_LUMP",
"ORIGINAL_FACES_LUMP",
"UNUSED_LUMP_28",
"PHYS_COLLIDE_LUMP",
"VERT_NORMALS_LUMP",
"VERT_NORMAL_INDICES_LUMP",
"DISP_LIGHTMAP_ALPHAS_LUMP",
"DISP_VERTS_LUMP",
"DISP_LIGHTMAP_SAMPLE_POS_LUMP",
"GAME_LUMP",
"LEAF_WATER_DATA_LUMP",
"PRIMITIVES_LUMP",
"PRIM_VERTS_LUMP",
"PRIM_INDICES_LUMP",
"PAK_FILE_LUMP",
"CLIP_PORTAL_VERTS_LUMP",
"CUBEMAPS_LUMP",
"TEXDATA_STRING_DATA_LUMP",
"TEXDATA_STRING_TABLE_LUMP",
"OVERLAYS_LUMP",
"LEAF_MIN_DIST_TO_WATER_LUMP",
"FACE_MACRO_TEXTURE_INFO_LUMP",
"DISP_TRIS_LUMP",
"PHYS_COLLIDE_SURFACE_LUMP",
"UNUSED_LUMP_50",
"UNUSED_LUMP_51",
"UNUSED_LUMP_52",
"LIGHTING_HDR_LUMP",
"WORLD_LIGHTS_HDR_LUMP",
"LEAF_LIGHT_HDR_1_LUMP",
"LEAF_LIGHT_HDR_2_LUMP",
"UNUSED_LUMP_57",
"UNUSED_LUMP_58",
"UNUSED_LUMP_59",
"UNUSED_LUMP_60",
"UNUSED_LUMP_61",
"UNUSED_LUMP_62",
"UNUSED_LUMP_63"
};
struct LumpEntry
{
int file_offset;
int lump_length;
int lump_version;
char ident_code[4];
};
struct Header
{
int magic_number;
int bsp_version;
LumpEntry lump_table[MAX_LUMPS];
int map_revision;
};
struct Plane
{
osg::Vec3f plane_normal;
float origin_dist;
int type;
};
struct Edge
{
unsigned short vertex[2];
};
struct Face
{
unsigned short plane_index;
unsigned char plane_side;
unsigned char on_node;
int first_edge;
short num_edges;
short texinfo_index;
short dispinfo_index;
short surface_fog_volume_id;
unsigned char styles[4];
int light_offset;
float face_area;
int lightmap_texture_mins_in_luxels[2];
int lightmap_texture_size_in_luxels[2];
int original_face;
unsigned short num_primitives;
unsigned short first_primitive_id;
unsigned int smoothing_groups;
};
struct TexInfo
{
float texture_vecs[2][4];
float lightmap_vecs[2][4];
int texture_flags;
int texdata_index;
};
struct TexData
{
osg::Vec3f texture_reflectivity;
int name_string_table_id;
int texture_width;
int texture_height;
int view_width;
int view_height;
};
struct DisplaceSubNeighbor
{
unsigned short neighbor_index;
unsigned char neighbor_orient;
unsigned char local_span;
unsigned char neighbor_span;
};
struct DisplaceNeighbor
{
DisplaceSubNeighbor sub_neighbors[2];
};
struct DisplaceCornerNeighbor
{
unsigned short neighbor_indices[4];
unsigned char neighbor_count;
};
struct DisplaceInfo
{
osg::Vec3f start_position;
int disp_vert_start;
int disp_tri_start;
int power;
int min_tesselation;
float smooth_angle;
int surface_contents;
unsigned short map_face;
int lightmap_alpha_start;
int lightmap_sample_pos_start;
DisplaceNeighbor edge_neighbors[4];
DisplaceCornerNeighbor corner_neighbors[4];
unsigned long allowed_verts[10];
};
struct DisplacedVertex
{
osg::Vec3f displace_vec;
float displace_dist;
float alpha_blend;
};
class VBSPReader
{
protected:
std::string map_name;
osg::ref_ptr<osg::Node> root_node;
char ** entity_list;
int num_entities;
Plane * plane_list;
int num_planes;
osg::Vec3f * vertex_list;
int num_vertices;
Edge * edge_list;
int num_edges;
int * surface_edges;
int num_surf_edges;
Face * face_list;
int num_faces;
TexInfo * texinfo_list;
int num_texinfo_entries;
TexData * texdata_list;
int num_texdata_entries;
int * texdata_string_table;
int num_texdata_string_table_entries;
char ** texdata_string_data;
int num_texdata_strings;
DisplaceInfo * dispinfo_list;
int num_dispinfo_entries;
DisplacedVertex * displaced_vertex_list;
int num_displaced_vertices;
void writeBlanks(int count) const;
void processEntities(std::istream & str, int offset, int length);
void processPlanes(std::istream & str, int offset, int length);
void processVertices(std::istream & str, int offset, int length);
void processEdges(std::istream & str, int offset, int length);
void processSurfEdges(std::istream & str, int offset, int length);
void processFaces(std::istream & str, int offset, int length);
void processTexInfo(std::istream & str, int offset, int length);
void processTexData(std::istream & str, int offset, int length);
void processTexDataStringTable(std::istream & str, int offset,
int length);
void processTexDataStringData(std::istream & str, int offset,
int length);
void processDispInfo(std::istream & str, int offset, int length);
void processDispVerts(std::istream & str, int offset, int length);
std::string getToken(std::string str, const char * delim,
int & index);
std::string findFileIgnoreCase(std::string filePath);
osg::ref_ptr<osg::StateSet> createBlendShader(osg::Texture * tex1,
osg::Texture * tex2);
osg::ref_ptr<osg::Texture> readTextureFile(std::string file);
osg::ref_ptr<osg::StateSet> readMaterialFile(std::string file);
void createScene();
public:
VBSPReader();
virtual ~VBSPReader();
const char * getEntity(int index) const;
const Plane & getPlane(int index) const;
const osg::Vec3f & getVertex(int index) const;
const Edge & getEdge(int index) const;
const int getSurfaceEdge(int index) const;
const Face & getFace(int index) const;
const TexInfo & getTexInfo(int index) const;
const TexData & getTexData(int index) const;
const char * getTexDataString(int index) const;
const DisplaceInfo & getDispInfo(int index) const;
const DisplacedVertex & getDispVertex(int index) const;
bool readFile(const std::string & file);
osg::ref_ptr<osg::Node> getRootNode();
};
}
#endif

View File

@@ -0,0 +1,99 @@
Source Engine BSP map reader for OSG
by Jason Daly
Overview
--------
This plugin allows .bsp files from games that make use of Valve's Source
engine (Half Life 2, etc) to be loaded by OSG.
I've tested this plugin on several HL2 deathmatch maps, as well as some 3rd
party maps.
Using the Plugin
----------------
Unless you've got a 3rd party map that doesn't make use of any data from the
original Source engine games, you'll need to extract the relevant maps and
materials from the .gcf files that come with the game. A good tool for this
is GCFScape, available at http://www.nemesis.thewavelength.net/index.php?p=26
The plugin expects the maps and materials to be arranged as they are in the
.gcf file (maps in maps/ materials and textures in materials/). Only the
maps/ and materials/ directories are used by this plugin (models aren't loaded
yet, see below).
Make sure to preserve the file and directory structure as it is in the .gcf
files. The standard osgDB search routines don't work, because the .bsp
file and .vmt files often mix case when referring to materials, textures, etc.
The regular osgDB search routines can't do a fully case-insensitive search
for a file, so an explicit, special-purpose search is performed by the plugin
instead.
What Works
----------
All brush geometry and faces.
Displacement surfaces.
Textures (including blended textures on displacement surfaces). This makes
use of the VTF Reader plugin which was submitted at the same time as this
plugin.
What Doesn't Work (yet)
-----------------------
Light maps. This requires reading the light map information lumps from the
.bsp file, and creating a shader to render them on the various surfaces.
Shouldn't be too hard. Currently, the plugin just treats lightmapped surfaces
the same as vertex lit surfaces.
Environment maps and shiny textures. Both of these require reading cube maps.
Cube maps and environment mapping materials are typically stored inside the
.bsp file as an embedded .pak file (essentially a .zip file with no
compression. Reading them would require parsing the .pak file and reading the
embedded .vmt to find the pointers to the base texture and cube map. Then the
cube map must be read from the embedded .pak file as well. Finally, a shader
would be created to render them. Currently, the plugin cheats by guessing
the base texture from the embedded material name, and loading it. This
seems to work well enough for now.
Water. Water shaders in the Source engine are a lot more complicated than
the generic lightmapped and vertex lit shaders. Currently, water surfaces just
show up solid white.
Props (both static and physics). This is a big one, as props often contribute
to the overall flow and feeling of a map. Unfortunately, this requires yet
another plugin to load the .mdl/.vvd/.vtx files that store Source models.
These formats aren't nearly as well documented as the .bsp format, which is the
main reason they're not done yet.
World lights (light sources). Should be simple to read, but I don't know
how many you'll find on a given map. If there are a lot of them, you'll
have to be creative on how they're rendered.
HDR materials, bump maps, detail props, and other eye-candy. Implement it if
you want! :-)
Acknowledgements
----------------
This plugin was written for some real-world work I'm doing at the University
of Central Florida Institute for Simulation and Training. I want to thank
our sponsors for funding our work and allowing me to contribute this code to
the OSG community.
This plugin wouldn't have been possible without Rof's "The Source Engine BSP
Format" at http://www.geocities.com/cofrdrbob/bspformat.html which itself
is based on Max McGuire's "Quake 2 BSP File Format" at
http://www.flipcode.com/archives/Quake_2_BSP_File_Format.shtml
Portions of the code borrow heavily from the Source SDK (especially the
code for the displacement surfaces), so thanks to Valve for making much of
that code public.
Of course, this code would be superfluous without the Open Scene Graph and
all of its contributors.