Files
OpenSceneGraph/src/osgPlugins/bsp/VBSPEntity.cpp
Robert Osfield 6a67be2e32 Introduced CMake option OSG_PROVIDE_READFILE option that defaults to ON, but when switched to OFF disables the building of the osgDB::read*File() methods,
forcing users to use osgDB::readRef*File() methods.  The later is preferable as it closes a potential threading bug when using paging databases in conjunction
with the osgDB::Registry Object Cache.  This threading bug occurs when one thread gets an object from the Cache via an osgDB::read*File() call where only
a pointer to the object is passed back, so taking a reference to the object is delayed till it gets reassigned to a ref_ptr<>, but at the same time another
thread calls a flush of the Object Cache deleting this object as it's referenceCount is now zero.  Using osgDB::readREf*File() makes sure the a ref_ptr<> is
passed back and the referenceCount never goes to zero.

To ensure the OSG builds when OSG_PROVIDE_READFILE is to OFF the many cases of osgDB::read*File() usage had to be replaced with a ref_ptr<> osgDB::readRef*File()
usage.  The avoid this change causing lots of other client code to be rewritten to handle the use of ref_ptr<> in place of C pointer I introduced a serious of
templte methods in various class to adapt ref_ptr<> to the underly C pointer to be passed to old OSG API's, example of this is found in include/osg/Group:

    bool addChild(Node* child); // old method which can only be used with a Node*

    tempalte<class T> bool addChild(const osg::ref_ptr<T>& child) { return addChild(child.get()); } // adapter template method

These changes together cover 149 modified files, so it's a large submission. This extent of changes are warrent to make use of the Object Cache
and multi-threaded loaded more robust.



git-svn-id: http://svn.openscenegraph.org/osg/OpenSceneGraph/branches/OpenSceneGraph-3.4@15165 16af8721-9629-0410-8352-f15c8da7e697
2015-10-22 14:14:53 +00:00

607 lines
18 KiB
C++

#include "VBSPEntity.h"
#include "VBSPGeometry.h"
#include <osg/MatrixTransform>
#include <osgDB/ReadFile>
#include <osgDB/FileUtils>
#include <osgDB/FileNameUtils>
#include <iostream>
#include <sstream>
#include <stdlib.h>
#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)
{
float x, y, z;
// Look for the first non-whitespace
std::string::size_type start = str.find_first_not_of(" \t\r\n", 0);
// Look for the first whitespace after this
std::string::size_type end = str.find_first_of(" \t\r\n", start);
if ((end > start) && (start != std::string::npos))
x = osg::asciiToFloat(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 = osg::asciiToFloat(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 = osg::asciiToFloat(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)
{
std::string::size_type end = std::string::npos;
std::string token;
// Look for the first quotation mark
std::string::size_type 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
class_name = (*param).second;
if (class_name.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 (class_name.compare(0, 3, "env") == 0)
{
// This is an environmental effect (such as a fire or dust cloud)
entity_class = ENTITY_ENV;
processEnv();
}
else if ((class_name.compare("func_brush") == 0) ||
(class_name.compare("func_illusionary") == 0) ||
(class_name.compare("func_wall_toggle") == 0) ||
(class_name.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 (class_name.compare(0, 4, "prop") == 0)
{
// This is a "prop", an external model placed somewhere in the
// scene
entity_class = ENTITY_PROP;
processProp();
}
else if (class_name.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 (class_name.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;
std::stringstream groupName;
// 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());
}
}
}
// Name the entity group
groupName << class_name << ":" << entity_model_index;
entityGroup->setName(groupName.str());
// 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::readRefNodeFile(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());
// Set the group's name
entityGroup->setName(class_name + std::string(":") + entity_model);
}
else
{
OSG_WARN << "Couldn't find prop \"" << entity_model << "\".";
OSG_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;
}