root/OpenSceneGraph/trunk/src/osgPlugins/bsp/VBSPEntity.cpp @ 10415

Revision 10415, 17.9 kB (checked in by robert, 5 years ago)

From Mathias Froehlich, "We are currently getting issues with locale settings and some osg plugins.
Therefore I have changed all the occurances of atof by asciiToFloat or
asciiToDouble.

I believe that it is safe to do so at least for all the plugins.
Included here are also asciiToFloat conversion of environment variables. One
might argue that these should be locale dependent. But IMO these should be
set and interpreted by osg independent of the current locale.
"

Line 
1
2#include "VBSPEntity.h"
3#include "VBSPGeometry.h"
4
5#include <osg/MatrixTransform>
6#include <osgDB/ReadFile>
7#include <osgDB/FileUtils>
8#include <osgDB/FileNameUtils>
9#include <iostream>
10#include <sstream>
11#include <stdlib.h>
12#include <string.h>
13
14
15using namespace bsp;
16using namespace osg;
17using namespace osgDB;
18
19
20// strcasecmp for MSVC
21#ifdef _MSC_VER
22    #define strcasecmp  _stricmp
23#endif
24
25
26VBSPEntity::VBSPEntity(std::string & entityText, VBSPData * bspData)
27{
28    // Save a handle to the bsp data, as we'll need this to construct the
29    // entity
30    bsp_data = bspData;
31
32    // Assume we're not visible at first
33    entity_visible = false;
34
35    // Assume no transform
36    entity_transformed = false;
37
38    // No model (external or internal) yet
39    entity_model_index = -1;
40    entity_model.clear();
41
42    // Don't know the class yet
43    entity_class = ENTITY_OTHER;
44
45    // Parse the entity's text to gather parameters
46    parseParameters(entityText);
47}
48
49
50VBSPEntity::~VBSPEntity()
51{
52}
53
54
55void VBSPEntity::processWorldSpawn()
56{
57    // World spawn is definitely visible
58    entity_visible = true;
59
60    // World spawn is always centered at the origin, so there's no need for
61    // a transform
62    entity_transformed = false;
63
64    // The world spawn's internal model index is always zero
65    entity_model_index = 0;
66}
67
68
69void VBSPEntity::processEnv()
70{
71    // We don't support these entities yet, so leave them invisible
72}
73
74
75void VBSPEntity::processFuncBrush()
76{
77    // These entities are usually transformed
78    entity_transformed = true;
79
80    // Get the internal model index for this entity
81    EntityParameters::iterator param = entity_parameters.find("model");
82    if (param != entity_parameters.end())
83    {
84        // Get the model number
85        std::string value = (*param).second;
86
87        // Skip the leading asterisk (internal models are denoted with a
88        // leading asterisk), and then parse the model number
89        if (value[0] == '*')
90        {
91            value = value.substr(1, std::string::npos);
92            entity_model_index = atoi(value.c_str());
93
94            // Make the entity visible
95            entity_visible = true;
96        }
97        else
98        {
99            // This shouldn't happen (brush entities don't reference
100            // external models).  Leave the entity invisible in this case
101            entity_visible = false;
102        }
103    }
104    else
105    {
106        // We can't locate the model for this entity, so leave it invisible
107        entity_visible = false;
108    }
109
110    // Get the origin and angles for this entity
111    param = entity_parameters.find("origin");
112    if (param != entity_parameters.end())
113    {
114        // Get the origin parameter's value
115        std::string value = (*param).second;
116
117        // Parse the value into a vector
118        entity_origin = getVector(value);
119    }
120    param = entity_parameters.find("angles");
121    if (param != entity_parameters.end())
122    {
123        // Get the origin parameter's value
124        std::string value = (*param).second;
125
126        // Parse the value into a vector
127        entity_angles = getVector(value);
128    }
129}
130
131
132void VBSPEntity::processProp()
133{
134    // These entities are visible
135    entity_visible = true;
136
137    // These entities are usually transformed
138    entity_transformed = true;
139
140    // Get the model we need to load for this entity
141    EntityParameters::iterator param = entity_parameters.find("model");
142    if (param != entity_parameters.end())
143    {
144        // Get the model parameter's value
145        entity_model = (*param).second;
146    }
147
148    // Get the origin and angles for this entity
149    param = entity_parameters.find("origin");
150    if (param != entity_parameters.end())
151    {
152        // Get the origin parameter's value
153        std::string value = (*param).second;
154
155        // Parse the value into a vector
156        entity_origin = getVector(value);
157    }
158    param = entity_parameters.find("angles");
159    if (param != entity_parameters.end())
160    {
161        // Get the origin parameter's value
162        std::string value = (*param).second;
163
164        // Parse the value into a vector
165        entity_angles = getVector(value);
166    }
167}
168
169
170void VBSPEntity::processInfoDecal()
171{
172    // We don't support these entities yet, so leave them invisible
173}
174
175
176void VBSPEntity::processItem()
177{
178    // We don't support these entities yet, so leave them invisible
179}
180
181
182Vec3f VBSPEntity::getVector(std::string str)
183{
184    float x, y, z;
185
186    // Look for the first non-whitespace
187    std::string::size_type start = str.find_first_not_of(" \t\r\n", 0);
188
189    // Look for the first whitespace after this
190    std::string::size_type end = str.find_first_of(" \t\r\n", start);
191
192    if ((end > start) && (start != std::string::npos))
193        x = osg::asciiToFloat(str.substr(start, end-start).c_str());
194    else
195        return Vec3f();
196
197    // Look for the next non-whitespace
198    start = str.find_first_not_of(" \t\r\n", end+1);
199
200    // Look for the first whitespace after this
201    end = str.find_first_of(" \t\r\n", start);
202
203    if ((end > start) && (start != std::string::npos))
204        y = osg::asciiToFloat(str.substr(start, end-start).c_str());
205    else
206        return Vec3f();
207
208    // Look for the next non-whitespace
209    start = str.find_first_not_of(" \t\r\n", end+1);
210
211    // Look for the first whitespace after this
212    end = str.find_first_of(" \t\r\n", start);
213    if (end == std::string::npos)
214        end = str.length();
215
216    if ((end > start) && (start != std::string::npos))
217        z = osg::asciiToFloat(str.substr(start, end-start).c_str());
218    else
219        return Vec3f();
220
221    // If we get this far, return the vector that we parsed
222    return Vec3f(x, y, z);
223}
224
225
226std::string VBSPEntity::getToken(std::string str, size_t & index)
227{
228    std::string::size_type end = std::string::npos;
229    std::string   token;
230
231    // Look for the first quotation mark
232    std::string::size_type start = str.find_first_of("\"", index);
233    if (start != std::string::npos)
234    {
235        // From there, look for the next occurrence of a delimiter
236        start++;
237        end = str.find_first_of("\"", start);
238        if (end != std::string::npos)
239        {
240            // Found a delimiter, so grab the string in between
241            token = str.substr(start, end-start);
242        }
243        else
244        {
245            // Ran off the end of the string, so just grab everything from
246            // the first good character
247            token = str.substr(start);
248        }
249    }
250    else
251    {
252        // No token to be found
253        token.clear();
254    }
255
256    // Update the index (in case we want to keep looking for tokens in this
257    // string)
258    if (end != std::string::npos)
259        index = end+1;
260    else
261        index = std::string::npos;
262
263    // Return the token
264    return token;
265}
266
267
268void VBSPEntity::parseParameters(std::string & entityText)
269{
270    // Create a string stream on the entity text
271    std::istringstream str(entityText, std::istringstream::in);
272
273    // Iterate over the parameters
274    while (!str.eof())
275    {
276        // Get the next line of text
277        std::string line;
278        std::getline(str, line);
279
280        // Look for the first quotation mark on the line
281        size_t start = 0;
282        std::string token = getToken(line, start);
283
284        // If we have a valid token it will be the parameter name (the key),
285        // look for a second token, which will be the parameter's value
286        while (!token.empty())
287        {
288            // Save the token as the key
289            std::string key = token;
290
291            // Get the next token
292            start++;
293            token = getToken(line, start);
294
295            // See if the token is valid
296            if (!token.empty())
297            {
298                // This token is the value, create an entity parameter from
299                // these two strings and add it to our parameters map
300                EntityParameter param(key, token);
301                entity_parameters.insert(param);
302            }
303        }
304    }
305
306    // Now that we have all of the parameters, figure out what kind of entity
307    // this is
308    EntityParameters::iterator param = entity_parameters.find("classname");
309
310    // See if we found the class
311    if (param == entity_parameters.end())
312    {
313        // We need the class to be able to do anything with this entity
314        return;
315    }
316
317    // Get the class name and process the entity appropriately
318    std::string className = (*param).second;
319    if (className.compare("worldspawn") == 0)
320    {
321        // This is the entity that represents the main geometry of the map
322        // (the terrain and much of the static geometry)
323        entity_class = ENTITY_WORLDSPAWN;
324        processWorldSpawn();
325    }
326    else if (className.compare(0, 3, "env") == 0)
327    {
328        // This is an environmental effect (such as a fire or dust cloud)
329        entity_class = ENTITY_ENV;
330        processEnv();
331    }
332    else if ((className.compare("func_brush") == 0) ||
333             (className.compare("func_illusionary") == 0) ||
334             (className.compare("func_wall_toggle") == 0) ||
335             (className.compare("func_breakable") == 0))
336    {
337        // This is secondary map geometry, created along with the main
338        // map geometry (not an external model)
339        entity_class = ENTITY_FUNC_BRUSH;
340        processFuncBrush();
341    }
342    else if (className.compare(0, 4, "prop") == 0)
343    {
344        // This is a "prop", an external model placed somewhere in the
345        // scene
346        entity_class = ENTITY_PROP;
347        processProp();
348    }
349    else if (className.compare("infodecal") == 0)
350    {
351        // This is a decal, which applies a texture to some surface in the
352        // scene
353        entity_class = ENTITY_INFO_DECAL;
354        processInfoDecal();
355    }
356    else if (className.compare(0, 4, "item") == 0)
357    {
358        // This is an "item".  Like a prop, these are external models
359        // placed in the scene, but the specific model is determined
360        // directly by the entity's class.  In HL2, these entities are
361        // useable by the player (ammunition and health packs are examples)
362        entity_class = ENTITY_ITEM;
363        processItem();
364    }
365}
366
367
368ref_ptr<Group> VBSPEntity::createBrushGeometry()
369{
370    int                i;
371    int                numGeoms;
372    VBSPGeometry **    vbspGeomList;
373    Model              currentModel;
374    Face               currentFace;
375    TexInfo            currentTexInfo;
376    TexData            currentTexData;
377    const char *       texName;
378    char               currentTexName[256];
379    int                currentGeomIndex;
380    VBSPGeometry *     currentGeom;
381    ref_ptr<Group>     entityGroup;
382    ref_ptr<Group>     geomGroup;
383
384    // Create a list of VBSPGeometry objects for each texdata entry in the
385    // scene.  These objects will hold the necessary geometry data until we
386    // convert them back into OSG geometry objects.  We potentially will need
387    // one for each state set in the map
388    numGeoms = bsp_data->getNumStateSets();
389    vbspGeomList = new VBSPGeometry *[numGeoms];
390
391    // Initialize the list to all NULL for now.  We'll create the geometry
392    // objects as we need them
393    memset(vbspGeomList, 0, sizeof(VBSPGeometry *) * numGeoms);
394
395    // Get this entity's internal model from the bsp data
396    currentModel = bsp_data->getModel(entity_model_index);
397
398    // Iterate over the face list and assign faces to the appropriate geometry
399    // objects
400    for (i = 0; i < currentModel.num_faces; i++)
401    {
402        // Get the current face
403        currentFace = bsp_data->getFace(currentModel.first_face + i);
404
405        // Get the texdata used by this face
406        currentTexInfo = bsp_data->getTexInfo(currentFace.texinfo_index);
407        currentTexData = bsp_data->getTexData(currentTexInfo.texdata_index);
408
409        // Get the texture name
410        texName = bsp_data->
411            getTexDataString(currentTexData.name_string_table_id).c_str();
412        strcpy(currentTexName, texName);
413
414        // See if this is a non-drawable surface
415        if ((strcasecmp(currentTexName, "tools/toolsareaportal") != 0) &&
416            (strcasecmp(currentTexName, "tools/toolsblocklos") != 0) &&
417            (strcasecmp(currentTexName, "tools/toolsblockbullets") != 0) &&
418            (strcasecmp(currentTexName, "tools/toolsblocklight") != 0) &&
419            (strcasecmp(currentTexName, "tools/toolsclip") != 0) &&
420            (strcasecmp(currentTexName, "tools/toolscontrolclip") != 0) &&
421            (strcasecmp(currentTexName, "tools/toolsdotted") != 0) &&
422            (strcasecmp(currentTexName, "tools/toolshint") != 0) &&
423            (strcasecmp(currentTexName, "tools/toolsinvisible") != 0) &&
424            (strcasecmp(currentTexName, "tools/toolsinvisibleladder") != 0) &&
425            (strcasecmp(currentTexName, "tools/toolsnodraw") != 0) &&
426            (strcasecmp(currentTexName, "tools/toolsnpcclip") != 0) &&
427            (strcasecmp(currentTexName, "tools/toolsoccluder") != 0) &&
428            (strcasecmp(currentTexName, "tools/toolsorigin") != 0) &&
429            (strcasecmp(currentTexName, "tools/toolsskip") != 0) &&
430            (strcasecmp(currentTexName, "tools/toolsskybox") != 0) &&
431            (strcasecmp(currentTexName, "tools/toolsskyfog") != 0) &&
432            (strcasecmp(currentTexName, "tools/toolstrigger") != 0))
433        {
434            // Get or create the corresponding VBSPGeometry object from the
435            // list
436            currentGeomIndex = currentTexInfo.texdata_index;
437            currentGeom = vbspGeomList[currentGeomIndex];
438            if (currentGeom == NULL)
439            {
440                // Create the geometry object
441                vbspGeomList[currentGeomIndex] = new VBSPGeometry(bsp_data);
442                currentGeom = vbspGeomList[currentGeomIndex];
443            }
444
445            // Add the face to the appropriate VBSPGeometry object
446            currentGeom->addFace(currentModel.first_face + i);
447        }
448    }
449
450    // Create a top-level group to hold the geometry objects
451    if (entity_transformed)
452    {
453        // Create a matrix transform
454        MatrixTransform * entityXform = new MatrixTransform();
455
456        // Set it up with the entity's transform information (scale the
457        // position from inches to meters)
458        Matrixf transMat, rotMat;
459        Quat roll, yaw, pitch;
460        transMat.makeTranslate(entity_origin * 0.0254);
461        pitch.makeRotate(osg::DegreesToRadians(entity_angles.x()),
462                         Vec3f(0.0, 1.0, 0.0));
463        yaw.makeRotate(osg::DegreesToRadians(entity_angles.y()),
464                       Vec3f(0.0, 0.0, 1.0));
465        roll.makeRotate(osg::DegreesToRadians(entity_angles.z()),
466                        Vec3f(1.0, 0.0, 0.0));
467        rotMat.makeRotate(roll * pitch * yaw);
468
469        // Set the transform matrix
470        entityXform->setMatrix(rotMat * transMat);
471
472        // Use the transform node as the main entity group
473        entityGroup = entityXform;
474    }
475    else
476    {
477        // Create a group to represent the entire entity
478        entityGroup = new Group();
479    }
480
481    // Iterate over the geometry array and convert each geometry object
482    // into OSG geometry
483    for (i = 0; i < numGeoms; i++)
484    {
485        // Get the next geometry object (if any)
486        currentGeom = vbspGeomList[i];
487        if (currentGeom != NULL)
488        {
489            // Convert the BSP geometry to OSG geometry
490            geomGroup = currentGeom->createGeometry();
491
492            // Make sure the geometry converted properly
493            if (geomGroup.valid())
494            {
495                // Set this group's state set
496                geomGroup->setStateSet(bsp_data->getStateSet(i));
497
498                // Add the geometry group to the entity group
499                entityGroup->addChild(geomGroup.get());
500            }
501        }
502    }
503
504    // Return the group we created
505    return entityGroup;
506}
507
508
509ref_ptr<Group> VBSPEntity::createModelGeometry()
510{
511    std::string      modelFile;
512    ref_ptr<Node>    modelNode;
513    ref_ptr<Group>   entityGroup;
514
515    // Try to load the model
516    modelNode = osgDB::readNodeFile(entity_model);
517    if (modelNode.valid())
518    {
519        // Create a group and add the model to it
520        if (entity_transformed)
521        {
522            // Create a matrix transform
523            MatrixTransform * entityXform = new MatrixTransform();
524
525            // Set it up with the entity's transform information (scale
526            // the position from inches to meters)
527            Matrixf transMat, rotMat;
528            Quat roll, yaw, pitch;
529            transMat.makeTranslate(entity_origin * 0.0254);
530            pitch.makeRotate(osg::DegreesToRadians(entity_angles.x()),
531                             Vec3f(0.0, 1.0, 0.0));
532            yaw.makeRotate(osg::DegreesToRadians(entity_angles.y()),
533                           Vec3f(0.0, 0.0, 1.0));
534            roll.makeRotate(osg::DegreesToRadians(entity_angles.z()),
535                            Vec3f(1.0, 0.0, 0.0));
536            rotMat.makeRotate(roll * pitch * yaw);
537
538            // Set the transform matrix
539            entityXform->setMatrix(rotMat * transMat);
540
541            // Use the transform node as the main entity group
542            entityGroup = entityXform;
543        }
544        else
545        {
546            // Create a group to represent the entire entity
547            entityGroup = new Group();
548        }
549
550        // Add the model node to the group
551        entityGroup->addChild(modelNode.get());
552    }
553    else
554    {
555        notify(WARN) << "Couldn't find prop \"" << entity_model << "\".";
556        notify(WARN) << std::endl;
557
558        // Leave the group empty (no model to show)
559        entityGroup = NULL;
560    }
561
562    return entityGroup;
563}
564
565
566EntityClass VBSPEntity::getClass()
567{
568    return entity_class;
569}
570
571
572bool VBSPEntity::isVisible()
573{
574    return entity_visible;
575}
576
577
578ref_ptr<Group> VBSPEntity::createGeometry()
579{
580    // If we're not a visible entity, we have no geometry
581    if (!entity_visible)
582        return NULL;
583
584    // Create the geometry for the entity based on the class
585    if ((entity_class == ENTITY_WORLDSPAWN) ||
586        (entity_class == ENTITY_FUNC_BRUSH))
587    {
588        return createBrushGeometry();
589    }
590    else if (entity_class == ENTITY_PROP)
591    {
592        return createModelGeometry();
593    }
594
595    // If we get here, we don't handle this kind of entity (yet)
596    return NULL;
597}
Note: See TracBrowser for help on using the browser.