Merge branch 'blender2.7'
[blender.git] / intern / cycles / blender / blender_util.h
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_UTIL_H__
18 #define __BLENDER_UTIL_H__
19
20 #include "render/mesh.h"
21
22 #include "util/util_algorithm.h"
23 #include "util/util_array.h"
24 #include "util/util_map.h"
25 #include "util/util_path.h"
26 #include "util/util_set.h"
27 #include "util/util_transform.h"
28 #include "util/util_types.h"
29 #include "util/util_vector.h"
30
31 /* Hacks to hook into Blender API
32  * todo: clean this up ... */
33
34 extern "C" {
35 void BKE_image_user_frame_calc(void *iuser, int cfra);
36 void BKE_image_user_file_path(void *iuser, void *ima, char *path);
37 unsigned char *BKE_image_get_pixels_for_frame(void *image, int frame);
38 float *BKE_image_get_float_pixels_for_frame(void *image, int frame);
39 }
40
41 CCL_NAMESPACE_BEGIN
42
43 void python_thread_state_save(void **python_thread_state);
44 void python_thread_state_restore(void **python_thread_state);
45
46 static inline BL::Mesh object_to_mesh(BL::BlendData& data,
47                                       BL::Object& object,
48                                       BL::Depsgraph& depsgraph,
49                                       bool calc_undeformed,
50                                       Mesh::SubdivisionType subdivision_type)
51 {
52         /* TODO: make this work with copy-on-write, modifiers are already evaluated. */
53 #if 0
54         bool subsurf_mod_show_render = false;
55         bool subsurf_mod_show_viewport = false;
56
57         if(subdivision_type != Mesh::SUBDIVISION_NONE) {
58                 BL::Modifier subsurf_mod = object.modifiers[object.modifiers.length()-1];
59
60                 subsurf_mod_show_render = subsurf_mod.show_render();
61                 subsurf_mod_show_viewport = subsurf_mod.show_viewport();
62
63                 subsurf_mod.show_render(false);
64                 subsurf_mod.show_viewport(false);
65         }
66 #endif
67
68         BL::Mesh mesh(PointerRNA_NULL);
69         if(object.type() == BL::Object::type_MESH) {
70                 /* TODO: calc_undeformed is not used. */
71                 mesh = BL::Mesh(object.data());
72
73                 /* Make a copy to split faces if we use autosmooth, otherwise not needed.
74                  * Also in edit mode do we need to make a copy, to ensure data layers like
75                  * UV are not empty. */
76                 if (mesh.is_editmode() ||
77                     (mesh.use_auto_smooth() && subdivision_type == Mesh::SUBDIVISION_NONE))
78                 {
79                         mesh = data.meshes.new_from_object(depsgraph, object, false, false);
80                 }
81         }
82         else {
83                 mesh = data.meshes.new_from_object(depsgraph, object, true, calc_undeformed);
84         }
85
86 #if 0
87         if(subdivision_type != Mesh::SUBDIVISION_NONE) {
88                 BL::Modifier subsurf_mod = object.modifiers[object.modifiers.length()-1];
89
90                 subsurf_mod.show_render(subsurf_mod_show_render);
91                 subsurf_mod.show_viewport(subsurf_mod_show_viewport);
92         }
93 #endif
94
95         if((bool)mesh && subdivision_type == Mesh::SUBDIVISION_NONE) {
96                 if(mesh.use_auto_smooth()) {
97                         mesh.split_faces(false);
98                 }
99
100                 mesh.calc_loop_triangles();
101         }
102
103         return mesh;
104 }
105
106 static inline void free_object_to_mesh(BL::BlendData& data,
107                                        BL::Object& object,
108                                        BL::Mesh& mesh)
109 {
110         /* Free mesh if we didn't just use the existing one. */
111         if(object.data().ptr.data != mesh.ptr.data) {
112                 data.meshes.remove(mesh, false, true, false);
113         }
114 }
115
116 static inline void colorramp_to_array(BL::ColorRamp& ramp,
117                                       array<float3>& ramp_color,
118                                       array<float>& ramp_alpha,
119                                       int size)
120 {
121         ramp_color.resize(size);
122         ramp_alpha.resize(size);
123
124         for(int i = 0; i < size; i++) {
125                 float color[4];
126
127                 ramp.evaluate((float)i/(float)(size-1), color);
128                 ramp_color[i] = make_float3(color[0], color[1], color[2]);
129                 ramp_alpha[i] = color[3];
130         }
131 }
132
133 static inline void curvemap_minmax_curve(/*const*/ BL::CurveMap& curve,
134                                          float *min_x,
135                                          float *max_x)
136 {
137         *min_x = min(*min_x, curve.points[0].location()[0]);
138         *max_x = max(*max_x, curve.points[curve.points.length() - 1].location()[0]);
139 }
140
141 static inline void curvemapping_minmax(/*const*/ BL::CurveMapping& cumap,
142                                        bool rgb_curve,
143                                        float *min_x,
144                                        float *max_x)
145 {
146         /* const int num_curves = cumap.curves.length(); */  /* Gives linking error so far. */
147         const int num_curves = rgb_curve? 4: 3;
148         *min_x = FLT_MAX;
149         *max_x = -FLT_MAX;
150         for(int i = 0; i < num_curves; ++i) {
151                 BL::CurveMap map(cumap.curves[i]);
152                 curvemap_minmax_curve(map, min_x, max_x);
153         }
154 }
155
156 static inline void curvemapping_to_array(BL::CurveMapping& cumap,
157                                          array<float>& data,
158                                          int size)
159 {
160         cumap.update();
161         BL::CurveMap curve = cumap.curves[0];
162         data.resize(size);
163         for(int i = 0; i < size; i++) {
164                 float t = (float)i/(float)(size-1);
165                 data[i] = curve.evaluate(t);
166         }
167 }
168
169 static inline void curvemapping_color_to_array(BL::CurveMapping& cumap,
170                                                array<float3>& data,
171                                                int size,
172                                                bool rgb_curve)
173 {
174         float min_x = 0.0f, max_x = 1.0f;
175
176         /* TODO(sergey): There is no easy way to automatically guess what is
177          * the range to be used here for the case when mapping is applied on
178          * top of another mapping (i.e. R curve applied on top of common
179          * one).
180          *
181          * Using largest possible range form all curves works correct for the
182          * cases like vector curves and should be good enough heuristic for
183          * the color curves as well.
184          *
185          * There might be some better estimations here tho.
186          */
187         curvemapping_minmax(cumap, rgb_curve, &min_x, &max_x);
188
189         const float range_x = max_x - min_x;
190
191         cumap.update();
192
193         BL::CurveMap mapR = cumap.curves[0];
194         BL::CurveMap mapG = cumap.curves[1];
195         BL::CurveMap mapB = cumap.curves[2];
196
197         data.resize(size);
198
199         if(rgb_curve) {
200                 BL::CurveMap mapI = cumap.curves[3];
201                 for(int i = 0; i < size; i++) {
202                         const float t = min_x + (float)i/(float)(size-1) * range_x;
203                         data[i] = make_float3(mapR.evaluate(mapI.evaluate(t)),
204                                               mapG.evaluate(mapI.evaluate(t)),
205                                               mapB.evaluate(mapI.evaluate(t)));
206                 }
207         }
208         else {
209                 for(int i = 0; i < size; i++) {
210                         float t = min_x + (float)i/(float)(size-1) * range_x;
211                         data[i] = make_float3(mapR.evaluate(t),
212                                               mapG.evaluate(t),
213                                               mapB.evaluate(t));
214                 }
215         }
216 }
217
218 static inline bool BKE_object_is_modified(BL::Object& self,
219                                           BL::Scene& scene,
220                                           bool preview)
221 {
222         return self.is_modified(scene, (preview)? (1<<0): (1<<1))? true: false;
223 }
224
225 static inline bool BKE_object_is_deform_modified(BL::Object& self,
226                                                  BL::Scene& scene,
227                                                  bool preview)
228 {
229         return self.is_deform_modified(scene, (preview)? (1<<0): (1<<1))? true: false;
230 }
231
232 static inline int render_resolution_x(BL::RenderSettings& b_render)
233 {
234         return b_render.resolution_x()*b_render.resolution_percentage()/100;
235 }
236
237 static inline int render_resolution_y(BL::RenderSettings& b_render)
238 {
239         return b_render.resolution_y()*b_render.resolution_percentage()/100;
240 }
241
242 static inline string image_user_file_path(BL::ImageUser& iuser,
243                                           BL::Image& ima,
244                                           int cfra)
245 {
246         char filepath[1024];
247         BKE_image_user_frame_calc(iuser.ptr.data, cfra);
248         BKE_image_user_file_path(iuser.ptr.data, ima.ptr.data, filepath);
249         return string(filepath);
250 }
251
252 static inline int image_user_frame_number(BL::ImageUser& iuser, int cfra)
253 {
254         BKE_image_user_frame_calc(iuser.ptr.data, cfra);
255         return iuser.frame_current();
256 }
257
258 static inline unsigned char *image_get_pixels_for_frame(BL::Image& image,
259                                                         int frame)
260 {
261         return BKE_image_get_pixels_for_frame(image.ptr.data, frame);
262 }
263
264 static inline float *image_get_float_pixels_for_frame(BL::Image& image,
265                                                       int frame)
266 {
267         return BKE_image_get_float_pixels_for_frame(image.ptr.data, frame);
268 }
269
270 static inline void render_add_metadata(BL::RenderResult& b_rr, string name, string value)
271 {
272         b_rr.stamp_data_add_field(name.c_str(), value.c_str());
273 }
274
275
276 /* Utilities */
277
278 static inline Transform get_transform(const BL::Array<float, 16>& array)
279 {
280         ProjectionTransform projection;
281
282         /* We assume both types to be just 16 floats, and transpose because blender
283          * use column major matrix order while we use row major. */
284         memcpy((void *)&projection, &array, sizeof(float)*16);
285         projection = projection_transpose(projection);
286
287         /* Drop last row, matrix is assumed to be affine transform. */
288         return projection_to_transform(projection);
289 }
290
291 static inline float2 get_float2(const BL::Array<float, 2>& array)
292 {
293         return make_float2(array[0], array[1]);
294 }
295
296 static inline float3 get_float3(const BL::Array<float, 2>& array)
297 {
298         return make_float3(array[0], array[1], 0.0f);
299 }
300
301 static inline float3 get_float3(const BL::Array<float, 3>& array)
302 {
303         return make_float3(array[0], array[1], array[2]);
304 }
305
306 static inline float3 get_float3(const BL::Array<float, 4>& array)
307 {
308         return make_float3(array[0], array[1], array[2]);
309 }
310
311 static inline float4 get_float4(const BL::Array<float, 4>& array)
312 {
313         return make_float4(array[0], array[1], array[2], array[3]);
314 }
315
316 static inline int3 get_int3(const BL::Array<int, 3>& array)
317 {
318         return make_int3(array[0], array[1], array[2]);
319 }
320
321 static inline int4 get_int4(const BL::Array<int, 4>& array)
322 {
323         return make_int4(array[0], array[1], array[2], array[3]);
324 }
325
326 static inline float3 get_float3(PointerRNA& ptr, const char *name)
327 {
328         float3 f;
329         RNA_float_get_array(&ptr, name, &f.x);
330         return f;
331 }
332
333 static inline void set_float3(PointerRNA& ptr, const char *name, float3 value)
334 {
335         RNA_float_set_array(&ptr, name, &value.x);
336 }
337
338 static inline float4 get_float4(PointerRNA& ptr, const char *name)
339 {
340         float4 f;
341         RNA_float_get_array(&ptr, name, &f.x);
342         return f;
343 }
344
345 static inline void set_float4(PointerRNA& ptr, const char *name, float4 value)
346 {
347         RNA_float_set_array(&ptr, name, &value.x);
348 }
349
350 static inline bool get_boolean(PointerRNA& ptr, const char *name)
351 {
352         return RNA_boolean_get(&ptr, name)? true: false;
353 }
354
355 static inline void set_boolean(PointerRNA& ptr, const char *name, bool value)
356 {
357         RNA_boolean_set(&ptr, name, (int)value);
358 }
359
360 static inline float get_float(PointerRNA& ptr, const char *name)
361 {
362         return RNA_float_get(&ptr, name);
363 }
364
365 static inline void set_float(PointerRNA& ptr, const char *name, float value)
366 {
367         RNA_float_set(&ptr, name, value);
368 }
369
370 static inline int get_int(PointerRNA& ptr, const char *name)
371 {
372         return RNA_int_get(&ptr, name);
373 }
374
375 static inline void set_int(PointerRNA& ptr, const char *name, int value)
376 {
377         RNA_int_set(&ptr, name, value);
378 }
379
380 /* Get a RNA enum value with sanity check: if the RNA value is above num_values
381  * the function will return a fallback default value.
382  *
383  * NOTE: This function assumes that RNA enum values are a continuous sequence
384  * from 0 to num_values-1. Be careful to use it with enums where some values are
385  * deprecated!
386  */
387 static inline int get_enum(PointerRNA& ptr,
388                            const char *name,
389                            int num_values = -1,
390                            int default_value = -1)
391 {
392         int value = RNA_enum_get(&ptr, name);
393         if(num_values != -1 && value >= num_values) {
394                 assert(default_value != -1);
395                 value = default_value;
396         }
397         return value;
398 }
399
400 static inline string get_enum_identifier(PointerRNA& ptr, const char *name)
401 {
402         PropertyRNA *prop = RNA_struct_find_property(&ptr, name);
403         const char *identifier = "";
404         int value = RNA_property_enum_get(&ptr, prop);
405
406         RNA_property_enum_identifier(NULL, &ptr, prop, value, &identifier);
407
408         return string(identifier);
409 }
410
411 static inline void set_enum(PointerRNA& ptr, const char *name, int value)
412 {
413         RNA_enum_set(&ptr, name, value);
414 }
415
416 static inline void set_enum(PointerRNA& ptr, const char *name, const string &identifier)
417 {
418         RNA_enum_set_identifier(NULL, &ptr, name, identifier.c_str());
419 }
420
421 static inline string get_string(PointerRNA& ptr, const char *name)
422 {
423         char cstrbuf[1024];
424         char *cstr = RNA_string_get_alloc(&ptr, name, cstrbuf, sizeof(cstrbuf));
425         string str(cstr);
426         if(cstr != cstrbuf)
427                 MEM_freeN(cstr);
428
429         return str;
430 }
431
432 static inline void set_string(PointerRNA& ptr, const char *name, const string &value)
433 {
434         RNA_string_set(&ptr, name, value.c_str());
435 }
436
437 /* Relative Paths */
438
439 static inline string blender_absolute_path(BL::BlendData& b_data,
440                                            BL::ID& b_id,
441                                            const string& path)
442 {
443         if(path.size() >= 2 && path[0] == '/' && path[1] == '/') {
444                 string dirname;
445
446                 if(b_id.library()) {
447                         BL::ID b_library_id(b_id.library());
448                         dirname = blender_absolute_path(b_data,
449                                                         b_library_id,
450                                                         b_id.library().filepath());
451                 }
452                 else
453                         dirname = b_data.filepath();
454
455                 return path_join(path_dirname(dirname), path.substr(2));
456         }
457
458         return path;
459 }
460
461 static inline string get_text_datablock_content(const PointerRNA& ptr)
462 {
463         if(ptr.data == NULL) {
464                 return "";
465         }
466
467         string content;
468         BL::Text::lines_iterator iter;
469         for(iter.begin(ptr); iter; ++iter) {
470                 content += iter->body() + "\n";
471         }
472
473         return content;
474 }
475
476 /* Texture Space */
477
478 static inline void mesh_texture_space(BL::Mesh& b_mesh,
479                                       float3& loc,
480                                       float3& size)
481 {
482         loc = get_float3(b_mesh.texspace_location());
483         size = get_float3(b_mesh.texspace_size());
484
485         if(size.x != 0.0f) size.x = 0.5f/size.x;
486         if(size.y != 0.0f) size.y = 0.5f/size.y;
487         if(size.z != 0.0f) size.z = 0.5f/size.z;
488
489         loc = loc*size - make_float3(0.5f, 0.5f, 0.5f);
490 }
491
492 /* Object motion steps, returns 0 if no motion blur needed. */
493 static inline uint object_motion_steps(BL::Object& b_parent, BL::Object& b_ob)
494 {
495         /* Get motion enabled and steps from object itself. */
496         PointerRNA cobject = RNA_pointer_get(&b_ob.ptr, "cycles");
497         bool use_motion = get_boolean(cobject, "use_motion_blur");
498         if(!use_motion) {
499                 return 0;
500         }
501
502         uint steps = max(1, get_int(cobject, "motion_steps"));
503
504         /* Also check parent object, so motion blur and steps can be
505          * controlled by dupligroup duplicator for linked groups. */
506         if(b_parent.ptr.data != b_ob.ptr.data) {
507                 PointerRNA parent_cobject = RNA_pointer_get(&b_parent.ptr, "cycles");
508                 use_motion &= get_boolean(parent_cobject, "use_motion_blur");
509
510                 if(!use_motion) {
511                         return 0;
512                 }
513
514                 steps = max(steps, get_int(parent_cobject, "motion_steps"));
515         }
516
517         /* Use uneven number of steps so we get one keyframe at the current frame,
518          * and use 2^(steps - 1) so objects with more/fewer steps still have samples
519          * at the same times, to avoid sampling at many different times. */
520         return (2 << (steps - 1)) + 1;
521 }
522
523 /* object uses deformation motion blur */
524 static inline bool object_use_deform_motion(BL::Object& b_parent,
525                                             BL::Object& b_ob)
526 {
527         PointerRNA cobject = RNA_pointer_get(&b_ob.ptr, "cycles");
528         bool use_deform_motion = get_boolean(cobject, "use_deform_motion");
529         /* If motion blur is enabled for the object we also check
530          * whether it's enabled for the parent object as well.
531          *
532          * This way we can control motion blur from the dupligroup
533          * duplicator much easier.
534          */
535         if(use_deform_motion && b_parent.ptr.data != b_ob.ptr.data) {
536                 PointerRNA parent_cobject = RNA_pointer_get(&b_parent.ptr, "cycles");
537                 use_deform_motion &= get_boolean(parent_cobject, "use_deform_motion");
538         }
539         return use_deform_motion;
540 }
541
542 static inline BL::SmokeDomainSettings object_smoke_domain_find(BL::Object& b_ob)
543 {
544         BL::Object::modifiers_iterator b_mod;
545
546         for(b_ob.modifiers.begin(b_mod); b_mod != b_ob.modifiers.end(); ++b_mod) {
547                 if(b_mod->is_a(&RNA_SmokeModifier)) {
548                         BL::SmokeModifier b_smd(*b_mod);
549
550                         if(b_smd.smoke_type() == BL::SmokeModifier::smoke_type_DOMAIN)
551                                 return b_smd.domain_settings();
552                 }
553         }
554
555         return BL::SmokeDomainSettings(PointerRNA_NULL);
556 }
557
558 static inline BL::DomainFluidSettings object_fluid_domain_find(BL::Object b_ob)
559 {
560         BL::Object::modifiers_iterator b_mod;
561
562         for(b_ob.modifiers.begin(b_mod); b_mod != b_ob.modifiers.end(); ++b_mod) {
563                 if(b_mod->is_a(&RNA_FluidSimulationModifier)) {
564                         BL::FluidSimulationModifier b_fmd(*b_mod);
565                         BL::FluidSettings fss = b_fmd.settings();
566
567                         if(fss.type() == BL::FluidSettings::type_DOMAIN)
568                                 return (BL::DomainFluidSettings)b_fmd.settings();
569                 }
570         }
571
572         return BL::DomainFluidSettings(PointerRNA_NULL);
573 }
574
575 static inline Mesh::SubdivisionType object_subdivision_type(BL::Object& b_ob, bool preview, bool experimental)
576 {
577         PointerRNA cobj = RNA_pointer_get(&b_ob.ptr, "cycles");
578
579         if(cobj.data && b_ob.modifiers.length() > 0 && experimental) {
580                 BL::Modifier mod = b_ob.modifiers[b_ob.modifiers.length()-1];
581                 bool enabled = preview ? mod.show_viewport() : mod.show_render();
582
583                 if(enabled && mod.type() == BL::Modifier::type_SUBSURF && RNA_boolean_get(&cobj, "use_adaptive_subdivision")) {
584                         BL::SubsurfModifier subsurf(mod);
585
586                         if(subsurf.subdivision_type() == BL::SubsurfModifier::subdivision_type_CATMULL_CLARK) {
587                                 return Mesh::SUBDIVISION_CATMULL_CLARK;
588                         }
589                         else {
590                                 return Mesh::SUBDIVISION_LINEAR;
591                         }
592                 }
593         }
594
595         return Mesh::SUBDIVISION_NONE;
596 }
597
598 /* ID Map
599  *
600  * Utility class to keep in sync with blender data.
601  * Used for objects, meshes, lights and shaders. */
602
603 template<typename K, typename T>
604 class id_map {
605 public:
606         id_map(vector<T*> *scene_data_)
607         {
608                 scene_data = scene_data_;
609         }
610
611         T *find(const BL::ID& id)
612         {
613                 return find(id.ptr.id.data);
614         }
615
616         T *find(const K& key)
617         {
618                 if(b_map.find(key) != b_map.end()) {
619                         T *data = b_map[key];
620                         return data;
621                 }
622
623                 return NULL;
624         }
625
626         void set_recalc(const BL::ID& id)
627         {
628                 b_recalc.insert(id.ptr.data);
629         }
630
631         bool has_recalc()
632         {
633                 return !(b_recalc.empty());
634         }
635
636         void pre_sync()
637         {
638                 used_set.clear();
639         }
640
641         bool sync(T **r_data, const BL::ID& id)
642         {
643                 return sync(r_data, id, id, id.ptr.id.data);
644         }
645
646         bool sync(T **r_data, const BL::ID& id, const BL::ID& parent, const K& key)
647         {
648                 T *data = find(key);
649                 bool recalc;
650
651                 if(!data) {
652                         /* add data if it didn't exist yet */
653                         data = new T();
654                         scene_data->push_back(data);
655                         b_map[key] = data;
656                         recalc = true;
657                 }
658                 else {
659                         recalc = (b_recalc.find(id.ptr.data) != b_recalc.end());
660                         if(parent.ptr.data)
661                                 recalc = recalc || (b_recalc.find(parent.ptr.data) != b_recalc.end());
662                 }
663
664                 used(data);
665
666                 *r_data = data;
667                 return recalc;
668         }
669
670         bool is_used(const K& key)
671         {
672                 T *data = find(key);
673                 return (data) ? used_set.find(data) != used_set.end() : false;
674         }
675
676         void used(T *data)
677         {
678                 /* tag data as still in use */
679                 used_set.insert(data);
680         }
681
682         void set_default(T *data)
683         {
684                 b_map[NULL] = data;
685         }
686
687         bool post_sync(bool do_delete = true)
688         {
689                 /* remove unused data */
690                 vector<T*> new_scene_data;
691                 typename vector<T*>::iterator it;
692                 bool deleted = false;
693
694                 for(it = scene_data->begin(); it != scene_data->end(); it++) {
695                         T *data = *it;
696
697                         if(do_delete && used_set.find(data) == used_set.end()) {
698                                 delete data;
699                                 deleted = true;
700                         }
701                         else
702                                 new_scene_data.push_back(data);
703                 }
704
705                 *scene_data = new_scene_data;
706
707                 /* update mapping */
708                 map<K, T*> new_map;
709                 typedef pair<const K, T*> TMapPair;
710                 typename map<K, T*>::iterator jt;
711
712                 for(jt = b_map.begin(); jt != b_map.end(); jt++) {
713                         TMapPair& pair = *jt;
714
715                         if(used_set.find(pair.second) != used_set.end())
716                                 new_map[pair.first] = pair.second;
717                 }
718
719                 used_set.clear();
720                 b_recalc.clear();
721                 b_map = new_map;
722
723                 return deleted;
724         }
725
726 protected:
727         vector<T*> *scene_data;
728         map<K, T*> b_map;
729         set<T*> used_set;
730         set<void*> b_recalc;
731 };
732
733 /* Object Key */
734
735 enum { OBJECT_PERSISTENT_ID_SIZE = 16 };
736
737 struct ObjectKey {
738         void *parent;
739         int id[OBJECT_PERSISTENT_ID_SIZE];
740         void *ob;
741
742         ObjectKey(void *parent_, int id_[OBJECT_PERSISTENT_ID_SIZE], void *ob_)
743         : parent(parent_), ob(ob_)
744         {
745                 if(id_)
746                         memcpy(id, id_, sizeof(id));
747                 else
748                         memset(id, 0, sizeof(id));
749         }
750
751         bool operator<(const ObjectKey& k) const
752         {
753                 if(ob < k.ob) {
754                         return true;
755                 }
756                 else if(ob == k.ob) {
757                         if(parent < k.parent)
758                                 return true;
759                         else if(parent == k.parent)
760                                 return memcmp(id, k.id, sizeof(id)) < 0;
761                 }
762
763                 return false;
764         }
765 };
766
767 /* Particle System Key */
768
769 struct ParticleSystemKey {
770         void *ob;
771         int id[OBJECT_PERSISTENT_ID_SIZE];
772
773         ParticleSystemKey(void *ob_, int id_[OBJECT_PERSISTENT_ID_SIZE])
774         : ob(ob_)
775         {
776                 if(id_)
777                         memcpy(id, id_, sizeof(id));
778                 else
779                         memset(id, 0, sizeof(id));
780         }
781
782         bool operator<(const ParticleSystemKey& k) const
783         {
784                 /* first id is particle index, we don't compare that */
785                 if(ob < k.ob)
786                         return true;
787                 else if(ob == k.ob)
788                         return memcmp(id+1, k.id+1, sizeof(int)*(OBJECT_PERSISTENT_ID_SIZE-1)) < 0;
789
790                 return false;
791         }
792 };
793
794 class EdgeMap {
795 public:
796         EdgeMap() {
797         }
798
799         void clear() {
800                 edges_.clear();
801         }
802
803         void insert(int v0, int v1) {
804                 get_sorted_verts(v0, v1);
805                 edges_.insert(std::pair<int, int>(v0, v1));
806         }
807
808         bool exists(int v0, int v1) {
809                 get_sorted_verts(v0, v1);
810                 return edges_.find(std::pair<int, int>(v0, v1)) != edges_.end();
811         }
812
813 protected:
814         void get_sorted_verts(int& v0, int& v1) {
815                 if(v0 > v1) {
816                         swap(v0, v1);
817                 }
818         }
819
820         set< std::pair<int, int> > edges_;
821 };
822
823 CCL_NAMESPACE_END
824
825 #endif  /* __BLENDER_UTIL_H__ */