Files
test2/source/blender/draw/intern/draw_handle.hh
2025-06-22 11:34:32 +00:00

350 lines
10 KiB
C++

/* SPDX-FileCopyrightText: 2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup draw
*
* A unique identifier for each object component.
* It is used to access each component data such as matrices and object attributes.
* It is valid only for the current draw, it is not persistent.
*
* The most significant bit is used to encode if the object needs to invert the front face winding
* because of its object matrix handedness. This is handy because this means sorting inside
* #DrawGroup command will put all inverted commands last.
*
* Default value of 0 points toward an non-cull-able object with unit bounding box centered at
* the origin.
*/
#include "BKE_duplilist.hh"
#include "draw_shader_shared.hh"
/* random */
#include "BLI_hash.h"
/* find_rgba_attribute */
#include "GPU_material.hh"
/* particles_matrix */
#include "BLI_math_matrix.hh"
#include "DNA_collection_types.h"
/* parent_is_in_edit_paint_mode */
#include "BKE_context.hh"
#include "BKE_paint.hh"
#include "DNA_layer_types.h"
#include "DRW_render.hh"
/* ObjectKey */
#include "DEG_depsgraph_query.hh"
struct DupliCacheManager;
namespace blender::draw {
struct ResourceHandle {
/* Index for getting a specific resource in the resource arrays (e.g. object matrices).
* Last bit contains handedness. */
uint32_t raw;
ResourceHandle() = default;
ResourceHandle(uint raw_) : raw(raw_){};
ResourceHandle(uint index, bool inverted_handedness)
{
raw = index;
SET_FLAG_FROM_TEST(raw, inverted_handedness, 0x80000000u);
}
bool has_inverted_handedness() const
{
return (raw & 0x80000000u) != 0;
}
uint resource_index() const
{
return (raw & 0x7FFFFFFFu);
}
};
/* Refers to a range of contiguous handles in the resource arrays.
* Typically used to render instances of an object, but can represent a single instance too.
* The associated objects will all share handedness and state and can be rendered together. */
struct ResourceHandleRange {
/* First handle in the range. */
ResourceHandle handle_first;
/* Number of handle in the range. */
uint32_t count;
ResourceHandleRange() = default;
ResourceHandleRange(ResourceHandle handle) : handle_first(handle), count(1) {}
ResourceHandleRange(ResourceHandle handle, uint len) : handle_first(handle), count(len) {}
IndexRange index_range() const
{
return {handle_first.raw, count};
}
/* TODO(fclem): Temporary workaround to keep existing code to work. Should be removed once we
* complete the instance optimization project. */
operator ResourceHandle() const
{
return handle_first;
}
};
/* TODO(fclem): Move to somewhere more appropriated after cleaning up the header dependencies. */
class ObjectRef {
friend class Manager;
friend class ObjectKey;
friend DupliCacheManager;
private:
/** Duplicated object that corresponds to the current object. */
DupliObject *const dupli_object_ = nullptr;
/** Object that created the dupli-list the current object is part of. */
Object *const dupli_parent_ = nullptr;
/** Unique handle per object ref. */
ResourceHandleRange handle_ = {0, 0};
ResourceHandleRange sculpt_handle_ = {0, 0};
public:
Object *const object;
ObjectRef(DEGObjectIterData &iter_data, Object *ob);
explicit ObjectRef(Object *ob);
/* Is the object coming from a Dupli system. */
bool is_dupli() const
{
return dupli_object_ != nullptr;
}
bool is_active(const Object *active_object) const
{
return (dupli_object_ ? dupli_parent_ : object) == active_object;
}
float random() const
{
if (dupli_object_ == nullptr) {
/* TODO(fclem): this is rather costly to do at draw time. Maybe we can
* put it in ob->runtime and make depsgraph ensure it is up to date. */
return BLI_hash_int_2d(BLI_hash_string(object->id.name + 2), 0) * (1.0f / (float)0xFFFFFFFF);
}
return dupli_object_->random_id * (1.0f / (float)0xFFFFFFFF);
}
bool find_rgba_attribute(const GPUUniformAttr &attr, float r_value[4]) const
{
/* If requesting instance data, check the parent particle system and object. */
if (attr.use_dupli) {
return BKE_object_dupli_find_rgba_attribute(
object, dupli_object_, dupli_parent_, attr.name, r_value);
}
return BKE_object_dupli_find_rgba_attribute(object, nullptr, nullptr, attr.name, r_value);
}
LightLinking *light_linking() const
{
/* TODO: Could this be handled directly by deg_iterator_duplis_step? */
return dupli_parent_ ? dupli_parent_->light_linking : object->light_linking;
}
int recalc_flags(uint64_t last_update) const
{
/* TODO: There should also be a way to get the min last_update for all objects in the range. */
auto get_flags = [&](const ObjectRuntimeHandle &runtime) {
int flags = 0;
SET_FLAG_FROM_TEST(flags, runtime.last_update_transform > last_update, ID_RECALC_TRANSFORM);
SET_FLAG_FROM_TEST(flags, runtime.last_update_geometry > last_update, ID_RECALC_GEOMETRY);
SET_FLAG_FROM_TEST(flags, runtime.last_update_shading > last_update, ID_RECALC_SHADING);
return flags;
};
int flags = get_flags(*object->runtime);
if (dupli_parent_) {
flags |= get_flags(*dupli_parent_->runtime);
}
return flags;
}
/* Particle data are stored in world space. If an object is instanced, the associated particle
* systems need to be offset appropriately. */
float4x4 particles_matrix() const
{
/* TODO: Pass particle systems as a separate ObRef? */
float4x4 dupli_mat = float4x4::identity();
if (dupli_parent_ && dupli_object_) {
if (dupli_object_->type & OB_DUPLICOLLECTION) {
Collection *collection = dupli_parent_->instance_collection;
if (collection != nullptr) {
dupli_mat[3] -= float4(float3(collection->instance_offset), 0.0f);
}
dupli_mat = dupli_parent_->object_to_world() * dupli_mat;
}
else {
dupli_mat = object->object_to_world() * math::invert(dupli_object_->ob->object_to_world());
}
}
return dupli_mat;
}
int preview_instance_index() const
{
if (dupli_object_) {
return dupli_object_->preview_instance_index;
}
return -1;
}
const blender::bke::GeometrySet *preview_base_geometry() const
{
if (dupli_object_) {
return dupli_object_->preview_base_geometry;
}
return nullptr;
}
bool parent_is_in_edit_paint_mode(const Object *active_object,
eObjectMode ob_mode,
eContextObjectMode ctx_mode) const
{
/* TODO: Deduplicate code with Overlay engine.
* Move to BKE ? Or check if T72490 is still relevant. */
if (!dupli_parent_ || active_object != dupli_parent_) {
return false;
}
if (object->base_flag & BASE_FROM_DUPLI) {
/* TODO: Is this code reachable? */
return false;
}
if (dupli_parent_->sculpt && (dupli_parent_->sculpt->mode_type == OB_MODE_SCULPT)) {
return true;
}
if (ob_mode & (OB_MODE_ALL_PAINT | OB_MODE_ALL_PAINT_GPENCIL)) {
return true;
}
if (DRW_object_is_in_edit_mode(dupli_parent_)) {
/* Also check for context mode as the object mode is not 100% reliable. (see T72490) */
switch (dupli_parent_->type) {
case OB_MESH:
return ctx_mode == CTX_MODE_EDIT_MESH;
case OB_ARMATURE:
return ctx_mode == CTX_MODE_EDIT_ARMATURE;
case OB_CURVES_LEGACY:
return ctx_mode == CTX_MODE_EDIT_CURVE;
case OB_SURF:
return ctx_mode == CTX_MODE_EDIT_SURFACE;
case OB_LATTICE:
return ctx_mode == CTX_MODE_EDIT_LATTICE;
case OB_MBALL:
return ctx_mode == CTX_MODE_EDIT_METABALL;
case OB_FONT:
return ctx_mode == CTX_MODE_EDIT_TEXT;
case OB_CURVES:
return ctx_mode == CTX_MODE_EDIT_CURVES;
case OB_POINTCLOUD:
return ctx_mode == CTX_MODE_EDIT_POINTCLOUD;
case OB_GREASE_PENCIL:
return ctx_mode == CTX_MODE_EDIT_GREASE_PENCIL;
case OB_VOLUME:
/* No edit mode yet. */
return false;
}
}
return false;
}
};
/* -------------------------------------------------------------------- */
/** \name ObjectKey
*
* Unique key to identify each object in a hash-map.
* Note that we get a unique key for each object component.
* \{ */
class ObjectKey {
/** Hash value of the key. */
uint64_t hash_value_ = 0;
/** Original Object or source object for duplis. */
Object *ob_ = nullptr;
/** Original Parent object for duplis. */
Object *parent_ = nullptr;
/** Dupli objects recursive unique identifier */
int id_[MAX_DUPLI_RECUR];
/** Used for particle system hair. */
int sub_key_ = 0;
public:
ObjectKey() = default;
ObjectKey(const ObjectRef &ob_ref, int sub_key = 0)
{
ob_ = DEG_get_original(ob_ref.object);
hash_value_ = get_default_hash(ob_);
if (DupliObject *dupli = ob_ref.dupli_object_) {
parent_ = ob_ref.dupli_parent_;
hash_value_ = get_default_hash(hash_value_, get_default_hash(parent_));
for (int i : IndexRange(MAX_DUPLI_RECUR)) {
id_[i] = dupli->persistent_id[i];
if (id_[i] == std::numeric_limits<int>::max()) {
break;
}
hash_value_ = get_default_hash(hash_value_, get_default_hash(id_[i]));
}
}
if (sub_key != 0) {
sub_key_ = sub_key;
hash_value_ = get_default_hash(hash_value_, get_default_hash(sub_key_));
}
}
uint64_t hash() const
{
return hash_value_;
}
bool operator==(const ObjectKey &k) const
{
if (hash_value_ != k.hash_value_) {
return false;
}
if (ob_ != k.ob_) {
return false;
}
if (parent_ != k.parent_) {
return false;
}
if (sub_key_ != k.sub_key_) {
return false;
}
if (parent_) {
for (int i : IndexRange(MAX_DUPLI_RECUR)) {
if (id_[i] != k.id_[i]) {
return false;
}
if (id_[i] == std::numeric_limits<int>::max()) {
break;
}
}
}
return true;
}
};
/** \} */
}; // namespace blender::draw