Files
OpenSceneGraph/src/osgPlugins/bsp/Q3BSPReader.cpp
2016-06-10 19:35:44 +01:00

655 lines
19 KiB
C++
Raw Blame History

#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 <string.h>
#include "Q3BSPReader.h"
#include "Q3BSPLoad.h"
#ifndef GL_RGBA8
#define GL_RGBA8 0x8058
#endif
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_tessellation(0),
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_textureIndex(0),
m_lightmapIndex(0),
m_width(0), m_height(0),
m_numQuadraticPatches(0),
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::ref_ptr<osg::Vec3Array> vertex_array = new osg::Vec3Array(num_load_vertices);
osg::ref_ptr<osg::Vec2Array> text_decal_array = new osg::Vec2Array(num_load_vertices);
osg::ref_ptr<osg::Vec2Array> text_lmap_array = new osg::Vec2Array(num_load_vertices);
float scale = 0.0254;
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::ref_ptr<osg::Image> image = osgDB::readRefImageFile(tgaExtendedName);
if (!image)
{
image = osgDB::readRefImageFile(jpgExtendedName);
if (!image)
{
aTextureArray.push_back(NULL);
continue; //?
}
}
osg::Texture2D* texture= new osg::Texture2D;
texture->setImage(image.get());
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;
}