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

Revision 13041, 18.2 kB (checked in by robert, 2 years ago)

Ran script to remove trailing spaces and tabs

  • Property svn:eol-style set to native
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    class_name = (*param).second;
319    if (class_name.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 (class_name.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 ((class_name.compare("func_brush") == 0) ||
333             (class_name.compare("func_illusionary") == 0) ||
334             (class_name.compare("func_wall_toggle") == 0) ||
335             (class_name.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 (class_name.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 (class_name.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 (class_name.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    std::stringstream   groupName;
384
385    // Create a list of VBSPGeometry objects for each texdata entry in the
386    // scene.  These objects will hold the necessary geometry data until we
387    // convert them back into OSG geometry objects.  We potentially will need
388    // one for each state set in the map
389    numGeoms = bsp_data->getNumStateSets();
390    vbspGeomList = new VBSPGeometry *[numGeoms];
391
392    // Initialize the list to all NULL for now.  We'll create the geometry
393    // objects as we need them
394    memset(vbspGeomList, 0, sizeof(VBSPGeometry *) * numGeoms);
395
396    // Get this entity's internal model from the bsp data
397    currentModel = bsp_data->getModel(entity_model_index);
398
399    // Iterate over the face list and assign faces to the appropriate geometry
400    // objects
401    for (i = 0; i < currentModel.num_faces; i++)
402    {
403        // Get the current face
404        currentFace = bsp_data->getFace(currentModel.first_face + i);
405
406        // Get the texdata used by this face
407        currentTexInfo = bsp_data->getTexInfo(currentFace.texinfo_index);
408        currentTexData = bsp_data->getTexData(currentTexInfo.texdata_index);
409
410        // Get the texture name
411        texName = bsp_data->
412            getTexDataString(currentTexData.name_string_table_id).c_str();
413        strcpy(currentTexName, texName);
414
415        // See if this is a non-drawable surface
416        if ((strcasecmp(currentTexName, "tools/toolsareaportal") != 0) &&
417            (strcasecmp(currentTexName, "tools/toolsblocklos") != 0) &&
418            (strcasecmp(currentTexName, "tools/toolsblockbullets") != 0) &&
419            (strcasecmp(currentTexName, "tools/toolsblocklight") != 0) &&
420            (strcasecmp(currentTexName, "tools/toolsclip") != 0) &&
421            (strcasecmp(currentTexName, "tools/toolscontrolclip") != 0) &&
422            (strcasecmp(currentTexName, "tools/toolsdotted") != 0) &&
423            (strcasecmp(currentTexName, "tools/toolshint") != 0) &&
424            (strcasecmp(currentTexName, "tools/toolsinvisible") != 0) &&
425            (strcasecmp(currentTexName, "tools/toolsinvisibleladder") != 0) &&
426            (strcasecmp(currentTexName, "tools/toolsnodraw") != 0) &&
427            (strcasecmp(currentTexName, "tools/toolsnpcclip") != 0) &&
428            (strcasecmp(currentTexName, "tools/toolsoccluder") != 0) &&
429            (strcasecmp(currentTexName, "tools/toolsorigin") != 0) &&
430            (strcasecmp(currentTexName, "tools/toolsskip") != 0) &&
431            (strcasecmp(currentTexName, "tools/toolsskybox") != 0) &&
432            (strcasecmp(currentTexName, "tools/toolsskyfog") != 0) &&
433            (strcasecmp(currentTexName, "tools/toolstrigger") != 0))
434        {
435            // Get or create the corresponding VBSPGeometry object from the
436            // list
437            currentGeomIndex = currentTexInfo.texdata_index;
438            currentGeom = vbspGeomList[currentGeomIndex];
439            if (currentGeom == NULL)
440            {
441                // Create the geometry object
442                vbspGeomList[currentGeomIndex] = new VBSPGeometry(bsp_data);
443                currentGeom = vbspGeomList[currentGeomIndex];
444            }
445
446            // Add the face to the appropriate VBSPGeometry object
447            currentGeom->addFace(currentModel.first_face + i);
448        }
449    }
450
451    // Create a top-level group to hold the geometry objects
452    if (entity_transformed)
453    {
454        // Create a matrix transform
455        MatrixTransform * entityXform = new MatrixTransform();
456
457        // Set it up with the entity's transform information (scale the
458        // position from inches to meters)
459        Matrixf transMat, rotMat;
460        Quat roll, yaw, pitch;
461        transMat.makeTranslate(entity_origin * 0.0254);
462        pitch.makeRotate(osg::DegreesToRadians(entity_angles.x()),
463                         Vec3f(0.0, 1.0, 0.0));
464        yaw.makeRotate(osg::DegreesToRadians(entity_angles.y()),
465                       Vec3f(0.0, 0.0, 1.0));
466        roll.makeRotate(osg::DegreesToRadians(entity_angles.z()),
467                        Vec3f(1.0, 0.0, 0.0));
468        rotMat.makeRotate(roll * pitch * yaw);
469
470        // Set the transform matrix
471        entityXform->setMatrix(rotMat * transMat);
472
473        // Use the transform node as the main entity group
474        entityGroup = entityXform;
475    }
476    else
477    {
478        // Create a group to represent the entire entity
479        entityGroup = new Group();
480    }
481
482    // Iterate over the geometry array and convert each geometry object
483    // into OSG geometry
484    for (i = 0; i < numGeoms; i++)
485    {
486        // Get the next geometry object (if any)
487        currentGeom = vbspGeomList[i];
488        if (currentGeom != NULL)
489        {
490            // Convert the BSP geometry to OSG geometry
491            geomGroup = currentGeom->createGeometry();
492
493            // Make sure the geometry converted properly
494            if (geomGroup.valid())
495            {
496                // Set this group's state set
497                geomGroup->setStateSet(bsp_data->getStateSet(i));
498
499                // Add the geometry group to the entity group
500                entityGroup->addChild(geomGroup.get());
501            }
502        }
503    }
504
505    // Name the entity group
506    groupName << class_name << ":" << entity_model_index;
507    entityGroup->setName(groupName.str());
508
509    // Return the group we created
510    return entityGroup;
511}
512
513
514ref_ptr<Group> VBSPEntity::createModelGeometry()
515{
516    std::string      modelFile;
517    ref_ptr<Node>    modelNode;
518    ref_ptr<Group>   entityGroup;
519
520    // Try to load the model
521    modelNode = osgDB::readNodeFile(entity_model);
522    if (modelNode.valid())
523    {
524        // Create a group and add the model to it
525        if (entity_transformed)
526        {
527            // Create a matrix transform
528            MatrixTransform * entityXform = new MatrixTransform();
529
530            // Set it up with the entity's transform information (scale
531            // the position from inches to meters)
532            Matrixf transMat, rotMat;
533            Quat roll, yaw, pitch;
534            transMat.makeTranslate(entity_origin * 0.0254);
535            pitch.makeRotate(osg::DegreesToRadians(entity_angles.x()),
536                             Vec3f(0.0, 1.0, 0.0));
537            yaw.makeRotate(osg::DegreesToRadians(entity_angles.y()),
538                           Vec3f(0.0, 0.0, 1.0));
539            roll.makeRotate(osg::DegreesToRadians(entity_angles.z()),
540                            Vec3f(1.0, 0.0, 0.0));
541            rotMat.makeRotate(roll * pitch * yaw);
542
543            // Set the transform matrix
544            entityXform->setMatrix(rotMat * transMat);
545
546            // Use the transform node as the main entity group
547            entityGroup = entityXform;
548        }
549        else
550        {
551            // Create a group to represent the entire entity
552            entityGroup = new Group();
553        }
554
555        // Add the model node to the group
556        entityGroup->addChild(modelNode.get());
557
558        // Set the group's name
559        entityGroup->setName(class_name + std::string(":") + entity_model);
560    }
561    else
562    {
563        OSG_WARN << "Couldn't find prop \"" << entity_model << "\".";
564        OSG_WARN << std::endl;
565
566        // Leave the group empty (no model to show)
567        entityGroup = NULL;
568    }
569
570    return entityGroup;
571}
572
573
574EntityClass VBSPEntity::getClass()
575{
576    return entity_class;
577}
578
579
580bool VBSPEntity::isVisible()
581{
582    return entity_visible;
583}
584
585
586ref_ptr<Group> VBSPEntity::createGeometry()
587{
588    // If we're not a visible entity, we have no geometry
589    if (!entity_visible)
590        return NULL;
591
592    // Create the geometry for the entity based on the class
593    if ((entity_class == ENTITY_WORLDSPAWN) ||
594        (entity_class == ENTITY_FUNC_BRUSH))
595    {
596        return createBrushGeometry();
597    }
598    else if (entity_class == ENTITY_PROP)
599    {
600        return createModelGeometry();
601    }
602
603    // If we get here, we don't handle this kind of entity (yet)
604    return NULL;
605}
Note: See TracBrowser for help on using the browser.