TDME2  1.9.200
ModelEditorTabView.cpp
Go to the documentation of this file.
2 
3 #include <memory>
4 #include <string>
5 
6 #include <tdme/tdme.h>
7 #include <tdme/audio/Audio.h>
8 #include <tdme/audio/Sound.h>
21 #include <tdme/engine/Engine.h>
23 #include <tdme/engine/Object.h>
25 #include <tdme/engine/Timing.h>
27 #include <tdme/gui/GUI.h>
28 #include <tdme/math/Vector3.h>
47 #include <tdme/utilities/Action.h>
48 #include <tdme/utilities/Console.h>
53 
54 using std::make_unique;
55 using std::string;
56 using std::unique_ptr;
57 
58 using tdme::audio::Audio;
59 using tdme::audio::Sound;
77 using tdme::gui::GUI;
103 
104 ModelEditorTabView::ModelEditorTabView(EditorView* editorView, const string& tabId, Prototype* prototype)
105 {
106  this->editorView = editorView;
107  this->tabId = tabId;
108  this->popUps = editorView->getPopUps();
109  engine = unique_ptr<Engine>(Engine::createOffScreenInstance(512, 512, true, true, true));
110  engine->setPartition(new SimplePartition());
111  engine->setShadowMapLightEyeDistanceScale(0.1f);
112  engine->setSceneColor(Color4(39.0f / 255.0f, 39.0f / 255.0f, 39.0f / 255.0f, 1.0f));
113  audio = Audio::getInstance();
114  prototypeDisplayView = nullptr;
115  prototypePhysicsView = nullptr;
116  prototypeSoundsView = nullptr;
117  prototypeFileName = "";
118  lodLevel = 1;
119  audioStarted = -1LL;
120  audioOffset = -1LL;
121  cameraRotationInputHandler = make_unique<CameraRotationInputHandler>(engine.get(), this);
123  outlinerState.expandedOutlinerParentOptionValues.push_back("prototype");
124 }
125 
127 }
128 
130  return modelEditorTabController.get();
131 }
132 
134 {
135  engine->reset();
136  this->prototype = unique_ptr<Prototype>(prototype);
137  lodLevel = 1;
138  //
139  initModel(false);
140  // if setting a prototype before initalize() we have no controller yet
141  if (modelEditorTabController != nullptr) modelEditorTabController->setOutlinerContent();
142 }
143 
145 {
146  engine->reset();
147  initModel(true);
148  modelEditorTabController->setOutlinerContent();
149 }
150 
152 {
153  engine->reset();
154  initModel(true);
155  modelEditorTabController->setOutlinerContent();
156 }
157 
159 {
160  engine->reset();
161  initModel(false);
162  modelEditorTabController->setOutlinerContent();
163 }
164 
166 {
167  if (prototype == nullptr) return;
168  engine->removeEntity("model");
169  engine->removeEntity("attachment1");
170  attachment1Model = nullptr;
171  attachment1Bone.clear();
172  prototypeFileName = prototype->getFileName().length() > 0 ? prototype->getFileName() : prototype->getModelFileName();
173  Tools::setupPrototype(prototype.get(), engine.get(), cameraRotationInputHandler->getLookFromRotations(), lodLevel, objectScale, cameraRotationInputHandler.get(), 1.5f, resetup);
175  auto currentModelObject = dynamic_cast<Object*>(engine->getEntity("model"));
176  if (currentModelObject != nullptr) {
177  ModelStatistics modelStatistics;
178  ModelUtilities::computeModelStatistics(currentModelObject->getModel(), &modelStatistics);
179  modelEditorTabController->setStatistics(modelStatistics.opaqueFaceCount, modelStatistics.transparentFaceCount, modelStatistics.materialCount);
180  } else
181  {
182  modelEditorTabController->unsetStatistics();
183  }
184 }
185 
187 {
188  return prototypeFileName;
189 }
190 
192  return lodLevel;
193 }
194 
196  if (this->lodLevel != lodLevel) {
197  this->lodLevel = lodLevel;
198  initModel(false);
199  }
200 }
201 
203  engine->reset();
204  reloadPrototype();
205 }
206 
207 void ModelEditorTabView::loadModel(const string& pathName, const string& fileName)
208 {
209  // new model file
210  prototypeFileName = FileSystem::getInstance()->composeURI(pathName, fileName);
211  try {
212  // set model in entity
213  prototype->setModel(
214  ModelReader::read(
215  pathName,
216  fileName
217  )
218  );
219  } catch (Exception& exception) {
220  modelEditorTabController->showInfoPopUp("Warning", string(exception.what()));
221  }
223 }
224 
225 void ModelEditorTabView::reimportModel(const string& pathName, const string& fileName)
226 {
227  if (prototype == nullptr) return;
228  engine->removeEntity("model");
229  engine->removeEntity("attachment1");
230  attachment1Model = nullptr;
231  attachment1Bone.clear();
232  struct AnimationSetupStruct {
233  bool loop;
234  string overlayFromNodeId;
235  float speed;
236  };
237  // store old animation setups
238  map<string, AnimationSetupStruct> originalAnimationSetups;
239  for (const auto& [animationSetupId, animationSetup]: prototype->getModel()->getAnimationSetups()) {
240  originalAnimationSetups[animationSetup->getId()] = {
241  .loop = animationSetup->isLoop(),
242  .overlayFromNodeId = animationSetup->getOverlayFromNodeId(),
243  .speed = animationSetup->getSpeed()
244  };
245  }
246  // new model file
247  prototypeFileName = FileSystem::getInstance()->composeURI(pathName, fileName);
248  string log;
249  try {
250  // load model
251  auto model = unique_ptr<Model>(
252  ModelReader::read(
253  pathName,
254  fileName
255  )
256  );
257  // restore animation setup properties
258  for (const auto& [originalAnimationSetupId, originalAnimationSetup]: originalAnimationSetups) {
259  auto animationSetup = model->getAnimationSetup(originalAnimationSetupId);
260  if (animationSetup == nullptr) {
261  Console::println("ModelEditorTabView::reimportModel(): missing animation setup: " + originalAnimationSetupId);
262  log+= "Missing animation setup: " + originalAnimationSetupId + ", skipping.\n";
263  continue;
264  }
265  Console::println("ModelEditorTabView::reimportModel(): reimport animation setup: " + originalAnimationSetupId);
266  animationSetup->setLoop(originalAnimationSetup.loop);
267  animationSetup->setOverlayFromNodeId(originalAnimationSetup.overlayFromNodeId);
268  animationSetup->setSpeed(originalAnimationSetup.speed);
269  }
270  // set model in entity
271  prototype->setModel(model.release());
272  } catch (Exception& exception) {
273  modelEditorTabController->showInfoPopUp("Warning", string(exception.what()));
274  }
276  if (log.size() > 0) {
277  modelEditorTabController->showInfoPopUp("Warning", log);
278  }
279 }
280 
281 void ModelEditorTabView::saveFile(const string& pathName, const string& fileName)
282 {
283  PrototypeWriter::write(pathName, fileName, prototype.get());
284 }
285 
287 {
288  engine->reset();
289  loadModel();
290  initModel(true);
291  modelEditorTabController->setOutlinerContent();
292 }
293 
295  if (prototype == nullptr || prototype->getModel() == nullptr) return;
296  engine->removeEntity("model");
297  class ComputeNormalsProgressCallback: public ProgressCallback {
298  private:
299  ProgressBarScreenController* progressBarScreenController;
300  public:
301  ComputeNormalsProgressCallback(ProgressBarScreenController* progressBarScreenController): progressBarScreenController(progressBarScreenController) {
302  }
303  virtual void progress(float value) {
304  progressBarScreenController->progress(value);
305  }
306  };
307  popUps->getProgressBarScreenController()->show("Computing smooth normals ...");
308  ModelTools::computeNormals(prototype->getModel(), make_unique<ComputeNormalsProgressCallback>(popUps->getProgressBarScreenController()).release());
310  resetPrototype();
311 }
312 
314  if (prototype == nullptr || prototype->getModel() == nullptr) return;
315  engine->removeEntity("model");
316  prototype->setModel(ModelTools::optimizeModel(prototype->unsetModel()));
317  reloadPrototype();
318 }
319 
321 {
323  cameraRotationInputHandler->handleInputEvents();
324 }
325 
327 {
328  // audio
329  if (audioOffset > 0 && Time::getCurrentMillis() - audioStarted >= audioOffset) {
330  auto sound = audio->getEntity("sound");
331  if (sound != nullptr) sound->play();
332  audioOffset = -1LL;
333  }
334 
335  //
336  auto model = static_cast<Object*>(engine->getEntity("model"));
337 
338  // attachment1
339  auto attachment1 = static_cast<Object*>(engine->getEntity("attachment1"));
340  if (model != nullptr && attachment1 != nullptr) {
341  // model attachment bone matrix
342  auto transformMatrix = model->getNodeTransformMatrix(attachment1Bone);
343  transformMatrix*= model->getTransform().getTransformMatrix();
344  attachment1->setTranslation(transformMatrix * Vector3(0.0f, 0.0f, 0.0f));
345  // euler angles
346  auto euler = transformMatrix.computeEulerAngles();
347  // rotations
348  attachment1->setRotationAngle(0, euler.getZ());
349  attachment1->setRotationAngle(1, euler.getY());
350  attachment1->setRotationAngle(2, euler.getX());
351  // scale
352  Vector3 scale;
353  transformMatrix.getScale(scale);
354  attachment1->setScale(scale);
355  // finally update
356  attachment1->update();
357  }
358 
359  //
360  modelEditorTabController->updateInfoText(MutableString(engine->getTiming()->getAvarageFPS()).append(" FPS"));
361 
362  // rendering
365  engine->display();
366 }
367 
369 {
370  // TODO: a.drewke
371  try {
372  Properties settings;
373  settings.load("settings", "modeleditor.properties");
374  prototypePhysicsView->setDisplayBoundingVolume(settings.get("display.boundingvolumes", "false") == "true");
375  prototypeDisplayView->setDisplayGroundPlate(settings.get("display.groundplate", "true") == "true");
376  prototypeDisplayView->setDisplayShadowing(settings.get("display.shadowing", "true") == "true");
377  } catch (Exception& exception) {
378  Console::println("ModelEditorTabView::loadSettings(): An error occurred: " + string(exception.what()));
379  }
380 }
381 
383 {
384  try {
385  modelEditorTabController = make_unique<ModelEditorTabController>(this);
387  prototypePhysicsView = modelEditorTabController->getPrototypePhysicsSubController()->getView();
388  prototypeDisplayView = modelEditorTabController->getPrototypeDisplaySubController()->getView();
389  prototypeSoundsView = modelEditorTabController->getPrototypeSoundsSubController()->getView();
390  } catch (Exception& exception) {
391  Console::println("ModelEditorTabView::initialize(): An error occurred: " + string(exception.what()));
392  }
393  //
394  loadSettings();
395  //
397 }
398 
400 {
401  // TODO: a.drewke
402  try {
403  Properties settings;
404  settings.put("display.boundingvolumes", prototypePhysicsView->isDisplayBoundingVolume() == true ? "true" : "false");
405  settings.put("display.groundplate", prototypeDisplayView->isDisplayGroundPlate() == true ? "true" : "false");
406  settings.put("display.shadowing", prototypeDisplayView->isDisplayShadowing() == true ? "true" : "false");
407  settings.store("settings", "modeleditor.properties");
408  } catch (Exception& exception) {
409  Console::println("ModelEditorTabView::storeSettings(): An error occurred: " + string(exception.what()));
410  }
411 }
412 
414 
415 {
416  storeSettings();
417  engine->dispose();
418  audio->removeEntity("sound");
419 }
420 
422 {
423  try {
424  setPrototype(
426  FileSystem::getInstance()->getFileName(prototypeFileName),
427  "",
428  FileSystem::getInstance()->getPathName(prototypeFileName),
429  FileSystem::getInstance()->getFileName(prototypeFileName)
430  )
431  );
432  } catch (Exception& exception) {
433  popUps->getInfoDialogScreenController()->show("Warning", exception.what());
434  }
435 }
436 
437 Prototype* ModelEditorTabView::loadModelPrototype(const string& name, const string& description, const string& pathName, const string& fileName)
438 {
439  try {
440  auto prototype = unique_ptr<Prototype>(
441  PrototypeReader::read(
442  pathName,
443  fileName
444  )
445  );
446  return prototype.release();
447  } catch (Exception& exception) {
448  popUps->getInfoDialogScreenController()->show("Warning", exception.what());
449  }
450  //
451  return nullptr;
452 }
453 
454 void ModelEditorTabView::playAnimation(const string& baseAnimationId, const string& overlay1AnimationId, const string& overlay2AnimationId, const string& overlay3AnimationId) {
455  auto object = dynamic_cast<Object*>(engine->getEntity("model"));
456  if (object == nullptr) return;
457  audio->removeEntity("sound");
458  object->removeOverlayAnimations();
459  object->setAnimation(baseAnimationId);
460  if (overlay1AnimationId.empty() == false) object->addOverlayAnimation(overlay1AnimationId);
461  if (overlay2AnimationId.empty() == false) object->addOverlayAnimation(overlay2AnimationId);
462  if (overlay3AnimationId.empty() == false) object->addOverlayAnimation(overlay3AnimationId);
463 }
464 
465 void ModelEditorTabView::addAttachment1(const string& nodeId, const string& attachmentModelFile) {
466  engine->removeEntity("attachment1");
467  try {
469  unique_ptr<Model>(
470  attachmentModelFile.empty() == true?
471  nullptr:
472  ModelReader::read(Tools::getPathName(attachmentModelFile), Tools::getFileName(attachmentModelFile))
473  );
474  } catch (Exception& exception) {
475  Console::println("ModelEditorTabView::addAttachment1(): An error occurred: " + string(exception.what()));
476  popUps->getInfoDialogScreenController()->show("Warning", (exception.what()));
477  }
478  if (attachment1Model != nullptr) {
479  Entity* attachment = nullptr;
480  engine->addEntity(attachment = new Object("attachment1", attachment1Model.get()));
481  attachment->addRotation(Vector3(0.0f, 0.0f, 1.0f), 0.0f);
482  attachment->addRotation(Vector3(0.0f, 1.0f, 0.0f), 0.0f);
483  attachment->addRotation(Vector3(1.0f, 0.0f, 0.0f), 0.0f);
484  }
485  attachment1Bone = nodeId;
486 }
487 
488 void ModelEditorTabView::setAttachment1NodeId(const string& nodeId) {
489  attachment1Bone = nodeId;
490 }
491 
492 void ModelEditorTabView::playSound(const string& soundId) {
493  audio->removeEntity("sound");
494  auto object = dynamic_cast<Object*>(engine->getEntity("model"));
495  auto soundDefinition = prototype->getSound(soundId);
496  if (soundDefinition == nullptr || soundDefinition->getFileName().empty() == true) return;
497  ///
498  if (object != nullptr && soundDefinition->getAnimation().empty() == false) object->setAnimation(soundDefinition->getAnimation());
499  auto pathName = PrototypeReader::getResourcePathName(
500  Tools::getPathName(prototype->getFileName()),
501  soundDefinition->getFileName()
502  );
503  auto fileName = Tools::getFileName(soundDefinition->getFileName());
504  auto sound = new Sound(
505  "sound",
506  pathName,
507  fileName
508  );
509  sound->setGain(soundDefinition->getGain());
510  sound->setPitch(soundDefinition->getPitch());
511  sound->setLooping(soundDefinition->isLooping());
512  sound->setFixed(true);
513  audio->addEntity(sound);
514  audioStarted = Time::getCurrentMillis();
515  audioOffset = -1LL;
516  if (soundDefinition->getOffset() <= 0) {
517  sound->play();
518  } else {
519  audioOffset = soundDefinition->getOffset();
520  }
521 }
522 
524  audio->removeEntity("sound");
525 }
526 
528  auto object = dynamic_cast<Object*>(engine->getEntity("model"));
529  if (object == nullptr || prototype == nullptr) return;
530  engine->removeEntity("model");
531  ModelTools::prepareForShader(prototype->getModel(), prototype->getShader());
532  reloadPrototype();
533 }
534 
536  auto object = dynamic_cast<Object*>(engine->getEntity("model"));
537  if (object == nullptr || prototype == nullptr) return;
538  for (const auto& parameterName: Engine::getShaderParameterNames(prototype->getShader())) {
539  auto parameterValue = prototype->getShaderParameters().getShaderParameter(parameterName);
540  object->setShaderParameter(parameterName, parameterValue);
541  }
542 }
543 
546 }
547 
550 }
551 
553  return engine.get();
554 }
555 
557  modelEditorTabController->setOutlinerAddDropDownContent();
558  modelEditorTabController->setOutlinerContent();
561 }
562 
565 }
566 
568  modelEditorTabController->setOutlinerContent();
570 }
virtual void play()=0
Plays this audio entity.
Interface to audio module.
Definition: Audio.h:29
AudioEntity * getEntity(const string &id)
Returns an audio entity identified by given id.
Definition: Audio.h:145
void addEntity(AudioEntity *entity)
Adds a audio entity.
Definition: Audio.cpp:67
void removeEntity(const string &id)
Removes an audio entity.
Definition: Audio.cpp:86
Sound audio entity implementation.
Definition: Sound.h:19
Color 4 definition class.
Definition: Color4.h:18
Engine main class.
Definition: Engine.h:131
Engine entity.
Definition: Entity.h:30
virtual void addRotation(const Vector3 &axis, const float angle)=0
Add rotation.
Object to be used with engine class.
Definition: Object.h:60
Bogus/Simple partition implementation.
Timing class.
Definition: Timing.h:16
Representation of a 3D model.
Definition: Model.h:35
Axis aligned bounding box used for frustum, this is not directly connectable with physics engine.
Definition: BoundingBox.h:26
Base property model class.
Definition: BaseProperty.h:15
Prototype audio definition.
Prototype definition.
Definition: Prototype.h:55
const Matrix4x4 getNodeTransformMatrix(const string &id)
Returns transform matrix for given node.
Definition: ObjectBase.h:269
GUI module class.
Definition: GUI.h:64
GUI screen node that represents a screen that can be rendered via GUI system.
Definition: GUIScreenNode.h:72
Vector3 computeEulerAngles() const
Compute Euler angles (rotation around x, y, z axes)
Definition: Matrix4x4.h:531
Matrix4x4 & setTranslation(const Vector3 &translation)
Set translation.
Definition: Matrix4x4.h:442
Vector3 class representing vector3 mathematical structure and operations with x, y,...
Definition: Vector3.h:20
File system singleton class.
Definition: FileSystem.h:17
void restoreOutlinerState(const TabView::OutlinerState &outlinerState)
Restore outliner state.
void storeOutlinerState(TabView::OutlinerState &outlinerState)
Store outliner state.
void show(const string &caption, const string &message)
Shows the pop up.
void show(const string &message, bool showProgressBar=true)
Shows the pop up.
Pop ups controller accessor class.
Definition: PopUps.h:29
ProgressBarScreenController * getProgressBarScreenController()
Definition: PopUps.h:82
InfoDialogScreenController * getInfoDialogScreenController()
Definition: PopUps.h:75
void saveFile(const string &pathName, const string &fileName)
Saving prototype as tmodel prototype.
void playSound(const string &soundId) override
Play sound.
unique_ptr< ModelEditorTabController > modelEditorTabController
void onCameraRotation() override
On rotation event to be overloaded.
void addAttachment1(const string &nodeId, const string &attachmentModelFile)
Add attachment 1.
void handleInputEvents() override
Handle input events that have not yet been processed.
void setLODLevel(int lodLevel)
Set LOD level to display.
virtual Prototype * loadModelPrototype(const string &name, const string &description, const string &pathName, const string &fileName)
Load model prototype.
void playAnimation(const string &baseAnimationId, const string &overlay1AnimationId=string(), const string &overlay2AnimationId=string(), const string &overlay3AnimationId=string())
Play animation.
unique_ptr< CameraRotationInputHandler > cameraRotationInputHandler
void setPrototype(Prototype *prototype)
Set prototype.
void updateShaderParemeters()
Update shader parameters.
void reimportModel(const string &pathName, const string &fileName)
Issue reimport model file.
void onCameraScale() override
On scale event to be overloaded.
void setAttachment1NodeId(const string &nodeId)
Set attachment 1 node id.
void setDisplayShadowing(bool shadowing)
Set up shadow rendering.
void setDisplayGroundPlate(bool groundPlate)
Set up ground plate visibility.
void handleInputEvents(Prototype *prototype)
Handle input events.
void setDisplayBoundingVolume(bool displayBoundingVolume)
Set up bounding volume visibility.
void setObjectScale(const Vector3 &objectScale)
Set object scale.
EditorScreenController * getScreenController()
Definition: EditorView.h:69
Console class.
Definition: Console.h:29
Model tools functions class.
Definition: ModelTools.h:42
Mutable utf8 aware string class.
Definition: MutableString.h:23
Properties class, which helps out with storeing or loading key value pairs from/to property files.
Definition: Properties.h:23
void put(const string &key, const string &value)
Add property.
Definition: Properties.h:58
const string & get(const string &key, const string &defaultValue) const
Get property value by key.
Definition: Properties.h:46
void load(const string &pathName, const string &fileName, FileSystemInterface *fileSystem=nullptr)
Load property file.
Definition: Properties.cpp:24
void store(const string &pathName, const string &fileName, FileSystemInterface *fileSystem=nullptr) const
Store property file.
Definition: Properties.cpp:41
String tools class.
Definition: StringTools.h:22
std::exception Exception
Exception base class.
Definition: Exception.h:18
Tab controller, which connects UI with logic.
Definition: TabController.h:34
Playable sound view interface, which represents a view that supports playing sounds additionally.
Action Interface.
Definition: Action.h:11