SceneLoader.cpp 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098
  1. #include "Base.h"
  2. #include "AudioSource.h"
  3. #include "Game.h"
  4. #include "Bundle.h"
  5. #include "SceneLoader.h"
  6. namespace gameplay
  7. {
  8. // Utility functions (shared with Properties).
  9. extern void calculateNamespacePath(const std::string& urlString, std::string& fileString, std::vector<std::string>& namespacePath);
  10. extern Properties* getPropertiesFromNamespacePath(Properties* properties, const std::vector<std::string>& namespacePath);
  11. Scene* SceneLoader::load(const char* url)
  12. {
  13. SceneLoader loader;
  14. return loader.loadInternal(url);
  15. }
  16. Scene* SceneLoader::loadInternal(const char* url)
  17. {
  18. // Get the file part of the url that we are loading the scene from.
  19. std::string urlStr = url ? url : "";
  20. std::string id;
  21. splitURL(urlStr, &_path, &id);
  22. // Load the scene properties from file.
  23. Properties* properties = Properties::create(url);
  24. if (properties == NULL)
  25. {
  26. GP_ERROR("Failed to load scene file '%s'.", url);
  27. return NULL;
  28. }
  29. // Check if the properties object is valid and has a valid namespace.
  30. Properties* sceneProperties = (strlen(properties->getNamespace()) > 0) ? properties : properties->getNextNamespace();
  31. if (!sceneProperties || !(strcmp(sceneProperties->getNamespace(), "scene") == 0))
  32. {
  33. GP_ERROR("Failed to load scene from properties object: must be non-null object and have namespace equal to 'scene'.");
  34. SAFE_DELETE(properties);
  35. return NULL;
  36. }
  37. // Get the path to the main GPB.
  38. _gpbPath = sceneProperties->getString("path");
  39. // Build the node URL/property and animation reference tables and load the referenced files/store the inline properties objects.
  40. buildReferenceTables(sceneProperties);
  41. loadReferencedFiles();
  42. // Load the main scene data from GPB and apply the global scene properties.
  43. Scene* scene = loadMainSceneData(sceneProperties);
  44. if (!scene)
  45. {
  46. GP_ERROR("Failed to load main scene from bundle.");
  47. SAFE_DELETE(properties);
  48. return NULL;
  49. }
  50. // First apply the node url properties. Following that,
  51. // apply the normal node properties and create the animations.
  52. // We apply physics properties after all other node properties
  53. // so that the transform (SRT) properties get applied before
  54. // processing physics collision objects.
  55. applyNodeUrls(scene);
  56. applyNodeProperties(scene, sceneProperties,
  57. SceneNodeProperty::AUDIO |
  58. SceneNodeProperty::MATERIAL |
  59. SceneNodeProperty::PARTICLE |
  60. SceneNodeProperty::ROTATE |
  61. SceneNodeProperty::SCALE |
  62. SceneNodeProperty::TRANSLATE);
  63. applyNodeProperties(scene, sceneProperties, SceneNodeProperty::COLLISION_OBJECT);
  64. // Apply node tags
  65. for (unsigned int i = 0, sncount = _sceneNodes.size(); i < sncount; ++i)
  66. {
  67. SceneNode& sceneNode = _sceneNodes[i];
  68. for (std::map<std::string, std::string>::const_iterator itr = sceneNode._tags.begin(); itr != sceneNode._tags.end(); ++itr)
  69. {
  70. for (unsigned int n = 0, ncount = sceneNode._nodes.size(); n < ncount; ++n)
  71. sceneNode._nodes[n]->setTag(itr->first.c_str(), itr->second.c_str());
  72. }
  73. }
  74. // Create animations for scene
  75. createAnimations(scene);
  76. // Find the physics properties object.
  77. Properties* physics = NULL;
  78. sceneProperties->rewind();
  79. while (true)
  80. {
  81. Properties* ns = sceneProperties->getNextNamespace();
  82. if (ns == NULL || strcmp(ns->getNamespace(), "physics") == 0)
  83. {
  84. physics = ns;
  85. break;
  86. }
  87. }
  88. // Load physics properties and constraints.
  89. if (physics)
  90. loadPhysics(physics, scene);
  91. // Clean up all loaded properties objects.
  92. std::map<std::string, Properties*>::iterator iter = _propertiesFromFile.begin();
  93. for (; iter != _propertiesFromFile.end(); ++iter)
  94. {
  95. SAFE_DELETE(iter->second);
  96. }
  97. // Clean up the .scene file's properties object.
  98. SAFE_DELETE(properties);
  99. return scene;
  100. }
  101. void SceneLoader::addSceneAnimation(const char* animationID, const char* targetID, const char* url)
  102. {
  103. std::string urlStr = url ? url : "";
  104. // If there is a file that needs to be loaded later, add an
  105. // empty entry to the properties table to signify it.
  106. if (urlStr.length() > 0 && _properties.count(urlStr) == 0)
  107. _properties[urlStr] = NULL;
  108. // Add the animation to the list of animations to be resolved later.
  109. _animations.push_back(SceneAnimation(animationID, targetID, urlStr));
  110. }
  111. void SceneLoader::addSceneNodeProperty(SceneNode& sceneNode, SceneNodeProperty::Type type, const char* url, int index)
  112. {
  113. std::string urlStr = url ? url : "";
  114. // If there is a non-GPB file that needs to be loaded later, add an
  115. // empty entry to the properties table to signify it.
  116. if (urlStr.length() > 0 && urlStr.find(".") != urlStr.npos && urlStr.find(".gpb") == urlStr.npos && _properties.count(urlStr) == 0)
  117. _properties[urlStr] = NULL;
  118. SceneNodeProperty prop(type, urlStr, index);
  119. // Parse for wildcharacter character (only supported on the URL attribute)
  120. if (type == SceneNodeProperty::URL)
  121. {
  122. if (urlStr.length() > 1 && urlStr.at(urlStr.length()-1) == '*')
  123. {
  124. prop._url = urlStr.substr(0, urlStr.length()-1);
  125. sceneNode._exactMatch = false;
  126. }
  127. }
  128. // Add the node property to the list of node properties to be resolved later.
  129. sceneNode._properties.push_back(prop);
  130. }
  131. void SceneLoader::applyNodeProperties(const Scene* scene, const Properties* sceneProperties, unsigned int typeFlags)
  132. {
  133. for (unsigned int i = 0, sncount = _sceneNodes.size(); i < sncount; ++i)
  134. {
  135. SceneNode& sceneNode = _sceneNodes[i];
  136. for (unsigned int p = 0, pcount = sceneNode._properties.size(); p < pcount; ++p)
  137. {
  138. SceneNodeProperty& snp = sceneNode._properties[p];
  139. if (typeFlags & snp._type)
  140. {
  141. for (unsigned int n = 0, ncount = sceneNode._nodes.size(); n < ncount; ++n)
  142. applyNodeProperty(sceneNode, sceneNode._nodes[n], sceneProperties, snp, scene);
  143. }
  144. }
  145. }
  146. }
  147. void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Properties* sceneProperties, const SceneNodeProperty& snp, const Scene* scene)
  148. {
  149. if (snp._type == SceneNodeProperty::AUDIO ||
  150. snp._type == SceneNodeProperty::MATERIAL ||
  151. snp._type == SceneNodeProperty::PARTICLE ||
  152. snp._type == SceneNodeProperty::COLLISION_OBJECT)
  153. {
  154. // Check to make sure the referenced properties object was loaded properly.
  155. Properties* p = _properties[snp._url];
  156. if (!p)
  157. {
  158. GP_ERROR("The referenced node data at url '%s' failed to load.", snp._url.c_str());
  159. return;
  160. }
  161. // If the URL didn't specify a particular namespace within the file, pick the first one.
  162. p = (strlen(p->getNamespace()) > 0) ? p : p->getNextNamespace();
  163. switch (snp._type)
  164. {
  165. case SceneNodeProperty::AUDIO:
  166. {
  167. AudioSource* audioSource = AudioSource::create(p);
  168. node->setAudioSource(audioSource);
  169. SAFE_RELEASE(audioSource);
  170. break;
  171. }
  172. case SceneNodeProperty::MATERIAL:
  173. if (!node->getModel())
  174. {
  175. GP_ERROR("Attempting to set a material on node '%s', which has no model.", sceneNode._nodeID);
  176. return;
  177. }
  178. else
  179. {
  180. Material* material = Material::create(p);
  181. node->getModel()->setMaterial(material, snp._index);
  182. SAFE_RELEASE(material);
  183. }
  184. break;
  185. case SceneNodeProperty::PARTICLE:
  186. {
  187. ParticleEmitter* particleEmitter = ParticleEmitter::create(p);
  188. node->setParticleEmitter(particleEmitter);
  189. SAFE_RELEASE(particleEmitter);
  190. break;
  191. }
  192. case SceneNodeProperty::COLLISION_OBJECT:
  193. {
  194. // Check to make sure the type of the namespace used to load the physics collision object is correct.
  195. if (snp._type == SceneNodeProperty::COLLISION_OBJECT && strcmp(p->getNamespace(), "collisionObject") != 0)
  196. {
  197. GP_ERROR("Attempting to set a physics collision object on a node using a '%s' definition.", p->getNamespace());
  198. return;
  199. }
  200. else
  201. {
  202. // If the scene file specifies a rigid body model, use it for creating the collision object.
  203. Properties* np = sceneProperties->getNamespace(sceneNode._nodeID);
  204. const char* name = NULL;
  205. if (np && (name = np->getString("rigidBodyModel")))
  206. {
  207. GP_ASSERT(scene);
  208. Node* modelNode = scene->findNode(name);
  209. if (!modelNode)
  210. {
  211. GP_ERROR("Node '%s' does not exist; attempting to use its model for collision object creation.", name);
  212. return;
  213. }
  214. else
  215. {
  216. if (!modelNode->getModel())
  217. {
  218. GP_ERROR("Node '%s' does not have a model; attempting to use its model for collision object creation.", name);
  219. }
  220. else
  221. {
  222. // Temporarily set rigidBody model on model so it's used during collision object creation.
  223. Model* model = node->getModel();
  224. // Up ref count to prevent node from releasing the model when we swap it.
  225. if (model)
  226. model->addRef();
  227. // Create collision object with new rigidBodyModel set.
  228. node->setModel(modelNode->getModel());
  229. node->setCollisionObject(p);
  230. // Restore original model.
  231. node->setModel(model);
  232. // Decrement temporarily added reference.
  233. if (model)
  234. model->release();
  235. }
  236. }
  237. }
  238. else
  239. node->setCollisionObject(p);
  240. }
  241. break;
  242. }
  243. default:
  244. GP_ERROR("Unsupported node property type (%d).", snp._type);
  245. break;
  246. }
  247. }
  248. else
  249. {
  250. // Handle scale, rotate and translate.
  251. Properties* np = sceneProperties->getNamespace(sceneNode._nodeID);
  252. switch (snp._type)
  253. {
  254. case SceneNodeProperty::TRANSLATE:
  255. {
  256. Vector3 t;
  257. if (np && np->getVector3("translate", &t))
  258. node->setTranslation(t);
  259. break;
  260. }
  261. case SceneNodeProperty::ROTATE:
  262. {
  263. Quaternion r;
  264. if (np && np->getQuaternionFromAxisAngle("rotate", &r))
  265. node->setRotation(r);
  266. break;
  267. }
  268. case SceneNodeProperty::SCALE:
  269. {
  270. Vector3 s;
  271. if (np && np->getVector3("scale", &s))
  272. node->setScale(s);
  273. break;
  274. }
  275. default:
  276. GP_ERROR("Unsupported node property type (%d).", snp._type);
  277. break;
  278. }
  279. }
  280. }
  281. void SceneLoader::applyNodeUrls(Scene* scene)
  282. {
  283. GP_ASSERT(scene);
  284. // Apply all URL node properties so that when we go to apply
  285. // the other node properties, the node is in the scene.
  286. for (unsigned int i = 0, ncount = _sceneNodes.size(); i < ncount; ++i)
  287. {
  288. SceneNode& sceneNode = _sceneNodes[i];
  289. // Iterate backwards over the properties list so we can remove properties as we go
  290. // without danger of indexing out of bounds.
  291. bool hasURL = false;
  292. for (int j = sceneNode._properties.size() - 1; j >= 0; --j)
  293. {
  294. SceneNodeProperty& snp = sceneNode._properties[j];
  295. if (snp._type != SceneNodeProperty::URL)
  296. continue;
  297. hasURL = true;
  298. std::string file;
  299. std::string id;
  300. splitURL(snp._url, &file, &id);
  301. if (file.empty())
  302. {
  303. // The node is from the main GPB and should just be renamed.
  304. // TODO: Should we do all nodes with this case first to allow users to stitch in nodes with
  305. // IDs equal to IDs that were in the original GPB file but were changed in the scene file?
  306. if (sceneNode._exactMatch)
  307. {
  308. Node* node = scene->findNode(id.c_str());
  309. if (node)
  310. {
  311. node->setId(sceneNode._nodeID);
  312. }
  313. else
  314. {
  315. GP_ERROR("Could not find node '%s' in main scene GPB file.", id.c_str());
  316. }
  317. sceneNode._nodes.push_back(node);
  318. }
  319. else
  320. {
  321. // Search for nodes using a partial match
  322. std::vector<Node*> nodes;
  323. unsigned int nodeCount = scene->findNodes(id.c_str(), nodes, true, false);
  324. if (nodeCount > 0)
  325. {
  326. for (unsigned int k = 0; k < nodeCount; ++k)
  327. {
  328. // Construct a new node ID using _nodeID plus the remainder of the partial match.
  329. Node* node = nodes[k];
  330. std::string newID(sceneNode._nodeID);
  331. newID += (node->getId() + id.length());
  332. node->setId(newID.c_str());
  333. sceneNode._nodes.push_back(node);
  334. }
  335. }
  336. else
  337. {
  338. GP_ERROR("Could not find any nodes matching '%s' in main scene GPB file.", id.c_str());
  339. }
  340. }
  341. }
  342. else
  343. {
  344. // An external file was referenced, so load the node(s) from file and then insert it into the scene with the new ID.
  345. // TODO: Revisit this to determine if we should cache Bundle objects for the duration of the scene
  346. // load to prevent constantly creating/destroying the same externally referenced bundles each time
  347. // a url with a file is encountered.
  348. Bundle* tmpBundle = Bundle::create(file.c_str());
  349. if (tmpBundle)
  350. {
  351. if (sceneNode._exactMatch)
  352. {
  353. Node* node = tmpBundle->loadNode(id.c_str(), scene);
  354. if (node)
  355. {
  356. node->setId(sceneNode._nodeID);
  357. scene->addNode(node);
  358. sceneNode._nodes.push_back(node);
  359. SAFE_RELEASE(node);
  360. }
  361. else
  362. {
  363. GP_ERROR("Could not load node '%s' from GPB file '%s'.", id.c_str(), file.c_str());
  364. }
  365. }
  366. else
  367. {
  368. // Search for nodes in the package using a partial match
  369. unsigned int objectCount = tmpBundle->getObjectCount();
  370. unsigned int matchCount = 0;
  371. for (unsigned int k = 0; k < objectCount; ++k)
  372. {
  373. const char* objid = tmpBundle->getObjectID(k);
  374. if (strstr(objid, id.c_str()) == objid)
  375. {
  376. // This object ID matches (starts with).
  377. // Try to load this object as a Node.
  378. Node* node = tmpBundle->loadNode(objid);
  379. if (node)
  380. {
  381. // Construct a new node ID using _nodeID plus the remainder of the partial match.
  382. std::string newID(sceneNode._nodeID);
  383. newID += (node->getId() + id.length());
  384. node->setId(newID.c_str());
  385. scene->addNode(node);
  386. sceneNode._nodes.push_back(node);
  387. SAFE_RELEASE(node);
  388. matchCount++;
  389. }
  390. }
  391. }
  392. if (matchCount == 0)
  393. {
  394. GP_ERROR("Could not find any nodes matching '%s' in GPB file '%s'.", id.c_str(), file.c_str());
  395. }
  396. }
  397. SAFE_RELEASE(tmpBundle);
  398. }
  399. else
  400. {
  401. GP_ERROR("Failed to load GPB file '%s' for node stitching.", file.c_str());
  402. }
  403. }
  404. // Remove the 'url' node property since we are done applying it.
  405. sceneNode._properties.erase(sceneNode._properties.begin() + j);
  406. // Processed URL property, no need to inspect remaining properties
  407. break;
  408. }
  409. if (!hasURL)
  410. {
  411. // No explicit URL, find the node in the main scene with the existing ID
  412. Node* node = scene->findNode(sceneNode._nodeID);
  413. if (node)
  414. {
  415. sceneNode._nodes.push_back(node);
  416. }
  417. else
  418. {
  419. GP_ERROR("Failed to locate node with id '%s' in bundle '%s'.", sceneNode._nodeID, _gpbPath.c_str());
  420. }
  421. }
  422. }
  423. }
  424. void SceneLoader::buildReferenceTables(Properties* sceneProperties)
  425. {
  426. // Go through the child namespaces of the scene.
  427. Properties* ns;
  428. const char* name = NULL;
  429. while ((ns = sceneProperties->getNextNamespace()) != NULL)
  430. {
  431. if (strcmp(ns->getNamespace(), "node") == 0)
  432. {
  433. if (strlen(ns->getId()) == 0)
  434. {
  435. GP_ERROR("Attempting to load a node without an ID.");
  436. continue;
  437. }
  438. // Add a SceneNode to the end of the list.
  439. _sceneNodes.resize(_sceneNodes.size() + 1);
  440. SceneNode& sceneNode = _sceneNodes[_sceneNodes.size()-1];
  441. sceneNode._nodeID = ns->getId();
  442. // Parse the node's sub-namespaces.
  443. Properties* subns;
  444. std::string propertyUrl = _path + "#" + ns->getId() + "/";
  445. while ((subns = ns->getNextNamespace()) != NULL)
  446. {
  447. if (strcmp(subns->getNamespace(), "audio") == 0)
  448. {
  449. propertyUrl += "audio/" + std::string(subns->getId());
  450. addSceneNodeProperty(sceneNode, SceneNodeProperty::AUDIO, propertyUrl.c_str());
  451. _properties[propertyUrl] = subns;
  452. }
  453. else if (strcmp(subns->getNamespace(), "material") == 0)
  454. {
  455. propertyUrl += "material/" + std::string(subns->getId());
  456. addSceneNodeProperty(sceneNode, SceneNodeProperty::MATERIAL, propertyUrl.c_str());
  457. _properties[propertyUrl] = subns;
  458. }
  459. else if (strcmp(subns->getNamespace(), "particle") == 0)
  460. {
  461. propertyUrl += "particle/" + std::string(subns->getId());
  462. addSceneNodeProperty(sceneNode, SceneNodeProperty::PARTICLE, propertyUrl.c_str());
  463. _properties[propertyUrl] = subns;
  464. }
  465. else if (strcmp(subns->getNamespace(), "collisionObject") == 0)
  466. {
  467. propertyUrl += "collisionObject/" + std::string(subns->getId());
  468. addSceneNodeProperty(sceneNode, SceneNodeProperty::COLLISION_OBJECT, propertyUrl.c_str());
  469. _properties[propertyUrl] = subns;
  470. }
  471. else if (strcmp(subns->getNamespace(), "tags") == 0)
  472. {
  473. while ((name = subns->getNextProperty()) != NULL)
  474. {
  475. sceneNode._tags[name] = subns->getString();
  476. }
  477. }
  478. else
  479. {
  480. GP_ERROR("Unsupported child namespace '%s' of 'node' namespace.", subns->getNamespace());
  481. }
  482. }
  483. // Parse the node's attributes.
  484. while ((name = ns->getNextProperty()) != NULL)
  485. {
  486. if (strcmp(name, "url") == 0)
  487. {
  488. addSceneNodeProperty(sceneNode, SceneNodeProperty::URL, ns->getString());
  489. }
  490. else if (strcmp(name, "audio") == 0)
  491. {
  492. addSceneNodeProperty(sceneNode, SceneNodeProperty::AUDIO, ns->getString());
  493. }
  494. else if (strncmp(name, "material", 8) == 0)
  495. {
  496. int materialIndex = -1;
  497. name = strchr(name, '[');
  498. if (name && strlen(name) >= 3)
  499. {
  500. std::string indexString(name);
  501. indexString = indexString.substr(1, indexString.size()-2);
  502. materialIndex = (unsigned int)atoi(indexString.c_str());
  503. }
  504. addSceneNodeProperty(sceneNode, SceneNodeProperty::MATERIAL, ns->getString(), materialIndex);
  505. }
  506. else if (strcmp(name, "particle") == 0)
  507. {
  508. addSceneNodeProperty(sceneNode, SceneNodeProperty::PARTICLE, ns->getString());
  509. }
  510. else if (strcmp(name, "collisionObject") == 0)
  511. {
  512. addSceneNodeProperty(sceneNode, SceneNodeProperty::COLLISION_OBJECT, ns->getString());
  513. }
  514. else if (strcmp(name, "rigidBodyModel") == 0)
  515. {
  516. // Ignore this for now. We process this when we do rigid body creation.
  517. }
  518. else if (strcmp(name, "translate") == 0)
  519. {
  520. addSceneNodeProperty(sceneNode, SceneNodeProperty::TRANSLATE);
  521. }
  522. else if (strcmp(name, "rotate") == 0)
  523. {
  524. addSceneNodeProperty(sceneNode, SceneNodeProperty::ROTATE);
  525. }
  526. else if (strcmp(name, "scale") == 0)
  527. {
  528. addSceneNodeProperty(sceneNode, SceneNodeProperty::SCALE);
  529. }
  530. else
  531. {
  532. GP_ERROR("Unsupported node property: %s = %s", name, ns->getString());
  533. }
  534. }
  535. }
  536. else if (strcmp(ns->getNamespace(), "animations") == 0)
  537. {
  538. // Load all the animations.
  539. Properties* animation;
  540. while ((animation = ns->getNextNamespace()) != NULL)
  541. {
  542. if (strcmp(animation->getNamespace(), "animation") == 0)
  543. {
  544. const char* animationID = animation->getId();
  545. if (strlen(animationID) == 0)
  546. {
  547. GP_ERROR("Attempting to load an animation without an ID.");
  548. continue;
  549. }
  550. const char* url = animation->getString("url");
  551. if (!url)
  552. {
  553. GP_ERROR("Attempting to load animation '%s' without a URL.", animationID);
  554. continue;
  555. }
  556. const char* targetID = animation->getString("target");
  557. if (!targetID)
  558. {
  559. GP_ERROR("Attempting to load animation '%s' without a target.", animationID);
  560. continue;
  561. }
  562. addSceneAnimation(animationID, targetID, url);
  563. }
  564. else
  565. {
  566. GP_ERROR("Unsupported child namespace (of 'animations'): %s", ns->getNamespace());
  567. }
  568. }
  569. }
  570. else if (strcmp(ns->getNamespace(), "physics") == 0)
  571. {
  572. // Note: we don't load physics until the whole scene file has been
  573. // loaded so that all node references (i.e. for constraints) can be resolved.
  574. }
  575. else
  576. {
  577. // TODO: Should we ignore these items? They could be used for generic properties file inheritance.
  578. GP_ERROR("Unsupported child namespace (of 'scene'): %s", ns->getNamespace());
  579. }
  580. }
  581. }
  582. void SceneLoader::createAnimations(const Scene* scene)
  583. {
  584. // Create the scene animations.
  585. for (unsigned int i = 0, count = _animations.size(); i < count; i++)
  586. {
  587. // If the target node doesn't exist in the scene, then we
  588. // can't do anything so we skip to the next animation.
  589. Node* node = scene->findNode(_animations[i]._targetID);
  590. if (!node)
  591. {
  592. GP_ERROR("Attempting to create an animation targeting node '%s', which does not exist in the scene.", _animations[i]._targetID);
  593. continue;
  594. }
  595. // Check to make sure the referenced properties object was loaded properly.
  596. Properties* p = _properties[_animations[i]._url];
  597. if (!p)
  598. {
  599. GP_ERROR("The referenced animation data at url '%s' failed to load.", _animations[i]._url.c_str());
  600. continue;
  601. }
  602. node->createAnimation(_animations[i]._animationID, p);
  603. }
  604. }
  605. PhysicsConstraint* SceneLoader::loadGenericConstraint(const Properties* constraint, PhysicsRigidBody* rbA, PhysicsRigidBody* rbB)
  606. {
  607. GP_ASSERT(rbA);
  608. GP_ASSERT(constraint);
  609. GP_ASSERT(Game::getInstance()->getPhysicsController());
  610. PhysicsGenericConstraint* physicsConstraint = NULL;
  611. // Create the constraint from the specified properties.
  612. Quaternion roA;
  613. Vector3 toA;
  614. bool offsetSpecified = constraint->getQuaternionFromAxisAngle("rotationOffsetA", &roA);
  615. offsetSpecified |= constraint->getVector3("translationOffsetA", &toA);
  616. if (offsetSpecified)
  617. {
  618. if (rbB)
  619. {
  620. Quaternion roB;
  621. Vector3 toB;
  622. constraint->getQuaternionFromAxisAngle("rotationOffsetB", &roB);
  623. constraint->getVector3("translationOffsetB", &toB);
  624. physicsConstraint = Game::getInstance()->getPhysicsController()->createGenericConstraint(rbA, roA, toB, rbB, roB, toB);
  625. }
  626. else
  627. {
  628. physicsConstraint = Game::getInstance()->getPhysicsController()->createGenericConstraint(rbA, roA, toA);
  629. }
  630. }
  631. else
  632. {
  633. physicsConstraint = Game::getInstance()->getPhysicsController()->createGenericConstraint(rbA, rbB);
  634. }
  635. GP_ASSERT(physicsConstraint);
  636. // Set the optional parameters that were specified.
  637. Vector3 v;
  638. if (constraint->getVector3("angularLowerLimit", &v))
  639. physicsConstraint->setAngularLowerLimit(v);
  640. if (constraint->getVector3("angularUpperLimit", &v))
  641. physicsConstraint->setAngularUpperLimit(v);
  642. if (constraint->getVector3("linearLowerLimit", &v))
  643. physicsConstraint->setLinearLowerLimit(v);
  644. if (constraint->getVector3("linearUpperLimit", &v))
  645. physicsConstraint->setLinearUpperLimit(v);
  646. return physicsConstraint;
  647. }
  648. PhysicsConstraint* SceneLoader::loadHingeConstraint(const Properties* constraint, PhysicsRigidBody* rbA, PhysicsRigidBody* rbB)
  649. {
  650. GP_ASSERT(rbA);
  651. GP_ASSERT(constraint);
  652. GP_ASSERT(Game::getInstance()->getPhysicsController());
  653. PhysicsHingeConstraint* physicsConstraint = NULL;
  654. // Create the constraint from the specified properties.
  655. Quaternion roA;
  656. Vector3 toA;
  657. constraint->getQuaternionFromAxisAngle("rotationOffsetA", &roA);
  658. constraint->getVector3("translationOffsetA", &toA);
  659. if (rbB)
  660. {
  661. Quaternion roB;
  662. Vector3 toB;
  663. constraint->getQuaternionFromAxisAngle("rotationOffsetB", &roB);
  664. constraint->getVector3("translationOffsetB", &toB);
  665. physicsConstraint = Game::getInstance()->getPhysicsController()->createHingeConstraint(rbA, roA, toB, rbB, roB, toB);
  666. }
  667. else
  668. {
  669. physicsConstraint = Game::getInstance()->getPhysicsController()->createHingeConstraint(rbA, roA, toA);
  670. }
  671. // Load the hinge angle limits (lower and upper) and the hinge bounciness (if specified).
  672. const char* limitsString = constraint->getString("limits");
  673. if (limitsString)
  674. {
  675. float lowerLimit, upperLimit;
  676. int scanned;
  677. scanned = sscanf(limitsString, "%f,%f", &lowerLimit, &upperLimit);
  678. if (scanned == 2)
  679. {
  680. physicsConstraint->setLimits(MATH_DEG_TO_RAD(lowerLimit), MATH_DEG_TO_RAD(upperLimit));
  681. }
  682. else
  683. {
  684. float bounciness;
  685. scanned = sscanf(limitsString, "%f,%f,%f", &lowerLimit, &upperLimit, &bounciness);
  686. if (scanned == 3)
  687. {
  688. physicsConstraint->setLimits(MATH_DEG_TO_RAD(lowerLimit), MATH_DEG_TO_RAD(upperLimit), bounciness);
  689. }
  690. else
  691. {
  692. GP_ERROR("Failed to parse 'limits' attribute for hinge constraint '%s'.", constraint->getId());
  693. }
  694. }
  695. }
  696. return physicsConstraint;
  697. }
  698. Scene* SceneLoader::loadMainSceneData(const Properties* sceneProperties)
  699. {
  700. GP_ASSERT(sceneProperties);
  701. // Load the main scene from the specified path.
  702. Bundle* bundle = Bundle::create(_gpbPath.c_str());
  703. if (!bundle)
  704. {
  705. GP_ERROR("Failed to load scene GPB file '%s'.", _gpbPath.c_str());
  706. return NULL;
  707. }
  708. // TODO: Support loading a specific scene from a GPB file using the URL syntax (i.e. "res/scene.gpb#myscene").
  709. Scene* scene = bundle->loadScene(NULL);
  710. if (!scene)
  711. {
  712. GP_ERROR("Failed to load scene from '%s'.", _gpbPath.c_str());
  713. SAFE_RELEASE(bundle);
  714. return NULL;
  715. }
  716. // Go through the supported scene properties and apply them to the scene.
  717. const char* name = sceneProperties->getString("activeCamera");
  718. if (name)
  719. {
  720. Node* camera = scene->findNode(name);
  721. if (camera && camera->getCamera())
  722. scene->setActiveCamera(camera->getCamera());
  723. }
  724. SAFE_RELEASE(bundle);
  725. return scene;
  726. }
  727. void SceneLoader::loadPhysics(Properties* physics, Scene* scene)
  728. {
  729. GP_ASSERT(physics);
  730. GP_ASSERT(scene);
  731. GP_ASSERT(Game::getInstance()->getPhysicsController());
  732. // Go through the supported global physics properties and apply them.
  733. Vector3 gravity;
  734. if (physics->getVector3("gravity", &gravity))
  735. Game::getInstance()->getPhysicsController()->setGravity(gravity);
  736. Properties* constraint;
  737. const char* name;
  738. while ((constraint = physics->getNextNamespace()) != NULL)
  739. {
  740. if (strcmp(constraint->getNamespace(), "constraint") == 0)
  741. {
  742. // Get the constraint type.
  743. std::string type = constraint->getString("type");
  744. // Attempt to load the first rigid body. If the first rigid body cannot
  745. // be loaded or found, then continue to the next constraint (error).
  746. name = constraint->getString("rigidBodyA");
  747. if (!name)
  748. {
  749. GP_ERROR("Missing property 'rigidBodyA' for constraint '%s'.", constraint->getId());
  750. continue;
  751. }
  752. Node* rbANode = scene->findNode(name);
  753. if (!rbANode)
  754. {
  755. GP_ERROR("Node '%s' to be used as 'rigidBodyA' for constraint '%s' cannot be found.", name, constraint->getId());
  756. continue;
  757. }
  758. if (!rbANode->getCollisionObject() || rbANode->getCollisionObject()->getType() != PhysicsCollisionObject::RIGID_BODY)
  759. {
  760. GP_ERROR("Node '%s' to be used as 'rigidBodyA' does not have a rigid body.", name);
  761. continue;
  762. }
  763. PhysicsRigidBody* rbA = static_cast<PhysicsRigidBody*>(rbANode->getCollisionObject());
  764. // Attempt to load the second rigid body. If the second rigid body is not
  765. // specified, that is usually okay (only spring constraints require both and
  766. // we check that below), but if the second rigid body is specified and it doesn't
  767. // load properly, then continue to the next constraint (error).
  768. name = constraint->getString("rigidBodyB");
  769. PhysicsRigidBody* rbB = NULL;
  770. if (name)
  771. {
  772. Node* rbBNode = scene->findNode(name);
  773. if (!rbBNode)
  774. {
  775. GP_ERROR("Node '%s' to be used as 'rigidBodyB' for constraint '%s' cannot be found.", name, constraint->getId());
  776. continue;
  777. }
  778. if (!rbBNode->getCollisionObject() || rbBNode->getCollisionObject()->getType() != PhysicsCollisionObject::RIGID_BODY)
  779. {
  780. GP_ERROR("Node '%s' to be used as 'rigidBodyB' does not have a rigid body.", name);
  781. continue;
  782. }
  783. rbB = static_cast<PhysicsRigidBody*>(rbBNode->getCollisionObject());
  784. }
  785. PhysicsConstraint* physicsConstraint = NULL;
  786. // Load the constraint based on its type.
  787. if (type == "FIXED")
  788. {
  789. physicsConstraint = Game::getInstance()->getPhysicsController()->createFixedConstraint(rbA, rbB);
  790. }
  791. else if (type == "GENERIC")
  792. {
  793. physicsConstraint = loadGenericConstraint(constraint, rbA, rbB);
  794. }
  795. else if (type == "HINGE")
  796. {
  797. physicsConstraint = loadHingeConstraint(constraint, rbA, rbB);
  798. }
  799. else if (type == "SOCKET")
  800. {
  801. physicsConstraint = loadSocketConstraint(constraint, rbA, rbB);
  802. }
  803. else if (type == "SPRING")
  804. {
  805. physicsConstraint = loadSpringConstraint(constraint, rbA, rbB);
  806. }
  807. else
  808. {
  809. GP_ERROR("Unsupported physics constraint type '%s'.", type.c_str());
  810. }
  811. // If the constraint failed to load, continue on to the next one.
  812. if (!physicsConstraint)
  813. {
  814. GP_ERROR("Failed to create physics constraint.");
  815. continue;
  816. }
  817. // If the breaking impulse was specified, apply it to the constraint.
  818. if (constraint->exists("breakingImpulse"))
  819. physicsConstraint->setBreakingImpulse(constraint->getFloat("breakingImpulse"));
  820. }
  821. else
  822. {
  823. GP_ERROR("Unsupported 'physics' child namespace '%s'.", physics->getNamespace());
  824. }
  825. }
  826. }
  827. void SceneLoader::loadReferencedFiles()
  828. {
  829. // Load all referenced properties files.
  830. std::map<std::string, Properties*>::iterator iter = _properties.begin();
  831. for (; iter != _properties.end(); ++iter)
  832. {
  833. if (iter->second == NULL)
  834. {
  835. std::string fileString;
  836. std::vector<std::string> namespacePath;
  837. calculateNamespacePath(iter->first, fileString, namespacePath);
  838. // Check if the referenced properties file has already been loaded.
  839. Properties* properties = NULL;
  840. std::map<std::string, Properties*>::iterator pffIter = _propertiesFromFile.find(fileString);
  841. if (pffIter != _propertiesFromFile.end() && pffIter->second)
  842. {
  843. properties = pffIter->second;
  844. }
  845. else
  846. {
  847. properties = Properties::create(fileString.c_str());
  848. if (properties == NULL)
  849. {
  850. GP_ERROR("Failed to load referenced properties file '%s'.", fileString.c_str());
  851. continue;
  852. }
  853. // Add the properties object to the cache.
  854. _propertiesFromFile.insert(std::make_pair(fileString, properties));
  855. }
  856. Properties* p = getPropertiesFromNamespacePath(properties, namespacePath);
  857. if (!p)
  858. {
  859. GP_ERROR("Failed to load referenced properties from url '%s'.", iter->first.c_str());
  860. continue;
  861. }
  862. iter->second = p;
  863. }
  864. }
  865. }
  866. PhysicsConstraint* SceneLoader::loadSocketConstraint(const Properties* constraint, PhysicsRigidBody* rbA, PhysicsRigidBody* rbB)
  867. {
  868. GP_ASSERT(rbA);
  869. GP_ASSERT(constraint);
  870. GP_ASSERT(Game::getInstance()->getPhysicsController());
  871. PhysicsSocketConstraint* physicsConstraint = NULL;
  872. Vector3 toA;
  873. bool offsetSpecified = constraint->getVector3("translationOffsetA", &toA);
  874. if (offsetSpecified)
  875. {
  876. if (rbB)
  877. {
  878. Vector3 toB;
  879. constraint->getVector3("translationOffsetB", &toB);
  880. physicsConstraint = Game::getInstance()->getPhysicsController()->createSocketConstraint(rbA, toA, rbB, toB);
  881. }
  882. else
  883. {
  884. physicsConstraint = Game::getInstance()->getPhysicsController()->createSocketConstraint(rbA, toA);
  885. }
  886. }
  887. else
  888. {
  889. physicsConstraint = Game::getInstance()->getPhysicsController()->createSocketConstraint(rbA, rbB);
  890. }
  891. return physicsConstraint;
  892. }
  893. PhysicsConstraint* SceneLoader::loadSpringConstraint(const Properties* constraint, PhysicsRigidBody* rbA, PhysicsRigidBody* rbB)
  894. {
  895. GP_ASSERT(rbA);
  896. GP_ASSERT(constraint);
  897. GP_ASSERT(Game::getInstance()->getPhysicsController());
  898. if (!rbB)
  899. {
  900. GP_ERROR("Spring constraints require two rigid bodies.");
  901. return NULL;
  902. }
  903. PhysicsSpringConstraint* physicsConstraint = NULL;
  904. // Create the constraint from the specified properties.
  905. Quaternion roA, roB;
  906. Vector3 toA, toB;
  907. bool offsetsSpecified = constraint->getQuaternionFromAxisAngle("rotationOffsetA", &roA);
  908. offsetsSpecified |= constraint->getVector3("translationOffsetA", &toA);
  909. offsetsSpecified |= constraint->getQuaternionFromAxisAngle("rotationOffsetB", &roB);
  910. offsetsSpecified |= constraint->getVector3("translationOffsetB", &toB);
  911. if (offsetsSpecified)
  912. {
  913. physicsConstraint = Game::getInstance()->getPhysicsController()->createSpringConstraint(rbA, roA, toB, rbB, roB, toB);
  914. }
  915. else
  916. {
  917. physicsConstraint = Game::getInstance()->getPhysicsController()->createSpringConstraint(rbA, rbB);
  918. }
  919. GP_ASSERT(physicsConstraint);
  920. // Set the optional parameters that were specified.
  921. Vector3 v;
  922. if (constraint->getVector3("angularLowerLimit", &v))
  923. physicsConstraint->setAngularLowerLimit(v);
  924. if (constraint->getVector3("angularUpperLimit", &v))
  925. physicsConstraint->setAngularUpperLimit(v);
  926. if (constraint->getVector3("linearLowerLimit", &v))
  927. physicsConstraint->setLinearLowerLimit(v);
  928. if (constraint->getVector3("linearUpperLimit", &v))
  929. physicsConstraint->setLinearUpperLimit(v);
  930. if (constraint->getString("angularDampingX"))
  931. physicsConstraint->setAngularDampingX(constraint->getFloat("angularDampingX"));
  932. if (constraint->getString("angularDampingY"))
  933. physicsConstraint->setAngularDampingY(constraint->getFloat("angularDampingY"));
  934. if (constraint->getString("angularDampingZ"))
  935. physicsConstraint->setAngularDampingZ(constraint->getFloat("angularDampingZ"));
  936. if (constraint->getString("angularStrengthX"))
  937. physicsConstraint->setAngularStrengthX(constraint->getFloat("angularStrengthX"));
  938. if (constraint->getString("angularStrengthY"))
  939. physicsConstraint->setAngularStrengthY(constraint->getFloat("angularStrengthY"));
  940. if (constraint->getString("angularStrengthZ"))
  941. physicsConstraint->setAngularStrengthZ(constraint->getFloat("angularStrengthZ"));
  942. if (constraint->getString("linearDampingX"))
  943. physicsConstraint->setLinearDampingX(constraint->getFloat("linearDampingX"));
  944. if (constraint->getString("linearDampingY"))
  945. physicsConstraint->setLinearDampingY(constraint->getFloat("linearDampingY"));
  946. if (constraint->getString("linearDampingZ"))
  947. physicsConstraint->setLinearDampingZ(constraint->getFloat("linearDampingZ"));
  948. if (constraint->getString("linearStrengthX"))
  949. physicsConstraint->setLinearStrengthX(constraint->getFloat("linearStrengthX"));
  950. if (constraint->getString("linearStrengthY"))
  951. physicsConstraint->setLinearStrengthY(constraint->getFloat("linearStrengthY"));
  952. if (constraint->getString("linearStrengthZ"))
  953. physicsConstraint->setLinearStrengthZ(constraint->getFloat("linearStrengthZ"));
  954. return physicsConstraint;
  955. }
  956. void splitURL(const std::string& url, std::string* file, std::string* id)
  957. {
  958. if (url.empty())
  959. {
  960. // This is allowed since many scene node properties do not use the URL.
  961. return;
  962. }
  963. // Check if the url references a file (otherwise, it only references a node within the main GPB).
  964. unsigned int loc = url.rfind(".");
  965. if (loc != url.npos)
  966. {
  967. // If the url references a specific namespace within the file,
  968. // set the id out parameter appropriately. Otherwise, set the id out
  969. // parameter to the empty string so we know to load the first namespace.
  970. loc = url.rfind("#");
  971. if (loc != url.npos)
  972. {
  973. *file = url.substr(0, loc);
  974. *id = url.substr(loc + 1);
  975. }
  976. else
  977. {
  978. *file = url;
  979. *id = std::string();
  980. }
  981. }
  982. else
  983. {
  984. *file = std::string();
  985. *id = url;
  986. }
  987. }
  988. SceneLoader::SceneNode::SceneNode()
  989. : _nodeID(""), _exactMatch(true)
  990. {
  991. }
  992. SceneLoader::SceneNodeProperty::SceneNodeProperty(Type type, const std::string& url, int index)
  993. : _type(type), _url(url), _index(index)
  994. {
  995. }
  996. }