00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035 #include "model-loader.h"
00036
00037 #include <fstream>
00038
00039 #include "common/wave_ex.h"
00040 #include "datahash/datahash_text.h"
00041 #include "geometry/matrix_4.h"
00042 #include "glut-model/glut-model.h"
00043 #include "hfield/heightfield.h"
00044 #include "md3-model/md3-model.h"
00045 #include "obj-model/obj-model.h"
00046 #include "opengl-effects/opengl-effects.h"
00047 #include "perf/perf.h"
00048 #include "skybox/skybox.h"
00049 #include "terrain/terrain.h"
00050 #include "threadsafe/threadsafe_map.h"
00051 #include "util/file.h"
00052 #include "util/parsing.h"
00053 #include "util/token_stream.h"
00054 #include "xform-model/xform-model.h"
00055
00056
00057 namespace aesop {
00058
00060
00063 static const char * s_name = "Glut 3D Model Loader";
00064
00065
00066 static const float s_radiansPerDegree = M_PI / 180.0;
00067
00068
00070
00071
00072
00074
00075
00077
00078
00079
00081
00082 class ModelData : public ComponentData {
00083 public:
00084 ~ModelData(void) throw() { }
00085
00086 smart_ptr<glut::Renderable> m_model;
00087 };
00088
00089
00090
00092
00093
00094
00096
00097 class ModelRegistry : public TypeComponentLoader,
00098 public glut::MaterialRegistry {
00099 public:
00100
00101 ModelRegistry(void) throw() { }
00102 ~ModelRegistry(void) throw() { }
00103
00104
00105 void initialize(IN smart_ptr<story::Story>& story);
00106
00107
00108 const char * getComponentName(void) const throw() { return "model"; }
00109 const char * getLoaderName(void) const throw() { return s_name; }
00110 bool isMyFormat(IN const Datahash * data,
00111 IN const char * path) const;
00112 smart_ptr<ComponentData> loadTS(IN smart_ptr<Instance>& instance,
00113 IN const Datahash * data,
00114 IN const char * path);
00115
00116
00117 smart_ptr<glut::material_t> getMaterial(IN const char * id);
00118
00119 private:
00120
00121
00122
00123 typedef threadsafe_map<std::string, smart_ptr<Datahash> > data_map_t;
00124
00125
00126 typedef threadsafe_map<std::string, smart_ptr<glut::material_t> > material_map_t;
00127
00128
00129
00130
00131 smart_ptr<story::Story> m_story;
00132 data_map_t m_modelData;
00133 material_map_t m_materials;
00134 };
00135
00136 static smart_ptr<ModelRegistry> s_registry;
00137
00138
00139 void
00140 ModelRegistry::initialize
00141 (
00142 IN smart_ptr<story::Story>& story
00143 )
00144 {
00145 ASSERT(story, "null");
00146
00147 m_story = story;
00148 }
00149
00150
00151
00152 bool
00153 ModelRegistry::isMyFormat
00154 (
00155 IN const Datahash * data,
00156 IN const char * path
00157 )
00158 const
00159 {
00160 ASSERT(data, "null");
00161 ASSERT(path, "null");
00162
00163 DPRINTF("Checking '%s'", path);
00164
00165
00166 const char * modelId = getString(data, "id");
00167
00168
00169 if (strcmp("model", GetExtension(modelId))) {
00170 DPRINTF("Extension does not match!");
00171 return false;
00172 }
00173
00174 std::ifstream stream(path);
00175 if (!stream.good()) {
00176 WAVE_EX(wex);
00177 wex << "Failed to open file for reading: " << path;
00178 }
00179
00180 std::string token;
00181 getNextToken(stream, token);
00182 DPRINTF(" token: %s", token.c_str());
00183 if ("modelFormat" != token)
00184 return false;
00185
00186 getNextToken(stream, token);
00187 DPRINTF(" token: %s", token.c_str());
00188 if ("model-0.1" != token)
00189 return false;
00190
00191
00192 return true;
00193 }
00194
00195
00196
00197 smart_ptr<ComponentData>
00198 ModelRegistry::loadTS
00199 (
00200 IN smart_ptr<Instance>& instance,
00201 IN const Datahash * typeData,
00202 IN const char * path
00203 )
00204 {
00205 ASSERT(instance, "null");
00206 ASSERT(typeData, "null");
00207 ASSERT(path, "null");
00208
00209 const char * modelId = getString(typeData, "id");
00210 DPRINTF("Loading model data for model: %s", modelId);
00211 DPRINTF(" path: %s", path);
00212
00213 std::string parentDir;
00214 GetParentDirectory(path, parentDir);
00215
00216
00217 smart_ptr<nstream::Manager> mgr =
00218 nstream::getFilesystemManager(parentDir.c_str());
00219 ASSERT(mgr, "null");
00220
00221
00222 smart_ptr<Datahash> instanceData = instance->getInstanceData("model");
00223
00224
00225
00226 smart_ptr<Datahash> modelData;
00227 if (m_modelData.lookup(modelId, modelData) && modelData) {
00228 DPRINTF(" model data already loaded from disk");
00229 } else {
00230 DPRINTF(" need to load model data from disk");
00231 modelData = readHashFromTextFile(path);
00232 m_modelData.insert(modelId, modelData);
00233 }
00234 ASSERT(modelData, "null");
00235
00236 smart_ptr<ModelData> md = new ModelData;
00237 ASSERT(md, "out of memory");
00238
00239 try {
00240 if (strcmp("model-0.1", getString(modelData, "modelFormat"))) {
00241 WAVE_EX(wex);
00242 wex << "Invalid or missing model format";
00243 }
00244
00245 const char * type =
00246 getKeyWithOverrides("type", instanceData, typeData,
00247 modelData, eDatahash_Required);
00248 DPRINTF(" model type: %s", type);
00249
00250 if (!strcmp("hfield", type)) {
00251 const char * hFile = getKeyWithOverrides("hfield",
00252 instanceData, typeData, modelData,
00253 eDatahash_Required);
00254
00255
00256
00257
00258
00259
00260 DPRINTF("Reading heightfield from path: %s",
00261 hFile);
00262 smart_ptr<nstream::Stream> stream =
00263 nstream::openNamedStream(mgr, hFile);
00264 ASSERT(stream, "null");
00265 smart_ptr<hfield::Heightfield> hfield =
00266 hfield::Heightfield::create(stream);
00267 ASSERT(hfield, "failed to load heightfield");
00268
00269 smart_ptr<glut::Renderable> model =
00270 glut::createTerrain(hfield);
00271 ASSERT(model, "failed to create heightfield renderer");
00272 md->m_model = model;
00273 } else if (!strcmp("md3", type)) {
00274 DPRINTF("Loading an MD3 model!");
00275
00276 const char * md3Dir = getKeyWithOverrides("md3Dir",
00277 instanceData, typeData, modelData,
00278 eDatahash_Required);
00279
00280
00281 std::string absolute =
00282 getPathRelativeTo(path, md3Dir);
00283 DPRINTF("Reading md3 model from path: %s",
00284 absolute.c_str());
00285 md->m_model = md3::loadMd3Player(absolute.c_str());
00286 } else if (!strcmp("obj-model", type)) {
00287 DPRINTF("Loading an OBJ model!");
00288
00289 const char * objFile = getKeyWithOverrides("file",
00290 instanceData, typeData, modelData,
00291 eDatahash_Required);
00292
00293 float scale = atof(getKeyWithOverrides("scale",
00294 instanceData, typeData, modelData,
00295 eDatahash_Required));
00296 ASSERT_THROW(scale > 0,
00297 "Bad obj file scale: " << scale);
00298
00299
00300 smart_ptr<nstream::Stream> stream =
00301 nstream::openNamedStream(mgr, objFile);
00302 ASSERT_THROW(stream, "Failed to open stream for file: "
00303 << objFile);
00304 md->m_model = obj::Model::create(stream, scale);
00305 } else if (!strcmp("lod-model", type)) {
00306 bool isSkybox = false;
00307 const char * flags = getKeyWithOverrides("flags",
00308 instanceData, typeData, modelData,
00309 eDatahash_Optional);
00310 if (flags && !strcmp("skybox", flags)) {
00311 isSkybox = true;
00312 }
00313 const char * filename = getKeyWithOverrides("file",
00314 instanceData, typeData, modelData,
00315 eDatahash_Required);
00316 std::string absolute = parentDir;
00317 absolute += "/";
00318 absolute += filename;
00319 DPRINTF("Loading glut model from path: %s",
00320 absolute.c_str());
00321 smart_ptr<glut::Renderable> model =
00322 glut::loadModel(absolute.c_str(), this);
00323 if (isSkybox) {
00324 model = glut::createSkybox(model);
00325 }
00326 md->m_model = model;
00327 ASSERT(md->m_model, "failed to load model");
00328 } else if (!strcmp("sphere", type)) {
00329 glut::sphere_init_t si;
00330 si.radius = atof(getKeyWithOverrides("radius",
00331 instanceData, typeData, modelData,
00332 eDatahash_Required));
00333 si.nRings = atoi(getKeyWithOverrides("nRings",
00334 instanceData, typeData, modelData,
00335 eDatahash_Required));
00336 getImgColorFromString(getKeyWithOverrides("color",
00337 instanceData, typeData, modelData,
00338 eDatahash_Required), si.color);
00339 ASSERT_THROW(si.isValid(),
00340 "Just read sphere data--not valid");
00341
00342 md->m_model = glut::createCoolSphere(si);
00343 ASSERT(md->m_model, "null");
00344
00345 } else if (!strcmp("xform", type)) {
00346
00347
00348 const char * baseModel = getKeyWithOverrides("baseModel",
00349 instanceData, typeData, modelData,
00350 eDatahash_Required);
00351 DPRINTF("xform is wrapping model: '%s'", baseModel);
00352
00353
00354 smart_ptr<Datahash> td = Datahash::create();
00355 ASSERT(td, "null");
00356 td->insert("id", baseModel);
00357
00358
00359 std::string basePath = parentDir;
00360 basePath += "/";
00361 basePath += baseModel;
00362
00363
00364 smart_ptr<ComponentData> base =
00365 this->loadTS(instance, td, basePath.c_str());
00366 ASSERT(base, "null");
00367 smart_ptr<ModelData> mdBase = base;
00368 ASSERT_THROW(mdBase, "component data is bad type?");
00369 ASSERT(mdBase->m_model, "null");
00370
00371
00372 const char * scaleSz = getKeyWithOverrides("scale",
00373 instanceData, typeData, modelData,
00374 eDatahash_Required);
00375 float scale = atof(scaleSz);
00376 DPRINTF(" scale: %s == %f", scaleSz, scale);
00377
00378
00379 Datahash::iterator_t i;
00380 modelData->getIterator("rotate", i);
00381 std::string keystr;
00382 hash_value_t hv;
00383 matrix4_t xform;
00384 xform.setXYZScale(scale);
00385 while (modelData->getNextElement(i, keystr, hv)) {
00386 if (hv.hash)
00387 continue;
00388
00389 const char * val = hv.text.c_str();
00390 DPRINTF(" Parsing rotation: '%s'", val);
00391 if (strlen(val) < 3) {
00392 continue;
00393 }
00394
00395 char axis = val[0];
00396 float degrees = atof(val + 2);
00397 float radians = degrees * s_radiansPerDegree;
00398
00399 matrix4_t R;
00400 switch (axis) {
00401 case 'x': R.setXRotation(radians); break;
00402 case 'y': R.setYRotation(radians); break;
00403 case 'z': R.setZRotation(radians); break;
00404 default:
00405 ASSERT_THROW(false,
00406 "Bad rotation axis: 'rotate "
00407 << val << "'");
00408 }
00409
00410 matrix4_t T = R * xform;
00411 xform = T;
00412 }
00413
00414
00415 md->m_model =
00416 glut::createXformModel(xform, mdBase->m_model);
00417 ASSERT(md->m_model, "null");
00418
00419 } else {
00420 WAVE_EX(wex);
00421 wex << "Unknown model type: " << type;
00422 }
00423 } catch (std::exception& e) {
00424 DPRINTF("Error loading model '%s' from file '%s'",
00425 modelId, path);
00426 DPRINTF("Exception: %s", e.what());
00427
00428
00429 ASSERT(false, "Failed to load model!");
00430 }
00431
00432
00433 ASSERT(md->m_model, "null");
00434 return md;
00435 }
00436
00437
00438
00439 smart_ptr<glut::material_t>
00440 ModelRegistry::getMaterial
00441 (
00442 IN const char * id
00443 )
00444 {
00445 ASSERT(m_story, "null");
00446 ASSERT(id, "null");
00447
00448
00449 smart_ptr<glut::material_t> material;
00450 if (m_materials.lookup(id, material))
00451 return material;
00452
00453
00454 material = new glut::material_t;
00455 ASSERT(material, "out of memory");
00456
00457 std::string path = m_story->getObjectPath("model", "foo.model");
00458 std::string dir;
00459 GetParentDirectory(path.c_str(), dir);
00460 dir += "/wgm";
00461
00462
00463 path = dir;
00464 path += "/";
00465 path += id;
00466
00467 DPRINTF("Attempting to load material '%s' from path: %s",
00468 id, path.c_str());
00469
00470 std::ifstream infile(path.c_str());
00471 if (!infile.good()) {
00472 WAVE_EX(wex);
00473 wex << "Failed to open material file: " << path;
00474 }
00475
00476 try {
00477 expectToken(infile, "materialVersion");
00478 expectToken(infile, "0.1");
00479 expectToken(infile, "material");
00480 parseMaterial(infile, dir.c_str(), *material);
00481 } catch (std::exception& e) {
00482 WAVE_EX(wex);
00483 wex << "Failed to load material '" << id << "' from file: ";
00484 wex << path << "\n" << e.what();
00485 }
00486
00487
00488 m_materials.insert(id, material);
00489 return material;
00490 }
00491
00492
00493
00495
00496
00497
00499
00500 smart_ptr<TypeComponentLoader>
00501 getGlutModelLoader
00502 (
00503 void
00504 )
00505 {
00506 if (!s_registry) {
00507 WAVE_EX(wex);
00508 wex << "Glut model loader is not yet created";
00509 }
00510
00511 return s_registry;
00512 }
00513
00514
00515
00516 smart_ptr<TypeComponentLoader>
00517 createModelLoader
00518 (
00519 IN smart_ptr<story::Story>& story
00520 )
00521 {
00522 ASSERT(story, "null");
00523
00524 s_registry = new ModelRegistry;
00525 ASSERT(s_registry, "null");
00526 s_registry->initialize(story);
00527
00528 return s_registry;
00529 }
00530
00531
00532 smart_ptr<glut::Renderable>
00533 getModel
00534 (
00535 IN smart_ptr<Instance>& instance
00536 )
00537 {
00538 ASSERT(instance, "null");
00539
00540 smart_ptr<ComponentData> cdata = instance->getComponentData("model");
00541 if (!cdata)
00542 return NULL;
00543
00544 smart_ptr<ModelData> md = cdata;
00545 ASSERT(md, "failed to upcast?");
00546
00547 return md->m_model;
00548 }
00549
00550
00551
00552 };
00553