forked from bartvdbraak/blender
74c7707e8c
This way we avoid passing structures which could be up to few hundred bytes by value to the utility functions. Ideally we'll also have to add `const` qualifier in majority of the calls, but C++ RNA does not allow us to do that because it does not know if some function modifies contents or not.
707 lines
18 KiB
C++
707 lines
18 KiB
C++
/*
|
|
* Copyright 2011-2013 Blender Foundation
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#ifndef __BLENDER_UTIL_H__
|
|
#define __BLENDER_UTIL_H__
|
|
|
|
#include "util_map.h"
|
|
#include "util_path.h"
|
|
#include "util_set.h"
|
|
#include "util_transform.h"
|
|
#include "util_types.h"
|
|
#include "util_vector.h"
|
|
|
|
/* Hacks to hook into Blender API
|
|
* todo: clean this up ... */
|
|
|
|
extern "C" {
|
|
size_t BLI_timecode_string_from_time_simple(char *str, size_t maxlen, double time_seconds);
|
|
void BKE_image_user_frame_calc(void *iuser, int cfra, int fieldnr);
|
|
void BKE_image_user_file_path(void *iuser, void *ima, char *path);
|
|
unsigned char *BKE_image_get_pixels_for_frame(void *image, int frame);
|
|
float *BKE_image_get_float_pixels_for_frame(void *image, int frame);
|
|
}
|
|
|
|
CCL_NAMESPACE_BEGIN
|
|
|
|
void python_thread_state_save(void **python_thread_state);
|
|
void python_thread_state_restore(void **python_thread_state);
|
|
|
|
static inline BL::Mesh object_to_mesh(BL::BlendData& data,
|
|
BL::Object& object,
|
|
BL::Scene& scene,
|
|
bool apply_modifiers,
|
|
bool render,
|
|
bool calc_undeformed)
|
|
{
|
|
BL::Mesh me = data.meshes.new_from_object(scene, object, apply_modifiers, (render)? 2: 1, false, calc_undeformed);
|
|
if((bool)me) {
|
|
if(me.use_auto_smooth()) {
|
|
me.calc_normals_split();
|
|
}
|
|
me.calc_tessface(true);
|
|
}
|
|
return me;
|
|
}
|
|
|
|
static inline void colorramp_to_array(BL::ColorRamp& ramp,
|
|
float4 *data,
|
|
int size)
|
|
{
|
|
for(int i = 0; i < size; i++) {
|
|
float color[4];
|
|
|
|
ramp.evaluate((float)i/(float)(size-1), color);
|
|
data[i] = make_float4(color[0], color[1], color[2], color[3]);
|
|
}
|
|
}
|
|
|
|
static inline void curvemap_minmax_curve(/*const*/ BL::CurveMap& curve,
|
|
float *min_x,
|
|
float *max_x)
|
|
{
|
|
*min_x = min(*min_x, curve.points[0].location()[0]);
|
|
*max_x = max(*max_x, curve.points[curve.points.length() - 1].location()[0]);
|
|
}
|
|
|
|
static inline void curvemapping_minmax(/*const*/ BL::CurveMapping& cumap,
|
|
bool rgb_curve,
|
|
float *min_x,
|
|
float *max_x)
|
|
{
|
|
/* const int num_curves = cumap.curves.length(); */ /* Gives linking error so far. */
|
|
const int num_curves = rgb_curve? 4: 3;
|
|
*min_x = FLT_MAX;
|
|
*max_x = -FLT_MAX;
|
|
for(int i = 0; i < num_curves; ++i) {
|
|
BL::CurveMap map(cumap.curves[i]);
|
|
curvemap_minmax_curve(map, min_x, max_x);
|
|
}
|
|
}
|
|
|
|
static inline void curvemapping_to_array(BL::CurveMapping& cumap,
|
|
float *data,
|
|
int size)
|
|
{
|
|
cumap.update();
|
|
BL::CurveMap curve = cumap.curves[0];
|
|
for(int i = 0; i < size; i++) {
|
|
float t = (float)i/(float)(size-1);
|
|
data[i] = curve.evaluate(t);
|
|
}
|
|
}
|
|
|
|
static inline void curvemapping_color_to_array(BL::CurveMapping& cumap,
|
|
float4 *data,
|
|
int size,
|
|
bool rgb_curve)
|
|
{
|
|
float min_x = 0.0f, max_x = 1.0f;
|
|
|
|
/* TODO(sergey): There is no easy way to automatically guess what is
|
|
* the range to be used here for the case when mapping is applied on
|
|
* top of another mapping (i.e. R curve applied on top of common
|
|
* one).
|
|
*
|
|
* Using largest possible range form all curves works correct for the
|
|
* cases like vector curves and should be good enough heuristic for
|
|
* the color curves as well.
|
|
*
|
|
* There might be some better estimations here tho.
|
|
*/
|
|
curvemapping_minmax(cumap, rgb_curve, &min_x, &max_x);
|
|
|
|
const float range_x = max_x - min_x;
|
|
|
|
cumap.update();
|
|
|
|
BL::CurveMap mapR = cumap.curves[0];
|
|
BL::CurveMap mapG = cumap.curves[1];
|
|
BL::CurveMap mapB = cumap.curves[2];
|
|
|
|
if(rgb_curve) {
|
|
BL::CurveMap mapI = cumap.curves[3];
|
|
|
|
for(int i = 0; i < size; i++) {
|
|
float t = min_x + (float)i/(float)(size-1) * range_x;
|
|
|
|
data[i][0] = mapR.evaluate(mapI.evaluate(t));
|
|
data[i][1] = mapG.evaluate(mapI.evaluate(t));
|
|
data[i][2] = mapB.evaluate(mapI.evaluate(t));
|
|
}
|
|
}
|
|
else {
|
|
for(int i = 0; i < size; i++) {
|
|
float t = min_x + (float)i/(float)(size-1) * range_x;
|
|
|
|
data[i][0] = mapR.evaluate(t);
|
|
data[i][1] = mapG.evaluate(t);
|
|
data[i][2] = mapB.evaluate(t);
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline bool BKE_object_is_modified(BL::Object& self,
|
|
BL::Scene& scene,
|
|
bool preview)
|
|
{
|
|
return self.is_modified(scene, (preview)? (1<<0): (1<<1))? true: false;
|
|
}
|
|
|
|
static inline bool BKE_object_is_deform_modified(BL::Object& self,
|
|
BL::Scene& scene,
|
|
bool preview)
|
|
{
|
|
return self.is_deform_modified(scene, (preview)? (1<<0): (1<<1))? true: false;
|
|
}
|
|
|
|
static inline int render_resolution_x(BL::RenderSettings& b_render)
|
|
{
|
|
return b_render.resolution_x()*b_render.resolution_percentage()/100;
|
|
}
|
|
|
|
static inline int render_resolution_y(BL::RenderSettings& b_render)
|
|
{
|
|
return b_render.resolution_y()*b_render.resolution_percentage()/100;
|
|
}
|
|
|
|
static inline string image_user_file_path(BL::ImageUser& iuser,
|
|
BL::Image& ima,
|
|
int cfra)
|
|
{
|
|
char filepath[1024];
|
|
BKE_image_user_frame_calc(iuser.ptr.data, cfra, 0);
|
|
BKE_image_user_file_path(iuser.ptr.data, ima.ptr.data, filepath);
|
|
return string(filepath);
|
|
}
|
|
|
|
static inline int image_user_frame_number(BL::ImageUser& iuser, int cfra)
|
|
{
|
|
BKE_image_user_frame_calc(iuser.ptr.data, cfra, 0);
|
|
return iuser.frame_current();
|
|
}
|
|
|
|
static inline unsigned char *image_get_pixels_for_frame(BL::Image& image,
|
|
int frame)
|
|
{
|
|
return BKE_image_get_pixels_for_frame(image.ptr.data, frame);
|
|
}
|
|
|
|
static inline float *image_get_float_pixels_for_frame(BL::Image& image,
|
|
int frame)
|
|
{
|
|
return BKE_image_get_float_pixels_for_frame(image.ptr.data, frame);
|
|
}
|
|
|
|
/* Utilities */
|
|
|
|
static inline Transform get_transform(const BL::Array<float, 16>& array)
|
|
{
|
|
Transform tfm;
|
|
|
|
/* we assume both types to be just 16 floats, and transpose because blender
|
|
* use column major matrix order while we use row major */
|
|
memcpy(&tfm, &array, sizeof(float)*16);
|
|
tfm = transform_transpose(tfm);
|
|
|
|
return tfm;
|
|
}
|
|
|
|
static inline float2 get_float2(const BL::Array<float, 2>& array)
|
|
{
|
|
return make_float2(array[0], array[1]);
|
|
}
|
|
|
|
static inline float3 get_float3(const BL::Array<float, 2>& array)
|
|
{
|
|
return make_float3(array[0], array[1], 0.0f);
|
|
}
|
|
|
|
static inline float3 get_float3(const BL::Array<float, 3>& array)
|
|
{
|
|
return make_float3(array[0], array[1], array[2]);
|
|
}
|
|
|
|
static inline float3 get_float3(const BL::Array<float, 4>& array)
|
|
{
|
|
return make_float3(array[0], array[1], array[2]);
|
|
}
|
|
|
|
static inline float4 get_float4(const BL::Array<float, 4>& array)
|
|
{
|
|
return make_float4(array[0], array[1], array[2], array[3]);
|
|
}
|
|
|
|
static inline int3 get_int3(const BL::Array<int, 3>& array)
|
|
{
|
|
return make_int3(array[0], array[1], array[2]);
|
|
}
|
|
|
|
static inline int4 get_int4(const BL::Array<int, 4>& array)
|
|
{
|
|
return make_int4(array[0], array[1], array[2], array[3]);
|
|
}
|
|
|
|
static inline uint get_layer(const BL::Array<int, 20>& array)
|
|
{
|
|
uint layer = 0;
|
|
|
|
for(uint i = 0; i < 20; i++)
|
|
if(array[i])
|
|
layer |= (1 << i);
|
|
|
|
return layer;
|
|
}
|
|
|
|
static inline uint get_layer(const BL::Array<int, 20>& array,
|
|
const BL::Array<int, 8>& local_array,
|
|
bool use_local,
|
|
bool is_light = false,
|
|
uint scene_layers = (1 << 20) - 1)
|
|
{
|
|
uint layer = 0;
|
|
|
|
for(uint i = 0; i < 20; i++)
|
|
if(array[i])
|
|
layer |= (1 << i);
|
|
|
|
if(is_light) {
|
|
/* Consider light is visible if it was visible without layer
|
|
* override, which matches behavior of Blender Internal.
|
|
*/
|
|
if(layer & scene_layers) {
|
|
for(uint i = 0; i < 8; i++)
|
|
layer |= (1 << (20+i));
|
|
}
|
|
}
|
|
else {
|
|
for(uint i = 0; i < 8; i++)
|
|
if(local_array[i])
|
|
layer |= (1 << (20+i));
|
|
}
|
|
|
|
/* we don't have spare bits for localview (normally 20-28) because
|
|
* PATH_RAY_LAYER_SHIFT uses 20-32. So - check if we have localview and if
|
|
* so, shift local view bits down to 1-8, since this is done for the view
|
|
* port only - it should be OK and not conflict with render layers. */
|
|
if(use_local)
|
|
layer >>= 20;
|
|
|
|
return layer;
|
|
}
|
|
|
|
static inline float3 get_float3(PointerRNA& ptr, const char *name)
|
|
{
|
|
float3 f;
|
|
RNA_float_get_array(&ptr, name, &f.x);
|
|
return f;
|
|
}
|
|
|
|
static inline void set_float3(PointerRNA& ptr, const char *name, float3 value)
|
|
{
|
|
RNA_float_set_array(&ptr, name, &value.x);
|
|
}
|
|
|
|
static inline float4 get_float4(PointerRNA& ptr, const char *name)
|
|
{
|
|
float4 f;
|
|
RNA_float_get_array(&ptr, name, &f.x);
|
|
return f;
|
|
}
|
|
|
|
static inline void set_float4(PointerRNA& ptr, const char *name, float4 value)
|
|
{
|
|
RNA_float_set_array(&ptr, name, &value.x);
|
|
}
|
|
|
|
static inline bool get_boolean(PointerRNA& ptr, const char *name)
|
|
{
|
|
return RNA_boolean_get(&ptr, name)? true: false;
|
|
}
|
|
|
|
static inline void set_boolean(PointerRNA& ptr, const char *name, bool value)
|
|
{
|
|
RNA_boolean_set(&ptr, name, (int)value);
|
|
}
|
|
|
|
static inline float get_float(PointerRNA& ptr, const char *name)
|
|
{
|
|
return RNA_float_get(&ptr, name);
|
|
}
|
|
|
|
static inline void set_float(PointerRNA& ptr, const char *name, float value)
|
|
{
|
|
RNA_float_set(&ptr, name, value);
|
|
}
|
|
|
|
static inline int get_int(PointerRNA& ptr, const char *name)
|
|
{
|
|
return RNA_int_get(&ptr, name);
|
|
}
|
|
|
|
static inline void set_int(PointerRNA& ptr, const char *name, int value)
|
|
{
|
|
RNA_int_set(&ptr, name, value);
|
|
}
|
|
|
|
static inline int get_enum(PointerRNA& ptr, const char *name)
|
|
{
|
|
return RNA_enum_get(&ptr, name);
|
|
}
|
|
|
|
static inline string get_enum_identifier(PointerRNA& ptr, const char *name)
|
|
{
|
|
PropertyRNA *prop = RNA_struct_find_property(&ptr, name);
|
|
const char *identifier = "";
|
|
int value = RNA_property_enum_get(&ptr, prop);
|
|
|
|
RNA_property_enum_identifier(NULL, &ptr, prop, value, &identifier);
|
|
|
|
return string(identifier);
|
|
}
|
|
|
|
static inline void set_enum(PointerRNA& ptr, const char *name, int value)
|
|
{
|
|
RNA_enum_set(&ptr, name, value);
|
|
}
|
|
|
|
static inline void set_enum(PointerRNA& ptr, const char *name, const string &identifier)
|
|
{
|
|
RNA_enum_set_identifier(NULL, &ptr, name, identifier.c_str());
|
|
}
|
|
|
|
static inline string get_string(PointerRNA& ptr, const char *name)
|
|
{
|
|
char cstrbuf[1024];
|
|
char *cstr = RNA_string_get_alloc(&ptr, name, cstrbuf, sizeof(cstrbuf));
|
|
string str(cstr);
|
|
if(cstr != cstrbuf)
|
|
MEM_freeN(cstr);
|
|
|
|
return str;
|
|
}
|
|
|
|
static inline void set_string(PointerRNA& ptr, const char *name, const string &value)
|
|
{
|
|
RNA_string_set(&ptr, name, value.c_str());
|
|
}
|
|
|
|
/* Relative Paths */
|
|
|
|
static inline string blender_absolute_path(BL::BlendData& b_data,
|
|
BL::ID& b_id,
|
|
const string& path)
|
|
{
|
|
if(path.size() >= 2 && path[0] == '/' && path[1] == '/') {
|
|
string dirname;
|
|
|
|
if(b_id.library()) {
|
|
BL::ID b_library_id(b_id.library());
|
|
dirname = blender_absolute_path(b_data,
|
|
b_library_id,
|
|
b_id.library().filepath());
|
|
}
|
|
else
|
|
dirname = b_data.filepath();
|
|
|
|
return path_join(path_dirname(dirname), path.substr(2));
|
|
}
|
|
|
|
return path;
|
|
}
|
|
|
|
/* Texture Space */
|
|
|
|
static inline void mesh_texture_space(BL::Mesh& b_mesh,
|
|
float3& loc,
|
|
float3& size)
|
|
{
|
|
loc = get_float3(b_mesh.texspace_location());
|
|
size = get_float3(b_mesh.texspace_size());
|
|
|
|
if(size.x != 0.0f) size.x = 0.5f/size.x;
|
|
if(size.y != 0.0f) size.y = 0.5f/size.y;
|
|
if(size.z != 0.0f) size.z = 0.5f/size.z;
|
|
|
|
loc = loc*size - make_float3(0.5f, 0.5f, 0.5f);
|
|
}
|
|
|
|
/* object used for motion blur */
|
|
static inline bool object_use_motion(BL::Object& b_parent, BL::Object& b_ob)
|
|
{
|
|
PointerRNA cobject = RNA_pointer_get(&b_ob.ptr, "cycles");
|
|
bool use_motion = get_boolean(cobject, "use_motion_blur");
|
|
/* If motion blur is enabled for the object we also check
|
|
* whether it's enabled for the parent object as well.
|
|
*
|
|
* This way we can control motion blur from the dupligroup
|
|
* duplicator much easier.
|
|
*/
|
|
if(use_motion && b_parent.ptr.data != b_ob.ptr.data) {
|
|
PointerRNA parent_cobject = RNA_pointer_get(&b_parent.ptr, "cycles");
|
|
use_motion &= get_boolean(parent_cobject, "use_motion_blur");
|
|
}
|
|
return use_motion;
|
|
}
|
|
|
|
/* object motion steps */
|
|
static inline uint object_motion_steps(BL::Object& b_ob)
|
|
{
|
|
PointerRNA cobject = RNA_pointer_get(&b_ob.ptr, "cycles");
|
|
uint steps = get_int(cobject, "motion_steps");
|
|
|
|
/* use uneven number of steps so we get one keyframe at the current frame,
|
|
* and ue 2^(steps - 1) so objects with more/fewer steps still have samples
|
|
* at the same times, to avoid sampling at many different times */
|
|
return (2 << (steps - 1)) + 1;
|
|
}
|
|
|
|
/* object uses deformation motion blur */
|
|
static inline bool object_use_deform_motion(BL::Object& b_parent,
|
|
BL::Object& b_ob)
|
|
{
|
|
PointerRNA cobject = RNA_pointer_get(&b_ob.ptr, "cycles");
|
|
bool use_deform_motion = get_boolean(cobject, "use_deform_motion");
|
|
/* If motion blur is enabled for the object we also check
|
|
* whether it's enabled for the parent object as well.
|
|
*
|
|
* This way we can control motion blur from the dupligroup
|
|
* duplicator much easier.
|
|
*/
|
|
if(use_deform_motion && b_parent.ptr.data != b_ob.ptr.data) {
|
|
PointerRNA parent_cobject = RNA_pointer_get(&b_parent.ptr, "cycles");
|
|
use_deform_motion &= get_boolean(parent_cobject, "use_deform_motion");
|
|
}
|
|
return use_deform_motion;
|
|
}
|
|
|
|
static inline BL::SmokeDomainSettings object_smoke_domain_find(BL::Object& b_ob)
|
|
{
|
|
BL::Object::modifiers_iterator b_mod;
|
|
|
|
for(b_ob.modifiers.begin(b_mod); b_mod != b_ob.modifiers.end(); ++b_mod) {
|
|
if(b_mod->is_a(&RNA_SmokeModifier)) {
|
|
BL::SmokeModifier b_smd(*b_mod);
|
|
|
|
if(b_smd.smoke_type() == BL::SmokeModifier::smoke_type_DOMAIN)
|
|
return b_smd.domain_settings();
|
|
}
|
|
}
|
|
|
|
return BL::SmokeDomainSettings(PointerRNA_NULL);
|
|
}
|
|
|
|
/* ID Map
|
|
*
|
|
* Utility class to keep in sync with blender data.
|
|
* Used for objects, meshes, lights and shaders. */
|
|
|
|
template<typename K, typename T>
|
|
class id_map {
|
|
public:
|
|
id_map(vector<T*> *scene_data_)
|
|
{
|
|
scene_data = scene_data_;
|
|
}
|
|
|
|
T *find(const BL::ID& id)
|
|
{
|
|
return find(id.ptr.id.data);
|
|
}
|
|
|
|
T *find(const K& key)
|
|
{
|
|
if(b_map.find(key) != b_map.end()) {
|
|
T *data = b_map[key];
|
|
return data;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void set_recalc(const BL::ID& id)
|
|
{
|
|
b_recalc.insert(id.ptr.data);
|
|
}
|
|
|
|
bool has_recalc()
|
|
{
|
|
return !(b_recalc.empty());
|
|
}
|
|
|
|
void pre_sync()
|
|
{
|
|
used_set.clear();
|
|
}
|
|
|
|
bool sync(T **r_data, const BL::ID& id)
|
|
{
|
|
return sync(r_data, id, id, id.ptr.id.data);
|
|
}
|
|
|
|
bool sync(T **r_data, const BL::ID& id, const BL::ID& parent, const K& key)
|
|
{
|
|
T *data = find(key);
|
|
bool recalc;
|
|
|
|
if(!data) {
|
|
/* add data if it didn't exist yet */
|
|
data = new T();
|
|
scene_data->push_back(data);
|
|
b_map[key] = data;
|
|
recalc = true;
|
|
}
|
|
else {
|
|
recalc = (b_recalc.find(id.ptr.data) != b_recalc.end());
|
|
if(parent.ptr.data)
|
|
recalc = recalc || (b_recalc.find(parent.ptr.data) != b_recalc.end());
|
|
}
|
|
|
|
used(data);
|
|
|
|
*r_data = data;
|
|
return recalc;
|
|
}
|
|
|
|
bool is_used(const K& key)
|
|
{
|
|
T *data = find(key);
|
|
return (data) ? used_set.find(data) != used_set.end() : false;
|
|
}
|
|
|
|
void used(T *data)
|
|
{
|
|
/* tag data as still in use */
|
|
used_set.insert(data);
|
|
}
|
|
|
|
void set_default(T *data)
|
|
{
|
|
b_map[NULL] = data;
|
|
}
|
|
|
|
bool post_sync(bool do_delete = true)
|
|
{
|
|
/* remove unused data */
|
|
vector<T*> new_scene_data;
|
|
typename vector<T*>::iterator it;
|
|
bool deleted = false;
|
|
|
|
for(it = scene_data->begin(); it != scene_data->end(); it++) {
|
|
T *data = *it;
|
|
|
|
if(do_delete && used_set.find(data) == used_set.end()) {
|
|
delete data;
|
|
deleted = true;
|
|
}
|
|
else
|
|
new_scene_data.push_back(data);
|
|
}
|
|
|
|
*scene_data = new_scene_data;
|
|
|
|
/* update mapping */
|
|
map<K, T*> new_map;
|
|
typedef pair<const K, T*> TMapPair;
|
|
typename map<K, T*>::iterator jt;
|
|
|
|
for(jt = b_map.begin(); jt != b_map.end(); jt++) {
|
|
TMapPair& pair = *jt;
|
|
|
|
if(used_set.find(pair.second) != used_set.end())
|
|
new_map[pair.first] = pair.second;
|
|
}
|
|
|
|
used_set.clear();
|
|
b_recalc.clear();
|
|
b_map = new_map;
|
|
|
|
return deleted;
|
|
}
|
|
|
|
protected:
|
|
vector<T*> *scene_data;
|
|
map<K, T*> b_map;
|
|
set<T*> used_set;
|
|
set<void*> b_recalc;
|
|
};
|
|
|
|
/* Object Key */
|
|
|
|
enum { OBJECT_PERSISTENT_ID_SIZE = 8 };
|
|
|
|
struct ObjectKey {
|
|
void *parent;
|
|
int id[OBJECT_PERSISTENT_ID_SIZE];
|
|
void *ob;
|
|
|
|
ObjectKey(void *parent_, int id_[OBJECT_PERSISTENT_ID_SIZE], void *ob_)
|
|
: parent(parent_), ob(ob_)
|
|
{
|
|
if(id_)
|
|
memcpy(id, id_, sizeof(id));
|
|
else
|
|
memset(id, 0, sizeof(id));
|
|
}
|
|
|
|
bool operator<(const ObjectKey& k) const
|
|
{
|
|
if(ob < k.ob) {
|
|
return true;
|
|
}
|
|
else if(ob == k.ob) {
|
|
if(parent < k.parent)
|
|
return true;
|
|
else if(parent == k.parent)
|
|
return memcmp(id, k.id, sizeof(id)) < 0;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
};
|
|
|
|
/* Particle System Key */
|
|
|
|
struct ParticleSystemKey {
|
|
void *ob;
|
|
int id[OBJECT_PERSISTENT_ID_SIZE];
|
|
|
|
ParticleSystemKey(void *ob_, int id_[OBJECT_PERSISTENT_ID_SIZE])
|
|
: ob(ob_)
|
|
{
|
|
if(id_)
|
|
memcpy(id, id_, sizeof(id));
|
|
else
|
|
memset(id, 0, sizeof(id));
|
|
}
|
|
|
|
bool operator<(const ParticleSystemKey& k) const
|
|
{
|
|
/* first id is particle index, we don't compare that */
|
|
if(ob < k.ob)
|
|
return true;
|
|
else if(ob == k.ob)
|
|
return memcmp(id+1, k.id+1, sizeof(int)*(OBJECT_PERSISTENT_ID_SIZE-1)) < 0;
|
|
|
|
return false;
|
|
}
|
|
};
|
|
|
|
CCL_NAMESPACE_END
|
|
|
|
#endif /* __BLENDER_UTIL_H__ */
|
|
|