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

Revision 9475, 17.9 kB (checked in by robert, 6 years ago)

From Andy Skinner, fixes for Solaris build

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    double 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 = atof(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 = atof(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 = atof(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.