From Jason Daly, "'ve been busy working on the Source engine plugins. There are several contributions in this submission:
osgDB/FileUtils.cpp: Needed this extra code to allow a true case-insensitive search. This is because the HL2 map and model files are often sloppy with case. For example, the file might look for materials/models/alyx/alyx_sheet.vtf, but the file is actually in materials/Models/Alyx/alyx_sheet.vtf. In case-insensitive mode, the new code recursively disassembles the path and checks each path element without regard to case. In case-sensitive mode, the code behaves exactly as it used to. The new code is also mostly skipped on Windows because of the case-insensitive file system. Previously, I did all of this with custom search code in the .bsp plugin, but this allows the user to tailor the search using OSGFILEPATH. There are some instructions in the plugins' README files about this. osgPlugins/mdl: This is a new plug-in for Half-Life 2 models (as opposed to maps). This allows you to load Source models individually, as well as allowing the .bsp plugin to load models (props) that are embedded into maps. Mdl files can contain simple object (crates, barrels, bottles), as well as fully articulated characters with skeletal animations. Currently, it can load the simple objects. It can also load the characters, but it can't load the skeletons or animations. osgPlugins/bsp: This contains all of the changes needed to load props along with the basic map geometry. There are also several bugs fixed. osgPlugins/vtf: This is the loader for Valve's texture format. Previously, we had agreed to put this in with the bsp plugin, but I didn't think of the .mdl plugin at that time. It's conceivable that a user might want to load models individually (not as part of a map), so the vtf reader does have to be separate. I also fixed a rather significant bug. I tested all of this code on RHEL 5.2 (32-bit), and Fedora 9 (64-bit). I'll be testing on Windows soon. I also attached a simple .mdl file, along with it's associated files and textures. Just extract the tarball into it's own directory, set your OSGFILEPATH to point at that directory, and load the model like this: osgviewer models/props_junk/gascan001a.mdl"
This commit is contained in:
@@ -378,38 +378,122 @@ std::string osgDB::findFileInDirectory(const std::string& fileName,const std::st
|
||||
bool needDirectoryName = true;
|
||||
osgDB::DirectoryContents dc;
|
||||
|
||||
if (dirName.empty())
|
||||
std::string realDirName = dirName;
|
||||
std::string realFileName = fileName;
|
||||
|
||||
// Skip case-insensitive recursion if on Windows
|
||||
#ifdef WIN32
|
||||
bool win32 = true;
|
||||
#else
|
||||
bool win32 = false;
|
||||
#endif
|
||||
|
||||
// If the fileName contains extra path information, make that part of the
|
||||
// directory name instead
|
||||
if (fileName != getSimpleFileName(fileName))
|
||||
{
|
||||
// See if we need to add a slash between the directory and file
|
||||
if (realDirName.empty())
|
||||
{
|
||||
realDirName = getFilePath(fileName);
|
||||
}
|
||||
else if (realDirName=="." || realDirName=="./" || realDirName==".\\")
|
||||
{
|
||||
realDirName = "./" + getFilePath(fileName);
|
||||
}
|
||||
else
|
||||
{
|
||||
char lastChar = dirName[dirName.size()-1];
|
||||
if ((lastChar == '/') || (lastChar == '\\'))
|
||||
realDirName = dirName + getFilePath(fileName);
|
||||
else
|
||||
realDirName = dirName + "/" + getFilePath(fileName);
|
||||
}
|
||||
|
||||
// Simplify the file name
|
||||
realFileName = getSimpleFileName(fileName);
|
||||
}
|
||||
|
||||
osg::notify(osg::DEBUG_INFO) << "findFileInDirectory() : looking for " << realFileName << " in " << realDirName << "...\n";
|
||||
|
||||
if (realDirName.empty())
|
||||
{
|
||||
dc = osgDB::getDirectoryContents(".");
|
||||
needFollowingBackslash = false;
|
||||
needDirectoryName = false;
|
||||
}
|
||||
else if (dirName=="." || dirName=="./" || dirName==".\\")
|
||||
else if (realDirName=="." || realDirName=="./" || realDirName==".\\")
|
||||
{
|
||||
dc = osgDB::getDirectoryContents(".");
|
||||
needFollowingBackslash = false;
|
||||
needDirectoryName = false;
|
||||
}
|
||||
else if (realDirName=="/")
|
||||
{
|
||||
dc = osgDB::getDirectoryContents("/");
|
||||
needFollowingBackslash = false;
|
||||
needDirectoryName = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
dc = osgDB::getDirectoryContents(dirName);
|
||||
char lastChar = dirName[dirName.size()-1];
|
||||
if (lastChar=='/') needFollowingBackslash = false;
|
||||
else if (lastChar=='\\') needFollowingBackslash = false;
|
||||
else needFollowingBackslash = true;
|
||||
needDirectoryName = true;
|
||||
// See if we're working in case insensitive mode, and that we're not
|
||||
// using Windows (the recursive search is not needed in these
|
||||
// cases)
|
||||
if ((caseSensitivity == CASE_INSENSITIVE) && (!win32))
|
||||
{
|
||||
// Split the last path element from the directory name
|
||||
std::string parentPath = getFilePath(realDirName);
|
||||
std::string lastElement = getSimpleFileName(realDirName);
|
||||
|
||||
// See if we're already at the top level of the filesystem
|
||||
if ((parentPath.empty()) && (!lastElement.empty()))
|
||||
{
|
||||
// Search for the first path element (ignoring case) in
|
||||
// the top-level directory
|
||||
realDirName = findFileInDirectory(lastElement, "/",
|
||||
CASE_INSENSITIVE);
|
||||
|
||||
dc = osgDB::getDirectoryContents(realDirName);
|
||||
needFollowingBackslash = true;
|
||||
needDirectoryName = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Recursively search for the last path element (ignoring case)
|
||||
// in the parent path
|
||||
realDirName = findFileInDirectory(lastElement, parentPath,
|
||||
CASE_INSENSITIVE);
|
||||
|
||||
dc = osgDB::getDirectoryContents(realDirName);
|
||||
char lastChar = realDirName[realDirName.size()-1];
|
||||
if (lastChar=='/') needFollowingBackslash = false;
|
||||
else if (lastChar=='\\') needFollowingBackslash = false;
|
||||
else needFollowingBackslash = true;
|
||||
needDirectoryName = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No need for recursive search if we're doing an exact comparison
|
||||
dc = osgDB::getDirectoryContents(realDirName);
|
||||
char lastChar = realDirName[realDirName.size()-1];
|
||||
if (lastChar=='/') needFollowingBackslash = false;
|
||||
else if (lastChar=='\\') needFollowingBackslash = false;
|
||||
else needFollowingBackslash = true;
|
||||
needDirectoryName = true;
|
||||
}
|
||||
}
|
||||
|
||||
for(osgDB::DirectoryContents::iterator itr=dc.begin();
|
||||
itr!=dc.end();
|
||||
++itr)
|
||||
{
|
||||
if ((caseSensitivity==CASE_INSENSITIVE && osgDB::equalCaseInsensitive(fileName,*itr)) ||
|
||||
(fileName==*itr))
|
||||
if ((caseSensitivity==CASE_INSENSITIVE && osgDB::equalCaseInsensitive(realFileName,*itr)) ||
|
||||
(realFileName==*itr))
|
||||
{
|
||||
if (!needDirectoryName) return *itr;
|
||||
else if (needFollowingBackslash) return dirName+'/'+*itr;
|
||||
else return dirName+*itr;
|
||||
else if (needFollowingBackslash) return realDirName+'/'+*itr;
|
||||
else return realDirName+*itr;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
|
||||
@@ -81,7 +81,7 @@ ADD_SUBDIRECTORY(dds)
|
||||
ADD_SUBDIRECTORY(tga)
|
||||
ADD_SUBDIRECTORY(hdr)
|
||||
ADD_SUBDIRECTORY(dot)
|
||||
ADD_SUBDIRECTORY(bsp)
|
||||
ADD_SUBDIRECTORY(vtf)
|
||||
|
||||
IF(JPEG_FOUND)
|
||||
ADD_SUBDIRECTORY(jpeg)
|
||||
@@ -180,6 +180,9 @@ ADD_SUBDIRECTORY(txp)
|
||||
ADD_SUBDIRECTORY(shp)
|
||||
ADD_SUBDIRECTORY(txf)
|
||||
|
||||
ADD_SUBDIRECTORY(bsp)
|
||||
ADD_SUBDIRECTORY(mdl)
|
||||
|
||||
IF(XINE_FOUND)
|
||||
ADD_SUBDIRECTORY(xine)
|
||||
ENDIF(XINE_FOUND)
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
SET(TARGET_SRC
|
||||
ReaderWriterBSP.cpp
|
||||
ReaderWriterVTF.cpp
|
||||
BITSET.cpp
|
||||
Q3BSPReader.cpp
|
||||
Q3BSPLoad.cpp
|
||||
VBSPData.cpp
|
||||
VBSPEntity.cpp
|
||||
VBSPGeometry.cpp
|
||||
VBSPReader.cpp
|
||||
)
|
||||
@@ -13,6 +14,8 @@ SET(TARGET_H
|
||||
BITSET.h
|
||||
Q3BSPReader.h
|
||||
Q3BSPLoad.h
|
||||
VBSPData.h
|
||||
VBSPEntity.h
|
||||
VBSPGeometry.h
|
||||
VBSPReader.h
|
||||
)
|
||||
|
||||
@@ -56,11 +56,11 @@ ReaderWriter::ReadResult ReaderWriterBSP::readNode(
|
||||
return ReadResult::FILE_NOT_FOUND;
|
||||
|
||||
// Open the file and read the magic number and version
|
||||
stream.open(file.c_str(), std::ios::binary);
|
||||
stream.open(fileName.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))
|
||||
|
||||
304
src/osgPlugins/bsp/VBSPData.cpp
Normal file
304
src/osgPlugins/bsp/VBSPData.cpp
Normal file
@@ -0,0 +1,304 @@
|
||||
|
||||
#include "VBSPData.h"
|
||||
#include <string.h>
|
||||
|
||||
|
||||
using namespace bsp;
|
||||
using namespace osg;
|
||||
|
||||
|
||||
VBSPData::VBSPData()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
VBSPData::~VBSPData()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void VBSPData::addEntity(std::string & newEntity)
|
||||
{
|
||||
entity_list.push_back(newEntity);
|
||||
}
|
||||
|
||||
|
||||
const int VBSPData::getNumEntities() const
|
||||
{
|
||||
return entity_list.size();
|
||||
}
|
||||
|
||||
|
||||
const std::string & VBSPData::getEntity(int index) const
|
||||
{
|
||||
return entity_list[index];
|
||||
}
|
||||
|
||||
|
||||
void VBSPData::addModel(Model & newModel)
|
||||
{
|
||||
model_list.push_back(newModel);
|
||||
}
|
||||
|
||||
|
||||
const int VBSPData::getNumModels() const
|
||||
{
|
||||
return model_list.size();
|
||||
}
|
||||
|
||||
|
||||
const Model & VBSPData::getModel(int index) const
|
||||
{
|
||||
return model_list[index];
|
||||
}
|
||||
|
||||
|
||||
void VBSPData::addPlane(bsp::Plane & newPlane)
|
||||
{
|
||||
plane_list.push_back(newPlane);
|
||||
}
|
||||
|
||||
|
||||
const int VBSPData::getNumPlanes() const
|
||||
{
|
||||
return plane_list.size();
|
||||
}
|
||||
|
||||
|
||||
const bsp::Plane & VBSPData::getPlane(int index) const
|
||||
{
|
||||
return plane_list[index];
|
||||
}
|
||||
|
||||
|
||||
void VBSPData::addVertex(osg::Vec3f & newVertex)
|
||||
{
|
||||
// Scale the vertex from inches up to meter scale
|
||||
vertex_list.push_back(newVertex * 0.0254);
|
||||
}
|
||||
|
||||
|
||||
const int VBSPData::getNumVertices() const
|
||||
{
|
||||
return vertex_list.size();
|
||||
}
|
||||
|
||||
|
||||
const osg::Vec3f & VBSPData::getVertex(int index) const
|
||||
{
|
||||
return vertex_list[index];
|
||||
}
|
||||
|
||||
|
||||
void VBSPData::addEdge(Edge & newEdge)
|
||||
{
|
||||
edge_list.push_back(newEdge);
|
||||
}
|
||||
|
||||
|
||||
const int VBSPData::getNumEdges() const
|
||||
{
|
||||
return edge_list.size();
|
||||
}
|
||||
|
||||
|
||||
const Edge & VBSPData::getEdge(int index) const
|
||||
{
|
||||
return edge_list[index];
|
||||
}
|
||||
|
||||
|
||||
void VBSPData::addSurfaceEdge(int & newSurfEdge)
|
||||
{
|
||||
surface_edge_list.push_back(newSurfEdge);
|
||||
}
|
||||
|
||||
|
||||
const int VBSPData::getNumSurfaceEdges() const
|
||||
{
|
||||
return surface_edge_list.size();
|
||||
}
|
||||
|
||||
|
||||
const int VBSPData::getSurfaceEdge(int index) const
|
||||
{
|
||||
return surface_edge_list[index];
|
||||
}
|
||||
|
||||
|
||||
void VBSPData::addFace(Face & newFace)
|
||||
{
|
||||
face_list.push_back(newFace);
|
||||
}
|
||||
|
||||
|
||||
const int VBSPData::getNumFaces() const
|
||||
{
|
||||
return face_list.size();
|
||||
}
|
||||
|
||||
|
||||
const Face & VBSPData::getFace(int index) const
|
||||
{
|
||||
return face_list[index];
|
||||
}
|
||||
|
||||
|
||||
void VBSPData::addTexInfo(TexInfo & newTexInfo)
|
||||
{
|
||||
texinfo_list.push_back(newTexInfo);
|
||||
}
|
||||
|
||||
|
||||
const int VBSPData::getNumTexInfos() const
|
||||
{
|
||||
return texinfo_list.size();
|
||||
}
|
||||
|
||||
|
||||
const TexInfo & VBSPData::getTexInfo(int index) const
|
||||
{
|
||||
return texinfo_list[index];
|
||||
}
|
||||
|
||||
|
||||
void VBSPData::addTexData(TexData & newTexData)
|
||||
{
|
||||
texdata_list.push_back(newTexData);
|
||||
}
|
||||
|
||||
|
||||
const int VBSPData::getNumTexDatas() const
|
||||
{
|
||||
return texdata_list.size();
|
||||
}
|
||||
|
||||
|
||||
const TexData & VBSPData::getTexData(int index) const
|
||||
{
|
||||
return texdata_list[index];
|
||||
}
|
||||
|
||||
|
||||
void VBSPData::addTexDataString(std::string & newTexDataString)
|
||||
{
|
||||
texdata_string_list.push_back(newTexDataString);
|
||||
}
|
||||
|
||||
|
||||
const int VBSPData::getNumTexDataStrings() const
|
||||
{
|
||||
return texdata_string_list.size();
|
||||
}
|
||||
|
||||
|
||||
const std::string & VBSPData::getTexDataString(int index) const
|
||||
{
|
||||
return texdata_string_list[index];
|
||||
}
|
||||
|
||||
|
||||
void VBSPData::addDispInfo(DisplaceInfo & newDispInfo)
|
||||
{
|
||||
dispinfo_list.push_back(newDispInfo);
|
||||
}
|
||||
|
||||
|
||||
const int VBSPData::getNumDispInfos() const
|
||||
{
|
||||
return dispinfo_list.size();
|
||||
}
|
||||
|
||||
|
||||
const DisplaceInfo & VBSPData::getDispInfo(int index) const
|
||||
{
|
||||
return dispinfo_list[index];
|
||||
}
|
||||
|
||||
|
||||
void VBSPData::addDispVertex(DisplacedVertex & newDispVertex)
|
||||
{
|
||||
displaced_vertex_list.push_back(newDispVertex);
|
||||
}
|
||||
|
||||
|
||||
const int VBSPData::getNumDispVertices() const
|
||||
{
|
||||
return displaced_vertex_list.size();
|
||||
}
|
||||
|
||||
|
||||
const DisplacedVertex & VBSPData::getDispVertex(int index) const
|
||||
{
|
||||
return displaced_vertex_list[index];
|
||||
}
|
||||
|
||||
|
||||
void VBSPData::addStaticPropModel(std::string & newModel)
|
||||
{
|
||||
static_prop_model_list.push_back(newModel);
|
||||
}
|
||||
|
||||
|
||||
const int VBSPData::getNumStaticPropModels() const
|
||||
{
|
||||
return static_prop_model_list.size();
|
||||
}
|
||||
|
||||
|
||||
const std::string & VBSPData::getStaticPropModel(int index) const
|
||||
{
|
||||
return static_prop_model_list[index];
|
||||
}
|
||||
|
||||
|
||||
void VBSPData::addStaticProp(StaticPropV4 & newProp)
|
||||
{
|
||||
StaticProp newPropV5;
|
||||
|
||||
// Create a version 5 static prop and copy the data from the given
|
||||
// version 4 prop into it
|
||||
memcpy(&newPropV5, &newProp, sizeof(StaticPropV4));
|
||||
newPropV5.forced_fade_scale = 1.0;
|
||||
|
||||
// Add the new prop to the list
|
||||
static_prop_list.push_back(newPropV5);
|
||||
}
|
||||
|
||||
|
||||
void VBSPData::addStaticProp(StaticProp & newProp)
|
||||
{
|
||||
static_prop_list.push_back(newProp);
|
||||
}
|
||||
|
||||
|
||||
const int VBSPData::getNumStaticProps() const
|
||||
{
|
||||
return static_prop_list.size();
|
||||
}
|
||||
|
||||
|
||||
const StaticProp & VBSPData::getStaticProp(int index) const
|
||||
{
|
||||
return static_prop_list[index];
|
||||
}
|
||||
|
||||
|
||||
void VBSPData::addStateSet(StateSet * stateSet)
|
||||
{
|
||||
state_set_list.push_back(stateSet);
|
||||
}
|
||||
|
||||
|
||||
const int VBSPData::getNumStateSets() const
|
||||
{
|
||||
return state_set_list.size();
|
||||
}
|
||||
|
||||
|
||||
StateSet * VBSPData::getStateSet(int index) const
|
||||
{
|
||||
return state_set_list[index].get();
|
||||
}
|
||||
|
||||
|
||||
284
src/osgPlugins/bsp/VBSPData.h
Normal file
284
src/osgPlugins/bsp/VBSPData.h
Normal file
@@ -0,0 +1,284 @@
|
||||
#ifndef __VBSP_DATA_H_
|
||||
#define __VBSP_DATA_H_
|
||||
|
||||
|
||||
#include <osg/Vec3f>
|
||||
#include <osg/StateSet>
|
||||
#include <osg/Referenced>
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace bsp
|
||||
{
|
||||
|
||||
|
||||
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 Model
|
||||
{
|
||||
osg::Vec3f bound_min;
|
||||
osg::Vec3f bound_max;
|
||||
osg::Vec3f model_origin;
|
||||
int head_node;
|
||||
int first_face;
|
||||
int num_faces;
|
||||
};
|
||||
|
||||
|
||||
struct StaticPropV4
|
||||
{
|
||||
osg::Vec3f prop_origin;
|
||||
osg::Vec3f prop_angles;
|
||||
unsigned short prop_type;
|
||||
unsigned short first_leaf;
|
||||
unsigned short leaf_count;
|
||||
unsigned char prop_solid;
|
||||
unsigned char prop_flags;
|
||||
unsigned int prop_skin;
|
||||
float min_fade_dist;
|
||||
float max_fade_dist;
|
||||
|
||||
osg::Vec3f lighting_origin;
|
||||
};
|
||||
|
||||
|
||||
struct StaticProp
|
||||
{
|
||||
osg::Vec3f prop_origin;
|
||||
osg::Vec3f prop_angles;
|
||||
unsigned short prop_type;
|
||||
unsigned short first_leaf;
|
||||
unsigned short leaf_count;
|
||||
unsigned char prop_solid;
|
||||
unsigned char prop_flags;
|
||||
unsigned int prop_skin;
|
||||
float min_fade_dist;
|
||||
float max_fade_dist;
|
||||
|
||||
osg::Vec3f lighting_origin;
|
||||
float forced_fade_scale;
|
||||
};
|
||||
|
||||
|
||||
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 int allowed_verts[10];
|
||||
};
|
||||
|
||||
|
||||
struct DisplacedVertex
|
||||
{
|
||||
osg::Vec3f displace_vec;
|
||||
float displace_dist;
|
||||
float alpha_blend;
|
||||
};
|
||||
|
||||
|
||||
class VBSPData
|
||||
{
|
||||
protected:
|
||||
|
||||
typedef std::vector<std::string> EntityList;
|
||||
EntityList entity_list;
|
||||
|
||||
typedef std::vector<Model> ModelList;
|
||||
ModelList model_list;
|
||||
|
||||
typedef std::vector<Plane> PlaneList;
|
||||
PlaneList plane_list;
|
||||
|
||||
typedef std::vector<osg::Vec3f> VertexList;
|
||||
VertexList vertex_list;
|
||||
|
||||
typedef std::vector<Edge> EdgeList;
|
||||
EdgeList edge_list;
|
||||
|
||||
typedef std::vector<int> SurfEdgeList;
|
||||
SurfEdgeList surface_edge_list;
|
||||
|
||||
typedef std::vector<Face> FaceList;
|
||||
FaceList face_list;
|
||||
|
||||
typedef std::vector<TexInfo> TexInfoList;
|
||||
TexInfoList texinfo_list;
|
||||
|
||||
typedef std::vector<TexData> TexDataList;
|
||||
TexDataList texdata_list;
|
||||
|
||||
typedef std::vector<std::string> TexDataStringList;
|
||||
TexDataStringList texdata_string_list;
|
||||
|
||||
typedef std::vector<DisplaceInfo> DisplaceInfoList;
|
||||
DisplaceInfoList dispinfo_list;
|
||||
|
||||
typedef std::vector<DisplacedVertex> DisplacedVertexList;
|
||||
DisplacedVertexList displaced_vertex_list;
|
||||
|
||||
typedef std::vector<std::string> StaticPropModelList;
|
||||
StaticPropModelList static_prop_model_list;
|
||||
|
||||
typedef std::vector<StaticProp> StaticPropList;
|
||||
StaticPropList static_prop_list;
|
||||
|
||||
typedef std::vector< osg::ref_ptr<osg::StateSet> > StateSetList;
|
||||
StateSetList state_set_list;
|
||||
|
||||
public:
|
||||
|
||||
VBSPData();
|
||||
virtual ~VBSPData();
|
||||
|
||||
void addEntity(std::string & newEntity);
|
||||
const int getNumEntities() const;
|
||||
const std::string & getEntity(int index) const;
|
||||
|
||||
void addModel(Model & newModel);
|
||||
const int getNumModels() const;
|
||||
const Model & getModel(int index) const;
|
||||
|
||||
void addPlane(Plane & newPlane);
|
||||
const int getNumPlanes() const;
|
||||
const Plane & getPlane(int index) const;
|
||||
|
||||
void addVertex(osg::Vec3f & newVertex);
|
||||
const int getNumVertices() const;
|
||||
const osg::Vec3f & getVertex(int index) const;
|
||||
|
||||
void addEdge(Edge & newEdge);
|
||||
const int getNumEdges() const;
|
||||
const Edge & getEdge(int index) const;
|
||||
|
||||
void addSurfaceEdge(int & newSurfEdge);
|
||||
const int getNumSurfaceEdges() const;
|
||||
const int getSurfaceEdge(int index) const;
|
||||
|
||||
void addFace(Face & newFace);
|
||||
const int getNumFaces() const;
|
||||
const Face & getFace(int index) const;
|
||||
|
||||
void addTexInfo(TexInfo & newTexInfo);
|
||||
const int getNumTexInfos() const;
|
||||
const TexInfo & getTexInfo(int index) const;
|
||||
|
||||
void addTexData(TexData & newTexData);
|
||||
const int getNumTexDatas() const;
|
||||
const TexData & getTexData(int index) const;
|
||||
|
||||
void addTexDataString(std::string & newTexDataString);
|
||||
const int getNumTexDataStrings() const;
|
||||
const std::string & getTexDataString(int index) const;
|
||||
|
||||
void addDispInfo(DisplaceInfo & newDispInfo);
|
||||
const int getNumDispInfos() const;
|
||||
const DisplaceInfo & getDispInfo(int index) const;
|
||||
|
||||
void addDispVertex(DisplacedVertex & newDispVert);
|
||||
const int getNumDispVertices() const;
|
||||
const DisplacedVertex & getDispVertex(int index) const;
|
||||
|
||||
void addStaticPropModel(std::string & newModel);
|
||||
const int getNumStaticPropModels() const;
|
||||
const std::string & getStaticPropModel(int index) const;
|
||||
|
||||
void addStaticProp(StaticPropV4 & newProp);
|
||||
void addStaticProp(StaticProp & newProp);
|
||||
const int getNumStaticProps() const;
|
||||
const StaticProp & getStaticProp(int index) const;
|
||||
|
||||
void addStateSet(osg::StateSet * stateSet);
|
||||
const int getNumStateSets() const;
|
||||
osg::StateSet * getStateSet(int index) const;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
597
src/osgPlugins/bsp/VBSPEntity.cpp
Normal file
597
src/osgPlugins/bsp/VBSPEntity.cpp
Normal file
@@ -0,0 +1,597 @@
|
||||
|
||||
#include "VBSPEntity.h"
|
||||
#include "VBSPGeometry.h"
|
||||
|
||||
#include <osg/MatrixTransform>
|
||||
#include <osgDB/ReadFile>
|
||||
#include <osgDB/FileUtils>
|
||||
#include <osgDB/FileNameUtils>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
using namespace bsp;
|
||||
using namespace osg;
|
||||
using namespace osgDB;
|
||||
|
||||
|
||||
// strcasecmp for MSVC
|
||||
#ifdef _MSC_VER
|
||||
#define strcasecmp _stricmp
|
||||
#endif
|
||||
|
||||
|
||||
VBSPEntity::VBSPEntity(std::string & entityText, VBSPData * bspData)
|
||||
{
|
||||
// Save a handle to the bsp data, as we'll need this to construct the
|
||||
// entity
|
||||
bsp_data = bspData;
|
||||
|
||||
// Assume we're not visible at first
|
||||
entity_visible = false;
|
||||
|
||||
// Assume no transform
|
||||
entity_transformed = false;
|
||||
|
||||
// No model (external or internal) yet
|
||||
entity_model_index = -1;
|
||||
entity_model.clear();
|
||||
|
||||
// Don't know the class yet
|
||||
entity_class = ENTITY_OTHER;
|
||||
|
||||
// Parse the entity's text to gather parameters
|
||||
parseParameters(entityText);
|
||||
}
|
||||
|
||||
|
||||
VBSPEntity::~VBSPEntity()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void VBSPEntity::processWorldSpawn()
|
||||
{
|
||||
// World spawn is definitely visible
|
||||
entity_visible = true;
|
||||
|
||||
// World spawn is always centered at the origin, so there's no need for
|
||||
// a transform
|
||||
entity_transformed = false;
|
||||
|
||||
// The world spawn's internal model index is always zero
|
||||
entity_model_index = 0;
|
||||
}
|
||||
|
||||
|
||||
void VBSPEntity::processEnv()
|
||||
{
|
||||
// We don't support these entities yet, so leave them invisible
|
||||
}
|
||||
|
||||
|
||||
void VBSPEntity::processFuncBrush()
|
||||
{
|
||||
// These entities are usually transformed
|
||||
entity_transformed = true;
|
||||
|
||||
// Get the internal model index for this entity
|
||||
EntityParameters::iterator param = entity_parameters.find("model");
|
||||
if (param != entity_parameters.end())
|
||||
{
|
||||
// Get the model number
|
||||
std::string value = (*param).second;
|
||||
|
||||
// Skip the leading asterisk (internal models are denoted with a
|
||||
// leading asterisk), and then parse the model number
|
||||
if (value[0] == '*')
|
||||
{
|
||||
value = value.substr(1, std::string::npos);
|
||||
entity_model_index = atoi(value.c_str());
|
||||
|
||||
// Make the entity visible
|
||||
entity_visible = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This shouldn't happen (brush entities don't reference
|
||||
// external models). Leave the entity invisible in this case
|
||||
entity_visible = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We can't locate the model for this entity, so leave it invisible
|
||||
entity_visible = false;
|
||||
}
|
||||
|
||||
// Get the origin and angles for this entity
|
||||
param = entity_parameters.find("origin");
|
||||
if (param != entity_parameters.end())
|
||||
{
|
||||
// Get the origin parameter's value
|
||||
std::string value = (*param).second;
|
||||
|
||||
// Parse the value into a vector
|
||||
entity_origin = getVector(value);
|
||||
}
|
||||
param = entity_parameters.find("angles");
|
||||
if (param != entity_parameters.end())
|
||||
{
|
||||
// Get the origin parameter's value
|
||||
std::string value = (*param).second;
|
||||
|
||||
// Parse the value into a vector
|
||||
entity_angles = getVector(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void VBSPEntity::processProp()
|
||||
{
|
||||
// These entities are visible
|
||||
entity_visible = true;
|
||||
|
||||
// These entities are usually transformed
|
||||
entity_transformed = true;
|
||||
|
||||
// Get the model we need to load for this entity
|
||||
EntityParameters::iterator param = entity_parameters.find("model");
|
||||
if (param != entity_parameters.end())
|
||||
{
|
||||
// Get the model parameter's value
|
||||
entity_model = (*param).second;
|
||||
}
|
||||
|
||||
// Get the origin and angles for this entity
|
||||
param = entity_parameters.find("origin");
|
||||
if (param != entity_parameters.end())
|
||||
{
|
||||
// Get the origin parameter's value
|
||||
std::string value = (*param).second;
|
||||
|
||||
// Parse the value into a vector
|
||||
entity_origin = getVector(value);
|
||||
}
|
||||
param = entity_parameters.find("angles");
|
||||
if (param != entity_parameters.end())
|
||||
{
|
||||
// Get the origin parameter's value
|
||||
std::string value = (*param).second;
|
||||
|
||||
// Parse the value into a vector
|
||||
entity_angles = getVector(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void VBSPEntity::processInfoDecal()
|
||||
{
|
||||
// We don't support these entities yet, so leave them invisible
|
||||
}
|
||||
|
||||
|
||||
void VBSPEntity::processItem()
|
||||
{
|
||||
// We don't support these entities yet, so leave them invisible
|
||||
}
|
||||
|
||||
|
||||
Vec3f VBSPEntity::getVector(std::string str)
|
||||
{
|
||||
double x, y, z;
|
||||
|
||||
// Look for the first non-whitespace
|
||||
int start = str.find_first_not_of(" \t\r\n", 0);
|
||||
|
||||
// Look for the first whitespace after this
|
||||
int end = str.find_first_of(" \t\r\n", start);
|
||||
|
||||
if ((end > start) && (start != std::string::npos))
|
||||
x = atof(str.substr(start, end-start).c_str());
|
||||
else
|
||||
return Vec3f();
|
||||
|
||||
// Look for the next non-whitespace
|
||||
start = str.find_first_not_of(" \t\r\n", end+1);
|
||||
|
||||
// Look for the first whitespace after this
|
||||
end = str.find_first_of(" \t\r\n", start);
|
||||
|
||||
if ((end > start) && (start != std::string::npos))
|
||||
y = atof(str.substr(start, end-start).c_str());
|
||||
else
|
||||
return Vec3f();
|
||||
|
||||
// Look for the next non-whitespace
|
||||
start = str.find_first_not_of(" \t\r\n", end+1);
|
||||
|
||||
// Look for the first whitespace after this
|
||||
end = str.find_first_of(" \t\r\n", start);
|
||||
if (end == std::string::npos)
|
||||
end = str.length();
|
||||
|
||||
if ((end > start) && (start != std::string::npos))
|
||||
z = atof(str.substr(start, end-start).c_str());
|
||||
else
|
||||
return Vec3f();
|
||||
|
||||
// If we get this far, return the vector that we parsed
|
||||
return Vec3f(x, y, z);
|
||||
}
|
||||
|
||||
|
||||
std::string VBSPEntity::getToken(std::string str, size_t & index)
|
||||
{
|
||||
size_t start, end;
|
||||
std::string token;
|
||||
|
||||
// Look for the first quotation mark
|
||||
start = str.find_first_of("\"", index);
|
||||
if (start != std::string::npos)
|
||||
{
|
||||
// From there, look for the next occurrence of a delimiter
|
||||
start++;
|
||||
end = str.find_first_of("\"", start);
|
||||
if (end != std::string::npos)
|
||||
{
|
||||
// Found a delimiter, so grab the string in between
|
||||
token = str.substr(start, end-start);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ran off the end of the string, so just grab everything from
|
||||
// the first good character
|
||||
token = str.substr(start);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No token to be found
|
||||
token.clear();
|
||||
}
|
||||
|
||||
// Update the index (in case we want to keep looking for tokens in this
|
||||
// string)
|
||||
if (end != std::string::npos)
|
||||
index = end+1;
|
||||
else
|
||||
index = std::string::npos;
|
||||
|
||||
// Return the token
|
||||
return token;
|
||||
}
|
||||
|
||||
|
||||
void VBSPEntity::parseParameters(std::string & entityText)
|
||||
{
|
||||
// Create a string stream on the entity text
|
||||
std::istringstream str(entityText, std::istringstream::in);
|
||||
|
||||
// Iterate over the parameters
|
||||
while (!str.eof())
|
||||
{
|
||||
// Get the next line of text
|
||||
std::string line;
|
||||
std::getline(str, line);
|
||||
|
||||
// Look for the first quotation mark on the line
|
||||
size_t start = 0;
|
||||
std::string token = getToken(line, start);
|
||||
|
||||
// If we have a valid token it will be the parameter name (the key),
|
||||
// look for a second token, which will be the parameter's value
|
||||
while (!token.empty())
|
||||
{
|
||||
// Save the token as the key
|
||||
std::string key = token;
|
||||
|
||||
// Get the next token
|
||||
start++;
|
||||
token = getToken(line, start);
|
||||
|
||||
// See if the token is valid
|
||||
if (!token.empty())
|
||||
{
|
||||
// This token is the value, create an entity parameter from
|
||||
// these two strings and add it to our parameters map
|
||||
EntityParameter param(key, token);
|
||||
entity_parameters.insert(param);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now that we have all of the parameters, figure out what kind of entity
|
||||
// this is
|
||||
EntityParameters::iterator param = entity_parameters.find("classname");
|
||||
|
||||
// See if we found the class
|
||||
if (param == entity_parameters.end())
|
||||
{
|
||||
// We need the class to be able to do anything with this entity
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the class name and process the entity appropriately
|
||||
std::string className = (*param).second;
|
||||
if (className.compare("worldspawn") == 0)
|
||||
{
|
||||
// This is the entity that represents the main geometry of the map
|
||||
// (the terrain and much of the static geometry)
|
||||
entity_class = ENTITY_WORLDSPAWN;
|
||||
processWorldSpawn();
|
||||
}
|
||||
else if (className.compare(0, 3, "env") == 0)
|
||||
{
|
||||
// This is an environmental effect (such as a fire or dust cloud)
|
||||
entity_class = ENTITY_ENV;
|
||||
processEnv();
|
||||
}
|
||||
else if ((className.compare("func_brush") == 0) ||
|
||||
(className.compare("func_illusionary") == 0) ||
|
||||
(className.compare("func_wall_toggle") == 0) ||
|
||||
(className.compare("func_breakable") == 0))
|
||||
{
|
||||
// This is secondary map geometry, created along with the main
|
||||
// map geometry (not an external model)
|
||||
entity_class = ENTITY_FUNC_BRUSH;
|
||||
processFuncBrush();
|
||||
}
|
||||
else if (className.compare(0, 4, "prop") == 0)
|
||||
{
|
||||
// This is a "prop", an external model placed somewhere in the
|
||||
// scene
|
||||
entity_class = ENTITY_PROP;
|
||||
processProp();
|
||||
}
|
||||
else if (className.compare("infodecal") == 0)
|
||||
{
|
||||
// This is a decal, which applies a texture to some surface in the
|
||||
// scene
|
||||
entity_class = ENTITY_INFO_DECAL;
|
||||
processInfoDecal();
|
||||
}
|
||||
else if (className.compare(0, 4, "item") == 0)
|
||||
{
|
||||
// This is an "item". Like a prop, these are external models
|
||||
// placed in the scene, but the specific model is determined
|
||||
// directly by the entity's class. In HL2, these entities are
|
||||
// useable by the player (ammunition and health packs are examples)
|
||||
entity_class = ENTITY_ITEM;
|
||||
processItem();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ref_ptr<Group> VBSPEntity::createBrushGeometry()
|
||||
{
|
||||
int i;
|
||||
int numGeoms;
|
||||
VBSPGeometry ** vbspGeomList;
|
||||
Model currentModel;
|
||||
Face currentFace;
|
||||
TexInfo currentTexInfo;
|
||||
TexData currentTexData;
|
||||
const char * texName;
|
||||
char currentTexName[256];
|
||||
int currentGeomIndex;
|
||||
VBSPGeometry * currentGeom;
|
||||
ref_ptr<Group> entityGroup;
|
||||
ref_ptr<Group> geomGroup;
|
||||
|
||||
// Create a list of VBSPGeometry objects for each texdata entry in the
|
||||
// scene. These objects will hold the necessary geometry data until we
|
||||
// convert them back into OSG geometry objects. We potentially will need
|
||||
// one for each state set in the map
|
||||
numGeoms = bsp_data->getNumStateSets();
|
||||
vbspGeomList = new VBSPGeometry *[numGeoms];
|
||||
|
||||
// Initialize the list to all NULL for now. We'll create the geometry
|
||||
// objects as we need them
|
||||
memset(vbspGeomList, 0, sizeof(VBSPGeometry *) * numGeoms);
|
||||
|
||||
// Get this entity's internal model from the bsp data
|
||||
currentModel = bsp_data->getModel(entity_model_index);
|
||||
|
||||
// Iterate over the face list and assign faces to the appropriate geometry
|
||||
// objects
|
||||
for (i = 0; i < currentModel.num_faces; i++)
|
||||
{
|
||||
// Get the current face
|
||||
currentFace = bsp_data->getFace(currentModel.first_face + i);
|
||||
|
||||
// Get the texdata used by this face
|
||||
currentTexInfo = bsp_data->getTexInfo(currentFace.texinfo_index);
|
||||
currentTexData = bsp_data->getTexData(currentTexInfo.texdata_index);
|
||||
|
||||
// Get the texture name
|
||||
texName = bsp_data->
|
||||
getTexDataString(currentTexData.name_string_table_id).c_str();
|
||||
strcpy(currentTexName, texName);
|
||||
|
||||
// See if this is a non-drawable surface
|
||||
if ((strcasecmp(currentTexName, "tools/toolsareaportal") != 0) &&
|
||||
(strcasecmp(currentTexName, "tools/toolsblocklos") != 0) &&
|
||||
(strcasecmp(currentTexName, "tools/toolsblockbullets") != 0) &&
|
||||
(strcasecmp(currentTexName, "tools/toolsblocklight") != 0) &&
|
||||
(strcasecmp(currentTexName, "tools/toolsclip") != 0) &&
|
||||
(strcasecmp(currentTexName, "tools/toolscontrolclip") != 0) &&
|
||||
(strcasecmp(currentTexName, "tools/toolsdotted") != 0) &&
|
||||
(strcasecmp(currentTexName, "tools/toolshint") != 0) &&
|
||||
(strcasecmp(currentTexName, "tools/toolsinvisible") != 0) &&
|
||||
(strcasecmp(currentTexName, "tools/toolsinvisibleladder") != 0) &&
|
||||
(strcasecmp(currentTexName, "tools/toolsnodraw") != 0) &&
|
||||
(strcasecmp(currentTexName, "tools/toolsnpcclip") != 0) &&
|
||||
(strcasecmp(currentTexName, "tools/toolsoccluder") != 0) &&
|
||||
(strcasecmp(currentTexName, "tools/toolsorigin") != 0) &&
|
||||
(strcasecmp(currentTexName, "tools/toolsskip") != 0) &&
|
||||
(strcasecmp(currentTexName, "tools/toolsskybox") != 0) &&
|
||||
(strcasecmp(currentTexName, "tools/toolsskyfog") != 0) &&
|
||||
(strcasecmp(currentTexName, "tools/toolstrigger") != 0))
|
||||
{
|
||||
// Get or create the corresponding VBSPGeometry object from the
|
||||
// list
|
||||
currentGeomIndex = currentTexInfo.texdata_index;
|
||||
currentGeom = vbspGeomList[currentGeomIndex];
|
||||
if (currentGeom == NULL)
|
||||
{
|
||||
// Create the geometry object
|
||||
vbspGeomList[currentGeomIndex] = new VBSPGeometry(bsp_data);
|
||||
currentGeom = vbspGeomList[currentGeomIndex];
|
||||
}
|
||||
|
||||
// Add the face to the appropriate VBSPGeometry object
|
||||
currentGeom->addFace(currentModel.first_face + i);
|
||||
}
|
||||
}
|
||||
|
||||
// Create a top-level group to hold the geometry objects
|
||||
if (entity_transformed)
|
||||
{
|
||||
// Create a matrix transform
|
||||
MatrixTransform * entityXform = new MatrixTransform();
|
||||
|
||||
// Set it up with the entity's transform information (scale the
|
||||
// position from inches to meters)
|
||||
Matrixf transMat, rotMat;
|
||||
Quat roll, yaw, pitch;
|
||||
transMat.makeTranslate(entity_origin * 0.0254);
|
||||
pitch.makeRotate(osg::DegreesToRadians(entity_angles.x()),
|
||||
Vec3f(0.0, 1.0, 0.0));
|
||||
yaw.makeRotate(osg::DegreesToRadians(entity_angles.y()),
|
||||
Vec3f(0.0, 0.0, 1.0));
|
||||
roll.makeRotate(osg::DegreesToRadians(entity_angles.z()),
|
||||
Vec3f(1.0, 0.0, 0.0));
|
||||
rotMat.makeRotate(roll * pitch * yaw);
|
||||
|
||||
// Set the transform matrix
|
||||
entityXform->setMatrix(rotMat * transMat);
|
||||
|
||||
// Use the transform node as the main entity group
|
||||
entityGroup = entityXform;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create a group to represent the entire entity
|
||||
entityGroup = new Group();
|
||||
}
|
||||
|
||||
// Iterate over the geometry array and convert each geometry object
|
||||
// into OSG geometry
|
||||
for (i = 0; i < numGeoms; i++)
|
||||
{
|
||||
// Get the next geometry object (if any)
|
||||
currentGeom = vbspGeomList[i];
|
||||
if (currentGeom != NULL)
|
||||
{
|
||||
// Convert the BSP geometry to OSG geometry
|
||||
geomGroup = currentGeom->createGeometry();
|
||||
|
||||
// Make sure the geometry converted properly
|
||||
if (geomGroup.valid())
|
||||
{
|
||||
// Set this group's state set
|
||||
geomGroup->setStateSet(bsp_data->getStateSet(i));
|
||||
|
||||
// Add the geometry group to the entity group
|
||||
entityGroup->addChild(geomGroup.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the group we created
|
||||
return entityGroup;
|
||||
}
|
||||
|
||||
|
||||
ref_ptr<Group> VBSPEntity::createModelGeometry()
|
||||
{
|
||||
std::string modelFile;
|
||||
ref_ptr<Node> modelNode;
|
||||
ref_ptr<Group> entityGroup;
|
||||
|
||||
// Try to load the model
|
||||
modelNode = osgDB::readNodeFile(entity_model);
|
||||
if (modelNode.valid())
|
||||
{
|
||||
// Create a group and add the model to it
|
||||
if (entity_transformed)
|
||||
{
|
||||
// Create a matrix transform
|
||||
MatrixTransform * entityXform = new MatrixTransform();
|
||||
|
||||
// Set it up with the entity's transform information (scale
|
||||
// the position from inches to meters)
|
||||
Matrixf transMat, rotMat;
|
||||
Quat roll, yaw, pitch;
|
||||
transMat.makeTranslate(entity_origin * 0.0254);
|
||||
pitch.makeRotate(osg::DegreesToRadians(entity_angles.x()),
|
||||
Vec3f(0.0, 1.0, 0.0));
|
||||
yaw.makeRotate(osg::DegreesToRadians(entity_angles.y()),
|
||||
Vec3f(0.0, 0.0, 1.0));
|
||||
roll.makeRotate(osg::DegreesToRadians(entity_angles.z()),
|
||||
Vec3f(1.0, 0.0, 0.0));
|
||||
rotMat.makeRotate(roll * pitch * yaw);
|
||||
|
||||
// Set the transform matrix
|
||||
entityXform->setMatrix(rotMat * transMat);
|
||||
|
||||
// Use the transform node as the main entity group
|
||||
entityGroup = entityXform;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create a group to represent the entire entity
|
||||
entityGroup = new Group();
|
||||
}
|
||||
|
||||
// Add the model node to the group
|
||||
entityGroup->addChild(modelNode.get());
|
||||
}
|
||||
else
|
||||
{
|
||||
notify(WARN) << "Couldn't find prop \"" << entity_model << "\".";
|
||||
notify(WARN) << std::endl;
|
||||
|
||||
// Leave the group empty (no model to show)
|
||||
entityGroup = NULL;
|
||||
}
|
||||
|
||||
return entityGroup;
|
||||
}
|
||||
|
||||
|
||||
EntityClass VBSPEntity::getClass()
|
||||
{
|
||||
return entity_class;
|
||||
}
|
||||
|
||||
|
||||
bool VBSPEntity::isVisible()
|
||||
{
|
||||
return entity_visible;
|
||||
}
|
||||
|
||||
|
||||
ref_ptr<Group> VBSPEntity::createGeometry()
|
||||
{
|
||||
// If we're not a visible entity, we have no geometry
|
||||
if (!entity_visible)
|
||||
return NULL;
|
||||
|
||||
// Create the geometry for the entity based on the class
|
||||
if ((entity_class == ENTITY_WORLDSPAWN) ||
|
||||
(entity_class == ENTITY_FUNC_BRUSH))
|
||||
{
|
||||
return createBrushGeometry();
|
||||
}
|
||||
else if (entity_class == ENTITY_PROP)
|
||||
{
|
||||
return createModelGeometry();
|
||||
}
|
||||
|
||||
// If we get here, we don't handle this kind of entity (yet)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
77
src/osgPlugins/bsp/VBSPEntity.h
Normal file
77
src/osgPlugins/bsp/VBSPEntity.h
Normal file
@@ -0,0 +1,77 @@
|
||||
|
||||
#ifndef __VBSP_ENTITY_H_
|
||||
#define __VBSP_ENTITY_H_
|
||||
|
||||
|
||||
#include <osg/Group>
|
||||
#include <osg/Vec3f>
|
||||
|
||||
#include "VBSPData.h"
|
||||
|
||||
|
||||
namespace bsp
|
||||
{
|
||||
|
||||
|
||||
enum EntityClass
|
||||
{
|
||||
ENTITY_WORLDSPAWN,
|
||||
ENTITY_ENV,
|
||||
ENTITY_FUNC_BRUSH,
|
||||
ENTITY_PROP,
|
||||
ENTITY_INFO_DECAL,
|
||||
ENTITY_ITEM,
|
||||
ENTITY_OTHER
|
||||
};
|
||||
|
||||
|
||||
class VBSPEntity
|
||||
{
|
||||
protected:
|
||||
|
||||
VBSPData * bsp_data;
|
||||
|
||||
EntityClass entity_class;
|
||||
|
||||
typedef std::pair<std::string, std::string> EntityParameter;
|
||||
typedef std::map<std::string, std::string> EntityParameters;
|
||||
EntityParameters entity_parameters;
|
||||
|
||||
bool entity_visible;
|
||||
bool entity_transformed;
|
||||
int entity_model_index;
|
||||
std::string entity_model;
|
||||
osg::Vec3f entity_origin;
|
||||
osg::Vec3f entity_angles;
|
||||
osg::ref_ptr<osg::Group> entity_geometry;
|
||||
|
||||
void processWorldSpawn();
|
||||
void processEnv();
|
||||
void processFuncBrush();
|
||||
void processProp();
|
||||
void processInfoDecal();
|
||||
void processItem();
|
||||
|
||||
osg::Vec3f getVector(std::string str);
|
||||
std::string getToken(std::string str, size_t & index);
|
||||
void parseParameters(std::string & entityText);
|
||||
|
||||
osg::ref_ptr<osg::Group> createBrushGeometry();
|
||||
osg::ref_ptr<osg::Group> createModelGeometry();
|
||||
|
||||
public:
|
||||
|
||||
VBSPEntity(std::string & entityText, VBSPData * bspData);
|
||||
~VBSPEntity();
|
||||
|
||||
EntityClass getClass();
|
||||
bool isVisible();
|
||||
|
||||
osg::ref_ptr<osg::Group> createGeometry();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <osg/Geode>
|
||||
|
||||
#include "VBSPGeometry.h"
|
||||
|
||||
|
||||
using namespace osg;
|
||||
using namespace osgDB;
|
||||
using namespace bsp;
|
||||
|
||||
|
||||
VBSPGeometry::VBSPGeometry(VBSPReader * reader)
|
||||
VBSPGeometry::VBSPGeometry(VBSPData * bspData)
|
||||
{
|
||||
// Keep track of the reader, as it has all of the data lists that we
|
||||
// Keep track of the bsp data, as it has all of the data lists that we
|
||||
// need
|
||||
vbsp_reader = reader;
|
||||
bsp_data = bspData;
|
||||
|
||||
// Create arrays for the vertex attributes
|
||||
vertex_array = new Vec3Array();
|
||||
@@ -249,17 +251,17 @@ void VBSPGeometry::createDispSurface(Face & face, DisplaceInfo & dispInfo)
|
||||
Vec3 texV;
|
||||
float texVOffset;
|
||||
float texVScale;
|
||||
int i, j, k;
|
||||
unsigned int i, j, k;
|
||||
double dist, minDist;
|
||||
int minIndex = 0;
|
||||
int minIndex;
|
||||
osg::Vec3 temp;
|
||||
int edgeIndex;
|
||||
int currentSurfEdge;
|
||||
Edge currentEdge;
|
||||
osg::Vec3 currentVertex;
|
||||
osg::Vec3 vertices[4];
|
||||
int firstVertex;
|
||||
int numEdgeVertices;
|
||||
unsigned int firstVertex;
|
||||
unsigned int numEdgeVertices;
|
||||
double subdivideScale;
|
||||
osg::Vec3 leftEdge, rightEdge;
|
||||
osg::Vec3 leftEdgeStep, rightEdgeStep;
|
||||
@@ -276,8 +278,8 @@ void VBSPGeometry::createDispSurface(Face & face, DisplaceInfo & dispInfo)
|
||||
|
||||
|
||||
// Get the texture info for this face
|
||||
currentTexInfo = vbsp_reader->getTexInfo(face.texinfo_index);
|
||||
currentTexData = vbsp_reader->getTexData(currentTexInfo.texdata_index);
|
||||
currentTexInfo = bsp_data->getTexInfo(face.texinfo_index);
|
||||
currentTexData = bsp_data->getTexData(currentTexInfo.texdata_index);
|
||||
|
||||
// Get the texture vectors and offsets. These are used to calculate
|
||||
// texture coordinates
|
||||
@@ -290,6 +292,10 @@ void VBSPGeometry::createDispSurface(Face & face, DisplaceInfo & dispInfo)
|
||||
currentTexInfo.texture_vecs[1][2]);
|
||||
texVOffset = currentTexInfo.texture_vecs[1][3];
|
||||
|
||||
// Scale the texture vectors from inches to meters
|
||||
texU *= 39.37;
|
||||
texV *= 39.37;
|
||||
|
||||
// Get the size of the texture involved, as the planar texture projection
|
||||
// assumes non-normalized texture coordinates
|
||||
texUScale = 1.0 / (float)currentTexData.texture_width;
|
||||
@@ -304,16 +310,16 @@ void VBSPGeometry::createDispSurface(Face & face, DisplaceInfo & dispInfo)
|
||||
// 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));
|
||||
currentSurfEdge = bsp_data->getSurfaceEdge(edgeIndex);
|
||||
currentEdge = bsp_data->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]);
|
||||
currentVertex = bsp_data->getVertex(currentEdge.vertex[1]);
|
||||
else
|
||||
currentVertex = vbsp_reader->getVertex(currentEdge.vertex[0]);
|
||||
currentVertex = bsp_data->getVertex(currentEdge.vertex[0]);
|
||||
|
||||
// Add the vertex to the array
|
||||
vertices[i] = currentVertex;
|
||||
@@ -328,7 +334,7 @@ void VBSPGeometry::createDispSurface(Face & face, DisplaceInfo & dispInfo)
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
// Calculate the distance of the start position from this vertex
|
||||
dist = (vertices[i] - dispInfo.start_position).length();
|
||||
dist = (vertices[i] - dispInfo.start_position * 0.0254).length();
|
||||
|
||||
// If this is the smallest distance we've seen, remember it
|
||||
if (dist < minDist)
|
||||
@@ -388,14 +394,15 @@ void VBSPGeometry::createDispSurface(Face & face, DisplaceInfo & dispInfo)
|
||||
// Get the displacement info for this vertex
|
||||
dispVertIndex = dispInfo.disp_vert_start;
|
||||
dispVertIndex += i * numEdgeVertices + j;
|
||||
dispVertInfo = vbsp_reader->getDispVertex(dispVertIndex);
|
||||
dispVertInfo = bsp_data->getDispVertex(dispVertIndex);
|
||||
|
||||
// Calculate the flat vertex
|
||||
flatVertex = leftEnd + (leftRightStep * (double) j);
|
||||
|
||||
// Calculate the displaced vertex
|
||||
dispVertex =
|
||||
dispVertInfo.displace_vec * dispVertInfo.displace_dist;
|
||||
dispVertInfo.displace_vec *
|
||||
(dispVertInfo.displace_dist * 0.0254);
|
||||
dispVertex += flatVertex;
|
||||
|
||||
// Add the vertex to the displaced vertex array
|
||||
@@ -510,14 +517,14 @@ void VBSPGeometry::addFace(int faceIndex)
|
||||
|
||||
// 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);
|
||||
currentFace = bsp_data->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);
|
||||
bsp_data->getDispInfo(currentFace.dispinfo_index);
|
||||
|
||||
// Generate the displacement surface
|
||||
createDispSurface(currentFace, currentDispInfo);
|
||||
@@ -525,13 +532,13 @@ void VBSPGeometry::addFace(int faceIndex)
|
||||
else
|
||||
{
|
||||
// Get the face normal, using the plane information
|
||||
normal = vbsp_reader->getPlane(currentFace.plane_index).plane_normal;
|
||||
normal = bsp_data->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);
|
||||
currentTexInfo = bsp_data->getTexInfo(currentFace.texinfo_index);
|
||||
currentTexData = bsp_data->getTexData(currentTexInfo.texdata_index);
|
||||
|
||||
// Get the texture vectors and offsets. These are used to calculate
|
||||
// texture coordinates
|
||||
@@ -544,6 +551,10 @@ void VBSPGeometry::addFace(int faceIndex)
|
||||
currentTexInfo.texture_vecs[1][2]);
|
||||
texVOffset = currentTexInfo.texture_vecs[1][3];
|
||||
|
||||
// Scale the texture vectors from inches to meters
|
||||
texU *= 39.37;
|
||||
texV *= 39.37;
|
||||
|
||||
// Get the texture size, as the planar texture projection results in
|
||||
// non-normalized texture coordinates
|
||||
texUScale = 1.0 / (float)currentTexData.texture_width;
|
||||
@@ -562,16 +573,16 @@ void VBSPGeometry::addFace(int faceIndex)
|
||||
// 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));
|
||||
currentSurfEdge = bsp_data->getSurfaceEdge(edgeIndex);
|
||||
currentEdge = bsp_data->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]);
|
||||
currentVertex = bsp_data->getVertex(currentEdge.vertex[1]);
|
||||
else
|
||||
currentVertex = vbsp_reader->getVertex(currentEdge.vertex[0]);
|
||||
currentVertex = bsp_data->getVertex(currentEdge.vertex[0]);
|
||||
|
||||
// Add the vertex to the array
|
||||
vertex_array->push_back(currentVertex);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <osg/Array>
|
||||
#include <osg/Geometry>
|
||||
|
||||
#include "VBSPReader.h"
|
||||
#include "VBSPData.h"
|
||||
|
||||
|
||||
namespace bsp
|
||||
@@ -17,7 +17,7 @@ class VBSPGeometry
|
||||
{
|
||||
protected:
|
||||
|
||||
VBSPReader * vbsp_reader;
|
||||
VBSPData * bsp_data;
|
||||
|
||||
osg::ref_ptr<osg::Vec3Array> vertex_array;
|
||||
osg::ref_ptr<osg::Vec3Array> normal_array;
|
||||
@@ -39,7 +39,7 @@ class VBSPGeometry
|
||||
|
||||
public:
|
||||
|
||||
VBSPGeometry(VBSPReader * reader);
|
||||
VBSPGeometry(VBSPData * bspData);
|
||||
virtual ~VBSPGeometry();
|
||||
|
||||
void addFace(int faceIndex);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,6 @@
|
||||
|
||||
|
||||
#include <osg/Geometry>
|
||||
#include <osg/Matrixd>
|
||||
#include <osg/Node>
|
||||
#include <osg/Object>
|
||||
#include <osg/StateSet>
|
||||
@@ -11,6 +10,7 @@
|
||||
#include <osgDB/FileNameUtils>
|
||||
#include <osg/Referenced>
|
||||
|
||||
#include "VBSPData.h"
|
||||
|
||||
namespace bsp
|
||||
{
|
||||
@@ -178,107 +178,52 @@ struct Header
|
||||
};
|
||||
|
||||
|
||||
struct Plane
|
||||
struct GameHeader
|
||||
{
|
||||
osg::Vec3f plane_normal;
|
||||
float origin_dist;
|
||||
int type;
|
||||
int num_lumps;
|
||||
|
||||
// This is followed by this many GameLump entries (see below)
|
||||
};
|
||||
|
||||
|
||||
struct GameLump
|
||||
{
|
||||
int lump_id;
|
||||
unsigned short lump_flags;
|
||||
unsigned short lump_version;
|
||||
int lump_offset;
|
||||
int lump_length;
|
||||
};
|
||||
|
||||
|
||||
// This is the ID for the static prop game lump
|
||||
const int STATIC_PROP_ID = (('s'<<24)+('p'<<16)+('r'<<8)+'p');
|
||||
|
||||
|
||||
struct StaticPropModelNames
|
||||
{
|
||||
int num_model_names;
|
||||
|
||||
// This is followed by this many names, each 128 characters long
|
||||
};
|
||||
|
||||
|
||||
struct Edge
|
||||
struct StaticPropLeaves
|
||||
{
|
||||
unsigned short vertex[2];
|
||||
int num_leaf_entries;
|
||||
|
||||
// This is followed by this many unsigned shorts, indicating which BSP
|
||||
// leaves this prop occupies
|
||||
};
|
||||
|
||||
|
||||
struct Face
|
||||
struct StaticProps
|
||||
{
|
||||
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;
|
||||
};
|
||||
int num_static_props;
|
||||
|
||||
|
||||
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 int allowed_verts[10];
|
||||
};
|
||||
|
||||
|
||||
struct DisplacedVertex
|
||||
{
|
||||
osg::Vec3f displace_vec;
|
||||
float displace_dist;
|
||||
float alpha_blend;
|
||||
// This is followed by this many StaticProp entries (see VBSPData.h), note
|
||||
// that there are two possible StaticProp versions, depending on the
|
||||
// version of the GameLump
|
||||
};
|
||||
|
||||
|
||||
@@ -288,48 +233,17 @@ protected:
|
||||
|
||||
std::string map_name;
|
||||
|
||||
VBSPData * bsp_data;
|
||||
|
||||
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;
|
||||
|
||||
char * texdata_string;
|
||||
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 processModels(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);
|
||||
@@ -339,15 +253,15 @@ protected:
|
||||
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 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);
|
||||
void processGameData(std::istream & str, int offset, int length);
|
||||
void processStaticProps(std::istream & str, int offset, int length,
|
||||
int lumpVersion);
|
||||
|
||||
std::string getToken(std::string str, const char * delim,
|
||||
std::string::size_type & index);
|
||||
|
||||
std::string findFileIgnoreCase(std::string filePath);
|
||||
size_t & index);
|
||||
|
||||
osg::ref_ptr<osg::StateSet> createBlendShader(osg::Texture * tex1,
|
||||
osg::Texture * tex2);
|
||||
@@ -361,18 +275,6 @@ 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);
|
||||
|
||||
|
||||
@@ -21,20 +21,40 @@ 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).
|
||||
maps/ and materials/ directories are used by this plugin. Models (in the
|
||||
models/ directory) are handled by the .mdl plugin, but the .bsp plugin
|
||||
typically will read model files as part of loading a .bsp map.
|
||||
|
||||
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.
|
||||
It is important to preserve the file and directory structure as it is in the
|
||||
.gcf files, although you can merge the data from several files (different
|
||||
games) together. For example, you can extract the models/ directory from
|
||||
source models.gcf, the materials/ directory from source materials.gcf, and the
|
||||
maps/, models/, and materials/ directories from half-life 2 deathmatch.gcf and
|
||||
combine them together into a single parent directory (called "hl2data/", for
|
||||
example). This arrangement will let you load any of the HL2 deathmatch maps,
|
||||
with all of the props and materials that might be needed.
|
||||
|
||||
If you're confused, here's a lame ASCII art drawing to confuse you even more:
|
||||
|
||||
hl2data
|
||||
|
|
||||
+----maps
|
||||
|
|
||||
+----materials
|
||||
|
|
||||
+----models
|
||||
|
||||
|
||||
If you want to use the OSGFILEPATH environment variable to let OSG search for
|
||||
maps or models, point it to the parent directory ("hl2data" in the example
|
||||
above), then load your map like this:
|
||||
|
||||
osgviewer maps/dm_runoff.bsp
|
||||
|
||||
|
||||
What Works
|
||||
----------
|
||||
All brush geometry and faces.
|
||||
All visible brush geometry and faces (except sky boxes).
|
||||
|
||||
Displacement surfaces.
|
||||
|
||||
@@ -42,6 +62,13 @@ 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.
|
||||
|
||||
Props (including static props embedded into the map file, and dynamic and
|
||||
physics props as loaded by the mdl plugin). Note that because of Source's
|
||||
physics engine, you might see some of the physics props suspended in the air
|
||||
when loading a map. The level designer placed them there so they fall to
|
||||
the ground when the level starts. Presumably, this is quicker and safer than
|
||||
trying to get them positioned exactly on the ground.
|
||||
|
||||
|
||||
What Doesn't Work (yet)
|
||||
-----------------------
|
||||
@@ -62,18 +89,25 @@ 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.
|
||||
show up mostly white. At the very least, this needs environment map support to
|
||||
work properly.
|
||||
|
||||
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.
|
||||
Sky boxes. These were left out by design (they tended to get in the way
|
||||
when doing the work required by the sponsor). These could easily be put
|
||||
back in, and this would probably make a good ReaderWriter option.
|
||||
|
||||
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.
|
||||
|
||||
Environmental effects (fires, dust clouds, etc.). Each one would likely
|
||||
require specific code.
|
||||
|
||||
Certain classes of props (collectable items, weapons, etc.) are not loaded
|
||||
right now. With these props, there is an explicit mapping between the prop's
|
||||
object class and the model that gets loaded for it. I just haven't set up
|
||||
these mappings yet.
|
||||
|
||||
HDR materials, bump maps, detail props, and other eye-candy. Implement it if
|
||||
you want! :-)
|
||||
|
||||
|
||||
48
src/osgPlugins/mdl/BodyPart.cpp
Normal file
48
src/osgPlugins/mdl/BodyPart.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
|
||||
#include "BodyPart.h"
|
||||
|
||||
|
||||
using namespace mdl;
|
||||
|
||||
|
||||
BodyPart::BodyPart(MDLBodyPart * myPart)
|
||||
{
|
||||
// Save the body part information
|
||||
my_body_part = myPart;
|
||||
}
|
||||
|
||||
|
||||
BodyPart::~BodyPart()
|
||||
{
|
||||
// Clean up the associated data
|
||||
delete my_body_part;
|
||||
}
|
||||
|
||||
|
||||
MDLBodyPart * BodyPart::getBodyPart()
|
||||
{
|
||||
return my_body_part;
|
||||
}
|
||||
|
||||
|
||||
void BodyPart::addModel(Model * newModel)
|
||||
{
|
||||
// Add the new node to our list
|
||||
part_models.push_back(newModel);
|
||||
}
|
||||
|
||||
|
||||
int BodyPart::getNumModels()
|
||||
{
|
||||
return part_models.size();
|
||||
}
|
||||
|
||||
|
||||
Model * BodyPart::getModel(int partIndex)
|
||||
{
|
||||
if ((partIndex < 0) || (partIndex >= part_models.size()))
|
||||
return NULL;
|
||||
else
|
||||
return part_models[partIndex];
|
||||
}
|
||||
|
||||
50
src/osgPlugins/mdl/BodyPart.h
Normal file
50
src/osgPlugins/mdl/BodyPart.h
Normal file
@@ -0,0 +1,50 @@
|
||||
|
||||
#ifndef __BODY_PART_H_
|
||||
#define __BODY_PART_H_
|
||||
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "Model.h"
|
||||
|
||||
|
||||
namespace mdl
|
||||
{
|
||||
|
||||
|
||||
struct MDLBodyPart
|
||||
{
|
||||
int mdl_name_index;
|
||||
int num_models;
|
||||
int body_part_base;
|
||||
int model_offset;
|
||||
};
|
||||
|
||||
|
||||
class BodyPart
|
||||
{
|
||||
protected:
|
||||
|
||||
typedef std::vector<Model *> ModelList;
|
||||
|
||||
MDLBodyPart * my_body_part;
|
||||
|
||||
ModelList part_models;
|
||||
|
||||
public:
|
||||
|
||||
BodyPart(MDLBodyPart * myPart);
|
||||
virtual ~BodyPart();
|
||||
|
||||
MDLBodyPart * getBodyPart();
|
||||
|
||||
void addModel(Model * newModel);
|
||||
int getNumModels();
|
||||
Model * getModel(int modelIndex);
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
26
src/osgPlugins/mdl/CMakeLists.txt
Normal file
26
src/osgPlugins/mdl/CMakeLists.txt
Normal file
@@ -0,0 +1,26 @@
|
||||
SET(TARGET_SRC
|
||||
ReaderWriterMDL.cpp
|
||||
BodyPart.cpp
|
||||
Mesh.cpp
|
||||
Model.cpp
|
||||
MDLReader.cpp
|
||||
MDLRoot.cpp
|
||||
VTXReader.cpp
|
||||
VVDReader.cpp
|
||||
)
|
||||
|
||||
SET(TARGET_H
|
||||
ReaderWriterMDL.h
|
||||
BodyPart.h
|
||||
Mesh.h
|
||||
Model.h
|
||||
MDLLimits.h
|
||||
MDLReader.h
|
||||
MDLRoot.h
|
||||
VTXReader.h
|
||||
VVDReader.h
|
||||
)
|
||||
|
||||
#### end var setup ###
|
||||
SETUP_PLUGIN(mdl)
|
||||
|
||||
22
src/osgPlugins/mdl/MDLLimits.h
Normal file
22
src/osgPlugins/mdl/MDLLimits.h
Normal file
@@ -0,0 +1,22 @@
|
||||
|
||||
#ifndef __MDL_LIMITS_H_
|
||||
#define __MDL_LIMITS_H_
|
||||
|
||||
|
||||
namespace mdl
|
||||
{
|
||||
|
||||
|
||||
// Maximum number of LODs per model
|
||||
const int MAX_LODS = 8;
|
||||
|
||||
|
||||
// Maximum number of bones per vertex
|
||||
const int MAX_BONES_PER_VERTEX = 3;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
677
src/osgPlugins/mdl/MDLReader.cpp
Normal file
677
src/osgPlugins/mdl/MDLReader.cpp
Normal file
@@ -0,0 +1,677 @@
|
||||
#include <osg/BlendFunc>
|
||||
#include <osg/BoundingSphere>
|
||||
#include <osg/Geometry>
|
||||
#include <osg/Group>
|
||||
#include <osg/Object>
|
||||
#include <osg/Material>
|
||||
#include <osg/MatrixTransform>
|
||||
#include <osg/Node>
|
||||
#include <osg/Notify>
|
||||
#include <osg/StateSet>
|
||||
#include <osg/Texture1D>
|
||||
#include <osg/Texture2D>
|
||||
#include <osg/Texture3D>
|
||||
#include <osgDB/Registry>
|
||||
#include <osgDB/FileUtils>
|
||||
#include <osgDB/ReadFile>
|
||||
#include <osg/io_utils>
|
||||
#include <iostream>
|
||||
|
||||
#include "MDLReader.h"
|
||||
#include "VVDReader.h"
|
||||
#include "VTXReader.h"
|
||||
|
||||
|
||||
using namespace mdl;
|
||||
using namespace osg;
|
||||
using namespace osgDB;
|
||||
|
||||
|
||||
MDLReader::MDLReader()
|
||||
{
|
||||
// Start with no root node
|
||||
root_node = NULL;
|
||||
}
|
||||
|
||||
|
||||
MDLReader::~MDLReader()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
std::string MDLReader::getToken(std::string str, const char * delim,
|
||||
size_t & index)
|
||||
{
|
||||
size_t start, end;
|
||||
std::string token;
|
||||
|
||||
// Look for the first non-occurrence of the delimiters
|
||||
start = str.find_first_not_of(" \t\n\r\"", index);
|
||||
if (start != std::string::npos)
|
||||
{
|
||||
// From there, look for the first occurrence of a delimiter
|
||||
end = str.find_first_of(" \t\n\r\"", start+1);
|
||||
if (end != std::string::npos)
|
||||
{
|
||||
// Found a delimiter, so grab the string in between
|
||||
token = str.substr(start, end-start);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ran off the end of the string, so just grab everything from
|
||||
// the first good character
|
||||
token = str.substr(start);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No token to be found
|
||||
token = "";
|
||||
}
|
||||
|
||||
// Update the index (in case we want to keep looking for tokens in this
|
||||
// string)
|
||||
if (end != std::string::npos)
|
||||
index = end+1;
|
||||
else
|
||||
index = std::string::npos;
|
||||
|
||||
// Return the token
|
||||
return token;
|
||||
}
|
||||
|
||||
|
||||
ref_ptr<Texture> MDLReader::readTextureFile(std::string textureName)
|
||||
{
|
||||
int i;
|
||||
std::string texFile;
|
||||
std::string texPath;
|
||||
Image * texImage;
|
||||
Texture * texture;
|
||||
|
||||
// Find the texture's image file
|
||||
texFile = std::string(textureName) + ".vtf";
|
||||
texPath = findDataFile(texFile, CASE_INSENSITIVE);
|
||||
|
||||
// If we don't find it right away, check in a "materials" subdirectory
|
||||
if (texPath.empty())
|
||||
{
|
||||
// Check for a leading slash and concatenate appropriately
|
||||
if ((textureName[0] == '\\') || (textureName[0] == '/'))
|
||||
texFile = "materials" + std::string(textureName) + ".vtf";
|
||||
else
|
||||
texFile = "materials/" + std::string(textureName) + ".vtf";
|
||||
|
||||
// Look for the texture at this location
|
||||
texPath = findDataFile(texFile, CASE_INSENSITIVE);
|
||||
|
||||
// Check up one directory if we don't find it here (the map file is
|
||||
// usually located in the "maps" directory, adjacent to the materials
|
||||
// directory)
|
||||
if (texPath.empty())
|
||||
{
|
||||
// Check for a leading slash and concatenate appropriately
|
||||
if ((textureName[0] == '\\') || (textureName[0] == '/'))
|
||||
texFile = "../materials" + std::string(textureName) + ".vtf";
|
||||
else
|
||||
texFile = "../materials/" + std::string(textureName) + ".vtf";
|
||||
|
||||
// Look for the texture at this location
|
||||
texPath = findDataFile(texFile, CASE_INSENSITIVE);
|
||||
}
|
||||
}
|
||||
|
||||
// If we found the file, read it, otherwise bail
|
||||
if (!texPath.empty())
|
||||
{
|
||||
texImage = readImageFile(texPath);
|
||||
|
||||
// If we got the image, create the texture attribute
|
||||
if (texImage != NULL)
|
||||
{
|
||||
// Create the texture
|
||||
if (texImage->t() == 1)
|
||||
{
|
||||
texture = new Texture1D();
|
||||
((Texture1D *)texture)->setImage(texImage);
|
||||
}
|
||||
else if (texImage->r() == 1)
|
||||
{
|
||||
texture = new Texture2D();
|
||||
((Texture2D *)texture)->setImage(texImage);
|
||||
}
|
||||
else
|
||||
{
|
||||
texture = new Texture3D();
|
||||
((Texture3D *)texture)->setImage(texImage);
|
||||
}
|
||||
|
||||
// Set texture attributes
|
||||
texture->setWrap(Texture::WRAP_S, Texture::REPEAT);
|
||||
texture->setWrap(Texture::WRAP_T, Texture::REPEAT);
|
||||
texture->setWrap(Texture::WRAP_R, Texture::REPEAT);
|
||||
texture->setFilter(Texture::MAG_FILTER, Texture::LINEAR);
|
||||
texture->setFilter(Texture::MIN_FILTER,
|
||||
Texture::LINEAR_MIPMAP_LINEAR);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We were unable to find the texture file
|
||||
notify(WARN) << "Couldn't find texture " << textureName;
|
||||
notify(WARN) << std::endl;
|
||||
|
||||
// No texture
|
||||
texture = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We were unable to find the texture file
|
||||
notify(WARN) << "Couldn't find texture " << textureName;
|
||||
notify(WARN) << std::endl;
|
||||
|
||||
// No texture
|
||||
texture = NULL;
|
||||
}
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
|
||||
ref_ptr<StateSet> MDLReader::readMaterialFile(std::string materialName)
|
||||
{
|
||||
std::string mtlFileName;
|
||||
std::string mtlPath;
|
||||
StringList::iterator searchItr;
|
||||
std::ifstream * mtlFile;
|
||||
std::string line;
|
||||
size_t start;
|
||||
std::string token;
|
||||
bool found;
|
||||
ref_ptr<StateSet> stateSet;
|
||||
std::string shaderName;
|
||||
osg::Image * texImage;
|
||||
std::string texName;
|
||||
std::string tex2Name;
|
||||
ref_ptr<Texture> texture;
|
||||
ref_ptr<Texture> texture2;
|
||||
ref_ptr<BlendFunc> blend;
|
||||
bool translucent;
|
||||
|
||||
// Find the material file
|
||||
mtlFileName = std::string(materialName) + ".vmt";
|
||||
mtlPath = findDataFile(mtlFileName, CASE_INSENSITIVE);
|
||||
|
||||
// If we don't find it right away, search the texture file search paths
|
||||
if (mtlPath.empty())
|
||||
{
|
||||
searchItr = texture_paths.begin();
|
||||
while ((mtlPath.empty()) && (searchItr != texture_paths.end()))
|
||||
{
|
||||
// The search paths assume that materials are always located in
|
||||
// a "materials" subdirectory. Also, check to see if there is
|
||||
// a leading slash and concatenate appropriately
|
||||
if (((*searchItr)[0] == '\\') || ((*searchItr)[0] == '/'))
|
||||
mtlFileName = "materials" + *searchItr +
|
||||
std::string(materialName) + ".vmt";
|
||||
else
|
||||
mtlFileName = "materials/" + *searchItr +
|
||||
std::string(materialName) + ".vmt";
|
||||
|
||||
// Try to find the file in this path
|
||||
mtlPath = findDataFile(mtlFileName, CASE_INSENSITIVE);
|
||||
|
||||
// Next path
|
||||
searchItr++;
|
||||
}
|
||||
|
||||
// If we still haven't found it, check up one directory level (the
|
||||
// model file is usually located in the "models" directory, adjacent
|
||||
// to the "materials" directory)
|
||||
if (mtlPath.empty())
|
||||
{
|
||||
searchItr = texture_paths.begin();
|
||||
while ((mtlPath.empty()) && (searchItr != texture_paths.end()))
|
||||
{
|
||||
// The search paths assume that materials are always located in
|
||||
// a "materials" subdirectory, but this time try going up one
|
||||
// level first
|
||||
if (((*searchItr)[0] == '\\') || ((*searchItr)[0] == '/'))
|
||||
mtlFileName = "../materials" + *searchItr +
|
||||
std::string(materialName) + ".vmt";
|
||||
else
|
||||
mtlFileName = "../materials/" + *searchItr +
|
||||
std::string(materialName) + ".vmt";
|
||||
|
||||
// Try to find the file in this path
|
||||
mtlPath = findDataFile(mtlFileName, CASE_INSENSITIVE);
|
||||
|
||||
// Next path
|
||||
searchItr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// See if we found the file
|
||||
if (!mtlPath.empty())
|
||||
{
|
||||
// Try to open the file, bail out if we fail
|
||||
mtlFile = new std::ifstream(mtlPath.c_str(), std::ifstream::in);
|
||||
if (!mtlFile)
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Didn't find the material file, so return NULL
|
||||
notify(WARN) << "Can't find material " << materialName << std::endl;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// First, look for the shader name
|
||||
found = false;
|
||||
while ((!found) && (!mtlFile->eof()))
|
||||
{
|
||||
// Read a line from the file
|
||||
std::getline(*mtlFile, line);
|
||||
|
||||
// Try to find the shader name
|
||||
start = 0;
|
||||
token = getToken(line, " \t\n\r\"", start);
|
||||
|
||||
// If we got something, it must be the shader
|
||||
if ((!token.empty()) && (token.compare(0, 2, "//") != 0))
|
||||
{
|
||||
shaderName = token;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If we didn't find a shader, this isn't a valid material file
|
||||
if (!found)
|
||||
{
|
||||
mtlFile->close();
|
||||
notify(WARN) << "Material " << materialName << " isn't valid.";
|
||||
notify(WARN) << std::endl;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// No textures loaded yet
|
||||
texture = NULL;
|
||||
texture2 = NULL;
|
||||
|
||||
// Assume not translucent unless the properties say otherwise
|
||||
translucent = false;
|
||||
|
||||
// Read the material properties next
|
||||
while (!mtlFile->eof())
|
||||
{
|
||||
// Get the next line
|
||||
std::getline(*mtlFile, line);
|
||||
|
||||
// Look for tokens starting at the beginning
|
||||
start = 0;
|
||||
token = getToken(line, " \t\n\r\"", start);
|
||||
|
||||
while ((!token.empty()) && (token.compare(0, 2, "//") != 0))
|
||||
{
|
||||
if (equalCaseInsensitive(token, "$basetexture"))
|
||||
{
|
||||
// Get the base texture name
|
||||
token = getToken(line, " \t\n\r\"", start);
|
||||
|
||||
// Read the texture
|
||||
if (!token.empty())
|
||||
texture = readTextureFile(token);
|
||||
}
|
||||
else if (equalCaseInsensitive(token, "$basetexture2"))
|
||||
{
|
||||
// Get the second base texture name
|
||||
token = getToken(line, " \t\n\r\"", start);
|
||||
|
||||
// Read the texture
|
||||
if (!token.empty())
|
||||
texture2 = readTextureFile(token);
|
||||
}
|
||||
else if ((equalCaseInsensitive(token, "$translucent")) ||
|
||||
(equalCaseInsensitive(token, "$alphatest")))
|
||||
{
|
||||
// Get the translucency setting
|
||||
token = getToken(line, " \t\n\r\"", start);
|
||||
|
||||
// Interpret the setting
|
||||
if (!token.empty())
|
||||
{
|
||||
if ((token == "1") || (token == "true"))
|
||||
translucent = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Try the next token
|
||||
token = getToken(line, " \t\n\r\"", start);
|
||||
}
|
||||
}
|
||||
|
||||
// Start with no StateSet (in case the stuff below fails)
|
||||
stateSet = NULL;
|
||||
|
||||
// Check the shader's name
|
||||
if (equalCaseInsensitive(shaderName, "UnlitGeneric"))
|
||||
{
|
||||
// Create the StateSet
|
||||
stateSet = new StateSet();
|
||||
|
||||
// Disable lighting on this StateSet
|
||||
stateSet->setMode(GL_LIGHTING, StateAttribute::OFF);
|
||||
|
||||
// Add the texture attribute (or disable texturing if no base texture)
|
||||
if (texture != NULL)
|
||||
{
|
||||
stateSet->setTextureAttributeAndModes(0, texture.get(),
|
||||
StateAttribute::ON);
|
||||
}
|
||||
else
|
||||
{
|
||||
notify(WARN) << "No base texture for material " << materialName;
|
||||
notify(WARN) << std::endl;
|
||||
stateSet->setTextureMode(0, GL_TEXTURE_2D, StateAttribute::OFF);
|
||||
}
|
||||
|
||||
// See if the material is translucent
|
||||
if (translucent)
|
||||
{
|
||||
// Add the blending attribute as well
|
||||
blend = new BlendFunc(BlendFunc::SRC_ALPHA,
|
||||
BlendFunc::ONE_MINUS_SRC_ALPHA);
|
||||
stateSet->setAttributeAndModes(blend.get(), StateAttribute::ON);
|
||||
|
||||
// Set the rendering hint for this stateset to transparent
|
||||
stateSet->setRenderingHint(StateSet::TRANSPARENT_BIN);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// All other shaders fall back to fixed function
|
||||
// TODO: LightMappedGeneric shader
|
||||
|
||||
// Create the StateSet
|
||||
stateSet = new StateSet();
|
||||
|
||||
// Add the texture attribute (or disable texturing if no base texture)
|
||||
if (texture != NULL)
|
||||
{
|
||||
stateSet->setTextureAttributeAndModes(0, texture.get(),
|
||||
StateAttribute::ON);
|
||||
|
||||
// See if the material is translucent
|
||||
if (translucent)
|
||||
{
|
||||
// Add the blending attribute as well
|
||||
blend = new BlendFunc(BlendFunc::SRC_ALPHA,
|
||||
BlendFunc::ONE_MINUS_SRC_ALPHA);
|
||||
stateSet->setAttributeAndModes(blend.get(),
|
||||
StateAttribute::ON);
|
||||
|
||||
// Set the rendering hint for this stateset to transparent
|
||||
stateSet->setRenderingHint(StateSet::TRANSPARENT_BIN);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
notify(WARN) << "No base texture for material " << materialName;
|
||||
notify(WARN) << std::endl;
|
||||
stateSet->setTextureMode(0, GL_TEXTURE_2D, StateAttribute::OFF);
|
||||
}
|
||||
}
|
||||
|
||||
// Close the file
|
||||
mtlFile->close();
|
||||
|
||||
// Return the resulting StateSet
|
||||
return stateSet;
|
||||
}
|
||||
|
||||
|
||||
BodyPart * MDLReader::processBodyPart(std::istream * str, int offset)
|
||||
{
|
||||
int i;
|
||||
MDLBodyPart * part;
|
||||
BodyPart * partNode;
|
||||
Model * modelNode;
|
||||
|
||||
// Seek to the body part
|
||||
str->seekg(offset);
|
||||
|
||||
// Read it
|
||||
part = new MDLBodyPart;
|
||||
str->read((char *) part, sizeof(MDLBodyPart));
|
||||
|
||||
// Create the body part node
|
||||
partNode = new BodyPart(part);
|
||||
|
||||
// Process the models
|
||||
for (i = 0; i < part->num_models; i++)
|
||||
{
|
||||
// Process the model
|
||||
modelNode = processModel(str, offset + part->model_offset +
|
||||
(i * sizeof(MDLModel)));
|
||||
|
||||
// Add the model to the body part
|
||||
partNode->addModel(modelNode);
|
||||
}
|
||||
|
||||
// Return the new node
|
||||
return partNode;
|
||||
}
|
||||
|
||||
|
||||
Model * MDLReader::processModel(std::istream * str, int offset)
|
||||
{
|
||||
int i;
|
||||
MDLModel * model;
|
||||
Model * modelNode;
|
||||
Mesh * meshNode;
|
||||
|
||||
// Seek to the model
|
||||
str->seekg(offset);
|
||||
|
||||
// Read it
|
||||
model = new MDLModel;
|
||||
str->read((char *) model, sizeof(MDLModel));
|
||||
|
||||
// Create the model node
|
||||
modelNode = new Model(model);
|
||||
|
||||
// Process the meshes
|
||||
for (i = 0; i < model->num_meshes; i++)
|
||||
{
|
||||
// Process the mesh
|
||||
meshNode = processMesh(str, offset + model->mesh_offset +
|
||||
(i * sizeof(MDLMesh)));
|
||||
|
||||
// Add the mesh to the model
|
||||
modelNode->addMesh(meshNode);
|
||||
}
|
||||
|
||||
// Return the model node
|
||||
return modelNode;
|
||||
}
|
||||
|
||||
|
||||
Mesh * MDLReader::processMesh(std::istream * str, int offset)
|
||||
{
|
||||
int i;
|
||||
MDLMesh * mesh;
|
||||
Mesh * meshNode;
|
||||
|
||||
// Seek to the mesh
|
||||
str->seekg(offset);
|
||||
|
||||
// Read it
|
||||
mesh = new MDLMesh;
|
||||
str->read((char *) mesh, sizeof(MDLMesh));
|
||||
|
||||
// Create the mesh node
|
||||
meshNode = new Mesh(mesh);
|
||||
|
||||
// Set the mesh's state set based on the material id
|
||||
meshNode->setStateSet((state_sets[mesh->material_index]).get());
|
||||
|
||||
// Return the mesh node
|
||||
return meshNode;
|
||||
}
|
||||
|
||||
|
||||
bool MDLReader::readFile(const std::string & file)
|
||||
{
|
||||
std::string baseName;
|
||||
std::string fileName;
|
||||
std::ifstream * mdlFile;
|
||||
MDLHeader header;
|
||||
int i, j;
|
||||
int offset;
|
||||
MDLRoot * mdlRoot;
|
||||
BodyPart * partNode;
|
||||
std::string vvdFile;
|
||||
VVDReader * vvdReader;
|
||||
std::string vtxFile;
|
||||
VTXReader * vtxReader;
|
||||
|
||||
// Remember the model name
|
||||
mdl_name = getStrippedName(file);
|
||||
|
||||
// Try to open the file
|
||||
fileName = findDataFile(file, CASE_INSENSITIVE);
|
||||
mdlFile = new std::ifstream(fileName.c_str(), std::ios::binary);
|
||||
if (!mdlFile)
|
||||
{
|
||||
osg::notify(osg::NOTICE) << "MDL file not found" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the header
|
||||
mdlFile->read((char *) &header, sizeof(MDLHeader));
|
||||
|
||||
// Make sure the file is a valid Valve MDL file
|
||||
if (header.magic_number != MDL_MAGIC_NUMBER)
|
||||
{
|
||||
osg::notify(osg::NOTICE) << "This is not a valid .mdl file";
|
||||
osg::notify(osg::NOTICE) << std::endl;
|
||||
|
||||
// Close the file before we quit
|
||||
mdlFile->close();
|
||||
delete mdlFile;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure the version is one that we handle
|
||||
// TODO: Not sure which versions are valid yet
|
||||
|
||||
// Get the texture paths from the file (we'll have to search these paths
|
||||
// for each texture that we load)
|
||||
for (i = 0; i < header.num_texture_paths; i++)
|
||||
{
|
||||
int texPathBase;
|
||||
int texPathOffset;
|
||||
char texPath[256];
|
||||
|
||||
texPathBase = header.texture_path_offset + (i * sizeof(int));
|
||||
mdlFile->seekg(texPathBase);
|
||||
mdlFile->read((char *) &texPathOffset, sizeof(int));
|
||||
mdlFile->seekg(texPathOffset);
|
||||
|
||||
// Read characters from the file until we reach the end of this path
|
||||
j = 0;
|
||||
do
|
||||
{
|
||||
mdlFile->get(texPath[j]);
|
||||
j++;
|
||||
}
|
||||
while ((j < sizeof(texPath)) && (texPath[j-1] != 0));
|
||||
|
||||
// Store this path
|
||||
texture_paths.push_back(texPath);
|
||||
}
|
||||
|
||||
// Read the texture info from the file, and create a StateSet for each
|
||||
// one
|
||||
for (i = 0; i < header.num_textures; i++)
|
||||
{
|
||||
int texBase;
|
||||
MDLTexture tempTex;
|
||||
char texName[256];
|
||||
ref_ptr<StateSet> stateSet;
|
||||
|
||||
texBase = header.texture_offset + (i * sizeof(MDLTexture));
|
||||
mdlFile->seekg(texBase);
|
||||
mdlFile->read((char *) &tempTex, sizeof(MDLTexture));
|
||||
mdlFile->seekg(texBase + tempTex.tex_name_offset);
|
||||
j = 0;
|
||||
do
|
||||
{
|
||||
mdlFile->get(texName[j]);
|
||||
j++;
|
||||
}
|
||||
while ((j < sizeof(texName)) && (texName[j-1] != 0));
|
||||
|
||||
// Load this texture
|
||||
stateSet = readMaterialFile(texName);
|
||||
|
||||
// Add it to our list
|
||||
state_sets.push_back(stateSet);
|
||||
}
|
||||
|
||||
// Create the root node of the MDL tree
|
||||
mdlRoot = new MDLRoot();
|
||||
|
||||
// Process the main model's body parts
|
||||
for (i = 0; i < header.num_body_parts; i++)
|
||||
{
|
||||
// Calculate the offset to the next body part
|
||||
offset = header.body_part_offset + (i * sizeof(MDLBodyPart));
|
||||
|
||||
// Process the body part and get the part's node
|
||||
partNode = processBodyPart(mdlFile, offset);
|
||||
|
||||
// Add the body part to the MDL root node
|
||||
mdlRoot->addBodyPart(partNode);
|
||||
}
|
||||
|
||||
// Open the VVD file that goes with this model
|
||||
vvdFile = findDataFile(getNameLessExtension(file) + ".vvd",
|
||||
CASE_INSENSITIVE);
|
||||
vvdReader = new VVDReader();
|
||||
vvdReader->readFile(vvdFile);
|
||||
|
||||
// Open the VTX file that goes with this model (at this point, I don't
|
||||
// see a reason not to always just use the DX9 version)
|
||||
vtxFile = findDataFile(getNameLessExtension(file) + ".dx90.vtx",
|
||||
CASE_INSENSITIVE);
|
||||
vtxReader = new VTXReader(vvdReader, mdlRoot);
|
||||
vtxReader->readFile(vtxFile);
|
||||
|
||||
// Get the root group from the VTX reader
|
||||
root_node = vtxReader->getModel();
|
||||
|
||||
// Close the .mdl file
|
||||
mdlFile->close();
|
||||
delete mdlFile;
|
||||
|
||||
// Close the two auxiliary readers
|
||||
delete vvdReader;
|
||||
delete vtxReader;
|
||||
|
||||
// Clean up the MDL tree (we don't need its data anymore)
|
||||
delete mdlRoot;
|
||||
|
||||
// Return true to indicate success
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
ref_ptr<Node> MDLReader::getRootNode()
|
||||
{
|
||||
return root_node;
|
||||
}
|
||||
|
||||
|
||||
193
src/osgPlugins/mdl/MDLReader.h
Normal file
193
src/osgPlugins/mdl/MDLReader.h
Normal file
@@ -0,0 +1,193 @@
|
||||
#ifndef __MDL_READER_H_
|
||||
#define __MDL_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>
|
||||
|
||||
#include "MDLLimits.h"
|
||||
#include "MDLRoot.h"
|
||||
|
||||
|
||||
namespace mdl
|
||||
{
|
||||
|
||||
|
||||
// The magic number for a Valve MDL file is 'IDST' in little-endian
|
||||
// order
|
||||
const int MDL_MAGIC_NUMBER = (('T'<<24)+('S'<<16)+('D'<<8)+'I');
|
||||
|
||||
|
||||
struct MDLHeader
|
||||
{
|
||||
int magic_number;
|
||||
int mdl_version;
|
||||
int check_sum;
|
||||
char mdl_name[64];
|
||||
int mdl_length;
|
||||
|
||||
osg::Vec3 eye_position;
|
||||
osg::Vec3 illum_position;
|
||||
osg::Vec3 hull_min;
|
||||
osg::Vec3 hull_max;
|
||||
osg::Vec3 view_bbox_min;
|
||||
osg::Vec3 view_bbox_max;
|
||||
|
||||
int mdl_flags;
|
||||
|
||||
int num_bones;
|
||||
int bone_offset;
|
||||
|
||||
int num_bone_controllers;
|
||||
int bone_controller_offset;
|
||||
|
||||
int num_hitbox_sets;
|
||||
int hitbox_set_offset;
|
||||
|
||||
int num_local_animations;
|
||||
int local_animation_offset;
|
||||
|
||||
int num_local_sequences;
|
||||
int local_sequence_offset;
|
||||
|
||||
mutable int activity_list_version;
|
||||
mutable int events_offseted;
|
||||
|
||||
int num_textures;
|
||||
int texture_offset;
|
||||
|
||||
int num_texture_paths;
|
||||
int texture_path_offset;
|
||||
|
||||
int num_skin_refs;
|
||||
int num_skin_families;
|
||||
int skin_offset;
|
||||
|
||||
int num_body_parts;
|
||||
int body_part_offset;
|
||||
|
||||
int num_local_attachments;
|
||||
int local_attachment_offset;
|
||||
|
||||
int num_local_nodes;
|
||||
int local_node_offset;
|
||||
int local_node_name_offset;
|
||||
|
||||
int num_flex_desc;
|
||||
int flex_desc_offset;
|
||||
|
||||
int num_flex_controllers;
|
||||
int flex_controller_offset;
|
||||
|
||||
int num_flex_rules;
|
||||
int flex_rule_offset;
|
||||
|
||||
int num_ik_chains;
|
||||
int ik_chain_offset;
|
||||
|
||||
int num_mouths;
|
||||
int mouth_offset;
|
||||
|
||||
int num_local_pose_params;
|
||||
int local_pose_param_offset;
|
||||
|
||||
int surface_prop_offset;
|
||||
|
||||
int key_value_offset;
|
||||
int key_value_size;
|
||||
|
||||
int num_local_ik_autoplay_locks;
|
||||
int local_ik_autoplay_lock_offset;
|
||||
|
||||
float mdl_mass;
|
||||
int mdl_contents;
|
||||
|
||||
int num_include_models;
|
||||
int include_model_offset;
|
||||
|
||||
// Originally a mutable void * (changed for portability)
|
||||
mutable int virtual_model;
|
||||
|
||||
int anim_block_name_offset;
|
||||
int num_anim_blocks;
|
||||
int anim_block_offset;
|
||||
|
||||
// Originally a mutable void * (changed for portability)
|
||||
mutable int anim_block_model;
|
||||
|
||||
int bone_table_by_name_offset;
|
||||
|
||||
// Originally both void * (changed for portability)
|
||||
int vertex_base;
|
||||
int offset_base;
|
||||
|
||||
u_char const_direction_light_dot;
|
||||
u_char root_lod;
|
||||
u_char unused_byte[2];
|
||||
|
||||
int zero_frame_cache_offset;
|
||||
|
||||
int unused_fields[2];
|
||||
};
|
||||
|
||||
|
||||
struct MDLTexture
|
||||
{
|
||||
int tex_name_offset;
|
||||
int tex_flags;
|
||||
int tex_used;
|
||||
|
||||
int unused_1;
|
||||
|
||||
// Originally both mutable void * (changed for portability)
|
||||
mutable int tex_material;
|
||||
mutable int client_material;
|
||||
|
||||
int unused_array[10];
|
||||
};
|
||||
|
||||
|
||||
class MDLReader
|
||||
{
|
||||
protected:
|
||||
|
||||
std::string mdl_name;
|
||||
|
||||
osg::ref_ptr<osg::Node> root_node;
|
||||
|
||||
typedef std::vector<std::string> StringList;
|
||||
StringList texture_paths;
|
||||
|
||||
typedef std::vector< osg::ref_ptr<osg::StateSet> > StateSetList;
|
||||
StateSetList state_sets;
|
||||
|
||||
std::string getToken(std::string str, const char * delim, size_t & index);
|
||||
std::string findFileIgnoreCase(std::string filePath);
|
||||
|
||||
osg::ref_ptr<osg::Texture> readTextureFile(std::string textureName);
|
||||
osg::ref_ptr<osg::StateSet> readMaterialFile(std::string mtlName);
|
||||
|
||||
BodyPart * processBodyPart(std::istream * str, int offset);
|
||||
Model * processModel(std::istream * str, int offset);
|
||||
Mesh * processMesh(std::istream * str, int offset);
|
||||
|
||||
public:
|
||||
|
||||
MDLReader();
|
||||
virtual ~MDLReader();
|
||||
|
||||
bool readFile(const std::string & file);
|
||||
|
||||
osg::ref_ptr<osg::Node> getRootNode();
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
38
src/osgPlugins/mdl/MDLRoot.cpp
Normal file
38
src/osgPlugins/mdl/MDLRoot.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
|
||||
#include "MDLRoot.h"
|
||||
|
||||
|
||||
using namespace mdl;
|
||||
|
||||
|
||||
MDLRoot::MDLRoot()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
MDLRoot::~MDLRoot()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void MDLRoot::addBodyPart(BodyPart * newPart)
|
||||
{
|
||||
// Add the new part to our list
|
||||
body_parts.push_back(newPart);
|
||||
}
|
||||
|
||||
|
||||
int MDLRoot::getNumBodyParts()
|
||||
{
|
||||
return body_parts.size();
|
||||
}
|
||||
|
||||
|
||||
BodyPart * MDLRoot::getBodyPart(int partIndex)
|
||||
{
|
||||
if ((partIndex < 0) || (partIndex >= body_parts.size()))
|
||||
return NULL;
|
||||
else
|
||||
return body_parts[partIndex];
|
||||
}
|
||||
|
||||
37
src/osgPlugins/mdl/MDLRoot.h
Normal file
37
src/osgPlugins/mdl/MDLRoot.h
Normal file
@@ -0,0 +1,37 @@
|
||||
|
||||
#ifndef __MDL_ROOT_H_
|
||||
#define __MDL_ROOT_H_
|
||||
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "BodyPart.h"
|
||||
|
||||
|
||||
namespace mdl
|
||||
{
|
||||
|
||||
|
||||
class MDLRoot
|
||||
{
|
||||
protected:
|
||||
|
||||
typedef std::vector<BodyPart *> BodyPartList;
|
||||
|
||||
BodyPartList body_parts;
|
||||
|
||||
public:
|
||||
|
||||
MDLRoot();
|
||||
virtual ~MDLRoot();
|
||||
|
||||
void addBodyPart(BodyPart * newPart);
|
||||
int getNumBodyParts();
|
||||
BodyPart * getBodyPart(int partIndex);
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
97
src/osgPlugins/mdl/MDL_README.txt
Normal file
97
src/osgPlugins/mdl/MDL_README.txt
Normal file
@@ -0,0 +1,97 @@
|
||||
|
||||
Source Engine MDL reader for OSG
|
||||
|
||||
by Jason Daly
|
||||
|
||||
Overview
|
||||
--------
|
||||
This plugin allows .mdl 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 with dozens of on several HL2 models, as well as some
|
||||
3rd party models.
|
||||
|
||||
|
||||
Using the Plugin
|
||||
----------------
|
||||
If you want to load models from the original Source engine games, you'll need
|
||||
to extract the relevant models 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 models and materials to be arranged as they are in the
|
||||
.gcf files (models in models/ materials and textures in materials/). Only the
|
||||
models/ and materials/ directories are used by this plugin. Note that the
|
||||
.mdl plugin typically will need to read additional data from companion .vvd
|
||||
and .vtx files, as well as textures from other files in the materials/
|
||||
directory.
|
||||
|
||||
It is important to preserve the file and directory structure as it is in the
|
||||
.gcf files, although you can merge the data from several files (different
|
||||
games) together. For example, you can extract the models/ directory from
|
||||
source models.gcf, the materials/ directory from source materials.gcf, and the
|
||||
maps/, models/, and materials/ directories from half-life 2 deathmatch.gcf and
|
||||
combine them together into a single parent directory (called "hl2data/", for
|
||||
example). This arrangement will let you load any of the HL2 deathmatch maps,
|
||||
with all of the props and materials that might be needed.
|
||||
|
||||
If you're confused, here's a lame ASCII art drawing to confuse you even more:
|
||||
|
||||
hl2data
|
||||
|
|
||||
+----maps
|
||||
|
|
||||
+----materials
|
||||
|
|
||||
+----models
|
||||
|
||||
|
||||
If you want to use the OSGFILEPATH environment variable to let OSG search for
|
||||
maps or models, point it to the parent directory ("hl2data" in the example
|
||||
above), then load your model like this:
|
||||
|
||||
osgviewer models/alyx.mdl
|
||||
|
||||
|
||||
What Works
|
||||
----------
|
||||
All geometry and textures.
|
||||
|
||||
Some models have multiple sub-models in them (for example, a door model might
|
||||
have multiple variations of handles). These are supported with a switch,
|
||||
but you'll have to find it and manipulate it yourself (I can't figure out
|
||||
how the Source engine selects which sub-model to show).
|
||||
|
||||
|
||||
What Doesn't Work (yet)
|
||||
-----------------------
|
||||
Skeletons and bone animations. My guess is that this would be possible using
|
||||
osgAnimation, but I didn't have time to do it for the first cut.
|
||||
|
||||
Facial animations. Would require support for vertex morphing (I don't know
|
||||
if osgAnimation provides this yet or not).
|
||||
|
||||
Physics. There is no OSG infrastructure yet, and I can't find much information
|
||||
on the .phy file format that models use for physics calculations. Rag-doll
|
||||
physics obviously doesn't work either.
|
||||
|
||||
Eyes. This is one of the more ugly artifacts you'll see if you load a
|
||||
character model. Only the whites of the eyes are drawn (no iris or pupil).
|
||||
The eyes (as well as the teeth) use special shaders that I didn't have time to
|
||||
figure out. This is ugly enough that I might come back to it soon.
|
||||
|
||||
|
||||
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.
|
||||
|
||||
Portions of the code borrow heavily from the Source SDK. Most of the
|
||||
file format reading came from the header files for studio models, so thanks to
|
||||
Valve for making much of that code public.
|
||||
|
||||
Of course, this code would be pointless without the Open Scene Graph and
|
||||
all of its contributors.
|
||||
|
||||
46
src/osgPlugins/mdl/Mesh.cpp
Normal file
46
src/osgPlugins/mdl/Mesh.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
|
||||
#include "Mesh.h"
|
||||
|
||||
|
||||
using namespace mdl;
|
||||
|
||||
|
||||
Mesh::Mesh(MDLMesh * myMesh)
|
||||
{
|
||||
// Save the mesh information
|
||||
my_mesh = myMesh;
|
||||
|
||||
// Initialize the state set to NULL
|
||||
state_set = NULL;
|
||||
}
|
||||
|
||||
|
||||
Mesh::~Mesh()
|
||||
{
|
||||
// Clean up the associated data
|
||||
delete my_mesh;
|
||||
}
|
||||
|
||||
|
||||
void Mesh::setStateSet(osg::StateSet * stateSet)
|
||||
{
|
||||
state_set = stateSet;
|
||||
}
|
||||
|
||||
|
||||
osg::StateSet * Mesh::getStateSet()
|
||||
{
|
||||
return state_set.get();
|
||||
}
|
||||
|
||||
|
||||
MDLMesh * Mesh::getMesh()
|
||||
{
|
||||
return my_mesh;
|
||||
}
|
||||
|
||||
|
||||
int Mesh::getNumLODVertices(int lodNum)
|
||||
{
|
||||
return my_mesh->vertex_data.num_lod_vertices[lodNum];
|
||||
}
|
||||
76
src/osgPlugins/mdl/Mesh.h
Normal file
76
src/osgPlugins/mdl/Mesh.h
Normal file
@@ -0,0 +1,76 @@
|
||||
|
||||
#ifndef __MESH_H_
|
||||
#define __MESH_H_
|
||||
|
||||
|
||||
#include <osg/Vec3f>
|
||||
#include <osg/StateSet>
|
||||
|
||||
#include "MDLLimits.h"
|
||||
|
||||
|
||||
namespace mdl
|
||||
{
|
||||
|
||||
|
||||
struct MDLMeshVertexData
|
||||
{
|
||||
// Used by the Source engine for cache purposes. This value is allocated
|
||||
// in the file, but no meaningful data is stored there
|
||||
int model_vertex_data_ptr;
|
||||
|
||||
// Indicates the number of vertices used by each LOD of this mesh
|
||||
int num_lod_vertices[MAX_LODS];
|
||||
};
|
||||
|
||||
|
||||
struct MDLMesh
|
||||
{
|
||||
int material_index;
|
||||
int model_index;
|
||||
|
||||
int num_vertices;
|
||||
int vertex_offset;
|
||||
|
||||
int num_flexes;
|
||||
int flex_offset;
|
||||
|
||||
int material_type;
|
||||
int material_param;
|
||||
|
||||
int mesh_id;
|
||||
|
||||
osg::Vec3f mesh_center;
|
||||
|
||||
MDLMeshVertexData vertex_data;
|
||||
|
||||
int unused_array[8];
|
||||
};
|
||||
|
||||
|
||||
class Mesh
|
||||
{
|
||||
protected:
|
||||
|
||||
MDLMesh * my_mesh;
|
||||
|
||||
osg::ref_ptr<osg::StateSet> state_set;
|
||||
|
||||
public:
|
||||
|
||||
Mesh(MDLMesh * myMesh);
|
||||
virtual ~Mesh();
|
||||
|
||||
void setStateSet(osg::StateSet * stateSet);
|
||||
osg::StateSet * getStateSet();
|
||||
|
||||
MDLMesh * getMesh();
|
||||
|
||||
int getNumLODVertices(int lodNum);
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
56
src/osgPlugins/mdl/Model.cpp
Normal file
56
src/osgPlugins/mdl/Model.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
|
||||
#include "Model.h"
|
||||
#include "VVDReader.h"
|
||||
|
||||
|
||||
using namespace mdl;
|
||||
|
||||
|
||||
Model::Model(MDLModel * myModel)
|
||||
{
|
||||
// Save the model information
|
||||
my_model = myModel;
|
||||
}
|
||||
|
||||
|
||||
Model::~Model()
|
||||
{
|
||||
// Clean up the associated data
|
||||
delete my_model;
|
||||
}
|
||||
|
||||
|
||||
MDLModel * Model::getModel()
|
||||
{
|
||||
return my_model;
|
||||
}
|
||||
|
||||
|
||||
int Model::getVertexBase()
|
||||
{
|
||||
// Return the base index for this model's vertices
|
||||
return my_model->vertex_index / sizeof(VVDVertex);
|
||||
}
|
||||
|
||||
|
||||
void Model::addMesh(Mesh * newMesh)
|
||||
{
|
||||
// Add the new node to our list
|
||||
model_meshes.push_back(newMesh);
|
||||
}
|
||||
|
||||
|
||||
int Model::getNumMeshes()
|
||||
{
|
||||
return model_meshes.size();
|
||||
}
|
||||
|
||||
|
||||
Mesh * Model::getMesh(int meshIndex)
|
||||
{
|
||||
if ((meshIndex < 0) || (meshIndex >= model_meshes.size()))
|
||||
return NULL;
|
||||
else
|
||||
return model_meshes[meshIndex];
|
||||
}
|
||||
|
||||
76
src/osgPlugins/mdl/Model.h
Normal file
76
src/osgPlugins/mdl/Model.h
Normal file
@@ -0,0 +1,76 @@
|
||||
|
||||
#ifndef __MODEL_H_
|
||||
#define __MODEL_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "Mesh.h"
|
||||
|
||||
|
||||
namespace mdl
|
||||
{
|
||||
|
||||
|
||||
struct MDLModelVertexData
|
||||
{
|
||||
// No useful values are stored in the file for this structure, but we
|
||||
// need the size to be right so we can properly read subsequent models
|
||||
// from the file
|
||||
int vertex_data_ptr;
|
||||
int tangent_data_ptr;
|
||||
};
|
||||
|
||||
|
||||
struct MDLModel
|
||||
{
|
||||
char model_name[64];
|
||||
int model_type;
|
||||
float bounding_radius;
|
||||
int num_meshes;
|
||||
int mesh_offset;
|
||||
|
||||
int num_vertices;
|
||||
int vertex_index;
|
||||
int tangents_index;
|
||||
|
||||
int num_attachments;
|
||||
int attachment_offset;
|
||||
int num_eyeballs;
|
||||
int eyeball_offset;
|
||||
|
||||
MDLModelVertexData vertex_data;
|
||||
|
||||
int unused_array[8];
|
||||
};
|
||||
|
||||
|
||||
|
||||
class Model
|
||||
{
|
||||
protected:
|
||||
|
||||
typedef std::vector<Mesh *> MeshList;
|
||||
|
||||
MDLModel * my_model;
|
||||
|
||||
MeshList model_meshes;
|
||||
|
||||
public:
|
||||
|
||||
Model(MDLModel * myModel);
|
||||
virtual ~Model();
|
||||
|
||||
MDLModel * getModel();
|
||||
|
||||
int getVertexBase();
|
||||
|
||||
void addMesh(Mesh * newMesh);
|
||||
int getNumMeshes();
|
||||
Mesh * getMesh(int meshIndex);
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
72
src/osgPlugins/mdl/ReaderWriterMDL.cpp
Normal file
72
src/osgPlugins/mdl/ReaderWriterMDL.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#include <osg/Node>
|
||||
#include <osg/Notify>
|
||||
#include <osgDB/Registry>
|
||||
#include <osgDB/FileUtils>
|
||||
#include <osgDB/ReadFile>
|
||||
|
||||
#include "ReaderWriterMDL.h"
|
||||
#include "MDLReader.h"
|
||||
|
||||
using namespace mdl;
|
||||
using namespace osg;
|
||||
using namespace osgDB;
|
||||
|
||||
|
||||
const char* ReaderWriterMDL::className() const
|
||||
{
|
||||
// Return a description of this class
|
||||
return "Valve/Source Engine MDL Reader";
|
||||
}
|
||||
|
||||
|
||||
bool ReaderWriterMDL::acceptsExtension(const std::string& extension) const
|
||||
{
|
||||
// If the extension is empty or "mdl", we accept it
|
||||
return osgDB::equalCaseInsensitive(extension, "mdl") || extension.empty();
|
||||
}
|
||||
|
||||
|
||||
ReaderWriter::ReadResult ReaderWriterMDL::readNode(
|
||||
const std::string& file,
|
||||
const ReaderWriter::Options* options) const
|
||||
{
|
||||
MDLReader * mdlReader;
|
||||
ref_ptr<Node> result;
|
||||
|
||||
// See if we handle this kind of file
|
||||
if (!acceptsExtension(osgDB::getFileExtension(file)))
|
||||
return ReadResult::FILE_NOT_HANDLED;
|
||||
|
||||
// See if we can find the requested file
|
||||
std::string fileName = osgDB::findDataFile(file, options, CASE_INSENSITIVE);
|
||||
if (fileName.empty())
|
||||
return ReadResult::FILE_NOT_FOUND;
|
||||
|
||||
// Read the file (pass the base name and not the file that was found, this
|
||||
// allows us to also find the .vvd and .vtx files without the leading
|
||||
// path confusing things)
|
||||
mdlReader = new MDLReader();
|
||||
if (mdlReader->readFile(file))
|
||||
{
|
||||
// Get the results of our read
|
||||
result = mdlReader->getRootNode();
|
||||
|
||||
// Clean up the reader
|
||||
delete mdlReader;
|
||||
|
||||
// Return the results
|
||||
return ReadResult(result.get());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Clean up the reader
|
||||
delete mdlReader;
|
||||
|
||||
// Return the error
|
||||
return ReadResult::ERROR_IN_READING_FILE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
REGISTER_OSGPLUGIN(mdl, ReaderWriterMDL)
|
||||
|
||||
27
src/osgPlugins/mdl/ReaderWriterMDL.h
Normal file
27
src/osgPlugins/mdl/ReaderWriterMDL.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef __READERWRITER_MDL_H_
|
||||
#define __READERWRITER_MDL_H_
|
||||
|
||||
|
||||
#include <osgDB/Registry>
|
||||
#include <osgDB/FileNameUtils>
|
||||
|
||||
|
||||
namespace mdl
|
||||
{
|
||||
|
||||
class ReaderWriterMDL : public osgDB::ReaderWriter
|
||||
{
|
||||
public:
|
||||
|
||||
virtual const char* className() const;
|
||||
|
||||
virtual bool acceptsExtension(const std::string& extension) const;
|
||||
|
||||
virtual ReadResult readNode(const std::string& file,
|
||||
const Options* options) const;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
422
src/osgPlugins/mdl/VTXReader.cpp
Normal file
422
src/osgPlugins/mdl/VTXReader.cpp
Normal file
@@ -0,0 +1,422 @@
|
||||
#include <osg/Geometry>
|
||||
#include <osg/Group>
|
||||
#include <osg/Node>
|
||||
#include <osg/Notify>
|
||||
#include <osg/StateSet>
|
||||
#include <osg/Switch>
|
||||
#include <iostream>
|
||||
|
||||
#include "VTXReader.h"
|
||||
|
||||
|
||||
using namespace mdl;
|
||||
using namespace osg;
|
||||
using namespace osgDB;
|
||||
|
||||
|
||||
VTXReader::VTXReader(VVDReader * vvd, MDLRoot * mdlRoot)
|
||||
{
|
||||
// Save the VVD reader, as we'll need it to read vertex data
|
||||
vvd_reader = vvd;
|
||||
|
||||
// Save the root of the MDL tree, as we'll need it to make sure we
|
||||
// index the vertex data properly
|
||||
mdl_root = mdlRoot;
|
||||
|
||||
// Initialize the root group
|
||||
model_root = NULL;
|
||||
}
|
||||
|
||||
|
||||
VTXReader::~VTXReader()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
ref_ptr<Group> VTXReader::processBodyPart(std::istream * str, int offset,
|
||||
BodyPart * currentPart)
|
||||
{
|
||||
int i;
|
||||
VTXBodyPart part;
|
||||
Model * currentModel;
|
||||
ref_ptr<Group> partSwitch;
|
||||
ref_ptr<Group> modelGroup;
|
||||
|
||||
// Seek to the body part
|
||||
str->seekg(offset);
|
||||
|
||||
// Read it
|
||||
str->read((char *) &part, sizeof(VTXBodyPart));
|
||||
|
||||
// If there is more than one model, create a switch to select between them
|
||||
// (it seems that only one model is supposed to be seen at a given time,
|
||||
// but I don't know the mechanism in the engine that selects a desired
|
||||
// model)
|
||||
if (part.num_models > 1)
|
||||
{
|
||||
partSwitch = new Switch();
|
||||
}
|
||||
|
||||
// Process the models
|
||||
for (i = 0; i < part.num_models; i++)
|
||||
{
|
||||
// Get the corresponding MDL model from the current body part
|
||||
currentModel = currentPart->getModel(i);
|
||||
|
||||
// Process the model
|
||||
modelGroup = processModel(str,
|
||||
offset + part.model_offset +
|
||||
(i * sizeof(VTXModel)),
|
||||
currentModel);
|
||||
|
||||
// If there is more than one model, add this model to the part group
|
||||
if (part.num_models > 1)
|
||||
{
|
||||
// Add the model to the switch
|
||||
partSwitch->addChild(modelGroup.get());
|
||||
|
||||
// If this is the first child, turn it on, otherwise turn it off
|
||||
if (i == 0)
|
||||
((osg::Switch *)partSwitch.get())->setValue(i, true);
|
||||
else
|
||||
((osg::Switch *)partSwitch.get())->setValue(i, false);
|
||||
}
|
||||
}
|
||||
|
||||
// If there is only one model, just return it
|
||||
if (part.num_models == 1)
|
||||
return modelGroup;
|
||||
else
|
||||
return partSwitch;
|
||||
}
|
||||
|
||||
|
||||
ref_ptr<Group> VTXReader::processModel(std::istream * str, int offset,
|
||||
Model * currentModel)
|
||||
{
|
||||
int i;
|
||||
VTXModel model;
|
||||
float lastDistance;
|
||||
float distance;
|
||||
LOD * lodNode;
|
||||
ref_ptr<Group> group;
|
||||
ref_ptr<Group> result;
|
||||
|
||||
// Seek to the model
|
||||
str->seekg(offset);
|
||||
|
||||
// Read it
|
||||
str->read((char *) &model, sizeof(VTXModel));
|
||||
|
||||
// If we have multiple LODs, create an LOD node for them
|
||||
if (model.num_lods > 1)
|
||||
lodNode = new LOD();
|
||||
|
||||
// Initialize the distances
|
||||
lastDistance = 0.0;
|
||||
distance = 0.0;
|
||||
|
||||
// Process the LODs
|
||||
for (i = 0; i < model.num_lods; i++)
|
||||
{
|
||||
// Process the LOD group, passing the current MDL model through
|
||||
group = processLOD(i, &distance, str,
|
||||
offset + model.lod_offset +
|
||||
(i * sizeof(VTXModelLOD)),
|
||||
currentModel);
|
||||
|
||||
// If this isn't the only LOD, add it to the LOD node
|
||||
if (model.num_lods > 1)
|
||||
{
|
||||
lodNode->addChild(group.get());
|
||||
|
||||
// Fix the LOD distances
|
||||
if (distance < 0)
|
||||
{
|
||||
// Fix negative distance (my best guess is that these mean
|
||||
// for the LOD to never switch out)
|
||||
distance = 100000.0;
|
||||
}
|
||||
|
||||
// Set the ranges on the previous LOD (now that we know the
|
||||
// switch point for this one)
|
||||
if (i > 0)
|
||||
lodNode->setRange(i-1, lastDistance, distance);
|
||||
|
||||
lastDistance = distance;
|
||||
}
|
||||
}
|
||||
|
||||
if (i > 1)
|
||||
lodNode->setRange(i-1, lastDistance, 100000.0);
|
||||
|
||||
// Return either the LOD node or the single LOD group
|
||||
if (model.num_lods > 1)
|
||||
result = lodNode;
|
||||
else
|
||||
result = group;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
ref_ptr<Group> VTXReader::processLOD(int lodNum, float * distance,
|
||||
std::istream * str, int offset,
|
||||
Model * currentModel)
|
||||
{
|
||||
int i;
|
||||
VTXModelLOD lod;
|
||||
Mesh * currentMesh;
|
||||
int vertexOffset;
|
||||
ref_ptr<Group> lodGroup;
|
||||
ref_ptr<Geode> geode;
|
||||
|
||||
// Seek to the LOD
|
||||
str->seekg(offset);
|
||||
|
||||
// Read it
|
||||
str->read((char *) &lod, sizeof(VTXModelLOD));
|
||||
|
||||
// Create a group to hold this LOD
|
||||
lodGroup = new Group();
|
||||
|
||||
// Process the meshes
|
||||
vertexOffset = currentModel->getVertexBase();
|
||||
for (i = 0; i < lod.num_meshes; i++)
|
||||
{
|
||||
// Get the corresponding MDL mesh from the current model
|
||||
currentMesh = currentModel->getMesh(i);
|
||||
|
||||
// Process the mesh to get a geode
|
||||
geode = processMesh(lodNum, str,
|
||||
offset + lod.mesh_offset + (i * VTX_MESH_SIZE),
|
||||
vertexOffset);
|
||||
|
||||
// Set the geode's state set based on the current mesh node's state
|
||||
// set
|
||||
geode->setStateSet(currentMesh->getStateSet());
|
||||
|
||||
// Add the geode to the group
|
||||
lodGroup->addChild(geode.get());
|
||||
|
||||
// Add the number of vertices for this mesh at this LOD to the offset,
|
||||
// so we can start the next mesh at the proper vertex ID
|
||||
vertexOffset += currentMesh->getNumLODVertices(lodNum);
|
||||
}
|
||||
|
||||
// Set the distance for this LOD
|
||||
*distance = lod.switch_point;
|
||||
|
||||
// Return the LOD group that we created
|
||||
return lodGroup;
|
||||
}
|
||||
|
||||
|
||||
ref_ptr<Geode> VTXReader::processMesh(int lodNum, std::istream * str,
|
||||
int offset, int vertexOffset)
|
||||
{
|
||||
int i;
|
||||
VTXMesh mesh;
|
||||
ref_ptr<Geode> geode;
|
||||
ref_ptr<Geometry> geom;
|
||||
|
||||
// Seek to the mesh
|
||||
str->seekg(offset);
|
||||
|
||||
// Read it
|
||||
str->read((char *) &mesh, VTX_MESH_SIZE);
|
||||
|
||||
// Create a geode to hold the geometry
|
||||
geode = new Geode();
|
||||
|
||||
// Process the strip groups
|
||||
for (i = 0; i < mesh.num_strip_groups; i++)
|
||||
{
|
||||
// Process the strip group to get a Geometry
|
||||
geom = processStripGroup(lodNum, str,
|
||||
offset + mesh.strip_group_offset + (i * VTX_STRIP_GROUP_SIZE),
|
||||
vertexOffset);
|
||||
|
||||
// Add the geometry to the geode
|
||||
geode->addDrawable(geom.get());
|
||||
}
|
||||
|
||||
// Return the geode
|
||||
return geode;
|
||||
}
|
||||
|
||||
|
||||
ref_ptr<Geometry> VTXReader::processStripGroup(int lodNum, std::istream * str,
|
||||
int offset, int vertexOffset)
|
||||
{
|
||||
int i;
|
||||
VTXStripGroup stripGroup;
|
||||
VTXVertex vtxVertex;
|
||||
int vertexID;
|
||||
ref_ptr<Vec3Array> vertexArray;
|
||||
ref_ptr<Vec3Array> normalArray;
|
||||
ref_ptr<Vec2Array> texcoordArray;
|
||||
unsigned short index;
|
||||
unsigned short * indexArray;
|
||||
ref_ptr<Geometry> geom;
|
||||
ref_ptr<PrimitiveSet> primSet;
|
||||
|
||||
// Seek to the strip group
|
||||
str->seekg(offset);
|
||||
|
||||
// Read it
|
||||
str->read((char *) &stripGroup, VTX_STRIP_GROUP_SIZE);
|
||||
|
||||
// Create and fill the vertex arrays
|
||||
vertexArray = new Vec3Array();
|
||||
normalArray = new Vec3Array();
|
||||
texcoordArray = new Vec2Array();
|
||||
str->seekg(offset + stripGroup.vertex_offset);
|
||||
for (i = 0; i < stripGroup.num_vertices; i++)
|
||||
{
|
||||
// Get the vertex ID from the strip group
|
||||
str->read((char *) &vtxVertex, VTX_VERTEX_SIZE);
|
||||
vertexID = vtxVertex.orig_mesh_vertex_id + vertexOffset;
|
||||
|
||||
// Get the corresponding vertex, normal, texture coordinates from the
|
||||
// VVD file
|
||||
vertexArray->push_back(vvd_reader->getVertex(lodNum, vertexID));
|
||||
normalArray->push_back(vvd_reader->getNormal(lodNum, vertexID));
|
||||
texcoordArray->push_back(vvd_reader->getTexCoords(lodNum, vertexID));
|
||||
}
|
||||
|
||||
// Create the geometry and add the vertex data to it
|
||||
geom = new Geometry();
|
||||
geom->setVertexArray(vertexArray.get());
|
||||
geom->setNormalArray(normalArray.get());
|
||||
geom->setNormalBinding(Geometry::BIND_PER_VERTEX);
|
||||
geom->setTexCoordArray(0, texcoordArray.get());
|
||||
|
||||
// Create and fill the index array
|
||||
indexArray = new unsigned short[stripGroup.num_indices];
|
||||
str->seekg(offset + stripGroup.index_offset);
|
||||
for (i = 0; i < stripGroup.num_indices; i++)
|
||||
{
|
||||
// Get the index from the file
|
||||
str->read((char *) &index, sizeof(unsigned short));
|
||||
|
||||
// Add to the array
|
||||
indexArray[i] = index;
|
||||
}
|
||||
|
||||
// Process the strips
|
||||
for (i = 0; i < stripGroup.num_strips; i++)
|
||||
{
|
||||
// Process the strip to create a triangle list or strip
|
||||
primSet = processStrip(indexArray, str,
|
||||
offset + stripGroup.strip_offset + (i * VTX_STRIP_SIZE));
|
||||
|
||||
// Add the primitive set to the geometry
|
||||
geom->addPrimitiveSet(primSet.get());
|
||||
}
|
||||
|
||||
// Clean up
|
||||
delete [] indexArray;
|
||||
|
||||
// Return the geometry
|
||||
return geom;
|
||||
}
|
||||
|
||||
|
||||
ref_ptr<PrimitiveSet> VTXReader::processStrip(unsigned short * indexArray,
|
||||
std::istream * str,
|
||||
int offset)
|
||||
{
|
||||
int i;
|
||||
VTXStrip strip;
|
||||
ref_ptr<PrimitiveSet> primSet;
|
||||
unsigned short * start;
|
||||
unsigned short * end;
|
||||
|
||||
// Seek to the strip
|
||||
str->seekg(offset);
|
||||
|
||||
// Read it. We have to do this in a kind of screwy way because of the
|
||||
// weird byte packing. Valve uses pragma pack, but we can't do that
|
||||
// because it's non-portable. Of course, I'm assuming a 4-byte alignment
|
||||
// here, which might also be non-portable...
|
||||
str->read((char *) &strip, VTX_STRIP_SIZE - 8);
|
||||
str->read((char *) &strip.num_bone_state_changes, 8);
|
||||
|
||||
// Get the range of indices in question from the strip
|
||||
start = &indexArray[strip.index_offset];
|
||||
end = &indexArray[strip.index_offset + strip.num_indices];
|
||||
|
||||
// Create the primitive set (based on the flag)
|
||||
if (strip.strip_flags & STRIP_IS_TRI_LIST)
|
||||
primSet =
|
||||
new DrawElementsUShort(PrimitiveSet::TRIANGLES, start, end);
|
||||
else
|
||||
primSet =
|
||||
new DrawElementsUShort(PrimitiveSet::TRIANGLE_STRIP, start, end);
|
||||
|
||||
// Return the primitive set
|
||||
return primSet;
|
||||
}
|
||||
|
||||
|
||||
bool VTXReader::readFile(const std::string & file)
|
||||
{
|
||||
osgDB::ifstream * vtxFile;
|
||||
VTXHeader header;
|
||||
int i, j;
|
||||
int offset;
|
||||
BodyPart * currentPart;
|
||||
ref_ptr<Group> partGroup;
|
||||
Group * rootGroup;
|
||||
|
||||
// Remember the map name
|
||||
vtx_name = getStrippedName(file);
|
||||
|
||||
vtxFile = new osgDB::ifstream(file.c_str(), std::ios::binary);
|
||||
if (!vtxFile || vtxFile->fail())
|
||||
{
|
||||
notify(NOTICE) << "Vertex index file not found" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the header
|
||||
vtxFile->read((char *) &header, sizeof(VTXHeader));
|
||||
|
||||
// Make sure the version is one that we handle
|
||||
// TODO: Not sure which versions are valid yet
|
||||
|
||||
// Create the root group
|
||||
rootGroup = new Group();
|
||||
|
||||
// Process the body parts
|
||||
for (i = 0; i < header.num_body_parts; i++)
|
||||
{
|
||||
// Get the corresponding body part from the MDL tree
|
||||
currentPart = mdl_root->getBodyPart(i);
|
||||
|
||||
// Process the body part
|
||||
partGroup = processBodyPart(vtxFile,
|
||||
header.body_part_offset +
|
||||
(i * sizeof(VTXBodyPart)),
|
||||
currentPart);
|
||||
|
||||
// Add the result to the root group
|
||||
rootGroup->addChild(partGroup.get());
|
||||
}
|
||||
|
||||
// Set the model's root node
|
||||
model_root = rootGroup;
|
||||
|
||||
// Close the file
|
||||
vtxFile->close();
|
||||
delete vtxFile;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
ref_ptr<Node> VTXReader::getModel()
|
||||
{
|
||||
return model_root;
|
||||
}
|
||||
222
src/osgPlugins/mdl/VTXReader.h
Normal file
222
src/osgPlugins/mdl/VTXReader.h
Normal file
@@ -0,0 +1,222 @@
|
||||
#ifndef __VTX_READER_H_
|
||||
#define __VTX_READER_H_
|
||||
|
||||
|
||||
#include <osg/Array>
|
||||
#include <osg/Geometry>
|
||||
#include <osg/Matrixd>
|
||||
#include <osg/Node>
|
||||
#include <osg/Object>
|
||||
#include <osg/StateSet>
|
||||
#include <osgDB/Registry>
|
||||
#include <osgDB/FileNameUtils>
|
||||
#include <osg/Referenced>
|
||||
|
||||
#include "MDLLimits.h"
|
||||
#include "MDLRoot.h"
|
||||
#include "VVDReader.h"
|
||||
|
||||
|
||||
namespace mdl
|
||||
{
|
||||
|
||||
struct VTXHeader
|
||||
{
|
||||
int vtx_version;
|
||||
int vertex_cache_size;
|
||||
unsigned short max_bones_per_strip;
|
||||
unsigned short max_bones_per_tri;
|
||||
int max_bones_per_vertex;
|
||||
|
||||
int check_sum;
|
||||
int num_lods;
|
||||
|
||||
int mtl_replace_list_offset;
|
||||
|
||||
int num_body_parts;
|
||||
int body_part_offset;
|
||||
};
|
||||
|
||||
|
||||
struct VTXMaterialReplacementList
|
||||
{
|
||||
int num_replacements;
|
||||
int replacement_offset;
|
||||
};
|
||||
|
||||
|
||||
struct VTXMaterialReplacment
|
||||
{
|
||||
short material_id;
|
||||
int replacement_material_name_offset;
|
||||
};
|
||||
|
||||
|
||||
struct VTXBodyPart
|
||||
{
|
||||
int num_models;
|
||||
int model_offset;
|
||||
};
|
||||
|
||||
|
||||
struct VTXModel
|
||||
{
|
||||
int num_lods;
|
||||
int lod_offset;
|
||||
};
|
||||
|
||||
|
||||
struct VTXModelLOD
|
||||
{
|
||||
int num_meshes;
|
||||
int mesh_offset;
|
||||
float switch_point;
|
||||
};
|
||||
|
||||
|
||||
enum VTXMeshFlags
|
||||
{
|
||||
MESH_IS_TEETH = 0x01,
|
||||
MESH_IS_EYES = 0x02
|
||||
};
|
||||
|
||||
|
||||
struct VTXMesh
|
||||
{
|
||||
int num_strip_groups;
|
||||
int strip_group_offset;
|
||||
|
||||
unsigned char mesh_flags;
|
||||
};
|
||||
|
||||
// Can't rely on sizeof() because Valve explicitly packs these structures to
|
||||
// 1-byte alignment in the file, which isn't portable
|
||||
const int VTX_MESH_SIZE = 9;
|
||||
|
||||
|
||||
enum VTXStripGroupFlags
|
||||
{
|
||||
STRIP_GROUP_IS_FLEXED = 0x01,
|
||||
STRIP_GROUP_IS_HW_SKINNED = 0x02,
|
||||
STRIP_GROUP_IS_DELTA_FLEXED = 0x04
|
||||
};
|
||||
|
||||
|
||||
struct VTXStripGroup
|
||||
{
|
||||
int num_vertices;
|
||||
int vertex_offset;
|
||||
|
||||
int num_indices;
|
||||
int index_offset;
|
||||
|
||||
int num_strips;
|
||||
int strip_offset;
|
||||
|
||||
unsigned char strip_group_flags;
|
||||
};
|
||||
|
||||
// Can't rely on sizeof() because Valve explicitly packs these structures to
|
||||
// 1-byte alignment in the file, which isn't portable
|
||||
const int VTX_STRIP_GROUP_SIZE = 25;
|
||||
|
||||
|
||||
enum VTXStripFlags
|
||||
{
|
||||
STRIP_IS_TRI_LIST = 0x01,
|
||||
STRIP_IS_TRI_STRIP = 0x02
|
||||
};
|
||||
|
||||
|
||||
struct VTXStrip
|
||||
{
|
||||
int num_indices;
|
||||
int index_offset;
|
||||
|
||||
int num_vertices;
|
||||
int vertex_offset;
|
||||
|
||||
short num_bones;
|
||||
|
||||
unsigned char strip_flags;
|
||||
|
||||
int num_bone_state_changes;
|
||||
int bone_state_change_offset;
|
||||
};
|
||||
|
||||
|
||||
// Can't rely on sizeof() because Valve explicitly packs these structures to
|
||||
// 1-byte alignment in the .vtx file, which isn't portable
|
||||
const int VTX_STRIP_SIZE = 27;
|
||||
|
||||
|
||||
struct VTXVertex
|
||||
{
|
||||
unsigned char bone_weight_index[MAX_BONES_PER_VERTEX];
|
||||
unsigned char num_bones;
|
||||
|
||||
short orig_mesh_vertex_id;
|
||||
|
||||
char bone_id[MAX_BONES_PER_VERTEX];
|
||||
};
|
||||
|
||||
|
||||
// Can't rely on sizeof() because Valve explicitly packs these structures to
|
||||
// 1-byte alignment in the .vtx file, which isn't portable
|
||||
const int VTX_VERTEX_SIZE = 9;
|
||||
|
||||
|
||||
struct VTXBoneStateChange
|
||||
{
|
||||
int hardware_id;
|
||||
int new_bone_id;
|
||||
};
|
||||
|
||||
|
||||
class VTXReader
|
||||
{
|
||||
protected:
|
||||
|
||||
std::string vtx_name;
|
||||
|
||||
VVDReader * vvd_reader;
|
||||
|
||||
MDLRoot * mdl_root;
|
||||
|
||||
osg::ref_ptr<osg::Node> model_root;
|
||||
|
||||
osg::ref_ptr<osg::Group> processBodyPart(std::istream * str,
|
||||
int offset,
|
||||
BodyPart * currentPart);
|
||||
osg::ref_ptr<osg::Group> processModel(std::istream * str,
|
||||
int offset,
|
||||
Model * currentModel);
|
||||
osg::ref_ptr<osg::Group> processLOD(int lodNum, float * distance,
|
||||
std::istream * str,
|
||||
int offset,
|
||||
Model * currentModel);
|
||||
osg::ref_ptr<osg::Geode> processMesh(int lodNum,
|
||||
std::istream * str,
|
||||
int offset, int vertexOffset);
|
||||
osg::ref_ptr<osg::Geometry> processStripGroup(int lodNum,
|
||||
std::istream * str,
|
||||
int offset,
|
||||
int vertexOffset);
|
||||
osg::ref_ptr<osg::PrimitiveSet> processStrip(unsigned short * indexArray,
|
||||
std::istream * str,
|
||||
int offset);
|
||||
|
||||
public:
|
||||
|
||||
VTXReader(VVDReader * vvd, MDLRoot * mdlRoot);
|
||||
virtual ~VTXReader();
|
||||
|
||||
bool readFile(const std::string & file);
|
||||
|
||||
osg::ref_ptr<osg::Node> getModel();
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
161
src/osgPlugins/mdl/VVDReader.cpp
Normal file
161
src/osgPlugins/mdl/VVDReader.cpp
Normal file
@@ -0,0 +1,161 @@
|
||||
#include <osg/BoundingSphere>
|
||||
#include <osg/Geometry>
|
||||
#include <osg/Group>
|
||||
#include <osg/Object>
|
||||
#include <osg/Material>
|
||||
#include <osg/MatrixTransform>
|
||||
#include <osg/Node>
|
||||
#include <osg/Notify>
|
||||
#include <osg/StateSet>
|
||||
#include <osg/Texture1D>
|
||||
#include <osg/Texture2D>
|
||||
#include <osg/Texture3D>
|
||||
#include <osgDB/Registry>
|
||||
#include <osgDB/FileUtils>
|
||||
#include <osgDB/ReadFile>
|
||||
#include <osg/io_utils>
|
||||
#include <iostream>
|
||||
#include <string.h>
|
||||
|
||||
#include "VVDReader.h"
|
||||
|
||||
|
||||
using namespace mdl;
|
||||
using namespace osg;
|
||||
using namespace osgDB;
|
||||
|
||||
|
||||
VVDReader::VVDReader()
|
||||
{
|
||||
// Initialize the vertex buffer arrays
|
||||
memset(vertex_buffer, 0, sizeof(vertex_buffer));
|
||||
memset(vertex_buffer_size, 0, sizeof(vertex_buffer_size));
|
||||
}
|
||||
|
||||
|
||||
VVDReader::~VVDReader()
|
||||
{
|
||||
int i;
|
||||
|
||||
// Clean up the vertex buffer arrays
|
||||
for (i = 0; i < MAX_LODS; i++)
|
||||
delete [] vertex_buffer[i];
|
||||
}
|
||||
|
||||
|
||||
bool VVDReader::readFile(const std::string & file)
|
||||
{
|
||||
osgDB::ifstream * vvdFile;
|
||||
VVDHeader header;
|
||||
int vertIndex;
|
||||
int i, j, k;
|
||||
|
||||
// Remember the map name
|
||||
vvd_name = getStrippedName(file);
|
||||
|
||||
vvdFile = new osgDB::ifstream(file.c_str(), std::ios::binary);
|
||||
if (!vvdFile)
|
||||
{
|
||||
notify(NOTICE) << "Vertex data file not found" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the header
|
||||
memset(&header, 0xcd, sizeof(VVDHeader));
|
||||
vvdFile->read((char *) &header, sizeof(VVDHeader));
|
||||
|
||||
// Make sure the file is a valid Valve VVD file
|
||||
if (header.magic_number != VVD_MAGIC_NUMBER)
|
||||
{
|
||||
notify(NOTICE) << "Vertex data file not valid" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure the version is one that we handle
|
||||
// TODO: Not sure which versions are valid yet
|
||||
|
||||
// Read the fixup table
|
||||
fixup_table = new VVDFixupEntry[header.num_fixups];
|
||||
vvdFile->seekg(header.fixup_table_offset);
|
||||
for (i = 0; i < header.num_fixups; i++)
|
||||
vvdFile->read((char *) &fixup_table[i], sizeof(VVDFixupEntry));
|
||||
|
||||
// Create the vertex buffers
|
||||
for (i = 0; i < header.num_lods; i++)
|
||||
{
|
||||
// Create the vertex buffer for this LOD
|
||||
vertex_buffer[i] = new VVDVertex[header.num_lod_verts[i]];
|
||||
vertex_buffer_size[i] = header.num_lod_verts[i];
|
||||
|
||||
// See if this model needs fixups
|
||||
if (header.num_fixups > 0)
|
||||
{
|
||||
// Scan the fixup table and apply any fixups at this LOD
|
||||
vertIndex = 0;
|
||||
for (j = 0; j < header.num_fixups; j++)
|
||||
{
|
||||
// Skip this entry if the LOD number is lower (more detailed)
|
||||
// than the LOD we're working on
|
||||
if (fixup_table[j].lod_number >= i)
|
||||
{
|
||||
// Seek to the vertex indicated by the fixup table entry
|
||||
vvdFile->seekg(header.vertex_data_offset +
|
||||
fixup_table[j].source_vertex_id *
|
||||
sizeof(VVDVertex));
|
||||
|
||||
// Read the number of vertices specified
|
||||
vvdFile->read((char *) &vertex_buffer[i][vertIndex],
|
||||
fixup_table[j].num_vertices *
|
||||
sizeof(VVDVertex));
|
||||
|
||||
// Advance the target index
|
||||
vertIndex += fixup_table[j].num_vertices;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Seek to the vertex data
|
||||
vvdFile->seekg(header.vertex_data_offset);
|
||||
|
||||
// Just read the vertices directly
|
||||
vvdFile->read((char *) &vertex_buffer[i][0],
|
||||
header.num_lod_verts[i] * sizeof(VVDVertex));
|
||||
}
|
||||
|
||||
// Scale the vertices from inches up to meters
|
||||
for (j = 0; j < vertex_buffer_size[i]; j++)
|
||||
vertex_buffer[i][j].vertex_position *= 0.0254;
|
||||
}
|
||||
|
||||
// Close the file
|
||||
vvdFile->close();
|
||||
delete vvdFile;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int VVDReader::getNumLODVertices(int lod)
|
||||
{
|
||||
return vertex_buffer_size[lod];
|
||||
}
|
||||
|
||||
|
||||
Vec3 VVDReader::getVertex(int lod, int index)
|
||||
{
|
||||
return vertex_buffer[lod][index].vertex_position;
|
||||
}
|
||||
|
||||
|
||||
Vec3 VVDReader::getNormal(int lod, int index)
|
||||
{
|
||||
return vertex_buffer[lod][index].vertex_normal;
|
||||
}
|
||||
|
||||
|
||||
Vec2 VVDReader::getTexCoords(int lod, int index)
|
||||
{
|
||||
return vertex_buffer[lod][index].vertex_texcoord;
|
||||
}
|
||||
|
||||
98
src/osgPlugins/mdl/VVDReader.h
Normal file
98
src/osgPlugins/mdl/VVDReader.h
Normal file
@@ -0,0 +1,98 @@
|
||||
#ifndef __VVD_READER_H_
|
||||
#define __VVD_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>
|
||||
|
||||
#include "MDLLimits.h"
|
||||
|
||||
|
||||
namespace mdl
|
||||
{
|
||||
|
||||
|
||||
// The magic number for a Valve VVD file is 'IDSV' in little-endian
|
||||
// order
|
||||
const int VVD_MAGIC_NUMBER = (('V'<<24)+('S'<<16)+('D'<<8)+'I');
|
||||
|
||||
|
||||
struct VVDHeader
|
||||
{
|
||||
int magic_number;
|
||||
int vvd_version;
|
||||
int check_sum;
|
||||
|
||||
int num_lods;
|
||||
int num_lod_verts[MAX_LODS];
|
||||
|
||||
int num_fixups;
|
||||
int fixup_table_offset;
|
||||
|
||||
int vertex_data_offset;
|
||||
|
||||
int tangent_data_offset;
|
||||
};
|
||||
|
||||
|
||||
struct VVDFixupEntry
|
||||
{
|
||||
int lod_number;
|
||||
|
||||
int source_vertex_id;
|
||||
int num_vertices;
|
||||
};
|
||||
|
||||
|
||||
struct VVDBoneWeight
|
||||
{
|
||||
float weight[MAX_BONES_PER_VERTEX];
|
||||
char bone[MAX_BONES_PER_VERTEX];
|
||||
u_char num_bones;
|
||||
};
|
||||
|
||||
|
||||
struct VVDVertex
|
||||
{
|
||||
VVDBoneWeight bone_weights;
|
||||
osg::Vec3 vertex_position;
|
||||
osg::Vec3 vertex_normal;
|
||||
osg::Vec2 vertex_texcoord;
|
||||
};
|
||||
|
||||
|
||||
class VVDReader
|
||||
{
|
||||
protected:
|
||||
|
||||
std::string vvd_name;
|
||||
|
||||
VVDVertex * vertex_buffer[MAX_LODS];
|
||||
int vertex_buffer_size[MAX_LODS];
|
||||
|
||||
VVDFixupEntry * fixup_table;
|
||||
|
||||
public:
|
||||
|
||||
VVDReader();
|
||||
virtual ~VVDReader();
|
||||
|
||||
bool readFile(const std::string & file);
|
||||
|
||||
int getNumLODVertices(int lod);
|
||||
|
||||
osg::Vec3 getVertex(int lod, int index);
|
||||
osg::Vec3 getNormal(int lod, int index);
|
||||
osg::Vec2 getTexCoords(int lod, int index);
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
6
src/osgPlugins/vtf/CMakeLists.txt
Normal file
6
src/osgPlugins/vtf/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
#this file is automatically generated
|
||||
|
||||
|
||||
SET(TARGET_SRC ReaderWriterVTF.cpp )
|
||||
#### end var setup ###
|
||||
SETUP_PLUGIN(vtf)
|
||||
@@ -5,8 +5,8 @@
|
||||
* DESCRIPTION: Class for reading a Valve Texture Format (VTF) file
|
||||
* into an osg::Image.
|
||||
*
|
||||
* Borrows heavily from Rune Schmidt Jensen's DDS
|
||||
* plugin for OSG, as well as the Valve Source SDK
|
||||
* Borrows heavily from the DDS plugin for OSG, as well
|
||||
* as the Valve Source SDK
|
||||
*
|
||||
* CREATED BY: Jason Daly (jdaly@ist.ucf.edu)
|
||||
*
|
||||
@@ -27,97 +27,97 @@
|
||||
|
||||
enum VTFFlags
|
||||
{
|
||||
VTF_FLAGS_POINTSAMPLE = 0x00000001,
|
||||
VTF_FLAGS_TRILINEAR = 0x00000002,
|
||||
VTF_FLAGS_CLAMP_S = 0x00000004,
|
||||
VTF_FLAGS_CLAMP_T = 0x00000008,
|
||||
VTF_FLAGS_ANISOTROPIC = 0x00000010,
|
||||
VTF_FLAGS_HINT_DXT5 = 0x00000020,
|
||||
VTF_FLAGS_NOCOMPRESS = 0x00000040,
|
||||
VTF_FLAGS_NORMAL = 0x00000080,
|
||||
VTF_FLAGS_NOMIP = 0x00000100,
|
||||
VTF_FLAGS_NOLOD = 0x00000200,
|
||||
VTF_FLAGS_MINMIP = 0x00000400,
|
||||
VTF_FLAGS_PROCEDURAL = 0x00000800,
|
||||
VTF_FLAGS_ONEBITALPHA = 0x00001000,
|
||||
VTF_FLAGS_EIGHTBITALPHA = 0x00002000,
|
||||
VTF_FLAGS_ENVMAP = 0x00004000,
|
||||
VTF_FLAGS_RENDERTARGET = 0x00008000,
|
||||
VTF_FLAGS_DEPTHRENDERTARGET = 0x00010000,
|
||||
VTF_FLAGS_NODEBUGOVERRIDE = 0x00020000,
|
||||
VTF_FLAGS_SINGLECOPY = 0x00040000,
|
||||
VTF_FLAGS_ONEOVERMIPLEVELINALPHA = 0x00080000,
|
||||
VTF_FLAGS_PREMULTCOLORBYONEOVERMIPLEVEL = 0x00100000,
|
||||
VTF_FLAGS_NORMALTODUDV = 0x00200000,
|
||||
VTF_FLAGS_ALPHATESTMIPGENERATION = 0x00400000,
|
||||
VTF_FLAGS_NODEPTHBUFFER = 0x00800000,
|
||||
VTF_FLAGS_NICEFILTERED = 0x01000000,
|
||||
VTF_FLAGS_CLAMP_U = 0x02000000,
|
||||
VTF_FLAGS_PRESWIZZLED = 0x04000000,
|
||||
VTF_FLAGS_CACHEABLE = 0x08000000,
|
||||
VTF_FLAGS_UNFILTERABLE_OK = 0x10000000,
|
||||
VTF_FLAGS_LASTFLAG = 0x10000000
|
||||
VTF_FLAGS_POINTSAMPLE = 0x00000001,
|
||||
VTF_FLAGS_TRILINEAR = 0x00000002,
|
||||
VTF_FLAGS_CLAMP_S = 0x00000004,
|
||||
VTF_FLAGS_CLAMP_T = 0x00000008,
|
||||
VTF_FLAGS_ANISOTROPIC = 0x00000010,
|
||||
VTF_FLAGS_HINT_DXT5 = 0x00000020,
|
||||
VTF_FLAGS_NOCOMPRESS = 0x00000040,
|
||||
VTF_FLAGS_NORMAL = 0x00000080,
|
||||
VTF_FLAGS_NOMIP = 0x00000100,
|
||||
VTF_FLAGS_NOLOD = 0x00000200,
|
||||
VTF_FLAGS_MINMIP = 0x00000400,
|
||||
VTF_FLAGS_PROCEDURAL = 0x00000800,
|
||||
VTF_FLAGS_ONEBITALPHA = 0x00001000,
|
||||
VTF_FLAGS_EIGHTBITALPHA = 0x00002000,
|
||||
VTF_FLAGS_ENVMAP = 0x00004000,
|
||||
VTF_FLAGS_RENDERTARGET = 0x00008000,
|
||||
VTF_FLAGS_DEPTHRENDERTARGET = 0x00010000,
|
||||
VTF_FLAGS_NODEBUGOVERRIDE = 0x00020000,
|
||||
VTF_FLAGS_SINGLECOPY = 0x00040000,
|
||||
VTF_FLAGS_ONEOVERMIPLEVELINALPHA = 0x00080000,
|
||||
VTF_FLAGS_PREMULTCOLORBYONEOVERMIPLEVEL = 0x00100000,
|
||||
VTF_FLAGS_NORMALTODUDV = 0x00200000,
|
||||
VTF_FLAGS_ALPHATESTMIPGENERATION = 0x00400000,
|
||||
VTF_FLAGS_NODEPTHBUFFER = 0x00800000,
|
||||
VTF_FLAGS_NICEFILTERED = 0x01000000,
|
||||
VTF_FLAGS_CLAMP_U = 0x02000000,
|
||||
VTF_FLAGS_PRESWIZZLED = 0x04000000,
|
||||
VTF_FLAGS_CACHEABLE = 0x08000000,
|
||||
VTF_FLAGS_UNFILTERABLE_OK = 0x10000000,
|
||||
VTF_FLAGS_LASTFLAG = 0x10000000
|
||||
};
|
||||
|
||||
|
||||
enum VTFCubeMapFaceIndex
|
||||
{
|
||||
VTF_CUBEMAP_FACE_RIGHT = 0,
|
||||
VTF_CUBEMAP_FACE_LEFT,
|
||||
VTF_CUBEMAP_FACE_BACK,
|
||||
VTF_CUBEMAP_FACE_FRONT,
|
||||
VTF_CUBEMAP_FACE_UP,
|
||||
VTF_CUBEMAP_FACE_DOWN,
|
||||
VTF_CUBEMAP_FACE_SPHEREMAP,
|
||||
VTF_CUBEMAP_FACE_COUNT
|
||||
VTF_CUBEMAP_FACE_RIGHT = 0,
|
||||
VTF_CUBEMAP_FACE_LEFT,
|
||||
VTF_CUBEMAP_FACE_BACK,
|
||||
VTF_CUBEMAP_FACE_FRONT,
|
||||
VTF_CUBEMAP_FACE_UP,
|
||||
VTF_CUBEMAP_FACE_DOWN,
|
||||
VTF_CUBEMAP_FACE_SPHEREMAP,
|
||||
VTF_CUBEMAP_FACE_COUNT
|
||||
};
|
||||
|
||||
|
||||
enum VTFLookDir
|
||||
{
|
||||
VTF_LOOK_DOWN_X = 0,
|
||||
VTF_LOOK_DOWN_NEGX,
|
||||
VTF_LOOK_DOWN_Y = 0,
|
||||
VTF_LOOK_DOWN_NEGY,
|
||||
VTF_LOOK_DOWN_Z = 0,
|
||||
VTF_LOOK_DOWN_NEGZ
|
||||
VTF_LOOK_DOWN_X = 0,
|
||||
VTF_LOOK_DOWN_NEGX,
|
||||
VTF_LOOK_DOWN_Y = 0,
|
||||
VTF_LOOK_DOWN_NEGY,
|
||||
VTF_LOOK_DOWN_Z = 0,
|
||||
VTF_LOOK_DOWN_NEGZ
|
||||
};
|
||||
|
||||
|
||||
enum VTFImageFormat
|
||||
{
|
||||
VTF_FORMAT_UNKNOWN = -1,
|
||||
VTF_FORMAT_RGBA8888 = 0,
|
||||
VTF_FORMAT_ABGR8888,
|
||||
VTF_FORMAT_RGB888,
|
||||
VTF_FORMAT_BGR888,
|
||||
VTF_FORMAT_RGB565,
|
||||
VTF_FORMAT_I8,
|
||||
VTF_FORMAT_IA88,
|
||||
VTF_FORMAT_P8,
|
||||
VTF_FORMAT_A8,
|
||||
VTF_FORMAT_RGB888_BLUESCREEN,
|
||||
VTF_FORMAT_BGR888_BLUESCREEN,
|
||||
VTF_FORMAT_ARGB8888,
|
||||
VTF_FORMAT_BGRA8888,
|
||||
VTF_FORMAT_DXT1,
|
||||
VTF_FORMAT_DXT3,
|
||||
VTF_FORMAT_DXT5,
|
||||
VTF_FORMAT_BGRX8888,
|
||||
VTF_FORMAT_BGR565,
|
||||
VTF_FORMAT_BGRX5551,
|
||||
VTF_FORMAT_BGRA4444,
|
||||
VTF_FORMAT_DXT1_ONEBITALPHA,
|
||||
VTF_FORMAT_BGRA5551,
|
||||
VTF_FORMAT_UV88,
|
||||
VTF_FORMAT_UVWQ8888,
|
||||
VTF_FORMAT_RGBA16161616F,
|
||||
VTF_FORMAT_RGBA16161616,
|
||||
VTF_FORMAT_UVLX8888,
|
||||
VTF_FORMAT_R32F,
|
||||
VTF_FORMAT_RGB323232F,
|
||||
VTF_FORMAT_RGBA32323232F,
|
||||
VTF_NUM_IMAGE_FORMATS
|
||||
VTF_FORMAT_UNKNOWN = -1,
|
||||
VTF_FORMAT_RGBA8888 = 0,
|
||||
VTF_FORMAT_ABGR8888,
|
||||
VTF_FORMAT_RGB888,
|
||||
VTF_FORMAT_BGR888,
|
||||
VTF_FORMAT_RGB565,
|
||||
VTF_FORMAT_I8,
|
||||
VTF_FORMAT_IA88,
|
||||
VTF_FORMAT_P8,
|
||||
VTF_FORMAT_A8,
|
||||
VTF_FORMAT_RGB888_BLUESCREEN,
|
||||
VTF_FORMAT_BGR888_BLUESCREEN,
|
||||
VTF_FORMAT_ARGB8888,
|
||||
VTF_FORMAT_BGRA8888,
|
||||
VTF_FORMAT_DXT1,
|
||||
VTF_FORMAT_DXT3,
|
||||
VTF_FORMAT_DXT5,
|
||||
VTF_FORMAT_BGRX8888,
|
||||
VTF_FORMAT_BGR565,
|
||||
VTF_FORMAT_BGRX5551,
|
||||
VTF_FORMAT_BGRA4444,
|
||||
VTF_FORMAT_DXT1_ONEBITALPHA,
|
||||
VTF_FORMAT_BGRA5551,
|
||||
VTF_FORMAT_UV88,
|
||||
VTF_FORMAT_UVWQ8888,
|
||||
VTF_FORMAT_RGBA16161616F,
|
||||
VTF_FORMAT_RGBA16161616,
|
||||
VTF_FORMAT_UVLX8888,
|
||||
VTF_FORMAT_R32F,
|
||||
VTF_FORMAT_RGB323232F,
|
||||
VTF_FORMAT_RGBA32323232F,
|
||||
VTF_NUM_IMAGE_FORMATS
|
||||
};
|
||||
|
||||
|
||||
@@ -126,27 +126,27 @@ enum VTFImageFormat
|
||||
|
||||
struct VTFFileHeader
|
||||
{
|
||||
char magic_number[4];
|
||||
unsigned int file_version[2];
|
||||
unsigned int header_size;
|
||||
unsigned short image_width;
|
||||
unsigned short image_height;
|
||||
unsigned int image_flags;
|
||||
unsigned short num_frames;
|
||||
unsigned short start_frame;
|
||||
char magic_number[4];
|
||||
unsigned int file_version[2];
|
||||
unsigned int header_size;
|
||||
unsigned short image_width;
|
||||
unsigned short image_height;
|
||||
unsigned int image_flags;
|
||||
unsigned short num_frames;
|
||||
unsigned short start_frame;
|
||||
|
||||
unsigned char padding_0[4];
|
||||
osg::Vec3f reflectivity_value;
|
||||
unsigned char padding_1[4];
|
||||
unsigned char padding_0[4];
|
||||
osg::Vec3f reflectivity_value;
|
||||
unsigned char padding_1[4];
|
||||
|
||||
float bump_scale;
|
||||
unsigned int image_format;
|
||||
unsigned char num_mip_levels;
|
||||
unsigned char low_res_image_format;
|
||||
unsigned char padding_2[3];
|
||||
unsigned char low_res_image_width;
|
||||
unsigned char low_res_image_height;
|
||||
unsigned short image_depth;
|
||||
float bump_scale;
|
||||
unsigned int image_format;
|
||||
unsigned char num_mip_levels;
|
||||
unsigned char low_res_image_format;
|
||||
unsigned char padding_2[3];
|
||||
unsigned char low_res_image_width;
|
||||
unsigned char low_res_image_height;
|
||||
unsigned short image_depth;
|
||||
};
|
||||
|
||||
|
||||
@@ -357,6 +357,7 @@ osg::Image* ReadVTFFile(std::istream& _istream)
|
||||
int s, t, r;
|
||||
unsigned int lrSize;
|
||||
unsigned char * imageData;
|
||||
unsigned char * imageDataPtr;
|
||||
unsigned int base;
|
||||
unsigned int size;
|
||||
int mip;
|
||||
@@ -395,12 +396,53 @@ osg::Image* ReadVTFFile(std::istream& _istream)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
osg::notify(osg::INFO) << "VTF Header: (" << sizeof(VTFFileHeader);
|
||||
osg::notify(osg::INFO) << " bytes)" << std::endl;
|
||||
osg::notify(osg::INFO) << " magic_number = ";
|
||||
osg::notify(osg::INFO) << vtf_header.magic_number[0];
|
||||
osg::notify(osg::INFO) << vtf_header.magic_number[1];
|
||||
osg::notify(osg::INFO) << vtf_header.magic_number[2];
|
||||
osg::notify(osg::INFO) << vtf_header.magic_number[3] << std:: endl;
|
||||
osg::notify(osg::INFO) << " file_version = ";
|
||||
osg::notify(osg::INFO) << vtf_header.file_version[0] << ".";
|
||||
osg::notify(osg::INFO) << vtf_header.file_version[1] << std:: endl;
|
||||
osg::notify(osg::INFO) << " header_size = ";
|
||||
osg::notify(osg::INFO) << vtf_header.header_size << std::endl;
|
||||
osg::notify(osg::INFO) << " image_width = ";
|
||||
osg::notify(osg::INFO) << vtf_header.image_width << std::endl;
|
||||
osg::notify(osg::INFO) << " image_height = ";
|
||||
osg::notify(osg::INFO) << vtf_header.image_height << std::endl;
|
||||
osg::notify(osg::INFO) << " num_frames = ";
|
||||
osg::notify(osg::INFO) << vtf_header.num_frames << std::endl;
|
||||
osg::notify(osg::INFO) << " start_frame = ";
|
||||
osg::notify(osg::INFO) << vtf_header.start_frame << std::endl;
|
||||
osg::notify(osg::INFO) << " reflectivity = ";
|
||||
osg::notify(osg::INFO) << vtf_header.reflectivity_value.x() << ", ";
|
||||
osg::notify(osg::INFO) << vtf_header.reflectivity_value.y() << ", ";
|
||||
osg::notify(osg::INFO) << vtf_header.reflectivity_value.z() << std::endl;
|
||||
osg::notify(osg::INFO) << " bump_scale = ";
|
||||
osg::notify(osg::INFO) << vtf_header.bump_scale << std::endl;
|
||||
osg::notify(osg::INFO) << " image_format = ";
|
||||
osg::notify(osg::INFO) << vtf_header.image_format << std::endl;
|
||||
osg::notify(osg::INFO) << " num_mip_lvls = ";
|
||||
osg::notify(osg::INFO) << (int)vtf_header.num_mip_levels << std::endl;
|
||||
osg::notify(osg::INFO) << " lr_image_fmt = ";
|
||||
osg::notify(osg::INFO) << (int)vtf_header.low_res_image_format << std::endl;
|
||||
osg::notify(osg::INFO) << " lr_width = ";
|
||||
osg::notify(osg::INFO) << (int)vtf_header.low_res_image_width << std::endl;
|
||||
osg::notify(osg::INFO) << " lr_height = ";
|
||||
osg::notify(osg::INFO) << (int)vtf_header.low_res_image_height << std::endl;
|
||||
osg::notify(osg::INFO) << " image_depth = ";
|
||||
osg::notify(osg::INFO) << (int)vtf_header.image_depth << std::endl;
|
||||
|
||||
// Before we get to the real image, we need to skip over the "low res"
|
||||
// image that's often stored along with VTF textures, so get the
|
||||
// low-res image dimensions
|
||||
s = vtf_header.low_res_image_width;
|
||||
t = vtf_header.low_res_image_height;
|
||||
r = 1;
|
||||
osg::notify(osg::INFO) << "Low-res s = " << s << std::endl;
|
||||
osg::notify(osg::INFO) << "Low-res t = " << t << std::endl;
|
||||
|
||||
// See if the low-res image is there
|
||||
lrSize = 0;
|
||||
@@ -428,6 +470,7 @@ osg::Image* ReadVTFFile(std::istream& _istream)
|
||||
lrSize = loResImage->getTotalSizeInBytes();
|
||||
|
||||
// Skip over the low-res image data
|
||||
osg::notify(osg::INFO) << "Low-res size = " << lrSize << std::endl;
|
||||
_istream.ignore(lrSize);
|
||||
}
|
||||
|
||||
@@ -525,7 +568,7 @@ osg::Image* ReadVTFFile(std::istream& _istream)
|
||||
if (height == 0)
|
||||
height = 1;
|
||||
if (depth == 0)
|
||||
height = 1;
|
||||
depth = 1;
|
||||
|
||||
// Compute and store the offset into the final image data
|
||||
offset += depth * height *
|
||||
@@ -551,10 +594,11 @@ osg::Image* ReadVTFFile(std::istream& _istream)
|
||||
osgImage->setImage(s, t, r, internalFormat, pixelFormat, dataType,
|
||||
0, osg::Image::USE_NEW_DELETE);
|
||||
if (mipmaps.size() > 0)
|
||||
osgImage->setMipmapLevels(mipmaps);
|
||||
osgImage->setMipmapLevels(mipmaps);
|
||||
|
||||
// Compute the total image size
|
||||
size = osgImage->getTotalSizeInBytesIncludingMipmaps();
|
||||
osg::notify(osg::INFO) << "ReadVTFFile info : size = " << size << std::endl;
|
||||
if(size <= 0)
|
||||
{
|
||||
osg::notify(osg::WARN) << "ReadVTFFile warning: size <= 0" << std::endl;
|
||||
@@ -602,6 +646,30 @@ osg::Image* ReadVTFFile(std::istream& _istream)
|
||||
_istream.read((char*)imageData, size);
|
||||
}
|
||||
|
||||
/*
|
||||
// Check if alpha information embedded in the 8-byte encoding blocks
|
||||
if (checkIfUsingOneBitAlpha)
|
||||
{
|
||||
const DXT1TexelsBlock *texelsBlock =
|
||||
reinterpret_cast<const DXT1TexelsBlock*>(imageData);
|
||||
|
||||
// Only do the check on the first mipmap level
|
||||
unsigned int numBlocks = mipmaps.size()>0 ? mipmaps[0] / 8 : size / 8;
|
||||
|
||||
for (int i=numBlocks; i>0; --i, ++texelsBlock)
|
||||
{
|
||||
if (texelsBlock->color_0<=texelsBlock->color_1)
|
||||
{
|
||||
// Texture is using the 1-bit alpha encoding, so we need to
|
||||
// update the assumed pixel format
|
||||
internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
|
||||
pixelFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// Now, set the actual image data and mipmap levels
|
||||
osgImage->setImage(s,t,r, internalFormat, pixelFormat, dataType,
|
||||
imageData, osg::Image::USE_NEW_DELETE);
|
||||
@@ -612,6 +680,13 @@ osg::Image* ReadVTFFile(std::istream& _istream)
|
||||
}
|
||||
|
||||
|
||||
bool WriteVTFFile(const osg::Image *img, std::ostream& fout)
|
||||
{
|
||||
// Not supported
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
class ReaderWriterVTF : public osgDB::ReaderWriter
|
||||
{
|
||||
public:
|
||||
@@ -625,22 +700,17 @@ public:
|
||||
return osgDB::equalCaseInsensitive(extension, "vtf");
|
||||
}
|
||||
|
||||
virtual ReadResult readObject(
|
||||
const std::string& file,
|
||||
const osgDB::ReaderWriter::Options* options) const
|
||||
virtual ReadResult readObject(const std::string& file, const osgDB::ReaderWriter::Options* options) const
|
||||
{
|
||||
return readImage(file,options);
|
||||
}
|
||||
|
||||
virtual ReadResult readObject(std::istream& fin,
|
||||
const Options* options) const
|
||||
virtual ReadResult readObject(std::istream& fin, const Options* options) const
|
||||
{
|
||||
return readImage(fin,options);
|
||||
}
|
||||
|
||||
virtual ReadResult readImage(
|
||||
const std::string& file,
|
||||
const osgDB::ReaderWriter::Options* options) const
|
||||
virtual ReadResult readImage(const std::string& file, const osgDB::ReaderWriter::Options* options) const
|
||||
{
|
||||
std::string ext = osgDB::getLowerCaseFileExtension(file);
|
||||
if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
|
||||
@@ -649,22 +719,19 @@ public:
|
||||
|
||||
if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;
|
||||
|
||||
osgDB::ifstream stream(fileName.c_str(),
|
||||
std::ios::in | std::ios::binary);
|
||||
std::ifstream stream(fileName.c_str(), std::ios::in | std::ios::binary);
|
||||
if(!stream) return ReadResult::FILE_NOT_HANDLED;
|
||||
ReadResult rr = readImage(stream, options);
|
||||
if(rr.validImage()) rr.getImage()->setFileName(file);
|
||||
return rr;
|
||||
}
|
||||
|
||||
virtual ReadResult readImage(std::istream& fin,
|
||||
const Options* options) const
|
||||
virtual ReadResult readImage(std::istream& fin, const Options* options) const
|
||||
{
|
||||
osg::Image* osgImage = ReadVTFFile(fin);
|
||||
if (osgImage==NULL) return ReadResult::FILE_NOT_HANDLED;
|
||||
|
||||
if (options &&
|
||||
options->getOptionString().find("vtf_flip")!=std::string::npos)
|
||||
if (options && options->getOptionString().find("vtf_flip")!=std::string::npos)
|
||||
{
|
||||
osgImage->flipVertical();
|
||||
}
|
||||
@@ -672,10 +739,7 @@ public:
|
||||
return osgImage;
|
||||
}
|
||||
|
||||
virtual WriteResult writeObject(
|
||||
const osg::Object& object,
|
||||
const std::string& file,
|
||||
const osgDB::ReaderWriter::Options* options) const
|
||||
virtual WriteResult writeObject(const osg::Object& object,const std::string& file, const osgDB::ReaderWriter::Options* options) const
|
||||
{
|
||||
const osg::Image* image = dynamic_cast<const osg::Image*>(&object);
|
||||
if (!image) return WriteResult::FILE_NOT_HANDLED;
|
||||
@@ -683,26 +747,34 @@ public:
|
||||
return writeImage(*image,file,options);
|
||||
}
|
||||
|
||||
virtual WriteResult writeObject(const osg::Object& object,
|
||||
std::ostream& fout,
|
||||
const Options* options) const
|
||||
virtual WriteResult writeObject(const osg::Object& object,std::ostream& fout,const Options* options) const
|
||||
{
|
||||
return WriteResult::FILE_NOT_HANDLED;
|
||||
const osg::Image* image = dynamic_cast<const osg::Image*>(&object);
|
||||
if (!image) return WriteResult::FILE_NOT_HANDLED;
|
||||
|
||||
return writeImage(*image,fout,options);
|
||||
}
|
||||
|
||||
|
||||
virtual WriteResult writeImage(
|
||||
const osg::Image &image,
|
||||
const std::string& file,
|
||||
const osgDB::ReaderWriter::Options* options) const
|
||||
virtual WriteResult writeImage(const osg::Image &image,const std::string& file, const osgDB::ReaderWriter::Options* options) const
|
||||
{
|
||||
return WriteResult::FILE_NOT_HANDLED;
|
||||
std::string ext = osgDB::getFileExtension(file);
|
||||
if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED;
|
||||
|
||||
std::ofstream fout(file.c_str(), std::ios::out | std::ios::binary);
|
||||
if(!fout) return WriteResult::ERROR_IN_WRITING_FILE;
|
||||
|
||||
return writeImage(image,fout,options);
|
||||
}
|
||||
|
||||
virtual WriteResult writeImage(const osg::Image& image,
|
||||
std::ostream& fout,const Options*) const
|
||||
virtual WriteResult writeImage(const osg::Image& image,std::ostream& fout,const Options*) const
|
||||
{
|
||||
return WriteResult::FILE_NOT_HANDLED;
|
||||
bool success = WriteVTFFile(&image, fout);
|
||||
|
||||
if(success)
|
||||
return WriteResult::FILE_SAVED;
|
||||
else
|
||||
return WriteResult::ERROR_IN_WRITING_FILE;
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user