2023-06-14 16:52:36 +10:00
|
|
|
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
|
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: Apache-2.0 */
|
2011-04-27 11:58:34 +00:00
|
|
|
|
2024-12-26 17:53:56 +01:00
|
|
|
#pragma once
|
2011-04-27 11:58:34 +00:00
|
|
|
|
2021-10-24 14:19:19 +02:00
|
|
|
#include "scene/mesh.h"
|
2022-01-04 01:58:34 +01:00
|
|
|
#include "scene/scene.h"
|
2016-09-18 12:04:12 -04:00
|
|
|
|
2021-10-24 14:19:19 +02:00
|
|
|
#include "util/algorithm.h"
|
|
|
|
|
#include "util/array.h"
|
|
|
|
|
#include "util/path.h"
|
|
|
|
|
#include "util/set.h"
|
|
|
|
|
#include "util/transform.h"
|
|
|
|
|
#include "util/types.h"
|
2024-12-26 17:53:59 +01:00
|
|
|
|
|
|
|
|
#include "RNA_blender_cpp.hh"
|
|
|
|
|
|
|
|
|
|
#include "DNA_mesh_types.h"
|
2011-04-27 11:58:34 +00:00
|
|
|
|
2024-11-12 15:21:59 +01:00
|
|
|
#include "BKE_image.hh"
|
2024-12-26 17:53:59 +01:00
|
|
|
#include "BKE_mesh.h"
|
|
|
|
|
#include "BKE_mesh_types.hh"
|
2023-09-18 02:50:09 +02:00
|
|
|
|
2011-04-27 11:58:34 +00:00
|
|
|
CCL_NAMESPACE_BEGIN
|
|
|
|
|
|
2021-09-06 18:22:24 +02:00
|
|
|
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. */
|
|
|
|
|
BL::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. */
|
|
|
|
|
BL::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 real_object.data(). */
|
|
|
|
|
BL::ID object_data;
|
|
|
|
|
|
|
|
|
|
/* 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 const_cast<BL::Object &>(real_object).data() == object_data;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2024-12-26 17:53:59 +01:00
|
|
|
using BlenderAttributeType = BL::ShaderNodeAttribute::attribute_type_enum;
|
Materials: add custom object properties as uniform attributes.
This patch allows the user to type a property name into the
Attribute node, which will then output the value of the property
for each individual object, allowing to e.g. customize shaders
by object without duplicating the shader.
In order to make supporting this easier for Eevee, it is necessary
to explicitly choose whether the attribute is varying or uniform
via a dropdown option of the Attribute node. The dropdown also
allows choosing whether instancing should be taken into account.
The Cycles design treats all attributes as one common namespace,
so the Blender interface converts the enum to a name prefix that
can't be entered using keyboard.
In Eevee, the attributes are provided to the shader via a UBO indexed
with resource_id, similar to the existing Object Info data. Unlike it,
however, it is necessary to maintain a separate buffer for every
requested combination of attributes.
This is done using a hash table with the attribute set as the key,
as it is expected that technically different but similar materials
may use the same set of attributes. In addition, in order to minimize
wasted memory, a sparse UBO pool is implemented, so that chunks that
don't contain any data don't have to be allocated.
The back-end Cycles code is already refactored and committed by Brecht.
Differential Revision: https://developer.blender.org/D2057
2020-08-05 19:14:40 +03:00
|
|
|
BlenderAttributeType blender_attribute_name_split_type(ustring name, string *r_real_name);
|
|
|
|
|
|
2014-02-12 23:13:45 +01:00
|
|
|
void python_thread_state_save(void **python_thread_state);
|
|
|
|
|
void python_thread_state_restore(void **python_thread_state);
|
2014-02-12 21:49:34 +01:00
|
|
|
|
2024-04-26 20:02:13 +02:00
|
|
|
static bool mesh_use_corner_normals(BL::Mesh &mesh, Mesh::SubdivisionType subdivision_type)
|
|
|
|
|
{
|
|
|
|
|
return mesh && (subdivision_type == Mesh::SUBDIVISION_NONE) &&
|
|
|
|
|
(static_cast<const ::Mesh *>(mesh.ptr.data)->normals_domain(true) ==
|
|
|
|
|
blender::bke::MeshNormalDomain::Corner);
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-16 13:49:21 +02:00
|
|
|
static inline BL::Mesh object_to_mesh(BL::BlendData & /*data*/,
|
2021-09-06 18:22:24 +02:00
|
|
|
BObjectInfo &b_ob_info,
|
Dependency graph API changes
Main goal here is to make it obvious and predictable about
what is going on.
Summary of changes.
- Access to dependency graph is now only possible to a fully evaluated
graph. This is now done via context.evaluated_depsgraph_get().
The call will ensure both relations and datablocks are updated.
This way we don't allow access to some known bad state of the graph,
and also making explicit that getting update dependency graph is not
cheap.
- Access to evaluated ID is now possible via id.evaluated_get().
It was already possible to get evaluated ID via dependency graph,
but that was a bit confusing why access to original is done via ID
and to evaluated via depsgraph.
If datablock is not covered by dependency graph it will be returned
as-is.
- Similarly, request for original from an ID which is not evaluated
will return ID as-is.
- Removed scene.update().
This is very expensive to update all the view layers.
- Added depsgraph.update().
Now when temporary changes to objects are to be done, this is to
happen on original object and then dependency graph is to be
updated.
- Changed object.to_mesh() to behave the following way:
* When is used for original object modifiers are ignored.
For meshes this acts similar to mesh-copy, not very useful but
allows to keep code paths similar (i.e. for exporter which has
Apply Modifiers option it's only matter choosing between original
and evaluated object, the to_mesh() part can stay the same).
For curves this gives a mesh which is constructed from displist
without taking own modifiers and modifiers of bevel/taper objects
into account.
For metaballs this gives empty mesh.
Polygonization of metaball is not possible from a single object.
* When is used for evaluated object modifiers are always applied.
In fact, no evaluation is happening, the mesh is either copied
as-is, or constructed from current state of curve cache.
Arguments to apply modifiers and calculate original coordinates (ORCO,
aka undeformed coordinates) are removed. The ORCO is to be calculated
as part of dependency graph evaluation.
File used to regression-test (a packed Python script into .blend):
{F7033464}
Patch to make addons tests to pass:
{F7033466}
NOTE: I've included changes to FBX exporter, and those are addressing
report T63689.
NOTE: All the enabled-by-default addons are to be ported still, but
first want to have agreement on this part of changes.
NOTE: Also need to work on documentation for Python API, but, again,
better be done after having agreement on this work.
Reviewers: brecht, campbellbarton, mont29
Differential Revision: https://developer.blender.org/D4834
2019-05-09 11:26:49 +02:00
|
|
|
BL::Depsgraph & /*depsgraph*/,
|
|
|
|
|
bool /*calc_undeformed*/,
|
2017-01-11 16:23:54 +01:00
|
|
|
Mesh::SubdivisionType subdivision_type)
|
2011-04-27 11:58:34 +00:00
|
|
|
{
|
2024-02-19 15:54:08 +01:00
|
|
|
/* TODO: make this work with copy-on-evaluation, modifiers are already evaluated. */
|
2019-01-15 13:03:09 +01:00
|
|
|
#if 0
|
2017-07-20 23:51:15 +02:00
|
|
|
bool subsurf_mod_show_render = false;
|
|
|
|
|
bool subsurf_mod_show_viewport = false;
|
2016-07-16 19:56:45 -04:00
|
|
|
|
2019-04-17 08:16:53 +02:00
|
|
|
if (subdivision_type != Mesh::SUBDIVISION_NONE) {
|
|
|
|
|
BL::Modifier subsurf_mod = object.modifiers[object.modifiers.length() - 1];
|
2016-07-16 19:56:45 -04:00
|
|
|
|
|
|
|
|
subsurf_mod_show_render = subsurf_mod.show_render();
|
2016-09-18 22:11:07 -04:00
|
|
|
subsurf_mod_show_viewport = subsurf_mod.show_viewport();
|
2016-07-16 19:56:45 -04:00
|
|
|
|
|
|
|
|
subsurf_mod.show_render(false);
|
|
|
|
|
subsurf_mod.show_viewport(false);
|
|
|
|
|
}
|
2019-01-15 13:03:09 +01:00
|
|
|
#endif
|
2016-07-16 19:56:45 -04:00
|
|
|
|
2021-09-27 18:52:09 +02:00
|
|
|
BL::Mesh mesh = (b_ob_info.object_data.is_a(&RNA_Mesh)) ? BL::Mesh(b_ob_info.object_data) :
|
|
|
|
|
BL::Mesh(PointerRNA_NULL);
|
2024-04-26 20:02:13 +02:00
|
|
|
|
|
|
|
|
bool use_corner_normals = false;
|
2021-09-27 18:52:09 +02:00
|
|
|
|
|
|
|
|
if (b_ob_info.is_real_object_data()) {
|
|
|
|
|
if (mesh) {
|
2024-04-26 20:02:13 +02:00
|
|
|
if (mesh.is_editmode()) {
|
2024-04-27 11:57:36 +10:00
|
|
|
/* Flush edit-mesh to mesh, including all data layers. */
|
2024-04-26 20:02:13 +02:00
|
|
|
BL::Depsgraph depsgraph(PointerRNA_NULL);
|
|
|
|
|
mesh = b_ob_info.real_object.to_mesh(false, depsgraph);
|
|
|
|
|
use_corner_normals = mesh_use_corner_normals(mesh, subdivision_type);
|
|
|
|
|
}
|
|
|
|
|
else if (mesh_use_corner_normals(mesh, subdivision_type)) {
|
|
|
|
|
/* Make a copy to split faces. */
|
2021-09-27 18:52:09 +02:00
|
|
|
BL::Depsgraph depsgraph(PointerRNA_NULL);
|
|
|
|
|
mesh = b_ob_info.real_object.to_mesh(false, depsgraph);
|
2024-04-26 20:02:13 +02:00
|
|
|
use_corner_normals = true;
|
2021-09-27 18:52:09 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
2019-05-24 14:37:47 +02:00
|
|
|
BL::Depsgraph depsgraph(PointerRNA_NULL);
|
2021-09-06 18:22:24 +02:00
|
|
|
mesh = b_ob_info.real_object.to_mesh(false, depsgraph);
|
2024-04-26 20:02:13 +02:00
|
|
|
use_corner_normals = mesh_use_corner_normals(mesh, subdivision_type);
|
2019-01-15 13:03:09 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
2021-09-27 18:52:09 +02:00
|
|
|
/* TODO: what to do about non-mesh geometry instances? */
|
2024-04-26 20:02:13 +02:00
|
|
|
use_corner_normals = mesh_use_corner_normals(mesh, subdivision_type);
|
2019-01-15 13:03:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if 0
|
2019-04-17 08:16:53 +02:00
|
|
|
if (subdivision_type != Mesh::SUBDIVISION_NONE) {
|
|
|
|
|
BL::Modifier subsurf_mod = object.modifiers[object.modifiers.length() - 1];
|
2016-07-16 19:56:45 -04:00
|
|
|
|
|
|
|
|
subsurf_mod.show_render(subsurf_mod_show_render);
|
|
|
|
|
subsurf_mod.show_viewport(subsurf_mod_show_viewport);
|
|
|
|
|
}
|
2019-01-15 13:03:09 +01:00
|
|
|
#endif
|
2016-07-16 19:56:45 -04:00
|
|
|
|
2024-01-19 17:22:14 +01:00
|
|
|
if (mesh) {
|
2024-04-26 20:02:13 +02:00
|
|
|
if (use_corner_normals) {
|
2023-05-23 17:35:33 -04:00
|
|
|
mesh.split_faces();
|
2016-07-16 19:42:28 -04:00
|
|
|
}
|
2019-01-15 13:03:09 +01:00
|
|
|
|
2024-01-19 17:22:14 +01:00
|
|
|
if (subdivision_type == Mesh::SUBDIVISION_NONE) {
|
|
|
|
|
mesh.calc_loop_triangles();
|
|
|
|
|
}
|
2019-01-15 13:03:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return mesh;
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-16 13:49:21 +02:00
|
|
|
static inline void free_object_to_mesh(BL::BlendData & /*data*/,
|
2021-09-06 18:22:24 +02:00
|
|
|
BObjectInfo &b_ob_info,
|
2019-05-16 13:49:21 +02:00
|
|
|
BL::Mesh &mesh)
|
2019-01-15 13:03:09 +01:00
|
|
|
{
|
2021-09-06 18:22:24 +02:00
|
|
|
if (!b_ob_info.is_real_object_data()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-01-15 13:03:09 +01:00
|
|
|
/* Free mesh if we didn't just use the existing one. */
|
2021-09-06 18:22:24 +02:00
|
|
|
BL::Object object = b_ob_info.real_object;
|
2019-01-15 13:03:09 +01:00
|
|
|
if (object.data().ptr.data != mesh.ptr.data) {
|
2019-05-16 13:49:21 +02:00
|
|
|
object.to_mesh_clear();
|
2014-04-13 12:51:06 +02:00
|
|
|
}
|
2011-04-27 11:58:34 +00:00
|
|
|
}
|
|
|
|
|
|
2016-01-30 14:18:29 +01:00
|
|
|
static inline void colorramp_to_array(BL::ColorRamp &ramp,
|
2016-05-08 01:54:35 +02:00
|
|
|
array<float3> &ramp_color,
|
|
|
|
|
array<float> &ramp_alpha,
|
2025-01-01 18:15:54 +01:00
|
|
|
const int size)
|
2012-03-26 12:45:14 +00:00
|
|
|
{
|
2023-08-31 05:08:04 +02:00
|
|
|
const int full_size = size + 1;
|
|
|
|
|
ramp_color.resize(full_size);
|
|
|
|
|
ramp_alpha.resize(full_size);
|
2016-05-08 01:54:35 +02:00
|
|
|
|
2023-08-31 05:08:04 +02:00
|
|
|
for (int i = 0; i < full_size; i++) {
|
2012-03-26 12:45:14 +00:00
|
|
|
float color[4];
|
|
|
|
|
|
2023-08-31 05:08:04 +02:00
|
|
|
ramp.evaluate(float(i) / float(size), color);
|
2016-05-08 01:54:35 +02:00
|
|
|
ramp_color[i] = make_float3(color[0], color[1], color[2]);
|
|
|
|
|
ramp_alpha[i] = color[3];
|
2012-03-26 12:45:14 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-04 20:17:25 +05:00
|
|
|
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,
|
2025-01-01 18:15:54 +01:00
|
|
|
const int num_curves,
|
2015-12-04 20:17:25 +05:00
|
|
|
float *min_x,
|
|
|
|
|
float *max_x)
|
|
|
|
|
{
|
2021-08-12 14:34:41 +10:00
|
|
|
// const int num_curves = cumap.curves.length(); /* Gives linking error so far. */
|
2015-12-04 20:17:25 +05:00
|
|
|
*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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-01 18:15:54 +01:00
|
|
|
static inline void curvemapping_to_array(BL::CurveMapping &cumap,
|
|
|
|
|
array<float> &data,
|
|
|
|
|
const int size)
|
2015-10-27 19:00:51 +05:00
|
|
|
{
|
|
|
|
|
cumap.update();
|
|
|
|
|
BL::CurveMap curve = cumap.curves[0];
|
2023-08-31 05:08:04 +02:00
|
|
|
const int full_size = size + 1;
|
|
|
|
|
data.resize(full_size);
|
|
|
|
|
for (int i = 0; i < full_size; i++) {
|
2024-12-29 17:32:00 +01:00
|
|
|
const float t = float(i) / float(size);
|
2019-11-01 12:09:55 +01:00
|
|
|
data[i] = cumap.evaluate(curve, t);
|
2015-10-27 19:00:51 +05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-30 19:05:08 +01:00
|
|
|
static inline void curvemapping_float_to_array(BL::CurveMapping &cumap,
|
|
|
|
|
array<float> &data,
|
2025-01-01 18:15:54 +01:00
|
|
|
const int size)
|
2021-09-30 19:05:08 +01:00
|
|
|
{
|
2024-12-29 17:32:00 +01:00
|
|
|
float min = 0.0f;
|
|
|
|
|
float max = 1.0f;
|
2021-09-30 19:05:08 +01:00
|
|
|
|
|
|
|
|
curvemapping_minmax(cumap, 1, &min, &max);
|
|
|
|
|
|
|
|
|
|
const float range = max - min;
|
|
|
|
|
|
|
|
|
|
cumap.update();
|
|
|
|
|
|
|
|
|
|
BL::CurveMap map = cumap.curves[0];
|
|
|
|
|
|
2023-08-31 05:08:04 +02:00
|
|
|
const int full_size = size + 1;
|
|
|
|
|
data.resize(full_size);
|
2021-09-30 19:05:08 +01:00
|
|
|
|
2023-08-31 05:08:04 +02:00
|
|
|
for (int i = 0; i < full_size; i++) {
|
2024-12-29 17:32:00 +01:00
|
|
|
const float t = min + float(i) / float(size) * range;
|
2021-09-30 19:05:08 +01:00
|
|
|
data[i] = cumap.evaluate(map, t);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-30 14:18:29 +01:00
|
|
|
static inline void curvemapping_color_to_array(BL::CurveMapping &cumap,
|
2016-05-08 01:54:35 +02:00
|
|
|
array<float3> &data,
|
2025-01-01 18:15:54 +01:00
|
|
|
const int size,
|
2015-12-04 20:17:25 +05:00
|
|
|
bool rgb_curve)
|
2012-12-11 14:39:37 +00:00
|
|
|
{
|
2024-12-29 17:32:00 +01:00
|
|
|
float min_x = 0.0f;
|
|
|
|
|
float max_x = 1.0f;
|
2015-12-15 16:23:33 +05:00
|
|
|
|
|
|
|
|
/* 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.
|
|
|
|
|
*/
|
2021-09-30 19:05:08 +01:00
|
|
|
const int num_curves = rgb_curve ? 4 : 3;
|
|
|
|
|
curvemapping_minmax(cumap, num_curves, &min_x, &max_x);
|
2015-12-15 16:23:33 +05:00
|
|
|
|
2015-12-04 20:17:25 +05:00
|
|
|
const float range_x = max_x - min_x;
|
|
|
|
|
|
2012-12-11 14:39:37 +00:00
|
|
|
cumap.update();
|
|
|
|
|
|
|
|
|
|
BL::CurveMap mapR = cumap.curves[0];
|
|
|
|
|
BL::CurveMap mapG = cumap.curves[1];
|
|
|
|
|
BL::CurveMap mapB = cumap.curves[2];
|
|
|
|
|
|
2023-08-31 05:08:04 +02:00
|
|
|
const int full_size = size + 1;
|
|
|
|
|
data.resize(full_size);
|
2016-05-08 01:54:35 +02:00
|
|
|
|
2012-12-11 14:39:37 +00:00
|
|
|
if (rgb_curve) {
|
|
|
|
|
BL::CurveMap mapI = cumap.curves[3];
|
2023-08-31 05:08:04 +02:00
|
|
|
for (int i = 0; i < full_size; i++) {
|
|
|
|
|
const float t = min_x + float(i) / float(size) * range_x;
|
2019-11-01 12:09:55 +01:00
|
|
|
data[i] = make_float3(cumap.evaluate(mapR, cumap.evaluate(mapI, t)),
|
|
|
|
|
cumap.evaluate(mapG, cumap.evaluate(mapI, t)),
|
|
|
|
|
cumap.evaluate(mapB, cumap.evaluate(mapI, t)));
|
2012-12-11 14:39:37 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2012-12-11 14:39:37 +00:00
|
|
|
else {
|
2023-08-31 05:08:04 +02:00
|
|
|
for (int i = 0; i < full_size; i++) {
|
2024-12-29 17:32:00 +01:00
|
|
|
const float t = min_x + float(i) / float(size) * range_x;
|
2019-11-01 12:09:55 +01:00
|
|
|
data[i] = make_float3(
|
|
|
|
|
cumap.evaluate(mapR, t), cumap.evaluate(mapG, t), cumap.evaluate(mapB, t));
|
2012-12-11 14:39:37 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-30 14:18:29 +01:00
|
|
|
static inline bool BKE_object_is_modified(BL::Object &self, BL::Scene &scene, bool preview)
|
2011-04-27 11:58:34 +00:00
|
|
|
{
|
2012-10-22 17:34:16 +00:00
|
|
|
return self.is_modified(scene, (preview) ? (1 << 0) : (1 << 1)) ? true : false;
|
2011-04-27 11:58:34 +00:00
|
|
|
}
|
|
|
|
|
|
2021-09-06 18:22:24 +02:00
|
|
|
static inline bool BKE_object_is_deform_modified(BObjectInfo &self, BL::Scene &scene, bool preview)
|
2012-04-30 12:49:26 +00:00
|
|
|
{
|
2021-09-06 18:22:24 +02:00
|
|
|
if (!self.is_real_object_data()) {
|
2022-05-10 20:38:31 +02:00
|
|
|
/* Comes from geometry nodes, can't use heuristic to guess if it's animated. */
|
|
|
|
|
return true;
|
2021-09-06 18:22:24 +02:00
|
|
|
}
|
2022-05-10 20:38:31 +02:00
|
|
|
|
|
|
|
|
/* Use heuristic to quickly check if object is potentially animated. */
|
2021-09-06 18:22:24 +02:00
|
|
|
return self.real_object.is_deform_modified(scene, (preview) ? (1 << 0) : (1 << 1)) ? true :
|
|
|
|
|
false;
|
2012-04-30 12:49:26 +00:00
|
|
|
}
|
|
|
|
|
|
2016-01-30 14:18:29 +01:00
|
|
|
static inline int render_resolution_x(BL::RenderSettings &b_render)
|
2013-05-16 21:53:21 +00:00
|
|
|
{
|
|
|
|
|
return b_render.resolution_x() * b_render.resolution_percentage() / 100;
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-30 14:18:29 +01:00
|
|
|
static inline int render_resolution_y(BL::RenderSettings &b_render)
|
2013-05-16 21:53:21 +00:00
|
|
|
{
|
|
|
|
|
return b_render.resolution_y() * b_render.resolution_percentage() / 100;
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-08 16:58:34 +02:00
|
|
|
static inline string image_user_file_path(BL::BlendData &data,
|
|
|
|
|
BL::ImageUser &iuser,
|
|
|
|
|
BL::Image &ima,
|
2025-01-01 18:15:54 +01:00
|
|
|
const int cfra)
|
2012-06-04 19:38:33 +00:00
|
|
|
{
|
|
|
|
|
char filepath[1024];
|
Add support for tiled images and the UDIM naming scheme
This patch contains the work that I did during my week at the Code Quest - adding support for tiled images to Blender.
With this patch, images now contain a list of tiles. By default, this just contains one tile, but if the source type is set to Tiled, the user can add additional tiles. When acquiring an ImBuf, the tile to be loaded is specified in the ImageUser.
Therefore, code that is not yet aware of tiles will just access the default tile as usual.
The filenames of the additional tiles are derived from the original filename according to the UDIM naming scheme - the filename contains an index that is calculated as (1001 + 10*<y coordinate of the tile> + <x coordinate of the tile>), where the x coordinate never goes above 9.
Internally, the various tiles are stored in a cache just like sequences. When acquired for the first time, the code will try to load the corresponding file from disk. Alternatively, a new operator can be used to initialize the tile similar to the New Image operator.
The following features are supported so far:
- Automatic detection and loading of all tiles when opening the first tile (1001)
- Saving all tiles
- Adding and removing tiles
- Filling tiles with generated images
- Drawing all tiles in the Image Editor
- Viewing a tiled grid even if no image is selected
- Rendering tiled images in Eevee
- Rendering tiled images in Cycles (in SVM mode)
- Automatically skipping loading of unused tiles in Cycles
- 2D texture painting (also across tiles)
- 3D texture painting (also across tiles, only limitation: individual faces can not cross tile borders)
- Assigning custom labels to individual tiles (drawn in the Image Editor instead of the ID)
- Different resolutions between tiles
There still are some missing features that will be added later (see T72390):
- Workbench engine support
- Packing/Unpacking support
- Baking support
- Cycles OSL support
- many other Blender features that rely on images
Thanks to Brecht for the review and to all who tested the intermediate versions!
Differential Revision: https://developer.blender.org/D3509
2019-12-12 16:06:08 +01:00
|
|
|
iuser.tile(0);
|
2024-11-12 15:21:59 +01:00
|
|
|
BKE_image_user_frame_calc(
|
|
|
|
|
static_cast<Image *>(ima.ptr.data), static_cast<ImageUser *>(iuser.ptr.data), cfra);
|
|
|
|
|
BKE_image_user_file_path_ex(static_cast<Main *>(data.ptr.data),
|
|
|
|
|
static_cast<ImageUser *>(iuser.ptr.data),
|
|
|
|
|
static_cast<Image *>(ima.ptr.data),
|
|
|
|
|
filepath,
|
|
|
|
|
false,
|
|
|
|
|
true);
|
2019-12-26 18:55:36 +01:00
|
|
|
|
UDIM: Support virtual filenames
This implements the design detailed in T92696 to support virtual
filenames for UDIM textures. Currently, the following 2 substitution
tokens are supported:
| Token | Meaning |
| ----- | ---- |
| <UDIM> | 1001 + u-tile + v-tile * 10 |
| <UVTILE> | Equivalent to u<u-tile + 1>_v<v-tile + 1> |
Example for u-tile of 3 and v-tile of 1:
filename.<UDIM>_ver0023.png --> filename.1014_ver0023.png
filename.<UVTILE>_ver0023.png --> filename.u4_v2_ver0023.png
For image loading, the existing workflow is unchanged. A user can select
one or more image files, belonging to one or more UDIM tile sets, and
have Blender load them all as it does today. Now the <UVTILE> format is
"guessed" just as the <UDIM> format was guessed before.
If guessing fails, the user can simply go into the Image Editor and type
the proper substitution in the filename. Once typing is complete,
Blender will reload the files and correctly fill the tiles. This
workflow is new as attempting to fix the guessing in current versions
did not really work, and the user was often stuck with a confusing
situation.
For image saving, the existing workflow is changed slightly. Currently,
when saving, a user has to be sure to type the filename of the first
tile (e.g. filename.1001.png) to save the entire UDIM set. The number
could differ if they start at a different tile etc. This is confusing.
Now, the user should type a filename containing the appropriate
substitution token. By default Blender will fill in a default name using
the <UDIM> token but the user is free to save out images using <UVTILE>
if they wish.
Differential Revision: https://developer.blender.org/D13057
2021-12-30 22:06:23 -08:00
|
|
|
return string(filepath);
|
2012-06-04 19:38:33 +00:00
|
|
|
}
|
|
|
|
|
|
2025-01-01 18:15:54 +01:00
|
|
|
static inline int image_user_frame_number(BL::ImageUser &iuser, BL::Image &ima, const int cfra)
|
Movies support for Cycles
This adds support of movie textures for Cycles rendering.
Uses the same builtin images routines as packed/generated images,
but with some extra non-rna hookups from blender_session side.
Basically, it's not so clear how to give access to video frames
via C++ RNA -- it'll require exposing ImBuf to API, doing some
threading locks and so. Ended up adding two more functions which
are actually bad level call, but don't consider it's so much bad
-- we have few bad calls already, which are actually related.
Changed a bit how builtin images names are passing to image
manager. Now it's not just an ID datablock name, but also a frame
number concatenated via '@' character, which makes itpossible to
easily know frame number to be used for movie images, without
adding extra descriptors to image manager.
Decoding of builtin name is a bit slower now, but it should be
still nothing in comparison with rendering complexity.
Also exposed image user's frame_current to python API, which
is needed to get absolute frame number of movie from node's
image user.
P.S. Generated/packed images are also using bad level call but
only does it to make things more clear here. Either all images
are using C++ RNA here or no images does. That's the most clear
for now.
2013-01-16 17:07:25 +00:00
|
|
|
{
|
2024-11-12 15:21:59 +01:00
|
|
|
BKE_image_user_frame_calc(
|
|
|
|
|
static_cast<Image *>(ima.ptr.data), static_cast<ImageUser *>(iuser.ptr.data), cfra);
|
Movies support for Cycles
This adds support of movie textures for Cycles rendering.
Uses the same builtin images routines as packed/generated images,
but with some extra non-rna hookups from blender_session side.
Basically, it's not so clear how to give access to video frames
via C++ RNA -- it'll require exposing ImBuf to API, doing some
threading locks and so. Ended up adding two more functions which
are actually bad level call, but don't consider it's so much bad
-- we have few bad calls already, which are actually related.
Changed a bit how builtin images names are passing to image
manager. Now it's not just an ID datablock name, but also a frame
number concatenated via '@' character, which makes itpossible to
easily know frame number to be used for movie images, without
adding extra descriptors to image manager.
Decoding of builtin name is a bit slower now, but it should be
still nothing in comparison with rendering complexity.
Also exposed image user's frame_current to python API, which
is needed to get absolute frame number of movie from node's
image user.
P.S. Generated/packed images are also using bad level call but
only does it to make things more clear here. Either all images
are using C++ RNA here or no images does. That's the most clear
for now.
2013-01-16 17:07:25 +00:00
|
|
|
return iuser.frame_current();
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-19 00:40:30 +02:00
|
|
|
static inline bool image_is_builtin(BL::Image &ima, BL::RenderEngine &engine)
|
|
|
|
|
{
|
|
|
|
|
const BL::Image::source_enum image_source = ima.source();
|
|
|
|
|
if (image_source == BL::Image::source_TILED) {
|
|
|
|
|
/* If any tile is marked as generated, then treat the entire Image as built-in. */
|
|
|
|
|
for (BL::UDIMTile &tile : ima.tiles) {
|
|
|
|
|
if (tile.is_generated_tile()) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ima.packed_file() || image_source == BL::Image::source_GENERATED ||
|
|
|
|
|
image_source == BL::Image::source_MOVIE ||
|
|
|
|
|
(engine.is_preview() && image_source != BL::Image::source_SEQUENCE);
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-07 04:05:47 +01:00
|
|
|
static inline void render_add_metadata(BL::RenderResult &b_rr, string name, string value)
|
|
|
|
|
{
|
|
|
|
|
b_rr.stamp_data_add_field(name.c_str(), value.c_str());
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-27 11:58:34 +00:00
|
|
|
/* Utilities */
|
|
|
|
|
|
2016-01-30 14:18:29 +01:00
|
|
|
static inline Transform get_transform(const BL::Array<float, 16> &array)
|
2011-04-27 11:58:34 +00:00
|
|
|
{
|
2024-12-27 21:50:31 +01:00
|
|
|
/* Convert from Blender column major to Cycles row major, assume it's an affine transform that
|
|
|
|
|
* does not need the last row. */
|
|
|
|
|
return make_transform(array[0],
|
|
|
|
|
array[4],
|
|
|
|
|
array[8],
|
|
|
|
|
array[12],
|
|
|
|
|
|
|
|
|
|
array[1],
|
|
|
|
|
array[5],
|
|
|
|
|
array[9],
|
|
|
|
|
array[13],
|
|
|
|
|
|
|
|
|
|
array[2],
|
|
|
|
|
array[6],
|
|
|
|
|
array[10],
|
|
|
|
|
array[14]);
|
2011-04-27 11:58:34 +00:00
|
|
|
}
|
|
|
|
|
|
2016-01-30 14:18:29 +01:00
|
|
|
static inline float2 get_float2(const BL::Array<float, 2> &array)
|
2011-04-27 11:58:34 +00:00
|
|
|
{
|
|
|
|
|
return make_float2(array[0], array[1]);
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-30 14:18:29 +01:00
|
|
|
static inline float3 get_float3(const BL::Array<float, 2> &array)
|
2011-04-27 11:58:34 +00:00
|
|
|
{
|
|
|
|
|
return make_float3(array[0], array[1], 0.0f);
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-30 14:18:29 +01:00
|
|
|
static inline float3 get_float3(const BL::Array<float, 3> &array)
|
2011-04-27 11:58:34 +00:00
|
|
|
{
|
|
|
|
|
return make_float3(array[0], array[1], array[2]);
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-30 14:18:29 +01:00
|
|
|
static inline float3 get_float3(const BL::Array<float, 4> &array)
|
2011-04-27 11:58:34 +00:00
|
|
|
{
|
|
|
|
|
return make_float3(array[0], array[1], array[2]);
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-30 14:18:29 +01:00
|
|
|
static inline float4 get_float4(const BL::Array<float, 4> &array)
|
2012-08-31 19:38:59 +00:00
|
|
|
{
|
|
|
|
|
return make_float4(array[0], array[1], array[2], array[3]);
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-30 14:18:29 +01:00
|
|
|
static inline int3 get_int3(const BL::Array<int, 3> &array)
|
2014-03-29 13:03:48 +01:00
|
|
|
{
|
|
|
|
|
return make_int3(array[0], array[1], array[2]);
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-30 14:18:29 +01:00
|
|
|
static inline int4 get_int4(const BL::Array<int, 4> &array)
|
2011-04-27 11:58:34 +00:00
|
|
|
{
|
|
|
|
|
return make_int4(array[0], array[1], array[2], array[3]);
|
|
|
|
|
}
|
|
|
|
|
|
2012-06-09 17:22:52 +00:00
|
|
|
static inline float3 get_float3(PointerRNA &ptr, const char *name)
|
2011-04-27 11:58:34 +00:00
|
|
|
{
|
|
|
|
|
float3 f;
|
|
|
|
|
RNA_float_get_array(&ptr, name, &f.x);
|
|
|
|
|
return f;
|
2012-06-09 17:22:52 +00:00
|
|
|
}
|
2013-03-18 16:34:57 +00:00
|
|
|
|
2025-01-01 18:15:54 +01:00
|
|
|
static inline void set_float3(PointerRNA &ptr, const char *name, const float3 value)
|
2013-03-18 16:34:57 +00:00
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-01 18:15:54 +01:00
|
|
|
static inline void set_float4(PointerRNA &ptr, const char *name, const float4 value)
|
2013-03-18 16:34:57 +00:00
|
|
|
{
|
|
|
|
|
RNA_float_set_array(&ptr, name, &value.x);
|
|
|
|
|
}
|
2011-04-27 11:58:34 +00:00
|
|
|
|
|
|
|
|
static inline bool get_boolean(PointerRNA &ptr, const char *name)
|
|
|
|
|
{
|
2011-05-03 18:29:11 +00:00
|
|
|
return RNA_boolean_get(&ptr, name) ? true : false;
|
2011-04-27 11:58:34 +00:00
|
|
|
}
|
|
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
static inline void set_boolean(PointerRNA &ptr, const char *name, bool value)
|
|
|
|
|
{
|
|
|
|
|
RNA_boolean_set(&ptr, name, (int)value);
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-27 11:58:34 +00:00
|
|
|
static inline float get_float(PointerRNA &ptr, const char *name)
|
|
|
|
|
{
|
|
|
|
|
return RNA_float_get(&ptr, name);
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-01 18:15:54 +01:00
|
|
|
static inline void set_float(PointerRNA &ptr, const char *name, const float value)
|
2013-03-18 16:34:57 +00:00
|
|
|
{
|
|
|
|
|
RNA_float_set(&ptr, name, value);
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-27 11:58:34 +00:00
|
|
|
static inline int get_int(PointerRNA &ptr, const char *name)
|
|
|
|
|
{
|
|
|
|
|
return RNA_int_get(&ptr, name);
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-01 18:15:54 +01:00
|
|
|
static inline void set_int(PointerRNA &ptr, const char *name, const int value)
|
2013-03-18 16:34:57 +00:00
|
|
|
{
|
|
|
|
|
RNA_int_set(&ptr, name, value);
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-10 15:09:45 +01:00
|
|
|
/* 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(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;
|
2011-04-27 11:58:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline string get_enum_identifier(PointerRNA &ptr, const char *name)
|
|
|
|
|
{
|
|
|
|
|
PropertyRNA *prop = RNA_struct_find_property(&ptr, name);
|
|
|
|
|
const char *identifier = "";
|
2024-12-29 17:32:00 +01:00
|
|
|
const int value = RNA_property_enum_get(&ptr, prop);
|
2011-04-27 11:58:34 +00:00
|
|
|
|
2024-12-26 17:53:55 +01:00
|
|
|
RNA_property_enum_identifier(nullptr, &ptr, prop, value, &identifier);
|
2011-04-27 11:58:34 +00:00
|
|
|
|
|
|
|
|
return string(identifier);
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-01 18:15:54 +01:00
|
|
|
static inline void set_enum(PointerRNA &ptr, const char *name, const int value)
|
2013-03-18 16:34:57 +00:00
|
|
|
{
|
|
|
|
|
RNA_enum_set(&ptr, name, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void set_enum(PointerRNA &ptr, const char *name, const string &identifier)
|
|
|
|
|
{
|
2024-12-26 17:53:55 +01:00
|
|
|
RNA_enum_set_identifier(nullptr, &ptr, name, identifier.c_str());
|
2013-03-18 16:34:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline string get_string(PointerRNA &ptr, const char *name)
|
|
|
|
|
{
|
|
|
|
|
char cstrbuf[1024];
|
2024-12-26 17:53:55 +01:00
|
|
|
char *cstr = RNA_string_get_alloc(&ptr, name, cstrbuf, sizeof(cstrbuf), nullptr);
|
2013-03-18 16:34:57 +00:00
|
|
|
string str(cstr);
|
2023-09-24 14:52:38 +10:00
|
|
|
if (cstr != cstrbuf) {
|
2013-03-18 16:34:57 +00:00
|
|
|
MEM_freeN(cstr);
|
2023-09-24 14:52:38 +10:00
|
|
|
}
|
2017-05-30 09:43:43 +02:00
|
|
|
|
2013-03-18 16:34:57 +00:00
|
|
|
return str;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void set_string(PointerRNA &ptr, const char *name, const string &value)
|
|
|
|
|
{
|
|
|
|
|
RNA_string_set(&ptr, name, value.c_str());
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-27 11:58:34 +00:00
|
|
|
/* Relative Paths */
|
|
|
|
|
|
2016-01-30 14:18:29 +01:00
|
|
|
static inline string blender_absolute_path(BL::BlendData &b_data, BL::ID &b_id, const string &path)
|
2011-04-27 11:58:34 +00:00
|
|
|
{
|
|
|
|
|
if (path.size() >= 2 && path[0] == '/' && path[1] == '/') {
|
2011-11-16 16:10:11 +00:00
|
|
|
string dirname;
|
2017-05-30 09:43:43 +02:00
|
|
|
|
2016-01-30 14:18:29 +01:00
|
|
|
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());
|
|
|
|
|
}
|
2023-09-24 14:52:38 +10:00
|
|
|
else {
|
2011-11-16 16:10:11 +00:00
|
|
|
dirname = b_data.filepath();
|
2023-09-24 14:52:38 +10:00
|
|
|
}
|
2011-11-16 16:10:11 +00:00
|
|
|
|
2011-04-27 11:58:34 +00:00
|
|
|
return path_join(path_dirname(dirname), path.substr(2));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return path;
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-27 17:14:01 +02:00
|
|
|
static inline string get_text_datablock_content(const PointerRNA &ptr)
|
Cycles: Add Support for IES files as textures for light strength
This patch adds support for IES files, a file format that is commonly used to store the directional intensity distribution of light sources.
The new IES node is supposed to be plugged into the Strength input of the Emission node of the lamp.
Since people generating IES files do not really seem to care about the standard, the parser is flexible enough to accept all test files I have tried.
Some common weirdnesses are distributing values over multiple lines that should go into one line, using commas instead of spaces as delimiters and adding various useless stuff at the end of the file.
The user interface of the node is similar to the script node, the user can either select an internal Text or load a file.
Internally, IES files are handled similar to Image textures: They are stored in slots by the LightManager and each unique IES is assigned to one slot.
The local coordinate system of the lamp is used, so that the direction of the light can be changed. For UI reasons, it's usually best to add an area light,
rotate it and then change its type, since especially the point light does not immediately show its local coordinate system in the viewport.
Reviewers: #cycles, dingto, sergey, brecht
Reviewed By: #cycles, dingto, brecht
Subscribers: OgDEV, crazyrobinhood, secundar, cardboard, pisuke, intrah, swerner, micah_denn, harvester, gottfried, disnel, campbellbarton, duarteframos, Lapineige, brecht, juicyfruit, dingto, marek, rickyblender, bliblubli, lockal, sergey
Differential Revision: https://developer.blender.org/D1543
2018-05-27 00:46:37 +02:00
|
|
|
{
|
2024-12-26 17:53:55 +01:00
|
|
|
if (ptr.data == nullptr) {
|
Cycles: Add Support for IES files as textures for light strength
This patch adds support for IES files, a file format that is commonly used to store the directional intensity distribution of light sources.
The new IES node is supposed to be plugged into the Strength input of the Emission node of the lamp.
Since people generating IES files do not really seem to care about the standard, the parser is flexible enough to accept all test files I have tried.
Some common weirdnesses are distributing values over multiple lines that should go into one line, using commas instead of spaces as delimiters and adding various useless stuff at the end of the file.
The user interface of the node is similar to the script node, the user can either select an internal Text or load a file.
Internally, IES files are handled similar to Image textures: They are stored in slots by the LightManager and each unique IES is assigned to one slot.
The local coordinate system of the lamp is used, so that the direction of the light can be changed. For UI reasons, it's usually best to add an area light,
rotate it and then change its type, since especially the point light does not immediately show its local coordinate system in the viewport.
Reviewers: #cycles, dingto, sergey, brecht
Reviewed By: #cycles, dingto, brecht
Subscribers: OgDEV, crazyrobinhood, secundar, cardboard, pisuke, intrah, swerner, micah_denn, harvester, gottfried, disnel, campbellbarton, duarteframos, Lapineige, brecht, juicyfruit, dingto, marek, rickyblender, bliblubli, lockal, sergey
Differential Revision: https://developer.blender.org/D1543
2018-05-27 00:46:37 +02:00
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string content;
|
|
|
|
|
BL::Text::lines_iterator iter;
|
|
|
|
|
for (iter.begin(ptr); iter; ++iter) {
|
|
|
|
|
content += iter->body() + "\n";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return content;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-03 12:31:05 +00:00
|
|
|
/* Texture Space */
|
|
|
|
|
|
2023-09-18 02:50:09 +02:00
|
|
|
static inline void mesh_texture_space(const ::Mesh &b_mesh, float3 &loc, float3 &size)
|
2013-01-03 12:31:05 +00:00
|
|
|
{
|
2024-12-29 17:32:00 +01:00
|
|
|
float texspace_location[3];
|
|
|
|
|
float texspace_size[3];
|
2023-09-18 02:50:09 +02:00
|
|
|
BKE_mesh_texspace_get(const_cast<::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]);
|
2013-01-03 12:31:05 +00:00
|
|
|
|
2023-09-24 14:52:38 +10:00
|
|
|
if (size.x != 0.0f) {
|
2013-01-03 12:31:05 +00:00
|
|
|
size.x = 0.5f / size.x;
|
2023-09-24 14:52:38 +10:00
|
|
|
}
|
|
|
|
|
if (size.y != 0.0f) {
|
2013-01-03 12:31:05 +00:00
|
|
|
size.y = 0.5f / size.y;
|
2023-09-24 14:52:38 +10:00
|
|
|
}
|
|
|
|
|
if (size.z != 0.0f) {
|
2013-01-03 12:31:05 +00:00
|
|
|
size.z = 0.5f / size.z;
|
2023-09-24 14:52:38 +10:00
|
|
|
}
|
2013-01-03 12:31:05 +00:00
|
|
|
|
|
|
|
|
loc = loc * size - make_float3(0.5f, 0.5f, 0.5f);
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-08 04:04:52 +01:00
|
|
|
/* Object motion steps, returns 0 if no motion blur needed. */
|
2020-02-18 14:02:40 +01:00
|
|
|
static inline uint object_motion_steps(BL::Object &b_parent,
|
|
|
|
|
BL::Object &b_ob,
|
|
|
|
|
const int max_steps = INT_MAX)
|
2014-03-29 13:03:47 +01:00
|
|
|
{
|
2018-03-08 04:04:52 +01:00
|
|
|
/* Get motion enabled and steps from object itself. */
|
2014-03-29 13:03:47 +01:00
|
|
|
PointerRNA cobject = RNA_pointer_get(&b_ob.ptr, "cycles");
|
|
|
|
|
bool use_motion = get_boolean(cobject, "use_motion_blur");
|
2018-03-08 04:04:52 +01:00
|
|
|
if (!use_motion) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-18 14:02:40 +01:00
|
|
|
int steps = max(1, get_int(cobject, "motion_steps"));
|
2018-03-08 04:04:52 +01:00
|
|
|
|
|
|
|
|
/* Also check parent object, so motion blur and steps can be
|
2024-01-22 12:44:56 +11:00
|
|
|
* controlled by dupli-group duplicator for linked groups. */
|
2018-03-08 04:04:52 +01:00
|
|
|
if (b_parent.ptr.data != b_ob.ptr.data) {
|
2015-06-12 13:54:17 +02:00
|
|
|
PointerRNA parent_cobject = RNA_pointer_get(&b_parent.ptr, "cycles");
|
|
|
|
|
use_motion &= get_boolean(parent_cobject, "use_motion_blur");
|
2014-03-29 13:03:47 +01:00
|
|
|
|
2018-03-08 04:04:52 +01:00
|
|
|
if (!use_motion) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
steps = max(steps, get_int(parent_cobject, "motion_steps"));
|
|
|
|
|
}
|
2014-03-29 13:03:47 +01:00
|
|
|
|
2018-03-08 04:04:52 +01:00
|
|
|
/* 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. */
|
2020-02-18 14:02:40 +01:00
|
|
|
return min((2 << (steps - 1)) + 1, max_steps);
|
2014-03-29 13:03:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* object uses deformation motion blur */
|
2016-01-30 14:18:29 +01:00
|
|
|
static inline bool object_use_deform_motion(BL::Object &b_parent, BL::Object &b_ob)
|
2014-03-29 13:03:47 +01:00
|
|
|
{
|
|
|
|
|
PointerRNA cobject = RNA_pointer_get(&b_ob.ptr, "cycles");
|
|
|
|
|
bool use_deform_motion = get_boolean(cobject, "use_deform_motion");
|
2015-06-12 13:54:17 +02:00
|
|
|
/* If motion blur is enabled for the object we also check
|
|
|
|
|
* whether it's enabled for the parent object as well.
|
|
|
|
|
*
|
2024-01-29 11:47:42 +11:00
|
|
|
* This way we can control motion blur from the dupli-group
|
|
|
|
|
* duplicator much easier. */
|
2015-06-12 13:54:17 +02:00
|
|
|
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");
|
|
|
|
|
}
|
2014-03-29 13:03:47 +01:00
|
|
|
return use_deform_motion;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-02 13:09:18 +01:00
|
|
|
static inline BL::FluidDomainSettings object_fluid_gas_domain_find(BL::Object &b_ob)
|
|
|
|
|
{
|
2021-01-25 16:20:10 +01:00
|
|
|
for (BL::Modifier &b_mod : b_ob.modifiers) {
|
|
|
|
|
if (b_mod.is_a(&RNA_FluidModifier)) {
|
|
|
|
|
BL::FluidModifier b_mmd(b_mod);
|
2020-02-02 13:09:18 +01:00
|
|
|
|
|
|
|
|
if (b_mmd.fluid_type() == BL::FluidModifier::fluid_type_DOMAIN &&
|
|
|
|
|
b_mmd.domain_settings().domain_type() == BL::FluidDomainSettings::domain_type_GAS)
|
|
|
|
|
{
|
|
|
|
|
return b_mmd.domain_settings();
|
|
|
|
|
}
|
2014-03-29 13:03:48 +01:00
|
|
|
}
|
|
|
|
|
}
|
2017-05-30 09:43:43 +02:00
|
|
|
|
2019-12-16 15:50:14 +01:00
|
|
|
return BL::FluidDomainSettings(PointerRNA_NULL);
|
2016-07-16 18:56:59 +02:00
|
|
|
}
|
|
|
|
|
|
2021-08-19 14:34:01 +02:00
|
|
|
static inline BL::MeshSequenceCacheModifier object_mesh_cache_find(BL::Object &b_ob,
|
2021-08-20 02:30:50 +02:00
|
|
|
bool *has_subdivision_modifier)
|
2021-08-19 14:34:01 +02:00
|
|
|
{
|
|
|
|
|
for (int i = b_ob.modifiers.length() - 1; i >= 0; --i) {
|
|
|
|
|
BL::Modifier b_mod = b_ob.modifiers[i];
|
|
|
|
|
|
|
|
|
|
if (b_mod.type() == BL::Modifier::type_MESH_SEQUENCE_CACHE) {
|
|
|
|
|
BL::MeshSequenceCacheModifier mesh_cache = BL::MeshSequenceCacheModifier(b_mod);
|
|
|
|
|
return mesh_cache;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Skip possible particles system modifiers as they do not modify the geometry. */
|
|
|
|
|
if (b_mod.type() == BL::Modifier::type_PARTICLE_SYSTEM) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-09 17:22:20 +02:00
|
|
|
if (b_mod.type() == BL::Modifier::type_SUBSURF) {
|
2021-08-20 02:30:50 +02:00
|
|
|
if (has_subdivision_modifier) {
|
|
|
|
|
*has_subdivision_modifier = true;
|
|
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-19 14:34:01 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return BL::MeshSequenceCacheModifier(PointerRNA_NULL);
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-09 04:20:05 +01:00
|
|
|
static BL::SubsurfModifier object_subdivision_modifier(BL::Object &b_ob,
|
|
|
|
|
const bool preview,
|
|
|
|
|
const bool experimental)
|
2016-09-18 12:04:12 -04:00
|
|
|
{
|
|
|
|
|
PointerRNA cobj = RNA_pointer_get(&b_ob.ptr, "cycles");
|
|
|
|
|
|
2021-11-22 23:47:26 -08:00
|
|
|
if (cobj.data && !b_ob.modifiers.empty() && experimental) {
|
2016-09-18 12:04:12 -04:00
|
|
|
BL::Modifier mod = b_ob.modifiers[b_ob.modifiers.length() - 1];
|
2024-12-29 17:32:00 +01:00
|
|
|
const bool enabled = preview ? mod.show_viewport() : mod.show_render();
|
2016-09-18 12:04:12 -04:00
|
|
|
|
|
|
|
|
if (enabled && mod.type() == BL::Modifier::type_SUBSURF &&
|
|
|
|
|
RNA_boolean_get(&cobj, "use_adaptive_subdivision"))
|
|
|
|
|
{
|
|
|
|
|
BL::SubsurfModifier subsurf(mod);
|
2025-03-09 04:20:05 +01:00
|
|
|
return subsurf;
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-09-18 12:04:12 -04:00
|
|
|
|
2025-03-09 04:20:05 +01:00
|
|
|
return PointerRNA_NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline Mesh::SubdivisionType object_subdivision_type(BL::Object &b_ob,
|
|
|
|
|
const bool preview,
|
|
|
|
|
const bool experimental)
|
|
|
|
|
{
|
|
|
|
|
BL::SubsurfModifier subsurf = object_subdivision_modifier(b_ob, preview, experimental);
|
|
|
|
|
|
|
|
|
|
if (subsurf) {
|
|
|
|
|
if (subsurf.subdivision_type() == BL::SubsurfModifier::subdivision_type_CATMULL_CLARK) {
|
|
|
|
|
return Mesh::SUBDIVISION_CATMULL_CLARK;
|
2016-09-18 12:04:12 -04:00
|
|
|
}
|
2025-03-09 04:20:05 +01:00
|
|
|
return Mesh::SUBDIVISION_LINEAR;
|
2016-09-18 12:04:12 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Mesh::SUBDIVISION_NONE;
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-09 04:20:05 +01:00
|
|
|
static inline void object_subdivision_to_mesh(BL::Object &b_ob,
|
|
|
|
|
Mesh &mesh,
|
|
|
|
|
const bool preview,
|
|
|
|
|
const bool experimental)
|
|
|
|
|
{
|
|
|
|
|
BL::SubsurfModifier subsurf = object_subdivision_modifier(b_ob, preview, experimental);
|
|
|
|
|
|
|
|
|
|
if (!subsurf) {
|
|
|
|
|
mesh.set_subdivision_type(Mesh::SUBDIVISION_NONE);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (subsurf.subdivision_type() != BL::SubsurfModifier::subdivision_type_CATMULL_CLARK) {
|
|
|
|
|
mesh.set_subdivision_type(Mesh::SUBDIVISION_LINEAR);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mesh.set_subdivision_type(Mesh::SUBDIVISION_CATMULL_CLARK);
|
|
|
|
|
|
|
|
|
|
switch (subsurf.boundary_smooth()) {
|
|
|
|
|
case BL::SubsurfModifier::boundary_smooth_PRESERVE_CORNERS:
|
|
|
|
|
mesh.set_subdivision_boundary_interpolation(Mesh::SUBDIVISION_BOUNDARY_EDGE_AND_CORNER);
|
|
|
|
|
break;
|
|
|
|
|
case BL::SubsurfModifier::boundary_smooth_ALL:
|
|
|
|
|
mesh.set_subdivision_boundary_interpolation(Mesh::SUBDIVISION_BOUNDARY_EDGE_ONLY);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (subsurf.uv_smooth()) {
|
|
|
|
|
case BL::SubsurfModifier::uv_smooth_NONE:
|
|
|
|
|
mesh.set_subdivision_fvar_interpolation(Mesh::SUBDIVISION_FVAR_LINEAR_ALL);
|
|
|
|
|
break;
|
|
|
|
|
case BL::SubsurfModifier::uv_smooth_PRESERVE_CORNERS:
|
|
|
|
|
mesh.set_subdivision_fvar_interpolation(Mesh::SUBDIVISION_FVAR_LINEAR_CORNERS_ONLY);
|
|
|
|
|
break;
|
|
|
|
|
case BL::SubsurfModifier::uv_smooth_PRESERVE_CORNERS_AND_JUNCTIONS:
|
|
|
|
|
mesh.set_subdivision_fvar_interpolation(Mesh::SUBDIVISION_FVAR_LINEAR_CORNERS_PLUS1);
|
|
|
|
|
break;
|
|
|
|
|
case BL::SubsurfModifier::uv_smooth_PRESERVE_CORNERS_JUNCTIONS_AND_CONCAVE:
|
|
|
|
|
mesh.set_subdivision_fvar_interpolation(Mesh::SUBDIVISION_FVAR_LINEAR_CORNERS_PLUS2);
|
|
|
|
|
break;
|
|
|
|
|
case BL::SubsurfModifier::uv_smooth_PRESERVE_BOUNDARIES:
|
|
|
|
|
mesh.set_subdivision_fvar_interpolation(Mesh::SUBDIVISION_FVAR_LINEAR_BOUNDARIES);
|
|
|
|
|
break;
|
|
|
|
|
case BL::SubsurfModifier::uv_smooth_SMOOTH_ALL:
|
|
|
|
|
mesh.set_subdivision_fvar_interpolation(Mesh::SUBDIVISION_FVAR_LINEAR_NONE);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-02 13:09:18 +01:00
|
|
|
static inline uint object_ray_visibility(BL::Object &b_ob)
|
|
|
|
|
{
|
|
|
|
|
uint flag = 0;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-08-04 19:43:40 +02:00
|
|
|
flag |= b_ob.visible_camera() ? PATH_RAY_CAMERA : 0;
|
|
|
|
|
flag |= b_ob.visible_diffuse() ? PATH_RAY_DIFFUSE : 0;
|
|
|
|
|
flag |= b_ob.visible_glossy() ? PATH_RAY_GLOSSY : 0;
|
|
|
|
|
flag |= b_ob.visible_transmission() ? PATH_RAY_TRANSMIT : 0;
|
|
|
|
|
flag |= b_ob.visible_shadow() ? PATH_RAY_SHADOW : 0;
|
|
|
|
|
flag |= b_ob.visible_volume_scatter() ? PATH_RAY_VOLUME_SCATTER : 0;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-02-02 13:09:18 +01:00
|
|
|
return flag;
|
|
|
|
|
}
|
2012-08-31 17:27:08 +00:00
|
|
|
|
2022-01-04 01:58:34 +01:00
|
|
|
/* 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()`. */
|
2022-01-05 16:06:23 +01:00
|
|
|
static inline bool object_need_motion_attribute(BObjectInfo &b_ob_info, Scene *scene)
|
2022-01-04 01:58:34 +01:00
|
|
|
{
|
|
|
|
|
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. */
|
|
|
|
|
PointerRNA cobject = RNA_pointer_get(&b_ob_info.real_object.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;
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-10 13:33:02 +01:00
|
|
|
class EdgeMap {
|
|
|
|
|
public:
|
2024-12-26 17:53:59 +01:00
|
|
|
EdgeMap() = default;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-02-10 13:33:02 +01:00
|
|
|
void clear()
|
|
|
|
|
{
|
|
|
|
|
edges_.clear();
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-02-10 13:33:02 +01:00
|
|
|
void insert(int v0, int v1)
|
|
|
|
|
{
|
|
|
|
|
get_sorted_verts(v0, v1);
|
|
|
|
|
edges_.insert(std::pair<int, int>(v0, v1));
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-02-10 13:33:02 +01:00
|
|
|
bool exists(int v0, int v1)
|
|
|
|
|
{
|
|
|
|
|
get_sorted_verts(v0, v1);
|
|
|
|
|
return edges_.find(std::pair<int, int>(v0, v1)) != edges_.end();
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-02-10 13:33:02 +01:00
|
|
|
protected:
|
|
|
|
|
void get_sorted_verts(int &v0, int &v1)
|
|
|
|
|
{
|
|
|
|
|
if (v0 > v1) {
|
|
|
|
|
swap(v0, v1);
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
|
2017-02-10 13:33:02 +01:00
|
|
|
set<std::pair<int, int>> edges_;
|
|
|
|
|
};
|
|
|
|
|
|
2011-04-27 11:58:34 +00:00
|
|
|
CCL_NAMESPACE_END
|