1 /*
2  * Copyright 2011-2013 Blender Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef __BLENDER_ID_MAP_H__
18 #define __BLENDER_ID_MAP_H__
19 
20 #include <string.h>
21 
22 #include "render/geometry.h"
23 #include "render/scene.h"
24 
25 #include "util/util_map.h"
26 #include "util/util_set.h"
27 #include "util/util_vector.h"
28 
29 CCL_NAMESPACE_BEGIN
30 
31 /* ID Map
32  *
33  * Utility class to map between Blender datablocks and Cycles data structures,
34  * and keep track of recalc tags from the dependency graph. */
35 
36 template<typename K, typename T> class id_map {
37  public:
id_map(Scene * scene_)38   id_map(Scene *scene_) : scene(scene_)
39   {
40   }
41 
~id_map()42   ~id_map()
43   {
44     set<T *> nodes;
45 
46     typename map<K, T *>::iterator jt;
47     for (jt = b_map.begin(); jt != b_map.end(); jt++) {
48       nodes.insert(jt->second);
49     }
50 
51     scene->delete_nodes(nodes);
52   }
53 
find(const BL::ID & id)54   T *find(const BL::ID &id)
55   {
56     return find(id.ptr.owner_id);
57   }
58 
find(const K & key)59   T *find(const K &key)
60   {
61     if (b_map.find(key) != b_map.end()) {
62       T *data = b_map[key];
63       return data;
64     }
65 
66     return NULL;
67   }
68 
set_recalc(const BL::ID & id)69   void set_recalc(const BL::ID &id)
70   {
71     b_recalc.insert(id.ptr.data);
72   }
73 
set_recalc(void * id_ptr)74   void set_recalc(void *id_ptr)
75   {
76     b_recalc.insert(id_ptr);
77   }
78 
has_recalc()79   bool has_recalc()
80   {
81     return !(b_recalc.empty());
82   }
83 
pre_sync()84   void pre_sync()
85   {
86     used_set.clear();
87   }
88 
89   /* Add new data. */
add(const K & key,T * data)90   void add(const K &key, T *data)
91   {
92     assert(find(key) == NULL);
93     b_map[key] = data;
94     used(data);
95   }
96 
97   /* Update existing data. */
update(T * data,const BL::ID & id)98   bool update(T *data, const BL::ID &id)
99   {
100     return update(data, id, id);
101   }
update(T * data,const BL::ID & id,const BL::ID & parent)102   bool update(T *data, const BL::ID &id, const BL::ID &parent)
103   {
104     bool recalc = (b_recalc.find(id.ptr.data) != b_recalc.end());
105     if (parent.ptr.data && parent.ptr.data != id.ptr.data) {
106       recalc = recalc || (b_recalc.find(parent.ptr.data) != b_recalc.end());
107     }
108     used(data);
109     return recalc;
110   }
111 
112   /* Combined add and update as needed. */
add_or_update(T ** r_data,const BL::ID & id)113   bool add_or_update(T **r_data, const BL::ID &id)
114   {
115     return add_or_update(r_data, id, id, id.ptr.owner_id);
116   }
add_or_update(T ** r_data,const BL::ID & id,const K & key)117   bool add_or_update(T **r_data, const BL::ID &id, const K &key)
118   {
119     return add_or_update(r_data, id, id, key);
120   }
add_or_update(T ** r_data,const BL::ID & id,const BL::ID & parent,const K & key)121   bool add_or_update(T **r_data, const BL::ID &id, const BL::ID &parent, const K &key)
122   {
123     T *data = find(key);
124     bool recalc;
125 
126     if (!data) {
127       /* Add data if it didn't exist yet. */
128       data = scene->create_node<T>();
129       add(key, data);
130       recalc = true;
131     }
132     else {
133       /* check if updated needed. */
134       recalc = update(data, id, parent);
135     }
136 
137     *r_data = data;
138     return recalc;
139   }
140 
141   /* Combined add or update for convenience. */
142 
is_used(const K & key)143   bool is_used(const K &key)
144   {
145     T *data = find(key);
146     return (data) ? used_set.find(data) != used_set.end() : false;
147   }
148 
used(T * data)149   void used(T *data)
150   {
151     /* tag data as still in use */
152     used_set.insert(data);
153   }
154 
set_default(T * data)155   void set_default(T *data)
156   {
157     b_map[NULL] = data;
158   }
159 
160   void post_sync(bool do_delete = true)
161   {
162     map<K, T *> new_map;
163     typedef pair<const K, T *> TMapPair;
164     typename map<K, T *>::iterator jt;
165 
166     for (jt = b_map.begin(); jt != b_map.end(); jt++) {
167       TMapPair &pair = *jt;
168 
169       if (do_delete && used_set.find(pair.second) == used_set.end()) {
170         scene->delete_node(pair.second);
171       }
172       else {
173         new_map[pair.first] = pair.second;
174       }
175     }
176 
177     used_set.clear();
178     b_recalc.clear();
179     b_map = new_map;
180   }
181 
key_to_scene_data()182   const map<K, T *> &key_to_scene_data()
183   {
184     return b_map;
185   }
186 
187  protected:
188   map<K, T *> b_map;
189   set<T *> used_set;
190   set<void *> b_recalc;
191   Scene *scene;
192 };
193 
194 /* Object Key
195  *
196  * To uniquely identify instances, we use the parent, object and persistent instance ID.
197  * We also export separate object for a mesh and its particle hair. */
198 
199 enum { OBJECT_PERSISTENT_ID_SIZE = 8 /* MAX_DUPLI_RECUR in Blender. */ };
200 
201 struct ObjectKey {
202   void *parent;
203   int id[OBJECT_PERSISTENT_ID_SIZE];
204   void *ob;
205   bool use_particle_hair;
206 
ObjectKeyObjectKey207   ObjectKey(void *parent_, int id_[OBJECT_PERSISTENT_ID_SIZE], void *ob_, bool use_particle_hair_)
208       : parent(parent_), ob(ob_), use_particle_hair(use_particle_hair_)
209   {
210     if (id_)
211       memcpy(id, id_, sizeof(id));
212     else
213       memset(id, 0, sizeof(id));
214   }
215 
216   bool operator<(const ObjectKey &k) const
217   {
218     if (ob < k.ob) {
219       return true;
220     }
221     else if (ob == k.ob) {
222       if (parent < k.parent) {
223         return true;
224       }
225       else if (parent == k.parent) {
226         if (use_particle_hair < k.use_particle_hair) {
227           return true;
228         }
229         else if (use_particle_hair == k.use_particle_hair) {
230           return memcmp(id, k.id, sizeof(id)) < 0;
231         }
232       }
233     }
234 
235     return false;
236   }
237 };
238 
239 /* Geometry Key
240  *
241  * We export separate geometry for a mesh and its particle hair, so key needs to
242  * distinguish between them. */
243 
244 struct GeometryKey {
245   void *id;
246   Geometry::Type geometry_type;
247 
GeometryKeyGeometryKey248   GeometryKey(void *id, Geometry::Type geometry_type) : id(id), geometry_type(geometry_type)
249   {
250   }
251 
252   bool operator<(const GeometryKey &k) const
253   {
254     if (id < k.id) {
255       return true;
256     }
257     else if (id == k.id) {
258       if (geometry_type < k.geometry_type) {
259         return true;
260       }
261     }
262 
263     return false;
264   }
265 };
266 
267 /* Particle System Key */
268 
269 struct ParticleSystemKey {
270   void *ob;
271   int id[OBJECT_PERSISTENT_ID_SIZE];
272 
ParticleSystemKeyParticleSystemKey273   ParticleSystemKey(void *ob_, int id_[OBJECT_PERSISTENT_ID_SIZE]) : ob(ob_)
274   {
275     if (id_)
276       memcpy(id, id_, sizeof(id));
277     else
278       memset(id, 0, sizeof(id));
279   }
280 
281   bool operator<(const ParticleSystemKey &k) const
282   {
283     /* first id is particle index, we don't compare that */
284     if (ob < k.ob)
285       return true;
286     else if (ob == k.ob)
287       return memcmp(id + 1, k.id + 1, sizeof(int) * (OBJECT_PERSISTENT_ID_SIZE - 1)) < 0;
288 
289     return false;
290   }
291 };
292 
293 CCL_NAMESPACE_END
294 
295 #endif /* __BLENDER_ID_MAP_H__ */
296