Skyscraper 2.0
dynamicmesh.cpp
Go to the documentation of this file.
1/*
2 Scalable Building Simulator - Dynamic Mesh
3 The Skyscraper Project - Version 2.0
4 Copyright (C)2004-2024 Ryan Thoryk
5 https://www.skyscrapersim.net
6 https://sourceforge.net/projects/skyscraper/
7 Contact - ryan@skyscrapersim.net
8
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22*/
23
24#include <OgreSceneManager.h>
25#include <OgreSubMesh.h>
26#include <OgreMeshManager.h>
27#include <OgreMesh.h>
28#include <OgreEntity.h>
29#include <OgreCamera.h>
30#include "globals.h"
31#include "sbs.h"
32#include "mesh.h"
33#include "wall.h"
34#include "polymesh.h"
35#include "polygon.h"
36#include "triangle.h"
37#include "texture.h"
38#include "scenenode.h"
39#include "profiler.h"
40#include "dynamicmesh.h"
41
42//this file includes function implementations of the low-level SBS geometry and mesh code
43
44namespace SBS {
45
46DynamicMesh::DynamicMesh(Object* parent, SceneNode *node, const std::string &name, Real max_render_distance, bool dynamic_buffers) : ObjectBase(parent)
47{
48 //creates a new Dynamic Mesh object, which manages multiple sets of geometry data for efficiency.
49 //A MeshObject is a client of this object, which submits it's geometry data to be handled automatically
50
51 //if "dynamic_buffers" is true, a MeshObject's geometry can be part of a larger combined mesh, yet still be movable
52 //and rotated independently like a regular mesh, by using dynamic (writable) render buffers, becoming a virtual mesh
53
54 SetName(name);
55 this->node = node;
56 render_distance = max_render_distance;
57 file_model = false;
58 prepared = false;
59 this->dynamic_buffers = dynamic_buffers;
60 force_combine = false;
61 auto_combine = sbs->GetConfigBool("Skyscraper.SBS.DynamicMesh.AutoCombine", true);
63}
64
66{
67 for (size_t i = 0; i < meshes.size(); i++)
68 {
69 meshes[i]->parent_deleting = true;
70 delete meshes[i];
71 }
72 meshes.clear();
73
74 if (sbs->FastDelete == false)
76}
77
78bool DynamicMesh::LoadFromFile(const std::string &filename, const std::string &path)
79{
80 if (meshes.empty() == false)
81 return false;
82
83 Mesh* mesh = new Mesh(this, "", node, render_distance, filename, "", path);
84
85 //if load failed
86 if (!mesh->MeshWrapper)
87 {
88 delete mesh;
89 return false;
90 }
91
92 meshes.emplace_back(mesh);
93 file_model = true;
94 return true;
95}
96
97bool DynamicMesh::LoadFromMesh(const std::string &meshname)
98{
99 if (meshes.empty() == false)
100 return false;
101
102 Mesh* mesh = new Mesh(this, "", node, render_distance, "", meshname);
103
104 //if load failed
105 if (!mesh->MeshWrapper)
106 {
107 delete mesh;
108 return false;
109 }
110
111 meshes.emplace_back(mesh);
112 file_model = true;
113 return true;
114}
115
116void DynamicMesh::Enabled(bool value, MeshObject *client)
117{
118 if (client == 0 || meshes.size() == 1)
119 {
120 //manage client enabled status
121 if (client != 0)
122 {
123 int index = GetClientIndex(client);
124 if (index >= 0)
125 client_enable[index] = value;
126
127 //don't disable mesh if another client has it enabled
128 if (value == false && (int)client_enable.size() > 1)
129 {
130 for (size_t i = 0; i < client_enable.size(); i++)
131 {
132 if (i == index)
133 continue;
134
135 if (client_enable[i] == true)
136 return;
137 }
138 }
139 }
140
141 //enable all meshes if no client specified
142 for (size_t i = 0; i < meshes.size(); i++)
143 meshes[i]->Enabled(value);
144 }
145 else if (meshes.size() > 1)
146 {
147 //enable a per-client mesh if specified
148
149 int index = GetClientIndex(client);
150
151 if (index >= 0)
152 meshes[index]->Enabled(value);
153 }
154}
155
156bool DynamicMesh::ChangeTexture(const std::string &old_texture, const std::string &new_texture, MeshObject *client)
157{
158 if (client == 0 || meshes.size() == 1)
159 {
160 bool result = true;
161 for (size_t i = 0; i < meshes.size(); i++)
162 {
163 bool change = meshes[i]->ChangeTexture(old_texture, new_texture);
164 if (change == false)
165 result = false;
166 }
167 return result;
168 }
169 else if (meshes.size() > 1)
170 {
171 int index = GetClientIndex(client);
172
173 if (index >= 0)
174 return meshes[index]->ChangeTexture(old_texture, new_texture);
175 }
176 return false;
177}
178
180{
181 if (client == 0 || meshes.size() == 1)
182 {
183 for (size_t i = 0; i < meshes.size(); i++)
184 meshes[i]->EnableDebugView(value);
185 }
186 else if (meshes.size() > 1)
187 {
188 int index = GetClientIndex(client);
189
190 if (index >= 0)
191 meshes[index]->EnableDebugView(value);
192 }
193}
194
196{
197 if (meshes.empty() == true)
198 return false;
199
200 if (client == 0 || meshes.size() == 1)
201 return meshes[0]->IsVisible();
202 else if (meshes.size() > 1)
203 {
204 int index = GetClientIndex(client);
205
206 if (index >= 0)
207 return meshes[index]->IsVisible();
208 }
209 return false;
210}
211
212bool DynamicMesh::IsVisible(Ogre::Camera *camera, MeshObject *client)
213{
214 if (meshes.empty() == true)
215 return false;
216
217 if (client == 0 || meshes.size() == 1)
218 return IsVisible(camera, 0);
219 else if (meshes.size() > 1)
220 {
221 int index = GetClientIndex(client);
222
223 if (index >= 0)
224 return IsVisible(camera, index);
225 }
226 return false;
227}
228
229bool DynamicMesh::IsVisible(Ogre::Camera *camera, int mesh_index)
230{
231 if (meshes.empty() == true)
232 return false;
233
234 if (mesh_index < 0 || mesh_index >= (int)meshes.size())
235 return false;
236
237 return meshes[mesh_index]->IsVisible(camera);
238}
239
241{
242 SBS_PROFILE("DynamicMesh::Prepare");
243
244 if (file_model == true || prepared == true)
245 return;
246
247 if (clients.empty() == true)
248 return;
249
250 //determine if meshes should be combined or separated
251 if (meshes.empty() == true)
252 {
253 int meshes_to_create = 0;
254
255 if (clients.size() > 1)
256 {
257 int total = GetMaterialCount();
258 int separate_total = 0;
259
260 int limit = 3; //compare against 3 client meshes totaled together
261
262 for (size_t i = 0; i < clients.size(); i++)
263 {
264 if (i == limit)
265 break;
266
267 //separate_total += clients[i]->GetPolyMesh()->GetSubmeshCount();
268 separate_total += 1;
269 }
270
271 bool combined = false;
272
273 //if combined submesh/material count is less than three separate meshes
274 if (((total < separate_total || total <= 10) && auto_combine == true) || force_combine == true)
275 {
276 meshes_to_create = 1; //create a single combined mesh for all clients
277 combined = true;
278 }
279 else
280 {
281 meshes_to_create = (int)clients.size(); //create separate meshes for each client
282 dynamic_buffers = false; //don't use dynamic buffers if keeping separate
283 }
284
285 std::string status;
286 if (combined == true)
287 {
288 if (force_combine == false)
289 status = "using combined";
290 else
291 status = "using force combined";
292 }
293 else
294 status = "using separate";
295
296 //print optimization report
297 //if (sbs->Verbose)
298 Report(this->GetName() + ": Combined: " + ToString(total) + " - Separate (3): " + ToString(separate_total) + " - " + status);
299 }
300 else
301 meshes_to_create = 1;
302
303 //create mesh objects
304 for (int i = 0; i < meshes_to_create; i++)
305 {
306 Mesh *mesh = 0;
307
308 if (meshes_to_create == 1)
309 mesh = new Mesh(this, GetName(), node, render_distance);
310 else
311 mesh = new Mesh(this, clients[i]->GetName(), clients[i]->GetSceneNode(), render_distance);
312
313 meshes.emplace_back(mesh);
314 }
315 }
316
317 if (client == 0 || meshes.size() == 1)
318 {
319 for (size_t i = 0; i < meshes.size(); i++)
320 {
321 meshes[i]->prepared = false;
322 if (meshes.size() > 1)
323 meshes[i]->Prepare(true, (int)i);
324 else
325 meshes[i]->Prepare();
326 }
327 }
328 else if (meshes.size() > 1)
329 {
330 int index = GetClientIndex(client);
331
332 if (index >= 0)
333 meshes[index]->Prepare(true, index);
334 }
335
336 prepared = true;
337}
338
340{
341 //add a client mesh object to this dynamic mesh
342
343 clients.emplace_back(mesh);
344 client_enable.emplace_back(true);
345}
346
348{
349 //remove a client mesh from this dynamic mesh
350
351 for (size_t i = 0; i < clients.size(); i++)
352 {
353 if (clients[i] == mesh)
354 {
355 clients.erase(clients.begin() + i);
356 client_enable.erase(client_enable.begin() + i);
357 return;
358 }
359 }
360}
361
363{
364 if (number < 0 || number >= (int)clients.size())
365 return 0;
366
367 return clients[number];
368}
369
371{
372 if (!client)
373 return -1;
374
375 for (size_t i = 0; i < clients.size(); i++)
376 {
377 if (clients[i] == client)
378 return (int)i;
379 }
380 return -1;
381}
382
384{
385 if (prepared == false)
386 return;
387
388 if (client == 0 || meshes.size() == 1)
389 prepared = false;
390 else if (meshes.size() > 1)
391 {
392 int index = GetClientIndex(client);
393
394 if (index >= 0 && index < (int)meshes.size())
395 {
396 meshes[index]->prepared = false;
397 prepared = false;
398 }
399 }
400
401 Prepare(client);
402}
403
404int DynamicMesh::GetMaterials(std::vector<std::string> &materials, int client)
405{
406 //calculate the total number of materials used by all clients
407
408 int start = 0;
409 int end = (int)clients.size() - 1;
410
411 if (client >= 0)
412 {
413 start = client;
414 end = client;
415 }
416
417 //for each client
418 for (int i = start; i <= end; i++)
419 {
420 for (size_t j = 0; j < clients[i]->Walls.size(); j++)
421 {
422 if (!clients[i]->Walls[j])
423 continue;
424
425 for (size_t k = 0; k < clients[i]->Walls[j]->GetPolygonCount(); k++)
426 {
427 if (!clients[i]->Walls[j]->GetPolygon(k))
428 continue;
429
430 std::string material = clients[i]->Walls[j]->GetPolygon(k)->material;
431
432 //find material in current list
433 bool found = false;
434 for (size_t k = 0; k < materials.size(); k++)
435 {
436 if (materials[k] == material)
437 {
438 found = true;
439 break;
440 }
441 }
442
443 if (found == false)
444 materials.emplace_back(material);
445 }
446 }
447 }
448
449 return (int)materials.size();
450}
451
453{
454 //calculate the number of materials needed for all clients or a specific client
455
456 std::vector<std::string> materials;
457 int total = GetMaterials(materials, client);
458 return total;
459}
460
461unsigned int DynamicMesh::GetVertexCount(const std::string &material, int client)
462{
463 //calculate combined vertex count for all clients
464
465 int start = 0;
466 int end = (int)clients.size() - 1;
467
468 if (client >= 0)
469 {
470 start = client;
471 end = client;
472 }
473
474 unsigned int total = 0;
475
476 for (int i = start; i <= end; i++)
477 {
478 if (material != "")
479 {
480 for (size_t j = 0; j < clients[i]->Walls.size(); j++)
481 {
482 if (!clients[i]->Walls[j])
483 continue;
484
485 for (int k = 0; k < clients[i]->Walls[j]->GetPolygonCount(); k++)
486 {
487 Polygon *poly = clients[i]->Walls[j]->GetPolygon(k);
488
489 if (!poly)
490 continue;
491
492 if (poly->material == material)
493 {
494 for (size_t l = 0; l < poly->geometry.size(); l++)
495 {
496 total += poly->geometry[l].size();
497 }
498 }
499 }
500 }
501 }
502 else
503 total += clients[i]->GetVertexCount();
504 }
505
506 return total;
507}
508
509unsigned int DynamicMesh::GetTriangleCount(const std::string &material, int &client_count, int client)
510{
511 //calculate combined triangle count for all clients
512
513 int start = 0;
514 int end = (int)clients.size() - 1;
515
516 if (client >= 0)
517 {
518 start = client;
519 end = client;
520 }
521
522 unsigned int total = 0;
523 client_count = 0;
524
525 for (int i = start; i <= end; i++)
526 {
527 total += clients[i]->GetTriangleCount(material, false);
528 client_count += 1;
529 }
530
531 return total;
532}
533
535{
536 //get vertex index offset of specific client mesh
537 //if multiple geometry sets are combined together, each set has a starting index number
538
539 unsigned int index = 0;
540
541 //return value if using separate meshes
542 if (meshes.size() > 1)
543 return index;
544
545 for (size_t i = 0; i < clients.size(); i++)
546 {
547 //if found, return current index value
548 if (clients[i] == client)
549 return index;
550
551 //if not found, increment by client's vertex count
552 index += clients[i]->GetVertexCount();
553 }
554
555 return index;
556}
557
558void DynamicMesh::UpdateVertices(MeshObject *client, const std::string &material, Polygon *polygon, bool single)
559{
560 int client_index = GetClientIndex(client);
561
562 if (client_index == -1 || meshes.empty())
563 return;
564
565 if (meshes.size() == 1)
566 meshes[0]->UpdateVertices(client_index, material, polygon, single);
567 else
568 meshes[client_index]->UpdateVertices(client_index, material, polygon, single);
569}
570
572{
573 if (meshes.size() > 1)
574 {
575 int index = GetClientIndex(client);
576
577 if (index >= 0)
578 meshes[index]->Detach();
579 }
580}
581
583{
584 if (meshes.empty() == true)
585 return -1;
586
587 if (mesh_index < 0 || mesh_index >= (int)meshes.size())
588 return -1;
589
590 return meshes[mesh_index]->GetSubMeshCount();
591}
592
593std::string DynamicMesh::GetMeshName(int mesh_index)
594{
595 if (meshes.empty() == true)
596 return "";
597
598 if (mesh_index < 0 || mesh_index >= (int)meshes.size())
599 return "";
600
601 return meshes[mesh_index]->name;
602}
603
604Ogre::AxisAlignedBox DynamicMesh::GetBounds(MeshObject *client)
605{
606 if (meshes.empty() == true)
607 return Ogre::AxisAlignedBox::BOX_NULL;
608
609 if (client == 0 || meshes.size() == 1)
610 return meshes[0]->MeshWrapper->getBounds();
611 else if (meshes.size() > 1)
612 {
613 int index = GetClientIndex(client);
614
615 if (index >= 0)
616 return meshes[index]->MeshWrapper->getBounds();
617 }
618 return Ogre::AxisAlignedBox::BOX_NULL;
619}
620
622{
623 //enable shadows
624
625 for (size_t i = 0; i < meshes.size(); i++)
626 meshes[i]->EnableShadows(value);
627}
628
629void DynamicMesh::SetMaterial(const std::string& material)
630{
631 //set material on all meshes
632
633 for (size_t i = 0; i < meshes.size(); i++)
634 meshes[i]->SetMaterial(material);
635}
636
637DynamicMesh::Mesh::Mesh(DynamicMesh *parent, const std::string &name, SceneNode *node, Real max_render_distance, const std::string &filename, const std::string &meshname, const std::string &path)
638{
639 Parent = parent;
640 sbs = Parent->GetRoot();
641 this->node = node;
642 enabled = false;
643 prepared = false;
644 Movable = 0;
645 auto_shadows = true;
646 parent_deleting = false;
647
648 if (filename == "")
649 {
650 this->name = name;
651
652 //create mesh
653 if (meshname == "")
654 MeshWrapper = Ogre::MeshManager::getSingleton().createManual(node->GetNameBase() + name, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
655 else
656 {
657 //get true parent SBS object
658 Object *object = parent->GetParent()->GetParent()->GetParent();
659
660 //get loaded mesh
661 MeshWrapper = Ogre::MeshManager::getSingleton().getByName(object->GetNameBase() + meshname);
662
663 if (!MeshWrapper)
664 {
665 sbs->ReportError("Error loading mesh " + meshname + "\n");
666 return;
667 }
668
669 }
670 }
671 else
672 {
673 this->name = filename;
674
675 //load model
676 try
677 {
678 MeshWrapper = Ogre::MeshManager::getSingleton().load(filename, path);
679 }
680 catch (Ogre::Exception &e)
681 {
682 sbs->ReportError("Error loading model " + filename + "\n" + e.getDescription());
683 MeshWrapper = 0;
684 return;
685 }
686 }
687
688 //create and enable movable
689 Movable = sbs->mSceneManager->createEntity(node->GetNameBase() + name, MeshWrapper);
690 Enabled(true);
691
692 //set maximum render distance
693 Movable->setRenderingDistance(sbs->ToRemote(max_render_distance));
694}
695
697{
698 Detach();
699
700 for (size_t i = 0; i < client_entries.size(); i++)
701 {
702 delete client_entries[i].bounds;
703 }
704 client_entries.clear();
705
706 try
707 {
708 if (MeshWrapper)
709 {
710 if (Ogre::MeshManager::getSingleton().getByHandle(MeshWrapper->getHandle()))
711 Ogre::MeshManager::getSingleton().remove(MeshWrapper->getHandle());
712 }
713 }
714 catch (Ogre::Exception &e)
715 {
716 sbs->ReportError("Error unloading mesh: " + e.getDescription());
717 }
718 MeshWrapper = 0;
719}
720
722{
723 //attach or detach from scenegraph
724
725 if (enabled == value || !node)
726 return;
727
728 if (value == false)
729 node->DetachObject(Movable);
730 else
731 node->AttachObject(Movable);
732
733 if (auto_shadows == true)
734 Movable->setCastShadows(value);
735
736 enabled = value;
737}
738
739bool DynamicMesh::Mesh::ChangeTexture(const std::string &old_texture, const std::string &new_texture)
740{
741 //change a texture on this mesh
742
743 //if multiple clients are referencing the matching submesh,
744 //re-prepare mesh to move client's triangles into the proper new submesh,
745 //to prevent changing (and interfering with) other clients' textures
746
747 SBS_PROFILE("DynamicMesh::Mesh::ChangeTexture");
748
749 //get new material
750 Ogre::MaterialPtr newmat = sbs->GetTextureManager()->GetMaterialByName(new_texture, "General");
751
752 if (!newmat.get())
753 return sbs->ReportError("ChangeTexture: Invalid texture '" + new_texture + "'");
754
755 int submesh = FindMatchingSubMesh(old_texture);
756
757 if (submesh == -1)
758 return false;
759
760 /*bool reprepare = false;
761
762 if (Submeshes[submesh].clients > 1)
763 reprepare = true; //re-prepare if breaking out of shared mesh
764 else
765 {
766 int existing = FindMatchingSubMesh(new_texture);
767
768 if (existing > -1)
769 reprepare = true; //re-prepare if integrating into an existing shared mesh
770 }
771
772 if (reprepare == true)
773 {
774 //re-prepare mesh
775 prepared = false;
776 Prepare(false);
777 return true;
778 }*/
779
780 //set material if valid
781 Submeshes[submesh].object->setMaterialName(ToString(sbs->InstanceNumber) + ":" + new_texture);
782
783 //apply changes (refresh mesh state)
784 MeshWrapper->_dirtyState();
785
786 return true;
787}
788
789int DynamicMesh::Mesh::FindMatchingSubMesh(const std::string &material)
790{
791 //find a submesh with a matching material
792 //returns array index
793
794 std::string full_name = ToString(sbs->InstanceNumber) + ":" + material;
795
796 for (size_t i = 0; i < Submeshes.size(); i++)
797 {
798 if (Submeshes[i].object->getMaterialName() == full_name)
799 return (int)i;
800 }
801 return -1;
802}
803
805{
806 if (!node)
807 return 0;
808
809 int index = (int)Submeshes.size();
810
811 //create and add submesh
812 Submesh submesh;
813 submesh.object = MeshWrapper->createSubMesh(node->GetFullName() + ":" + ToString(GetSubMeshCount()));
814 submesh.object->useSharedVertices = true;
815 submesh.clients = 0;
816
817 //bind material
818 submesh.object->setMaterialName(ToString(sbs->InstanceNumber) + ":" + material);
819 submesh.material = material;
820
821 Submeshes.emplace_back(submesh);
822 return &Submeshes[index];
823}
824
825void DynamicMesh::Mesh::DeleteSubMesh(int client, int index)
826{
827 //delete a submesh
828 //if no index is provided, delete any empty submeshes
829
830 if (index == -1)
831 {
832 for (size_t i = 0; i < Submeshes.size(); i++)
833 {
834 bool used = false;
835
836 if (client == -1)
837 {
838 for (int j = 0; j < Parent->GetClientCount(); j++)
839 {
840 for (size_t k = 0; k < Parent->GetClient(j)->Walls.size(); k++)
841 {
842 if (!Parent->GetClient(j)->Walls[k])
843 continue;
844
845 for (int l = 0; l < Parent->GetClient(j)->Walls[k]->GetPolygonCount(); l++)
846 {
847 Polygon *poly = Parent->GetClient(j)->Walls[k]->GetPolygon(l);
848 if (!poly)
849 continue;
850
851 if (poly->material == Submeshes[i].material)
852 {
853 used = true;
854 break;
855 }
856 }
857 }
858 }
859 }
860 else
861 {
862 if (!Parent->GetClient(client))
863 return;
864
865 for (size_t k = 0; k < Parent->GetClient(client)->Walls.size(); k++)
866 {
867 if (!Parent->GetClient(client)->Walls[k])
868 continue;
869
870 for (int l = 0; l < Parent->GetClient(client)->Walls[k]->GetPolygonCount(); l++)
871 {
872 Polygon *poly = Parent->GetClient(client)->Walls[k]->GetPolygon(l);
873 if (!poly)
874 continue;
875
876 if (poly->material == Submeshes[i].material)
877 {
878 used = true;
879 break;
880 }
881 }
882 }
883 }
884
885 if (used == false)
886 {
887 MeshWrapper->destroySubMesh((unsigned short)i);
888 Submeshes.erase(Submeshes.begin() + i);
889 i--;
890 }
891 }
892 }
893 else if (index >= 0 && index < (int)Submeshes.size())
894 {
895 MeshWrapper->destroySubMesh(index);
896 Submeshes.erase(Submeshes.begin() + index);
897 }
898}
899
900void DynamicMesh::Mesh::Prepare(bool process_vertices, int client)
901{
902 //prepare mesh object
903
904 //this function collects stored geometry and triangle data from all associated client meshes,
905 //uses those to build GPU vertex and index render buffers, and prepares the dynamic mesh for rendering
906 //geometry arrays must be populated correctly before this function is called
907
908 //all submeshes share mesh vertex data, but triangle indices are stored in each submesh
909 //each submesh represents a portion of the mesh that uses the same material
910
911 SBS_PROFILE("DynamicMesh::Mesh::Prepare");
912
913 if (prepared == true || !node)
914 return;
915
916 unsigned int vertex_count = Parent->GetVertexCount("", client);
917
918 std::vector<std::string> materials;
919 int submesh_count = Parent->GetMaterials(materials, client);
920
921 //clear vertex data and exit if there's no associated submesh or geometry data
922 if (submesh_count == 0 || vertex_count == 0)
923 {
924 if (MeshWrapper->sharedVertexData)
925 delete MeshWrapper->sharedVertexData;
926 MeshWrapper->sharedVertexData = new Ogre::VertexData();
927
928 //delete any existing submeshes
929 for (size_t i = 0; i < Submeshes.size(); i++)
930 {
931 DeleteSubMesh(-1, (int)i);
932 }
933
934 return;
935 }
936
937 //if removing existing submeshes, use existing submesh count
938 if (submesh_count < (int)Submeshes.size())
939 submesh_count = (int)Submeshes.size();
940
941 int start = 0;
942 int end = Parent->GetClientCount() - 1;
943
944 if (client > -1)
945 {
946 start = client;
947 end = client;
948 }
949
950 size_t previous_count = 0;
951
952 if (process_vertices == true)
953 {
954 //set up vertex buffer
955 if (MeshWrapper->sharedVertexData)
956 {
957 previous_count = MeshWrapper->sharedVertexData->vertexCount;
958 delete MeshWrapper->sharedVertexData;
959 }
960 Ogre::VertexData* data = new Ogre::VertexData();
961 MeshWrapper->sharedVertexData = data;
962 data->vertexCount = vertex_count;
963 Ogre::VertexDeclaration* decl = data->vertexDeclaration;
964
965 //set up vertex data elements
966 size_t offset = 0;
967 decl->addElement(0, offset, Ogre::VET_FLOAT3, Ogre::VES_POSITION); //vertices
968 offset += Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3);
969 decl->addElement(0, offset, Ogre::VET_FLOAT3, Ogre::VES_NORMAL); //normals
970 offset += Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3);
971 decl->addElement(0, offset, Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES); //texels
972
973 //set up vertex data arrays
974 float *mVertexElements = new float[vertex_count * 8];
975
976 //populate array with vertex geometry from each client mesh
977 unsigned int loc = 0;
978 unsigned int vindex = 0;
979
980 //clear vertex offset, counts, and bounds tables
981 for (size_t i = 0; i < client_entries.size(); i++)
982 {
983 delete client_entries[i].bounds;
984 }
985 client_entries.clear();
986
987 for (int num = start; num <= end; num++)
988 {
989 MeshObject *mesh = Parent->GetClient(num);
990 Ogre::AxisAlignedBox client_box;
991 Real radius = 0;
992
993 ClientEntry entry;
994
995 //add current client's vertex index to offset table
996 entry.vertex_offset = vindex;
997
998 //get mesh's offset of associated scene node
999 Vector3 offset = sbs->ToRemote(mesh->GetPosition() - node->GetPosition());
1000
1001 //fill array with mesh's geometry data, from each wall
1002 for (size_t index = 0; index < mesh->Walls.size(); index++)
1003 {
1004 if (!mesh->Walls[index])
1005 continue;
1006
1007 for (size_t i = 0; i < mesh->Walls[index]->GetPolygonCount(); i++)
1008 {
1009 Polygon *poly = mesh->Walls[index]->GetPolygon(i);
1010
1011 if (!poly)
1012 continue;
1013
1014 for (size_t j = 0; j < poly->geometry.size(); j++)
1015 {
1016 for (size_t k = 0; k < poly->geometry[j].size(); k++)
1017 {
1018 Polygon::Geometry &element = poly->geometry[j][k];
1019
1020 //make mesh's vertex relative to this scene node
1021 Vector3 vertex;
1022 if (client == -1)
1023 {
1024 Vector3 raw_vertex = mesh->GetOrientation() * element.vertex; //add mesh's rotation
1025 vertex = (node->GetOrientation().Inverse() * raw_vertex) + offset; //remove node's rotation and add mesh offset
1026 }
1027 else
1028 vertex = element.vertex;
1029
1030 //add elements to array
1031 mVertexElements[loc] = (float)vertex.x;
1032 mVertexElements[loc + 1] = (float)vertex.y;
1033 mVertexElements[loc + 2] = (float)vertex.z;
1034 mVertexElements[loc + 3] = (float)element.normal.x;
1035 mVertexElements[loc + 4] = (float)element.normal.y;
1036 mVertexElements[loc + 5] = (float)element.normal.z;
1037 mVertexElements[loc + 6] = (float)element.texel.x;
1038 mVertexElements[loc + 7] = (float)element.texel.y;
1039 client_box.merge(vertex);
1040 radius = std::max(radius, vertex.length());
1041 loc += 8;
1042 }
1043 }
1044 }
1045 }
1046
1047 //store client bounding box and radius
1048 entry.bounds = new Ogre::AxisAlignedBox(client_box);
1049 entry.radius = radius;
1050
1051 //add client vertex count to list
1052 entry.vertex_count = mesh->GetVertexCount();
1053 vindex += entry.vertex_count;
1054
1055 //store client information
1056 client_entries.emplace_back(entry);
1057 }
1058
1059 //create vertex hardware buffer
1060 Ogre::HardwareVertexBufferSharedPtr vbuffer;
1061 if (Parent->UseDynamicBuffers() == false)
1062 vbuffer = Ogre::HardwareBufferManager::getSingleton().createVertexBuffer(decl->getVertexSize(0), vertex_count, Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY);
1063 else
1064 vbuffer = Ogre::HardwareBufferManager::getSingleton().createVertexBuffer(decl->getVertexSize(0), vertex_count, Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY);
1065
1066 vbuffer->writeData(0, vbuffer->getSizeInBytes(), mVertexElements, true);
1067 delete [] mVertexElements;
1068
1069 //bind vertex data to mesh
1070 data->vertexBufferBinding->setBinding(0, vbuffer);
1071 }
1072
1073 //process index arrays for each submesh
1074 for (int index = 0; index < submesh_count; index++)
1075 {
1076 std::string material;
1077 if (index < (int)materials.size())
1078 material = materials[index];
1079 else
1080 material = "";
1081
1082 Submesh *submesh;
1083 int client_count;
1084 unsigned int triangle_count = 0;
1085
1086 if (material != "")
1087 triangle_count = Parent->GetTriangleCount(material, client_count, client);
1088
1089 //get submesh index
1090 int match = FindMatchingSubMesh(material);
1091
1092 //skip if no triangles found
1093 if (triangle_count == 0)
1094 {
1095 //delete submesh if needed
1096 DeleteSubMesh(client, -1);
1097 continue;
1098 }
1099
1100 //if a match is not found, create a new submesh
1101 if (match == -1)
1102 submesh = CreateSubMesh(material);
1103 else
1104 submesh = &Submeshes[match];
1105
1106 if (!submesh)
1107 continue;
1108
1109 //skip this submesh, if old and new client counts are the same, and vertices weren't processed
1110 if (client_count == submesh->clients && process_vertices == false)
1111 continue;
1112
1113 //reset submesh's client reference count
1114 submesh->clients = 0;
1115
1116 //set up index data array
1117 unsigned int isize = triangle_count * 3;
1118 Ogre::HardwareIndexBufferSharedPtr ibuffer;
1119
1120 //if the number of vertices is greater than what can fit in a 16-bit index, use 32-bit indexes instead
1121 if (vertex_count > 65536)
1122 {
1123 //set up a 32-bit index buffer
1124 unsigned int *mIndices = new unsigned int[isize];
1125
1126 //create array of triangle indices
1127 unsigned int loc = 0;
1128
1129 //for each client, get triangles for a matching client submesh
1130 for (int num = start; num <= end; num++)
1131 {
1132 MeshObject *mesh = Parent->GetClient(num);
1133
1134 int poly_index = 0;
1135
1136 for (size_t i = 0; i < mesh->Walls.size(); i++)
1137 {
1138 if (!mesh->Walls[i])
1139 continue;
1140
1141 for (size_t j = 0; j < mesh->Walls[i]->GetPolygonCount(); j++)
1142 {
1143 Polygon *poly = mesh->Walls[i]->GetPolygon(j);
1144
1145 if (!poly)
1146 continue;
1147
1148 if (poly->material == material)
1149 {
1150 //get index offset of mesh
1151 unsigned int offset = Parent->GetIndexOffset(mesh);
1152
1153 //add mesh's triangles to array and adjust for offset
1154 for (size_t k = 0; k < poly->triangles.size(); k++)
1155 {
1156 Triangle &tri = poly->triangles[k];
1157 mIndices[loc] = poly_index + tri.a + offset;
1158 mIndices[loc + 1] = poly_index + tri.b + offset;
1159 mIndices[loc + 2] = poly_index + tri.c + offset;
1160 loc += 3;
1161 }
1162
1163 //increment submesh's client reference count
1164 submesh->clients += 1;
1165 }
1166
1167 poly_index += poly->vertex_count;
1168 }
1169 }
1170 }
1171
1172 //create index hardware buffer
1173 ibuffer = Ogre::HardwareBufferManager::getSingleton().createIndexBuffer(Ogre::HardwareIndexBuffer::IT_32BIT, isize, Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY);
1174
1175 //write data to index buffer
1176 ibuffer->writeData(0, ibuffer->getSizeInBytes(), mIndices, true);
1177 delete [] mIndices;
1178 }
1179 else
1180 {
1181 //set up a 16-bit index buffer
1182 unsigned short *mIndices = new unsigned short[isize];
1183
1184 //create array of triangle indices
1185 unsigned int loc = 0;
1186
1187 //for each client, get triangles for a matching client submesh
1188 for (int num = start; num <= end; num++)
1189 {
1190 MeshObject *mesh = Parent->GetClient(num);
1191
1192 int poly_index = 0;
1193
1194 for (size_t i = 0; i < mesh->Walls.size(); i++)
1195 {
1196 if (!mesh->Walls[i])
1197 continue;
1198
1199 for (size_t j = 0; j < mesh->Walls[i]->GetPolygonCount(); j++)
1200 {
1201 Polygon *poly = mesh->Walls[i]->GetPolygon(j);
1202
1203 if (!poly)
1204 continue;
1205
1206 if (poly->material == material)
1207 {
1208 //get index offset of mesh
1209 unsigned int offset = Parent->GetIndexOffset(mesh);
1210
1211 //add mesh's triangles to array and adjust for offset
1212 for (size_t k = 0; k < poly->triangles.size(); k++)
1213 {
1214 Triangle &tri = poly->triangles[k];
1215 mIndices[loc] = poly_index + tri.a + offset;
1216 mIndices[loc + 1] = poly_index + tri.b + offset;
1217 mIndices[loc + 2] = poly_index + tri.c + offset;
1218 loc += 3;
1219 }
1220
1221 //increment submesh's client reference count
1222 submesh->clients += 1;
1223 }
1224
1225 poly_index += poly->vertex_count;
1226 }
1227 }
1228 }
1229
1230 //create index hardware buffer
1231 ibuffer = Ogre::HardwareBufferManager::getSingleton().createIndexBuffer(Ogre::HardwareIndexBuffer::IT_16BIT, isize, Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY);
1232
1233 //write data to index buffer
1234 ibuffer->writeData(0, ibuffer->getSizeInBytes(), mIndices, true);
1235 delete [] mIndices;
1236 }
1237
1238 //delete any old index data
1239 if (submesh->object->indexData)
1240 {
1241 delete submesh->object->indexData;
1242 submesh->object->indexData = new Ogre::IndexData();
1243 }
1244
1245 //bind index data to submesh
1246 submesh->object->indexData->indexCount = isize;
1247 submesh->object->indexData->indexBuffer = ibuffer;
1248 submesh->object->indexData->indexStart = 0;
1249 }
1250
1251 //mark ogre mesh as dirty to update changes
1252 MeshWrapper->_dirtyState();
1253
1254 if (process_vertices == true)
1255 {
1256 UpdateBoundingBox();
1257
1258 MeshWrapper->load();
1259
1260 //if a mesh was attached and was empty, it needs to be reattached to be visible
1261 if (previous_count == 0 && enabled == true)
1262 {
1263 Enabled(false);
1264 Enabled(true);
1265 }
1266 }
1267
1268 prepared = true;
1269}
1270
1272{
1273 //enable or disable debug view of mesh
1274 Movable->setDebugDisplayEnabled(value);
1275}
1276
1278{
1279 //returns true if this mesh is currently visible (enabled, and within the rendering distance)
1280 return Movable->isVisible();
1281}
1282
1283bool DynamicMesh::Mesh::IsVisible(Ogre::Camera *camera)
1284{
1285 //returns if this mesh is visible in the provided camera's view frustum or not
1286
1287 if (enabled == false || !camera)
1288 return false;
1289
1290 //if beyond the max render distance
1291 if (IsVisible() == false)
1292 return false;
1293
1294 if (GetSubMeshCount() == 0)
1295 return false;
1296
1297 Ogre::AxisAlignedBox Bounds = MeshWrapper->getBounds();
1298 if (Bounds.isNull())
1299 return false;
1300
1301 Vector3 min = Bounds.getMinimum();
1302 Vector3 max = Bounds.getMaximum();
1303 Vector3 pos = sbs->ToRemote(node->GetPosition());
1304 Ogre::AxisAlignedBox global_box (pos + min, pos + max);
1305
1306 return camera->isVisible(global_box);
1307}
1308
1309void DynamicMesh::Mesh::UpdateVertices(int client, const std::string &material, Polygon *polygon, bool single)
1310{
1311 //update/write all vertices (or a single vertex) to the render buffer, if a dynamic mesh
1312
1313 SBS_PROFILE("DynamicMesh::Mesh::UpdateVertices");
1314
1315 if (Parent->UseDynamicBuffers() == false || !node)
1316 return;
1317
1318 if (prepared == false)
1319 return;
1320
1321 if (!Parent->GetClient(client))
1322 return;
1323
1324 bool combined = true;
1325 if (Parent->GetMeshCount() > 1)
1326 combined = false;
1327
1328 //get client's offset from offset table
1329 unsigned int loc = 0;
1330 if (combined == true)
1331 loc = client_entries[client].vertex_offset;
1332
1333 MeshObject *mesh = Parent->GetClient(client);
1334
1335 //get mesh's offset of associated scene node
1336 Vector3 offset = sbs->ToRemote(mesh->GetPosition() - node->GetPosition());
1337
1338 unsigned int vertex_count = mesh->GetVertexCount();
1339
1340 //exit if client mesh is empty or if no walls have been defined
1341 if (vertex_count == 0 || mesh->Walls.size() == 0)
1342 return;
1343
1344 //set up vertex data arrays
1345 float *mVertexElements;
1346 if (single == true)
1347 mVertexElements = new float[8];
1348 else
1349 mVertexElements = new float[vertex_count * 8];
1350
1351 if (single == false)
1352 {
1353 unsigned int count;
1354
1355 if (combined == true)
1356 count = client_entries[client].vertex_count;
1357 else
1358 count = client_entries[0].vertex_count;
1359
1360 if (count != vertex_count)
1361 {
1362 delete [] mVertexElements;
1363 return; //make sure vertex count is the same
1364 }
1365 }
1366
1367 Ogre::AxisAlignedBox box;
1368
1369 //fill array with vertex's data
1370 unsigned int pos = 0;
1371 unsigned int add = 0;
1372
1373 for (size_t i = 0; i < mesh->Walls.size(); i++)
1374 {
1375 if (!mesh->Walls[i])
1376 continue;
1377
1378 //skip empty walls
1379 if (mesh->Walls[i]->GetPolygonCount() == 0)
1380 continue;
1381
1382 for (size_t j = 0; j < mesh->Walls[i]->GetPolygonCount(); j++)
1383 {
1384 Polygon *poly = mesh->Walls[i]->GetPolygon(j);
1385
1386 if (single == true)
1387 {
1388 //match material
1389 if (poly->material != material)
1390 continue;
1391
1392 if (poly != polygon)
1393 continue;
1394 }
1395
1396 for (size_t k = 0; k < poly->geometry.size(); k++)
1397 {
1398 for (size_t l = 0; l < poly->geometry[k].size(); l++)
1399 {
1400 Polygon::Geometry &element = poly->geometry[k][l];
1401
1402 //make mesh's vertex relative to this scene node
1403 Vector3 raw_vertex = mesh->GetOrientation() * element.vertex; //add mesh's rotation
1404 Vector3 vertex2 = (node->GetOrientation().Inverse() * raw_vertex) + offset; //remove node's rotation and add mesh offset
1405
1406 //add elements to array
1407 mVertexElements[pos] = (float)vertex2.x;
1408 mVertexElements[pos + 1] = (float)vertex2.y;
1409 mVertexElements[pos + 2] = (float)vertex2.z;
1410 mVertexElements[pos + 3] = (float)element.normal.x;
1411 mVertexElements[pos + 4] = (float)element.normal.y;
1412 mVertexElements[pos + 5] = (float)element.normal.z;
1413 mVertexElements[pos + 6] = (float)element.texel.x;
1414 mVertexElements[pos + 7] = (float)element.texel.y;
1415 box.merge(vertex2);
1416 pos += 8;
1417 add += 1;
1418 }
1419 }
1420 }
1421 }
1422
1423 //store updated bounding box
1424 if (combined == true)
1425 *client_entries[client].bounds = box;
1426 else
1427 *client_entries[0].bounds = box;
1428
1429 //get vertex data
1430 Ogre::VertexData* data = MeshWrapper->sharedVertexData;
1431
1432 //get vertex buffer and size
1433 Ogre::HardwareVertexBufferSharedPtr vbuffer = data->vertexBufferBinding->getBuffer(0);
1434 size_t vsize = data->vertexDeclaration->getVertexSize(0);
1435
1436 /*
1437 //lock vertex buffer for writing
1438 float *vdata = static_cast<float*>(vbuffer->lock(vsize * loc, vsize * add, Ogre::HardwareBuffer::HBL_NORMAL));
1439
1440 //write elements
1441 for (int i = 0; i < add; i++)
1442 {
1443 vdata[i] = mVertexElements[i];
1444 }
1445
1446 //unlock vertex buffer
1447 vbuffer->unlock();
1448 */
1449
1450 //write data to buffer
1451 vbuffer->writeData(vsize * loc, vsize * add, mVertexElements, false);
1452
1453 delete [] mVertexElements;
1454
1455 //update mesh bounding box
1456 UpdateBoundingBox();
1457}
1458
1460{
1461 //detach entity and scene node from this mesh object
1462
1463 if (Movable)
1464 {
1465 if (parent_deleting == false)
1466 node->DetachObject(Movable);
1467 sbs->mSceneManager->destroyEntity(Movable);
1468 }
1469 Movable = 0;
1470 node = 0;
1471}
1472
1474{
1475 return MeshWrapper->getNumSubMeshes();
1476}
1477
1479{
1480 //set mesh's bounding box
1481
1482 if (client_entries.empty() == true)
1483 return;
1484
1485 if (Parent->GetMeshCount() == 1)
1486 {
1487 Ogre::AxisAlignedBox box;
1488 Real radius = 0;
1489
1490 if (Parent->GetClientCount() != (int)client_entries.size())
1491 return;
1492
1493 for (int i = 0; i < Parent->GetClientCount(); i++)
1494 {
1495 box.merge(*client_entries[i].bounds);
1496 radius = std::max(radius, client_entries[i].radius);
1497 }
1498
1499 MeshWrapper->_setBounds(box);
1500 MeshWrapper->_setBoundingSphereRadius(radius);
1501 }
1502 else if (Parent->GetMeshCount() > 1)
1503 {
1504 MeshWrapper->_setBounds(*client_entries[0].bounds);
1505 MeshWrapper->_setBoundingSphereRadius(client_entries[0].radius);
1506 }
1507}
1508
1510{
1511 //enable shadows, overriding automatic setting
1512
1513 auto_shadows = false;
1514 Movable->setCastShadows(value);
1515}
1516
1517void DynamicMesh::Mesh::SetMaterial(const std::string& material)
1518{
1519 //set material of this mesh
1520
1521 Ogre::MaterialPtr mat = sbs->GetTextureManager()->GetMaterialByName(material);
1522
1523 if (mat)
1524 Movable->setMaterial(mat);
1525 else
1526 {
1527 //set to default material if the specified one is not found
1528 mat = sbs->GetTextureManager()->GetMaterialByName("Default");
1529 Movable->setMaterial(mat);
1530 }
1531}
1532
1533}
void UpdateVertices(MeshObject *client, const std::string &material="", Polygon *polygon=0, bool single=false)
void NeedsUpdate(MeshObject *client=0)
void EnableDebugView(bool value, MeshObject *client=0)
void SetMaterial(const std::string &material)
void RemoveClient(MeshObject *mesh)
DynamicMesh(Object *parent, SceneNode *node, const std::string &name, Real max_render_distance=0, bool dynamic_buffers=false)
std::vector< Mesh * > meshes
unsigned int GetIndexOffset(MeshObject *client)
MeshObject * GetClient(int number)
unsigned int GetVertexCount(const std::string &material="", int client=-1)
bool ChangeTexture(const std::string &old_texture, const std::string &new_texture, MeshObject *client=0)
void AddClient(MeshObject *mesh)
int GetClientIndex(MeshObject *client)
void DetachClient(MeshObject *client)
SceneNode * node
std::vector< bool > client_enable
bool LoadFromMesh(const std::string &meshname)
void EnableShadows(bool value)
Ogre::AxisAlignedBox GetBounds(MeshObject *client=0)
std::string GetMeshName(int mesh_index)
void Prepare(MeshObject *client=0)
unsigned int GetTriangleCount(const std::string &material, int &client_count, int client=-1)
bool IsVisible(MeshObject *client=0)
int GetMaterials(std::vector< std::string > &materials, int client=-1)
std::vector< MeshObject * > clients
void Enabled(bool value, MeshObject *client=0)
int GetMaterialCount(int client=-1)
int GetSubMeshCount(int mesh_index)
bool LoadFromFile(const std::string &filename, const std::string &path)
size_t size
Definition mesh.h:122
std::vector< Wall * > Walls
Definition mesh.h:99
unsigned int GetVertexCount()
Definition mesh.cpp:1162
std::string GetNameBase()
Definition object.cpp:59
SBS * GetRoot()
Definition object.cpp:48
Object * Parent
Definition object.h:51
const std::string & GetName()
Definition object.cpp:53
virtual bool ReportError(const std::string &message)
Definition object.cpp:84
Object * GetParent()
Definition object.cpp:42
virtual void Report(const std::string &message)
Definition object.cpp:78
void SetName(const std::string &name)
Definition object.cpp:72
virtual Vector3 GetPosition(bool relative=false)
Definition object.cpp:321
std::string GetNameBase()
Definition object.cpp:599
Quaternion GetOrientation(bool relative=false)
Definition object.cpp:377
std::vector< Triangle > triangles
Definition polygon.h:49
int vertex_count
Definition polygon.h:48
std::string material
Definition polygon.h:56
GeometrySet geometry
Definition polygon.h:47
Ogre::SceneManager * mSceneManager
Definition sbs.h:138
void RegisterDynamicMesh(DynamicMesh *dynmesh)
Definition sbs.cpp:4497
bool FastDelete
Definition sbs.h:188
TextureManager * GetTextureManager()
Definition sbs.cpp:4558
int InstanceNumber
Definition sbs.h:197
bool GetConfigBool(const std::string &key, bool default_value)
Definition sbs.cpp:3243
Real ToRemote(Real local_value)
Definition sbs.cpp:2407
void UnregisterDynamicMesh(DynamicMesh *dynmesh)
Definition sbs.cpp:4504
void DetachObject(Ogre::MovableObject *object)
void AttachObject(Ogre::MovableObject *object)
Vector3 GetPosition(bool relative=false)
Quaternion GetOrientation(bool relative=false)
std::string GetFullName()
Ogre::MaterialPtr GetMaterialByName(const std::string &name, const std::string &group="General")
Definition texture.cpp:2224
Ogre::Vector3 Vector3
Definition globals.h:58
Ogre::Real Real
Definition globals.h:57
std::string ToString(int number)
Definition globals.cpp:279
#define SBS_PROFILE(name)
Definition profiler.h:131
Ogre::AxisAlignedBox * bounds
Definition dynamicmesh.h:96
Ogre::MeshPtr MeshWrapper
bool ChangeTexture(const std::string &old_texture, const std::string &new_texture)
Submesh * CreateSubMesh(const std::string &material)
void EnableShadows(bool value)
int FindMatchingSubMesh(const std::string &material)
void UpdateVertices(int client, const std::string &material, Polygon *polygon=0, bool single=false)
void DeleteSubMesh(int client, int index)
Mesh(DynamicMesh *parent, const std::string &name, SceneNode *node, Real max_render_distance, const std::string &filename="", const std::string &meshname="", const std::string &path="")
void Enabled(bool value)
void Prepare(bool process_vertices=true, int client=-1)
Ogre::Entity * Movable
void EnableDebugView(bool value)
void SetMaterial(const std::string &material)
unsigned int c
Definition triangle.h:33
unsigned int a
Definition triangle.h:33
unsigned int b
Definition triangle.h:33