Add Chromium-only Blender WebEngine parity work
This commit is contained in:
821
blender-5.2.0/intern/cycles/blender/util.h
Normal file
821
blender-5.2.0/intern/cycles/blender/util.h
Normal file
@@ -0,0 +1,821 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BKE_colorband.hh"
|
||||
#include "BKE_colortools.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "DNA_fluid_types.h"
|
||||
#include "DNA_text_types.h"
|
||||
#include "RE_engine.h"
|
||||
#include "RNA_access.hh"
|
||||
|
||||
#include "scene/mesh.h"
|
||||
#include "scene/scene.h"
|
||||
|
||||
#include "util/algorithm.h"
|
||||
#include "util/array.h"
|
||||
#include "util/path.h"
|
||||
#include "util/set.h"
|
||||
#include "util/transform.h"
|
||||
#include "util/types.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_modifier_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_view3d_types.h"
|
||||
|
||||
#include "BKE_global.hh"
|
||||
#include "BKE_image.hh"
|
||||
#include "BKE_lib_id.hh"
|
||||
#include "BKE_mesh.h"
|
||||
#include "BKE_mesh_types.hh"
|
||||
#include "BKE_mesh_wrapper.hh"
|
||||
#include "BKE_object.hh"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* To make GS macro work. */
|
||||
using ID_Type = blender::ID_Type;
|
||||
|
||||
static inline blender::ID *object_get_data(const blender::Object &b_ob,
|
||||
const bool use_adaptive_subdivision)
|
||||
{
|
||||
if (!use_adaptive_subdivision && b_ob.type == blender::OB_MESH) {
|
||||
return &BKE_mesh_wrapper_ensure_subdivision(blender::id_cast<blender::Mesh *>(b_ob.data))->id;
|
||||
}
|
||||
|
||||
return reinterpret_cast<blender::ID *>(b_ob.data);
|
||||
}
|
||||
|
||||
struct BObjectInfo {
|
||||
/* Object directly provided by the depsgraph iterator. This object is only valid during one
|
||||
* iteration and must not be accessed afterwards. Transforms and visibility should be checked on
|
||||
* this object. */
|
||||
blender::Object *iter_object;
|
||||
|
||||
/* This object remains alive even after the object iterator is done. It corresponds to one
|
||||
* original object. It is the object that owns the object data below. */
|
||||
blender::Object *real_object;
|
||||
|
||||
/* The object-data referenced by the iter object. This is still valid after the depsgraph
|
||||
* iterator is done. It might have a different type compared to object_get_data(real_object). */
|
||||
blender::ID *object_data;
|
||||
|
||||
/* Object will use adaptive subdivision. */
|
||||
bool use_adaptive_subdivision;
|
||||
|
||||
/* True when the current geometry is the data of the referenced object. False when it is a
|
||||
* geometry instance that does not have a 1-to-1 relationship with an object. */
|
||||
bool is_real_object_data() const
|
||||
{
|
||||
return object_get_data(*real_object, use_adaptive_subdivision) == object_data;
|
||||
}
|
||||
};
|
||||
|
||||
static inline blender::Mesh *object_copy_mesh_data(const BObjectInfo &b_ob_info)
|
||||
{
|
||||
blender::Mesh *mesh = BKE_mesh_new_from_object(
|
||||
nullptr, b_ob_info.real_object, false, false, !b_ob_info.use_adaptive_subdivision);
|
||||
return mesh;
|
||||
}
|
||||
|
||||
int blender_attribute_name_split_type(ustring name, string *r_real_name);
|
||||
|
||||
void python_thread_state_save(void **python_thread_state);
|
||||
void python_thread_state_restore(void **python_thread_state);
|
||||
|
||||
static inline blender::Mesh *object_to_mesh(BObjectInfo &b_ob_info)
|
||||
{
|
||||
blender::Mesh *mesh = (GS(b_ob_info.object_data->name) == blender::ID_ME) ?
|
||||
blender::id_cast<blender::Mesh *>(b_ob_info.object_data) :
|
||||
nullptr;
|
||||
|
||||
if (b_ob_info.is_real_object_data()) {
|
||||
if (mesh) {
|
||||
if (mesh->runtime->edit_mesh) {
|
||||
/* Flush edit-mesh to mesh, including all data layers. */
|
||||
mesh = object_copy_mesh_data(b_ob_info);
|
||||
}
|
||||
}
|
||||
else {
|
||||
mesh = object_copy_mesh_data(b_ob_info);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* TODO: what to do about non-mesh geometry instances? */
|
||||
}
|
||||
|
||||
if (mesh) {
|
||||
if (b_ob_info.use_adaptive_subdivision) {
|
||||
mesh->corner_tris();
|
||||
}
|
||||
}
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
static inline void free_object_to_mesh(BObjectInfo &b_ob_info, blender::Mesh &mesh)
|
||||
{
|
||||
if (!b_ob_info.is_real_object_data()) {
|
||||
return;
|
||||
}
|
||||
/* Free mesh if we didn't just use the existing one. */
|
||||
blender::Object *object = b_ob_info.real_object;
|
||||
if (object_get_data(*object, b_ob_info.use_adaptive_subdivision) != &mesh.id) {
|
||||
BKE_id_free(nullptr, &mesh.id);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void colorramp_to_array(const blender::ColorBand &ramp,
|
||||
array<packed_float3> &ramp_color,
|
||||
array<float> &ramp_alpha,
|
||||
const int size)
|
||||
{
|
||||
const int full_size = size + 1;
|
||||
ramp_color.resize(full_size);
|
||||
ramp_alpha.resize(full_size);
|
||||
|
||||
for (int i = 0; i < full_size; i++) {
|
||||
float color[4];
|
||||
|
||||
BKE_colorband_evaluate(&ramp, float(i) / float(size), color);
|
||||
ramp_color[i] = make_float3(color[0], color[1], color[2]);
|
||||
ramp_alpha[i] = color[3];
|
||||
}
|
||||
}
|
||||
|
||||
static inline void curvemap_minmax_curve(const blender::CurveMap &curve,
|
||||
float *min_x,
|
||||
float *max_x)
|
||||
{
|
||||
const blender::Span<blender::CurveMapPoint> points(curve.curve, curve.totpoint);
|
||||
*min_x = min(*min_x, points.first().x);
|
||||
*max_x = max(*max_x, points.last().x);
|
||||
}
|
||||
|
||||
static inline void curvemapping_minmax(const blender::CurveMapping &cumap,
|
||||
const int num_curves,
|
||||
float *min_x,
|
||||
float *max_x)
|
||||
{
|
||||
// const int num_curves = cumap.curves.length(); /* Gives linking error so far. */
|
||||
*min_x = FLT_MAX;
|
||||
*max_x = -FLT_MAX;
|
||||
for (int i = 0; i < num_curves; ++i) {
|
||||
const blender::CurveMap &map(cumap.cm[i]);
|
||||
curvemap_minmax_curve(map, min_x, max_x);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void curvemapping_to_array(const blender::CurveMapping &cumap,
|
||||
array<float> &data,
|
||||
const int size)
|
||||
{
|
||||
BKE_curvemapping_changed_all(&const_cast<blender::CurveMapping &>(cumap));
|
||||
const blender::CurveMap &curve = cumap.cm[0];
|
||||
const int full_size = size + 1;
|
||||
data.resize(full_size);
|
||||
if (!curve.table) {
|
||||
BKE_curvemapping_init(&const_cast<blender::CurveMapping &>(cumap));
|
||||
}
|
||||
for (int i = 0; i < full_size; i++) {
|
||||
const float t = float(i) / float(size);
|
||||
data[i] = BKE_curvemap_evaluateF(&cumap, &curve, t);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void curvemapping_float_to_array(const blender::CurveMapping &cumap,
|
||||
array<float> &data,
|
||||
const int size)
|
||||
{
|
||||
float min = 0.0f;
|
||||
float max = 1.0f;
|
||||
|
||||
curvemapping_minmax(cumap, 1, &min, &max);
|
||||
|
||||
const float range = max - min;
|
||||
|
||||
BKE_curvemapping_changed_all(&const_cast<blender::CurveMapping &>(cumap));
|
||||
|
||||
const blender::CurveMap &map = cumap.cm[0];
|
||||
|
||||
const int full_size = size + 1;
|
||||
data.resize(full_size);
|
||||
if (!map.table) {
|
||||
BKE_curvemapping_init(&const_cast<blender::CurveMapping &>(cumap));
|
||||
}
|
||||
for (int i = 0; i < full_size; i++) {
|
||||
const float t = min + float(i) / float(size) * range;
|
||||
data[i] = BKE_curvemap_evaluateF(&cumap, &map, t);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void curvemapping_color_to_array(const blender::CurveMapping &cumap,
|
||||
array<packed_float3> &data,
|
||||
const int size,
|
||||
bool rgb_curve)
|
||||
{
|
||||
float min_x = 0.0f;
|
||||
float 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.
|
||||
*/
|
||||
const int num_curves = rgb_curve ? 4 : 3;
|
||||
curvemapping_minmax(cumap, num_curves, &min_x, &max_x);
|
||||
|
||||
const float range_x = max_x - min_x;
|
||||
|
||||
BKE_curvemapping_changed_all(&const_cast<blender::CurveMapping &>(cumap));
|
||||
|
||||
const blender::CurveMap &mapR = cumap.cm[0];
|
||||
const blender::CurveMap &mapG = cumap.cm[1];
|
||||
const blender::CurveMap &mapB = cumap.cm[2];
|
||||
if (!mapR.table || !mapG.table || !mapB.table) {
|
||||
BKE_curvemapping_init(&const_cast<blender::CurveMapping &>(cumap));
|
||||
}
|
||||
|
||||
const int full_size = size + 1;
|
||||
data.resize(full_size);
|
||||
|
||||
if (rgb_curve) {
|
||||
const blender::CurveMap &mapI = cumap.cm[3];
|
||||
if (!mapR.table || !mapG.table || !mapB.table || !mapI.table) {
|
||||
BKE_curvemapping_init(&const_cast<blender::CurveMapping &>(cumap));
|
||||
}
|
||||
|
||||
for (int i = 0; i < full_size; i++) {
|
||||
const float t = min_x + float(i) / float(size) * range_x;
|
||||
data[i] = make_float3(
|
||||
BKE_curvemap_evaluateF(&cumap, &mapR, BKE_curvemap_evaluateF(&cumap, &mapI, t)),
|
||||
BKE_curvemap_evaluateF(&cumap, &mapG, BKE_curvemap_evaluateF(&cumap, &mapI, t)),
|
||||
BKE_curvemap_evaluateF(&cumap, &mapB, BKE_curvemap_evaluateF(&cumap, &mapI, t)));
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!mapR.table || !mapG.table || !mapB.table) {
|
||||
BKE_curvemapping_init(&const_cast<blender::CurveMapping &>(cumap));
|
||||
}
|
||||
|
||||
for (int i = 0; i < full_size; i++) {
|
||||
const float t = min_x + float(i) / float(size) * range_x;
|
||||
data[i] = make_float3(BKE_curvemap_evaluateF(&cumap, &mapR, t),
|
||||
BKE_curvemap_evaluateF(&cumap, &mapG, t),
|
||||
BKE_curvemap_evaluateF(&cumap, &mapB, t));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool BKE_object_is_deform_modified(BObjectInfo &self,
|
||||
blender::Scene &scene,
|
||||
bool preview)
|
||||
{
|
||||
if (!self.is_real_object_data()) {
|
||||
/* Comes from geometry nodes, can't use heuristic to guess if it's animated. */
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Use heuristic to quickly check if object is potentially animated. */
|
||||
const int settings = preview ? blender::eModifierMode_Realtime : blender::eModifierMode_Render;
|
||||
return (blender::BKE_object_is_deform_modified(&scene, self.real_object) & settings) != 0;
|
||||
}
|
||||
|
||||
static inline int render_resolution_x(const blender::RenderData &b_render)
|
||||
{
|
||||
return b_render.xsch * b_render.size / 100;
|
||||
}
|
||||
|
||||
static inline int render_resolution_y(const blender::RenderData &b_render)
|
||||
{
|
||||
return b_render.ysch * b_render.size / 100;
|
||||
}
|
||||
|
||||
static inline string image_user_file_path(blender::Main &data,
|
||||
blender::ImageUser &iuser,
|
||||
blender::Image &ima,
|
||||
const int cfra)
|
||||
{
|
||||
char filepath[1024];
|
||||
BKE_image_user_frame_calc(&ima, &iuser, cfra);
|
||||
BKE_image_user_file_path_ex(&data, &iuser, &ima, filepath, false, true);
|
||||
|
||||
return string(filepath);
|
||||
}
|
||||
|
||||
static inline int image_user_frame_number(blender::ImageUser &iuser,
|
||||
blender::Image &ima,
|
||||
const int cfra)
|
||||
{
|
||||
BKE_image_user_frame_calc(&ima, &iuser, cfra);
|
||||
return iuser.framenr;
|
||||
}
|
||||
|
||||
static inline bool image_is_builtin(blender::Image &ima, blender::RenderEngine &engine)
|
||||
{
|
||||
const blender::eImageSource image_source = blender::eImageSource(ima.source);
|
||||
if (image_source == blender::IMA_SRC_TILED) {
|
||||
/* If any tile is marked as generated, then treat the entire Image as built-in. */
|
||||
for (blender::ImageTile &tile : ima.tiles) {
|
||||
if (tile.gen_flag & blender::IMA_GEN_TILE) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return BKE_image_has_packedfile(&ima) || image_source == blender::IMA_SRC_GENERATED ||
|
||||
image_source == blender::IMA_SRC_MOVIE || BKE_image_is_dirty(&ima) ||
|
||||
((engine.flag & blender::RE_ENGINE_PREVIEW) != 0 &&
|
||||
image_source != blender::IMA_SRC_SEQUENCE);
|
||||
}
|
||||
|
||||
static inline void render_add_metadata(blender::RenderResult &b_rr, string name, string value)
|
||||
{
|
||||
BKE_render_result_stamp_data(&b_rr, name.c_str(), value.c_str());
|
||||
}
|
||||
|
||||
/* Utilities */
|
||||
|
||||
static inline Transform get_transform(const blender::float4x4 &matrix)
|
||||
{
|
||||
/* Convert from Blender column major to Cycles row major, assume it's an affine transform that
|
||||
* does not need the last row. */
|
||||
const float *ptr = matrix.base_ptr();
|
||||
return make_transform(ptr[0],
|
||||
ptr[4],
|
||||
ptr[8],
|
||||
ptr[12],
|
||||
|
||||
ptr[1],
|
||||
ptr[5],
|
||||
ptr[9],
|
||||
ptr[13],
|
||||
|
||||
ptr[2],
|
||||
ptr[6],
|
||||
ptr[10],
|
||||
ptr[14]);
|
||||
}
|
||||
|
||||
static inline float2 get_float2(blender::PointerRNA &ptr, const char *name)
|
||||
{
|
||||
float2 f;
|
||||
RNA_float_get_array(&ptr, name, &f.x);
|
||||
return f;
|
||||
}
|
||||
|
||||
static inline void set_float2(blender::PointerRNA &ptr, const char *name, const float2 value)
|
||||
{
|
||||
RNA_float_set_array(&ptr, name, &value.x);
|
||||
}
|
||||
|
||||
static inline float3 get_float3(blender::PointerRNA &ptr, const char *name)
|
||||
{
|
||||
float3 f;
|
||||
RNA_float_get_array(&ptr, name, &f.x);
|
||||
return f;
|
||||
}
|
||||
|
||||
static inline void set_float3(blender::PointerRNA &ptr, const char *name, const float3 value)
|
||||
{
|
||||
RNA_float_set_array(&ptr, name, &value.x);
|
||||
}
|
||||
|
||||
static inline float4 get_float4(blender::PointerRNA &ptr, const char *name)
|
||||
{
|
||||
float4 f;
|
||||
RNA_float_get_array(&ptr, name, &f.x);
|
||||
return f;
|
||||
}
|
||||
|
||||
static inline void set_float4(blender::PointerRNA &ptr, const char *name, const float4 value)
|
||||
{
|
||||
RNA_float_set_array(&ptr, name, &value.x);
|
||||
}
|
||||
|
||||
static inline bool get_boolean(blender::PointerRNA &ptr, const char *name)
|
||||
{
|
||||
return RNA_boolean_get(&ptr, name) ? true : false;
|
||||
}
|
||||
|
||||
static inline void set_boolean(blender::PointerRNA &ptr, const char *name, bool value)
|
||||
{
|
||||
RNA_boolean_set(&ptr, name, (int)value);
|
||||
}
|
||||
|
||||
static inline float get_float(blender::PointerRNA &ptr, const char *name)
|
||||
{
|
||||
return RNA_float_get(&ptr, name);
|
||||
}
|
||||
|
||||
static inline void set_float(blender::PointerRNA &ptr, const char *name, const float value)
|
||||
{
|
||||
RNA_float_set(&ptr, name, value);
|
||||
}
|
||||
|
||||
static inline int get_int(blender::PointerRNA &ptr, const char *name)
|
||||
{
|
||||
return RNA_int_get(&ptr, name);
|
||||
}
|
||||
|
||||
static inline void set_int(blender::PointerRNA &ptr, const char *name, const int value)
|
||||
{
|
||||
RNA_int_set(&ptr, name, value);
|
||||
}
|
||||
|
||||
/* Get a RNA enum value with sanity check: if the RNA value is above num_values
|
||||
* the function will return a fallback default value.
|
||||
*
|
||||
* NOTE: This function assumes that RNA enum values are a continuous sequence
|
||||
* from 0 to num_values-1. Be careful to use it with enums where some values are
|
||||
* deprecated!
|
||||
*/
|
||||
static inline int get_enum(blender::PointerRNA &ptr,
|
||||
const char *name,
|
||||
int num_values = -1,
|
||||
int default_value = -1)
|
||||
{
|
||||
int value = RNA_enum_get(&ptr, name);
|
||||
if (num_values != -1 && value >= num_values) {
|
||||
assert(default_value != -1);
|
||||
value = default_value;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
static inline string get_enum_identifier(blender::PointerRNA &ptr, const char *name)
|
||||
{
|
||||
blender::PropertyRNA *prop = RNA_struct_find_property(&ptr, name);
|
||||
const char *identifier = "";
|
||||
const int value = RNA_property_enum_get(&ptr, prop);
|
||||
|
||||
RNA_property_enum_identifier(nullptr, &ptr, prop, value, &identifier);
|
||||
|
||||
return string(identifier);
|
||||
}
|
||||
|
||||
static inline void set_enum(blender::PointerRNA &ptr, const char *name, const int value)
|
||||
{
|
||||
RNA_enum_set(&ptr, name, value);
|
||||
}
|
||||
|
||||
static inline void set_enum(blender::PointerRNA &ptr, const char *name, const string &identifier)
|
||||
{
|
||||
RNA_enum_set_identifier(nullptr, &ptr, name, identifier.c_str());
|
||||
}
|
||||
|
||||
static inline string get_string(blender::PointerRNA &ptr, const char *name)
|
||||
{
|
||||
return RNA_string_get(&ptr, name);
|
||||
}
|
||||
|
||||
static inline void set_string(blender::PointerRNA &ptr, const char *name, const string &value)
|
||||
{
|
||||
RNA_string_set(&ptr, name, value.c_str());
|
||||
}
|
||||
|
||||
/* Relative Paths */
|
||||
|
||||
static inline string blender_absolute_path(blender::Main &b_data,
|
||||
blender::ID *b_id,
|
||||
const string &path)
|
||||
{
|
||||
if (path.size() >= 2 && path[0] == '/' && path[1] == '/') {
|
||||
string dirname;
|
||||
|
||||
if (b_id && b_id->lib) {
|
||||
dirname = blender_absolute_path(b_data, &b_id->lib->id, b_id->lib->filepath);
|
||||
}
|
||||
else {
|
||||
dirname = b_data.filepath;
|
||||
}
|
||||
|
||||
return path_join(path_dirname(dirname), path.substr(2));
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
static inline string get_text_datablock_content(const blender::ID *id)
|
||||
{
|
||||
if (id == nullptr) {
|
||||
return "";
|
||||
}
|
||||
if (GS(id->name) != blender::ID_TXT) {
|
||||
return "";
|
||||
}
|
||||
const auto &text = *blender::id_cast<const blender::Text *>(id);
|
||||
|
||||
string content;
|
||||
for (blender::TextLine &line : text.lines) {
|
||||
content += line.line ? line.line : "";
|
||||
content += "\n";
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
/* Texture Space */
|
||||
|
||||
static inline void mesh_texture_space(const blender::Mesh &b_mesh, float3 &loc, float3 &size)
|
||||
{
|
||||
float texspace_location[3];
|
||||
float texspace_size[3];
|
||||
BKE_mesh_texspace_get(const_cast<blender::Mesh *>(&b_mesh), texspace_location, texspace_size);
|
||||
|
||||
loc = make_float3(texspace_location[0], texspace_location[1], texspace_location[2]);
|
||||
size = make_float3(texspace_size[0], texspace_size[1], texspace_size[2]);
|
||||
|
||||
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 motion steps, returns 0 if no motion blur needed. */
|
||||
static inline uint object_motion_steps(blender::Object &b_parent,
|
||||
blender::Object &b_ob,
|
||||
const int max_steps = INT_MAX)
|
||||
{
|
||||
/* Get motion enabled and steps from object itself. */
|
||||
blender::PointerRNA object_rna_ptr = RNA_id_pointer_create(&b_ob.id);
|
||||
blender::PointerRNA cobject = RNA_pointer_get(&object_rna_ptr, "cycles");
|
||||
bool use_motion = get_boolean(cobject, "use_motion_blur");
|
||||
if (!use_motion) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int steps = max(1, get_int(cobject, "motion_steps"));
|
||||
|
||||
/* Also check parent object, so motion blur and steps can be
|
||||
* controlled by dupli-group duplicator for linked groups. */
|
||||
if (&b_parent != &b_ob) {
|
||||
blender::PointerRNA parent_rna_ptr = RNA_id_pointer_create(&b_parent.id);
|
||||
blender::PointerRNA parent_cobject = RNA_pointer_get(&parent_rna_ptr, "cycles");
|
||||
use_motion &= get_boolean(parent_cobject, "use_motion_blur");
|
||||
|
||||
if (!use_motion) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
steps = max(steps, get_int(parent_cobject, "motion_steps"));
|
||||
}
|
||||
|
||||
/* Use uneven number of steps so we get one keyframe at the current frame,
|
||||
* and use 2^(steps - 1) so objects with more/fewer steps still have samples
|
||||
* at the same times, to avoid sampling at many different times. */
|
||||
return min((2 << (steps - 1)) + 1, max_steps);
|
||||
}
|
||||
|
||||
/* object uses deformation motion blur */
|
||||
static inline bool object_use_deform_motion(blender::Object &b_parent, blender::Object &b_ob)
|
||||
{
|
||||
blender::PointerRNA b_ob_rna_ptr = RNA_id_pointer_create(&b_ob.id);
|
||||
blender::PointerRNA cobject = RNA_pointer_get(&b_ob_rna_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 dupli-group
|
||||
* duplicator much easier. */
|
||||
if (use_deform_motion && &b_parent != &b_ob) {
|
||||
blender::PointerRNA b_parent_rna_ptr = RNA_id_pointer_create(&b_parent.id);
|
||||
blender::PointerRNA parent_cobject = RNA_pointer_get(&b_parent_rna_ptr, "cycles");
|
||||
use_deform_motion &= get_boolean(parent_cobject, "use_deform_motion");
|
||||
}
|
||||
return use_deform_motion;
|
||||
}
|
||||
|
||||
static inline blender::FluidDomainSettings *object_fluid_gas_domain_find(blender::Object &b_ob)
|
||||
{
|
||||
for (blender::ModifierData &b_mod : b_ob.modifiers) {
|
||||
if (b_mod.type == blender::eModifierType_Fluid) {
|
||||
auto *b_mmd = reinterpret_cast<blender::FluidModifierData *>(&b_mod);
|
||||
|
||||
if (b_mmd->type == blender::MOD_FLUID_TYPE_DOMAIN &&
|
||||
b_mmd->domain->type == blender::FLUID_DOMAIN_TYPE_GAS)
|
||||
{
|
||||
return b_mmd->domain;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static blender::SubsurfModifierData *object_subdivision_modifier(blender::Object &b_ob,
|
||||
const bool preview)
|
||||
{
|
||||
blender::ModifierData *md = static_cast<blender::ModifierData *>(b_ob.modifiers.last);
|
||||
if (!md) {
|
||||
return nullptr;
|
||||
}
|
||||
if (md->type != blender::eModifierType_Subsurf) {
|
||||
return nullptr;
|
||||
}
|
||||
const blender::ModifierMode enabled_mode = preview ? blender::eModifierMode_Realtime :
|
||||
blender::eModifierMode_Render;
|
||||
if ((md->mode & enabled_mode) == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
blender::SubsurfModifierData *subsurf = reinterpret_cast<blender::SubsurfModifierData *>(md);
|
||||
if ((subsurf->flags & blender::eSubsurfModifierFlag_UseAdaptiveSubdivision) == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
return subsurf;
|
||||
}
|
||||
|
||||
static inline Mesh::SubdivisionType object_subdivision_type(blender::Object &b_ob,
|
||||
const bool preview,
|
||||
const bool use_adaptive_subdivision)
|
||||
{
|
||||
if (!use_adaptive_subdivision) {
|
||||
return Mesh::SUBDIVISION_NONE;
|
||||
}
|
||||
|
||||
blender::SubsurfModifierData *subsurf = object_subdivision_modifier(b_ob, preview);
|
||||
|
||||
if (subsurf) {
|
||||
if (subsurf->subdivType == blender::SUBSURF_TYPE_CATMULL_CLARK) {
|
||||
return Mesh::SUBDIVISION_CATMULL_CLARK;
|
||||
}
|
||||
return Mesh::SUBDIVISION_LINEAR;
|
||||
}
|
||||
|
||||
return Mesh::SUBDIVISION_NONE;
|
||||
}
|
||||
|
||||
static inline void object_subdivision_to_mesh(blender::Object &b_ob,
|
||||
Mesh &mesh,
|
||||
const bool preview,
|
||||
const bool use_adaptive_subdivision)
|
||||
{
|
||||
if (!use_adaptive_subdivision) {
|
||||
mesh.set_subdivision_type(Mesh::SUBDIVISION_NONE);
|
||||
return;
|
||||
}
|
||||
|
||||
blender::SubsurfModifierData *subsurf = object_subdivision_modifier(b_ob, preview);
|
||||
|
||||
if (!subsurf) {
|
||||
mesh.set_subdivision_type(Mesh::SUBDIVISION_NONE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (subsurf->subdivType != blender::SUBSURF_TYPE_CATMULL_CLARK) {
|
||||
mesh.set_subdivision_type(Mesh::SUBDIVISION_LINEAR);
|
||||
return;
|
||||
}
|
||||
|
||||
mesh.set_subdivision_type(Mesh::SUBDIVISION_CATMULL_CLARK);
|
||||
|
||||
switch (subsurf->boundary_smooth) {
|
||||
case blender::SUBSURF_BOUNDARY_SMOOTH_PRESERVE_CORNERS:
|
||||
mesh.set_subdivision_boundary_interpolation(Mesh::SUBDIVISION_BOUNDARY_EDGE_AND_CORNER);
|
||||
break;
|
||||
case blender::SUBSURF_BOUNDARY_SMOOTH_ALL:
|
||||
mesh.set_subdivision_boundary_interpolation(Mesh::SUBDIVISION_BOUNDARY_EDGE_ONLY);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (subsurf->uv_smooth) {
|
||||
case blender::SUBSURF_UV_SMOOTH_NONE:
|
||||
mesh.set_subdivision_fvar_interpolation(Mesh::SUBDIVISION_FVAR_LINEAR_ALL);
|
||||
break;
|
||||
case blender::SUBSURF_UV_SMOOTH_PRESERVE_CORNERS:
|
||||
mesh.set_subdivision_fvar_interpolation(Mesh::SUBDIVISION_FVAR_LINEAR_CORNERS_ONLY);
|
||||
break;
|
||||
case blender::SUBSURF_UV_SMOOTH_PRESERVE_CORNERS_AND_JUNCTIONS:
|
||||
mesh.set_subdivision_fvar_interpolation(Mesh::SUBDIVISION_FVAR_LINEAR_CORNERS_PLUS1);
|
||||
break;
|
||||
case blender::SUBSURF_UV_SMOOTH_PRESERVE_CORNERS_JUNCTIONS_AND_CONCAVE:
|
||||
mesh.set_subdivision_fvar_interpolation(Mesh::SUBDIVISION_FVAR_LINEAR_CORNERS_PLUS2);
|
||||
break;
|
||||
case blender::SUBSURF_UV_SMOOTH_PRESERVE_BOUNDARIES:
|
||||
mesh.set_subdivision_fvar_interpolation(Mesh::SUBDIVISION_FVAR_LINEAR_BOUNDARIES);
|
||||
break;
|
||||
case blender::SUBSURF_UV_SMOOTH_ALL:
|
||||
mesh.set_subdivision_fvar_interpolation(Mesh::SUBDIVISION_FVAR_LINEAR_NONE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline PathRayVisibility object_ray_visibility(blender::Object &b_ob)
|
||||
{
|
||||
PathRayVisibility visibility = PATH_RAY_VISIBILITY_NONE;
|
||||
|
||||
visibility |= ((b_ob.visibility_flag & blender::OB_HIDE_CAMERA) == 0) ?
|
||||
PATH_RAY_VISIBILITY_CAMERA :
|
||||
PATH_RAY_VISIBILITY_NONE;
|
||||
visibility |= ((b_ob.visibility_flag & blender::OB_HIDE_DIFFUSE) == 0) ?
|
||||
PATH_RAY_VISIBILITY_DIFFUSE :
|
||||
PATH_RAY_VISIBILITY_NONE;
|
||||
visibility |= ((b_ob.visibility_flag & blender::OB_HIDE_GLOSSY) == 0) ?
|
||||
PATH_RAY_VISIBILITY_GLOSSY :
|
||||
PATH_RAY_VISIBILITY_NONE;
|
||||
visibility |= ((b_ob.visibility_flag & blender::OB_HIDE_TRANSMISSION) == 0) ?
|
||||
PATH_RAY_VISIBILITY_TRANSMIT :
|
||||
PATH_RAY_VISIBILITY_NONE;
|
||||
visibility |= ((b_ob.visibility_flag & blender::OB_HIDE_SHADOW) == 0) ?
|
||||
PATH_RAY_VISIBILITY_SHADOW :
|
||||
PATH_RAY_VISIBILITY_NONE;
|
||||
visibility |= ((b_ob.visibility_flag & blender::OB_HIDE_VOLUME_SCATTER) == 0) ?
|
||||
PATH_RAY_VISIBILITY_VOLUME_SCATTER :
|
||||
PATH_RAY_VISIBILITY_NONE;
|
||||
|
||||
return visibility;
|
||||
}
|
||||
|
||||
/* Check whether some of "built-in" motion-related attributes are needed to be exported (includes
|
||||
* things like velocity from cache modifier, fluid simulation).
|
||||
*
|
||||
* NOTE: This code is run prior to object motion blur initialization. so can not access properties
|
||||
* set by `sync_object_motion_init()`. */
|
||||
static inline bool object_need_motion_attribute(BObjectInfo &b_ob_info, Scene *scene)
|
||||
{
|
||||
const Scene::MotionType need_motion = scene->need_motion();
|
||||
if (need_motion == Scene::MOTION_NONE) {
|
||||
/* Simple case: neither motion pass nor motion blur is needed, no need in the motion related
|
||||
* attributes. */
|
||||
return false;
|
||||
}
|
||||
|
||||
if (need_motion == Scene::MOTION_BLUR) {
|
||||
/* A bit tricky and implicit case:
|
||||
* - Motion blur is enabled in the scene, which implies specific number of time steps for
|
||||
* objects.
|
||||
* - If the object has motion blur disabled on it, it will have 0 time steps.
|
||||
* - Motion attribute expects non-zero time steps.
|
||||
*
|
||||
* Avoid adding motion attributes if the motion blur will enforce 0 motion steps. */
|
||||
blender::PointerRNA b_ob_rna_ptr = RNA_id_pointer_create(&b_ob_info.real_object->id);
|
||||
blender::PointerRNA cobject = RNA_pointer_get(&b_ob_rna_ptr, "cycles");
|
||||
const bool use_motion = get_boolean(cobject, "use_motion_blur");
|
||||
if (!use_motion) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Motion pass which implies 3 motion steps, or motion blur which is not disabled on object
|
||||
* level. */
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool region_view3d_navigating_or_transforming(const blender::RegionView3D *b_rv3d)
|
||||
{
|
||||
return b_rv3d && ((b_rv3d->rflag & (blender::RV3D_NAVIGATING | blender::RV3D_PAINTING)) ||
|
||||
(blender::G.moving & (blender::G_TRANSFORM_OBJ | blender::G_TRANSFORM_EDIT)));
|
||||
}
|
||||
|
||||
class EdgeMap {
|
||||
public:
|
||||
EdgeMap() = default;
|
||||
|
||||
void clear()
|
||||
{
|
||||
edges_.clear();
|
||||
}
|
||||
|
||||
void insert(int v0, int v1)
|
||||
{
|
||||
get_sorted_verts(v0, v1);
|
||||
edges_.insert(std::pair<int, int>(v0, v1));
|
||||
}
|
||||
|
||||
bool exists(int v0, int v1)
|
||||
{
|
||||
get_sorted_verts(v0, v1);
|
||||
return edges_.contains(std::pair<int, int>(v0, v1));
|
||||
}
|
||||
|
||||
protected:
|
||||
void get_sorted_verts(int &v0, int &v1)
|
||||
{
|
||||
if (v0 > v1) {
|
||||
swap(v0, v1);
|
||||
}
|
||||
}
|
||||
|
||||
set<std::pair<int, int>> edges_;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
Reference in New Issue
Block a user