material editing in scene metafile
[laserbrain_demo] / src / scene.cc
1 #include <regex>
2 #include "scene.h"
3 #include "objmesh.h"
4
5 static void destroy_node_tree(SceneNode *n);
6
7 Scene::Scene(TextureSet *tset)
8 {
9         nodes = 0;
10
11         walk_mesh = 0;
12
13         if(tset) {
14                 texset = tset;
15                 own_texset = false;
16         } else {
17                 texset = new TextureSet;
18                 own_texset = true;
19         }
20 }
21
22 Scene::~Scene()
23 {
24         destroy();
25 }
26
27 void Scene::destroy()
28 {
29         destroy_node_tree(nodes);
30         nodes = 0;
31
32         for(int i=0; i<(int)meshes.size(); i++) {
33                 delete meshes[i];
34         }
35         meshes.clear();
36
37         delete walk_mesh;
38         walk_mesh = 0;
39
40         for(int i=0; i<(int)objects.size(); i++) {
41                 delete objects[i];
42         }
43         objects.clear();
44
45         if(own_texset) {
46                 delete texset;
47         }
48         texset = 0;
49 }
50
51 static void destroy_node_tree(SceneNode *n)
52 {
53         if(!n) return;
54
55         int nsub = n->get_num_children();
56         for(int i=0; i<nsub; i++) {
57                 destroy_node_tree(n->get_child(i));
58         }
59         delete n;
60 }
61
62 // Scene::load defined in sceneload.cc
63
64 bool Scene::merge(Scene *scn)
65 {
66         if(texset != scn->texset) {
67                 // TODO handle this properly
68                 error_log("for now only able to merge scenes using the same texture set\n");
69                 return false;
70         }
71
72         if(walk_mesh) {
73                 if(scn->walk_mesh) {
74                         walk_mesh->append(*scn->walk_mesh);
75                         delete scn->walk_mesh;
76                         scn->walk_mesh = 0;
77                 }
78         } else {
79                 walk_mesh = scn->walk_mesh;
80                 scn->walk_mesh = 0;
81         }
82
83         int nmeshes = scn->meshes.size();
84         for(int i=0; i<nmeshes; i++) {
85                 meshes.push_back(scn->meshes[i]);
86         }
87         scn->meshes.clear();
88
89         int nobj = scn->objects.size();
90         for(int i=0; i<nobj; i++) {
91                 objects.push_back(scn->objects[i]);
92         }
93         scn->objects.clear();
94
95         if(nodes) {
96                 int nchildren = scn->nodes ? scn->nodes->get_num_children() : 0;
97                 for(int i=0; i<nchildren; i++) {
98                         SceneNode *n = scn->nodes->get_child(i);
99                         scn->nodes->remove_child(n);
100                         nodes->add_child(n);
101                 }
102                 if(scn->nodes && scn->nodes->get_num_objects() > 0) {
103                         warning_log("merging with scene which has objects in its root node. these objects will not be merged!\n");
104                 }
105                 delete scn->nodes;
106         } else {
107                 nodes = scn->nodes;
108         }
109         scn->nodes = 0;
110
111         return true;
112 }
113
114 void Scene::add_object(Object *obj)
115 {
116         objects.push_back(obj);
117 }
118
119 bool Scene::remove_object(Object *obj)
120 {
121         std::vector<Object*>::iterator it = std::find(objects.begin(), objects.end(), obj);
122         if(it != objects.end()) {
123                 objects.erase(it);
124                 return true;
125         }
126         return false;
127 }
128
129 bool Scene::have_object(Object *obj) const
130 {
131         return std::find(objects.begin(), objects.end(), obj) != objects.end();
132 }
133
134 void Scene::add_mesh(Mesh *m)
135 {
136         meshes.push_back(m);
137 }
138
139 bool Scene::remove_mesh(Mesh *m)
140 {
141         std::vector<Mesh*>::iterator it = std::find(meshes.begin(), meshes.end(), m);
142         if(it != meshes.end()) {
143                 meshes.erase(it);
144                 return true;
145         }
146         return false;
147 }
148
149 bool Scene::have_mesh(Mesh *m) const
150 {
151         return std::find(meshes.begin(), meshes.end(), m) != meshes.end();
152 }
153
154 void Scene::add_node(SceneNode *n)
155 {
156         // we always want to have a dedicated root node
157         if(!nodes) {
158                 nodes = new SceneNode;
159                 nodes->scene = this;
160                 nodes->set_name("root");
161         }
162
163         nodes->add_child(n);
164 }
165
166 bool Scene::remove_node(SceneNode *n)
167 {
168         SceneNode *par;
169         if(!n || !(par = n->get_parent())) {
170                 return false;
171         }
172
173         int nsub = n->get_num_children();
174         for(int i=0; i<nsub; i++) {
175                 SceneNode *c = n->get_child(i);
176                 n->remove_child(c);
177                 par->add_child(c);
178         }
179
180         return par->remove_child(n);
181 }
182
183 bool Scene::have_node(SceneNode *n) const
184 {
185         return n->scene == this;
186 }
187
188 static void find_nodes_rec(std::list<SceneNode*> *res, SceneNode *tree, const std::regex &re)
189 {
190         if(std::regex_match(tree->get_name(), re)) {
191                 res->push_back(tree);
192         }
193
194         int num = tree->get_num_children();
195         for(int i=0; i<num; i++) {
196                 find_nodes_rec(res, tree->get_child(i), re);
197         }
198 }
199
200 Scene *Scene::extract_nodes(const char *qstr)
201 {
202         std::regex re{qstr};
203
204         std::list<SceneNode*> nodelist;
205         find_nodes_rec(&nodelist, nodes, re);
206         if(nodelist.empty()) {
207                 return 0;
208         }
209
210         Scene *res = new Scene(texset);
211
212         for(SceneNode *n : nodelist) {
213
214                 int nobj = n->get_num_objects();
215                 for(int i=0; i<nobj; i++) {
216                         Object *obj = n->get_object(i);
217                         if(obj->get_type() == OBJ_MESH) {
218                                 // XXX this assumes that meshes aren't shared between objects.
219                                 // maybe we'll have to refcount them at some point, and copy if nref>1
220                                 ObjMesh *om = (ObjMesh*)obj;
221                                 remove_mesh(om->mesh);
222                                 res->add_mesh(om->mesh);
223                         }
224
225                         remove_object(obj);
226                         res->add_object(obj);
227                 }
228
229                 remove_node(n);
230                 res->add_node(n);
231         }
232         return res;
233 }
234
235 void Scene::apply_xform()
236 {
237         nodes->apply_xform();
238 }
239
240 void Scene::update(float dt)
241 {
242         if(nodes) {
243                 nodes->update(dt);
244         }
245
246         int nobj = objects.size();
247         for(int i=0; i<nobj; i++) {
248                 if(!objects[i]->node) {
249                         // only update objects which don't belong to a scenegraph node
250                         // to avoid updating objects twice
251                         objects[i]->update(dt);
252                 }
253         }
254 }
255
256 void Scene::draw() const
257 {
258         if(!objects.empty()) {
259                 int nobj = objects.size();
260                 for(int i=0; i<nobj; i++) {
261                         objects[i]->draw();
262                 }
263         } else {
264                 int nmesh = meshes.size();
265                 for(int i=0; i<nmesh; i++) {
266                         meshes[i]->draw();
267                 }
268         }
269 }