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:
@@ -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)
|
||||
|
||||
21
src/osgPlugins/bsp/CMakeLists.txt
Normal file
21
src/osgPlugins/bsp/CMakeLists.txt
Normal 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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
641
src/osgPlugins/bsp/Q3BSPReader.cpp
Normal file
641
src/osgPlugins/bsp/Q3BSPReader.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
83
src/osgPlugins/bsp/Q3BSPReader.h
Normal file
83
src/osgPlugins/bsp/Q3BSPReader.h
Normal 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;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
654
src/osgPlugins/bsp/VBSPGeometry.cpp
Normal file
654
src/osgPlugins/bsp/VBSPGeometry.cpp
Normal 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;
|
||||
}
|
||||
|
||||
54
src/osgPlugins/bsp/VBSPGeometry.h
Normal file
54
src/osgPlugins/bsp/VBSPGeometry.h
Normal 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
|
||||
|
||||
1097
src/osgPlugins/bsp/VBSPReader.cpp
Normal file
1097
src/osgPlugins/bsp/VBSPReader.cpp
Normal file
File diff suppressed because it is too large
Load Diff
385
src/osgPlugins/bsp/VBSPReader.h
Normal file
385
src/osgPlugins/bsp/VBSPReader.h
Normal 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
|
||||
99
src/osgPlugins/bsp/VBSP_README.txt
Normal file
99
src/osgPlugins/bsp/VBSP_README.txt
Normal 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.
|
||||
|
||||
Reference in New Issue
Block a user