Add Chromium-only Blender WebEngine parity work
This commit is contained in:
1490
blender-5.2.0/source/blender/draw/intern/DRW_gpu_wrapper.hh
Normal file
1490
blender-5.2.0/source/blender/draw/intern/DRW_gpu_wrapper.hh
Normal file
File diff suppressed because it is too large
Load Diff
458
blender-5.2.0/source/blender/draw/intern/DRW_render.hh
Normal file
458
blender-5.2.0/source/blender/draw/intern/DRW_render.hh
Normal file
@@ -0,0 +1,458 @@
|
||||
/* SPDX-FileCopyrightText: 2016 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
/* This is the Render Functions used by Realtime engines to draw with OpenGL */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "BKE_mesh_wrapper.hh"
|
||||
#include "BKE_subdiv_modifier.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "DNA_object_enums.h"
|
||||
#include "DNA_object_types.h"
|
||||
|
||||
#include "GPU_material.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace gpu {
|
||||
class Batch;
|
||||
class Shader;
|
||||
class Texture;
|
||||
class UniformBuf;
|
||||
class FrameBuffer;
|
||||
} // namespace gpu
|
||||
struct ARegion;
|
||||
struct bContext;
|
||||
struct DefaultFramebufferList;
|
||||
struct DefaultTextureList;
|
||||
struct Depsgraph;
|
||||
struct DupliObject;
|
||||
struct GPUMaterial;
|
||||
struct Mesh;
|
||||
struct Object;
|
||||
struct ParticleSystem;
|
||||
struct rcti;
|
||||
struct RegionView3D;
|
||||
struct RenderEngine;
|
||||
struct RenderEngineType;
|
||||
struct RenderLayer;
|
||||
struct RenderResult;
|
||||
struct SpaceLink;
|
||||
struct TaskGraph;
|
||||
struct View3D;
|
||||
struct ViewLayer;
|
||||
struct DRWContext;
|
||||
struct World;
|
||||
struct DRWData;
|
||||
struct DRWViewData;
|
||||
struct GPUViewport;
|
||||
struct DRWTextStore;
|
||||
struct GPUViewport;
|
||||
namespace draw {
|
||||
class TextureFromPool;
|
||||
class ObjectRef;
|
||||
class Manager;
|
||||
} // namespace draw
|
||||
|
||||
/* TODO: Put it somewhere else? */
|
||||
struct BoundSphere {
|
||||
float center[3], radius;
|
||||
};
|
||||
|
||||
struct DrawEngine {
|
||||
static constexpr int GPU_INFO_SIZE = 512; /* IMA_MAX_RENDER_TEXT_SIZE */
|
||||
|
||||
char info[GPU_INFO_SIZE] = {'\0'};
|
||||
|
||||
bool used = false;
|
||||
|
||||
virtual ~DrawEngine() = default;
|
||||
|
||||
virtual StringRefNull name_get() = 0;
|
||||
|
||||
/* Functions called for viewport. */
|
||||
|
||||
/** Init engine. Run first and for every redraw. */
|
||||
virtual void init() = 0;
|
||||
/** Scene synchronization. Command buffers building. */
|
||||
virtual void begin_sync() = 0;
|
||||
virtual void object_sync(draw::ObjectRef &ob_ref, draw::Manager &manager) = 0;
|
||||
virtual void end_sync() = 0;
|
||||
/** Command Submission. */
|
||||
virtual void draw(draw::Manager &manager) = 0;
|
||||
|
||||
/* Called when closing blender.
|
||||
* Cleanup all lazily initialized static members that have GPU resources.
|
||||
* Implemented on a case by case basis and called directly. */
|
||||
// static void exit(){};
|
||||
|
||||
struct Pointer {
|
||||
DrawEngine *instance = nullptr;
|
||||
|
||||
~Pointer()
|
||||
{
|
||||
free_instance();
|
||||
}
|
||||
|
||||
void free_instance()
|
||||
{
|
||||
delete instance;
|
||||
instance = nullptr;
|
||||
}
|
||||
|
||||
void set_used(bool used)
|
||||
{
|
||||
if (used) {
|
||||
if (instance == nullptr) {
|
||||
instance = create_instance();
|
||||
}
|
||||
instance->used = true;
|
||||
}
|
||||
else if (instance) {
|
||||
instance->used = false;
|
||||
}
|
||||
}
|
||||
|
||||
virtual DrawEngine *create_instance() = 0;
|
||||
};
|
||||
};
|
||||
|
||||
/* Viewport. */
|
||||
|
||||
/**
|
||||
* Returns a TextureFromPool stored in the given view data for the pass identified by the given
|
||||
* pass name. Engines should call this function for each of the passes needed by the viewport
|
||||
* compositor in every redraw, then it should allocate the texture and write the pass data to it.
|
||||
* The texture should cover the entire viewport.
|
||||
*/
|
||||
draw::TextureFromPool &DRW_viewport_pass_texture_get(const char *pass_name);
|
||||
|
||||
/**
|
||||
* Returns true if the pass with the given name exists.
|
||||
*/
|
||||
bool DRW_viewport_pass_texture_exists(const char *pass_name);
|
||||
|
||||
void DRW_viewport_request_redraw();
|
||||
|
||||
void DRW_render_to_image(
|
||||
RenderEngine *engine,
|
||||
Depsgraph *depsgraph,
|
||||
::std::function<void(RenderEngine *, RenderLayer *, const rcti)> render_view_cb,
|
||||
::std::function<void(RenderResult *)> store_metadata_cb);
|
||||
|
||||
void DRW_render_object_iter(RenderEngine *engine,
|
||||
Depsgraph *depsgraph,
|
||||
::std::function<void(draw::ObjectRef &, RenderEngine *, Depsgraph *)>);
|
||||
|
||||
void DRW_render_set_time(RenderEngine *engine, Depsgraph *depsgraph, int frame, float subframe);
|
||||
|
||||
/**
|
||||
* Assume a valid GL context is bound (and that the gl_context_mutex has been acquired).
|
||||
* This function only setup DST and execute the given function.
|
||||
* \warning similar to DRW_render_to_image you cannot use default lists (`dfbl` & `dtxl`).
|
||||
*/
|
||||
void DRW_custom_pipeline_begin(DRWContext &draw_ctx, Depsgraph *depsgraph);
|
||||
void DRW_custom_pipeline_end(DRWContext &draw_ctx);
|
||||
|
||||
/**
|
||||
* Used when the render engine want to redo another cache populate inside the same render frame.
|
||||
* Assumes it is called between `DRW_custom_pipeline_begin/end()`.
|
||||
*/
|
||||
void DRW_cache_restart();
|
||||
|
||||
/* Settings. */
|
||||
|
||||
bool DRW_object_is_renderable(const Object *ob);
|
||||
/**
|
||||
* Does `ob` needs to be rendered in edit mode.
|
||||
*
|
||||
* When using duplicate linked meshes, objects that are not in edit-mode will be drawn as
|
||||
* it is in edit mode, when another object with the same mesh is in edit mode.
|
||||
* This will not be the case when one of the objects are influenced by modifiers.
|
||||
*/
|
||||
bool DRW_object_is_in_edit_mode(const Object *ob);
|
||||
/**
|
||||
* Return whether this object is visible depending if
|
||||
* we are rendering or drawing in the viewport.
|
||||
*/
|
||||
int DRW_object_visibility_in_active_context(const Object *ob);
|
||||
bool DRW_object_use_hide_faces(const Object *ob);
|
||||
|
||||
bool DRW_object_is_visible_psys_in_active_context(const Object *object,
|
||||
const ParticleSystem *psys);
|
||||
|
||||
/**
|
||||
* Convenient accessor for object data, that also automatically returns
|
||||
* the base or tessellated mesh depending if GPU subdivision is enabled.
|
||||
*/
|
||||
template<typename T> T &DRW_object_get_data_for_drawing(const Object &object)
|
||||
{
|
||||
return *id_cast<T *>(object.data);
|
||||
}
|
||||
|
||||
inline Mesh &DRW_mesh_get_for_drawing(Mesh &mesh)
|
||||
{
|
||||
/* For drawing we want either the base mesh if GPU subdivision is enabled, or the
|
||||
* tessellated mesh if GPU subdivision is disabled. */
|
||||
if (BKE_subsurf_modifier_has_gpu_subdiv(&mesh)) {
|
||||
return mesh;
|
||||
}
|
||||
return *BKE_mesh_wrapper_ensure_subdivision(&mesh);
|
||||
}
|
||||
|
||||
template<> inline Mesh &DRW_object_get_data_for_drawing(const Object &object)
|
||||
{
|
||||
BLI_assert(object.type == OB_MESH);
|
||||
return DRW_mesh_get_for_drawing(*id_cast<Mesh *>(object.data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as DRW_object_get_data_for_drawing, but for the editmesh cage,
|
||||
* if it exists.
|
||||
*/
|
||||
const Mesh *DRW_object_get_editmesh_cage_for_drawing(const Object &object);
|
||||
|
||||
/* Draw State. */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Draw Context
|
||||
* \{ */
|
||||
|
||||
struct DRWContext {
|
||||
private:
|
||||
/** Render State: No persistent data between draw calls. */
|
||||
static thread_local DRWContext *g_context;
|
||||
/** Timings recorded for performance overlay. */
|
||||
float last_sync_time_;
|
||||
float last_submission_time_;
|
||||
|
||||
/* TODO(fclem): Private? */
|
||||
public:
|
||||
/* TODO: clean up this struct a bit. */
|
||||
/** Cache generation */
|
||||
DRWData *data = nullptr;
|
||||
/** Active view data structure for one of the 2 stereo view. */
|
||||
DRWViewData *view_data_active = nullptr;
|
||||
|
||||
/** Optional associated viewport. Can be nullptr. */
|
||||
GPUViewport *viewport = nullptr;
|
||||
/** Size of the viewport or the final render frame. */
|
||||
float2 size = {0, 0};
|
||||
float2 inv_size = {0, 0};
|
||||
|
||||
/** Returns the viewport's default frame-buffer. */
|
||||
gpu::FrameBuffer *default_framebuffer();
|
||||
/** Returns the viewport's default frame-buffer list. Not all of them might be available. */
|
||||
DefaultFramebufferList *viewport_framebuffer_list_get() const;
|
||||
/** Returns the viewport's default texture list. Not all of them might be available. */
|
||||
DefaultTextureList *viewport_texture_list_get() const;
|
||||
|
||||
const enum Mode {
|
||||
/** Render for display of 2D or 3D area. Runs on main thread. */
|
||||
VIEWPORT = 0,
|
||||
|
||||
/* These viewport modes will render without some overlays (i.e. no text). */
|
||||
|
||||
/** Render for a 3D viewport in XR. Runs on main thread. */
|
||||
VIEWPORT_XR,
|
||||
/** Render for a 3D viewport offscreen render (python). Runs on main thread. */
|
||||
VIEWPORT_OFFSCREEN,
|
||||
/**
|
||||
* Render for a 3D viewport image render (Render Viewport Preview, also VSE scene strips).
|
||||
* Runs on main thread.
|
||||
*/
|
||||
VIEWPORT_RENDER,
|
||||
|
||||
/** Render for object mode selection. Runs on main thread. */
|
||||
SELECT_OBJECT,
|
||||
/** Render for object material selection. Runs on main thread. */
|
||||
SELECT_OBJECT_MATERIAL,
|
||||
/** Render for edit mesh selection. Runs on main thread. */
|
||||
SELECT_EDIT_MESH,
|
||||
|
||||
/** Render for depth picking (auto-depth). Runs on main thread. */
|
||||
DEPTH,
|
||||
DEPTH_ACTIVE_OBJECT,
|
||||
|
||||
/** Render for F12 final render. Can run in any thread. */
|
||||
RENDER,
|
||||
/** Used by custom pipeline. Can run in any thread. */
|
||||
CUSTOM,
|
||||
} mode;
|
||||
|
||||
struct {
|
||||
bool draw_background = false;
|
||||
} options;
|
||||
|
||||
/** Convenience pointer to text_store owned by the viewport */
|
||||
DRWTextStore **text_store_p = nullptr;
|
||||
|
||||
/** Contains list of objects that needs to be extracted from other objects. */
|
||||
Set<Object *> delayed_extraction;
|
||||
|
||||
/* TODO(fclem): Public. */
|
||||
|
||||
/** Current rendering context. Avoid too many lookups while drawing. */
|
||||
|
||||
/** Evaluated Depsgraph. */
|
||||
Depsgraph *depsgraph = nullptr;
|
||||
/** Evaluated Scene. */
|
||||
Scene *scene = nullptr;
|
||||
/** Evaluated ViewLayer. */
|
||||
ViewLayer *view_layer = nullptr;
|
||||
|
||||
/** Last resort (some functions take this as an arg so we can't easily avoid).
|
||||
* May be nullptr when used for selection or depth buffer. */
|
||||
const bContext *evil_C = nullptr;
|
||||
/** Can be nullptr depending on context. */
|
||||
ARegion *region = nullptr;
|
||||
/** Can be nullptr depending on context. */
|
||||
SpaceLink *space_data = nullptr;
|
||||
/** Can be nullptr depending on context. */
|
||||
RegionView3D *rv3d = nullptr;
|
||||
/** Can be nullptr depending on context. */
|
||||
View3D *v3d = nullptr;
|
||||
/** Use 'object_edit' for edit-mode */
|
||||
Object *obact = nullptr;
|
||||
Object *object_pose = nullptr;
|
||||
Object *object_edit = nullptr;
|
||||
|
||||
eObjectMode object_mode = OB_MODE_OBJECT;
|
||||
|
||||
public:
|
||||
/**
|
||||
* If `viewport` is not specified, `DRWData` will be considered temporary and discarded on exit.
|
||||
* If `C` is nullptr, it means that the context is **not** associated with any UI or operator.
|
||||
* If `region` is nullptr, it will be sourced from the context `C` or left as nullptr otherwise.
|
||||
* If `v3d` is nullptr, it will be sourced from the context `C` or left as nullptr otherwise.
|
||||
*/
|
||||
DRWContext(Mode mode,
|
||||
Depsgraph *depsgraph,
|
||||
GPUViewport *viewport,
|
||||
const bContext *C = nullptr,
|
||||
ARegion *region = nullptr,
|
||||
View3D *v3d = nullptr);
|
||||
DRWContext(Mode mode,
|
||||
Depsgraph *depsgraph,
|
||||
const int2 size = {1, 1},
|
||||
const bContext *C = nullptr,
|
||||
ARegion *region = nullptr,
|
||||
View3D *v3d = nullptr);
|
||||
|
||||
~DRWContext();
|
||||
|
||||
/**
|
||||
* Acquire `data` and `view_data_active`.
|
||||
* Needs to be called before enabling any draw engine.
|
||||
* IMPORTANT: This can be called multiple times before release_data.
|
||||
* IMPORTANT: This must be called with an active GPUContext.
|
||||
*/
|
||||
void acquire_data();
|
||||
|
||||
/**
|
||||
* Make sure to release acquired DRWData. If created on the fly, make sure to destroy them.
|
||||
* IMPORTANT: This needs to be called with the same active GPUContext `acquire_data()` was called
|
||||
* with.
|
||||
*/
|
||||
void release_data();
|
||||
|
||||
/**
|
||||
* Enable engines from context. Not needed for Mode::RENDER and Mode::CUSTOM.
|
||||
*
|
||||
* `render_engine_type` specify the engine to use in OB_MATERIAL or OB_RENDER modes.
|
||||
* `gpencil_engine_needed` should be set to true if the grease pencil engine is needed.
|
||||
*/
|
||||
void enable_engines(bool gpencil_engine_needed = false,
|
||||
RenderEngineType *render_engine_type = nullptr);
|
||||
|
||||
/** Free unused engine data. */
|
||||
void engines_data_validate();
|
||||
|
||||
using iter_callback_t =
|
||||
::std::function<void(struct DupliCacheManager &, struct ExtractionGraph &)>;
|
||||
|
||||
/** Run the sync phase with data extraction. iter_callback defines which object to sync. */
|
||||
void sync(iter_callback_t iter_callback);
|
||||
/** Run enabled engine init and sync callbacks. iter_callback defines which object to sync. */
|
||||
void engines_init_and_sync(iter_callback_t iter_callback);
|
||||
/** Run enabled engine init and draw scene callbacks. */
|
||||
void engines_draw_scene();
|
||||
|
||||
static DRWContext &get_active()
|
||||
{
|
||||
return *g_context;
|
||||
}
|
||||
|
||||
float2 viewport_size_get() const
|
||||
{
|
||||
return size;
|
||||
}
|
||||
|
||||
/** Return true if any #DRWContext is active on this thread. */
|
||||
static bool is_active()
|
||||
{
|
||||
return g_context != nullptr;
|
||||
}
|
||||
|
||||
bool is_select() const
|
||||
{
|
||||
return ELEM(mode, SELECT_OBJECT, SELECT_OBJECT_MATERIAL, SELECT_EDIT_MESH);
|
||||
}
|
||||
bool is_material_select() const
|
||||
{
|
||||
return ELEM(mode, SELECT_OBJECT_MATERIAL);
|
||||
}
|
||||
bool is_depth() const
|
||||
{
|
||||
return ELEM(mode, DEPTH, DEPTH_ACTIVE_OBJECT);
|
||||
}
|
||||
bool is_image_render() const
|
||||
{
|
||||
return ELEM(mode, VIEWPORT_RENDER, RENDER);
|
||||
}
|
||||
bool is_scene_render() const
|
||||
{
|
||||
return ELEM(mode, RENDER);
|
||||
}
|
||||
bool is_viewport_image_render() const
|
||||
{
|
||||
return ELEM(mode, VIEWPORT_RENDER);
|
||||
}
|
||||
bool is_viewport_xr() const
|
||||
{
|
||||
return ELEM(mode, VIEWPORT_XR);
|
||||
}
|
||||
float last_sync_time() const
|
||||
{
|
||||
return last_sync_time_;
|
||||
}
|
||||
float last_submission_time() const
|
||||
{
|
||||
return last_submission_time_;
|
||||
}
|
||||
|
||||
/** True if current viewport is drawn during playback. */
|
||||
bool is_playback() const;
|
||||
/** True if current viewport is drawn during navigation operator. */
|
||||
bool is_navigating() const;
|
||||
/** True if current viewport is drawn during painting operator. */
|
||||
bool is_painting() const;
|
||||
/** True if current viewport is drawn during transforming operator. */
|
||||
bool is_transforming() const;
|
||||
/** True if viewport compositor is enabled when drawing with this context. */
|
||||
bool is_viewport_compositor_enabled() const;
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
const DRWContext *DRW_context_get();
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,57 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "BLI_array_utils.hh"
|
||||
|
||||
#include "BKE_attribute_math.hh"
|
||||
|
||||
#include "GPU_vertex_buffer.hh"
|
||||
|
||||
#include "attribute_convert.hh"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
GPUVertFormat init_format_for_attribute(const bke::AttrType data_type, const StringRef vbo_name)
|
||||
{
|
||||
GPUVertFormat format{};
|
||||
bke::attribute_math::to_static_type(data_type, [&]<typename T>() {
|
||||
using Converter = AttributeConverter<T>;
|
||||
if constexpr (!std::is_void_v<typename Converter::VBOType>) {
|
||||
GPU_vertformat_attr_add_legacy(&format,
|
||||
vbo_name,
|
||||
Converter::gpu_component_type,
|
||||
Converter::gpu_component_len,
|
||||
Converter::gpu_fetch_mode);
|
||||
}
|
||||
});
|
||||
return format;
|
||||
}
|
||||
|
||||
void vertbuf_data_extract_direct(const GSpan attribute, gpu::VertBuf &vbo)
|
||||
{
|
||||
bke::attribute_math::to_static_type(attribute.type(), [&]<typename T>() {
|
||||
using Converter = AttributeConverter<T>;
|
||||
using VBOType = typename Converter::VBOType;
|
||||
if constexpr (!std::is_void_v<VBOType>) {
|
||||
const Span<T> src = attribute.typed<T>();
|
||||
MutableSpan<VBOType> data = vbo.data<VBOType>();
|
||||
if constexpr (std::is_same_v<T, VBOType>) {
|
||||
array_utils::copy(src, data);
|
||||
}
|
||||
else {
|
||||
threading::parallel_for(src.index_range(), 8192, [&](const IndexRange range) {
|
||||
for (const int i : range) {
|
||||
data[i] = Converter::convert(src[i]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace blender::draw
|
||||
170
blender-5.2.0/source/blender/draw/intern/attribute_convert.hh
Normal file
170
blender-5.2.0/source/blender/draw/intern/attribute_convert.hh
Normal file
@@ -0,0 +1,170 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "BLI_color_types.hh"
|
||||
#include "BLI_generic_span.hh"
|
||||
#include "BLI_math_color.h"
|
||||
#include "BLI_math_quaternion_types.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
#include "GPU_vertex_format.hh"
|
||||
|
||||
#include "IMB_colormanagement.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace gpu {
|
||||
class VertBuf;
|
||||
}
|
||||
|
||||
namespace bke {
|
||||
enum class AttrType : int16_t;
|
||||
}
|
||||
|
||||
/**
|
||||
* Component length of 3 is used for scalars because implicit conversion is done by OpenGL from a
|
||||
* scalar `s` will produce `float4(s, 0, 0, 1)`. However, following the Blender convention, it
|
||||
* should be `float4(s, s, s, 1)`.
|
||||
*/
|
||||
constexpr int COMPONENT_LEN_SCALAR = 3;
|
||||
|
||||
namespace draw {
|
||||
|
||||
/**
|
||||
* Utility to convert from the type used in the attributes to the types for GPU vertex buffers.
|
||||
*/
|
||||
template<typename T> struct AttributeConverter {
|
||||
using VBOType = void;
|
||||
};
|
||||
|
||||
template<> struct AttributeConverter<bool> {
|
||||
using VBOType = VecBase<float, COMPONENT_LEN_SCALAR>;
|
||||
static constexpr GPUVertCompType gpu_component_type = GPU_COMP_F32;
|
||||
static constexpr int gpu_component_len = COMPONENT_LEN_SCALAR;
|
||||
static constexpr GPUVertFetchMode gpu_fetch_mode = GPU_FETCH_FLOAT;
|
||||
static VBOType convert(const bool &value)
|
||||
{
|
||||
return VBOType(value);
|
||||
}
|
||||
};
|
||||
template<> struct AttributeConverter<int8_t> {
|
||||
using VBOType = VecBase<float, COMPONENT_LEN_SCALAR>;
|
||||
static constexpr GPUVertCompType gpu_component_type = GPU_COMP_F32;
|
||||
static constexpr int gpu_component_len = COMPONENT_LEN_SCALAR;
|
||||
static constexpr GPUVertFetchMode gpu_fetch_mode = GPU_FETCH_FLOAT;
|
||||
static VBOType convert(const int8_t &value)
|
||||
{
|
||||
return VecBase<float, COMPONENT_LEN_SCALAR>(value);
|
||||
}
|
||||
};
|
||||
template<> struct AttributeConverter<int> {
|
||||
using VBOType = VecBase<float, COMPONENT_LEN_SCALAR>;
|
||||
static constexpr GPUVertCompType gpu_component_type = GPU_COMP_F32;
|
||||
static constexpr int gpu_component_len = COMPONENT_LEN_SCALAR;
|
||||
static constexpr GPUVertFetchMode gpu_fetch_mode = GPU_FETCH_FLOAT;
|
||||
static VBOType convert(const int &value)
|
||||
{
|
||||
return VecBase<float, COMPONENT_LEN_SCALAR>(value);
|
||||
}
|
||||
};
|
||||
template<> struct AttributeConverter<int2> {
|
||||
using VBOType = float2;
|
||||
static constexpr GPUVertCompType gpu_component_type = GPU_COMP_F32;
|
||||
static constexpr int gpu_component_len = 2;
|
||||
static constexpr GPUVertFetchMode gpu_fetch_mode = GPU_FETCH_FLOAT;
|
||||
static VBOType convert(const int2 &value)
|
||||
{
|
||||
return float2(value.x, value.y);
|
||||
}
|
||||
};
|
||||
template<> struct AttributeConverter<float> {
|
||||
using VBOType = VecBase<float, COMPONENT_LEN_SCALAR>;
|
||||
static constexpr GPUVertCompType gpu_component_type = GPU_COMP_F32;
|
||||
static constexpr int gpu_component_len = COMPONENT_LEN_SCALAR;
|
||||
static constexpr GPUVertFetchMode gpu_fetch_mode = GPU_FETCH_FLOAT;
|
||||
static VBOType convert(const float &value)
|
||||
{
|
||||
return VBOType(value);
|
||||
}
|
||||
};
|
||||
template<> struct AttributeConverter<float2> {
|
||||
using VBOType = float2;
|
||||
static constexpr GPUVertCompType gpu_component_type = GPU_COMP_F32;
|
||||
static constexpr int gpu_component_len = 2;
|
||||
static constexpr GPUVertFetchMode gpu_fetch_mode = GPU_FETCH_FLOAT;
|
||||
static VBOType convert(const float2 &value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
};
|
||||
template<> struct AttributeConverter<float3> {
|
||||
using VBOType = float3;
|
||||
static constexpr GPUVertCompType gpu_component_type = GPU_COMP_F32;
|
||||
static constexpr int gpu_component_len = 3;
|
||||
static constexpr GPUVertFetchMode gpu_fetch_mode = GPU_FETCH_FLOAT;
|
||||
static VBOType convert(const float3 &value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
};
|
||||
template<> struct AttributeConverter<ColorGeometry4b> {
|
||||
/* 16 bits are required to store the color in linear space without precision loss. */
|
||||
using VBOType = ushort4;
|
||||
static constexpr GPUVertCompType gpu_component_type = GPU_COMP_U16;
|
||||
static constexpr int gpu_component_len = 4;
|
||||
static constexpr GPUVertFetchMode gpu_fetch_mode = GPU_FETCH_INT_TO_FLOAT_UNIT;
|
||||
static VBOType convert(const ColorGeometry4b &value)
|
||||
{
|
||||
float3 rgb = {BLI_color_from_srgb_table[value.r],
|
||||
BLI_color_from_srgb_table[value.g],
|
||||
BLI_color_from_srgb_table[value.b]};
|
||||
IMB_colormanagement_rec709_to_scene_linear(rgb, rgb);
|
||||
return {unit_float_to_ushort_clamp(rgb[0]),
|
||||
unit_float_to_ushort_clamp(rgb[1]),
|
||||
unit_float_to_ushort_clamp(rgb[2]),
|
||||
ushort(value.a * 257)};
|
||||
}
|
||||
};
|
||||
template<> struct AttributeConverter<ColorGeometry4f> {
|
||||
using VBOType = ColorGeometry4f;
|
||||
static constexpr GPUVertCompType gpu_component_type = GPU_COMP_F32;
|
||||
static constexpr int gpu_component_len = 4;
|
||||
static constexpr GPUVertFetchMode gpu_fetch_mode = GPU_FETCH_FLOAT;
|
||||
static VBOType convert(const ColorGeometry4f &value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
};
|
||||
template<> struct AttributeConverter<math::Quaternion> {
|
||||
using VBOType = float4;
|
||||
static constexpr GPUVertCompType gpu_component_type = GPU_COMP_F32;
|
||||
static constexpr int gpu_component_len = 4;
|
||||
static constexpr GPUVertFetchMode gpu_fetch_mode = GPU_FETCH_FLOAT;
|
||||
static VBOType convert(const math::Quaternion &value)
|
||||
{
|
||||
return float4(value.w, value.x, value.y, value.z);
|
||||
}
|
||||
};
|
||||
template<> struct AttributeConverter<float4> {
|
||||
using VBOType = float4;
|
||||
static constexpr GPUVertCompType gpu_component_type = GPU_COMP_F32;
|
||||
static constexpr int gpu_component_len = 4;
|
||||
static constexpr GPUVertFetchMode gpu_fetch_mode = GPU_FETCH_FLOAT;
|
||||
static VBOType convert(const float4 &value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
GPUVertFormat init_format_for_attribute(bke::AttrType data_type, StringRef vbo_name);
|
||||
|
||||
void vertbuf_data_extract_direct(GSpan attribute, gpu::VertBuf &vbo);
|
||||
|
||||
} // namespace draw
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,103 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef GPU_SHADER
|
||||
# include "GPU_shader_shared_utils.hh"
|
||||
#endif
|
||||
|
||||
/* Copy of DNA enum in `DNA_curves_types.h`. */
|
||||
enum [[host_shared]] CurveType : uint32_t {
|
||||
CURVE_TYPE_CATMULL_ROM = 0u,
|
||||
CURVE_TYPE_POLY = 1u,
|
||||
CURVE_TYPE_BEZIER = 2u,
|
||||
CURVE_TYPE_NURBS = 3u,
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Generic Attribute
|
||||
*
|
||||
* These types are necessary to overcome the issue with float3 alignment on GPU.
|
||||
* Having all types using the same interface allows the usage of templates to load and manipulate
|
||||
* them inside the shaders.
|
||||
* \{ */
|
||||
|
||||
struct [[host_shared]] StoredFloat4 {
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
float w;
|
||||
};
|
||||
|
||||
struct [[host_shared]] StoredFloat3 {
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
};
|
||||
|
||||
struct [[host_shared]] StoredFloat2 {
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
|
||||
struct [[host_shared]] StoredFloat {
|
||||
float x;
|
||||
};
|
||||
|
||||
float4 load_data(StoredFloat4 data)
|
||||
{
|
||||
return float4(data.x, data.y, data.z, data.w);
|
||||
}
|
||||
|
||||
float3 load_data(StoredFloat3 data)
|
||||
{
|
||||
return float3(data.x, data.y, data.z);
|
||||
}
|
||||
|
||||
float2 load_data(StoredFloat2 data)
|
||||
{
|
||||
return float2(data.x, data.y);
|
||||
}
|
||||
|
||||
float load_data(StoredFloat data)
|
||||
{
|
||||
return float(data.x);
|
||||
}
|
||||
|
||||
StoredFloat4 as_data(float4 interp)
|
||||
{
|
||||
StoredFloat4 data;
|
||||
data.x = interp.x;
|
||||
data.y = interp.y;
|
||||
data.z = interp.z;
|
||||
data.w = interp.w;
|
||||
return data;
|
||||
}
|
||||
|
||||
StoredFloat3 as_data(float3 interp)
|
||||
{
|
||||
StoredFloat3 data;
|
||||
data.x = interp.x;
|
||||
data.y = interp.y;
|
||||
data.z = interp.z;
|
||||
return data;
|
||||
}
|
||||
|
||||
StoredFloat2 as_data(float2 interp)
|
||||
{
|
||||
StoredFloat2 data;
|
||||
data.x = interp.x;
|
||||
data.y = interp.y;
|
||||
return data;
|
||||
}
|
||||
|
||||
StoredFloat as_data(float interp)
|
||||
{
|
||||
StoredFloat data;
|
||||
data.x = interp;
|
||||
return data;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
35
blender-5.2.0/source/blender/draw/intern/draw_attributes.cc
Normal file
35
blender-5.2.0/source/blender/draw/intern/draw_attributes.cc
Normal file
@@ -0,0 +1,35 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "GPU_shader.hh"
|
||||
|
||||
#include "draw_attributes.hh"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
void drw_attributes_merge(VectorSet<std::string> *dst, const VectorSet<std::string> *src)
|
||||
{
|
||||
dst->add_multiple(src->as_span());
|
||||
}
|
||||
|
||||
bool drw_attributes_overlap(const VectorSet<std::string> *a, const VectorSet<std::string> *b)
|
||||
{
|
||||
for (const std::string &req : b->as_span()) {
|
||||
if (!a->contains(req)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void drw_attributes_add_request(VectorSet<std::string> *attrs, const StringRef name)
|
||||
{
|
||||
if (attrs->size() >= GPU_MAX_ATTR) {
|
||||
return;
|
||||
}
|
||||
attrs->add_as(name);
|
||||
}
|
||||
|
||||
} // namespace blender::draw
|
||||
57
blender-5.2.0/source/blender/draw/intern/draw_attributes.hh
Normal file
57
blender-5.2.0/source/blender/draw/intern/draw_attributes.hh
Normal file
@@ -0,0 +1,57 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*
|
||||
* \brief Utilities for rendering attributes.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "BLI_string_ref.hh"
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
#include "BLI_vector_set.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace bke {
|
||||
enum class AttrDomain : int8_t;
|
||||
}
|
||||
|
||||
namespace draw {
|
||||
|
||||
struct DRW_MeshCDMask {
|
||||
VectorSet<std::string> uv;
|
||||
VectorSet<std::string> tan;
|
||||
bool orco = false;
|
||||
bool tan_orco = false;
|
||||
bool sculpt_overlays = false;
|
||||
bool edit_uv = false;
|
||||
|
||||
friend bool operator==(const DRW_MeshCDMask &a, const DRW_MeshCDMask &b) = default;
|
||||
|
||||
void clear()
|
||||
{
|
||||
uv.clear_and_keep_capacity();
|
||||
tan.clear_and_keep_capacity();
|
||||
orco = false;
|
||||
tan_orco = false;
|
||||
sculpt_overlays = false;
|
||||
edit_uv = false;
|
||||
}
|
||||
};
|
||||
|
||||
void drw_attributes_merge(VectorSet<std::string> *dst, const VectorSet<std::string> *src);
|
||||
|
||||
/* Return true if all requests in b are in a. */
|
||||
bool drw_attributes_overlap(const VectorSet<std::string> *a, const VectorSet<std::string> *b);
|
||||
|
||||
void drw_attributes_add_request(VectorSet<std::string> *attrs, StringRef name);
|
||||
|
||||
} // namespace draw
|
||||
} // namespace blender
|
||||
622
blender-5.2.0/source/blender/draw/intern/draw_cache.cc
Normal file
622
blender-5.2.0/source/blender/draw/intern/draw_cache.cc
Normal file
@@ -0,0 +1,622 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "DNA_curve_types.h"
|
||||
#include "DNA_curves_types.h"
|
||||
#include "DNA_grease_pencil_types.h"
|
||||
#include "DNA_lattice_types.h"
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_modifier_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_particle_types.h"
|
||||
#include "DNA_pointcloud_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_volume_types.h"
|
||||
|
||||
#include "BLI_ghash.h"
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_mesh_wrapper.hh"
|
||||
#include "BKE_object.hh"
|
||||
#include "BKE_object_types.hh"
|
||||
#include "BKE_subdiv_modifier.hh"
|
||||
|
||||
#include "DRW_render.hh"
|
||||
#include "GPU_batch.hh"
|
||||
#include "GPU_batch_utils.hh"
|
||||
#include "GPU_capabilities.hh"
|
||||
|
||||
#include "draw_cache.hh"
|
||||
#include "draw_cache_impl.hh"
|
||||
#include "draw_context_private.hh"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Internal Defines
|
||||
* \{ */
|
||||
|
||||
#define VCLASS_LIGHT_AREA_SHAPE (1 << 0)
|
||||
#define VCLASS_LIGHT_SPOT_SHAPE (1 << 1)
|
||||
#define VCLASS_LIGHT_SPOT_BLEND (1 << 2)
|
||||
#define VCLASS_LIGHT_SPOT_CONE (1 << 3)
|
||||
#define VCLASS_LIGHT_DIST (1 << 4)
|
||||
|
||||
#define VCLASS_CAMERA_FRAME (1 << 5)
|
||||
#define VCLASS_CAMERA_DIST (1 << 6)
|
||||
#define VCLASS_CAMERA_VOLUME (1 << 7)
|
||||
|
||||
#define VCLASS_SCREENSPACE (1 << 8)
|
||||
#define VCLASS_SCREENALIGNED (1 << 9)
|
||||
|
||||
#define VCLASS_EMPTY_SCALED (1 << 10)
|
||||
#define VCLASS_EMPTY_AXES (1 << 11)
|
||||
#define VCLASS_EMPTY_AXES_NAME (1 << 12)
|
||||
#define VCLASS_EMPTY_AXES_SHADOW (1 << 13)
|
||||
#define VCLASS_EMPTY_SIZE (1 << 14)
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Common
|
||||
* \{ */
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
void DRW_vertbuf_create_wiredata(gpu::VertBuf *vbo, const int vert_len)
|
||||
{
|
||||
static const GPUVertFormat format = GPU_vertformat_from_attribute("wd",
|
||||
gpu::VertAttrType::SFLOAT_32);
|
||||
GPU_vertbuf_init_with_format(*vbo, format);
|
||||
GPU_vertbuf_data_alloc(*vbo, vert_len);
|
||||
vbo->data<float>().fill(1.0f);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Common Object API
|
||||
*
|
||||
* \note Curve and text objects evaluate to the evaluated geometry set's mesh component if
|
||||
* they have a surface, so curve objects themselves do not have a surface (the mesh component
|
||||
* is presented to render engines as a separate object).
|
||||
* \{ */
|
||||
|
||||
gpu::Batch *DRW_cache_object_all_edges_get(Object *ob)
|
||||
{
|
||||
switch (ob->type) {
|
||||
case OB_MESH:
|
||||
return DRW_cache_mesh_all_edges_get(ob);
|
||||
/* TODO: should match #DRW_cache_object_surface_get. */
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
gpu::Batch *DRW_cache_object_edge_detection_get(Object *ob, bool *r_is_manifold)
|
||||
{
|
||||
switch (ob->type) {
|
||||
case OB_MESH:
|
||||
return DRW_cache_mesh_edge_detection_get(ob, r_is_manifold);
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
gpu::Batch *DRW_cache_object_face_wireframe_get(const Scene *scene, Object *ob)
|
||||
{
|
||||
switch (ob->type) {
|
||||
case OB_MESH:
|
||||
return DRW_cache_mesh_face_wireframe_get(ob);
|
||||
case OB_POINTCLOUD:
|
||||
return DRW_pointcloud_batch_cache_get_dots(ob);
|
||||
case OB_VOLUME:
|
||||
return DRW_cache_volume_face_wireframe_get(ob);
|
||||
case OB_GREASE_PENCIL:
|
||||
return DRW_cache_grease_pencil_face_wireframe_get(scene, ob);
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
gpu::Batch *DRW_cache_object_loose_edges_get(Object *ob)
|
||||
{
|
||||
switch (ob->type) {
|
||||
case OB_MESH:
|
||||
return DRW_cache_mesh_loose_edges_get(ob);
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
gpu::Batch *DRW_cache_object_surface_get(Object *ob)
|
||||
{
|
||||
switch (ob->type) {
|
||||
case OB_MESH:
|
||||
return DRW_cache_mesh_surface_get(ob);
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Span<gpu::Batch *> DRW_cache_object_surface_material_get(Object *ob,
|
||||
const Span<const GPUMaterial *> materials)
|
||||
{
|
||||
switch (ob->type) {
|
||||
case OB_MESH:
|
||||
return DRW_cache_mesh_surface_shaded_get(ob, materials);
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Meshes
|
||||
* \{ */
|
||||
|
||||
gpu::Batch *DRW_cache_mesh_all_verts_get(Object *ob)
|
||||
{
|
||||
BLI_assert(ob->type == OB_MESH);
|
||||
return DRW_mesh_batch_cache_get_all_verts(DRW_object_get_data_for_drawing<Mesh>(*ob));
|
||||
}
|
||||
|
||||
gpu::Batch *DRW_cache_mesh_paint_overlay_verts_get(Object *ob)
|
||||
{
|
||||
BLI_assert(ob->type == OB_MESH);
|
||||
return DRW_mesh_batch_cache_get_paint_overlay_verts(DRW_object_get_data_for_drawing<Mesh>(*ob));
|
||||
}
|
||||
|
||||
gpu::Batch *DRW_cache_mesh_all_edges_get(Object *ob)
|
||||
{
|
||||
BLI_assert(ob->type == OB_MESH);
|
||||
return DRW_mesh_batch_cache_get_all_edges(DRW_object_get_data_for_drawing<Mesh>(*ob));
|
||||
}
|
||||
|
||||
gpu::Batch *DRW_cache_mesh_loose_edges_get(Object *ob)
|
||||
{
|
||||
BLI_assert(ob->type == OB_MESH);
|
||||
return DRW_mesh_batch_cache_get_loose_edges(DRW_object_get_data_for_drawing<Mesh>(*ob));
|
||||
}
|
||||
|
||||
gpu::Batch *DRW_cache_mesh_edge_detection_get(Object *ob, bool *r_is_manifold)
|
||||
{
|
||||
BLI_assert(ob->type == OB_MESH);
|
||||
return DRW_mesh_batch_cache_get_edge_detection(DRW_object_get_data_for_drawing<Mesh>(*ob),
|
||||
r_is_manifold);
|
||||
}
|
||||
|
||||
gpu::Batch *DRW_cache_mesh_surface_get(Object *ob)
|
||||
{
|
||||
BLI_assert(ob->type == OB_MESH);
|
||||
return DRW_mesh_batch_cache_get_surface(DRW_object_get_data_for_drawing<Mesh>(*ob));
|
||||
}
|
||||
|
||||
gpu::Batch *DRW_cache_mesh_paint_overlay_surface_get(Object *ob)
|
||||
{
|
||||
BLI_assert(ob->type == OB_MESH);
|
||||
return DRW_mesh_batch_cache_get_paint_overlay_surface(
|
||||
DRW_object_get_data_for_drawing<Mesh>(*ob));
|
||||
}
|
||||
|
||||
gpu::Batch *DRW_cache_mesh_paint_overlay_edges_get(Object *ob)
|
||||
{
|
||||
BLI_assert(ob->type == OB_MESH);
|
||||
return DRW_mesh_batch_cache_get_paint_overlay_edges(DRW_object_get_data_for_drawing<Mesh>(*ob));
|
||||
}
|
||||
|
||||
Span<gpu::Batch *> DRW_cache_mesh_surface_shaded_get(Object *ob,
|
||||
const Span<const GPUMaterial *> materials)
|
||||
{
|
||||
BLI_assert(ob->type == OB_MESH);
|
||||
return DRW_mesh_batch_cache_get_surface_shaded(
|
||||
*ob, DRW_object_get_data_for_drawing<Mesh>(*ob), materials);
|
||||
}
|
||||
|
||||
Span<gpu::Batch *> DRW_cache_mesh_surface_texpaint_get(Object *ob)
|
||||
{
|
||||
BLI_assert(ob->type == OB_MESH);
|
||||
return DRW_mesh_batch_cache_get_surface_texpaint(*ob,
|
||||
DRW_object_get_data_for_drawing<Mesh>(*ob));
|
||||
}
|
||||
|
||||
gpu::Batch *DRW_cache_mesh_surface_texpaint_single_get(Object *ob)
|
||||
{
|
||||
BLI_assert(ob->type == OB_MESH);
|
||||
return DRW_mesh_batch_cache_get_surface_texpaint_single(
|
||||
*ob, DRW_object_get_data_for_drawing<Mesh>(*ob));
|
||||
}
|
||||
|
||||
gpu::Batch *DRW_cache_mesh_surface_vertpaint_get(Object *ob)
|
||||
{
|
||||
BLI_assert(ob->type == OB_MESH);
|
||||
return DRW_mesh_batch_cache_get_surface_vertpaint(*ob,
|
||||
DRW_object_get_data_for_drawing<Mesh>(*ob));
|
||||
}
|
||||
|
||||
gpu::Batch *DRW_cache_mesh_surface_sculptcolors_get(Object *ob)
|
||||
{
|
||||
BLI_assert(ob->type == OB_MESH);
|
||||
return DRW_mesh_batch_cache_get_surface_sculpt(*ob, DRW_object_get_data_for_drawing<Mesh>(*ob));
|
||||
}
|
||||
|
||||
gpu::Batch *DRW_cache_mesh_surface_weights_get(Object *ob)
|
||||
{
|
||||
BLI_assert(ob->type == OB_MESH);
|
||||
return DRW_mesh_batch_cache_get_surface_weights(DRW_object_get_data_for_drawing<Mesh>(*ob));
|
||||
}
|
||||
|
||||
gpu::Batch *DRW_cache_mesh_face_wireframe_get(Object *ob)
|
||||
{
|
||||
BLI_assert(ob->type == OB_MESH);
|
||||
return DRW_mesh_batch_cache_get_wireframes_face(DRW_object_get_data_for_drawing<Mesh>(*ob));
|
||||
}
|
||||
|
||||
gpu::Batch *DRW_cache_mesh_surface_mesh_analysis_get(Object *ob)
|
||||
{
|
||||
BLI_assert(ob->type == OB_MESH);
|
||||
return DRW_mesh_batch_cache_get_edit_mesh_analysis(DRW_object_get_data_for_drawing<Mesh>(*ob));
|
||||
}
|
||||
|
||||
gpu::Batch *DRW_cache_mesh_surface_viewer_attribute_get(Object *ob)
|
||||
{
|
||||
BLI_assert(ob->type == OB_MESH);
|
||||
return DRW_mesh_batch_cache_get_surface_viewer_attribute(
|
||||
DRW_object_get_data_for_drawing<Mesh>(*ob));
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Curve
|
||||
* \{ */
|
||||
|
||||
gpu::Batch *DRW_cache_curve_edge_wire_get(Object *ob)
|
||||
{
|
||||
BLI_assert(ob->type == OB_CURVES_LEGACY);
|
||||
Curve &cu = DRW_object_get_data_for_drawing<Curve>(*ob);
|
||||
return DRW_curve_batch_cache_get_wire_edge(&cu);
|
||||
}
|
||||
|
||||
gpu::Batch *DRW_cache_curve_edge_wire_viewer_attribute_get(Object *ob)
|
||||
{
|
||||
BLI_assert(ob->type == OB_CURVES_LEGACY);
|
||||
Curve &cu = DRW_object_get_data_for_drawing<Curve>(*ob);
|
||||
return DRW_curve_batch_cache_get_wire_edge_viewer_attribute(&cu);
|
||||
}
|
||||
|
||||
gpu::Batch *DRW_cache_curve_edge_normal_get(Object *ob)
|
||||
{
|
||||
BLI_assert(ob->type == OB_CURVES_LEGACY);
|
||||
Curve &cu = DRW_object_get_data_for_drawing<Curve>(*ob);
|
||||
return DRW_curve_batch_cache_get_normal_edge(&cu);
|
||||
}
|
||||
|
||||
gpu::Batch *DRW_cache_curve_edge_overlay_get(Object *ob)
|
||||
{
|
||||
BLI_assert(ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF));
|
||||
|
||||
Curve &cu = DRW_object_get_data_for_drawing<Curve>(*ob);
|
||||
return DRW_curve_batch_cache_get_edit_edges(&cu);
|
||||
}
|
||||
|
||||
gpu::Batch *DRW_cache_curve_vert_overlay_get(Object *ob)
|
||||
{
|
||||
BLI_assert(ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF));
|
||||
|
||||
Curve &cu = DRW_object_get_data_for_drawing<Curve>(*ob);
|
||||
return DRW_curve_batch_cache_get_edit_verts(&cu);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Font
|
||||
* \{ */
|
||||
|
||||
gpu::Batch *DRW_cache_text_edge_wire_get(Object *ob)
|
||||
{
|
||||
BLI_assert(ob->type == OB_FONT);
|
||||
Curve &cu = DRW_object_get_data_for_drawing<Curve>(*ob);
|
||||
return DRW_curve_batch_cache_get_wire_edge(&cu);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Surface
|
||||
* \{ */
|
||||
|
||||
gpu::Batch *DRW_cache_surf_edge_wire_get(Object *ob)
|
||||
{
|
||||
BLI_assert(ob->type == OB_SURF);
|
||||
Curve &cu = DRW_object_get_data_for_drawing<Curve>(*ob);
|
||||
return DRW_curve_batch_cache_get_wire_edge(&cu);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Lattice
|
||||
* \{ */
|
||||
|
||||
gpu::Batch *DRW_cache_lattice_verts_get(Object *ob)
|
||||
{
|
||||
BLI_assert(ob->type == OB_LATTICE);
|
||||
|
||||
Lattice < = DRW_object_get_data_for_drawing<Lattice>(*ob);
|
||||
return DRW_lattice_batch_cache_get_all_verts(<);
|
||||
}
|
||||
|
||||
gpu::Batch *DRW_cache_lattice_wire_get(Object *ob, bool use_weight)
|
||||
{
|
||||
BLI_assert(ob->type == OB_LATTICE);
|
||||
|
||||
Lattice < = DRW_object_get_data_for_drawing<Lattice>(*ob);
|
||||
int actdef = -1;
|
||||
|
||||
if (use_weight && !lt.vertex_group_names.is_empty() && lt.editlatt && lt.editlatt->latt->dvert) {
|
||||
actdef = lt.vertex_group_active_index - 1;
|
||||
}
|
||||
|
||||
return DRW_lattice_batch_cache_get_all_edges(<, use_weight, actdef);
|
||||
}
|
||||
|
||||
gpu::Batch *DRW_cache_lattice_vert_overlay_get(Object *ob)
|
||||
{
|
||||
BLI_assert(ob->type == OB_LATTICE);
|
||||
|
||||
Lattice < = DRW_object_get_data_for_drawing<Lattice>(*ob);
|
||||
return DRW_lattice_batch_cache_get_edit_verts(<);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name PointCloud
|
||||
* \{ */
|
||||
|
||||
gpu::Batch *DRW_cache_pointcloud_vert_overlay_get(Object *ob)
|
||||
{
|
||||
BLI_assert(ob->type == OB_POINTCLOUD);
|
||||
|
||||
PointCloud &pointcloud = DRW_object_get_data_for_drawing<PointCloud>(*ob);
|
||||
return DRW_pointcloud_batch_cache_get_edit_dots(&pointcloud);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Volume
|
||||
* \{ */
|
||||
|
||||
gpu::Batch *DRW_cache_volume_face_wireframe_get(Object *ob)
|
||||
{
|
||||
BLI_assert(ob->type == OB_VOLUME);
|
||||
return DRW_volume_batch_cache_get_wireframes_face(&DRW_object_get_data_for_drawing<Volume>(*ob));
|
||||
}
|
||||
|
||||
gpu::Batch *DRW_cache_volume_selection_surface_get(Object *ob)
|
||||
{
|
||||
BLI_assert(ob->type == OB_VOLUME);
|
||||
return DRW_volume_batch_cache_get_selection_surface(
|
||||
&DRW_object_get_data_for_drawing<Volume>(*ob));
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Particles
|
||||
* \{ */
|
||||
|
||||
gpu::Batch *DRW_cache_particles_get_hair(Object *object, ParticleSystem *psys, ModifierData *md)
|
||||
{
|
||||
return DRW_particles_batch_cache_get_hair(object, psys, md);
|
||||
}
|
||||
|
||||
gpu::Batch *DRW_cache_particles_get_dots(Object *object, ParticleSystem *psys)
|
||||
{
|
||||
return DRW_particles_batch_cache_get_dots(object, psys);
|
||||
}
|
||||
|
||||
gpu::Batch *DRW_cache_particles_get_edit_strands(Object *object,
|
||||
ParticleSystem *psys,
|
||||
PTCacheEdit *edit,
|
||||
bool use_weight)
|
||||
{
|
||||
return DRW_particles_batch_cache_get_edit_strands(object, psys, edit, use_weight);
|
||||
}
|
||||
|
||||
gpu::Batch *DRW_cache_particles_get_edit_inner_points(Object *object,
|
||||
ParticleSystem *psys,
|
||||
PTCacheEdit *edit)
|
||||
{
|
||||
return DRW_particles_batch_cache_get_edit_inner_points(object, psys, edit);
|
||||
}
|
||||
|
||||
gpu::Batch *DRW_cache_particles_get_edit_tip_points(Object *object,
|
||||
ParticleSystem *psys,
|
||||
PTCacheEdit *edit)
|
||||
{
|
||||
return DRW_particles_batch_cache_get_edit_tip_points(object, psys, edit);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Batch Cache Implementation (common)
|
||||
* \{ */
|
||||
|
||||
void drw_batch_cache_validate(Object *ob)
|
||||
{
|
||||
switch (ob->type) {
|
||||
case OB_MESH:
|
||||
DRW_mesh_batch_cache_validate(DRW_object_get_data_for_drawing<Mesh>(*ob));
|
||||
break;
|
||||
case OB_CURVES_LEGACY:
|
||||
case OB_FONT:
|
||||
case OB_SURF:
|
||||
DRW_curve_batch_cache_validate(&DRW_object_get_data_for_drawing<Curve>(*ob));
|
||||
break;
|
||||
case OB_LATTICE:
|
||||
DRW_lattice_batch_cache_validate(&DRW_object_get_data_for_drawing<Lattice>(*ob));
|
||||
break;
|
||||
case OB_CURVES:
|
||||
DRW_curves_batch_cache_validate(&DRW_object_get_data_for_drawing<Curves>(*ob));
|
||||
break;
|
||||
case OB_POINTCLOUD:
|
||||
DRW_pointcloud_batch_cache_validate(&DRW_object_get_data_for_drawing<PointCloud>(*ob));
|
||||
break;
|
||||
case OB_VOLUME:
|
||||
DRW_volume_batch_cache_validate(&DRW_object_get_data_for_drawing<Volume>(*ob));
|
||||
break;
|
||||
case OB_GREASE_PENCIL:
|
||||
DRW_grease_pencil_batch_cache_validate(&DRW_object_get_data_for_drawing<GreasePencil>(*ob));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void drw_batch_cache_generate_requested(Object *ob, TaskGraph &task_graph)
|
||||
{
|
||||
const DRWContext *draw_ctx = DRW_context_get();
|
||||
const Scene *scene = draw_ctx->scene;
|
||||
const enum eContextObjectMode mode = CTX_data_mode_enum_ex(
|
||||
draw_ctx->object_edit, draw_ctx->obact, draw_ctx->object_mode);
|
||||
const bool is_paint_mode = ELEM(
|
||||
mode, CTX_MODE_SCULPT, CTX_MODE_PAINT_TEXTURE, CTX_MODE_PAINT_VERTEX, CTX_MODE_PAINT_WEIGHT);
|
||||
|
||||
const bool use_hide = ((ob->type == OB_MESH) &&
|
||||
((is_paint_mode && (ob == draw_ctx->obact) &&
|
||||
DRW_object_use_hide_faces(ob)) ||
|
||||
((mode == CTX_MODE_EDIT_MESH) && (ob->mode == OB_MODE_EDIT))));
|
||||
|
||||
switch (ob->type) {
|
||||
case OB_MESH:
|
||||
DRW_mesh_batch_cache_create_requested(task_graph,
|
||||
*ob,
|
||||
DRW_object_get_data_for_drawing<Mesh>(*ob),
|
||||
*scene,
|
||||
is_paint_mode,
|
||||
use_hide);
|
||||
break;
|
||||
case OB_CURVES_LEGACY:
|
||||
case OB_FONT:
|
||||
case OB_SURF:
|
||||
DRW_curve_batch_cache_create_requested(ob, scene);
|
||||
break;
|
||||
case OB_CURVES:
|
||||
DRW_curves_batch_cache_create_requested(ob);
|
||||
break;
|
||||
case OB_POINTCLOUD:
|
||||
DRW_pointcloud_batch_cache_create_requested(ob);
|
||||
break;
|
||||
/* TODO: all cases. */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void drw_batch_cache_generate_requested_evaluated_mesh_or_curve(Object *ob, TaskGraph &task_graph)
|
||||
{
|
||||
/* NOTE: Logic here is duplicated from #drw_batch_cache_generate_requested. */
|
||||
|
||||
const DRWContext *draw_ctx = DRW_context_get();
|
||||
const Scene *scene = draw_ctx->scene;
|
||||
const enum eContextObjectMode mode = CTX_data_mode_enum_ex(
|
||||
draw_ctx->object_edit, draw_ctx->obact, draw_ctx->object_mode);
|
||||
const bool is_paint_mode = ELEM(
|
||||
mode, CTX_MODE_SCULPT, CTX_MODE_PAINT_TEXTURE, CTX_MODE_PAINT_VERTEX, CTX_MODE_PAINT_WEIGHT);
|
||||
|
||||
if (ob->type == OB_MESH) {
|
||||
const bool use_hide = ((is_paint_mode && (ob == draw_ctx->obact) &&
|
||||
DRW_object_use_hide_faces(ob)) ||
|
||||
((mode == CTX_MODE_EDIT_MESH) && (ob->mode == OB_MODE_EDIT)));
|
||||
if (Mesh *mesh = BKE_object_get_evaluated_mesh_no_subsurf_unchecked(ob)) {
|
||||
DRW_mesh_batch_cache_create_requested(
|
||||
task_graph, *ob, DRW_mesh_get_for_drawing(*mesh), *scene, is_paint_mode, use_hide);
|
||||
}
|
||||
}
|
||||
else if (Mesh *mesh = BKE_object_get_evaluated_mesh_no_subsurf_unchecked(ob)) {
|
||||
/* Legacy curve objects can have evaluated mesh data while the original object is still a curve
|
||||
* in edit mode (see #156971). #DRW_mesh_batch_cache_create_requested expects the object to
|
||||
* match the mesh data and to be in object mode for non-edit mesh extraction, so use a shallow
|
||||
* temporary mesh object for drawing. */
|
||||
bke::ObjectRuntime tmp_runtime = *ob->runtime;
|
||||
Object tmp_object = dna::shallow_copy(*ob);
|
||||
tmp_object.runtime = &tmp_runtime;
|
||||
tmp_object.mode = OB_MODE_OBJECT;
|
||||
BKE_object_replace_data_on_shallow_copy(&tmp_object, &mesh->id);
|
||||
DRW_mesh_batch_cache_create_requested(
|
||||
task_graph, tmp_object, DRW_mesh_get_for_drawing(*mesh), *scene, is_paint_mode, false);
|
||||
}
|
||||
else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_FONT, OB_SURF)) {
|
||||
DRW_curve_batch_cache_create_requested(ob, scene);
|
||||
}
|
||||
}
|
||||
|
||||
void drw_batch_cache_generate_requested_delayed(Object *ob)
|
||||
{
|
||||
DRWContext &draw_ctx = drw_get();
|
||||
draw_ctx.delayed_extraction.add(ob);
|
||||
}
|
||||
|
||||
void DRW_batch_cache_free_old(Object *ob, int ctime)
|
||||
{
|
||||
switch (ob->type) {
|
||||
case OB_MESH:
|
||||
DRW_mesh_batch_cache_free_old(&DRW_object_get_data_for_drawing<Mesh>(*ob), ctime);
|
||||
break;
|
||||
case OB_CURVES:
|
||||
DRW_curves_batch_cache_free_old(&DRW_object_get_data_for_drawing<Curves>(*ob), ctime);
|
||||
break;
|
||||
case OB_POINTCLOUD:
|
||||
DRW_pointcloud_batch_cache_free_old(&DRW_object_get_data_for_drawing<PointCloud>(*ob),
|
||||
ctime);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
void DRW_cdlayer_attr_aliases_add(GPUVertFormat *format,
|
||||
const char *base_name,
|
||||
const bke::AttrType data_type,
|
||||
const StringRef layer_name,
|
||||
bool is_active_render,
|
||||
bool is_active_layer)
|
||||
{
|
||||
char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
|
||||
GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME);
|
||||
|
||||
/* Attribute layer name. */
|
||||
SNPRINTF(attr_name, "%s%s", base_name, attr_safe_name);
|
||||
GPU_vertformat_alias_add(format, attr_name);
|
||||
|
||||
/* Auto layer name. */
|
||||
SNPRINTF(attr_name, "a%s", attr_safe_name);
|
||||
GPU_vertformat_alias_add(format, attr_name);
|
||||
|
||||
/* Active render layer name. */
|
||||
if (is_active_render) {
|
||||
GPU_vertformat_alias_add(format, data_type == bke::AttrType::Float2 ? "a" : base_name);
|
||||
}
|
||||
|
||||
/* Active display layer name. */
|
||||
if (is_active_layer) {
|
||||
SNPRINTF(attr_name, "a%s", base_name);
|
||||
GPU_vertformat_alias_add(format, attr_name);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::draw
|
||||
165
blender-5.2.0/source/blender/draw/intern/draw_cache.hh
Normal file
165
blender-5.2.0/source/blender/draw/intern/draw_cache.hh
Normal file
@@ -0,0 +1,165 @@
|
||||
/* SPDX-FileCopyrightText: 2016 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_math_matrix_types.hh"
|
||||
#include "BLI_span.hh"
|
||||
|
||||
#include "BKE_volume_grid_fwd.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct GPUMaterial;
|
||||
namespace gpu {
|
||||
class Texture;
|
||||
class Batch;
|
||||
class VertBuf;
|
||||
} // namespace gpu
|
||||
struct ModifierData;
|
||||
struct Object;
|
||||
struct PTCacheEdit;
|
||||
struct ParticleSystem;
|
||||
struct Volume;
|
||||
struct Scene;
|
||||
|
||||
namespace draw {
|
||||
|
||||
/**
|
||||
* Shape resolution level of detail.
|
||||
*/
|
||||
enum eDRWLevelOfDetail {
|
||||
DRW_LOD_LOW = 0,
|
||||
DRW_LOD_MEDIUM = 1,
|
||||
DRW_LOD_HIGH = 2,
|
||||
|
||||
DRW_LOD_MAX, /* Max number of level of detail */
|
||||
};
|
||||
|
||||
/* Common Object */
|
||||
|
||||
gpu::Batch *DRW_cache_object_all_edges_get(Object *ob);
|
||||
gpu::Batch *DRW_cache_object_edge_detection_get(Object *ob, bool *r_is_manifold);
|
||||
gpu::Batch *DRW_cache_object_surface_get(Object *ob);
|
||||
gpu::Batch *DRW_cache_object_loose_edges_get(Object *ob);
|
||||
Span<gpu::Batch *> DRW_cache_object_surface_material_get(Object *ob,
|
||||
Span<const GPUMaterial *> materials);
|
||||
gpu::Batch *DRW_cache_object_face_wireframe_get(const Scene *scene, Object *ob);
|
||||
|
||||
/* Meshes */
|
||||
|
||||
gpu::Batch *DRW_cache_mesh_all_verts_get(Object *ob);
|
||||
gpu::Batch *DRW_cache_mesh_paint_overlay_verts_get(Object *ob);
|
||||
gpu::Batch *DRW_cache_mesh_all_edges_get(Object *ob);
|
||||
gpu::Batch *DRW_cache_mesh_loose_edges_get(Object *ob);
|
||||
gpu::Batch *DRW_cache_mesh_edge_detection_get(Object *ob, bool *r_is_manifold);
|
||||
gpu::Batch *DRW_cache_mesh_surface_get(Object *ob);
|
||||
gpu::Batch *DRW_cache_mesh_paint_overlay_surface_get(Object *ob);
|
||||
gpu::Batch *DRW_cache_mesh_paint_overlay_edges_get(Object *ob);
|
||||
/**
|
||||
* Return list of batches with length equal to `max(1, totcol)`.
|
||||
*/
|
||||
Span<gpu::Batch *> DRW_cache_mesh_surface_shaded_get(Object *ob,
|
||||
Span<const GPUMaterial *> materials);
|
||||
/**
|
||||
* Return list of batches with length equal to `max(1, totcol)`.
|
||||
*/
|
||||
Span<gpu::Batch *> DRW_cache_mesh_surface_texpaint_get(Object *ob);
|
||||
gpu::Batch *DRW_cache_mesh_surface_texpaint_single_get(Object *ob);
|
||||
gpu::Batch *DRW_cache_mesh_surface_vertpaint_get(Object *ob);
|
||||
gpu::Batch *DRW_cache_mesh_surface_sculptcolors_get(Object *ob);
|
||||
gpu::Batch *DRW_cache_mesh_surface_weights_get(Object *ob);
|
||||
gpu::Batch *DRW_cache_mesh_surface_mesh_analysis_get(Object *ob);
|
||||
gpu::Batch *DRW_cache_mesh_face_wireframe_get(Object *ob);
|
||||
gpu::Batch *DRW_cache_mesh_surface_viewer_attribute_get(Object *ob);
|
||||
|
||||
/* Curve */
|
||||
|
||||
gpu::Batch *DRW_cache_curve_edge_wire_get(Object *ob);
|
||||
gpu::Batch *DRW_cache_curve_edge_wire_viewer_attribute_get(Object *ob);
|
||||
|
||||
/* edit-mode */
|
||||
|
||||
gpu::Batch *DRW_cache_curve_edge_normal_get(Object *ob);
|
||||
gpu::Batch *DRW_cache_curve_edge_overlay_get(Object *ob);
|
||||
gpu::Batch *DRW_cache_curve_vert_overlay_get(Object *ob);
|
||||
|
||||
/* Font */
|
||||
|
||||
gpu::Batch *DRW_cache_text_edge_wire_get(Object *ob);
|
||||
|
||||
/* Surface */
|
||||
|
||||
gpu::Batch *DRW_cache_surf_edge_wire_get(Object *ob);
|
||||
|
||||
/* Lattice */
|
||||
|
||||
gpu::Batch *DRW_cache_lattice_verts_get(Object *ob);
|
||||
gpu::Batch *DRW_cache_lattice_wire_get(Object *ob, bool use_weight);
|
||||
gpu::Batch *DRW_cache_lattice_vert_overlay_get(Object *ob);
|
||||
|
||||
/* Point Cloud */
|
||||
|
||||
gpu::Batch *DRW_cache_pointcloud_vert_overlay_get(Object *ob);
|
||||
|
||||
/* Particles */
|
||||
|
||||
gpu::Batch *DRW_cache_particles_get_hair(Object *object, ParticleSystem *psys, ModifierData *md);
|
||||
gpu::Batch *DRW_cache_particles_get_dots(Object *object, ParticleSystem *psys);
|
||||
gpu::Batch *DRW_cache_particles_get_edit_strands(Object *object,
|
||||
ParticleSystem *psys,
|
||||
PTCacheEdit *edit,
|
||||
bool use_weight);
|
||||
gpu::Batch *DRW_cache_particles_get_edit_inner_points(Object *object,
|
||||
ParticleSystem *psys,
|
||||
PTCacheEdit *edit);
|
||||
gpu::Batch *DRW_cache_particles_get_edit_tip_points(Object *object,
|
||||
ParticleSystem *psys,
|
||||
PTCacheEdit *edit);
|
||||
|
||||
/* Volume */
|
||||
|
||||
struct DRWVolumeGrid {
|
||||
DRWVolumeGrid *next, *prev;
|
||||
|
||||
/* Grid name. */
|
||||
char *name;
|
||||
|
||||
/* 3D texture. */
|
||||
gpu::Texture *texture;
|
||||
|
||||
/* Transform between 0..1 texture space and object space. */
|
||||
float4x4 texture_to_object;
|
||||
float4x4 object_to_texture;
|
||||
|
||||
/* Transform from bounds to texture space. */
|
||||
float4x4 object_to_bounds;
|
||||
float4x4 bounds_to_texture;
|
||||
};
|
||||
|
||||
DRWVolumeGrid *DRW_volume_batch_cache_get_grid(Volume *volume,
|
||||
const bke::VolumeGridData *volume_grid);
|
||||
gpu::Batch *DRW_cache_volume_face_wireframe_get(Object *ob);
|
||||
gpu::Batch *DRW_cache_volume_selection_surface_get(Object *ob);
|
||||
|
||||
/* Grease Pencil */
|
||||
|
||||
/* When there's no visible drawings in this grease pencil object, the returned `Batch` could be
|
||||
* nullptr as `grease_pencil_edit_batch_ensure` won't do anything in those cases. */
|
||||
gpu::Batch *DRW_cache_grease_pencil_get(const Scene *scene, Object *ob);
|
||||
gpu::Batch *DRW_cache_grease_pencil_edit_points_get(const Scene *scene, Object *ob);
|
||||
gpu::Batch *DRW_cache_grease_pencil_edit_lines_get(const Scene *scene, Object *ob);
|
||||
gpu::Batch *DRW_cache_grease_pencil_edit_handles_get(const Scene *scene, Object *ob);
|
||||
gpu::VertBuf *DRW_cache_grease_pencil_position_buffer_get(const Scene *scene, Object *ob);
|
||||
gpu::VertBuf *DRW_cache_grease_pencil_color_buffer_get(const Scene *scene, Object *ob);
|
||||
gpu::Batch *DRW_cache_grease_pencil_weight_points_get(const Scene *scene, Object *ob);
|
||||
gpu::Batch *DRW_cache_grease_pencil_weight_lines_get(const Scene *scene, Object *ob);
|
||||
gpu::Batch *DRW_cache_grease_pencil_face_wireframe_get(const Scene *scene, Object *ob);
|
||||
|
||||
} // namespace draw
|
||||
} // namespace blender
|
||||
353
blender-5.2.0/source/blender/draw/intern/draw_cache_extract.hh
Normal file
353
blender-5.2.0/source/blender/draw/intern/draw_cache_extract.hh
Normal file
@@ -0,0 +1,353 @@
|
||||
/* SPDX-FileCopyrightText: 2019 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_enum_flags.hh"
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_math_matrix_types.hh"
|
||||
|
||||
#include "DNA_view3d_enums.h"
|
||||
|
||||
#include "GPU_index_buffer.hh"
|
||||
#include "GPU_shader.hh"
|
||||
#include "GPU_vertex_buffer.hh"
|
||||
|
||||
#include "draw_attributes.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace gpu {
|
||||
class Batch;
|
||||
class IndexBuf;
|
||||
} // namespace gpu
|
||||
struct Mesh;
|
||||
struct Object;
|
||||
struct Scene;
|
||||
struct TaskGraph;
|
||||
struct ToolSettings;
|
||||
|
||||
namespace draw {
|
||||
|
||||
struct MeshRenderData;
|
||||
struct DRWSubdivCache;
|
||||
|
||||
/* Vertex Group Selection and display options */
|
||||
struct DRW_MeshWeightState {
|
||||
int defgroup_active;
|
||||
int defgroup_len;
|
||||
|
||||
short flags;
|
||||
char alert_mode;
|
||||
|
||||
/* Set of all selected bones for Multi-paint. */
|
||||
bool *defgroup_sel; /* #defgroup_len */
|
||||
int defgroup_sel_count;
|
||||
|
||||
/* Set of all locked and unlocked deform bones for Lock Relative mode. */
|
||||
bool *defgroup_locked; /* #defgroup_len */
|
||||
bool *defgroup_unlocked; /* #defgroup_len */
|
||||
};
|
||||
|
||||
/* DRW_MeshWeightState.flags */
|
||||
enum {
|
||||
DRW_MESH_WEIGHT_STATE_MULTIPAINT = (1 << 0),
|
||||
DRW_MESH_WEIGHT_STATE_AUTO_NORMALIZE = (1 << 1),
|
||||
DRW_MESH_WEIGHT_STATE_LOCK_RELATIVE = (1 << 2),
|
||||
};
|
||||
|
||||
/**
|
||||
* Vertex buffer types that can be use by batches in the mesh batch cache.
|
||||
*
|
||||
* \todo It would be good to change this to something like #draw::pbvh::AttributeRequest to
|
||||
* separate the generic attribute requests. While there is a limit on the number of vertex buffers
|
||||
* used by a single shader/batch, there is no need for that limit here; there are potentially many
|
||||
* shaders requiring attributes for a particular mesh. OTOH, it may be good to use flags for the
|
||||
* builtin buffer types, so that bitwise operations can be used.
|
||||
*/
|
||||
enum class VBOType : int8_t {
|
||||
Position,
|
||||
CornerNormal,
|
||||
EdgeFactor,
|
||||
VertexGroupWeight,
|
||||
UVs,
|
||||
Tangents,
|
||||
SculptData,
|
||||
Orco,
|
||||
EditData,
|
||||
EditUVData,
|
||||
EditUVStretchArea,
|
||||
EditUVStretchAngle,
|
||||
MeshAnalysis,
|
||||
FaceDotPosition,
|
||||
FaceDotNormal,
|
||||
FaceDotUV,
|
||||
FaceDotEditUVData,
|
||||
SkinRoots,
|
||||
IndexVert,
|
||||
IndexEdge,
|
||||
IndexFace,
|
||||
IndexFaceDot,
|
||||
Attr0,
|
||||
Attr1,
|
||||
Attr2,
|
||||
Attr3,
|
||||
Attr5,
|
||||
Attr6,
|
||||
Attr7,
|
||||
Attr8,
|
||||
Attr9,
|
||||
Attr10,
|
||||
Attr11,
|
||||
Attr12,
|
||||
Attr13,
|
||||
Attr14,
|
||||
Attr15,
|
||||
AttrViewer,
|
||||
VertexNormal,
|
||||
PaintOverlayFlag,
|
||||
};
|
||||
|
||||
/**
|
||||
* All index buffers used for mesh batches.
|
||||
*
|
||||
* \note "Tris per material" (#MeshBatchCache::tris_per_mat) is an exception. Since there are
|
||||
* an arbitrary numbers of materials, those are handled separately (as slices of the overall
|
||||
* triangles buffer).
|
||||
*/
|
||||
enum class IBOType : int8_t {
|
||||
Tris,
|
||||
Lines,
|
||||
LinesLoose,
|
||||
Points,
|
||||
FaceDots,
|
||||
LinesPaintMask,
|
||||
LinesAdjacency,
|
||||
UVTris,
|
||||
AllUVLines,
|
||||
UVLines,
|
||||
EditUVTris,
|
||||
EditUVLines,
|
||||
EditUVPoints,
|
||||
EditUVFaceDots,
|
||||
};
|
||||
|
||||
struct MeshBufferList {
|
||||
/* Though using maps here may add some overhead compared to just indexed arrays, it's a bit more
|
||||
* convenient currently, because the "buffer exists" test is very clear, it's just whether the
|
||||
* map contains it (e.g. compared to "buffer is allocated but not filled with data"). The
|
||||
* sparseness *may* be useful for reducing memory usage when only few buffers are used. */
|
||||
|
||||
Map<VBOType, std::unique_ptr<gpu::VertBuf, gpu::VertBufDeleter>> vbos;
|
||||
Map<IBOType, std::unique_ptr<gpu::IndexBuf, gpu::IndexBufDeleter>> ibos;
|
||||
};
|
||||
|
||||
struct MeshBatchList {
|
||||
/* Surfaces / Render */
|
||||
gpu::Batch *surface;
|
||||
gpu::Batch *surface_weights;
|
||||
/* Edit mode */
|
||||
gpu::Batch *edit_triangles;
|
||||
gpu::Batch *edit_vertices;
|
||||
gpu::Batch *edit_edges;
|
||||
gpu::Batch *edit_vnor;
|
||||
gpu::Batch *edit_lnor;
|
||||
gpu::Batch *edit_fdots;
|
||||
gpu::Batch *edit_mesh_analysis;
|
||||
gpu::Batch *edit_skin_roots;
|
||||
/* Edit UVs */
|
||||
gpu::Batch *edituv_faces_stretch_area;
|
||||
gpu::Batch *edituv_faces_stretch_angle;
|
||||
gpu::Batch *edituv_faces;
|
||||
gpu::Batch *edituv_edges;
|
||||
gpu::Batch *edituv_verts;
|
||||
gpu::Batch *edituv_fdots;
|
||||
/* Edit selection */
|
||||
gpu::Batch *edit_selection_verts;
|
||||
gpu::Batch *edit_selection_edges;
|
||||
gpu::Batch *edit_selection_faces;
|
||||
gpu::Batch *edit_selection_fdots;
|
||||
/* Common display / Other */
|
||||
gpu::Batch *uv_faces;
|
||||
gpu::Batch *all_verts;
|
||||
gpu::Batch *all_edges;
|
||||
gpu::Batch *loose_edges;
|
||||
gpu::Batch *edge_detection;
|
||||
/* Individual edges with face normals. */
|
||||
gpu::Batch *wire_edges;
|
||||
/* Loops around faces. no edges between selected faces */
|
||||
gpu::Batch *paint_overlay_wire_loops;
|
||||
gpu::Batch *wire_loops_all_uvs;
|
||||
gpu::Batch *wire_loops_uvs;
|
||||
gpu::Batch *wire_loops_edituvs;
|
||||
gpu::Batch *sculpt_overlays;
|
||||
gpu::Batch *surface_viewer_attribute;
|
||||
gpu::Batch *paint_overlay_verts;
|
||||
gpu::Batch *paint_overlay_surface;
|
||||
};
|
||||
|
||||
#define MBC_BATCH_LEN (sizeof(MeshBatchList) / sizeof(void *))
|
||||
|
||||
#define MBC_BATCH_INDEX(batch) (offsetof(MeshBatchList, batch) / sizeof(void *))
|
||||
|
||||
enum DRWBatchFlag : uint64_t {
|
||||
MBC_SURFACE = (1u << MBC_BATCH_INDEX(surface)),
|
||||
MBC_SURFACE_WEIGHTS = (1u << MBC_BATCH_INDEX(surface_weights)),
|
||||
MBC_EDIT_TRIANGLES = (1u << MBC_BATCH_INDEX(edit_triangles)),
|
||||
MBC_EDIT_VERTICES = (1u << MBC_BATCH_INDEX(edit_vertices)),
|
||||
MBC_EDIT_EDGES = (1u << MBC_BATCH_INDEX(edit_edges)),
|
||||
MBC_EDIT_VNOR = (1u << MBC_BATCH_INDEX(edit_vnor)),
|
||||
MBC_EDIT_LNOR = (1u << MBC_BATCH_INDEX(edit_lnor)),
|
||||
MBC_EDIT_FACEDOTS = (1u << MBC_BATCH_INDEX(edit_fdots)),
|
||||
MBC_EDIT_MESH_ANALYSIS = (1u << MBC_BATCH_INDEX(edit_mesh_analysis)),
|
||||
MBC_SKIN_ROOTS = (1u << MBC_BATCH_INDEX(edit_skin_roots)),
|
||||
MBC_UV_FACES = (1u << MBC_BATCH_INDEX(uv_faces)),
|
||||
MBC_EDITUV_FACES_STRETCH_AREA = (1u << MBC_BATCH_INDEX(edituv_faces_stretch_area)),
|
||||
MBC_EDITUV_FACES_STRETCH_ANGLE = (1u << MBC_BATCH_INDEX(edituv_faces_stretch_angle)),
|
||||
MBC_EDITUV_FACES = (1u << MBC_BATCH_INDEX(edituv_faces)),
|
||||
MBC_EDITUV_EDGES = (1u << MBC_BATCH_INDEX(edituv_edges)),
|
||||
MBC_EDITUV_VERTS = (1u << MBC_BATCH_INDEX(edituv_verts)),
|
||||
MBC_EDITUV_FACEDOTS = (1u << MBC_BATCH_INDEX(edituv_fdots)),
|
||||
MBC_EDIT_SELECTION_VERTS = (1u << MBC_BATCH_INDEX(edit_selection_verts)),
|
||||
MBC_EDIT_SELECTION_EDGES = (1u << MBC_BATCH_INDEX(edit_selection_edges)),
|
||||
MBC_EDIT_SELECTION_FACES = (1u << MBC_BATCH_INDEX(edit_selection_faces)),
|
||||
MBC_EDIT_SELECTION_FACEDOTS = (1u << MBC_BATCH_INDEX(edit_selection_fdots)),
|
||||
MBC_ALL_VERTS = (1u << MBC_BATCH_INDEX(all_verts)),
|
||||
MBC_ALL_EDGES = (1u << MBC_BATCH_INDEX(all_edges)),
|
||||
MBC_LOOSE_EDGES = (1u << MBC_BATCH_INDEX(loose_edges)),
|
||||
MBC_EDGE_DETECTION = (1u << MBC_BATCH_INDEX(edge_detection)),
|
||||
MBC_WIRE_EDGES = (1u << MBC_BATCH_INDEX(wire_edges)),
|
||||
MBC_PAINT_OVERLAY_WIRE_LOOPS = (1u << MBC_BATCH_INDEX(paint_overlay_wire_loops)),
|
||||
MBC_WIRE_LOOPS_ALL_UVS = (1u << MBC_BATCH_INDEX(wire_loops_all_uvs)),
|
||||
MBC_WIRE_LOOPS_UVS = (1u << MBC_BATCH_INDEX(wire_loops_uvs)),
|
||||
MBC_WIRE_LOOPS_EDITUVS = (1u << MBC_BATCH_INDEX(wire_loops_edituvs)),
|
||||
MBC_SCULPT_OVERLAYS = (1u << MBC_BATCH_INDEX(sculpt_overlays)),
|
||||
MBC_VIEWER_ATTRIBUTE_OVERLAY = (1u << MBC_BATCH_INDEX(surface_viewer_attribute)),
|
||||
MBC_PAINT_OVERLAY_VERTS = (uint64_t(1u) << MBC_BATCH_INDEX(paint_overlay_verts)),
|
||||
MBC_PAINT_OVERLAY_SURFACE = (uint64_t(1u) << MBC_BATCH_INDEX(paint_overlay_surface)),
|
||||
MBC_SURFACE_PER_MAT = (uint64_t(1u) << MBC_BATCH_LEN),
|
||||
};
|
||||
ENUM_OPERATORS(DRWBatchFlag);
|
||||
|
||||
BLI_STATIC_ASSERT(MBC_BATCH_LEN < 64, "Number of batches exceeded the limit of bit fields");
|
||||
|
||||
struct MeshExtractLooseGeom {
|
||||
/** Indices of all vertices not used by edges in the #Mesh or #BMesh. */
|
||||
IndexMask verts;
|
||||
/** Indices of all edges not used by faces in the #Mesh or #BMesh. */
|
||||
IndexMask edges;
|
||||
/** Used for BMesh which does not cache loose geometry index masks. */
|
||||
std::unique_ptr<LinearAllocator<>> allocator;
|
||||
};
|
||||
|
||||
struct SortedFaceData {
|
||||
/* The total number of visible triangles (a sum of the values in #mat_tri_counts). */
|
||||
int visible_tris_num;
|
||||
/** The number of visible triangles assigned to each material. */
|
||||
Array<int> tris_num_by_material;
|
||||
/**
|
||||
* The first triangle index for each face, sorted into slices by material.
|
||||
* May be empty if the mesh only has a single material.
|
||||
*/
|
||||
std::optional<Array<int>> face_tri_offsets;
|
||||
};
|
||||
|
||||
/**
|
||||
* Data that are kept around between extractions to reduce rebuilding time.
|
||||
*
|
||||
* - Loose geometry.
|
||||
*/
|
||||
struct MeshBufferCache {
|
||||
MeshBufferList buff;
|
||||
|
||||
MeshExtractLooseGeom loose_geom;
|
||||
|
||||
SortedFaceData face_sorted;
|
||||
};
|
||||
|
||||
#define FOREACH_MESH_BUFFER_CACHE(batch_cache, mbc) \
|
||||
for (MeshBufferCache *mbc = &batch_cache.final; \
|
||||
mbc == &batch_cache.final || mbc == &batch_cache.cage || mbc == &batch_cache.uv_cage; \
|
||||
mbc = (mbc == &batch_cache.final) ? \
|
||||
&batch_cache.cage : \
|
||||
((mbc == &batch_cache.cage) ? &batch_cache.uv_cage : nullptr))
|
||||
|
||||
struct MeshBatchCache {
|
||||
MeshBufferCache final, cage, uv_cage;
|
||||
|
||||
MeshBatchList batch;
|
||||
|
||||
/* Index buffer per material. These are sub-ranges of `ibo.tris`. */
|
||||
Array<gpu::IndexBufPtr> tris_per_mat;
|
||||
Array<gpu::Batch *> surface_per_mat;
|
||||
|
||||
DRWSubdivCache *subdiv_cache;
|
||||
|
||||
DRWBatchFlag batch_requested;
|
||||
DRWBatchFlag batch_ready;
|
||||
|
||||
/* Settings to determine if cache is invalid. */
|
||||
int edge_len;
|
||||
int tri_len;
|
||||
int face_len;
|
||||
int vert_len;
|
||||
int mat_len;
|
||||
/* Instantly invalidates cache, skipping mesh check */
|
||||
bool is_dirty;
|
||||
bool is_editmode;
|
||||
bool is_uvsyncsel;
|
||||
|
||||
DRW_MeshWeightState weight_state;
|
||||
|
||||
DRW_MeshCDMask cd_used, cd_needed, cd_used_over_time;
|
||||
|
||||
VectorSet<std::string> attr_used, attr_needed, attr_used_over_time;
|
||||
|
||||
int lastmatch;
|
||||
|
||||
/* Valid only if edge_detection is up to date. */
|
||||
bool is_manifold;
|
||||
|
||||
bool no_loose_wire;
|
||||
|
||||
/* Total areas for drawing UV Stretching. Contains the summed area in mesh
|
||||
* space (`tot_area`) and the summed area in uv space (`tot_uvarea`).
|
||||
*
|
||||
* Only valid after `DRW_mesh_batch_cache_create_requested` has been called. */
|
||||
float tot_area, tot_uv_area;
|
||||
};
|
||||
|
||||
#define MBC_EDITUV \
|
||||
(MBC_EDITUV_FACES_STRETCH_AREA | MBC_EDITUV_FACES_STRETCH_ANGLE | MBC_EDITUV_FACES | \
|
||||
MBC_EDITUV_EDGES | MBC_EDITUV_VERTS | MBC_EDITUV_FACEDOTS | MBC_UV_FACES | \
|
||||
MBC_WIRE_LOOPS_ALL_UVS | MBC_WIRE_LOOPS_UVS | MBC_WIRE_LOOPS_EDITUVS)
|
||||
|
||||
void mesh_buffer_cache_create_requested(TaskGraph &task_graph,
|
||||
const Scene &scene,
|
||||
MeshBatchCache &cache,
|
||||
MeshBufferCache &mbc,
|
||||
Span<IBOType> ibo_requests,
|
||||
Span<VBOType> vbo_requests,
|
||||
Object &object,
|
||||
Mesh &mesh,
|
||||
bool is_editmode,
|
||||
bool is_paint_mode,
|
||||
bool do_final,
|
||||
bool do_uvedit,
|
||||
bool use_hide);
|
||||
|
||||
void mesh_buffer_cache_create_requested_subdiv(MeshBatchCache &cache,
|
||||
MeshBufferCache &mbc,
|
||||
Span<IBOType> ibo_requests,
|
||||
Span<VBOType> vbo_requests,
|
||||
DRWSubdivCache &subdiv_cache,
|
||||
MeshRenderData &mr);
|
||||
|
||||
} // namespace draw
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,524 @@
|
||||
/* SPDX-FileCopyrightText: 2017 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*
|
||||
* \brief Extraction of Mesh data into VBO to feed to GPU.
|
||||
*/
|
||||
|
||||
#include "BKE_attribute.hh"
|
||||
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_set.hh"
|
||||
#include "BLI_task.hh"
|
||||
|
||||
#include "GPU_capabilities.hh"
|
||||
#include "GPU_debug.hh"
|
||||
|
||||
#include "draw_cache_extract.hh"
|
||||
#include "draw_subdivision.hh"
|
||||
|
||||
#include "mesh_extractors/extract_mesh.hh"
|
||||
|
||||
// #define DEBUG_TIME
|
||||
|
||||
#ifdef DEBUG_TIME
|
||||
# include "BLI_time_utildefines.h"
|
||||
#endif
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name Dependency Data
|
||||
* \{ */
|
||||
|
||||
static void ensure_dependency_data(MeshRenderData &mr,
|
||||
Span<IBOType> ibo_requests,
|
||||
Span<VBOType> vbo_requests,
|
||||
MeshBufferCache &cache)
|
||||
{
|
||||
const bool request_face_normals = vbo_requests.contains(VBOType::CornerNormal) ||
|
||||
vbo_requests.contains(VBOType::FaceDotNormal) ||
|
||||
vbo_requests.contains(VBOType::EdgeFactor) ||
|
||||
vbo_requests.contains(VBOType::MeshAnalysis);
|
||||
const bool request_corner_normals = vbo_requests.contains(VBOType::CornerNormal);
|
||||
const bool force_corner_normals = vbo_requests.contains(VBOType::Tangents);
|
||||
|
||||
if (request_face_normals) {
|
||||
mesh_render_data_update_face_normals(mr);
|
||||
}
|
||||
if ((request_corner_normals && mr.normals_domain == bke::MeshNormalDomain::Corner &&
|
||||
!mr.use_simplify_normals) ||
|
||||
force_corner_normals)
|
||||
{
|
||||
mesh_render_data_update_corner_normals(mr);
|
||||
}
|
||||
|
||||
const bool calc_loose_geom = ibo_requests.contains(IBOType::Lines) ||
|
||||
ibo_requests.contains(IBOType::LinesLoose) ||
|
||||
ibo_requests.contains(IBOType::Points) ||
|
||||
vbo_requests.contains(VBOType::Position) ||
|
||||
vbo_requests.contains(VBOType::EditData) ||
|
||||
vbo_requests.contains(VBOType::VertexNormal) ||
|
||||
vbo_requests.contains(VBOType::IndexVert) ||
|
||||
vbo_requests.contains(VBOType::IndexEdge) ||
|
||||
vbo_requests.contains(VBOType::EdgeFactor);
|
||||
|
||||
if (calc_loose_geom) {
|
||||
mesh_render_data_update_loose_geom(mr, cache);
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name Extract Loop
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* The mesh normals access functions can end up mixing face corner normals calculated with the
|
||||
* costly tangent space method. The "Simplify Normals" option is supposed to avoid that, but not
|
||||
* the "Free" normals which are actually cheaper than calculating true normals.
|
||||
*/
|
||||
static bool use_normals_simplify(const Scene &scene, const MeshRenderData &mr)
|
||||
{
|
||||
if (!(scene.r.mode & R_SIMPLIFY) || !(scene.r.mode & R_SIMPLIFY_NORMALS)) {
|
||||
return false;
|
||||
}
|
||||
if (!mr.mesh) {
|
||||
return true;
|
||||
}
|
||||
const Mesh &mesh = *mr.mesh;
|
||||
const std::optional<bke::AttributeMetaData> meta_data = mesh.attributes().lookup_meta_data(
|
||||
"custom_normal");
|
||||
if (!meta_data) {
|
||||
return false;
|
||||
}
|
||||
if (meta_data->domain == bke::AttrDomain::Corner &&
|
||||
meta_data->data_type == bke::AttrType::Int16_2D)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void mesh_buffer_cache_create_requested(TaskGraph & /*task_graph*/,
|
||||
const Scene &scene,
|
||||
MeshBatchCache &cache,
|
||||
MeshBufferCache &mbc,
|
||||
const Span<IBOType> ibo_requests,
|
||||
const Span<VBOType> vbo_requests,
|
||||
Object &object,
|
||||
Mesh &mesh,
|
||||
const bool is_editmode,
|
||||
const bool is_paint_mode,
|
||||
const bool do_final,
|
||||
const bool do_uvedit,
|
||||
const bool use_hide)
|
||||
{
|
||||
if (ibo_requests.is_empty() && vbo_requests.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
MeshBufferList &buffers = mbc.buff;
|
||||
|
||||
Vector<IBOType, 16> ibos_to_create;
|
||||
for (const IBOType request : ibo_requests) {
|
||||
if (!buffers.ibos.contains(request)) {
|
||||
ibos_to_create.append(request);
|
||||
}
|
||||
}
|
||||
|
||||
Vector<VBOType, 16> vbos_to_create;
|
||||
for (const VBOType request : vbo_requests) {
|
||||
if (!buffers.vbos.contains(request)) {
|
||||
vbos_to_create.append(request);
|
||||
}
|
||||
}
|
||||
|
||||
if (ibos_to_create.is_empty() && vbos_to_create.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_TIME
|
||||
SCOPED_TIMER(__func__);
|
||||
#endif
|
||||
|
||||
MeshRenderData mr = mesh_render_data_create(
|
||||
object, mesh, is_editmode, is_paint_mode, do_final, do_uvedit, use_hide, scene.toolsettings);
|
||||
|
||||
mr.use_subsurf_fdots = mr.mesh && !mr.mesh->runtime->subsurf_face_dot_tags.is_empty();
|
||||
mr.use_simplify_normals = use_normals_simplify(scene, mr);
|
||||
|
||||
ensure_dependency_data(mr, ibo_requests, vbo_requests, mbc);
|
||||
|
||||
Array<gpu::IndexBufPtr, 16> created_ibos(ibos_to_create.size());
|
||||
|
||||
{
|
||||
/* Because lines and loose lines are stored in the same buffer, they're handled separately
|
||||
* rather than from potentially multiple threads in the parallel_for_each loop below. */
|
||||
const int lines_index = ibos_to_create.as_span().first_index_try(IBOType::Lines);
|
||||
const int loose_lines_index = ibos_to_create.as_span().first_index_try(IBOType::LinesLoose);
|
||||
if (lines_index != -1 || loose_lines_index != -1) {
|
||||
extract_lines(mr,
|
||||
lines_index == -1 ? nullptr : &created_ibos[lines_index],
|
||||
loose_lines_index == -1 ? nullptr : &created_ibos[loose_lines_index],
|
||||
cache.no_loose_wire);
|
||||
}
|
||||
}
|
||||
|
||||
threading::parallel_for_each(ibos_to_create.index_range(), [&](const int i) {
|
||||
switch (ibos_to_create[i]) {
|
||||
case IBOType::Tris:
|
||||
created_ibos[i] = extract_tris(mr, mesh_render_data_faces_sorted_ensure(mr, mbc));
|
||||
break;
|
||||
case IBOType::Lines:
|
||||
case IBOType::LinesLoose:
|
||||
/* Handled as a special case above. */
|
||||
break;
|
||||
case IBOType::Points:
|
||||
created_ibos[i] = extract_points(mr);
|
||||
break;
|
||||
case IBOType::FaceDots:
|
||||
created_ibos[i] = extract_face_dots(mr);
|
||||
break;
|
||||
case IBOType::LinesPaintMask:
|
||||
created_ibos[i] = extract_lines_paint_mask(mr);
|
||||
break;
|
||||
case IBOType::LinesAdjacency:
|
||||
created_ibos[i] = extract_lines_adjacency(mr, cache.is_manifold);
|
||||
break;
|
||||
case IBOType::UVTris:
|
||||
created_ibos[i] = extract_edituv_tris(mr, false);
|
||||
break;
|
||||
case IBOType::EditUVTris:
|
||||
created_ibos[i] = extract_edituv_tris(mr, true);
|
||||
break;
|
||||
case IBOType::AllUVLines:
|
||||
created_ibos[i] = extract_edituv_lines(mr, UvExtractionMode::All);
|
||||
break;
|
||||
case IBOType::UVLines:
|
||||
created_ibos[i] = extract_edituv_lines(mr, UvExtractionMode::Selection);
|
||||
break;
|
||||
case IBOType::EditUVLines:
|
||||
created_ibos[i] = extract_edituv_lines(mr, UvExtractionMode::Edit);
|
||||
break;
|
||||
case IBOType::EditUVPoints:
|
||||
created_ibos[i] = extract_edituv_points(mr);
|
||||
break;
|
||||
case IBOType::EditUVFaceDots:
|
||||
created_ibos[i] = extract_edituv_face_dots(mr);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
Array<gpu::VertBufPtr, 16> created_vbos(vbos_to_create.size());
|
||||
|
||||
const bool do_hq_normals = (scene.r.perf_flag & SCE_PERF_HQ_NORMALS) != 0 ||
|
||||
GPU_use_hq_normals_workaround();
|
||||
|
||||
threading::parallel_for_each(vbos_to_create.index_range(), [&](const int i) {
|
||||
switch (vbos_to_create[i]) {
|
||||
case VBOType::Position:
|
||||
created_vbos[i] = extract_positions(mr);
|
||||
break;
|
||||
case VBOType::CornerNormal:
|
||||
created_vbos[i] = extract_normals(mr, do_hq_normals);
|
||||
break;
|
||||
case VBOType::EdgeFactor:
|
||||
created_vbos[i] = extract_edge_factor(mr);
|
||||
break;
|
||||
case VBOType::VertexGroupWeight:
|
||||
created_vbos[i] = extract_weights(mr, cache);
|
||||
break;
|
||||
case VBOType::UVs:
|
||||
created_vbos[i] = extract_uv_maps(mr, cache);
|
||||
break;
|
||||
case VBOType::Tangents:
|
||||
created_vbos[i] = extract_tangents(mr, cache, do_hq_normals);
|
||||
break;
|
||||
case VBOType::SculptData:
|
||||
created_vbos[i] = extract_sculpt_data(mr);
|
||||
break;
|
||||
case VBOType::Orco:
|
||||
created_vbos[i] = extract_orco(mr);
|
||||
break;
|
||||
case VBOType::EditData:
|
||||
created_vbos[i] = extract_edit_data(mr);
|
||||
break;
|
||||
case VBOType::EditUVData:
|
||||
created_vbos[i] = extract_edituv_data(mr);
|
||||
break;
|
||||
case VBOType::EditUVStretchArea:
|
||||
created_vbos[i] = extract_edituv_stretch_area(mr, cache.tot_area, cache.tot_uv_area);
|
||||
break;
|
||||
case VBOType::EditUVStretchAngle:
|
||||
created_vbos[i] = extract_edituv_stretch_angle(mr);
|
||||
break;
|
||||
case VBOType::MeshAnalysis:
|
||||
created_vbos[i] = extract_mesh_analysis(mr, object.object_to_world());
|
||||
break;
|
||||
case VBOType::FaceDotPosition:
|
||||
created_vbos[i] = extract_face_dots_position(mr);
|
||||
break;
|
||||
case VBOType::FaceDotNormal:
|
||||
created_vbos[i] = extract_face_dot_normals(mr, do_hq_normals);
|
||||
break;
|
||||
case VBOType::FaceDotUV:
|
||||
created_vbos[i] = extract_face_dots_uv(mr);
|
||||
break;
|
||||
case VBOType::FaceDotEditUVData:
|
||||
created_vbos[i] = extract_face_dots_edituv_data(mr);
|
||||
break;
|
||||
case VBOType::SkinRoots:
|
||||
created_vbos[i] = extract_skin_roots(mr);
|
||||
break;
|
||||
case VBOType::IndexVert:
|
||||
created_vbos[i] = extract_vert_index(mr);
|
||||
break;
|
||||
case VBOType::IndexEdge:
|
||||
created_vbos[i] = extract_edge_index(mr);
|
||||
break;
|
||||
case VBOType::IndexFace:
|
||||
created_vbos[i] = extract_face_index(mr);
|
||||
break;
|
||||
case VBOType::IndexFaceDot:
|
||||
created_vbos[i] = extract_face_dot_index(mr);
|
||||
break;
|
||||
case VBOType::Attr0:
|
||||
case VBOType::Attr1:
|
||||
case VBOType::Attr2:
|
||||
case VBOType::Attr3:
|
||||
case VBOType::Attr5:
|
||||
case VBOType::Attr6:
|
||||
case VBOType::Attr7:
|
||||
case VBOType::Attr8:
|
||||
case VBOType::Attr9:
|
||||
case VBOType::Attr10:
|
||||
case VBOType::Attr11:
|
||||
case VBOType::Attr12:
|
||||
case VBOType::Attr13:
|
||||
case VBOType::Attr14:
|
||||
case VBOType::Attr15: {
|
||||
const int8_t attr_index = int8_t(vbos_to_create[i]) - int8_t(VBOType::Attr0);
|
||||
created_vbos[i] = extract_attribute(mr, cache.attr_used[attr_index]);
|
||||
break;
|
||||
}
|
||||
case VBOType::AttrViewer:
|
||||
created_vbos[i] = extract_attr_viewer(mr);
|
||||
break;
|
||||
case VBOType::VertexNormal:
|
||||
created_vbos[i] = extract_vert_normals(mr);
|
||||
break;
|
||||
case VBOType::PaintOverlayFlag:
|
||||
created_vbos[i] = extract_paint_overlay_flags(mr);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
for (const int i : ibos_to_create.index_range()) {
|
||||
buffers.ibos.add_new(ibos_to_create[i], std::move(created_ibos[i]));
|
||||
}
|
||||
for (const int i : vbos_to_create.index_range()) {
|
||||
buffers.vbos.add_new(vbos_to_create[i], std::move(created_vbos[i]));
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name Subdivision Extract Loop
|
||||
* \{ */
|
||||
|
||||
void mesh_buffer_cache_create_requested_subdiv(MeshBatchCache &cache,
|
||||
MeshBufferCache &mbc,
|
||||
const Span<IBOType> ibo_requests,
|
||||
const Span<VBOType> vbo_requests,
|
||||
DRWSubdivCache &subdiv_cache,
|
||||
MeshRenderData &mr)
|
||||
{
|
||||
if (ibo_requests.is_empty() && vbo_requests.is_empty()) {
|
||||
return;
|
||||
}
|
||||
MeshBufferList &buffers = mbc.buff;
|
||||
|
||||
mesh_render_data_update_corner_normals(mr);
|
||||
mesh_render_data_update_loose_geom(mr, mbc);
|
||||
DRW_subdivide_loose_geom(subdiv_cache, mbc);
|
||||
|
||||
Set<IBOType, 16> ibos_to_create;
|
||||
for (const IBOType request : ibo_requests) {
|
||||
if (!buffers.ibos.contains(request)) {
|
||||
ibos_to_create.add_new(request);
|
||||
}
|
||||
}
|
||||
|
||||
Set<VBOType, 16> vbos_to_create;
|
||||
for (const VBOType request : vbo_requests) {
|
||||
if (!buffers.vbos.contains(request)) {
|
||||
vbos_to_create.add_new(request);
|
||||
}
|
||||
}
|
||||
|
||||
if (ibos_to_create.is_empty() && vbos_to_create.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
static gpu::DebugScope subdiv_extract_scope = {"SubdivExtraction"};
|
||||
auto capture = subdiv_extract_scope.scoped_capture();
|
||||
|
||||
if (vbos_to_create.contains(VBOType::Position) || vbos_to_create.contains(VBOType::Orco)) {
|
||||
gpu::VertBufPtr orco_vbo;
|
||||
/* Don't use `add_new` because #VBOType::Orco might be requested after #VBOType::Position
|
||||
* already exists. It's inefficient to build the position VBO a second time but that's the API
|
||||
* that GPU subdivision provides. */
|
||||
buffers.vbos.add(
|
||||
VBOType::Position,
|
||||
extract_positions_subdiv(
|
||||
subdiv_cache, mr, vbos_to_create.contains(VBOType::Orco) ? &orco_vbo : nullptr));
|
||||
if (orco_vbo) {
|
||||
buffers.vbos.add_new(VBOType::Orco, std::move(orco_vbo));
|
||||
}
|
||||
}
|
||||
if (vbos_to_create.contains(VBOType::CornerNormal)) {
|
||||
/* The corner normals calculation uses positions and normals stored in the `pos` VBO. */
|
||||
buffers.vbos.add_new(
|
||||
VBOType::CornerNormal,
|
||||
extract_normals_subdiv(mr, subdiv_cache, *buffers.vbos.lookup(VBOType::Position)));
|
||||
}
|
||||
if (vbos_to_create.contains(VBOType::EdgeFactor)) {
|
||||
buffers.vbos.add_new(
|
||||
VBOType::EdgeFactor,
|
||||
extract_edge_factor_subdiv(subdiv_cache, mr, *buffers.vbos.lookup(VBOType::Position)));
|
||||
}
|
||||
if (ibos_to_create.contains(IBOType::Lines) || ibos_to_create.contains(IBOType::LinesLoose)) {
|
||||
gpu::IndexBufPtr lines_ibo;
|
||||
gpu::IndexBufPtr lines_loose_ibo;
|
||||
extract_lines_subdiv(subdiv_cache,
|
||||
mr,
|
||||
ibos_to_create.contains(IBOType::Lines) ? &lines_ibo : nullptr,
|
||||
ibos_to_create.contains(IBOType::LinesLoose) ? &lines_loose_ibo : nullptr,
|
||||
cache.no_loose_wire);
|
||||
if (lines_ibo) {
|
||||
buffers.ibos.add_new(IBOType::Lines, std::move(lines_ibo));
|
||||
}
|
||||
if (lines_loose_ibo) {
|
||||
buffers.ibos.add_new(IBOType::LinesLoose, std::move(lines_loose_ibo));
|
||||
}
|
||||
}
|
||||
if (ibos_to_create.contains(IBOType::Tris)) {
|
||||
buffers.ibos.add_new(IBOType::Tris, extract_tris_subdiv(subdiv_cache, cache));
|
||||
}
|
||||
if (ibos_to_create.contains(IBOType::Points)) {
|
||||
buffers.ibos.add_new(IBOType::Points, extract_points_subdiv(mr, subdiv_cache));
|
||||
}
|
||||
if (vbos_to_create.contains(VBOType::EditData)) {
|
||||
buffers.vbos.add_new(VBOType::EditData, extract_edit_data_subdiv(mr, subdiv_cache));
|
||||
}
|
||||
if (vbos_to_create.contains(VBOType::Tangents)) {
|
||||
buffers.vbos.add_new(VBOType::Tangents, extract_tangents_subdiv(mr, subdiv_cache, cache));
|
||||
}
|
||||
if (vbos_to_create.contains(VBOType::IndexVert)) {
|
||||
buffers.vbos.add_new(VBOType::IndexVert, extract_vert_index_subdiv(subdiv_cache, mr));
|
||||
}
|
||||
if (vbos_to_create.contains(VBOType::IndexEdge)) {
|
||||
buffers.vbos.add_new(VBOType::IndexEdge, extract_edge_index_subdiv(subdiv_cache, mr));
|
||||
}
|
||||
if (vbos_to_create.contains(VBOType::IndexFace)) {
|
||||
buffers.vbos.add_new(VBOType::IndexFace, extract_face_index_subdiv(subdiv_cache, mr));
|
||||
}
|
||||
if (vbos_to_create.contains(VBOType::VertexGroupWeight)) {
|
||||
buffers.vbos.add_new(VBOType::VertexGroupWeight,
|
||||
extract_weights_subdiv(mr, subdiv_cache, cache));
|
||||
}
|
||||
if (vbos_to_create.contains(VBOType::FaceDotNormal) ||
|
||||
vbos_to_create.contains(VBOType::FaceDotPosition) ||
|
||||
ibos_to_create.contains(IBOType::FaceDots))
|
||||
{
|
||||
gpu::VertBufPtr face_dot_position_vbo;
|
||||
gpu::VertBufPtr face_dot_normal_vbo;
|
||||
gpu::IndexBufPtr face_dot_ibo;
|
||||
|
||||
/* We use only one extractor for face dots, as the work is done in a single compute shader. */
|
||||
extract_face_dots_subdiv(
|
||||
subdiv_cache,
|
||||
face_dot_position_vbo,
|
||||
vbos_to_create.contains(VBOType::FaceDotNormal) ? &face_dot_normal_vbo : nullptr,
|
||||
face_dot_ibo);
|
||||
if (vbos_to_create.contains(VBOType::FaceDotPosition)) {
|
||||
buffers.vbos.add_new(VBOType::FaceDotPosition, std::move(face_dot_position_vbo));
|
||||
}
|
||||
if (face_dot_normal_vbo) {
|
||||
buffers.vbos.add_new(VBOType::FaceDotNormal, std::move(face_dot_normal_vbo));
|
||||
}
|
||||
if (ibos_to_create.contains(IBOType::FaceDots)) {
|
||||
buffers.ibos.add_new(IBOType::FaceDots, std::move(face_dot_ibo));
|
||||
}
|
||||
}
|
||||
if (vbos_to_create.contains(VBOType::PaintOverlayFlag)) {
|
||||
buffers.vbos.add_new(VBOType::PaintOverlayFlag,
|
||||
extract_paint_overlay_flags_subdiv(mr, subdiv_cache));
|
||||
}
|
||||
if (ibos_to_create.contains(IBOType::LinesPaintMask)) {
|
||||
buffers.ibos.add_new(IBOType::LinesPaintMask,
|
||||
extract_lines_paint_mask_subdiv(mr, subdiv_cache));
|
||||
}
|
||||
if (ibos_to_create.contains(IBOType::LinesAdjacency)) {
|
||||
buffers.ibos.add_new(IBOType::LinesAdjacency,
|
||||
extract_lines_adjacency_subdiv(subdiv_cache, cache.is_manifold));
|
||||
}
|
||||
if (vbos_to_create.contains(VBOType::SculptData)) {
|
||||
buffers.vbos.add_new(VBOType::SculptData, extract_sculpt_data_subdiv(mr, subdiv_cache));
|
||||
}
|
||||
if (vbos_to_create.contains(VBOType::UVs)) {
|
||||
/* Make sure UVs are computed before edituv stuffs. */
|
||||
buffers.vbos.add_new(VBOType::UVs, extract_uv_maps_subdiv(subdiv_cache, cache));
|
||||
}
|
||||
if (ibos_to_create.contains(IBOType::AllUVLines)) {
|
||||
buffers.ibos.add_new(IBOType::AllUVLines,
|
||||
extract_edituv_lines_subdiv(mr, subdiv_cache, UvExtractionMode::All));
|
||||
}
|
||||
if (ibos_to_create.contains(IBOType::UVLines)) {
|
||||
buffers.ibos.add_new(
|
||||
IBOType::UVLines,
|
||||
extract_edituv_lines_subdiv(mr, subdiv_cache, UvExtractionMode::Selection));
|
||||
}
|
||||
if (vbos_to_create.contains(VBOType::EditUVStretchArea)) {
|
||||
buffers.vbos.add_new(
|
||||
VBOType::EditUVStretchArea,
|
||||
extract_edituv_stretch_area_subdiv(mr, subdiv_cache, cache.tot_area, cache.tot_uv_area));
|
||||
}
|
||||
if (vbos_to_create.contains(VBOType::EditUVStretchAngle)) {
|
||||
buffers.vbos.add_new(VBOType::EditUVStretchAngle,
|
||||
extract_edituv_stretch_angle_subdiv(mr, subdiv_cache, cache));
|
||||
}
|
||||
if (vbos_to_create.contains(VBOType::EditUVData)) {
|
||||
buffers.vbos.add_new(VBOType::EditUVData, extract_edituv_data_subdiv(mr, subdiv_cache));
|
||||
}
|
||||
if (ibos_to_create.contains(IBOType::EditUVTris)) {
|
||||
buffers.ibos.add_new(IBOType::EditUVTris, extract_edituv_tris_subdiv(mr, subdiv_cache));
|
||||
}
|
||||
if (ibos_to_create.contains(IBOType::EditUVLines)) {
|
||||
buffers.ibos.add_new(IBOType::EditUVLines,
|
||||
extract_edituv_lines_subdiv(mr, subdiv_cache, UvExtractionMode::Edit));
|
||||
}
|
||||
if (ibos_to_create.contains(IBOType::EditUVPoints)) {
|
||||
buffers.ibos.add_new(IBOType::EditUVPoints, extract_edituv_points_subdiv(mr, subdiv_cache));
|
||||
}
|
||||
for (const int8_t i : IndexRange(GPU_MAX_ATTR)) {
|
||||
const VBOType request = VBOType(int8_t(VBOType::Attr0) + i);
|
||||
if (vbos_to_create.contains(request)) {
|
||||
buffers.vbos.add_new(request,
|
||||
extract_attribute_subdiv(mr, subdiv_cache, cache.attr_used[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::draw
|
||||
@@ -0,0 +1,583 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*
|
||||
* \brief Extraction of Mesh data into VBO to feed to GPU.
|
||||
*/
|
||||
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_array_utils.hh"
|
||||
#include "BLI_enumerable_thread_specific.hh"
|
||||
#include "BLI_math_geom.h"
|
||||
#include "BLI_task.hh"
|
||||
#include "BLI_virtual_array.hh"
|
||||
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_editmesh.hh"
|
||||
#include "BKE_editmesh_cache.hh"
|
||||
#include "BKE_material.hh"
|
||||
#include "BKE_mesh.hh"
|
||||
#include "BKE_object.hh"
|
||||
#include "BKE_object_types.hh"
|
||||
|
||||
#include "ED_mesh.hh"
|
||||
|
||||
#include "DRW_render.hh"
|
||||
|
||||
#include "mesh_extractors/extract_mesh.hh"
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name Update Loose Geometry
|
||||
* \{ */
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
static void mesh_render_data_loose_geom_build(const MeshRenderData &mr, MeshBufferCache &cache)
|
||||
{
|
||||
if (mr.extract_type == MeshExtractType::Mesh) {
|
||||
cache.loose_geom.verts = mr.mesh->loose_verts();
|
||||
cache.loose_geom.edges = mr.mesh->loose_edges();
|
||||
}
|
||||
else {
|
||||
BMesh &bm = *mr.bm;
|
||||
/* Use LinearAllocator instead of IndexMaskMemory to avoid over-allocation. */
|
||||
cache.loose_geom.allocator = std::make_unique<LinearAllocator<>>();
|
||||
cache.loose_geom.verts = IndexMask::from_predicate(
|
||||
IndexRange(bm.totvert), *cache.loose_geom.allocator, [&](const int i) {
|
||||
return BM_vert_at_index(&bm, i)->e == nullptr;
|
||||
});
|
||||
cache.loose_geom.edges = IndexMask::from_predicate(
|
||||
IndexRange(bm.totedge), *cache.loose_geom.allocator, [&](const int i) {
|
||||
return BM_edge_at_index(&bm, i)->l == nullptr;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static void mesh_render_data_loose_geom_ensure(const MeshRenderData &mr, MeshBufferCache &cache)
|
||||
{
|
||||
/* Early exit: Are loose geometry already available.
|
||||
* Only checking for loose verts as loose edges and verts are calculated at the same time. */
|
||||
if (!cache.loose_geom.verts.is_empty()) {
|
||||
return;
|
||||
}
|
||||
mesh_render_data_loose_geom_build(mr, cache);
|
||||
}
|
||||
|
||||
void mesh_render_data_update_loose_geom(MeshRenderData &mr, MeshBufferCache &cache)
|
||||
{
|
||||
mesh_render_data_loose_geom_ensure(mr, cache);
|
||||
mr.loose_edges = cache.loose_geom.edges;
|
||||
mr.loose_verts = cache.loose_geom.verts;
|
||||
mr.loose_verts_num = cache.loose_geom.verts.size();
|
||||
mr.loose_edges_num = cache.loose_geom.edges.size();
|
||||
|
||||
mr.loose_indices_num = mr.loose_verts_num + (mr.loose_edges_num * 2);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name Polygons sorted per material
|
||||
*
|
||||
* Contains face indices sorted based on their material.
|
||||
* \{ */
|
||||
|
||||
static void accumululate_material_counts_bm(
|
||||
const BMesh &bm, threading::EnumerableThreadSpecific<Array<int>> &all_tri_counts)
|
||||
{
|
||||
threading::parallel_for(IndexRange(bm.totface), 1024, [&](const IndexRange range) {
|
||||
Array<int> &tri_counts = all_tri_counts.local();
|
||||
const short last_index = tri_counts.size() - 1;
|
||||
for (const int i : range) {
|
||||
const BMFace &face = *BM_face_at_index(&const_cast<BMesh &>(bm), i);
|
||||
if (!BM_elem_flag_test(&face, BM_ELEM_HIDDEN)) {
|
||||
const short mat = std::clamp<short>(face.mat_nr, 0, last_index);
|
||||
tri_counts[mat] += face.len - 2;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void accumululate_material_counts_mesh(
|
||||
const MeshRenderData &mr, threading::EnumerableThreadSpecific<Array<int>> &all_tri_counts)
|
||||
{
|
||||
const OffsetIndices faces = mr.faces;
|
||||
const Span<bool> hide_poly = mr.hide_poly;
|
||||
const Span material_indices = mr.material_indices;
|
||||
if (material_indices.is_empty()) {
|
||||
if (!hide_poly.is_empty()) {
|
||||
all_tri_counts.local().first() = threading::parallel_reduce(
|
||||
faces.index_range(),
|
||||
4096,
|
||||
0,
|
||||
[&](const IndexRange range, int count) {
|
||||
for (const int face : range) {
|
||||
if (!hide_poly[face]) {
|
||||
count += bke::mesh::face_triangles_num(faces[face].size());
|
||||
}
|
||||
}
|
||||
return count;
|
||||
},
|
||||
std::plus<>());
|
||||
}
|
||||
else {
|
||||
all_tri_counts.local().first() = poly_to_tri_count(mr.faces_num, mr.corners_num);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
threading::parallel_for(material_indices.index_range(), 1024, [&](const IndexRange range) {
|
||||
Array<int> &tri_counts = all_tri_counts.local();
|
||||
const int last_index = tri_counts.size() - 1;
|
||||
if (!hide_poly.is_empty()) {
|
||||
for (const int i : range) {
|
||||
if (!hide_poly[i]) {
|
||||
const int mat = std::clamp(material_indices[i], 0, last_index);
|
||||
tri_counts[mat] += bke::mesh::face_triangles_num(faces[i].size());
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (const int i : range) {
|
||||
const int mat = std::clamp(material_indices[i], 0, last_index);
|
||||
tri_counts[mat] += bke::mesh::face_triangles_num(faces[i].size());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* Count how many triangles for each material. */
|
||||
static Array<int> mesh_render_data_mat_tri_len_build(const MeshRenderData &mr)
|
||||
{
|
||||
threading::EnumerableThreadSpecific<Array<int>> all_tri_counts(
|
||||
[&]() { return Array<int>(mr.materials_num, 0); });
|
||||
|
||||
if (mr.extract_type == MeshExtractType::BMesh) {
|
||||
accumululate_material_counts_bm(*mr.bm, all_tri_counts);
|
||||
}
|
||||
else {
|
||||
accumululate_material_counts_mesh(mr, all_tri_counts);
|
||||
}
|
||||
|
||||
Array<int> &tris_num_by_material = all_tri_counts.local();
|
||||
for (const Array<int> &counts : all_tri_counts) {
|
||||
if (&counts != &tris_num_by_material) {
|
||||
for (const int i : tris_num_by_material.index_range()) {
|
||||
tris_num_by_material[i] += counts[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::move(tris_num_by_material);
|
||||
}
|
||||
|
||||
static Array<int> calc_face_tri_starts_bmesh(const MeshRenderData &mr,
|
||||
MutableSpan<int> material_tri_starts)
|
||||
{
|
||||
BMesh &bm = *mr.bm;
|
||||
Array<int> face_tri_offsets(bm.totface);
|
||||
#ifndef NDEBUG
|
||||
face_tri_offsets.fill(-1);
|
||||
#endif
|
||||
|
||||
const int mat_last = mr.materials_num - 1;
|
||||
BMIter iter;
|
||||
BMFace *face;
|
||||
int i;
|
||||
BM_ITER_MESH_INDEX (face, &iter, &bm, BM_FACES_OF_MESH, i) {
|
||||
if (BM_elem_flag_test(face, BM_ELEM_HIDDEN)) {
|
||||
continue;
|
||||
}
|
||||
const int mat = std::clamp(int(face->mat_nr), 0, mat_last);
|
||||
face_tri_offsets[i] = material_tri_starts[mat];
|
||||
material_tri_starts[mat] += face->len - 2;
|
||||
}
|
||||
|
||||
return face_tri_offsets;
|
||||
}
|
||||
|
||||
static bool mesh_is_single_material(const OffsetIndices<int> material_tri_starts)
|
||||
{
|
||||
const int used_materials = std::count_if(
|
||||
material_tri_starts.index_range().begin(),
|
||||
material_tri_starts.index_range().end(),
|
||||
[&](const int i) { return material_tri_starts[i].size() > 0; });
|
||||
return used_materials == 1;
|
||||
}
|
||||
|
||||
static std::optional<Array<int>> calc_face_tri_starts_mesh(const MeshRenderData &mr,
|
||||
MutableSpan<int> material_tri_starts)
|
||||
{
|
||||
const bool single_material = mesh_is_single_material(material_tri_starts.as_span());
|
||||
if (single_material && mr.hide_poly.is_empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const OffsetIndices faces = mr.faces;
|
||||
const Span<bool> hide_poly = mr.hide_poly;
|
||||
|
||||
Array<int> face_tri_offsets(faces.size());
|
||||
#ifndef NDEBUG
|
||||
face_tri_offsets.fill(-1);
|
||||
#endif
|
||||
|
||||
if (single_material) {
|
||||
int offset = 0;
|
||||
for (const int face : faces.index_range()) {
|
||||
if (hide_poly[face]) {
|
||||
continue;
|
||||
}
|
||||
face_tri_offsets[face] = offset;
|
||||
offset += bke::mesh::face_triangles_num(faces[face].size());
|
||||
}
|
||||
return face_tri_offsets;
|
||||
}
|
||||
|
||||
const Span<int> material_indices = mr.material_indices;
|
||||
const int mat_last = mr.materials_num - 1;
|
||||
for (const int face : faces.index_range()) {
|
||||
if (!hide_poly.is_empty() && hide_poly[face]) {
|
||||
continue;
|
||||
}
|
||||
const int mat = std::clamp(material_indices[face], 0, mat_last);
|
||||
face_tri_offsets[face] = material_tri_starts[mat];
|
||||
material_tri_starts[mat] += bke::mesh::face_triangles_num(faces[face].size());
|
||||
}
|
||||
|
||||
return face_tri_offsets;
|
||||
}
|
||||
|
||||
static SortedFaceData mesh_render_data_faces_sorted_build(const MeshRenderData &mr)
|
||||
{
|
||||
SortedFaceData cache;
|
||||
cache.tris_num_by_material = mesh_render_data_mat_tri_len_build(mr);
|
||||
const Span<int> tris_num_by_material = cache.tris_num_by_material;
|
||||
|
||||
Array<int, 32> material_tri_starts(mr.materials_num + 1);
|
||||
material_tri_starts.as_mutable_span().drop_back(1).copy_from(tris_num_by_material);
|
||||
offset_indices::accumulate_counts_to_offsets(material_tri_starts);
|
||||
cache.visible_tris_num = material_tri_starts.last();
|
||||
|
||||
/* Sort per material. */
|
||||
if (mr.extract_type == MeshExtractType::BMesh) {
|
||||
cache.face_tri_offsets = calc_face_tri_starts_bmesh(mr, material_tri_starts);
|
||||
}
|
||||
else {
|
||||
cache.face_tri_offsets = calc_face_tri_starts_mesh(mr, material_tri_starts);
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
const SortedFaceData &mesh_render_data_faces_sorted_ensure(const MeshRenderData &mr,
|
||||
MeshBufferCache &cache)
|
||||
{
|
||||
if (cache.face_sorted.visible_tris_num == 0) {
|
||||
cache.face_sorted = mesh_render_data_faces_sorted_build(mr);
|
||||
}
|
||||
return cache.face_sorted;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name Mesh/BMesh Interface (indirect, partially cached access to complex data).
|
||||
* \{ */
|
||||
|
||||
static bool bm_edge_is_sharp(const BMEdge *const &edge)
|
||||
{
|
||||
return !BM_elem_flag_test(edge, BM_ELEM_SMOOTH);
|
||||
}
|
||||
|
||||
static bool bm_face_is_sharp(const BMFace *const &face)
|
||||
{
|
||||
return !BM_elem_flag_test(face, BM_ELEM_SMOOTH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns which domain of normals is required because of sharp and smooth flags.
|
||||
* Similar to #Mesh::normals_domain().
|
||||
*/
|
||||
static bke::MeshNormalDomain bmesh_normals_domain(BMesh *bm)
|
||||
{
|
||||
if (bm->totface == 0) {
|
||||
return bke::MeshNormalDomain::Point;
|
||||
}
|
||||
|
||||
if (CustomData_has_layer_named(&bm->vdata, CD_PROP_FLOAT3, "custom_normal")) {
|
||||
return bke::MeshNormalDomain::Point;
|
||||
}
|
||||
if (CustomData_has_layer_named(&bm->pdata, CD_PROP_FLOAT3, "custom_normal")) {
|
||||
return bke::MeshNormalDomain::Face;
|
||||
}
|
||||
if (CustomData_has_layer_named(&bm->ldata, CD_PROP_FLOAT3, "custom_normal")) {
|
||||
return bke::MeshNormalDomain::Corner;
|
||||
}
|
||||
if (CustomData_has_layer_named(&bm->ldata, CD_PROP_INT16_2D, "custom_normal")) {
|
||||
return bke::MeshNormalDomain::Corner;
|
||||
}
|
||||
|
||||
BM_mesh_elem_table_ensure(bm, BM_FACE);
|
||||
const VArray<bool> sharp_faces =
|
||||
VArray<bool>::from_derived_span<const BMFace *, bm_face_is_sharp>(
|
||||
Span(bm->ftable, bm->totface));
|
||||
const array_utils::BooleanMix face_mix = array_utils::booleans_mix_calc(sharp_faces);
|
||||
if (face_mix == array_utils::BooleanMix::AllTrue) {
|
||||
return bke::MeshNormalDomain::Face;
|
||||
}
|
||||
|
||||
BM_mesh_elem_table_ensure(bm, BM_EDGE);
|
||||
const VArray<bool> sharp_edges =
|
||||
VArray<bool>::from_derived_span<const BMEdge *, bm_edge_is_sharp>(
|
||||
Span(bm->etable, bm->totedge));
|
||||
const array_utils::BooleanMix edge_mix = array_utils::booleans_mix_calc(sharp_edges);
|
||||
if (edge_mix == array_utils::BooleanMix::AllTrue) {
|
||||
return bke::MeshNormalDomain::Face;
|
||||
}
|
||||
|
||||
if (edge_mix == array_utils::BooleanMix::AllFalse &&
|
||||
face_mix == array_utils::BooleanMix::AllFalse)
|
||||
{
|
||||
return bke::MeshNormalDomain::Point;
|
||||
}
|
||||
|
||||
return bke::MeshNormalDomain::Corner;
|
||||
}
|
||||
|
||||
void mesh_render_data_update_corner_normals(MeshRenderData &mr)
|
||||
{
|
||||
if (mr.extract_type == MeshExtractType::Mesh) {
|
||||
mr.corner_normals = mr.mesh->corner_normals();
|
||||
}
|
||||
else {
|
||||
if (mr.bm_free_normal_offset_vert != -1 || mr.bm_free_normal_offset_face != -1 ||
|
||||
mr.bm_free_normal_offset_corner != -1)
|
||||
{
|
||||
/* If there are free custom normals they should be used directly. */
|
||||
mr.bm_loop_normals = {};
|
||||
return;
|
||||
}
|
||||
mr.bm_loop_normals.reinitialize(mr.corners_num);
|
||||
const int clnors_offset = CustomData_get_offset_named(
|
||||
&mr.bm->ldata, CD_PROP_INT16_2D, "custom_normal");
|
||||
BM_loops_calc_normal_vcos(mr.bm,
|
||||
mr.bm_vert_coords,
|
||||
mr.bm_vert_normals,
|
||||
mr.bm_face_normals,
|
||||
true,
|
||||
mr.bm_loop_normals,
|
||||
nullptr,
|
||||
nullptr,
|
||||
clnors_offset,
|
||||
false);
|
||||
}
|
||||
}
|
||||
|
||||
void mesh_render_data_update_face_normals(MeshRenderData &mr)
|
||||
{
|
||||
if (mr.extract_type == MeshExtractType::Mesh) {
|
||||
/* Eager calculation of face normals can reduce waiting on the lazy cache's lock. */
|
||||
mr.face_normals = mr.mesh->face_normals();
|
||||
}
|
||||
else {
|
||||
/* Use #BMFace.no. */
|
||||
}
|
||||
}
|
||||
|
||||
static void retrieve_active_attribute_names(MeshRenderData &mr,
|
||||
const Object &object,
|
||||
const Mesh &mesh)
|
||||
{
|
||||
const Mesh &mesh_final = editmesh_final_or_this(object, mesh);
|
||||
mr.active_color_name = mesh_final.active_color_attribute ? mesh_final.active_color_attribute :
|
||||
mesh_final.default_color_attribute;
|
||||
mr.default_color_name = mesh_final.default_color_attribute;
|
||||
}
|
||||
|
||||
static BMEditMesh *mesh_get_original_edit_mesh(const Object &object)
|
||||
{
|
||||
BLI_assert(object.type == OB_MESH);
|
||||
if (const ID *data_orig = object.runtime->data_orig) {
|
||||
if (GS(data_orig->name) == blender::ID_ME) {
|
||||
return id_cast<const Mesh *>(data_orig)->runtime->edit_mesh.get();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MeshRenderData mesh_render_data_create(Object &object,
|
||||
Mesh &mesh,
|
||||
const bool is_editmode,
|
||||
const bool is_paint_mode,
|
||||
const bool do_final,
|
||||
const bool do_uvedit,
|
||||
const bool use_hide,
|
||||
const ToolSettings *ts)
|
||||
{
|
||||
MeshRenderData mr{};
|
||||
mr.toolsettings = ts;
|
||||
mr.materials_num = BKE_object_material_used_with_fallback_eval(object);
|
||||
|
||||
mr.use_hide = use_hide;
|
||||
|
||||
if (BMEditMesh *edit_mesh = mesh_get_original_edit_mesh(object)) {
|
||||
const Mesh *eval_cage = DRW_object_get_editmesh_cage_for_drawing(object);
|
||||
|
||||
mr.bm = edit_mesh->bm;
|
||||
mr.edit_bmesh = edit_mesh;
|
||||
mr.mesh = (do_final) ? &mesh : eval_cage;
|
||||
mr.edit_data = is_editmode ? mr.mesh->runtime->edit_data.get() : nullptr;
|
||||
|
||||
/* If there is no distinct cage, hide unmapped edges that can't be selected. */
|
||||
mr.hide_unmapped_edges = !do_final || &mesh == eval_cage;
|
||||
|
||||
mr.bm_free_normal_offset_vert = CustomData_get_offset_named(
|
||||
&mr.bm->vdata, CD_PROP_FLOAT3, "custom_normal");
|
||||
mr.bm_free_normal_offset_face = CustomData_get_offset_named(
|
||||
&mr.bm->pdata, CD_PROP_FLOAT3, "custom_normal");
|
||||
mr.bm_free_normal_offset_corner = CustomData_get_offset_named(
|
||||
&mr.bm->ldata, CD_PROP_FLOAT3, "custom_normal");
|
||||
|
||||
if (bke::EditMeshData *emd = mr.edit_data) {
|
||||
if (!emd->vert_positions.is_empty()) {
|
||||
mr.bm_vert_coords = mr.edit_data->vert_positions;
|
||||
if (mr.bm_free_normal_offset_vert == -1) {
|
||||
mr.bm_vert_normals = BKE_editmesh_cache_ensure_vert_normals(*mr.edit_bmesh, *emd);
|
||||
}
|
||||
if (mr.bm_free_normal_offset_face == -1) {
|
||||
mr.bm_face_normals = BKE_editmesh_cache_ensure_face_normals(*mr.edit_bmesh, *emd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int bm_ensure_types = BM_VERT | BM_EDGE | BM_LOOP | BM_FACE;
|
||||
|
||||
BM_mesh_elem_index_ensure(mr.bm, bm_ensure_types);
|
||||
BM_mesh_elem_table_ensure(mr.bm, bm_ensure_types & ~BM_LOOP);
|
||||
|
||||
mr.efa_act_uv = EDBM_uv_active_face_get(mr.edit_bmesh, false, false);
|
||||
mr.efa_act = BM_mesh_active_face_get(mr.bm, false, true);
|
||||
mr.eed_act = BM_mesh_active_edge_get(mr.bm);
|
||||
mr.eve_act = BM_mesh_active_vert_get(mr.bm);
|
||||
|
||||
mr.vert_crease_ofs = CustomData_get_offset_named(&mr.bm->vdata, CD_PROP_FLOAT, "crease_vert");
|
||||
mr.edge_crease_ofs = CustomData_get_offset_named(&mr.bm->edata, CD_PROP_FLOAT, "crease_edge");
|
||||
mr.bweight_ofs = CustomData_get_offset_named(
|
||||
&mr.bm->edata, CD_PROP_FLOAT, "bevel_weight_edge");
|
||||
#ifdef WITH_FREESTYLE
|
||||
mr.freestyle_edge_ofs = CustomData_get_offset_named(
|
||||
&mr.bm->edata, CD_PROP_BOOL, "freestyle_edge");
|
||||
mr.freestyle_face_ofs = CustomData_get_offset_named(
|
||||
&mr.bm->pdata, CD_PROP_BOOL, "freestyle_face");
|
||||
#endif
|
||||
|
||||
/* Use bmesh directly when the object is unchanged by any modifiers. For non-final UVs, always
|
||||
* use original bmesh since the UV editor does not support using the cage mesh with deformed
|
||||
* coordinates. */
|
||||
if ((mr.mesh->runtime->is_original_bmesh &&
|
||||
mr.mesh->runtime->wrapper_type == ME_WRAPPER_TYPE_BMESH) ||
|
||||
(do_uvedit && !do_final))
|
||||
{
|
||||
mr.extract_type = MeshExtractType::BMesh;
|
||||
}
|
||||
else {
|
||||
mr.extract_type = MeshExtractType::Mesh;
|
||||
|
||||
/* Use mapping from final to original mesh when the object is in edit mode. */
|
||||
if (is_editmode && do_final) {
|
||||
mr.orig_index_vert = static_cast<const int *>(
|
||||
CustomData_get_layer(&mr.mesh->vert_data, CD_ORIGINDEX));
|
||||
mr.orig_index_edge = static_cast<const int *>(
|
||||
CustomData_get_layer(&mr.mesh->edge_data, CD_ORIGINDEX));
|
||||
mr.orig_index_face = static_cast<const int *>(
|
||||
CustomData_get_layer(&mr.mesh->face_data, CD_ORIGINDEX));
|
||||
}
|
||||
else {
|
||||
mr.orig_index_vert = nullptr;
|
||||
mr.orig_index_edge = nullptr;
|
||||
mr.orig_index_face = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
mr.mesh = &mesh;
|
||||
mr.edit_bmesh = nullptr;
|
||||
mr.extract_type = MeshExtractType::Mesh;
|
||||
mr.hide_unmapped_edges = false;
|
||||
|
||||
if (is_paint_mode && mr.mesh) {
|
||||
mr.orig_index_vert = static_cast<const int *>(
|
||||
CustomData_get_layer(&mr.mesh->vert_data, CD_ORIGINDEX));
|
||||
mr.orig_index_edge = static_cast<const int *>(
|
||||
CustomData_get_layer(&mr.mesh->edge_data, CD_ORIGINDEX));
|
||||
mr.orig_index_face = static_cast<const int *>(
|
||||
CustomData_get_layer(&mr.mesh->face_data, CD_ORIGINDEX));
|
||||
}
|
||||
else {
|
||||
mr.orig_index_vert = nullptr;
|
||||
mr.orig_index_edge = nullptr;
|
||||
mr.orig_index_face = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (mr.extract_type == MeshExtractType::Mesh) {
|
||||
mr.verts_num = mr.mesh->verts_num;
|
||||
mr.edges_num = mr.mesh->edges_num;
|
||||
mr.faces_num = mr.mesh->faces_num;
|
||||
mr.corners_num = mr.mesh->corners_num;
|
||||
mr.corner_tris_num = poly_to_tri_count(mr.faces_num, mr.corners_num);
|
||||
|
||||
mr.vert_positions = mr.mesh->vert_positions();
|
||||
mr.edges = mr.mesh->edges();
|
||||
mr.faces = mr.mesh->faces();
|
||||
mr.corner_verts = mr.mesh->corner_verts();
|
||||
mr.corner_edges = mr.mesh->corner_edges();
|
||||
|
||||
mr.orig_index_vert = static_cast<const int *>(
|
||||
CustomData_get_layer(&mr.mesh->vert_data, CD_ORIGINDEX));
|
||||
mr.orig_index_edge = static_cast<const int *>(
|
||||
CustomData_get_layer(&mr.mesh->edge_data, CD_ORIGINDEX));
|
||||
mr.orig_index_face = static_cast<const int *>(
|
||||
CustomData_get_layer(&mr.mesh->face_data, CD_ORIGINDEX));
|
||||
|
||||
mr.normals_domain = mr.mesh->normals_domain();
|
||||
|
||||
const bke::AttributeAccessor attributes = mr.mesh->attributes();
|
||||
|
||||
mr.material_indices = *attributes.lookup<int>("material_index", bke::AttrDomain::Face);
|
||||
|
||||
if (is_editmode || is_paint_mode) {
|
||||
if (use_hide) {
|
||||
mr.hide_vert = *attributes.lookup<bool>(".hide_vert", bke::AttrDomain::Point);
|
||||
mr.hide_edge = *attributes.lookup<bool>(".hide_edge", bke::AttrDomain::Edge);
|
||||
mr.hide_poly = *attributes.lookup<bool>(".hide_poly", bke::AttrDomain::Face);
|
||||
}
|
||||
|
||||
mr.select_vert = *attributes.lookup<bool>(".select_vert", bke::AttrDomain::Point);
|
||||
mr.select_edge = *attributes.lookup<bool>(".select_edge", bke::AttrDomain::Edge);
|
||||
mr.select_poly = *attributes.lookup<bool>(".select_poly", bke::AttrDomain::Face);
|
||||
}
|
||||
|
||||
mr.sharp_faces = *attributes.lookup<bool>("sharp_face", bke::AttrDomain::Face);
|
||||
}
|
||||
else {
|
||||
BMesh *bm = mr.bm;
|
||||
|
||||
mr.verts_num = bm->totvert;
|
||||
mr.edges_num = bm->totedge;
|
||||
mr.faces_num = bm->totface;
|
||||
mr.corners_num = bm->totloop;
|
||||
mr.corner_tris_num = poly_to_tri_count(mr.faces_num, mr.corners_num);
|
||||
|
||||
mr.normals_domain = bmesh_normals_domain(bm);
|
||||
}
|
||||
|
||||
retrieve_active_attribute_names(mr, object, *mr.mesh);
|
||||
|
||||
return mr;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::draw
|
||||
342
blender-5.2.0/source/blender/draw/intern/draw_cache_impl.hh
Normal file
342
blender-5.2.0/source/blender/draw/intern/draw_cache_impl.hh
Normal file
@@ -0,0 +1,342 @@
|
||||
/* SPDX-FileCopyrightText: 2016 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "BLI_math_matrix_types.hh"
|
||||
#include "BLI_span.hh"
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
#include "GPU_vertex_buffer.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct GPUMaterial;
|
||||
namespace draw {
|
||||
struct MeshBatchCache;
|
||||
};
|
||||
namespace gpu {
|
||||
class Batch;
|
||||
class UniformBuf;
|
||||
class VertBuf;
|
||||
} // namespace gpu
|
||||
struct ModifierData;
|
||||
struct PTCacheEdit;
|
||||
struct ParticleSystem;
|
||||
struct TaskGraph;
|
||||
|
||||
struct Curve;
|
||||
struct Curves;
|
||||
struct Lattice;
|
||||
struct Mesh;
|
||||
struct Object;
|
||||
struct Scene;
|
||||
struct PointCloud;
|
||||
struct Volume;
|
||||
struct GreasePencil;
|
||||
|
||||
enum eMeshBatchDirtyMode : int8_t;
|
||||
|
||||
namespace draw {
|
||||
|
||||
class ObjectRef;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Expose via BKE callbacks
|
||||
* \{ */
|
||||
|
||||
void DRW_curve_batch_cache_dirty_tag(Curve *cu, int mode);
|
||||
void DRW_curve_batch_cache_validate(Curve *cu);
|
||||
void DRW_curve_batch_cache_free(Curve *cu);
|
||||
|
||||
void DRW_mesh_batch_cache_dirty_tag(Mesh *mesh, eMeshBatchDirtyMode mode);
|
||||
void DRW_mesh_batch_cache_validate(Mesh &mesh);
|
||||
void DRW_mesh_batch_cache_free(draw::MeshBatchCache *batch_cache);
|
||||
|
||||
void DRW_lattice_batch_cache_dirty_tag(Lattice *lt, int mode);
|
||||
void DRW_lattice_batch_cache_validate(Lattice *lt);
|
||||
void DRW_lattice_batch_cache_free(Lattice *lt);
|
||||
|
||||
void DRW_particle_batch_cache_dirty_tag(ParticleSystem *psys, int mode);
|
||||
void DRW_particle_batch_cache_free(ParticleSystem *psys);
|
||||
|
||||
void DRW_curves_batch_cache_dirty_tag(Curves *curves, int mode);
|
||||
void DRW_curves_batch_cache_validate(Curves *curves);
|
||||
void DRW_curves_batch_cache_free(Curves *curves);
|
||||
|
||||
void DRW_pointcloud_batch_cache_dirty_tag(PointCloud *pointcloud, int mode);
|
||||
void DRW_pointcloud_batch_cache_validate(PointCloud *pointcloud);
|
||||
void DRW_pointcloud_batch_cache_free(PointCloud *pointcloud);
|
||||
|
||||
void DRW_volume_batch_cache_dirty_tag(Volume *volume, int mode);
|
||||
void DRW_volume_batch_cache_validate(Volume *volume);
|
||||
void DRW_volume_batch_cache_free(Volume *volume);
|
||||
|
||||
void DRW_grease_pencil_batch_cache_dirty_tag(GreasePencil *grease_pencil, int mode);
|
||||
void DRW_grease_pencil_batch_cache_validate(GreasePencil *grease_pencil);
|
||||
void DRW_grease_pencil_batch_cache_free(GreasePencil *grease_pencil);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Garbage Collection
|
||||
* \{ */
|
||||
|
||||
void DRW_batch_cache_free_old(Object *ob, int ctime);
|
||||
|
||||
/**
|
||||
* Thread safety need to be assured by caller. Don't call this during drawing.
|
||||
* \note For now this only free the shading batches / VBO if any cd layers is not needed anymore.
|
||||
*/
|
||||
void DRW_mesh_batch_cache_free_old(Mesh *mesh, int ctime);
|
||||
void DRW_curves_batch_cache_free_old(Curves *curves, int ctime);
|
||||
void DRW_pointcloud_batch_cache_free_old(PointCloud *pointcloud, int ctime);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Generic
|
||||
* \{ */
|
||||
|
||||
void DRW_vertbuf_create_wiredata(gpu::VertBuf *vbo, int vert_len);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Curve
|
||||
* \{ */
|
||||
|
||||
void DRW_curve_batch_cache_create_requested(Object *ob, const Scene *scene);
|
||||
|
||||
gpu::Batch *DRW_curve_batch_cache_get_wire_edge(Curve *cu);
|
||||
gpu::Batch *DRW_curve_batch_cache_get_wire_edge_viewer_attribute(Curve *cu);
|
||||
gpu::Batch *DRW_curve_batch_cache_get_normal_edge(Curve *cu);
|
||||
gpu::Batch *DRW_curve_batch_cache_get_edit_edges(Curve *cu);
|
||||
gpu::Batch *DRW_curve_batch_cache_get_edit_verts(Curve *cu);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Lattice
|
||||
* \{ */
|
||||
|
||||
gpu::Batch *DRW_lattice_batch_cache_get_all_edges(Lattice *lt, bool use_weight, int actdef);
|
||||
gpu::Batch *DRW_lattice_batch_cache_get_all_verts(Lattice *lt);
|
||||
gpu::Batch *DRW_lattice_batch_cache_get_edit_verts(Lattice *lt);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Curves
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Provide GPU access to a specific evaluated attribute on curves.
|
||||
*
|
||||
* \return A pointer to location where the texture will be
|
||||
* stored, which will be filled by #DRW_shgroup_curves_create_sub.
|
||||
*/
|
||||
gpu::VertBufPtr &DRW_curves_texture_for_evaluated_attribute(Curves *curves,
|
||||
StringRef name,
|
||||
bool &r_is_point_domain,
|
||||
bool &r_valid_attribute);
|
||||
|
||||
gpu::Batch *DRW_curves_batch_cache_get_edit_points(Curves *curves);
|
||||
gpu::Batch *DRW_curves_batch_cache_get_sculpt_curves_cage(Curves *curves);
|
||||
gpu::Batch *DRW_curves_batch_cache_get_edit_curves_handles(Curves *curves);
|
||||
gpu::Batch *DRW_curves_batch_cache_get_edit_curves_lines(Curves *curves);
|
||||
|
||||
void DRW_curves_batch_cache_create_requested(Object *ob);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name PointCloud
|
||||
* \{ */
|
||||
|
||||
gpu::VertBuf *DRW_pointcloud_position_and_radius_buffer_get(Object *ob);
|
||||
|
||||
gpu::VertBuf **DRW_pointcloud_evaluated_attribute(PointCloud *pointcloud, StringRef name);
|
||||
gpu::Batch *DRW_pointcloud_batch_cache_get_dots(Object *ob);
|
||||
gpu::Batch *DRW_pointcloud_batch_cache_get_edit_dots(PointCloud *pointcloud);
|
||||
|
||||
void DRW_pointcloud_batch_cache_create_requested(Object *ob);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Volume
|
||||
* \{ */
|
||||
|
||||
gpu::Batch *DRW_volume_batch_cache_get_wireframes_face(Volume *volume);
|
||||
gpu::Batch *DRW_volume_batch_cache_get_selection_surface(Volume *volume);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Mesh
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Can be called for any surface type. Mesh *mesh is the final mesh.
|
||||
*/
|
||||
void DRW_mesh_batch_cache_create_requested(TaskGraph &task_graph,
|
||||
Object &ob,
|
||||
Mesh &mesh,
|
||||
const Scene &scene,
|
||||
bool is_paint_mode,
|
||||
bool use_hide);
|
||||
|
||||
gpu::Batch *DRW_mesh_batch_cache_get_all_verts(Mesh &mesh);
|
||||
gpu::Batch *DRW_mesh_batch_cache_get_paint_overlay_verts(Mesh &mesh);
|
||||
gpu::Batch *DRW_mesh_batch_cache_get_all_edges(Mesh &mesh);
|
||||
gpu::Batch *DRW_mesh_batch_cache_get_loose_edges(Mesh &mesh);
|
||||
gpu::Batch *DRW_mesh_batch_cache_get_edge_detection(Mesh &mesh, bool *r_is_manifold);
|
||||
gpu::Batch *DRW_mesh_batch_cache_get_surface(Mesh &mesh);
|
||||
gpu::Batch *DRW_mesh_batch_cache_get_paint_overlay_surface(Mesh &mesh);
|
||||
gpu::Batch *DRW_mesh_batch_cache_get_paint_overlay_edges(Mesh &mesh);
|
||||
Span<gpu::Batch *> DRW_mesh_batch_cache_get_surface_shaded(Object &object,
|
||||
Mesh &mesh,
|
||||
Span<const GPUMaterial *> materials);
|
||||
|
||||
Span<gpu::Batch *> DRW_mesh_batch_cache_get_surface_texpaint(Object &object, Mesh &mesh);
|
||||
gpu::Batch *DRW_mesh_batch_cache_get_surface_texpaint_single(Object &object, Mesh &mesh);
|
||||
gpu::Batch *DRW_mesh_batch_cache_get_surface_vertpaint(Object &object, Mesh &mesh);
|
||||
gpu::Batch *DRW_mesh_batch_cache_get_surface_sculpt(Object &object, Mesh &mesh);
|
||||
gpu::Batch *DRW_mesh_batch_cache_get_surface_weights(Mesh &mesh);
|
||||
gpu::Batch *DRW_mesh_batch_cache_get_sculpt_overlays(Mesh &mesh);
|
||||
gpu::Batch *DRW_mesh_batch_cache_get_surface_viewer_attribute(Mesh &mesh);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Edit-Mesh Drawing
|
||||
* \{ */
|
||||
|
||||
gpu::Batch *DRW_mesh_batch_cache_get_edit_triangles(Mesh &mesh);
|
||||
gpu::Batch *DRW_mesh_batch_cache_get_edit_vertices(Mesh &mesh);
|
||||
gpu::Batch *DRW_mesh_batch_cache_get_edit_edges(Mesh &mesh);
|
||||
gpu::Batch *DRW_mesh_batch_cache_get_edit_vert_normals(Mesh &mesh);
|
||||
gpu::Batch *DRW_mesh_batch_cache_get_edit_loop_normals(Mesh &mesh);
|
||||
gpu::Batch *DRW_mesh_batch_cache_get_edit_facedots(Mesh &mesh);
|
||||
gpu::Batch *DRW_mesh_batch_cache_get_edit_skin_roots(Mesh &mesh);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Edit-mesh Selection
|
||||
* \{ */
|
||||
|
||||
gpu::Batch *DRW_mesh_batch_cache_get_triangles_with_select_id(Mesh &mesh);
|
||||
gpu::Batch *DRW_mesh_batch_cache_get_facedots_with_select_id(Mesh &mesh);
|
||||
gpu::Batch *DRW_mesh_batch_cache_get_edges_with_select_id(Mesh &mesh);
|
||||
gpu::Batch *DRW_mesh_batch_cache_get_verts_with_select_id(Mesh &mesh);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Object Mode Wireframe Overlays
|
||||
* \{ */
|
||||
|
||||
gpu::Batch *DRW_mesh_batch_cache_get_wireframes_face(Mesh &mesh);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Edit-mesh UV Editor
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Creates the #gpu::Batch for drawing the UV Stretching Area Overlay.
|
||||
* Optional retrieves the total area or total uv area of the mesh.
|
||||
*
|
||||
* The `cache->tot_area` and `cache->tot_uv_area` update are calculation are
|
||||
* only valid after calling `DRW_mesh_batch_cache_create_requested`.
|
||||
*/
|
||||
gpu::Batch *DRW_mesh_batch_cache_get_edituv_faces_stretch_area(Object &object,
|
||||
Mesh &mesh,
|
||||
float **tot_area,
|
||||
float **tot_uv_area);
|
||||
gpu::Batch *DRW_mesh_batch_cache_get_edituv_faces_stretch_angle(Object &object, Mesh &mesh);
|
||||
gpu::Batch *DRW_mesh_batch_cache_get_edituv_faces(Object &object, Mesh &mesh);
|
||||
gpu::Batch *DRW_mesh_batch_cache_get_edituv_wireframe(Object &object, Mesh &mesh);
|
||||
gpu::Batch *DRW_mesh_batch_cache_get_edituv_edges(Object &object, Mesh &mesh);
|
||||
gpu::Batch *DRW_mesh_batch_cache_get_edituv_verts(Object &object, Mesh &mesh);
|
||||
gpu::Batch *DRW_mesh_batch_cache_get_edituv_facedots(Object &object, Mesh &mesh);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name For Image UV Editor
|
||||
* \{ */
|
||||
|
||||
gpu::Batch *DRW_mesh_batch_cache_get_uv_faces(Object &object, Mesh &mesh);
|
||||
gpu::Batch *DRW_mesh_batch_cache_get_all_uv_wireframe(Object &object, Mesh &mesh);
|
||||
gpu::Batch *DRW_mesh_batch_cache_get_uv_wireframe(Object &object, Mesh &mesh);
|
||||
gpu::Batch *DRW_mesh_batch_cache_get_edit_mesh_analysis(Mesh &mesh);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name For Direct Data Access
|
||||
* \{ */
|
||||
|
||||
/* Edit mesh bit-flags (is this the right place?). */
|
||||
enum {
|
||||
VFLAG_VERT_ACTIVE = 1 << 0,
|
||||
VFLAG_VERT_SELECTED = 1 << 1,
|
||||
VFLAG_VERT_SELECTED_BEZT_HANDLE = 1 << 2,
|
||||
VFLAG_EDGE_ACTIVE = 1 << 3,
|
||||
VFLAG_EDGE_SELECTED = 1 << 4,
|
||||
VFLAG_EDGE_SEAM = 1 << 5,
|
||||
VFLAG_EDGE_SHARP = 1 << 6,
|
||||
VFLAG_EDGE_FREESTYLE = 1 << 7,
|
||||
/* Beware to not go over 1 << 7 (it's a byte flag). */
|
||||
/* NOTE: Grease pencil edit curve use another type of data format that allows for this value. */
|
||||
VFLAG_VERT_GPENCIL_BEZT_HANDLE = 1 << 30,
|
||||
};
|
||||
|
||||
enum {
|
||||
VFLAG_FACE_ACTIVE = 1 << 0,
|
||||
VFLAG_FACE_SELECTED = 1 << 1,
|
||||
VFLAG_FACE_FREESTYLE = 1 << 2,
|
||||
VFLAG_VERT_UV_SELECT = 1 << 3,
|
||||
VFLAG_VERT_UV_PINNED = 1 << 4,
|
||||
VFLAG_EDGE_UV_SELECT = 1 << 5,
|
||||
VFLAG_FACE_UV_ACTIVE = 1 << 6,
|
||||
VFLAG_FACE_UV_SELECT = 1 << 7,
|
||||
/* Beware to not go over 1 << 7 (it's a byte flag). */
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Particles
|
||||
* \{ */
|
||||
|
||||
gpu::Batch *DRW_particles_batch_cache_get_hair(Object *object,
|
||||
ParticleSystem *psys,
|
||||
ModifierData *md);
|
||||
gpu::Batch *DRW_particles_batch_cache_get_dots(Object *object, ParticleSystem *psys);
|
||||
gpu::Batch *DRW_particles_batch_cache_get_edit_strands(Object *object,
|
||||
ParticleSystem *psys,
|
||||
PTCacheEdit *edit,
|
||||
bool use_weight);
|
||||
gpu::Batch *DRW_particles_batch_cache_get_edit_inner_points(Object *object,
|
||||
ParticleSystem *psys,
|
||||
PTCacheEdit *edit);
|
||||
gpu::Batch *DRW_particles_batch_cache_get_edit_tip_points(Object *object,
|
||||
ParticleSystem *psys,
|
||||
PTCacheEdit *edit);
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace draw
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,932 @@
|
||||
/* SPDX-FileCopyrightText: 2017 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*
|
||||
* \brief Curve API for render engines
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_color_types.hh"
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math_rotation.h"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_span.hh"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "DNA_curve_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "BKE_curve.hh"
|
||||
#include "BKE_curves.hh"
|
||||
#include "BKE_geometry_set.hh"
|
||||
#include "BKE_object_types.hh"
|
||||
#include "BKE_vfont.hh"
|
||||
|
||||
#include "GPU_attribute_convert.hh"
|
||||
#include "GPU_batch.hh"
|
||||
#include "GPU_capabilities.hh"
|
||||
#include "GPU_texture.hh"
|
||||
|
||||
#include "UI_resources.hh"
|
||||
|
||||
#include "DRW_render.hh"
|
||||
|
||||
#include "draw_cache_inline.hh"
|
||||
|
||||
#include "draw_cache_impl.hh" /* own include */
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
/* See: edit_curve_point_vert.glsl for duplicate includes. */
|
||||
#define SELECT 1
|
||||
#define ACTIVE_NURB (1 << 2)
|
||||
#define BEZIER_HANDLE (1 << 3)
|
||||
#define EVEN_U_BIT (1 << 4) /* Alternate this bit for every U vert. */
|
||||
#define COLOR_SHIFT 5
|
||||
|
||||
/* Used as values of `color_id` in `edit_curve_overlay_handle_geom.glsl` */
|
||||
enum {
|
||||
COLOR_NURB_ULINE_ID = TH_HANDLE_AUTOCLAMP - TH_HANDLE_FREE + 2,
|
||||
|
||||
TOT_HANDLE_COL,
|
||||
};
|
||||
|
||||
/**
|
||||
* TODO
|
||||
* - Ensure `CurveCache`, `SEQUENCER_DAG_WORKAROUND`.
|
||||
* - Check number of verts/edges to see if cache is valid.
|
||||
* - Check if 'overlay.edges' can use single attribute per edge, not 2 (for selection drawing).
|
||||
*/
|
||||
|
||||
static void curve_batch_cache_clear(Curve *cu);
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Curve Interface, direct access to basic data. */
|
||||
|
||||
static void curve_render_overlay_verts_edges_len_get(ListBaseT<Nurb> *lb,
|
||||
int *r_vert_len,
|
||||
int *r_edge_len)
|
||||
{
|
||||
BLI_assert(r_vert_len || r_edge_len);
|
||||
int vert_len = 0;
|
||||
int edge_len = 0;
|
||||
for (Nurb &nu : *lb) {
|
||||
if (nu.bezt) {
|
||||
vert_len += nu.pntsu * 3;
|
||||
/* 2x handles per point. */
|
||||
edge_len += 2 * nu.pntsu;
|
||||
}
|
||||
else if (nu.bp) {
|
||||
vert_len += nu.pntsu * nu.pntsv;
|
||||
/* segments between points */
|
||||
edge_len += (nu.pntsu - 1) * nu.pntsv;
|
||||
edge_len += (nu.pntsv - 1) * nu.pntsu;
|
||||
}
|
||||
}
|
||||
if (r_vert_len) {
|
||||
*r_vert_len = vert_len;
|
||||
}
|
||||
if (r_edge_len) {
|
||||
*r_edge_len = edge_len;
|
||||
}
|
||||
}
|
||||
|
||||
static void curve_eval_render_wire_verts_edges_len_get(const bke::CurvesGeometry &curves,
|
||||
int *r_curve_len,
|
||||
int *r_vert_len,
|
||||
int *r_edge_len)
|
||||
{
|
||||
const OffsetIndices points_by_curve = curves.evaluated_points_by_curve();
|
||||
const VArray<bool> cyclic = curves.cyclic();
|
||||
|
||||
*r_curve_len = curves.curves_num();
|
||||
*r_vert_len = points_by_curve.total_size();
|
||||
*r_edge_len = 0;
|
||||
for (const int i : curves.curves_range()) {
|
||||
*r_edge_len += bke::curves::segments_num(points_by_curve[i].size(), cyclic[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static int curve_render_normal_len_get(const ListBaseT<Nurb> *lb, const CurveCache *ob_curve_cache)
|
||||
{
|
||||
int normal_len = 0;
|
||||
const BevList *bl;
|
||||
const Nurb *nu;
|
||||
for (bl = static_cast<const BevList *>(ob_curve_cache->bev.first),
|
||||
nu = static_cast<const Nurb *>(lb->first);
|
||||
nu && bl;
|
||||
bl = bl->next, nu = nu->next)
|
||||
{
|
||||
int nr = bl->nr;
|
||||
int skip = nu->resolu / 16;
|
||||
#if 0
|
||||
while (nr-- > 0) { /* accounts for empty bevel lists */
|
||||
normal_len += 1;
|
||||
nr -= skip;
|
||||
}
|
||||
#else
|
||||
/* Same as loop above */
|
||||
normal_len += (nr / (skip + 1)) + ((nr % (skip + 1)) != 0);
|
||||
#endif
|
||||
}
|
||||
return normal_len;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Curve Interface, indirect, partially cached access to complex data. */
|
||||
|
||||
struct CurveRenderData {
|
||||
int types;
|
||||
|
||||
struct {
|
||||
int vert_len;
|
||||
int edge_len;
|
||||
} overlay;
|
||||
|
||||
struct {
|
||||
int curve_len;
|
||||
int vert_len;
|
||||
int edge_len;
|
||||
} wire;
|
||||
|
||||
/* edit mode normal's */
|
||||
struct {
|
||||
/* 'edge_len == len * 2'
|
||||
* 'vert_len == len * 3' */
|
||||
int len;
|
||||
} normal;
|
||||
|
||||
struct {
|
||||
EditFont *edit_font;
|
||||
} text;
|
||||
|
||||
/* borrow from 'Object' */
|
||||
CurveCache *ob_curve_cache;
|
||||
|
||||
/* Owned by the evaluated object's geometry set (#geometry_set_eval). */
|
||||
const Curves *curve_eval;
|
||||
|
||||
/* borrow from 'Curve' */
|
||||
ListBaseT<Nurb> *nurbs;
|
||||
|
||||
/* edit, index in nurb list */
|
||||
int actnu;
|
||||
/* edit, index in active nurb (BPoint or BezTriple) */
|
||||
int actvert;
|
||||
};
|
||||
|
||||
enum {
|
||||
/* Wire center-line */
|
||||
CU_DATATYPE_WIRE = 1 << 0,
|
||||
/* Edit-mode verts and optionally handles */
|
||||
CU_DATATYPE_OVERLAY = 1 << 1,
|
||||
/* Edit-mode normals */
|
||||
CU_DATATYPE_NORMAL = 1 << 2,
|
||||
/* Geometry */
|
||||
CU_DATATYPE_SURFACE = 1 << 3,
|
||||
/* Text */
|
||||
CU_DATATYPE_TEXT_SELECT = 1 << 4,
|
||||
};
|
||||
|
||||
/**
|
||||
* \param ob_curve_cache: can be null.
|
||||
*/
|
||||
static CurveRenderData *curve_render_data_create(Curve *cu,
|
||||
CurveCache *ob_curve_cache,
|
||||
const int types)
|
||||
{
|
||||
CurveRenderData *rdata = MEM_new_zeroed<CurveRenderData>(__func__);
|
||||
rdata->types = types;
|
||||
ListBaseT<Nurb> *nurbs;
|
||||
|
||||
rdata->actnu = cu->actnu;
|
||||
rdata->actvert = cu->actvert;
|
||||
|
||||
rdata->ob_curve_cache = ob_curve_cache;
|
||||
|
||||
rdata->curve_eval = cu->curve_eval;
|
||||
|
||||
if (types & CU_DATATYPE_WIRE) {
|
||||
if (rdata->curve_eval != nullptr) {
|
||||
curve_eval_render_wire_verts_edges_len_get(rdata->curve_eval->geometry.wrap(),
|
||||
&rdata->wire.curve_len,
|
||||
&rdata->wire.vert_len,
|
||||
&rdata->wire.edge_len);
|
||||
}
|
||||
}
|
||||
|
||||
if (cu->editnurb) {
|
||||
EditNurb *editnurb = cu->editnurb;
|
||||
nurbs = &editnurb->nurbs;
|
||||
|
||||
if (types & CU_DATATYPE_OVERLAY) {
|
||||
curve_render_overlay_verts_edges_len_get(
|
||||
nurbs, &rdata->overlay.vert_len, &rdata->overlay.edge_len);
|
||||
|
||||
rdata->actnu = cu->actnu;
|
||||
rdata->actvert = cu->actvert;
|
||||
}
|
||||
if (types & CU_DATATYPE_NORMAL) {
|
||||
rdata->normal.len = curve_render_normal_len_get(nurbs, rdata->ob_curve_cache);
|
||||
}
|
||||
}
|
||||
else {
|
||||
nurbs = &cu->nurb;
|
||||
}
|
||||
|
||||
rdata->nurbs = nurbs;
|
||||
|
||||
rdata->text.edit_font = cu->editfont;
|
||||
|
||||
return rdata;
|
||||
}
|
||||
|
||||
static void curve_render_data_free(CurveRenderData *rdata)
|
||||
{
|
||||
#if 0
|
||||
if (rdata->loose_verts) {
|
||||
MEM_delete(rdata->loose_verts);
|
||||
}
|
||||
#endif
|
||||
MEM_delete(rdata);
|
||||
}
|
||||
|
||||
static int curve_render_data_overlay_verts_len_get(const CurveRenderData *rdata)
|
||||
{
|
||||
BLI_assert(rdata->types & CU_DATATYPE_OVERLAY);
|
||||
return rdata->overlay.vert_len;
|
||||
}
|
||||
|
||||
static int curve_render_data_overlay_edges_len_get(const CurveRenderData *rdata)
|
||||
{
|
||||
BLI_assert(rdata->types & CU_DATATYPE_OVERLAY);
|
||||
return rdata->overlay.edge_len;
|
||||
}
|
||||
|
||||
static int curve_render_data_wire_verts_len_get(const CurveRenderData *rdata)
|
||||
{
|
||||
BLI_assert(rdata->types & CU_DATATYPE_WIRE);
|
||||
return rdata->wire.vert_len;
|
||||
}
|
||||
|
||||
static int curve_render_data_wire_edges_len_get(const CurveRenderData *rdata)
|
||||
{
|
||||
BLI_assert(rdata->types & CU_DATATYPE_WIRE);
|
||||
return rdata->wire.edge_len;
|
||||
}
|
||||
|
||||
static int curve_render_data_wire_curve_len_get(const CurveRenderData *rdata)
|
||||
{
|
||||
BLI_assert(rdata->types & CU_DATATYPE_WIRE);
|
||||
return rdata->wire.curve_len;
|
||||
}
|
||||
|
||||
static int curve_render_data_normal_len_get(const CurveRenderData *rdata)
|
||||
{
|
||||
BLI_assert(rdata->types & CU_DATATYPE_NORMAL);
|
||||
return rdata->normal.len;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Curve gpu::Batch Cache */
|
||||
|
||||
struct CurveBatchCache {
|
||||
struct {
|
||||
gpu::VertBuf *curves_pos;
|
||||
gpu::VertBuf *attr_viewer;
|
||||
} ordered;
|
||||
|
||||
struct {
|
||||
gpu::VertBuf *curves_nor;
|
||||
/* Edit points (beztriples and bpoints) */
|
||||
gpu::VertBuf *pos;
|
||||
gpu::VertBuf *data;
|
||||
} edit;
|
||||
|
||||
struct {
|
||||
gpu::IndexBuf *curves_lines;
|
||||
/* Edit mode */
|
||||
gpu::IndexBuf *edit_verts;
|
||||
gpu::IndexBuf *edit_lines;
|
||||
} ibo;
|
||||
|
||||
struct {
|
||||
gpu::Batch *curves;
|
||||
gpu::Batch *curves_viewer_attribute;
|
||||
/* control handles and vertices */
|
||||
gpu::Batch *edit_edges;
|
||||
gpu::Batch *edit_verts;
|
||||
gpu::Batch *edit_normals;
|
||||
} batch;
|
||||
|
||||
/* settings to determine if cache is invalid */
|
||||
bool is_dirty;
|
||||
bool is_editmode;
|
||||
};
|
||||
|
||||
/* gpu::Batch cache management. */
|
||||
|
||||
static bool curve_batch_cache_valid(Curve *cu)
|
||||
{
|
||||
CurveBatchCache *cache = cu->batch_cache;
|
||||
|
||||
if (cache == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cache->is_dirty) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cache->is_editmode != ((cu->editnurb != nullptr) || (cu->editfont != nullptr))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cache->is_editmode) {
|
||||
if (cu->editfont) {
|
||||
/* TODO */
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void curve_batch_cache_init(Curve *cu)
|
||||
{
|
||||
CurveBatchCache *cache = cu->batch_cache;
|
||||
|
||||
if (!cache) {
|
||||
cache = MEM_new_zeroed<CurveBatchCache>(__func__);
|
||||
cu->batch_cache = cache;
|
||||
}
|
||||
else {
|
||||
memset(cache, 0, sizeof(*cache));
|
||||
}
|
||||
|
||||
#if 0
|
||||
ListBaseT<Nurb> *nurbs;
|
||||
if (cu->editnurb) {
|
||||
EditNurb *editnurb = cu->editnurb;
|
||||
nurbs = &editnurb->nurbs;
|
||||
}
|
||||
else {
|
||||
nurbs = &cu->nurb;
|
||||
}
|
||||
#endif
|
||||
|
||||
cache->is_editmode = (cu->editnurb != nullptr) || (cu->editfont != nullptr);
|
||||
|
||||
cache->is_dirty = false;
|
||||
}
|
||||
|
||||
void DRW_curve_batch_cache_validate(Curve *cu)
|
||||
{
|
||||
if (!curve_batch_cache_valid(cu)) {
|
||||
curve_batch_cache_clear(cu);
|
||||
curve_batch_cache_init(cu);
|
||||
}
|
||||
}
|
||||
|
||||
static CurveBatchCache *curve_batch_cache_get(Curve *cu)
|
||||
{
|
||||
return cu->batch_cache;
|
||||
}
|
||||
|
||||
void DRW_curve_batch_cache_dirty_tag(Curve *cu, int mode)
|
||||
{
|
||||
CurveBatchCache *cache = cu->batch_cache;
|
||||
if (cache == nullptr) {
|
||||
return;
|
||||
}
|
||||
switch (mode) {
|
||||
case BKE_CURVE_BATCH_DIRTY_ALL:
|
||||
cache->is_dirty = true;
|
||||
break;
|
||||
case BKE_CURVE_BATCH_DIRTY_SELECT:
|
||||
GPU_VERTBUF_DISCARD_SAFE(cache->edit.data);
|
||||
|
||||
GPU_BATCH_DISCARD_SAFE(cache->batch.edit_edges);
|
||||
GPU_BATCH_DISCARD_SAFE(cache->batch.edit_verts);
|
||||
break;
|
||||
default:
|
||||
BLI_assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void curve_batch_cache_clear(Curve *cu)
|
||||
{
|
||||
CurveBatchCache *cache = cu->batch_cache;
|
||||
if (!cache) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < sizeof(cache->ordered) / sizeof(void *); i++) {
|
||||
gpu::VertBuf **vbo = reinterpret_cast<gpu::VertBuf **>(&cache->ordered);
|
||||
GPU_VERTBUF_DISCARD_SAFE(vbo[i]);
|
||||
}
|
||||
for (int i = 0; i < sizeof(cache->edit) / sizeof(void *); i++) {
|
||||
gpu::VertBuf **vbo = reinterpret_cast<gpu::VertBuf **>(&cache->edit);
|
||||
GPU_VERTBUF_DISCARD_SAFE(vbo[i]);
|
||||
}
|
||||
for (int i = 0; i < sizeof(cache->ibo) / sizeof(void *); i++) {
|
||||
gpu::IndexBuf **ibo = reinterpret_cast<gpu::IndexBuf **>(&cache->ibo);
|
||||
GPU_INDEXBUF_DISCARD_SAFE(ibo[i]);
|
||||
}
|
||||
for (int i = 0; i < sizeof(cache->batch) / sizeof(void *); i++) {
|
||||
gpu::Batch **batch = reinterpret_cast<gpu::Batch **>(&cache->batch);
|
||||
GPU_BATCH_DISCARD_SAFE(batch[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void DRW_curve_batch_cache_free(Curve *cu)
|
||||
{
|
||||
curve_batch_cache_clear(cu);
|
||||
MEM_SAFE_DELETE(cu->batch_cache);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Private Curve Cache API
|
||||
* \{ */
|
||||
|
||||
/* gpu::Batch cache usage. */
|
||||
static void curve_create_curves_pos(CurveRenderData *rdata, gpu::VertBuf *vbo_curves_pos)
|
||||
{
|
||||
if (rdata->curve_eval == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
static struct {
|
||||
uint pos;
|
||||
} attr_id;
|
||||
|
||||
static const GPUVertFormat format = [&]() {
|
||||
GPUVertFormat format{};
|
||||
attr_id.pos = GPU_vertformat_attr_add(&format, "pos", gpu::VertAttrType::SFLOAT_32_32_32);
|
||||
return format;
|
||||
}();
|
||||
|
||||
const int vert_len = curve_render_data_wire_verts_len_get(rdata);
|
||||
GPU_vertbuf_init_with_format(*vbo_curves_pos, format);
|
||||
GPU_vertbuf_data_alloc(*vbo_curves_pos, vert_len);
|
||||
|
||||
const bke::CurvesGeometry &curves = rdata->curve_eval->geometry.wrap();
|
||||
const Span<float3> positions = curves.evaluated_positions();
|
||||
GPU_vertbuf_attr_fill(vbo_curves_pos, attr_id.pos, positions.data());
|
||||
}
|
||||
|
||||
static void curve_create_attribute(CurveRenderData *rdata, gpu::VertBuf &vbo_attr)
|
||||
{
|
||||
if (rdata->curve_eval == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
static const GPUVertFormat format = []() {
|
||||
GPUVertFormat format{};
|
||||
GPU_vertformat_attr_add(&format, "attribute_value", gpu::VertAttrType::SFLOAT_32_32_32_32);
|
||||
return format;
|
||||
}();
|
||||
|
||||
const int vert_len = curve_render_data_wire_verts_len_get(rdata);
|
||||
GPU_vertbuf_init_with_format(vbo_attr, format);
|
||||
GPU_vertbuf_data_alloc(vbo_attr, vert_len);
|
||||
|
||||
const bke::CurvesGeometry &curves = rdata->curve_eval->geometry.wrap();
|
||||
curves.ensure_can_interpolate_to_evaluated();
|
||||
const VArraySpan colors = *curves.attributes().lookup<ColorGeometry4f>(".viewer",
|
||||
bke::AttrDomain::Point);
|
||||
curves.interpolate_to_evaluated(colors, vbo_attr.data<ColorGeometry4f>());
|
||||
}
|
||||
|
||||
static void curve_create_curves_lines(CurveRenderData *rdata, gpu::IndexBuf *ibo_curve_lines)
|
||||
{
|
||||
if (rdata->curve_eval == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int vert_len = curve_render_data_wire_verts_len_get(rdata);
|
||||
const int edge_len = curve_render_data_wire_edges_len_get(rdata);
|
||||
const int curve_len = curve_render_data_wire_curve_len_get(rdata);
|
||||
/* Count the last vertex or each strip and the primitive restart. */
|
||||
const int index_len = edge_len + curve_len * 2;
|
||||
|
||||
GPUIndexBufBuilder elb;
|
||||
GPU_indexbuf_init_ex(&elb, GPU_PRIM_LINE_STRIP, index_len, vert_len);
|
||||
|
||||
const bke::CurvesGeometry &curves = rdata->curve_eval->geometry.wrap();
|
||||
const OffsetIndices points_by_curve = curves.evaluated_points_by_curve();
|
||||
const VArray<bool> cyclic = curves.cyclic();
|
||||
|
||||
for (const int i : curves.curves_range()) {
|
||||
const IndexRange points = points_by_curve[i];
|
||||
if (cyclic[i] && points.size() > 1) {
|
||||
GPU_indexbuf_add_generic_vert(&elb, points.last());
|
||||
}
|
||||
for (const int i_point : points) {
|
||||
GPU_indexbuf_add_generic_vert(&elb, i_point);
|
||||
}
|
||||
GPU_indexbuf_add_primitive_restart(&elb);
|
||||
}
|
||||
|
||||
GPU_indexbuf_build_in_place(&elb, ibo_curve_lines);
|
||||
}
|
||||
|
||||
static void curve_create_edit_curves_nor(CurveRenderData *rdata,
|
||||
gpu::VertBuf &vbo_curves_nor,
|
||||
const Scene *scene)
|
||||
{
|
||||
const bool do_hq_normals = (scene->r.perf_flag & SCE_PERF_HQ_NORMALS) != 0 ||
|
||||
GPU_use_hq_normals_workaround();
|
||||
|
||||
static struct {
|
||||
uint pos, nor, tan, rad;
|
||||
uint pos_hq, nor_hq, tan_hq, rad_hq;
|
||||
} attr_id;
|
||||
|
||||
static const GPUVertFormat format = [&]() {
|
||||
GPUVertFormat format{};
|
||||
attr_id.pos = GPU_vertformat_attr_add(&format, "pos", gpu::VertAttrType::SFLOAT_32_32_32);
|
||||
attr_id.rad = GPU_vertformat_attr_add(&format, "rad", gpu::VertAttrType::SFLOAT_32);
|
||||
attr_id.nor = GPU_vertformat_attr_add(&format, "nor", gpu::VertAttrType::SNORM_10_10_10_2);
|
||||
attr_id.tan = GPU_vertformat_attr_add(&format, "tangent", gpu::VertAttrType::SNORM_10_10_10_2);
|
||||
return format;
|
||||
}();
|
||||
|
||||
static const GPUVertFormat format_hq = [&]() {
|
||||
GPUVertFormat format{};
|
||||
attr_id.pos_hq = GPU_vertformat_attr_add(&format, "pos", gpu::VertAttrType::SFLOAT_32_32_32);
|
||||
attr_id.rad_hq = GPU_vertformat_attr_add(&format, "rad", gpu::VertAttrType::SFLOAT_32);
|
||||
attr_id.nor_hq = GPU_vertformat_attr_add(&format, "nor", gpu::VertAttrType::SNORM_16_16_16_16);
|
||||
attr_id.tan_hq = GPU_vertformat_attr_add(
|
||||
&format, "tangent", gpu::VertAttrType::SNORM_16_16_16_16);
|
||||
return format;
|
||||
}();
|
||||
|
||||
const GPUVertFormat &format_ptr = do_hq_normals ? format_hq : format;
|
||||
|
||||
int verts_len_capacity = curve_render_data_normal_len_get(rdata) * 2;
|
||||
int vbo_len_used = 0;
|
||||
|
||||
GPU_vertbuf_init_with_format(vbo_curves_nor, format_ptr);
|
||||
GPU_vertbuf_data_alloc(vbo_curves_nor, verts_len_capacity);
|
||||
|
||||
const BevList *bl;
|
||||
const Nurb *nu;
|
||||
|
||||
const uint pos_id = do_hq_normals ? attr_id.pos_hq : attr_id.pos;
|
||||
const uint nor_id = do_hq_normals ? attr_id.nor_hq : attr_id.nor;
|
||||
const uint tan_id = do_hq_normals ? attr_id.tan_hq : attr_id.tan;
|
||||
const uint rad_id = do_hq_normals ? attr_id.rad_hq : attr_id.rad;
|
||||
|
||||
for (bl = static_cast<const BevList *>(rdata->ob_curve_cache->bev.first),
|
||||
nu = static_cast<const Nurb *>(rdata->nurbs->first);
|
||||
nu && bl;
|
||||
bl = bl->next, nu = nu->next)
|
||||
{
|
||||
const BevPoint *bevp = bl->bevpoints;
|
||||
int nr = bl->nr;
|
||||
int skip = nu->resolu / 16;
|
||||
|
||||
while (nr-- > 0) { /* accounts for empty bevel lists */
|
||||
float nor[3] = {1.0f, 0.0f, 0.0f};
|
||||
mul_qt_v3(bevp->quat, nor);
|
||||
|
||||
/* Only set attributes for one vertex. */
|
||||
GPU_vertbuf_attr_set(&vbo_curves_nor, pos_id, vbo_len_used, bevp->vec);
|
||||
GPU_vertbuf_attr_set(&vbo_curves_nor, rad_id, vbo_len_used, &bevp->radius);
|
||||
if (do_hq_normals) {
|
||||
const short4 pnor = gpu::convert_normal<short4>(nor);
|
||||
const short4 ptan = gpu::convert_normal<short4>(bevp->dir);
|
||||
GPU_vertbuf_attr_set(&vbo_curves_nor, nor_id, vbo_len_used, &pnor);
|
||||
GPU_vertbuf_attr_set(&vbo_curves_nor, tan_id, vbo_len_used, &ptan);
|
||||
}
|
||||
else {
|
||||
const gpu::PackedNormal pnor = gpu::convert_normal<gpu::PackedNormal>(nor);
|
||||
const gpu::PackedNormal ptan = gpu::convert_normal<gpu::PackedNormal>(bevp->dir);
|
||||
GPU_vertbuf_attr_set(&vbo_curves_nor, nor_id, vbo_len_used, &pnor);
|
||||
GPU_vertbuf_attr_set(&vbo_curves_nor, tan_id, vbo_len_used, &ptan);
|
||||
}
|
||||
vbo_len_used++;
|
||||
|
||||
/* Skip the other vertex (it does not need to be offset). */
|
||||
GPU_vertbuf_attr_set(&vbo_curves_nor, attr_id.pos, vbo_len_used, bevp->vec);
|
||||
vbo_len_used++;
|
||||
|
||||
bevp += skip + 1;
|
||||
nr -= skip;
|
||||
}
|
||||
}
|
||||
BLI_assert(vbo_len_used == verts_len_capacity);
|
||||
}
|
||||
|
||||
static uint8_t beztriple_vflag_get(CurveRenderData *rdata,
|
||||
uint8_t flag,
|
||||
uint8_t col_id,
|
||||
int v_idx,
|
||||
int nu_id,
|
||||
bool handle_point,
|
||||
const bool handle_selected)
|
||||
{
|
||||
uint8_t vflag = 0;
|
||||
SET_FLAG_FROM_TEST(vflag, (flag & SELECT), VFLAG_VERT_SELECTED);
|
||||
SET_FLAG_FROM_TEST(vflag, (v_idx == rdata->actvert && nu_id == rdata->actnu), VFLAG_VERT_ACTIVE);
|
||||
SET_FLAG_FROM_TEST(vflag, (nu_id == rdata->actnu), ACTIVE_NURB);
|
||||
SET_FLAG_FROM_TEST(vflag, handle_point, BEZIER_HANDLE);
|
||||
SET_FLAG_FROM_TEST(vflag, handle_selected, VFLAG_VERT_SELECTED_BEZT_HANDLE);
|
||||
/* Setting flags that overlap with will cause the color id not to work properly. */
|
||||
BLI_assert((vflag >> COLOR_SHIFT) == 0);
|
||||
/* handle color id */
|
||||
vflag |= col_id << COLOR_SHIFT;
|
||||
return vflag;
|
||||
}
|
||||
|
||||
static uint8_t bpoint_vflag_get(CurveRenderData *rdata, uint8_t flag, int v_idx, int nu_id, int u)
|
||||
{
|
||||
uint8_t vflag = 0;
|
||||
SET_FLAG_FROM_TEST(vflag, (flag & SELECT), VFLAG_VERT_SELECTED);
|
||||
SET_FLAG_FROM_TEST(vflag, (v_idx == rdata->actvert && nu_id == rdata->actnu), VFLAG_VERT_ACTIVE);
|
||||
SET_FLAG_FROM_TEST(vflag, (nu_id == rdata->actnu), ACTIVE_NURB);
|
||||
SET_FLAG_FROM_TEST(vflag, ((u % 2) == 0), EVEN_U_BIT);
|
||||
/* Setting flags that overlap with will cause the color id not to work properly. */
|
||||
BLI_assert((vflag >> COLOR_SHIFT) == 0);
|
||||
vflag |= COLOR_NURB_ULINE_ID << COLOR_SHIFT;
|
||||
return vflag;
|
||||
}
|
||||
|
||||
static void curve_create_edit_data_and_handles(CurveRenderData *rdata,
|
||||
gpu::VertBuf *vbo_pos,
|
||||
gpu::VertBuf *vbo_data,
|
||||
gpu::IndexBuf *ibo_edit_verts_points,
|
||||
gpu::IndexBuf *ibo_edit_lines)
|
||||
{
|
||||
static struct {
|
||||
uint pos, data;
|
||||
} attr_id;
|
||||
|
||||
static const GPUVertFormat format_pos = [&]() {
|
||||
GPUVertFormat format{};
|
||||
attr_id.pos = GPU_vertformat_attr_add(&format, "pos", gpu::VertAttrType::SFLOAT_32_32_32);
|
||||
return format;
|
||||
}();
|
||||
|
||||
static const GPUVertFormat format_data = [&]() {
|
||||
GPUVertFormat format{};
|
||||
attr_id.data = GPU_vertformat_attr_add(&format, "data", gpu::VertAttrType::UINT_32);
|
||||
return format;
|
||||
}();
|
||||
|
||||
int verts_len_capacity = curve_render_data_overlay_verts_len_get(rdata);
|
||||
int edges_len_capacity = curve_render_data_overlay_edges_len_get(rdata) * 2;
|
||||
int vbo_len_used = 0;
|
||||
|
||||
#define DRW_TEST_ASSIGN_VBO(v) (v = (DRW_vbo_requested(v) ? (v) : nullptr))
|
||||
#define DRW_TEST_ASSIGN_IBO(v) (v = (DRW_ibo_requested(v) ? (v) : nullptr))
|
||||
|
||||
if (DRW_TEST_ASSIGN_VBO(vbo_pos)) {
|
||||
GPU_vertbuf_init_with_format(*vbo_pos, format_pos);
|
||||
GPU_vertbuf_data_alloc(*vbo_pos, verts_len_capacity);
|
||||
}
|
||||
if (DRW_TEST_ASSIGN_VBO(vbo_data)) {
|
||||
GPU_vertbuf_init_with_format(*vbo_data, format_data);
|
||||
GPU_vertbuf_data_alloc(*vbo_data, verts_len_capacity);
|
||||
}
|
||||
|
||||
GPUIndexBufBuilder elb_verts, *elbp_verts = nullptr;
|
||||
GPUIndexBufBuilder elb_lines, *elbp_lines = nullptr;
|
||||
if (DRW_TEST_ASSIGN_IBO(ibo_edit_verts_points)) {
|
||||
elbp_verts = &elb_verts;
|
||||
GPU_indexbuf_init(elbp_verts, GPU_PRIM_POINTS, verts_len_capacity, verts_len_capacity);
|
||||
}
|
||||
if (DRW_TEST_ASSIGN_IBO(ibo_edit_lines)) {
|
||||
elbp_lines = &elb_lines;
|
||||
GPU_indexbuf_init(elbp_lines, GPU_PRIM_LINES, edges_len_capacity, verts_len_capacity);
|
||||
}
|
||||
|
||||
#undef DRW_TEST_ASSIGN_VBO
|
||||
#undef DRW_TEST_ASSIGN_IBO
|
||||
|
||||
int nu_id = 0;
|
||||
for (Nurb *nu = static_cast<Nurb *>(rdata->nurbs->first); nu; nu = nu->next, nu_id++) {
|
||||
const BezTriple *bezt = nu->bezt;
|
||||
const BPoint *bp = nu->bp;
|
||||
|
||||
if (bezt) {
|
||||
for (int a = 0; a < nu->pntsu; a++, bezt++) {
|
||||
if (bezt->hide != 0) {
|
||||
continue;
|
||||
}
|
||||
const bool handle_selected = BEZT_ISSEL_ANY(bezt);
|
||||
|
||||
if (elbp_verts) {
|
||||
GPU_indexbuf_add_point_vert(elbp_verts, vbo_len_used + 0);
|
||||
GPU_indexbuf_add_point_vert(elbp_verts, vbo_len_used + 1);
|
||||
GPU_indexbuf_add_point_vert(elbp_verts, vbo_len_used + 2);
|
||||
}
|
||||
if (elbp_lines) {
|
||||
GPU_indexbuf_add_line_verts(elbp_lines, vbo_len_used + 1, vbo_len_used + 0);
|
||||
GPU_indexbuf_add_line_verts(elbp_lines, vbo_len_used + 1, vbo_len_used + 2);
|
||||
}
|
||||
if (vbo_data) {
|
||||
const uint32_t vflag[3] = {
|
||||
beztriple_vflag_get(rdata, bezt->f1, bezt->h1, a, nu_id, true, handle_selected),
|
||||
beztriple_vflag_get(rdata, bezt->f2, bezt->h1, a, nu_id, false, handle_selected),
|
||||
beztriple_vflag_get(rdata, bezt->f3, bezt->h2, a, nu_id, true, handle_selected),
|
||||
};
|
||||
for (int j = 0; j < 3; j++) {
|
||||
GPU_vertbuf_attr_set(vbo_data, attr_id.data, vbo_len_used + j, &vflag[j]);
|
||||
}
|
||||
}
|
||||
if (vbo_pos) {
|
||||
for (int j = 0; j < 3; j++) {
|
||||
GPU_vertbuf_attr_set(vbo_pos, attr_id.pos, vbo_len_used + j, bezt->vec[j]);
|
||||
}
|
||||
}
|
||||
vbo_len_used += 3;
|
||||
}
|
||||
}
|
||||
else if (bp) {
|
||||
int pt_len = nu->pntsu * nu->pntsv;
|
||||
for (int a = 0; a < pt_len; a++, bp++, vbo_len_used += 1) {
|
||||
if (bp->hide != 0) {
|
||||
continue;
|
||||
}
|
||||
int u = (a % nu->pntsu);
|
||||
int v = (a / nu->pntsu);
|
||||
/* Use indexed rendering for bezier.
|
||||
* Specify all points and use indices to hide/show. */
|
||||
if (elbp_verts) {
|
||||
GPU_indexbuf_add_point_vert(elbp_verts, vbo_len_used);
|
||||
}
|
||||
if (elbp_lines) {
|
||||
const BPoint *bp_next_u = (u < (nu->pntsu - 1)) ? &nu->bp[a + 1] : nullptr;
|
||||
const BPoint *bp_next_v = (v < (nu->pntsv - 1)) ? &nu->bp[a + nu->pntsu] : nullptr;
|
||||
if (bp_next_u && (bp_next_u->hide == false)) {
|
||||
GPU_indexbuf_add_line_verts(elbp_lines, vbo_len_used, vbo_len_used + 1);
|
||||
}
|
||||
if (bp_next_v && (bp_next_v->hide == false)) {
|
||||
GPU_indexbuf_add_line_verts(elbp_lines, vbo_len_used, vbo_len_used + nu->pntsu);
|
||||
}
|
||||
}
|
||||
if (vbo_data) {
|
||||
uint32_t vflag = bpoint_vflag_get(rdata, bp->f1, a, nu_id, u);
|
||||
GPU_vertbuf_attr_set(vbo_data, attr_id.data, vbo_len_used, &vflag);
|
||||
}
|
||||
if (vbo_pos) {
|
||||
GPU_vertbuf_attr_set(vbo_pos, attr_id.pos, vbo_len_used, bp->vec);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Resize & Finish */
|
||||
if (elbp_verts != nullptr) {
|
||||
GPU_indexbuf_build_in_place(elbp_verts, ibo_edit_verts_points);
|
||||
}
|
||||
if (elbp_lines != nullptr) {
|
||||
GPU_indexbuf_build_in_place(elbp_lines, ibo_edit_lines);
|
||||
}
|
||||
if (vbo_len_used != verts_len_capacity) {
|
||||
if (vbo_pos != nullptr) {
|
||||
GPU_vertbuf_data_resize(*vbo_pos, vbo_len_used);
|
||||
}
|
||||
if (vbo_data != nullptr) {
|
||||
GPU_vertbuf_data_resize(*vbo_data, vbo_len_used);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Public Object/Curve API
|
||||
* \{ */
|
||||
|
||||
gpu::Batch *DRW_curve_batch_cache_get_wire_edge(Curve *cu)
|
||||
{
|
||||
CurveBatchCache *cache = curve_batch_cache_get(cu);
|
||||
return DRW_batch_request(&cache->batch.curves);
|
||||
}
|
||||
|
||||
gpu::Batch *DRW_curve_batch_cache_get_wire_edge_viewer_attribute(Curve *cu)
|
||||
{
|
||||
CurveBatchCache *cache = curve_batch_cache_get(cu);
|
||||
return DRW_batch_request(&cache->batch.curves_viewer_attribute);
|
||||
}
|
||||
|
||||
gpu::Batch *DRW_curve_batch_cache_get_normal_edge(Curve *cu)
|
||||
{
|
||||
CurveBatchCache *cache = curve_batch_cache_get(cu);
|
||||
return DRW_batch_request(&cache->batch.edit_normals);
|
||||
}
|
||||
|
||||
gpu::Batch *DRW_curve_batch_cache_get_edit_edges(Curve *cu)
|
||||
{
|
||||
CurveBatchCache *cache = curve_batch_cache_get(cu);
|
||||
return DRW_batch_request(&cache->batch.edit_edges);
|
||||
}
|
||||
|
||||
gpu::Batch *DRW_curve_batch_cache_get_edit_verts(Curve *cu)
|
||||
{
|
||||
CurveBatchCache *cache = curve_batch_cache_get(cu);
|
||||
return DRW_batch_request(&cache->batch.edit_verts);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Grouped batch generation
|
||||
* \{ */
|
||||
|
||||
void DRW_curve_batch_cache_create_requested(Object *ob, const Scene *scene)
|
||||
{
|
||||
BLI_assert(ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF, OB_FONT));
|
||||
|
||||
Curve *cu = id_cast<Curve *>(ob->data);
|
||||
CurveBatchCache *cache = curve_batch_cache_get(cu);
|
||||
|
||||
/* Init batches and request VBOs & IBOs */
|
||||
if (DRW_batch_requested(cache->batch.curves, GPU_PRIM_LINE_STRIP)) {
|
||||
DRW_ibo_request(cache->batch.curves, &cache->ibo.curves_lines);
|
||||
DRW_vbo_request(cache->batch.curves, &cache->ordered.curves_pos);
|
||||
}
|
||||
if (DRW_batch_requested(cache->batch.curves_viewer_attribute, GPU_PRIM_LINE_STRIP)) {
|
||||
DRW_ibo_request(cache->batch.curves_viewer_attribute, &cache->ibo.curves_lines);
|
||||
DRW_vbo_request(cache->batch.curves_viewer_attribute, &cache->ordered.curves_pos);
|
||||
DRW_vbo_request(cache->batch.curves_viewer_attribute, &cache->ordered.attr_viewer);
|
||||
}
|
||||
|
||||
/* Edit mode */
|
||||
if (DRW_batch_requested(cache->batch.edit_edges, GPU_PRIM_LINES)) {
|
||||
DRW_ibo_request(cache->batch.edit_edges, &cache->ibo.edit_lines);
|
||||
DRW_vbo_request(cache->batch.edit_edges, &cache->edit.pos);
|
||||
DRW_vbo_request(cache->batch.edit_edges, &cache->edit.data);
|
||||
}
|
||||
if (DRW_batch_requested(cache->batch.edit_verts, GPU_PRIM_POINTS)) {
|
||||
DRW_ibo_request(cache->batch.edit_verts, &cache->ibo.edit_verts);
|
||||
DRW_vbo_request(cache->batch.edit_verts, &cache->edit.pos);
|
||||
DRW_vbo_request(cache->batch.edit_verts, &cache->edit.data);
|
||||
}
|
||||
if (DRW_batch_requested(cache->batch.edit_normals, GPU_PRIM_LINES)) {
|
||||
DRW_vbo_request(cache->batch.edit_normals, &cache->edit.curves_nor);
|
||||
}
|
||||
|
||||
#ifdef DRW_DEBUG_MESH_CACHE_REQUEST
|
||||
printf("-- %s %s --\n", __func__, ob->id.name + 2);
|
||||
#endif
|
||||
|
||||
/* Generate MeshRenderData flags */
|
||||
int mr_flag = 0;
|
||||
DRW_ADD_FLAG_FROM_VBO_REQUEST(mr_flag, cache->ordered.curves_pos, CU_DATATYPE_WIRE);
|
||||
DRW_ADD_FLAG_FROM_VBO_REQUEST(
|
||||
mr_flag, cache->ordered.attr_viewer, CU_DATATYPE_WIRE | CU_DATATYPE_OVERLAY);
|
||||
DRW_ADD_FLAG_FROM_IBO_REQUEST(mr_flag, cache->ibo.curves_lines, CU_DATATYPE_WIRE);
|
||||
|
||||
DRW_ADD_FLAG_FROM_VBO_REQUEST(mr_flag, cache->edit.pos, CU_DATATYPE_OVERLAY);
|
||||
DRW_ADD_FLAG_FROM_VBO_REQUEST(mr_flag, cache->edit.data, CU_DATATYPE_OVERLAY);
|
||||
DRW_ADD_FLAG_FROM_VBO_REQUEST(mr_flag, cache->edit.curves_nor, CU_DATATYPE_NORMAL);
|
||||
DRW_ADD_FLAG_FROM_IBO_REQUEST(mr_flag, cache->ibo.edit_verts, CU_DATATYPE_OVERLAY);
|
||||
DRW_ADD_FLAG_FROM_IBO_REQUEST(mr_flag, cache->ibo.edit_lines, CU_DATATYPE_OVERLAY);
|
||||
|
||||
#ifdef DRW_DEBUG_MESH_CACHE_REQUEST
|
||||
printf(" mr_flag %d\n\n", mr_flag);
|
||||
#endif
|
||||
|
||||
CurveRenderData *rdata = curve_render_data_create(cu, ob->runtime->curve_cache, mr_flag);
|
||||
|
||||
/* Generate VBOs */
|
||||
if (DRW_vbo_requested(cache->ordered.curves_pos)) {
|
||||
curve_create_curves_pos(rdata, cache->ordered.curves_pos);
|
||||
}
|
||||
if (DRW_vbo_requested(cache->ordered.attr_viewer)) {
|
||||
curve_create_attribute(rdata, *cache->ordered.attr_viewer);
|
||||
}
|
||||
if (DRW_ibo_requested(cache->ibo.curves_lines)) {
|
||||
curve_create_curves_lines(rdata, cache->ibo.curves_lines);
|
||||
}
|
||||
if (DRW_vbo_requested(cache->edit.pos) || DRW_vbo_requested(cache->edit.data) ||
|
||||
DRW_ibo_requested(cache->ibo.edit_verts) || DRW_ibo_requested(cache->ibo.edit_lines))
|
||||
{
|
||||
curve_create_edit_data_and_handles(
|
||||
rdata, cache->edit.pos, cache->edit.data, cache->ibo.edit_verts, cache->ibo.edit_lines);
|
||||
}
|
||||
if (DRW_vbo_requested(cache->edit.curves_nor)) {
|
||||
curve_create_edit_curves_nor(rdata, *cache->edit.curves_nor, scene);
|
||||
}
|
||||
|
||||
curve_render_data_free(rdata);
|
||||
|
||||
#ifndef NDEBUG
|
||||
/* Make sure all requested batches have been setup. */
|
||||
for (int i = 0; i < sizeof(cache->batch) / sizeof(void *); i++) {
|
||||
BLI_assert(!DRW_batch_requested(((gpu::Batch **)&cache->batch)[i], (GPUPrimType)0));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::draw
|
||||
1204
blender-5.2.0/source/blender/draw/intern/draw_cache_impl_curves.cc
Normal file
1204
blender-5.2.0/source/blender/draw/intern/draw_cache_impl_curves.cc
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,502 @@
|
||||
/* SPDX-FileCopyrightText: 2017 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*
|
||||
* \brief Lattice API for render engines
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "DNA_curve_types.h"
|
||||
#include "DNA_lattice_types.h"
|
||||
#include "DNA_meshdata_types.h"
|
||||
|
||||
#include "BKE_deform.hh"
|
||||
#include "BKE_lattice.hh"
|
||||
|
||||
#include "GPU_batch.hh"
|
||||
|
||||
#include "draw_cache_impl.hh" /* own include */
|
||||
|
||||
#define SELECT 1
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
static void lattice_batch_cache_clear(Lattice *lt);
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Lattice Interface, direct access to basic data. */
|
||||
|
||||
static int vert_len_calc(int u, int v, int w)
|
||||
{
|
||||
if (u <= 0 || v <= 0 || w <= 0) {
|
||||
return 0;
|
||||
}
|
||||
return u * v * w;
|
||||
}
|
||||
|
||||
static int edge_len_calc(int u, int v, int w)
|
||||
{
|
||||
if (u <= 0 || v <= 0 || w <= 0) {
|
||||
return 0;
|
||||
}
|
||||
return (((((u - 1) * v) + ((v - 1) * u)) * w) + ((w - 1) * (u * v)));
|
||||
}
|
||||
|
||||
static int lattice_render_verts_len_get(Lattice *lt)
|
||||
{
|
||||
if (lt->editlatt) {
|
||||
lt = lt->editlatt->latt;
|
||||
}
|
||||
|
||||
const int u = lt->pntsu;
|
||||
const int v = lt->pntsv;
|
||||
const int w = lt->pntsw;
|
||||
|
||||
if ((lt->flag & LT_OUTSIDE) == 0) {
|
||||
return vert_len_calc(u, v, w);
|
||||
}
|
||||
|
||||
/* TODO: remove internal coords. */
|
||||
return vert_len_calc(u, v, w);
|
||||
}
|
||||
|
||||
static int lattice_render_edges_len_get(Lattice *lt)
|
||||
{
|
||||
if (lt->editlatt) {
|
||||
lt = lt->editlatt->latt;
|
||||
}
|
||||
|
||||
const int u = lt->pntsu;
|
||||
const int v = lt->pntsv;
|
||||
const int w = lt->pntsw;
|
||||
|
||||
if ((lt->flag & LT_OUTSIDE) == 0) {
|
||||
return edge_len_calc(u, v, w);
|
||||
}
|
||||
|
||||
/* TODO: remove internal coords. */
|
||||
return edge_len_calc(u, v, w);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Lattice Interface, indirect, partially cached access to complex data. */
|
||||
|
||||
struct LatticeRenderData {
|
||||
int types;
|
||||
|
||||
int vert_len;
|
||||
int edge_len;
|
||||
|
||||
struct {
|
||||
int u_len, v_len, w_len;
|
||||
} dims;
|
||||
bool show_only_outside;
|
||||
|
||||
EditLatt *edit_latt;
|
||||
BPoint *bp;
|
||||
|
||||
int actbp;
|
||||
|
||||
const MDeformVert *dvert;
|
||||
};
|
||||
|
||||
enum {
|
||||
LR_DATATYPE_VERT = 1 << 0,
|
||||
LR_DATATYPE_EDGE = 1 << 1,
|
||||
LR_DATATYPE_OVERLAY = 1 << 2,
|
||||
};
|
||||
|
||||
static LatticeRenderData *lattice_render_data_create(Lattice *lt, const int types)
|
||||
{
|
||||
LatticeRenderData *rdata = MEM_new_zeroed<LatticeRenderData>(__func__);
|
||||
rdata->types = types;
|
||||
|
||||
if (lt->editlatt) {
|
||||
EditLatt *editlatt = lt->editlatt;
|
||||
lt = editlatt->latt;
|
||||
|
||||
rdata->edit_latt = editlatt;
|
||||
|
||||
rdata->dvert = lt->dvert;
|
||||
|
||||
if (types & (LR_DATATYPE_VERT)) {
|
||||
rdata->vert_len = lattice_render_verts_len_get(lt);
|
||||
}
|
||||
if (types & (LR_DATATYPE_EDGE)) {
|
||||
rdata->edge_len = lattice_render_edges_len_get(lt);
|
||||
}
|
||||
if (types & LR_DATATYPE_OVERLAY) {
|
||||
rdata->actbp = lt->actbp;
|
||||
}
|
||||
}
|
||||
else {
|
||||
rdata->dvert = nullptr;
|
||||
|
||||
if (types & (LR_DATATYPE_VERT)) {
|
||||
rdata->vert_len = lattice_render_verts_len_get(lt);
|
||||
}
|
||||
if (types & (LR_DATATYPE_EDGE)) {
|
||||
rdata->edge_len = lattice_render_edges_len_get(lt);
|
||||
/* No edge data. */
|
||||
}
|
||||
}
|
||||
|
||||
rdata->bp = lt->def;
|
||||
|
||||
rdata->dims.u_len = lt->pntsu;
|
||||
rdata->dims.v_len = lt->pntsv;
|
||||
rdata->dims.w_len = lt->pntsw;
|
||||
|
||||
rdata->show_only_outside = (lt->flag & LT_OUTSIDE) != 0;
|
||||
rdata->actbp = lt->actbp;
|
||||
|
||||
return rdata;
|
||||
}
|
||||
|
||||
static void lattice_render_data_free(LatticeRenderData *rdata)
|
||||
{
|
||||
#if 0
|
||||
if (rdata->loose_verts) {
|
||||
MEM_delete(rdata->loose_verts);
|
||||
}
|
||||
#endif
|
||||
MEM_delete(rdata);
|
||||
}
|
||||
|
||||
static int lattice_render_data_verts_len_get(const LatticeRenderData *rdata)
|
||||
{
|
||||
BLI_assert(rdata->types & LR_DATATYPE_VERT);
|
||||
return rdata->vert_len;
|
||||
}
|
||||
|
||||
static int lattice_render_data_edges_len_get(const LatticeRenderData *rdata)
|
||||
{
|
||||
BLI_assert(rdata->types & LR_DATATYPE_EDGE);
|
||||
return rdata->edge_len;
|
||||
}
|
||||
|
||||
static const BPoint *lattice_render_data_vert_bpoint(const LatticeRenderData *rdata,
|
||||
const int vert_idx)
|
||||
{
|
||||
BLI_assert(rdata->types & LR_DATATYPE_VERT);
|
||||
return &rdata->bp[vert_idx];
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Lattice gpu::Batch Cache */
|
||||
|
||||
struct LatticeBatchCache {
|
||||
gpu::VertBuf *pos;
|
||||
gpu::IndexBuf *edges;
|
||||
|
||||
gpu::Batch *all_verts;
|
||||
gpu::Batch *all_edges;
|
||||
|
||||
gpu::Batch *overlay_verts;
|
||||
|
||||
/* settings to determine if cache is invalid */
|
||||
bool is_dirty;
|
||||
|
||||
struct {
|
||||
int u_len, v_len, w_len;
|
||||
} dims;
|
||||
bool show_only_outside;
|
||||
|
||||
bool is_editmode;
|
||||
};
|
||||
|
||||
/* gpu::Batch cache management. */
|
||||
|
||||
static bool lattice_batch_cache_valid(Lattice *lt)
|
||||
{
|
||||
LatticeBatchCache *cache = lt->batch_cache;
|
||||
|
||||
if (cache == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cache->is_editmode != (lt->editlatt != nullptr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cache->is_dirty) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((cache->dims.u_len != lt->pntsu) || (cache->dims.v_len != lt->pntsv) ||
|
||||
(cache->dims.w_len != lt->pntsw) ||
|
||||
(cache->show_only_outside != ((lt->flag & LT_OUTSIDE) != 0)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void lattice_batch_cache_init(Lattice *lt)
|
||||
{
|
||||
LatticeBatchCache *cache = lt->batch_cache;
|
||||
|
||||
if (!cache) {
|
||||
lt->batch_cache = cache = MEM_new_zeroed<LatticeBatchCache>(__func__);
|
||||
}
|
||||
else {
|
||||
memset(cache, 0, sizeof(*cache));
|
||||
}
|
||||
|
||||
cache->dims.u_len = lt->pntsu;
|
||||
cache->dims.v_len = lt->pntsv;
|
||||
cache->dims.w_len = lt->pntsw;
|
||||
cache->show_only_outside = (lt->flag & LT_OUTSIDE) != 0;
|
||||
|
||||
cache->is_editmode = lt->editlatt != nullptr;
|
||||
|
||||
cache->is_dirty = false;
|
||||
}
|
||||
|
||||
void DRW_lattice_batch_cache_validate(Lattice *lt)
|
||||
{
|
||||
if (!lattice_batch_cache_valid(lt)) {
|
||||
lattice_batch_cache_clear(lt);
|
||||
lattice_batch_cache_init(lt);
|
||||
}
|
||||
}
|
||||
|
||||
static LatticeBatchCache *lattice_batch_cache_get(Lattice *lt)
|
||||
{
|
||||
return lt->batch_cache;
|
||||
}
|
||||
|
||||
void DRW_lattice_batch_cache_dirty_tag(Lattice *lt, int mode)
|
||||
{
|
||||
LatticeBatchCache *cache = lt->batch_cache;
|
||||
if (cache == nullptr) {
|
||||
return;
|
||||
}
|
||||
switch (mode) {
|
||||
case BKE_LATTICE_BATCH_DIRTY_ALL:
|
||||
cache->is_dirty = true;
|
||||
break;
|
||||
case BKE_LATTICE_BATCH_DIRTY_SELECT:
|
||||
/* TODO: Separate Flag VBO. */
|
||||
GPU_BATCH_DISCARD_SAFE(cache->overlay_verts);
|
||||
break;
|
||||
default:
|
||||
BLI_assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void lattice_batch_cache_clear(Lattice *lt)
|
||||
{
|
||||
LatticeBatchCache *cache = lt->batch_cache;
|
||||
if (!cache) {
|
||||
return;
|
||||
}
|
||||
|
||||
GPU_BATCH_DISCARD_SAFE(cache->all_verts);
|
||||
GPU_BATCH_DISCARD_SAFE(cache->all_edges);
|
||||
GPU_BATCH_DISCARD_SAFE(cache->overlay_verts);
|
||||
|
||||
GPU_VERTBUF_DISCARD_SAFE(cache->pos);
|
||||
GPU_INDEXBUF_DISCARD_SAFE(cache->edges);
|
||||
}
|
||||
|
||||
void DRW_lattice_batch_cache_free(Lattice *lt)
|
||||
{
|
||||
lattice_batch_cache_clear(lt);
|
||||
MEM_SAFE_DELETE(lt->batch_cache);
|
||||
}
|
||||
|
||||
/* gpu::Batch cache usage. */
|
||||
static gpu::VertBuf *lattice_batch_cache_get_pos(LatticeRenderData *rdata,
|
||||
LatticeBatchCache *cache,
|
||||
bool use_weight,
|
||||
const int actdef)
|
||||
{
|
||||
BLI_assert(rdata->types & LR_DATATYPE_VERT);
|
||||
|
||||
if (cache->pos == nullptr) {
|
||||
GPUVertFormat format = {0};
|
||||
struct {
|
||||
uint pos, col;
|
||||
} attr_id;
|
||||
|
||||
attr_id.pos = GPU_vertformat_attr_add(&format, "pos", gpu::VertAttrType::SFLOAT_32_32_32);
|
||||
if (use_weight) {
|
||||
attr_id.col = GPU_vertformat_attr_add(&format, "weight", gpu::VertAttrType::SFLOAT_32);
|
||||
}
|
||||
|
||||
const int vert_len = lattice_render_data_verts_len_get(rdata);
|
||||
|
||||
cache->pos = GPU_vertbuf_create_with_format(format);
|
||||
GPU_vertbuf_data_alloc(*cache->pos, vert_len);
|
||||
for (int i = 0; i < vert_len; i++) {
|
||||
const BPoint *bp = lattice_render_data_vert_bpoint(rdata, i);
|
||||
GPU_vertbuf_attr_set(cache->pos, attr_id.pos, i, bp->vec);
|
||||
|
||||
if (use_weight) {
|
||||
const float no_active_weight = 666.0f;
|
||||
float weight = (actdef > -1) ? BKE_defvert_find_weight(rdata->dvert + i, actdef) :
|
||||
no_active_weight;
|
||||
GPU_vertbuf_attr_set(cache->pos, attr_id.col, i, &weight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cache->pos;
|
||||
}
|
||||
|
||||
static gpu::IndexBuf *lattice_batch_cache_get_edges(LatticeRenderData *rdata,
|
||||
LatticeBatchCache *cache)
|
||||
{
|
||||
BLI_assert(rdata->types & (LR_DATATYPE_VERT | LR_DATATYPE_EDGE));
|
||||
|
||||
if (cache->edges == nullptr) {
|
||||
const int vert_len = lattice_render_data_verts_len_get(rdata);
|
||||
const int edge_len = lattice_render_data_edges_len_get(rdata);
|
||||
|
||||
GPUIndexBufBuilder builder;
|
||||
GPU_indexbuf_init(&builder, GPU_PRIM_LINES, edge_len, vert_len);
|
||||
MutableSpan<uint2> data = GPU_indexbuf_get_data(&builder).cast<uint2>();
|
||||
/* The buffer is allocated with **all** edges (see #lattice_render_edges_len_get()), but with
|
||||
* the LT_OUTSIDE flag not all are drawn. So fill those gaps with zeros to hide redundant
|
||||
* edges. */
|
||||
data.fill(uint2(0));
|
||||
int line_index = 0;
|
||||
|
||||
#define LATT_INDEX(u, v, w) ((((w) * rdata->dims.v_len + (v)) * rdata->dims.u_len) + (u))
|
||||
|
||||
for (int w = 0; w < rdata->dims.w_len; w++) {
|
||||
int wxt = ELEM(w, 0, rdata->dims.w_len - 1);
|
||||
for (int v = 0; v < rdata->dims.v_len; v++) {
|
||||
int vxt = ELEM(v, 0, rdata->dims.v_len - 1);
|
||||
for (int u = 0; u < rdata->dims.u_len; u++) {
|
||||
int uxt = ELEM(u, 0, rdata->dims.u_len - 1);
|
||||
|
||||
if (w && ((uxt || vxt) || !rdata->show_only_outside)) {
|
||||
data[line_index++] = uint2(LATT_INDEX(u, v, w - 1), LATT_INDEX(u, v, w));
|
||||
}
|
||||
if (v && ((uxt || wxt) || !rdata->show_only_outside)) {
|
||||
data[line_index++] = uint2(LATT_INDEX(u, v - 1, w), LATT_INDEX(u, v, w));
|
||||
}
|
||||
if (u && ((vxt || wxt) || !rdata->show_only_outside)) {
|
||||
data[line_index++] = uint2(LATT_INDEX(u - 1, v, w), LATT_INDEX(u, v, w));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef LATT_INDEX
|
||||
|
||||
if (rdata->show_only_outside) {
|
||||
BLI_assert(line_index <= edge_len);
|
||||
}
|
||||
else {
|
||||
BLI_assert(line_index == edge_len);
|
||||
}
|
||||
|
||||
cache->edges = GPU_indexbuf_build_ex(&builder, 0, vert_len, false);
|
||||
}
|
||||
|
||||
return cache->edges;
|
||||
}
|
||||
|
||||
static void lattice_batch_cache_create_overlay_batches(Lattice *lt)
|
||||
{
|
||||
/* Since LR_DATATYPE_OVERLAY is slow to generate, generate them all at once */
|
||||
int options = LR_DATATYPE_VERT | LR_DATATYPE_OVERLAY;
|
||||
|
||||
LatticeBatchCache *cache = lattice_batch_cache_get(lt);
|
||||
LatticeRenderData *rdata = lattice_render_data_create(lt, options);
|
||||
|
||||
if (cache->overlay_verts == nullptr) {
|
||||
static struct {
|
||||
uint pos, data;
|
||||
} attr_id;
|
||||
static const GPUVertFormat format = [&]() {
|
||||
GPUVertFormat format{};
|
||||
attr_id.pos = GPU_vertformat_attr_add(&format, "pos", gpu::VertAttrType::SFLOAT_32_32_32);
|
||||
attr_id.data = GPU_vertformat_attr_add(&format, "data", gpu::VertAttrType::UINT_32);
|
||||
return format;
|
||||
}();
|
||||
|
||||
const int vert_len = lattice_render_data_verts_len_get(rdata);
|
||||
|
||||
gpu::VertBuf *vbo = GPU_vertbuf_create_with_format(format);
|
||||
GPU_vertbuf_data_alloc(*vbo, vert_len);
|
||||
for (int i = 0; i < vert_len; i++) {
|
||||
const BPoint *bp = lattice_render_data_vert_bpoint(rdata, i);
|
||||
|
||||
uint32_t vflag = 0;
|
||||
if (bp->f1 & SELECT) {
|
||||
if (i == rdata->actbp) {
|
||||
vflag |= VFLAG_VERT_ACTIVE;
|
||||
}
|
||||
else {
|
||||
vflag |= VFLAG_VERT_SELECTED;
|
||||
}
|
||||
}
|
||||
|
||||
GPU_vertbuf_attr_set(vbo, attr_id.pos, i, bp->vec);
|
||||
GPU_vertbuf_attr_set(vbo, attr_id.data, i, &vflag);
|
||||
}
|
||||
|
||||
cache->overlay_verts = GPU_batch_create_ex(GPU_PRIM_POINTS, vbo, nullptr, GPU_BATCH_OWNS_VBO);
|
||||
}
|
||||
|
||||
lattice_render_data_free(rdata);
|
||||
}
|
||||
|
||||
gpu::Batch *DRW_lattice_batch_cache_get_all_edges(Lattice *lt, bool use_weight, const int actdef)
|
||||
{
|
||||
LatticeBatchCache *cache = lattice_batch_cache_get(lt);
|
||||
|
||||
if (cache->all_edges == nullptr) {
|
||||
/* create batch from Lattice */
|
||||
LatticeRenderData *rdata = lattice_render_data_create(lt, LR_DATATYPE_VERT | LR_DATATYPE_EDGE);
|
||||
|
||||
cache->all_edges = GPU_batch_create(
|
||||
GPU_PRIM_LINES,
|
||||
lattice_batch_cache_get_pos(rdata, cache, use_weight, actdef),
|
||||
lattice_batch_cache_get_edges(rdata, cache));
|
||||
|
||||
lattice_render_data_free(rdata);
|
||||
}
|
||||
|
||||
return cache->all_edges;
|
||||
}
|
||||
|
||||
gpu::Batch *DRW_lattice_batch_cache_get_all_verts(Lattice *lt)
|
||||
{
|
||||
LatticeBatchCache *cache = lattice_batch_cache_get(lt);
|
||||
|
||||
if (cache->all_verts == nullptr) {
|
||||
LatticeRenderData *rdata = lattice_render_data_create(lt, LR_DATATYPE_VERT);
|
||||
|
||||
cache->all_verts = GPU_batch_create(
|
||||
GPU_PRIM_POINTS, lattice_batch_cache_get_pos(rdata, cache, false, -1), nullptr);
|
||||
|
||||
lattice_render_data_free(rdata);
|
||||
}
|
||||
|
||||
return cache->all_verts;
|
||||
}
|
||||
|
||||
gpu::Batch *DRW_lattice_batch_cache_get_edit_verts(Lattice *lt)
|
||||
{
|
||||
LatticeBatchCache *cache = lattice_batch_cache_get(lt);
|
||||
|
||||
if (cache->overlay_verts == nullptr) {
|
||||
lattice_batch_cache_create_overlay_batches(lt);
|
||||
}
|
||||
|
||||
return cache->overlay_verts;
|
||||
}
|
||||
|
||||
} // namespace blender::draw
|
||||
1761
blender-5.2.0/source/blender/draw/intern/draw_cache_impl_mesh.cc
Normal file
1761
blender-5.2.0/source/blender/draw/intern/draw_cache_impl_mesh.cc
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,462 @@
|
||||
/* SPDX-FileCopyrightText: 2017 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*
|
||||
* \brief PointCloud API for render engines
|
||||
*/
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_color_types.hh"
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_task.hh"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_pointcloud_types.h"
|
||||
#include "DNA_userdef_types.h"
|
||||
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_material.hh"
|
||||
#include "BKE_pointcloud.hh"
|
||||
|
||||
#include "GPU_batch.hh"
|
||||
#include "GPU_material.hh"
|
||||
|
||||
#include "DRW_render.hh"
|
||||
|
||||
#include "draw_attributes.hh"
|
||||
#include "draw_cache_impl.hh"
|
||||
#include "draw_cache_inline.hh"
|
||||
#include "draw_pointcloud_private.hh" /* own include */
|
||||
|
||||
#include "draw_defines.hh"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name gpu::Batch cache management
|
||||
* \{ */
|
||||
|
||||
struct PointCloudEvalCache {
|
||||
/* Dot primitive types. */
|
||||
gpu::Batch *dots;
|
||||
/* Triangle primitive types. */
|
||||
gpu::Batch *surface;
|
||||
gpu::Batch **surface_per_mat;
|
||||
|
||||
/* Position and radius. */
|
||||
gpu::VertBuf *pos_rad;
|
||||
/* Active attribute in 3D view. */
|
||||
gpu::VertBuf *attr_viewer;
|
||||
/* Requested attributes */
|
||||
gpu::VertBuf *attributes_buf[GPU_MAX_ATTR];
|
||||
|
||||
/** Attributes currently being drawn or about to be drawn. */
|
||||
VectorSet<std::string> attr_used;
|
||||
/**
|
||||
* Attributes that were used at some point. This is used for garbage collection, to remove
|
||||
* attributes that are not used in shaders anymore due to user edits.
|
||||
*/
|
||||
VectorSet<std::string> attr_used_over_time;
|
||||
|
||||
/**
|
||||
* The last time in seconds that the `attr_used` and `attr_used_over_time` were exactly the same.
|
||||
* If the delta between this time and the current scene time is greater than the timeout set in
|
||||
* user preferences (`U.vbotimeout`) then garbage collection is performed.
|
||||
*/
|
||||
int last_attr_matching_time;
|
||||
|
||||
int mat_len;
|
||||
};
|
||||
|
||||
struct PointCloudBatchCache {
|
||||
PointCloudEvalCache eval_cache;
|
||||
|
||||
gpu::IndexBuf *edit_selection_indices = nullptr;
|
||||
gpu::Batch *edit_selection = nullptr;
|
||||
|
||||
/* settings to determine if cache is invalid */
|
||||
bool is_dirty;
|
||||
};
|
||||
|
||||
static PointCloudBatchCache *pointcloud_batch_cache_get(PointCloud &pointcloud)
|
||||
{
|
||||
return pointcloud.batch_cache;
|
||||
}
|
||||
|
||||
static bool pointcloud_batch_cache_valid(PointCloud &pointcloud)
|
||||
{
|
||||
PointCloudBatchCache *cache = pointcloud_batch_cache_get(pointcloud);
|
||||
|
||||
if (cache == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (cache->eval_cache.mat_len != BKE_id_material_used_with_fallback_eval(pointcloud.id)) {
|
||||
return false;
|
||||
}
|
||||
return cache->is_dirty == false;
|
||||
}
|
||||
|
||||
static void pointcloud_batch_cache_init(PointCloud &pointcloud)
|
||||
{
|
||||
PointCloudBatchCache *cache = pointcloud_batch_cache_get(pointcloud);
|
||||
|
||||
if (!cache) {
|
||||
cache = MEM_new<PointCloudBatchCache>(__func__);
|
||||
pointcloud.batch_cache = cache;
|
||||
}
|
||||
else {
|
||||
cache->eval_cache = {};
|
||||
cache->edit_selection = nullptr;
|
||||
cache->edit_selection_indices = nullptr;
|
||||
}
|
||||
|
||||
cache->eval_cache.mat_len = BKE_id_material_used_with_fallback_eval(pointcloud.id);
|
||||
cache->eval_cache.surface_per_mat = MEM_new_array_zeroed<gpu::Batch *>(cache->eval_cache.mat_len,
|
||||
__func__);
|
||||
|
||||
cache->is_dirty = false;
|
||||
}
|
||||
|
||||
void DRW_pointcloud_batch_cache_dirty_tag(PointCloud *pointcloud, int mode)
|
||||
{
|
||||
PointCloudBatchCache *cache = pointcloud_batch_cache_get(*pointcloud);
|
||||
if (cache == nullptr) {
|
||||
return;
|
||||
}
|
||||
switch (mode) {
|
||||
case BKE_POINTCLOUD_BATCH_DIRTY_ALL:
|
||||
cache->is_dirty = true;
|
||||
break;
|
||||
default:
|
||||
BLI_assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void pointcloud_discard_attributes(PointCloudBatchCache &cache)
|
||||
{
|
||||
for (const int j : IndexRange(GPU_MAX_ATTR)) {
|
||||
GPU_VERTBUF_DISCARD_SAFE(cache.eval_cache.attributes_buf[j]);
|
||||
}
|
||||
|
||||
cache.eval_cache.attr_used.clear();
|
||||
}
|
||||
|
||||
static void pointcloud_batch_cache_clear(PointCloud &pointcloud)
|
||||
{
|
||||
PointCloudBatchCache *cache = pointcloud_batch_cache_get(pointcloud);
|
||||
if (!cache) {
|
||||
return;
|
||||
}
|
||||
|
||||
GPU_BATCH_DISCARD_SAFE(cache->eval_cache.dots);
|
||||
GPU_BATCH_DISCARD_SAFE(cache->eval_cache.surface);
|
||||
GPU_VERTBUF_DISCARD_SAFE(cache->eval_cache.pos_rad);
|
||||
GPU_VERTBUF_DISCARD_SAFE(cache->eval_cache.attr_viewer);
|
||||
|
||||
GPU_INDEXBUF_DISCARD_SAFE(cache->edit_selection_indices);
|
||||
GPU_BATCH_DISCARD_SAFE(cache->edit_selection);
|
||||
|
||||
if (cache->eval_cache.surface_per_mat) {
|
||||
for (int i = 0; i < cache->eval_cache.mat_len; i++) {
|
||||
GPU_BATCH_DISCARD_SAFE(cache->eval_cache.surface_per_mat[i]);
|
||||
}
|
||||
}
|
||||
MEM_SAFE_DELETE(cache->eval_cache.surface_per_mat);
|
||||
|
||||
pointcloud_discard_attributes(*cache);
|
||||
}
|
||||
|
||||
void DRW_pointcloud_batch_cache_validate(PointCloud *pointcloud)
|
||||
{
|
||||
if (!pointcloud_batch_cache_valid(*pointcloud)) {
|
||||
pointcloud_batch_cache_clear(*pointcloud);
|
||||
pointcloud_batch_cache_init(*pointcloud);
|
||||
}
|
||||
}
|
||||
|
||||
void DRW_pointcloud_batch_cache_free(PointCloud *pointcloud)
|
||||
{
|
||||
pointcloud_batch_cache_clear(*pointcloud);
|
||||
MEM_delete(pointcloud->batch_cache);
|
||||
pointcloud->batch_cache = nullptr;
|
||||
}
|
||||
|
||||
void DRW_pointcloud_batch_cache_free_old(PointCloud *pointcloud, int ctime)
|
||||
{
|
||||
PointCloudBatchCache *cache = pointcloud_batch_cache_get(*pointcloud);
|
||||
if (!cache) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool do_discard = false;
|
||||
|
||||
if (drw_attributes_overlap(&cache->eval_cache.attr_used_over_time, &cache->eval_cache.attr_used))
|
||||
{
|
||||
cache->eval_cache.last_attr_matching_time = ctime;
|
||||
}
|
||||
|
||||
if (ctime - cache->eval_cache.last_attr_matching_time > U.vbotimeout) {
|
||||
do_discard = true;
|
||||
}
|
||||
|
||||
cache->eval_cache.attr_used_over_time.clear();
|
||||
|
||||
if (do_discard) {
|
||||
pointcloud_discard_attributes(*cache);
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name PointCloud extraction
|
||||
* \{ */
|
||||
|
||||
static void pointcloud_extract_position_and_radius(const PointCloud &pointcloud,
|
||||
PointCloudBatchCache &cache)
|
||||
{
|
||||
const bke::AttributeAccessor attributes = pointcloud.attributes();
|
||||
const Span<float3> positions = pointcloud.positions();
|
||||
const VArray<float> radii = *attributes.lookup<float>("radius");
|
||||
static const GPUVertFormat format = [&]() {
|
||||
GPUVertFormat format{};
|
||||
GPU_vertformat_attr_add(&format, "pos", gpu::VertAttrType::SFLOAT_32_32_32_32);
|
||||
GPU_vertformat_alias_add(&format, "pos_rad");
|
||||
return format;
|
||||
}();
|
||||
|
||||
GPUUsageType usage_flag = GPU_USAGE_STATIC | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY;
|
||||
GPU_vertbuf_init_with_format_ex(*cache.eval_cache.pos_rad, format, usage_flag);
|
||||
|
||||
GPU_vertbuf_data_alloc(*cache.eval_cache.pos_rad, positions.size());
|
||||
MutableSpan<float4> vbo_data = cache.eval_cache.pos_rad->data<float4>();
|
||||
if (radii) {
|
||||
const VArraySpan<float> radii_span(std::move(radii));
|
||||
threading::parallel_for(vbo_data.index_range(), 4096, [&](IndexRange range) {
|
||||
for (const int i : range) {
|
||||
vbo_data[i].x = positions[i].x;
|
||||
vbo_data[i].y = positions[i].y;
|
||||
vbo_data[i].z = positions[i].z;
|
||||
vbo_data[i].w = radii_span[i];
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
threading::parallel_for(vbo_data.index_range(), 4096, [&](IndexRange range) {
|
||||
for (const int i : range) {
|
||||
vbo_data[i].x = positions[i].x;
|
||||
vbo_data[i].y = positions[i].y;
|
||||
vbo_data[i].z = positions[i].z;
|
||||
vbo_data[i].w = 0.01f;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static void pointcloud_extract_attribute(const PointCloud &pointcloud,
|
||||
PointCloudBatchCache &cache,
|
||||
const StringRef name,
|
||||
int index)
|
||||
{
|
||||
gpu::VertBuf &attr_buf = *cache.eval_cache.attributes_buf[index];
|
||||
|
||||
const bke::AttributeAccessor attributes = pointcloud.attributes();
|
||||
|
||||
/* TODO(@kevindietrich): float4 is used for scalar attributes as the implicit conversion done
|
||||
* by OpenGL to float4 for a scalar `s` will produce a `float4(s, 0, 0, 1)`. However, following
|
||||
* the Blender convention, it should be `float4(s, s, s, 1)`. This could be resolved using a
|
||||
* similar texture state swizzle to map the attribute correctly as for volume attributes, so we
|
||||
* can control the conversion ourselves. */
|
||||
bke::AttributeReader<ColorGeometry4f> attribute = attributes.lookup_or_default<ColorGeometry4f>(
|
||||
name, bke::AttrDomain::Point, {0.0f, 0.0f, 0.0f, 1.0f});
|
||||
|
||||
static const GPUVertFormat format = [&]() {
|
||||
GPUVertFormat format{};
|
||||
GPU_vertformat_attr_add(&format, "attr", gpu::VertAttrType::SFLOAT_32_32_32_32);
|
||||
return format;
|
||||
}();
|
||||
GPUUsageType usage_flag = GPU_USAGE_STATIC | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY;
|
||||
GPU_vertbuf_init_with_format_ex(attr_buf, format, usage_flag);
|
||||
GPU_vertbuf_data_alloc(attr_buf, pointcloud.totpoint);
|
||||
|
||||
attribute.varray.materialize(attr_buf.data<ColorGeometry4f>());
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Private API
|
||||
* \{ */
|
||||
|
||||
gpu::VertBuf *pointcloud_position_and_radius_get(PointCloud *pointcloud)
|
||||
{
|
||||
PointCloudBatchCache *cache = pointcloud_batch_cache_get(*pointcloud);
|
||||
DRW_vbo_request(nullptr, &cache->eval_cache.pos_rad);
|
||||
return cache->eval_cache.pos_rad;
|
||||
}
|
||||
|
||||
gpu::Batch **pointcloud_surface_shaded_get(PointCloud *pointcloud,
|
||||
GPUMaterial **gpu_materials,
|
||||
int mat_len)
|
||||
{
|
||||
const bke::AttributeAccessor attributes = pointcloud->attributes();
|
||||
PointCloudBatchCache *cache = pointcloud_batch_cache_get(*pointcloud);
|
||||
VectorSet<std::string> attrs_needed;
|
||||
|
||||
for (GPUMaterial *gpu_material : Span<GPUMaterial *>(gpu_materials, mat_len)) {
|
||||
ListBaseT<GPUMaterialAttribute> gpu_attrs = GPU_material_attributes(gpu_material);
|
||||
for (GPUMaterialAttribute &gpu_attr : gpu_attrs) {
|
||||
const StringRef name = gpu_attr.name;
|
||||
if (!attributes.contains(name)) {
|
||||
continue;
|
||||
}
|
||||
drw_attributes_add_request(&attrs_needed, name);
|
||||
}
|
||||
}
|
||||
|
||||
if (!drw_attributes_overlap(&cache->eval_cache.attr_used, &attrs_needed)) {
|
||||
/* Some new attributes have been added, free all and start over. */
|
||||
for (const int i : IndexRange(GPU_MAX_ATTR)) {
|
||||
GPU_VERTBUF_DISCARD_SAFE(cache->eval_cache.attributes_buf[i]);
|
||||
}
|
||||
drw_attributes_merge(&cache->eval_cache.attr_used, &attrs_needed);
|
||||
}
|
||||
drw_attributes_merge(&cache->eval_cache.attr_used_over_time, &attrs_needed);
|
||||
|
||||
DRW_batch_request(&cache->eval_cache.surface_per_mat[0]);
|
||||
return cache->eval_cache.surface_per_mat;
|
||||
}
|
||||
|
||||
gpu::Batch *pointcloud_surface_get(PointCloud *pointcloud)
|
||||
{
|
||||
PointCloudBatchCache &cache = *pointcloud_batch_cache_get(*pointcloud);
|
||||
if (cache.eval_cache.surface != nullptr) {
|
||||
return cache.eval_cache.surface;
|
||||
}
|
||||
|
||||
cache.eval_cache.surface = GPU_batch_create_procedural(
|
||||
GPU_PRIM_TRI_STRIP, DRW_POINTCLOUD_STRIP_TILE_SIZE * pointcloud->totpoint);
|
||||
DRW_vbo_request(nullptr, &cache.eval_cache.pos_rad);
|
||||
return cache.eval_cache.surface;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name API
|
||||
* \{ */
|
||||
|
||||
gpu::Batch *DRW_pointcloud_batch_cache_get_dots(Object *ob)
|
||||
{
|
||||
PointCloud &pointcloud = DRW_object_get_data_for_drawing<PointCloud>(*ob);
|
||||
PointCloudBatchCache *cache = pointcloud_batch_cache_get(pointcloud);
|
||||
return DRW_batch_request(&cache->eval_cache.dots);
|
||||
}
|
||||
|
||||
gpu::VertBuf *DRW_pointcloud_position_and_radius_buffer_get(Object *ob)
|
||||
{
|
||||
PointCloud &pointcloud = DRW_object_get_data_for_drawing<PointCloud>(*ob);
|
||||
return pointcloud_position_and_radius_get(&pointcloud);
|
||||
}
|
||||
|
||||
gpu::VertBuf **DRW_pointcloud_evaluated_attribute(PointCloud *pointcloud, const StringRef name)
|
||||
{
|
||||
const bke::AttributeAccessor attributes = pointcloud->attributes();
|
||||
PointCloudBatchCache &cache = *pointcloud_batch_cache_get(*pointcloud);
|
||||
|
||||
if (!attributes.contains(name)) {
|
||||
return nullptr;
|
||||
}
|
||||
{
|
||||
VectorSet<std::string> requests{};
|
||||
drw_attributes_add_request(&requests, name);
|
||||
drw_attributes_merge(&cache.eval_cache.attr_used, &requests);
|
||||
}
|
||||
|
||||
int request_i = -1;
|
||||
for (const int i : IndexRange(cache.eval_cache.attr_used.index_range())) {
|
||||
if (cache.eval_cache.attr_used[i] == name) {
|
||||
request_i = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (request_i == -1) {
|
||||
return nullptr;
|
||||
}
|
||||
return &cache.eval_cache.attributes_buf[request_i];
|
||||
}
|
||||
|
||||
static void index_mask_to_ibo(const IndexMask &mask, gpu::IndexBuf &ibo)
|
||||
{
|
||||
const int max_index = mask.min_array_size();
|
||||
GPUIndexBufBuilder builder;
|
||||
GPU_indexbuf_init(&builder, GPU_PRIM_POINTS, mask.size(), max_index);
|
||||
MutableSpan<uint> data = GPU_indexbuf_get_data(&builder);
|
||||
mask.to_indices<int>(data.cast<int>());
|
||||
GPU_indexbuf_build_in_place_ex(&builder, 0, max_index, false, &ibo);
|
||||
}
|
||||
|
||||
static void build_edit_selection_indices(const PointCloud &pointcloud, gpu::IndexBuf &ibo)
|
||||
{
|
||||
const VArray selection = *pointcloud.attributes().lookup_or_default<bool>(
|
||||
".selection", bke::AttrDomain::Point, true);
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask mask = IndexMask::from_bools(selection, memory);
|
||||
if (mask.is_empty()) {
|
||||
return;
|
||||
}
|
||||
index_mask_to_ibo(mask, ibo);
|
||||
}
|
||||
|
||||
void DRW_pointcloud_batch_cache_create_requested(Object *ob)
|
||||
{
|
||||
PointCloud &pointcloud = DRW_object_get_data_for_drawing<PointCloud>(*ob);
|
||||
PointCloudBatchCache &cache = *pointcloud_batch_cache_get(pointcloud);
|
||||
|
||||
if (DRW_batch_requested(cache.eval_cache.dots, GPU_PRIM_POINTS)) {
|
||||
DRW_vbo_request(cache.eval_cache.dots, &cache.eval_cache.pos_rad);
|
||||
}
|
||||
|
||||
if (DRW_batch_requested(cache.edit_selection, GPU_PRIM_POINTS)) {
|
||||
DRW_ibo_request(cache.edit_selection, &cache.edit_selection_indices);
|
||||
DRW_vbo_request(cache.edit_selection, &cache.eval_cache.pos_rad);
|
||||
}
|
||||
|
||||
for (int i = 0; i < cache.eval_cache.mat_len; i++) {
|
||||
if (DRW_batch_requested(cache.eval_cache.surface_per_mat[i], GPU_PRIM_TRIS)) {
|
||||
/* TODO(fclem): Per material ranges. */
|
||||
}
|
||||
}
|
||||
for (const int j : cache.eval_cache.attr_used.index_range()) {
|
||||
DRW_vbo_request(nullptr, &cache.eval_cache.attributes_buf[j]);
|
||||
|
||||
if (DRW_vbo_requested(cache.eval_cache.attributes_buf[j])) {
|
||||
pointcloud_extract_attribute(pointcloud, cache, cache.eval_cache.attr_used[j], j);
|
||||
}
|
||||
}
|
||||
|
||||
if (DRW_ibo_requested(cache.edit_selection_indices)) {
|
||||
build_edit_selection_indices(pointcloud, *cache.edit_selection_indices);
|
||||
}
|
||||
|
||||
if (DRW_vbo_requested(cache.eval_cache.pos_rad)) {
|
||||
pointcloud_extract_position_and_radius(pointcloud, cache);
|
||||
}
|
||||
}
|
||||
|
||||
gpu::Batch *DRW_pointcloud_batch_cache_get_edit_dots(PointCloud *pointcloud)
|
||||
{
|
||||
PointCloudBatchCache *cache = pointcloud_batch_cache_get(*pointcloud);
|
||||
return DRW_batch_request(&cache->edit_selection);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::draw
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,351 @@
|
||||
/* SPDX-FileCopyrightText: 2017 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*
|
||||
* \brief Volume API for render engines
|
||||
*/
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math_matrix.hh"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_volume_types.h"
|
||||
|
||||
#include "BKE_global.hh"
|
||||
#include "BKE_volume.hh"
|
||||
#include "BKE_volume_grid_fwd.hh"
|
||||
#include "BKE_volume_render.hh"
|
||||
|
||||
#include "GPU_attribute_convert.hh"
|
||||
#include "GPU_batch.hh"
|
||||
#include "GPU_capabilities.hh"
|
||||
#include "GPU_texture.hh"
|
||||
|
||||
#include "DRW_render.hh"
|
||||
|
||||
#include "draw_cache.hh" /* own include */
|
||||
#include "draw_cache_impl.hh" /* own include */
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
static void volume_batch_cache_clear(Volume *volume);
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Volume gpu::Batch Cache */
|
||||
|
||||
struct VolumeBatchCache {
|
||||
/* 3D textures */
|
||||
ListBaseT<DRWVolumeGrid> grids;
|
||||
|
||||
/* Wireframe */
|
||||
struct {
|
||||
gpu::VertBuf *pos_nor_in_order;
|
||||
gpu::Batch *batch;
|
||||
} face_wire;
|
||||
|
||||
/* Surface for selection */
|
||||
gpu::Batch *selection_surface;
|
||||
|
||||
/* settings to determine if cache is invalid */
|
||||
bool is_dirty;
|
||||
};
|
||||
|
||||
/* gpu::Batch cache management. */
|
||||
|
||||
static bool volume_batch_cache_valid(Volume *volume)
|
||||
{
|
||||
VolumeBatchCache *cache = volume->batch_cache;
|
||||
return (cache && cache->is_dirty == false);
|
||||
}
|
||||
|
||||
static void volume_batch_cache_init(Volume *volume)
|
||||
{
|
||||
VolumeBatchCache *cache = volume->batch_cache;
|
||||
|
||||
if (!cache) {
|
||||
volume->batch_cache = cache = MEM_new_zeroed<VolumeBatchCache>(__func__);
|
||||
}
|
||||
else {
|
||||
memset(cache, 0, sizeof(*cache));
|
||||
}
|
||||
|
||||
cache->is_dirty = false;
|
||||
}
|
||||
|
||||
void DRW_volume_batch_cache_validate(Volume *volume)
|
||||
{
|
||||
if (!volume_batch_cache_valid(volume)) {
|
||||
volume_batch_cache_clear(volume);
|
||||
volume_batch_cache_init(volume);
|
||||
}
|
||||
}
|
||||
|
||||
static VolumeBatchCache *volume_batch_cache_get(Volume *volume)
|
||||
{
|
||||
DRW_volume_batch_cache_validate(volume);
|
||||
return volume->batch_cache;
|
||||
}
|
||||
|
||||
void DRW_volume_batch_cache_dirty_tag(Volume *volume, int mode)
|
||||
{
|
||||
VolumeBatchCache *cache = volume->batch_cache;
|
||||
if (cache == nullptr) {
|
||||
return;
|
||||
}
|
||||
switch (mode) {
|
||||
case BKE_VOLUME_BATCH_DIRTY_ALL:
|
||||
cache->is_dirty = true;
|
||||
break;
|
||||
default:
|
||||
BLI_assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void volume_batch_cache_clear(Volume *volume)
|
||||
{
|
||||
VolumeBatchCache *cache = volume->batch_cache;
|
||||
if (!cache) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (DRWVolumeGrid &grid : cache->grids) {
|
||||
MEM_SAFE_DELETE(grid.name);
|
||||
GPU_TEXTURE_FREE_SAFE(grid.texture);
|
||||
}
|
||||
cache->grids.free_no_destruct();
|
||||
|
||||
GPU_VERTBUF_DISCARD_SAFE(cache->face_wire.pos_nor_in_order);
|
||||
GPU_BATCH_DISCARD_SAFE(cache->face_wire.batch);
|
||||
GPU_BATCH_DISCARD_SAFE(cache->selection_surface);
|
||||
}
|
||||
|
||||
void DRW_volume_batch_cache_free(Volume *volume)
|
||||
{
|
||||
volume_batch_cache_clear(volume);
|
||||
MEM_SAFE_DELETE(volume->batch_cache);
|
||||
}
|
||||
struct VolumeWireframeUserData {
|
||||
Volume *volume;
|
||||
Scene *scene;
|
||||
};
|
||||
|
||||
static void drw_volume_wireframe_cb(
|
||||
void *userdata, const float (*verts)[3], const int (*edges)[2], int totvert, int totedge)
|
||||
{
|
||||
VolumeWireframeUserData *data = static_cast<VolumeWireframeUserData *>(userdata);
|
||||
Scene *scene = data->scene;
|
||||
Volume *volume = data->volume;
|
||||
VolumeBatchCache *cache = volume->batch_cache;
|
||||
const bool do_hq_normals = (scene->r.perf_flag & SCE_PERF_HQ_NORMALS) != 0 ||
|
||||
GPU_use_hq_normals_workaround();
|
||||
|
||||
/* Create vertex buffer. */
|
||||
static struct {
|
||||
uint pos_id, nor_id;
|
||||
uint pos_hq_id, nor_hq_id;
|
||||
} attr_id;
|
||||
|
||||
static const GPUVertFormat format = [&]() {
|
||||
GPUVertFormat format{};
|
||||
attr_id.pos_id = GPU_vertformat_attr_add(&format, "pos", gpu::VertAttrType::SFLOAT_32_32_32);
|
||||
attr_id.nor_id = GPU_vertformat_attr_add(&format, "nor", gpu::VertAttrType::SNORM_10_10_10_2);
|
||||
return format;
|
||||
}();
|
||||
|
||||
static const GPUVertFormat format_hq = [&]() {
|
||||
GPUVertFormat format{};
|
||||
attr_id.pos_hq_id = GPU_vertformat_attr_add(
|
||||
&format, "pos", gpu::VertAttrType::SFLOAT_32_32_32);
|
||||
attr_id.nor_hq_id = GPU_vertformat_attr_add(
|
||||
&format, "nor", gpu::VertAttrType::SNORM_16_16_16_16);
|
||||
return format;
|
||||
}();
|
||||
|
||||
uint pos_id = do_hq_normals ? attr_id.pos_hq_id : attr_id.pos_id;
|
||||
uint nor_id = do_hq_normals ? attr_id.nor_hq_id : attr_id.nor_id;
|
||||
|
||||
cache->face_wire.pos_nor_in_order = GPU_vertbuf_create_with_format(do_hq_normals ? format_hq :
|
||||
format);
|
||||
GPU_vertbuf_data_alloc(*cache->face_wire.pos_nor_in_order, totvert);
|
||||
GPU_vertbuf_attr_fill(cache->face_wire.pos_nor_in_order, pos_id, verts);
|
||||
const float3 normal(1.0f, 0.0f, 0.0f);
|
||||
if (do_hq_normals) {
|
||||
const gpu::PackedNormal packed_normal = gpu::convert_normal<gpu::PackedNormal>(normal);
|
||||
GPU_vertbuf_attr_fill_stride(cache->face_wire.pos_nor_in_order, nor_id, 0, &packed_normal);
|
||||
}
|
||||
else {
|
||||
const short4 packed_normal = gpu::convert_normal<short4>(normal);
|
||||
GPU_vertbuf_attr_fill_stride(cache->face_wire.pos_nor_in_order, nor_id, 0, &packed_normal);
|
||||
}
|
||||
|
||||
/* Create wiredata. */
|
||||
gpu::VertBuf *vbo_wiredata = GPU_vertbuf_calloc();
|
||||
DRW_vertbuf_create_wiredata(vbo_wiredata, totvert);
|
||||
|
||||
if (volume->display.wireframe_type == VOLUME_WIREFRAME_POINTS) {
|
||||
/* Create batch. */
|
||||
cache->face_wire.batch = GPU_batch_create(
|
||||
GPU_PRIM_POINTS, cache->face_wire.pos_nor_in_order, nullptr);
|
||||
}
|
||||
else {
|
||||
/* Create edge index buffer. */
|
||||
GPUIndexBufBuilder elb;
|
||||
GPU_indexbuf_init(&elb, GPU_PRIM_LINES, totedge, totvert);
|
||||
for (int i = 0; i < totedge; i++) {
|
||||
GPU_indexbuf_add_line_verts(&elb, edges[i][0], edges[i][1]);
|
||||
}
|
||||
gpu::IndexBuf *ibo = GPU_indexbuf_build(&elb);
|
||||
|
||||
/* Create batch. */
|
||||
cache->face_wire.batch = GPU_batch_create_ex(
|
||||
GPU_PRIM_LINES, cache->face_wire.pos_nor_in_order, ibo, GPU_BATCH_OWNS_INDEX);
|
||||
}
|
||||
|
||||
GPU_batch_vertbuf_add(cache->face_wire.batch, vbo_wiredata, true);
|
||||
}
|
||||
|
||||
gpu::Batch *DRW_volume_batch_cache_get_wireframes_face(Volume *volume)
|
||||
{
|
||||
if (volume->display.wireframe_type == VOLUME_WIREFRAME_NONE) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
VolumeBatchCache *cache = volume_batch_cache_get(volume);
|
||||
|
||||
if (cache->face_wire.batch == nullptr) {
|
||||
const bke::VolumeGridData *volume_grid = BKE_volume_grid_active_get_for_read(volume);
|
||||
if (volume_grid == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Create wireframe from OpenVDB tree. */
|
||||
const DRWContext *draw_ctx = DRW_context_get();
|
||||
VolumeWireframeUserData userdata;
|
||||
userdata.volume = volume;
|
||||
userdata.scene = draw_ctx->scene;
|
||||
BKE_volume_grid_wireframe(volume, volume_grid, drw_volume_wireframe_cb, &userdata);
|
||||
}
|
||||
|
||||
return cache->face_wire.batch;
|
||||
}
|
||||
|
||||
static void drw_volume_selection_surface_cb(
|
||||
void *userdata, float (*verts)[3], int (*tris)[3], int totvert, int tottris)
|
||||
{
|
||||
Volume *volume = static_cast<Volume *>(userdata);
|
||||
VolumeBatchCache *cache = volume->batch_cache;
|
||||
|
||||
static uint pos_id;
|
||||
static const GPUVertFormat format = [&]() {
|
||||
GPUVertFormat format{};
|
||||
pos_id = GPU_vertformat_attr_add(&format, "pos", gpu::VertAttrType::SFLOAT_32_32_32);
|
||||
return format;
|
||||
}();
|
||||
|
||||
/* Create vertex buffer. */
|
||||
gpu::VertBuf *vbo_surface = GPU_vertbuf_create_with_format(format);
|
||||
GPU_vertbuf_data_alloc(*vbo_surface, totvert);
|
||||
GPU_vertbuf_attr_fill(vbo_surface, pos_id, verts);
|
||||
|
||||
/* Create index buffer. */
|
||||
GPUIndexBufBuilder elb;
|
||||
GPU_indexbuf_init(&elb, GPU_PRIM_TRIS, tottris, totvert);
|
||||
for (int i = 0; i < tottris; i++) {
|
||||
GPU_indexbuf_add_tri_verts(&elb, UNPACK3(tris[i]));
|
||||
}
|
||||
gpu::IndexBuf *ibo_surface = GPU_indexbuf_build(&elb);
|
||||
|
||||
cache->selection_surface = GPU_batch_create_ex(
|
||||
GPU_PRIM_TRIS, vbo_surface, ibo_surface, GPU_BATCH_OWNS_VBO | GPU_BATCH_OWNS_INDEX);
|
||||
}
|
||||
|
||||
gpu::Batch *DRW_volume_batch_cache_get_selection_surface(Volume *volume)
|
||||
{
|
||||
VolumeBatchCache *cache = volume_batch_cache_get(volume);
|
||||
if (cache->selection_surface == nullptr) {
|
||||
const bke::VolumeGridData *volume_grid = BKE_volume_grid_active_get_for_read(volume);
|
||||
if (volume_grid == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
BKE_volume_grid_selection_surface(
|
||||
volume, volume_grid, drw_volume_selection_surface_cb, volume);
|
||||
}
|
||||
return cache->selection_surface;
|
||||
}
|
||||
|
||||
static DRWVolumeGrid *volume_grid_cache_get(const Volume *volume,
|
||||
const bke::VolumeGridData *grid,
|
||||
VolumeBatchCache *cache)
|
||||
{
|
||||
const std::string name = bke::volume_grid::get_name(*grid);
|
||||
|
||||
/* Return cached grid. */
|
||||
for (DRWVolumeGrid &cache_grid : cache->grids) {
|
||||
if (cache_grid.name == name) {
|
||||
return &cache_grid;
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate new grid. */
|
||||
DRWVolumeGrid *cache_grid = MEM_new_zeroed<DRWVolumeGrid>(__func__);
|
||||
cache_grid->name = BLI_strdup(name.c_str());
|
||||
BLI_addtail(&cache->grids, cache_grid);
|
||||
|
||||
/* TODO: can we load this earlier, avoid accessing the global and take
|
||||
* advantage of dependency graph multi-threading? */
|
||||
BKE_volume_load(volume, G.main);
|
||||
|
||||
/* Test if we support textures with the number of channels. */
|
||||
size_t channels = bke::volume_grid::get_channels_num(bke::volume_grid::get_type(*grid));
|
||||
if (!ELEM(channels, 1, 3)) {
|
||||
return cache_grid;
|
||||
}
|
||||
|
||||
DenseFloatVolumeGrid dense_grid;
|
||||
if (BKE_volume_grid_dense_floats(volume, grid, &dense_grid)) {
|
||||
cache_grid->texture_to_object = float4x4(dense_grid.texture_to_object);
|
||||
cache_grid->object_to_texture = math::invert(cache_grid->texture_to_object);
|
||||
|
||||
/* Create GPU texture. */
|
||||
gpu::TextureFormat format = (channels == 3) ? gpu::TextureFormat::SFLOAT_16_16_16 :
|
||||
gpu::TextureFormat::SFLOAT_16;
|
||||
cache_grid->texture = GPU_texture_create_3d("volume_grid",
|
||||
UNPACK3(dense_grid.resolution),
|
||||
1,
|
||||
format,
|
||||
GPU_TEXTURE_USAGE_SHADER_READ,
|
||||
dense_grid.voxels);
|
||||
/* The texture can be null if the resolution along one axis is larger than
|
||||
* GL_MAX_3D_TEXTURE_SIZE. */
|
||||
if (cache_grid->texture != nullptr) {
|
||||
GPU_texture_swizzle_set(cache_grid->texture, (channels == 3) ? "rgb1" : "rrr1");
|
||||
GPU_texture_extend_mode(cache_grid->texture, GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER);
|
||||
BKE_volume_dense_float_grid_clear(&dense_grid);
|
||||
}
|
||||
else {
|
||||
MEM_delete(dense_grid.voxels);
|
||||
printf("Error: Could not allocate 3D texture for volume.\n");
|
||||
}
|
||||
}
|
||||
|
||||
return cache_grid;
|
||||
}
|
||||
|
||||
DRWVolumeGrid *DRW_volume_batch_cache_get_grid(Volume *volume,
|
||||
const bke::VolumeGridData *volume_grid)
|
||||
{
|
||||
VolumeBatchCache *cache = volume_batch_cache_get(volume);
|
||||
DRWVolumeGrid *grid = volume_grid_cache_get(volume, volume_grid, cache);
|
||||
return (grid->texture != nullptr) ? grid : nullptr;
|
||||
}
|
||||
|
||||
} // namespace blender::draw
|
||||
@@ -0,0 +1,88 @@
|
||||
/* SPDX-FileCopyrightText: 2016 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GPU_batch.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* Common */
|
||||
// #define DRW_DEBUG_MESH_CACHE_REQUEST
|
||||
|
||||
#ifdef DRW_DEBUG_MESH_CACHE_REQUEST
|
||||
# define DRW_ADD_FLAG_FROM_VBO_REQUEST(flag, vbo, value) \
|
||||
(flag |= DRW_vbo_requested(vbo) ? (printf(" VBO requested " #vbo "\n") ? value : value) : 0)
|
||||
# define DRW_ADD_FLAG_FROM_IBO_REQUEST(flag, ibo, value) \
|
||||
(flag |= DRW_ibo_requested(ibo) ? (printf(" IBO requested " #ibo "\n") ? value : value) : 0)
|
||||
#else
|
||||
# define DRW_ADD_FLAG_FROM_VBO_REQUEST(flag, vbo, value) \
|
||||
(flag |= DRW_vbo_requested(vbo) ? (value) : 0)
|
||||
# define DRW_ADD_FLAG_FROM_IBO_REQUEST(flag, ibo, value) \
|
||||
(flag |= DRW_ibo_requested(ibo) ? (value) : 0)
|
||||
#endif
|
||||
|
||||
inline gpu::Batch *DRW_batch_request(gpu::Batch **batch)
|
||||
{
|
||||
/* XXX TODO(fclem): We are writing to batch cache here. Need to make this thread safe. */
|
||||
if (*batch == nullptr) {
|
||||
*batch = GPU_batch_calloc();
|
||||
}
|
||||
return *batch;
|
||||
}
|
||||
|
||||
inline bool DRW_batch_requested(gpu::Batch *batch, GPUPrimType prim_type)
|
||||
{
|
||||
/* Batch has been requested if it has been created but not initialized. */
|
||||
if (batch != nullptr && batch->verts[0] == nullptr) {
|
||||
/* HACK. We init without a valid VBO and let the first vbo binding
|
||||
* fill verts[0]. */
|
||||
GPU_batch_init_ex(
|
||||
batch, prim_type, reinterpret_cast<gpu::VertBuf *>(1), nullptr, GPUBatchFlag(0));
|
||||
batch->verts[0] = nullptr;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void DRW_ibo_request(gpu::Batch *batch, gpu::IndexBuf **ibo)
|
||||
{
|
||||
if (*ibo == nullptr) {
|
||||
*ibo = GPU_indexbuf_calloc();
|
||||
}
|
||||
if (batch != nullptr) {
|
||||
GPU_batch_elembuf_set(batch, *ibo, false);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool DRW_ibo_requested(gpu::IndexBuf *ibo)
|
||||
{
|
||||
/* TODO: do not rely on data uploaded. This prevents multi-threading.
|
||||
* (need access to a GPU context). */
|
||||
return (ibo != nullptr && !GPU_indexbuf_is_init(ibo));
|
||||
}
|
||||
|
||||
inline void DRW_vbo_request(gpu::Batch *batch, gpu::VertBuf **vbo)
|
||||
{
|
||||
if (*vbo == nullptr) {
|
||||
*vbo = GPU_vertbuf_calloc();
|
||||
}
|
||||
if (batch != nullptr) {
|
||||
BLI_assert_msg(batch->procedural_vertices == -1,
|
||||
"Request of vertex buffer for procedural batch is not valid operation.");
|
||||
/* HACK we set VBO's that may not yet be valid. */
|
||||
GPU_batch_vertbuf_add(batch, *vbo, false);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool DRW_vbo_requested(gpu::VertBuf *vbo)
|
||||
{
|
||||
return (vbo != nullptr && (GPU_vertbuf_get_status(vbo) & GPU_VERTBUF_INIT) == 0);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,164 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "GPU_viewport.hh"
|
||||
|
||||
#include "BLI_string_utf8.h"
|
||||
|
||||
#include "DRW_render.hh"
|
||||
|
||||
#include "DNA_space_types.h"
|
||||
#include "DNA_view3d_types.h"
|
||||
|
||||
#include "BKE_colortools.hh"
|
||||
#include "BKE_image.hh"
|
||||
#include "BKE_scene.hh"
|
||||
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
|
||||
#include "ED_node_c.hh"
|
||||
|
||||
#include "draw_color_management.hh"
|
||||
|
||||
namespace blender::draw::color_management {
|
||||
|
||||
enum class eDRWColorManagementType {
|
||||
ViewTransform = 0,
|
||||
ViewTransformAndLook,
|
||||
UseRenderSettings,
|
||||
};
|
||||
|
||||
static float dither_get(eDRWColorManagementType color_management_type, const Scene &scene)
|
||||
{
|
||||
if (ELEM(color_management_type,
|
||||
eDRWColorManagementType::ViewTransformAndLook,
|
||||
eDRWColorManagementType::UseRenderSettings))
|
||||
{
|
||||
return scene.r.dither_intensity;
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
static eDRWColorManagementType drw_color_management_type_for_v3d(const Scene &scene,
|
||||
const View3D &v3d)
|
||||
{
|
||||
|
||||
const bool use_workbench = BKE_scene_uses_blender_workbench(&scene);
|
||||
const bool use_scene_lights = V3D_USES_SCENE_LIGHTS(&v3d);
|
||||
const bool use_scene_world = V3D_USES_SCENE_WORLD(&v3d);
|
||||
|
||||
if ((use_workbench && v3d.shading.type == OB_RENDER) || use_scene_lights || use_scene_world) {
|
||||
return eDRWColorManagementType::UseRenderSettings;
|
||||
}
|
||||
if (v3d.shading.type >= OB_MATERIAL) {
|
||||
return eDRWColorManagementType::ViewTransformAndLook;
|
||||
}
|
||||
return eDRWColorManagementType::ViewTransform;
|
||||
}
|
||||
|
||||
static eDRWColorManagementType drw_color_management_type_for_space_image(const SpaceImage &sima)
|
||||
{
|
||||
const Image *image = sima.image;
|
||||
|
||||
/* Use inverse logic as there isn't a setting for `Color & Alpha`. */
|
||||
const eSpaceImage_Flag display_channels_mode = static_cast<eSpaceImage_Flag>(sima.flag);
|
||||
const bool display_color_channel = (display_channels_mode & (SI_SHOW_ALPHA | SI_SHOW_ZBUF)) == 0;
|
||||
|
||||
if (display_color_channel && image && (image->source != IMA_SRC_GENERATED) &&
|
||||
((image->flag & IMA_VIEW_AS_RENDER) != 0))
|
||||
{
|
||||
return eDRWColorManagementType::UseRenderSettings;
|
||||
}
|
||||
return eDRWColorManagementType::ViewTransform;
|
||||
}
|
||||
|
||||
static eDRWColorManagementType drw_color_management_type_for_space_node(Main &bmain,
|
||||
const SpaceNode &snode)
|
||||
{
|
||||
if ((snode.flag & SNODE_BACKDRAW) && ED_node_is_compositor(&snode)) {
|
||||
const Image *image = BKE_image_ensure_viewer(&bmain, IMA_TYPE_COMPOSITE, "Viewer Node");
|
||||
if ((image->flag & IMA_VIEW_AS_RENDER) == 0) {
|
||||
return eDRWColorManagementType::ViewTransform;
|
||||
}
|
||||
}
|
||||
|
||||
const eSpaceNode_Flag display_channels_mode = static_cast<eSpaceNode_Flag>(snode.flag);
|
||||
const bool display_color_channel = (display_channels_mode & SNODE_SHOW_ALPHA) == 0;
|
||||
if (display_color_channel) {
|
||||
return eDRWColorManagementType::UseRenderSettings;
|
||||
}
|
||||
return eDRWColorManagementType::ViewTransform;
|
||||
}
|
||||
|
||||
static eDRWColorManagementType drw_color_management_type_get(Main *bmain,
|
||||
const Scene &scene,
|
||||
const View3D *v3d,
|
||||
const SpaceLink *space_data)
|
||||
{
|
||||
if (v3d) {
|
||||
return drw_color_management_type_for_v3d(scene, *v3d);
|
||||
}
|
||||
if (space_data) {
|
||||
switch (space_data->spacetype) {
|
||||
case SPACE_IMAGE: {
|
||||
const SpaceImage *sima = reinterpret_cast<const SpaceImage *>(space_data);
|
||||
return drw_color_management_type_for_space_image(*sima);
|
||||
}
|
||||
case SPACE_NODE: {
|
||||
const SpaceNode *snode = reinterpret_cast<const SpaceNode *>(space_data);
|
||||
return drw_color_management_type_for_space_node(*bmain, *snode);
|
||||
}
|
||||
}
|
||||
}
|
||||
return eDRWColorManagementType::UseRenderSettings;
|
||||
}
|
||||
|
||||
static void viewport_settings_apply(GPUViewport &viewport,
|
||||
const Scene &scene,
|
||||
const eDRWColorManagementType color_management_type)
|
||||
{
|
||||
const ColorManagedDisplaySettings *display_settings = &scene.display_settings;
|
||||
ColorManagedViewSettings view_settings;
|
||||
|
||||
switch (color_management_type) {
|
||||
case eDRWColorManagementType::ViewTransform: {
|
||||
/* For workbench use only default view transform in configuration,
|
||||
* using no scene settings. */
|
||||
BKE_color_managed_view_settings_init(&view_settings, display_settings, nullptr);
|
||||
break;
|
||||
}
|
||||
case eDRWColorManagementType::ViewTransformAndLook: {
|
||||
/* Use only view transform + look and nothing else for lookdev without
|
||||
* scene lighting, as exposure depends on scene light intensity. */
|
||||
BKE_color_managed_view_settings_init(&view_settings, display_settings, nullptr);
|
||||
STRNCPY_UTF8(view_settings.view_transform, scene.view_settings.view_transform);
|
||||
STRNCPY_UTF8(view_settings.look, scene.view_settings.look);
|
||||
break;
|
||||
}
|
||||
case eDRWColorManagementType::UseRenderSettings: {
|
||||
/* Use full render settings, for renders with scene lighting. */
|
||||
view_settings = scene.view_settings;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const float dither = dither_get(color_management_type, scene);
|
||||
GPU_viewport_colorspace_set(&viewport, &view_settings, display_settings, dither);
|
||||
}
|
||||
|
||||
void viewport_color_management_set(GPUViewport &viewport, DRWContext &draw_ctx)
|
||||
{
|
||||
const Depsgraph *depsgraph = draw_ctx.depsgraph;
|
||||
Main *bmain = DEG_get_bmain(depsgraph);
|
||||
|
||||
const eDRWColorManagementType color_management_type = drw_color_management_type_get(
|
||||
bmain, *draw_ctx.scene, draw_ctx.v3d, draw_ctx.space_data);
|
||||
viewport_settings_apply(viewport, *draw_ctx.scene, color_management_type);
|
||||
}
|
||||
|
||||
} // namespace blender::draw::color_management
|
||||
@@ -0,0 +1,25 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace gpu {
|
||||
class Texture;
|
||||
}
|
||||
struct GPUViewport;
|
||||
struct DRWContext;
|
||||
|
||||
namespace draw::color_management {
|
||||
|
||||
void viewport_color_management_set(GPUViewport &viewport, DRWContext &draw_ctx);
|
||||
|
||||
} // namespace draw::color_management
|
||||
|
||||
} // namespace blender
|
||||
884
blender-5.2.0/source/blender/draw/intern/draw_command.cc
Normal file
884
blender-5.2.0/source/blender/draw/intern/draw_command.cc
Normal file
@@ -0,0 +1,884 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "GPU_batch.hh"
|
||||
#include "GPU_capabilities.hh"
|
||||
#include "GPU_compute.hh"
|
||||
#include "GPU_debug.hh"
|
||||
|
||||
#include "draw_command.hh"
|
||||
#include "draw_pass.hh"
|
||||
#include "draw_shader.hh"
|
||||
#include "draw_view.hh"
|
||||
|
||||
#include <bitset>
|
||||
#include <sstream>
|
||||
|
||||
namespace blender::draw::command {
|
||||
|
||||
static gpu::Batch *procedural_batch_get(GPUPrimType primitive)
|
||||
{
|
||||
switch (primitive) {
|
||||
case GPU_PRIM_POINTS:
|
||||
return GPU_batch_procedural_points_get();
|
||||
case GPU_PRIM_LINES:
|
||||
return GPU_batch_procedural_lines_get();
|
||||
case GPU_PRIM_TRIS:
|
||||
return GPU_batch_procedural_triangles_get();
|
||||
case GPU_PRIM_TRI_STRIP:
|
||||
return GPU_batch_procedural_triangle_strips_get();
|
||||
default:
|
||||
/* Add new one as needed. */
|
||||
BLI_assert_unreachable();
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Commands Execution
|
||||
* \{ */
|
||||
|
||||
void ShaderBind::execute(RecordingState &state) const
|
||||
{
|
||||
state.shader_use_specialization = !GPU_shader_get_default_constant_state(shader).is_empty();
|
||||
if (assign_if_different(state.shader, shader) || state.shader_use_specialization) {
|
||||
GPU_shader_bind(shader, state.specialization_constants_get());
|
||||
}
|
||||
/* Signal that we can reload the default for a different specialization later on.
|
||||
* However, we keep the specialization_constants state around for compute shaders. */
|
||||
state.specialization_constants_in_use = false;
|
||||
}
|
||||
|
||||
void FramebufferBind::execute() const
|
||||
{
|
||||
GPU_framebuffer_bind(*framebuffer);
|
||||
}
|
||||
|
||||
void SubPassTransition::execute() const
|
||||
{
|
||||
/* TODO(fclem): Require framebuffer bind to always be part of the pass so that we can track it
|
||||
* inside RecordingState. */
|
||||
gpu::FrameBuffer *framebuffer = GPU_framebuffer_active_get();
|
||||
/* Unpack to the real enum type. */
|
||||
const GPUAttachmentState states[9] = {
|
||||
GPUAttachmentState(depth_state),
|
||||
GPUAttachmentState(color_states[0]),
|
||||
GPUAttachmentState(color_states[1]),
|
||||
GPUAttachmentState(color_states[2]),
|
||||
GPUAttachmentState(color_states[3]),
|
||||
GPUAttachmentState(color_states[4]),
|
||||
GPUAttachmentState(color_states[5]),
|
||||
GPUAttachmentState(color_states[6]),
|
||||
GPUAttachmentState(color_states[7]),
|
||||
};
|
||||
GPU_framebuffer_subpass_transition_array(framebuffer, states, ARRAY_SIZE(states));
|
||||
}
|
||||
|
||||
void ResourceBind::execute() const
|
||||
{
|
||||
if (slot == -1) {
|
||||
return;
|
||||
}
|
||||
switch (type) {
|
||||
case ResourceBind::Type::Sampler:
|
||||
GPU_texture_bind_ex(is_reference ? *texture_ref : texture, sampler, slot);
|
||||
break;
|
||||
case ResourceBind::Type::BufferSampler:
|
||||
GPU_vertbuf_bind_as_texture(is_reference ? *vertex_buf_ref : vertex_buf, slot);
|
||||
break;
|
||||
case ResourceBind::Type::Image:
|
||||
GPU_texture_image_bind(is_reference ? *texture_ref : texture, slot);
|
||||
break;
|
||||
case ResourceBind::Type::UniformBuf:
|
||||
GPU_uniformbuf_bind(is_reference ? *uniform_buf_ref : uniform_buf, slot);
|
||||
break;
|
||||
case ResourceBind::Type::StorageBuf:
|
||||
GPU_storagebuf_bind(is_reference ? *storage_buf_ref : storage_buf, slot);
|
||||
break;
|
||||
case ResourceBind::Type::UniformAsStorageBuf:
|
||||
GPU_uniformbuf_bind_as_ssbo(is_reference ? *uniform_buf_ref : uniform_buf, slot);
|
||||
break;
|
||||
case ResourceBind::Type::VertexAsStorageBuf:
|
||||
GPU_vertbuf_bind_as_ssbo(is_reference ? *vertex_buf_ref : vertex_buf, slot);
|
||||
break;
|
||||
case ResourceBind::Type::IndexAsStorageBuf:
|
||||
GPU_indexbuf_bind_as_ssbo(is_reference ? *index_buf_ref : index_buf, slot);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void PushConstant::execute(RecordingState &state) const
|
||||
{
|
||||
if (location == -1) {
|
||||
return;
|
||||
}
|
||||
switch (type) {
|
||||
case PushConstant::Type::IntValue:
|
||||
GPU_shader_uniform_int_ex(state.shader, location, comp_len, array_len, int4_value);
|
||||
break;
|
||||
case PushConstant::Type::IntReference:
|
||||
GPU_shader_uniform_int_ex(state.shader, location, comp_len, array_len, int_ref);
|
||||
break;
|
||||
case PushConstant::Type::FloatValue:
|
||||
GPU_shader_uniform_float_ex(state.shader, location, comp_len, array_len, float4_value);
|
||||
break;
|
||||
case PushConstant::Type::FloatReference:
|
||||
GPU_shader_uniform_float_ex(state.shader, location, comp_len, array_len, float_ref);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SpecializeConstant::execute(command::RecordingState &state) const
|
||||
{
|
||||
/* All specialization constants should exist as they are not optimized out like uniforms. */
|
||||
BLI_assert(location != -1);
|
||||
|
||||
if (state.specialization_constants_in_use == false) {
|
||||
state.specialization_constants = GPU_shader_get_default_constant_state(this->shader);
|
||||
state.specialization_constants_in_use = true;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case SpecializeConstant::Type::IntValue:
|
||||
state.specialization_constants.set_value(location, int_value);
|
||||
break;
|
||||
case SpecializeConstant::Type::IntReference:
|
||||
state.specialization_constants.set_value(location, *int_ref);
|
||||
break;
|
||||
case SpecializeConstant::Type::UintValue:
|
||||
state.specialization_constants.set_value(location, uint_value);
|
||||
break;
|
||||
case SpecializeConstant::Type::UintReference:
|
||||
state.specialization_constants.set_value(location, *uint_ref);
|
||||
break;
|
||||
case SpecializeConstant::Type::FloatValue:
|
||||
state.specialization_constants.set_value(location, float_value);
|
||||
break;
|
||||
case SpecializeConstant::Type::FloatReference:
|
||||
state.specialization_constants.set_value(location, *float_ref);
|
||||
break;
|
||||
case SpecializeConstant::Type::BoolValue:
|
||||
state.specialization_constants.set_value(location, bool_value);
|
||||
break;
|
||||
case SpecializeConstant::Type::BoolReference:
|
||||
state.specialization_constants.set_value(location, *bool_ref);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Draw::execute(RecordingState &state) const
|
||||
{
|
||||
state.front_facing_set(res_id.has_inverted_handedness());
|
||||
|
||||
/* Use same logic as in `finalize_commands`. */
|
||||
uint instance_first = 0;
|
||||
if (res_id.raw > 0) {
|
||||
instance_first = state.instance_offset;
|
||||
state.instance_offset += instance_len;
|
||||
}
|
||||
|
||||
GPU_shader_get_default_constant_state(state.shader).is_empty();
|
||||
|
||||
if (is_primitive_expansion()) {
|
||||
/* Expanded draw-call. */
|
||||
IndexRange expanded_range = GPU_batch_draw_expanded_parameter_get(
|
||||
batch->prim_type,
|
||||
GPUPrimType(expand_prim_type),
|
||||
vertex_len,
|
||||
vertex_first,
|
||||
expand_prim_len);
|
||||
|
||||
if (expanded_range.is_empty()) {
|
||||
/* Nothing to draw, and can lead to asserts in GPU_batch_bind_as_resources. */
|
||||
return;
|
||||
}
|
||||
|
||||
GPU_batch_bind_as_resources(batch, state.shader, state.specialization_constants_get());
|
||||
|
||||
gpu::Batch *gpu_batch = procedural_batch_get(GPUPrimType(expand_prim_type));
|
||||
GPU_batch_set_shader(gpu_batch, state.shader, state.specialization_constants_get());
|
||||
GPU_batch_draw_advanced(
|
||||
gpu_batch, expanded_range.start(), expanded_range.size(), instance_first, instance_len);
|
||||
}
|
||||
else {
|
||||
/* Regular draw-call. */
|
||||
GPU_batch_set_shader(batch, state.shader, state.specialization_constants_get());
|
||||
GPU_batch_draw_advanced(batch, vertex_first, vertex_len, instance_first, instance_len);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawMulti::execute(RecordingState &state) const
|
||||
{
|
||||
DrawMultiBuf::DrawCommandBuf &indirect_buf = multi_draw_buf->command_buf_;
|
||||
DrawMultiBuf::DrawGroupBuf &groups = multi_draw_buf->group_buf_;
|
||||
|
||||
uint group_index = this->group_first;
|
||||
while (group_index != uint(-1)) {
|
||||
const DrawGroup &group = groups[group_index];
|
||||
|
||||
if (group.vertex_len > 0) {
|
||||
gpu::Batch *batch = group.desc.gpu_batch;
|
||||
|
||||
if (GPUPrimType(group.desc.expand_prim_type) != GPU_PRIM_NONE) {
|
||||
/* Bind original batch as resource and use a procedural batch to issue the draw-call. */
|
||||
GPU_batch_bind_as_resources(
|
||||
group.desc.gpu_batch, state.shader, state.specialization_constants_get());
|
||||
batch = procedural_batch_get(GPUPrimType(group.desc.expand_prim_type));
|
||||
}
|
||||
|
||||
GPU_batch_set_shader(batch, state.shader, state.specialization_constants_get());
|
||||
|
||||
constexpr intptr_t stride = sizeof(DrawCommand);
|
||||
/* We have 2 indirect command reserved per draw group. */
|
||||
intptr_t offset = stride * group_index * 2;
|
||||
|
||||
/* Draw negatively scaled geometry first. */
|
||||
if (group.len - group.front_facing_len > 0) {
|
||||
state.front_facing_set(true);
|
||||
GPU_batch_draw_indirect(batch, indirect_buf, offset);
|
||||
}
|
||||
|
||||
if (group.front_facing_len > 0) {
|
||||
state.front_facing_set(false);
|
||||
GPU_batch_draw_indirect(batch, indirect_buf, offset + stride);
|
||||
}
|
||||
}
|
||||
|
||||
group_index = group.next;
|
||||
}
|
||||
}
|
||||
|
||||
void DrawIndirect::execute(RecordingState &state) const
|
||||
{
|
||||
state.front_facing_set(res_id.has_inverted_handedness());
|
||||
|
||||
GPU_batch_draw_indirect(batch, *indirect_buf, 0);
|
||||
}
|
||||
|
||||
void Dispatch::execute(RecordingState &state) const
|
||||
{
|
||||
if (is_reference) {
|
||||
GPU_compute_dispatch(
|
||||
state.shader, size_ref->x, size_ref->y, size_ref->z, state.specialization_constants_get());
|
||||
}
|
||||
else {
|
||||
GPU_compute_dispatch(
|
||||
state.shader, size.x, size.y, size.z, state.specialization_constants_get());
|
||||
}
|
||||
}
|
||||
|
||||
void DispatchIndirect::execute(RecordingState &state) const
|
||||
{
|
||||
GPU_compute_dispatch_indirect(state.shader, *indirect_buf, state.specialization_constants_get());
|
||||
}
|
||||
|
||||
void Barrier::execute() const
|
||||
{
|
||||
GPU_memory_barrier(type);
|
||||
}
|
||||
|
||||
void Clear::execute() const
|
||||
{
|
||||
gpu::FrameBuffer *fb = GPU_framebuffer_active_get();
|
||||
GPU_framebuffer_clear(fb, GPUFrameBufferBits(clear_channels), double4(color), depth, stencil);
|
||||
}
|
||||
|
||||
void ClearMulti::execute() const
|
||||
{
|
||||
gpu::FrameBuffer *fb = GPU_framebuffer_active_get();
|
||||
GPU_framebuffer_multi_clear(fb, Span<double4>(colors, colors_len));
|
||||
}
|
||||
|
||||
void StateSet::execute(RecordingState &recording_state) const
|
||||
{
|
||||
bool state_changed = assign_if_different(recording_state.pipeline_state, new_state);
|
||||
bool clip_changed = assign_if_different(recording_state.clip_plane_count, clip_plane_count);
|
||||
|
||||
if (!state_changed && !clip_changed) {
|
||||
return;
|
||||
}
|
||||
|
||||
GPU_state_set(to_write_mask(new_state),
|
||||
to_blend(new_state),
|
||||
to_face_cull_test(new_state),
|
||||
to_depth_test(new_state),
|
||||
to_stencil_test(new_state),
|
||||
to_stencil_op(new_state),
|
||||
to_provoking_vertex(new_state));
|
||||
|
||||
if (new_state & DRW_STATE_CLIP_CONTROL_UNIT_RANGE) {
|
||||
GPU_clip_control_unit_range(true);
|
||||
}
|
||||
else {
|
||||
GPU_clip_control_unit_range(false);
|
||||
}
|
||||
|
||||
/* TODO: this should be part of shader state. */
|
||||
GPU_clip_distances(recording_state.clip_plane_count);
|
||||
|
||||
if (new_state & DRW_STATE_PROGRAM_POINT_SIZE) {
|
||||
GPU_program_point_size(true);
|
||||
}
|
||||
else {
|
||||
GPU_program_point_size(false);
|
||||
}
|
||||
}
|
||||
|
||||
void StateSet::set(DRWState state)
|
||||
{
|
||||
RecordingState recording_state;
|
||||
StateSet{state, 0}.execute(recording_state);
|
||||
|
||||
/* This function is used for cleaning the state for the viewport drawing.
|
||||
* Make sure to reset textures resources to avoid feedback loop when rendering (see #131652). */
|
||||
GPU_texture_unbind_all();
|
||||
GPU_texture_image_unbind_all();
|
||||
GPU_uniformbuf_debug_unbind_all();
|
||||
GPU_storagebuf_debug_unbind_all();
|
||||
|
||||
/* Remained of legacy draw manager. Kept it to avoid regression, but might become unneeded. */
|
||||
GPU_point_size(5);
|
||||
GPU_line_smooth(false);
|
||||
GPU_line_width(0.0f);
|
||||
}
|
||||
|
||||
void StencilSet::execute() const
|
||||
{
|
||||
GPU_stencil_write_mask_set(write_mask);
|
||||
GPU_stencil_compare_mask_set(compare_mask);
|
||||
GPU_stencil_reference_set(reference);
|
||||
}
|
||||
|
||||
void TextureCopy::execute() const
|
||||
{
|
||||
gpu::Texture *exec_src = src_is_ref ? *src_ref : src;
|
||||
gpu::Texture *exec_dst = dst_is_ref ? *dst_ref : dst;
|
||||
|
||||
if (!GPU_texture_is_view(exec_src) && !GPU_texture_is_view(exec_dst)) {
|
||||
GPU_texture_copy(exec_dst, exec_src);
|
||||
return;
|
||||
}
|
||||
|
||||
/* WORKAROUND: There are some issues with copies involving texture views.
|
||||
* Needed for the EEVEE Prepass depth copy. */
|
||||
/* TODO(@pragma37): This path should be removed in 5.3 once #158607 lands. */
|
||||
BLI_assert(GPU_texture_has_depth_format(exec_src));
|
||||
gpu::FrameBuffer *src_fb = GPU_framebuffer_create("TextureCopy-tmp-src");
|
||||
gpu::FrameBuffer *dst_fb = GPU_framebuffer_create("TextureCopy-tmp-dst");
|
||||
GPU_framebuffer_ensure_config(&src_fb, {GPU_ATTACHMENT_TEXTURE(exec_src)});
|
||||
GPU_framebuffer_ensure_config(&dst_fb, {GPU_ATTACHMENT_TEXTURE(exec_dst)});
|
||||
GPU_framebuffer_blit(src_fb, 0, dst_fb, 0, GPUFrameBufferBits::GPU_DEPTH_BIT);
|
||||
GPU_framebuffer_free(src_fb);
|
||||
GPU_framebuffer_free(dst_fb);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Commands Serialization for debugging
|
||||
* \{ */
|
||||
|
||||
std::string ShaderBind::serialize() const
|
||||
{
|
||||
return std::string(".shader_bind(") + GPU_shader_get_name(shader) + ")";
|
||||
}
|
||||
|
||||
std::string FramebufferBind::serialize() const
|
||||
{
|
||||
return std::string(".framebuffer_bind(") +
|
||||
(*framebuffer == nullptr ? "nullptr" : GPU_framebuffer_get_name(*framebuffer)) + ")";
|
||||
}
|
||||
|
||||
std::string SubPassTransition::serialize() const
|
||||
{
|
||||
auto to_str = [](GPUAttachmentState state) {
|
||||
return (state != GPU_ATTACHMENT_IGNORE) ?
|
||||
((state == GPU_ATTACHMENT_WRITE) ? "write" : "read") :
|
||||
"ignore";
|
||||
};
|
||||
|
||||
return std::string(".subpass_transition(\n") +
|
||||
"depth=" + to_str(GPUAttachmentState(depth_state)) + ",\n" +
|
||||
"color0=" + to_str(GPUAttachmentState(color_states[0])) + ",\n" +
|
||||
"color1=" + to_str(GPUAttachmentState(color_states[1])) + ",\n" +
|
||||
"color2=" + to_str(GPUAttachmentState(color_states[2])) + ",\n" +
|
||||
"color3=" + to_str(GPUAttachmentState(color_states[3])) + ",\n" +
|
||||
"color4=" + to_str(GPUAttachmentState(color_states[4])) + ",\n" +
|
||||
"color5=" + to_str(GPUAttachmentState(color_states[5])) + ",\n" +
|
||||
"color6=" + to_str(GPUAttachmentState(color_states[6])) + ",\n" +
|
||||
"color7=" + to_str(GPUAttachmentState(color_states[7])) + "\n)";
|
||||
}
|
||||
|
||||
std::string ResourceBind::serialize() const
|
||||
{
|
||||
switch (type) {
|
||||
case Type::Sampler:
|
||||
return std::string(".bind_texture") + (is_reference ? "_ref" : "") + "(" +
|
||||
std::to_string(slot) + ", sampler=" + sampler.to_string() + ")";
|
||||
case Type::BufferSampler:
|
||||
return std::string(".bind_vertbuf_as_texture") + (is_reference ? "_ref" : "") + "(" +
|
||||
std::to_string(slot) + ")";
|
||||
case Type::Image:
|
||||
return std::string(".bind_image") + (is_reference ? "_ref" : "") + "(" +
|
||||
std::to_string(slot) + ")";
|
||||
case Type::UniformBuf:
|
||||
return std::string(".bind_uniform_buf") + (is_reference ? "_ref" : "") + "(" +
|
||||
std::to_string(slot) + ")";
|
||||
case Type::StorageBuf:
|
||||
return std::string(".bind_storage_buf") + (is_reference ? "_ref" : "") + "(" +
|
||||
std::to_string(slot) + ")";
|
||||
case Type::UniformAsStorageBuf:
|
||||
return std::string(".bind_uniform_as_ssbo") + (is_reference ? "_ref" : "") + "(" +
|
||||
std::to_string(slot) + ")";
|
||||
case Type::VertexAsStorageBuf:
|
||||
return std::string(".bind_vertbuf_as_ssbo") + (is_reference ? "_ref" : "") + "(" +
|
||||
std::to_string(slot) + ")";
|
||||
case Type::IndexAsStorageBuf:
|
||||
return std::string(".bind_indexbuf_as_ssbo") + (is_reference ? "_ref" : "") + "(" +
|
||||
std::to_string(slot) + ")";
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
std::string PushConstant::serialize() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
for (int i = 0; i < array_len; i++) {
|
||||
switch (comp_len) {
|
||||
case 1:
|
||||
switch (type) {
|
||||
case Type::IntValue:
|
||||
ss << int1_value;
|
||||
break;
|
||||
case Type::IntReference:
|
||||
ss << int_ref[i];
|
||||
break;
|
||||
case Type::FloatValue:
|
||||
ss << float1_value;
|
||||
break;
|
||||
case Type::FloatReference:
|
||||
ss << float_ref[i];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
switch (type) {
|
||||
case Type::IntValue:
|
||||
ss << int2_value;
|
||||
break;
|
||||
case Type::IntReference:
|
||||
ss << int2_ref[i];
|
||||
break;
|
||||
case Type::FloatValue:
|
||||
ss << float2_value;
|
||||
break;
|
||||
case Type::FloatReference:
|
||||
ss << float2_ref[i];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
switch (type) {
|
||||
case Type::IntValue:
|
||||
ss << int3_value;
|
||||
break;
|
||||
case Type::IntReference:
|
||||
ss << int3_ref[i];
|
||||
break;
|
||||
case Type::FloatValue:
|
||||
ss << float3_value;
|
||||
break;
|
||||
case Type::FloatReference:
|
||||
ss << float3_ref[i];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
switch (type) {
|
||||
case Type::IntValue:
|
||||
ss << int4_value;
|
||||
break;
|
||||
case Type::IntReference:
|
||||
ss << int4_ref[i];
|
||||
break;
|
||||
case Type::FloatValue:
|
||||
ss << float4_value;
|
||||
break;
|
||||
case Type::FloatReference:
|
||||
ss << float4_ref[i];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 16:
|
||||
switch (type) {
|
||||
case Type::IntValue:
|
||||
case Type::IntReference:
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
case Type::FloatValue:
|
||||
ss << float4x4(
|
||||
(&float4_value)[0], (&float4_value)[1], (&float4_value)[2], (&float4_value)[3]);
|
||||
break;
|
||||
case Type::FloatReference:
|
||||
ss << *float4x4_ref;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (i < array_len - 1) {
|
||||
ss << ", ";
|
||||
}
|
||||
}
|
||||
|
||||
return std::string(".push_constant(") + std::to_string(location) + ", data=" + ss.str() + ")";
|
||||
}
|
||||
|
||||
std::string SpecializeConstant::serialize() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
switch (type) {
|
||||
case Type::IntValue:
|
||||
ss << int_value;
|
||||
break;
|
||||
case Type::UintValue:
|
||||
ss << uint_value;
|
||||
break;
|
||||
case Type::FloatValue:
|
||||
ss << float_value;
|
||||
break;
|
||||
case Type::BoolValue:
|
||||
ss << bool_value;
|
||||
break;
|
||||
case Type::IntReference:
|
||||
ss << *int_ref;
|
||||
break;
|
||||
case Type::UintReference:
|
||||
ss << *uint_ref;
|
||||
break;
|
||||
case Type::FloatReference:
|
||||
ss << *float_ref;
|
||||
break;
|
||||
case Type::BoolReference:
|
||||
ss << *bool_ref;
|
||||
break;
|
||||
}
|
||||
return std::string(".specialize_constant(") + std::to_string(location) + ", data=" + ss.str() +
|
||||
")";
|
||||
}
|
||||
|
||||
std::string Draw::serialize() const
|
||||
{
|
||||
std::string inst_len = std::to_string(instance_len);
|
||||
std::string vert_len = (vertex_len == uint(-1)) ? "from_batch" : std::to_string(vertex_len);
|
||||
std::string vert_first = (vertex_first == uint(-1)) ? "from_batch" :
|
||||
std::to_string(vertex_first);
|
||||
return std::string(".draw(inst_len=") + inst_len + ", vert_len=" + vert_len +
|
||||
", vert_first=" + vert_first + ", res_id=" + std::to_string(res_id.index()) + ")";
|
||||
}
|
||||
|
||||
std::string DrawMulti::serialize(const std::string &line_prefix) const
|
||||
{
|
||||
DrawMultiBuf::DrawGroupBuf &groups = multi_draw_buf->group_buf_;
|
||||
|
||||
MutableSpan<DrawPrototype> prototypes(multi_draw_buf->prototype_buf_.data(),
|
||||
multi_draw_buf->prototype_count_);
|
||||
|
||||
/* This emulates the GPU sorting but without the unstable draw order. */
|
||||
std::ranges::sort(prototypes, [](const DrawPrototype &a, const DrawPrototype &b) {
|
||||
return (a.group_id < b.group_id) || (a.group_id == b.group_id && a.res_id > b.res_id);
|
||||
});
|
||||
|
||||
/* Compute prefix sum to have correct offsets. */
|
||||
uint prefix_sum = 0u;
|
||||
for (DrawGroup &group : groups) {
|
||||
group.start = prefix_sum;
|
||||
prefix_sum += group.front_facing_counter + group.back_facing_counter;
|
||||
}
|
||||
|
||||
std::stringstream ss;
|
||||
|
||||
uint group_len = 0;
|
||||
uint group_index = this->group_first;
|
||||
while (group_index != uint(-1)) {
|
||||
const DrawGroup &grp = groups[group_index];
|
||||
|
||||
ss << std::endl << line_prefix << " .group(id=" << group_index << ", len=" << grp.len << ")";
|
||||
|
||||
intptr_t offset = grp.start;
|
||||
|
||||
if (grp.back_facing_counter > 0) {
|
||||
for (DrawPrototype &proto : prototypes.slice_safe({offset, grp.back_facing_counter})) {
|
||||
BLI_assert(proto.group_id == group_index);
|
||||
ResourceID res_id(proto.res_id);
|
||||
BLI_assert(res_id.has_inverted_handedness());
|
||||
ss << std::endl
|
||||
<< line_prefix << " .proto(instance_len=" << std::to_string(proto.instance_len)
|
||||
<< ", resource_id=" << std::to_string(res_id.index()) << ", back_face)";
|
||||
}
|
||||
offset += grp.back_facing_counter;
|
||||
}
|
||||
|
||||
if (grp.front_facing_counter > 0) {
|
||||
for (DrawPrototype &proto : prototypes.slice_safe({offset, grp.front_facing_counter})) {
|
||||
BLI_assert(proto.group_id == group_index);
|
||||
ResourceID res_id(proto.res_id);
|
||||
BLI_assert(!res_id.has_inverted_handedness());
|
||||
ss << std::endl
|
||||
<< line_prefix << " .proto(instance_len=" << std::to_string(proto.instance_len)
|
||||
<< ", resource_id=" << std::to_string(res_id.index()) << ", front_face)";
|
||||
}
|
||||
}
|
||||
|
||||
group_index = grp.next;
|
||||
group_len++;
|
||||
}
|
||||
|
||||
ss << std::endl;
|
||||
|
||||
return line_prefix + ".draw_multi(" + std::to_string(group_len) + ")" + ss.str();
|
||||
}
|
||||
|
||||
std::string DrawIndirect::serialize() const
|
||||
{
|
||||
return std::string(".draw_indirect()");
|
||||
}
|
||||
|
||||
std::string Dispatch::serialize() const
|
||||
{
|
||||
int3 sz = is_reference ? *size_ref : size;
|
||||
return std::string(".dispatch") + (is_reference ? "_ref" : "") + "(" + std::to_string(sz.x) +
|
||||
", " + std::to_string(sz.y) + ", " + std::to_string(sz.z) + ")";
|
||||
}
|
||||
|
||||
std::string DispatchIndirect::serialize() const
|
||||
{
|
||||
return std::string(".dispatch_indirect()");
|
||||
}
|
||||
|
||||
std::string Barrier::serialize() const
|
||||
{
|
||||
/* TODO(@fclem): Better serialization... */
|
||||
return std::string(".barrier(") + std::to_string(type) + ")";
|
||||
}
|
||||
|
||||
std::string Clear::serialize() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
if (GPUFrameBufferBits(clear_channels) & GPU_COLOR_BIT) {
|
||||
ss << "color=" << color;
|
||||
if (GPUFrameBufferBits(clear_channels) & (GPU_DEPTH_BIT | GPU_STENCIL_BIT)) {
|
||||
ss << ", ";
|
||||
}
|
||||
}
|
||||
if (GPUFrameBufferBits(clear_channels) & GPU_DEPTH_BIT) {
|
||||
ss << "depth=" << depth;
|
||||
if (GPUFrameBufferBits(clear_channels) & GPU_STENCIL_BIT) {
|
||||
ss << ", ";
|
||||
}
|
||||
}
|
||||
if (GPUFrameBufferBits(clear_channels) & GPU_STENCIL_BIT) {
|
||||
ss << "stencil=0b" << std::bitset<8>(stencil) << ")";
|
||||
}
|
||||
return std::string(".clear(") + ss.str() + ")";
|
||||
}
|
||||
|
||||
std::string ClearMulti::serialize() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
for (double4 color : Span<double4>(colors, colors_len)) {
|
||||
ss << color << ", ";
|
||||
}
|
||||
return std::string(".clear_multi(colors={") + ss.str() + "})";
|
||||
}
|
||||
|
||||
std::string StateSet::serialize() const
|
||||
{
|
||||
/* TODO(@fclem): Better serialization... */
|
||||
return std::string(".state_set(") + std::to_string(new_state) + ")";
|
||||
}
|
||||
|
||||
std::string StencilSet::serialize() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << ".stencil_set(write_mask=0b" << std::bitset<8>(write_mask) << ", reference=0b"
|
||||
<< std::bitset<8>(reference) << ", compare_mask=0b" << std::bitset<8>(compare_mask) << ")";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string TextureCopy::serialize() const
|
||||
{
|
||||
return ".texture_copy()";
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Commands buffers binding / command / resource ID generation
|
||||
* \{ */
|
||||
|
||||
void DrawCommandBuf::finalize_commands(Vector<Header, 0> &headers,
|
||||
Vector<Undetermined, 0> &commands,
|
||||
SubPassVector &sub_passes,
|
||||
uint &resource_id_count,
|
||||
ResourceIdBuf &resource_id_buf)
|
||||
{
|
||||
for (const Header &header : headers) {
|
||||
if (header.type == Type::SubPass) {
|
||||
/** WARNING: Recursive. */
|
||||
auto &sub = sub_passes[int64_t(header.index)];
|
||||
finalize_commands(
|
||||
sub.headers_, sub.commands_, sub_passes, resource_id_count, resource_id_buf);
|
||||
}
|
||||
|
||||
if (header.type != Type::Draw) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Draw &cmd = commands[header.index].draw;
|
||||
|
||||
int batch_vert_len, batch_vert_first, batch_base_index, batch_inst_len;
|
||||
/* Now that GPUBatches are guaranteed to be finished, extract their parameters. */
|
||||
GPU_batch_draw_parameter_get(
|
||||
cmd.batch, &batch_vert_len, &batch_vert_first, &batch_base_index, &batch_inst_len);
|
||||
/* Instancing attributes are not supported using the new pipeline since we use the base
|
||||
* instance to set the correct resource_id. Workaround is a storage_buf + gl_InstanceID. */
|
||||
BLI_assert(batch_inst_len == 1);
|
||||
|
||||
if (cmd.vertex_len == uint(-1)) {
|
||||
cmd.vertex_len = batch_vert_len;
|
||||
}
|
||||
|
||||
/* NOTE: Only do this if a handle is present. If a draw-call is using instancing with null
|
||||
* handle, the shader should not rely on `resource_id` at ***all***. This allows procedural
|
||||
* instanced draw-calls with lots of instances with no overhead. */
|
||||
/* TODO(fclem): Think about either fixing this feature or removing support for instancing all
|
||||
* together. */
|
||||
if (cmd.res_id.raw > 0) {
|
||||
/* Save correct offset to start of resource_id buffer region for this draw. */
|
||||
uint instance_first = resource_id_count;
|
||||
resource_id_count += cmd.instance_len;
|
||||
/* Ensure the buffer is big enough. */
|
||||
resource_id_buf.get_or_resize(resource_id_count - 1);
|
||||
|
||||
/* Copy the resource id for all instances. */
|
||||
uint index = cmd.res_id.index();
|
||||
for (int i = instance_first; i < (instance_first + cmd.instance_len); i++) {
|
||||
resource_id_buf[i] = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawCommandBuf::generate_commands(Vector<Header, 0> &headers,
|
||||
Vector<Undetermined, 0> &commands,
|
||||
SubPassVector &sub_passes)
|
||||
{
|
||||
/* First instance ID contains the null handle with identity transform.
|
||||
* This is referenced for draw-calls with no handle. */
|
||||
resource_id_buf_.get_or_resize(0) = 0;
|
||||
resource_id_count_ = 1;
|
||||
finalize_commands(headers, commands, sub_passes, resource_id_count_, resource_id_buf_);
|
||||
resource_id_buf_.push_update();
|
||||
}
|
||||
|
||||
void DrawCommandBuf::bind(RecordingState & /*state*/)
|
||||
{
|
||||
GPU_storagebuf_bind(resource_id_buf_, DRW_RESOURCE_ID_SLOT);
|
||||
}
|
||||
|
||||
void DrawMultiBuf::generate_commands(Vector<Header, 0> & /*headers*/,
|
||||
Vector<Undetermined, 0> & /*commands*/,
|
||||
VisibilityBuf &visibility_buf,
|
||||
int visibility_word_per_draw,
|
||||
int view_len,
|
||||
bool use_custom_ids)
|
||||
{
|
||||
GPU_debug_group_begin("DrawMultiBuf.bind");
|
||||
|
||||
resource_id_count_ = 0u;
|
||||
for (DrawGroup &group : MutableSpan<DrawGroup>(group_buf_.data(), group_count_)) {
|
||||
/* Compute prefix sum of all instance of previous group. */
|
||||
group.start = resource_id_count_;
|
||||
resource_id_count_ += group.len;
|
||||
|
||||
int batch_vert_len, batch_vert_first, batch_base_index, batch_inst_len;
|
||||
/* Now that GPUBatches are guaranteed to be finished, extract their parameters. */
|
||||
GPU_batch_draw_parameter_get(group.desc.gpu_batch,
|
||||
&batch_vert_len,
|
||||
&batch_vert_first,
|
||||
&batch_base_index,
|
||||
&batch_inst_len);
|
||||
|
||||
group.vertex_len = group.desc.vertex_len == 0 ? batch_vert_len : group.desc.vertex_len;
|
||||
group.vertex_first = group.desc.vertex_first == -1 ? batch_vert_first :
|
||||
group.desc.vertex_first;
|
||||
group.base_index = batch_base_index;
|
||||
/* Instancing attributes are not supported using the new pipeline since we use the base
|
||||
* instance to set the correct resource_id. Workaround is a storage_buf + gl_InstanceID. */
|
||||
BLI_assert(batch_inst_len == 1);
|
||||
UNUSED_VARS_NDEBUG(batch_inst_len);
|
||||
|
||||
if (group.desc.expand_prim_type != GPU_PRIM_NONE) {
|
||||
/* Expanded draw-call. */
|
||||
IndexRange vert_range = GPU_batch_draw_expanded_parameter_get(
|
||||
group.desc.gpu_batch->prim_type,
|
||||
GPUPrimType(group.desc.expand_prim_type),
|
||||
group.vertex_len,
|
||||
group.vertex_first,
|
||||
group.desc.expand_prim_len);
|
||||
|
||||
group.vertex_first = vert_range.start();
|
||||
group.vertex_len = vert_range.size();
|
||||
/* Override base index to -1 as the generated draw-call will not use an index buffer and do
|
||||
* the indirection manually inside the shader. */
|
||||
group.base_index = -1;
|
||||
}
|
||||
|
||||
/* Reset counters to 0 for the GPU. */
|
||||
group.total_counter = group.front_facing_counter = group.back_facing_counter = 0;
|
||||
}
|
||||
|
||||
group_buf_.push_update();
|
||||
prototype_buf_.push_update();
|
||||
/* Allocate enough for the expansion pass. */
|
||||
resource_id_buf_.get_or_resize(resource_id_count_ * view_len * (use_custom_ids ? 2 : 1));
|
||||
/* Two commands per group (inverted and non-inverted scale). */
|
||||
command_buf_.get_or_resize(group_count_ * 2);
|
||||
|
||||
if (prototype_count_ > 0) {
|
||||
gpu::Shader *shader = DRW_shader_draw_command_generate_get();
|
||||
GPU_shader_bind(shader);
|
||||
GPU_shader_uniform_1i(shader, "prototype_len", prototype_count_);
|
||||
GPU_shader_uniform_1i(shader, "visibility_word_per_draw", visibility_word_per_draw);
|
||||
GPU_shader_uniform_1i(shader, "view_len", view_len);
|
||||
GPU_shader_uniform_1i(shader, "view_shift", log2_ceil_u(view_len));
|
||||
GPU_shader_uniform_1b(shader, "use_custom_ids", use_custom_ids);
|
||||
GPU_storagebuf_bind(group_buf_, GPU_shader_get_ssbo_binding(shader, "group_buf"));
|
||||
GPU_storagebuf_bind(visibility_buf, GPU_shader_get_ssbo_binding(shader, "visibility_buf"));
|
||||
GPU_storagebuf_bind(prototype_buf_, GPU_shader_get_ssbo_binding(shader, "prototype_buf"));
|
||||
GPU_storagebuf_bind(command_buf_, GPU_shader_get_ssbo_binding(shader, "command_buf"));
|
||||
GPU_storagebuf_bind(resource_id_buf_, DRW_RESOURCE_ID_SLOT);
|
||||
GPU_compute_dispatch(shader, divide_ceil_u(prototype_count_, DRW_COMMAND_GROUP_SIZE), 1, 1);
|
||||
/* TODO(@fclem): Investigate moving the barrier in the bind function. */
|
||||
GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE);
|
||||
GPU_storagebuf_sync_as_indirect_buffer(command_buf_);
|
||||
}
|
||||
|
||||
GPU_debug_group_end();
|
||||
}
|
||||
|
||||
void DrawMultiBuf::bind(RecordingState & /*state*/)
|
||||
{
|
||||
GPU_storagebuf_bind(resource_id_buf_, DRW_RESOURCE_ID_SLOT);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
}; // namespace blender::draw::command
|
||||
783
blender-5.2.0/source/blender/draw/intern/draw_command.hh
Normal file
783
blender-5.2.0/source/blender/draw/intern/draw_command.hh
Normal file
@@ -0,0 +1,783 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*
|
||||
* Commands stored inside draw passes. Converted into GPU commands upon pass submission.
|
||||
*
|
||||
* Draw calls (primitive rendering commands) are managed by either `DrawCommandBuf` or
|
||||
* `DrawMultiBuf`. See implementation details at their definition.
|
||||
*/
|
||||
|
||||
#include "BKE_global.hh"
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_math_base.h"
|
||||
#include "DRW_gpu_wrapper.hh"
|
||||
|
||||
#include "GPU_index_buffer.hh"
|
||||
#include "draw_command_shared.hh"
|
||||
#include "draw_handle.hh"
|
||||
#include "draw_state.hh"
|
||||
#include "draw_view.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* Forward declarations. */
|
||||
namespace draw::detail {
|
||||
template<typename T, int64_t block_size> class SubPassVector;
|
||||
template<typename DrawCommandBufType> class PassBase;
|
||||
} // namespace draw::detail
|
||||
|
||||
namespace draw::command {
|
||||
|
||||
class DrawCommandBuf;
|
||||
class DrawMultiBuf;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Recording State
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Command recording state.
|
||||
* Keep track of several states and avoid redundant state changes.
|
||||
*/
|
||||
struct RecordingState {
|
||||
gpu::shader::SpecializationConstants specialization_constants;
|
||||
/* True if specialization_constants was set. */
|
||||
bool specialization_constants_in_use = false;
|
||||
/* True if the bound shader uses specialization. */
|
||||
bool shader_use_specialization = false;
|
||||
gpu::Shader *shader = nullptr;
|
||||
bool front_facing = true;
|
||||
bool inverted_view = false;
|
||||
DRWState pipeline_state = DRW_STATE_NO_DRAW;
|
||||
int clip_plane_count = 0;
|
||||
/** Used for pass simple resource ID. Starts at 1 as 0 is the identity handle. */
|
||||
int instance_offset = 1;
|
||||
|
||||
void front_facing_set(bool facing)
|
||||
{
|
||||
/* Facing is inverted if view is not in expected handedness. */
|
||||
facing = this->inverted_view == facing;
|
||||
/* Remove redundant changes. */
|
||||
if (assign_if_different(this->front_facing, facing)) {
|
||||
GPU_front_facing(!facing);
|
||||
}
|
||||
}
|
||||
|
||||
void cleanup()
|
||||
{
|
||||
if (front_facing == false) {
|
||||
GPU_front_facing(false);
|
||||
}
|
||||
|
||||
if (G.debug & G_DEBUG_GPU) {
|
||||
GPU_storagebuf_debug_unbind_all();
|
||||
GPU_texture_image_unbind_all();
|
||||
GPU_texture_unbind_all();
|
||||
GPU_uniformbuf_debug_unbind_all();
|
||||
}
|
||||
}
|
||||
|
||||
const gpu::shader::SpecializationConstants *specialization_constants_get()
|
||||
{
|
||||
return shader_use_specialization ? &specialization_constants : nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Regular Commands
|
||||
* \{ */
|
||||
|
||||
enum class Type : uint8_t {
|
||||
/**
|
||||
* None Type commands are either uninitialized or are repurposed as data storage.
|
||||
* They are skipped during submission.
|
||||
*/
|
||||
None = 0,
|
||||
|
||||
/** Commands stored as Undetermined in regular command buffer. */
|
||||
Barrier,
|
||||
Clear,
|
||||
ClearMulti,
|
||||
Dispatch,
|
||||
DispatchIndirect,
|
||||
Draw,
|
||||
DrawIndirect,
|
||||
FramebufferBind,
|
||||
PushConstant,
|
||||
SpecializeConstant,
|
||||
ResourceBind,
|
||||
ShaderBind,
|
||||
SubPassTransition,
|
||||
StateSet,
|
||||
StencilSet,
|
||||
TextureCopy,
|
||||
|
||||
/** Special commands stored in separate buffers. */
|
||||
SubPass,
|
||||
DrawMulti,
|
||||
};
|
||||
|
||||
/**
|
||||
* The index of the group is implicit since it is known by the one who want to
|
||||
* access it. This also allows to have an indexed object to split the command
|
||||
* stream.
|
||||
*/
|
||||
struct Header {
|
||||
/** Command type. */
|
||||
Type type;
|
||||
/** Command index in command heap of this type. */
|
||||
uint index;
|
||||
};
|
||||
|
||||
struct ShaderBind {
|
||||
gpu::Shader *shader;
|
||||
|
||||
void execute(RecordingState &state) const;
|
||||
std::string serialize() const;
|
||||
};
|
||||
|
||||
struct FramebufferBind {
|
||||
gpu::FrameBuffer **framebuffer;
|
||||
|
||||
void execute() const;
|
||||
std::string serialize() const;
|
||||
};
|
||||
|
||||
struct SubPassTransition {
|
||||
/** \note uint8_t storing `GPUAttachmentState` for compactness. */
|
||||
uint8_t depth_state;
|
||||
/** \note 8 is GPU_FB_MAX_COLOR_ATTACHMENT. */
|
||||
uint8_t color_states[8];
|
||||
|
||||
void execute() const;
|
||||
std::string serialize() const;
|
||||
};
|
||||
|
||||
struct ResourceBind {
|
||||
GPUSamplerState sampler;
|
||||
int slot;
|
||||
bool is_reference;
|
||||
|
||||
enum class Type : uint8_t {
|
||||
Sampler = 0,
|
||||
BufferSampler,
|
||||
Image,
|
||||
UniformBuf,
|
||||
StorageBuf,
|
||||
UniformAsStorageBuf,
|
||||
VertexAsStorageBuf,
|
||||
IndexAsStorageBuf,
|
||||
} type;
|
||||
|
||||
union {
|
||||
/** TODO: Use draw::Texture|StorageBuffer|UniformBuffer as resources as they will give more
|
||||
* debug info. */
|
||||
gpu::UniformBuf *uniform_buf;
|
||||
gpu::UniformBuf **uniform_buf_ref;
|
||||
gpu::StorageBuf *storage_buf;
|
||||
gpu::StorageBuf **storage_buf_ref;
|
||||
/** NOTE: Texture is used for both Sampler and Image binds. */
|
||||
gpu::Texture *texture;
|
||||
gpu::Texture **texture_ref;
|
||||
gpu::VertBuf *vertex_buf;
|
||||
gpu::VertBuf **vertex_buf_ref;
|
||||
gpu::IndexBuf *index_buf;
|
||||
gpu::IndexBuf **index_buf_ref;
|
||||
};
|
||||
|
||||
ResourceBind() = default;
|
||||
|
||||
ResourceBind(int slot_, gpu::UniformBuf *res)
|
||||
: slot(slot_), is_reference(false), type(Type::UniformBuf), uniform_buf(res) {};
|
||||
ResourceBind(int slot_, gpu::UniformBuf **res)
|
||||
: slot(slot_), is_reference(true), type(Type::UniformBuf), uniform_buf_ref(res) {};
|
||||
ResourceBind(int slot_, gpu::StorageBuf *res)
|
||||
: slot(slot_), is_reference(false), type(Type::StorageBuf), storage_buf(res) {};
|
||||
ResourceBind(int slot_, gpu::StorageBuf **res)
|
||||
: slot(slot_), is_reference(true), type(Type::StorageBuf), storage_buf_ref(res) {};
|
||||
ResourceBind(int slot_, gpu::UniformBuf *res, Type /*type*/)
|
||||
: slot(slot_), is_reference(false), type(Type::UniformAsStorageBuf), uniform_buf(res) {};
|
||||
ResourceBind(int slot_, gpu::UniformBuf **res, Type /*type*/)
|
||||
: slot(slot_), is_reference(true), type(Type::UniformAsStorageBuf), uniform_buf_ref(res) {};
|
||||
ResourceBind(int slot_, gpu::VertBuf *res, Type /*type*/)
|
||||
: slot(slot_), is_reference(false), type(Type::VertexAsStorageBuf), vertex_buf(res) {};
|
||||
ResourceBind(int slot_, gpu::VertBuf **res, Type /*type*/)
|
||||
: slot(slot_), is_reference(true), type(Type::VertexAsStorageBuf), vertex_buf_ref(res) {};
|
||||
ResourceBind(int slot_, gpu::IndexBuf *res, Type /*type*/)
|
||||
: slot(slot_), is_reference(false), type(Type::IndexAsStorageBuf), index_buf(res) {};
|
||||
ResourceBind(int slot_, gpu::IndexBuf **res, Type /*type*/)
|
||||
: slot(slot_), is_reference(true), type(Type::IndexAsStorageBuf), index_buf_ref(res) {};
|
||||
ResourceBind(int slot_, draw::Image *res)
|
||||
: slot(slot_), is_reference(false), type(Type::Image), texture(draw::as_texture(res)) {};
|
||||
ResourceBind(int slot_, draw::Image **res)
|
||||
: slot(slot_), is_reference(true), type(Type::Image), texture_ref(draw::as_texture(res)) {};
|
||||
ResourceBind(int slot_, gpu::Texture *res, GPUSamplerState state)
|
||||
: sampler(state), slot(slot_), is_reference(false), type(Type::Sampler), texture(res) {};
|
||||
ResourceBind(int slot_, gpu::Texture **res, GPUSamplerState state)
|
||||
: sampler(state), slot(slot_), is_reference(true), type(Type::Sampler), texture_ref(res) {};
|
||||
ResourceBind(int slot_, gpu::VertBuf *res)
|
||||
: slot(slot_), is_reference(false), type(Type::BufferSampler), vertex_buf(res) {};
|
||||
ResourceBind(int slot_, gpu::VertBuf **res)
|
||||
: slot(slot_), is_reference(true), type(Type::BufferSampler), vertex_buf_ref(res) {};
|
||||
|
||||
void execute() const;
|
||||
std::string serialize() const;
|
||||
};
|
||||
|
||||
struct PushConstant {
|
||||
int location;
|
||||
uint8_t array_len;
|
||||
uint8_t comp_len;
|
||||
enum class Type : uint8_t {
|
||||
IntValue = 0,
|
||||
FloatValue,
|
||||
IntReference,
|
||||
FloatReference,
|
||||
} type;
|
||||
/**
|
||||
* IMPORTANT: Data is at the end of the struct as it can span over the next commands.
|
||||
* These next commands are not real commands but just memory to hold the data and are not
|
||||
* referenced by any Command::Header.
|
||||
* This is a hack to support float4x4 copy.
|
||||
*/
|
||||
union {
|
||||
int int1_value;
|
||||
int2 int2_value;
|
||||
int3 int3_value;
|
||||
int4 int4_value;
|
||||
float float1_value;
|
||||
float2 float2_value;
|
||||
float3 float3_value;
|
||||
float4 float4_value;
|
||||
const int *int_ref;
|
||||
const int2 *int2_ref;
|
||||
const int3 *int3_ref;
|
||||
const int4 *int4_ref;
|
||||
const float *float_ref;
|
||||
const float2 *float2_ref;
|
||||
const float3 *float3_ref;
|
||||
const float4 *float4_ref;
|
||||
const float4x4 *float4x4_ref;
|
||||
};
|
||||
|
||||
PushConstant() = default;
|
||||
|
||||
PushConstant(int loc, const float &val)
|
||||
: location(loc), array_len(1), comp_len(1), type(Type::FloatValue), float1_value(val) {};
|
||||
PushConstant(int loc, const float2 &val)
|
||||
: location(loc), array_len(1), comp_len(2), type(Type::FloatValue), float2_value(val) {};
|
||||
PushConstant(int loc, const float3 &val)
|
||||
: location(loc), array_len(1), comp_len(3), type(Type::FloatValue), float3_value(val) {};
|
||||
PushConstant(int loc, const float4 &val)
|
||||
: location(loc), array_len(1), comp_len(4), type(Type::FloatValue), float4_value(val) {};
|
||||
|
||||
PushConstant(int loc, const int &val)
|
||||
: location(loc), array_len(1), comp_len(1), type(Type::IntValue), int1_value(val) {};
|
||||
PushConstant(int loc, const int2 &val)
|
||||
: location(loc), array_len(1), comp_len(2), type(Type::IntValue), int2_value(val) {};
|
||||
PushConstant(int loc, const int3 &val)
|
||||
: location(loc), array_len(1), comp_len(3), type(Type::IntValue), int3_value(val) {};
|
||||
PushConstant(int loc, const int4 &val)
|
||||
: location(loc), array_len(1), comp_len(4), type(Type::IntValue), int4_value(val) {};
|
||||
|
||||
PushConstant(int loc, const float *val, int arr)
|
||||
: location(loc), array_len(arr), comp_len(1), type(Type::FloatReference), float_ref(val) {};
|
||||
PushConstant(int loc, const float2 *val, int arr)
|
||||
: location(loc), array_len(arr), comp_len(2), type(Type::FloatReference), float2_ref(val) {};
|
||||
PushConstant(int loc, const float3 *val, int arr)
|
||||
: location(loc), array_len(arr), comp_len(3), type(Type::FloatReference), float3_ref(val) {};
|
||||
PushConstant(int loc, const float4 *val, int arr)
|
||||
: location(loc), array_len(arr), comp_len(4), type(Type::FloatReference), float4_ref(val) {};
|
||||
PushConstant(int loc, const float4x4 *val)
|
||||
: location(loc), array_len(1), comp_len(16), type(Type::FloatReference), float4x4_ref(val) {
|
||||
};
|
||||
|
||||
PushConstant(int loc, const int *val, int arr)
|
||||
: location(loc), array_len(arr), comp_len(1), type(Type::IntReference), int_ref(val) {};
|
||||
PushConstant(int loc, const int2 *val, int arr)
|
||||
: location(loc), array_len(arr), comp_len(2), type(Type::IntReference), int2_ref(val) {};
|
||||
PushConstant(int loc, const int3 *val, int arr)
|
||||
: location(loc), array_len(arr), comp_len(3), type(Type::IntReference), int3_ref(val) {};
|
||||
PushConstant(int loc, const int4 *val, int arr)
|
||||
: location(loc), array_len(arr), comp_len(4), type(Type::IntReference), int4_ref(val) {};
|
||||
|
||||
void execute(RecordingState &state) const;
|
||||
std::string serialize() const;
|
||||
};
|
||||
|
||||
struct SpecializeConstant {
|
||||
/* Shader to set the constant in. */
|
||||
gpu::Shader *shader;
|
||||
/* Value of the constant or a reference to it. */
|
||||
union {
|
||||
int int_value;
|
||||
uint uint_value;
|
||||
float float_value;
|
||||
bool bool_value;
|
||||
const int *int_ref;
|
||||
const uint *uint_ref;
|
||||
const float *float_ref;
|
||||
const bool *bool_ref;
|
||||
};
|
||||
|
||||
int location;
|
||||
|
||||
enum class Type : uint8_t {
|
||||
IntValue = 0,
|
||||
UintValue,
|
||||
FloatValue,
|
||||
BoolValue,
|
||||
IntReference,
|
||||
UintReference,
|
||||
FloatReference,
|
||||
BoolReference,
|
||||
} type;
|
||||
|
||||
SpecializeConstant() = default;
|
||||
|
||||
SpecializeConstant(gpu::Shader *sh, int loc, const float &val)
|
||||
: shader(sh), float_value(val), location(loc), type(Type::FloatValue) {};
|
||||
SpecializeConstant(gpu::Shader *sh, int loc, const int &val)
|
||||
: shader(sh), int_value(val), location(loc), type(Type::IntValue) {};
|
||||
SpecializeConstant(gpu::Shader *sh, int loc, const uint &val)
|
||||
: shader(sh), uint_value(val), location(loc), type(Type::UintValue) {};
|
||||
SpecializeConstant(gpu::Shader *sh, int loc, const bool &val)
|
||||
: shader(sh), bool_value(val), location(loc), type(Type::BoolValue) {};
|
||||
SpecializeConstant(gpu::Shader *sh, int loc, const float *val)
|
||||
: shader(sh), float_ref(val), location(loc), type(Type::FloatReference) {};
|
||||
SpecializeConstant(gpu::Shader *sh, int loc, const int *val)
|
||||
: shader(sh), int_ref(val), location(loc), type(Type::IntReference) {};
|
||||
SpecializeConstant(gpu::Shader *sh, int loc, const uint *val)
|
||||
: shader(sh), uint_ref(val), location(loc), type(Type::UintReference) {};
|
||||
SpecializeConstant(gpu::Shader *sh, int loc, const bool *val)
|
||||
: shader(sh), bool_ref(val), location(loc), type(Type::BoolReference) {};
|
||||
|
||||
void execute(RecordingState &state) const;
|
||||
std::string serialize() const;
|
||||
};
|
||||
|
||||
struct Draw {
|
||||
gpu::Batch *batch;
|
||||
uint32_t instance_len : 24;
|
||||
uint32_t expand_prim_type : 4; /* #GPUPrimType */
|
||||
uint32_t expand_prim_len : 4;
|
||||
uint32_t vertex_first;
|
||||
uint32_t vertex_len;
|
||||
ResourceID res_id;
|
||||
|
||||
Draw() = default;
|
||||
|
||||
Draw(gpu::Batch *batch,
|
||||
uint instance_len,
|
||||
uint vertex_len,
|
||||
uint vertex_first,
|
||||
GPUPrimType expanded_prim_type,
|
||||
uint expanded_prim_len,
|
||||
ResourceID res_id)
|
||||
{
|
||||
BLI_assert(batch != nullptr);
|
||||
BLI_assert(expanded_prim_type <= 15);
|
||||
BLI_assert(expanded_prim_len <= 15);
|
||||
this->batch = batch;
|
||||
this->res_id = res_id;
|
||||
this->instance_len = min_uu(instance_len, (1 << 24) - 1);
|
||||
this->vertex_len = vertex_len;
|
||||
this->vertex_first = vertex_first;
|
||||
this->expand_prim_type = expanded_prim_type;
|
||||
this->expand_prim_len = expanded_prim_len;
|
||||
}
|
||||
|
||||
bool is_primitive_expansion() const
|
||||
{
|
||||
return expand_prim_type != GPU_PRIM_NONE;
|
||||
}
|
||||
|
||||
void execute(RecordingState &state) const;
|
||||
std::string serialize() const;
|
||||
};
|
||||
|
||||
struct DrawMulti {
|
||||
gpu::Batch *batch;
|
||||
DrawMultiBuf *multi_draw_buf;
|
||||
uint group_first;
|
||||
uint uuid;
|
||||
|
||||
void execute(RecordingState &state) const;
|
||||
std::string serialize(const std::string &line_prefix) const;
|
||||
};
|
||||
|
||||
struct DrawIndirect {
|
||||
gpu::Batch *batch;
|
||||
gpu::StorageBuf **indirect_buf;
|
||||
ResourceID res_id;
|
||||
|
||||
void execute(RecordingState &state) const;
|
||||
std::string serialize() const;
|
||||
};
|
||||
|
||||
struct Dispatch {
|
||||
bool is_reference;
|
||||
union {
|
||||
int3 size;
|
||||
int3 *size_ref;
|
||||
};
|
||||
|
||||
Dispatch() = default;
|
||||
|
||||
Dispatch(int3 group_len) : is_reference(false), size(group_len) {};
|
||||
Dispatch(int3 *group_len) : is_reference(true), size_ref(group_len) {};
|
||||
|
||||
void execute(RecordingState &state) const;
|
||||
std::string serialize() const;
|
||||
};
|
||||
|
||||
struct DispatchIndirect {
|
||||
gpu::StorageBuf **indirect_buf;
|
||||
|
||||
void execute(RecordingState &state) const;
|
||||
std::string serialize() const;
|
||||
};
|
||||
|
||||
struct Barrier {
|
||||
GPUBarrier type;
|
||||
|
||||
void execute() const;
|
||||
std::string serialize() const;
|
||||
};
|
||||
|
||||
struct Clear {
|
||||
uint8_t clear_channels; /* #GPUFrameBufferBits. But want to save some bits. */
|
||||
uint8_t stencil;
|
||||
float depth;
|
||||
float4 color;
|
||||
|
||||
void execute() const;
|
||||
std::string serialize() const;
|
||||
};
|
||||
|
||||
struct ClearMulti {
|
||||
/** \note This should be a Span<float4> but we need have to only have trivial types here. */
|
||||
const double4 *colors;
|
||||
int colors_len;
|
||||
|
||||
void execute() const;
|
||||
std::string serialize() const;
|
||||
};
|
||||
|
||||
struct StateSet {
|
||||
DRWState new_state;
|
||||
int clip_plane_count;
|
||||
|
||||
void execute(RecordingState &state) const;
|
||||
std::string serialize() const;
|
||||
|
||||
/* Set state of the GPU module manually. */
|
||||
static void set(DRWState state = DRW_STATE_DEFAULT);
|
||||
};
|
||||
|
||||
struct StencilSet {
|
||||
uint write_mask;
|
||||
uint compare_mask;
|
||||
uint reference;
|
||||
|
||||
void execute() const;
|
||||
std::string serialize() const;
|
||||
};
|
||||
|
||||
struct TextureCopy {
|
||||
union {
|
||||
gpu::Texture *src;
|
||||
gpu::Texture **src_ref;
|
||||
};
|
||||
union {
|
||||
gpu::Texture *dst;
|
||||
gpu::Texture **dst_ref;
|
||||
};
|
||||
bool src_is_ref;
|
||||
bool dst_is_ref;
|
||||
|
||||
void execute() const;
|
||||
std::string serialize() const;
|
||||
};
|
||||
|
||||
union Undetermined {
|
||||
ShaderBind shader_bind;
|
||||
ResourceBind resource_bind;
|
||||
FramebufferBind framebuffer_bind;
|
||||
SubPassTransition subpass_transition;
|
||||
PushConstant push_constant;
|
||||
SpecializeConstant specialize_constant;
|
||||
Draw draw;
|
||||
DrawMulti draw_multi;
|
||||
DrawIndirect draw_indirect;
|
||||
Dispatch dispatch;
|
||||
DispatchIndirect dispatch_indirect;
|
||||
Barrier barrier;
|
||||
Clear clear;
|
||||
ClearMulti clear_multi;
|
||||
StateSet state_set;
|
||||
StencilSet stencil_set;
|
||||
TextureCopy texture_copy;
|
||||
};
|
||||
|
||||
/** Try to keep the command size as low as possible for performance. */
|
||||
|
||||
BLI_STATIC_ASSERT(sizeof(Undetermined) <= 24, "One of the command type is too large.")
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Draw Commands
|
||||
*
|
||||
* A draw command buffer used to issue single draw commands without instance merging or any
|
||||
* other optimizations.
|
||||
*
|
||||
* It still uses a ResourceIdBuf to keep the same shader interface as multi draw commands.
|
||||
*
|
||||
* \{ */
|
||||
|
||||
class DrawCommandBuf {
|
||||
friend Manager;
|
||||
|
||||
private:
|
||||
using ResourceIdBuf = StorageArrayBuffer<uint, 128, false>;
|
||||
using SubPassVector = detail::SubPassVector<detail::PassBase<DrawCommandBuf>, 16>;
|
||||
|
||||
/** Array of resource id. One per instance. Generated on GPU and send to GPU. */
|
||||
ResourceIdBuf resource_id_buf_;
|
||||
/** Used items in the resource_id_buf_. Not it's allocated length. */
|
||||
uint resource_id_count_ = 0;
|
||||
|
||||
public:
|
||||
void clear()
|
||||
{
|
||||
resource_id_buf_.trim_to_next_power_of_2(resource_id_count_);
|
||||
};
|
||||
|
||||
void append_draw(Vector<Header, 0> &headers,
|
||||
Vector<Undetermined, 0> &commands,
|
||||
gpu::Batch *batch,
|
||||
uint instance_len,
|
||||
uint vertex_len,
|
||||
uint vertex_first,
|
||||
ResourceIDRange id_range,
|
||||
uint custom_id,
|
||||
GPUPrimType expanded_prim_type,
|
||||
uint16_t expanded_prim_len)
|
||||
{
|
||||
BLI_assert(batch != nullptr);
|
||||
vertex_first = vertex_first != -1 ? vertex_first : 0;
|
||||
instance_len = instance_len != -1 ? instance_len : 1;
|
||||
|
||||
BLI_assert_msg(custom_id == 0, "Custom ID is not supported in PassSimple");
|
||||
UNUSED_VARS_NDEBUG(custom_id);
|
||||
|
||||
for (auto res_id : id_range.id_range()) {
|
||||
int64_t index = commands.append_and_get_index({});
|
||||
headers.append({Type::Draw, uint(index)});
|
||||
commands[index].draw = {batch,
|
||||
instance_len,
|
||||
vertex_len,
|
||||
vertex_first,
|
||||
expanded_prim_type,
|
||||
expanded_prim_len,
|
||||
ResourceID(res_id)};
|
||||
}
|
||||
}
|
||||
|
||||
void generate_commands(Vector<Header, 0> &headers,
|
||||
Vector<Undetermined, 0> &commands,
|
||||
SubPassVector &sub_passes);
|
||||
|
||||
void bind(RecordingState &state);
|
||||
|
||||
private:
|
||||
static void finalize_commands(Vector<Header, 0> &headers,
|
||||
Vector<Undetermined, 0> &commands,
|
||||
SubPassVector &sub_passes,
|
||||
uint &resource_id_count,
|
||||
ResourceIdBuf &resource_id_buf);
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Multi Draw Commands
|
||||
*
|
||||
* For efficient rendering of large scene we strive to minimize the number of draw call and state
|
||||
* changes. To this end, we group many rendering commands and sort them per render state using
|
||||
* `DrawGroup` as a container. This is done automatically for any successive commands with the
|
||||
* same state.
|
||||
*
|
||||
* A `DrawGroup` is the combination of a `gpu::Batch` (VBO state) and a `command::DrawMulti`
|
||||
* (Pipeline State).
|
||||
*
|
||||
* Inside each `DrawGroup` all instances of a same `gpu::Batch` is merged into a single indirect
|
||||
* command.
|
||||
*
|
||||
* To support this arbitrary reordering, we only need to know the offset of all the commands for a
|
||||
* specific `DrawGroup`. This is done on CPU by doing a simple prefix sum. The result is pushed to
|
||||
* GPU and used on CPU to issue the right command indirect.
|
||||
*
|
||||
* Each draw command is stored in an unsorted array of `DrawPrototype` and sent directly to the
|
||||
* GPU.
|
||||
*
|
||||
* A command generation compute shader then go over each `DrawPrototype`. For each it adds it (or
|
||||
* not depending on visibility) to the correct draw command using the offset of the `DrawGroup`
|
||||
* computed on CPU. After that, it also outputs one resource ID for each instance inside a
|
||||
* `DrawPrototype`.
|
||||
*
|
||||
* \{ */
|
||||
|
||||
class DrawMultiBuf {
|
||||
friend Manager;
|
||||
friend DrawMulti;
|
||||
|
||||
private:
|
||||
using DrawGroupBuf = StorageArrayBuffer<DrawGroup, 16>;
|
||||
using DrawPrototypeBuf = StorageArrayBuffer<DrawPrototype, 16>;
|
||||
using DrawCommandBuf = StorageArrayBuffer<DrawCommand, 16, true>;
|
||||
using ResourceIdBuf = StorageArrayBuffer<uint, 128, true>;
|
||||
|
||||
using DrawGroupKey = std::pair<uint, gpu::Batch *>;
|
||||
using DrawGroupMap = Map<DrawGroupKey, uint>;
|
||||
/** Maps a DrawMulti command and a gpu batch to their unique DrawGroup command. */
|
||||
DrawGroupMap group_ids_;
|
||||
|
||||
/** DrawGroup Command heap. Uploaded to GPU for sorting. */
|
||||
DrawGroupBuf group_buf_ = {"DrawGroupBuf"};
|
||||
/** Command Prototypes. Unsorted */
|
||||
DrawPrototypeBuf prototype_buf_ = {"DrawPrototypeBuf"};
|
||||
/** Command list generated by the sorting / compaction steps. Lives on GPU. */
|
||||
DrawCommandBuf command_buf_ = {"DrawCommandBuf"};
|
||||
/** Array of resource id. One per instance. Lives on GPU. */
|
||||
ResourceIdBuf resource_id_buf_ = {"ResourceIdBuf"};
|
||||
/** Give unique ID to each header so we can use that as hash key. */
|
||||
uint header_id_counter_ = 0;
|
||||
/** Number of groups inside group_buf_. */
|
||||
uint group_count_ = 0;
|
||||
/** Number of prototype command inside prototype_buf_. */
|
||||
uint prototype_count_ = 0;
|
||||
/** Used items in the resource_id_buf_. Not it's allocated length. */
|
||||
uint resource_id_count_ = 0;
|
||||
|
||||
public:
|
||||
void clear()
|
||||
{
|
||||
group_buf_.trim_to_next_power_of_2(group_count_);
|
||||
/* Two commands per group (inverted and non-inverted scale). */
|
||||
command_buf_.trim_to_next_power_of_2(group_count_ * 2);
|
||||
prototype_buf_.trim_to_next_power_of_2(prototype_count_);
|
||||
resource_id_buf_.trim_to_next_power_of_2(resource_id_count_);
|
||||
header_id_counter_ = 0;
|
||||
group_count_ = 0;
|
||||
prototype_count_ = 0;
|
||||
group_ids_.clear();
|
||||
}
|
||||
|
||||
void append_draw(Vector<Header, 0> &headers,
|
||||
Vector<Undetermined, 0> &commands,
|
||||
gpu::Batch *batch,
|
||||
uint instance_len,
|
||||
uint vertex_len,
|
||||
uint vertex_first,
|
||||
ResourceIDRange id_range,
|
||||
uint custom_id,
|
||||
GPUPrimType expanded_prim_type,
|
||||
uint16_t expanded_prim_len)
|
||||
{
|
||||
BLI_assert(batch != nullptr);
|
||||
/* Custom draw-calls cannot be batched and will produce one group per draw. */
|
||||
const bool custom_group = ((vertex_first != 0 && vertex_first != -1) || vertex_len != -1);
|
||||
|
||||
BLI_assert(vertex_len != 0);
|
||||
vertex_len = vertex_len == -1 ? 0 : vertex_len;
|
||||
instance_len = instance_len != -1 ? instance_len : 1;
|
||||
|
||||
/* If there was some state changes since previous call, we have to create another command. */
|
||||
if (headers.is_empty() || headers.last().type != Type::DrawMulti) {
|
||||
uint index = commands.append_and_get_index({});
|
||||
headers.append({Type::DrawMulti, index});
|
||||
commands[index].draw_multi = {batch, this, uint(-1), header_id_counter_++};
|
||||
}
|
||||
|
||||
DrawMulti &cmd = commands.last().draw_multi;
|
||||
|
||||
uint &group_id = group_ids_.lookup_or_add(DrawGroupKey(cmd.uuid, batch), uint(-1));
|
||||
|
||||
bool inverted = id_range.has_inverted_handedness();
|
||||
|
||||
for (auto res_id : id_range.id_range()) {
|
||||
DrawPrototype &draw = prototype_buf_.get_or_resize(prototype_count_++);
|
||||
draw.res_id = uint32_t(res_id);
|
||||
draw.custom_id = custom_id;
|
||||
draw.instance_len = instance_len;
|
||||
draw.group_id = group_id;
|
||||
|
||||
if (group_id == uint(-1) || custom_group) {
|
||||
uint new_group_id = group_count_++;
|
||||
draw.group_id = new_group_id;
|
||||
|
||||
DrawGroup &group = group_buf_.get_or_resize(new_group_id);
|
||||
group.next = cmd.group_first;
|
||||
group.len = instance_len;
|
||||
group.front_facing_len = inverted ? 0 : instance_len;
|
||||
group.front_facing_counter = 0;
|
||||
group.back_facing_counter = 0;
|
||||
group.desc.vertex_len = vertex_len;
|
||||
group.desc.vertex_first = vertex_first;
|
||||
group.desc.gpu_batch = batch;
|
||||
group.desc.expand_prim_type = expanded_prim_type;
|
||||
group.desc.expand_prim_len = expanded_prim_len;
|
||||
BLI_assert_msg(expanded_prim_len < (1 << 3),
|
||||
"Not enough bits to store primitive expansion");
|
||||
/* Custom group are not to be registered in the group_ids_. */
|
||||
if (!custom_group) {
|
||||
group_id = new_group_id;
|
||||
}
|
||||
/* For serialization only. Reset before use on GPU. */
|
||||
(inverted ? group.back_facing_counter : group.front_facing_counter)++;
|
||||
/* Append to list. */
|
||||
cmd.group_first = new_group_id;
|
||||
}
|
||||
else {
|
||||
DrawGroup &group = group_buf_[group_id];
|
||||
group.len += instance_len;
|
||||
group.front_facing_len += inverted ? 0 : instance_len;
|
||||
/* For serialization only. Reset before use on GPU. */
|
||||
(inverted ? group.back_facing_counter : group.front_facing_counter)++;
|
||||
/* NOTE: We assume that primitive expansion is coupled to the shader itself. Meaning we
|
||||
* rely on shader bind to isolate the expanded draws into their own group (as there could
|
||||
* be regular draws and extended draws using the same batch mixed inside the same pass).
|
||||
* This will cause issues if this assumption is broken. Also it is very hard to detect this
|
||||
* case for error checking. At least we can check that expansion settings don't change
|
||||
* inside a group. */
|
||||
BLI_assert(group.desc.expand_prim_type == expanded_prim_type);
|
||||
BLI_assert(group.desc.expand_prim_len == expanded_prim_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void generate_commands(Vector<Header, 0> &headers,
|
||||
Vector<Undetermined, 0> &commands,
|
||||
VisibilityBuf &visibility_buf,
|
||||
int visibility_word_per_draw,
|
||||
int view_len,
|
||||
bool use_custom_ids);
|
||||
|
||||
void bind(RecordingState &state);
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
}; // namespace draw::command
|
||||
|
||||
} // namespace blender
|
||||
107
blender-5.2.0/source/blender/draw/intern/draw_command_shared.hh
Normal file
107
blender-5.2.0/source/blender/draw/intern/draw_command_shared.hh
Normal file
@@ -0,0 +1,107 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GPU_shader_shared_utils.hh"
|
||||
|
||||
#ifndef GPU_SHADER
|
||||
namespace blender::gpu {
|
||||
class Batch;
|
||||
}
|
||||
namespace blender::draw::command {
|
||||
#endif
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Multi Draw
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* A DrawGroup allow to split the command stream into batch-able chunks of commands with
|
||||
* the same render state.
|
||||
*/
|
||||
struct [[host_shared]] DrawGroup {
|
||||
/** Index of next #DrawGroup from the same header. */
|
||||
uint next;
|
||||
|
||||
/**
|
||||
* IMPORTANT: All the following 3 members do not take multi-view into account.
|
||||
* They only count the number of input instances. The command generation shader must multiply
|
||||
* them by view_len to get the correct indices for resource ids.
|
||||
*/
|
||||
|
||||
/** Index of the first instances after sorting. */
|
||||
uint start;
|
||||
/** Total number of instances (including inverted facing). Needed to issue the draw call. */
|
||||
uint len;
|
||||
/** Number of non inverted scaling instances in this Group. */
|
||||
uint front_facing_len;
|
||||
|
||||
/** #gpu::Batch values (or subrange of) copied to #DrawCommand after sorting. */
|
||||
int vertex_len;
|
||||
int vertex_first;
|
||||
/* Set to -1 if not an indexed draw. */
|
||||
int base_index;
|
||||
|
||||
/** Atomic counters used during command sorting. GPU only. Reset on CPU. */
|
||||
|
||||
/* Counts visible and invisible instances. Create draw-calls when it reaches `DrawGroup::len`. */
|
||||
uint total_counter;
|
||||
/* Counts only visible instance (counting multi-view). Used to issue the draw-calls. */
|
||||
uint front_facing_counter;
|
||||
uint back_facing_counter;
|
||||
|
||||
/* CPU specific region of the struct. Should be kept constant after recording.
|
||||
* Can be used by GPU but needs to be initialized by GPU before usage. */
|
||||
#ifndef GPU_SHADER
|
||||
struct {
|
||||
/* Specific range of vertex to draw from the #gpu::Batch. */
|
||||
uint32_t vertex_first;
|
||||
/* Ugly packing to support expanded draws without inflating the struct.
|
||||
* Makes vertex range restricted to smaller range for expanded draw. */
|
||||
uint32_t expand_prim_type : 4;
|
||||
uint32_t expand_prim_len : 3;
|
||||
uint32_t vertex_len : 25;
|
||||
|
||||
/** Needed to create the correct draw call. */
|
||||
gpu::Batch *gpu_batch;
|
||||
uint64_t _cpu_pad0;
|
||||
} desc;
|
||||
#else
|
||||
uint _cpu_reserved_1;
|
||||
uint _cpu_reserved_2;
|
||||
|
||||
uint _cpu_reserved_3;
|
||||
uint _cpu_reserved_4;
|
||||
uint _cpu_reserved_5;
|
||||
uint _cpu_reserved_6;
|
||||
#endif
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(DrawGroup, 16)
|
||||
|
||||
/**
|
||||
* Representation of a future draw call inside a DrawGroup. This #DrawPrototype is then
|
||||
* converted into #DrawCommand on GPU after visibility and compaction. Multiple
|
||||
* #DrawPrototype might get merged into the same final #DrawCommand.
|
||||
*/
|
||||
struct [[host_shared]] DrawPrototype {
|
||||
/* Reference to parent DrawGroup to get the gpu::Batch vertex / instance count. */
|
||||
uint group_id;
|
||||
/* Resource handle associated with this call. Also reference visibility. */
|
||||
uint res_id;
|
||||
/* Custom extra value to be used by the engines. */
|
||||
uint custom_id;
|
||||
/* Number of instances. */
|
||||
uint instance_len;
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
#ifndef GPU_SHADER
|
||||
}; // namespace blender::draw::command
|
||||
#endif
|
||||
93
blender-5.2.0/source/blender/draw/intern/draw_common.hh
Normal file
93
blender-5.2.0/source/blender/draw/intern/draw_common.hh
Normal file
@@ -0,0 +1,93 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "draw_common_c.hh"
|
||||
#include "draw_manager.hh"
|
||||
#include "draw_pass.hh"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
/** Hair. */
|
||||
|
||||
void hair_init();
|
||||
|
||||
/**
|
||||
* \note Only valid after #DRW_curves_update().
|
||||
*/
|
||||
gpu::VertBuf *hair_pos_buffer_get(Scene *scene,
|
||||
Object *object,
|
||||
ParticleSystem *psys,
|
||||
ModifierData *md);
|
||||
|
||||
gpu::Batch *hair_sub_pass_setup(PassMain::Sub &sub_ps,
|
||||
const Scene *scene,
|
||||
const ObjectRef &ob_ref,
|
||||
ParticleSystem *psys,
|
||||
ModifierData *md,
|
||||
GPUMaterial *gpu_material = nullptr);
|
||||
|
||||
gpu::Batch *hair_sub_pass_setup(PassSimple::Sub &sub_ps,
|
||||
const Scene *scene,
|
||||
const ObjectRef &ob_ref,
|
||||
ParticleSystem *psys,
|
||||
ModifierData *md,
|
||||
GPUMaterial *gpu_material = nullptr);
|
||||
|
||||
/** Curves. */
|
||||
|
||||
/**
|
||||
* \note Content of the vertex buf is only valid after #DRW_curves_update().
|
||||
*/
|
||||
gpu::VertBuf *curves_pos_buffer_get(Object *object);
|
||||
|
||||
gpu::Batch *curves_sub_pass_setup(PassMain::Sub &ps,
|
||||
const Scene *scene,
|
||||
Object *ob,
|
||||
const char *&r_error,
|
||||
GPUMaterial *gpu_material = nullptr);
|
||||
|
||||
gpu::Batch *curves_sub_pass_setup(PassSimple::Sub &ps,
|
||||
const Scene *scene,
|
||||
Object *ob,
|
||||
const char *&r_error,
|
||||
GPUMaterial *gpu_material = nullptr);
|
||||
|
||||
/* Point cloud. */
|
||||
|
||||
gpu::Batch *pointcloud_sub_pass_setup(PassMain::Sub &sub_ps,
|
||||
Object *object,
|
||||
GPUMaterial *gpu_material = nullptr);
|
||||
|
||||
gpu::Batch *pointcloud_sub_pass_setup(PassSimple::Sub &sub_ps,
|
||||
Object *object,
|
||||
GPUMaterial *gpu_material = nullptr);
|
||||
|
||||
/** Volume. */
|
||||
|
||||
/**
|
||||
* Add attribute bindings of volume grids to an existing pass.
|
||||
* No draw call is added so the caller can decide how to use the data.
|
||||
* \return nullptr if there is nothing to draw.
|
||||
*/
|
||||
PassMain::Sub *volume_sub_pass(PassMain::Sub &ps,
|
||||
Scene *scene,
|
||||
Object *ob,
|
||||
GPUMaterial *gpu_material);
|
||||
/**
|
||||
* Add attribute bindings of volume grids to an existing pass.
|
||||
* No draw call is added so the caller can decide how to use the data.
|
||||
* \return nullptr if there is nothing to draw.
|
||||
*/
|
||||
PassSimple::Sub *volume_sub_pass(PassSimple::Sub &ps,
|
||||
Scene *scene,
|
||||
Object *ob,
|
||||
GPUMaterial *gpu_material);
|
||||
|
||||
} // namespace blender::draw
|
||||
74
blender-5.2.0/source/blender/draw/intern/draw_common_c.hh
Normal file
74
blender-5.2.0/source/blender/draw/intern/draw_common_c.hh
Normal file
@@ -0,0 +1,74 @@
|
||||
/* SPDX-FileCopyrightText: 2016 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct FluidModifierData;
|
||||
struct GPUMaterial;
|
||||
|
||||
namespace gpu {
|
||||
class Texture;
|
||||
class UniformBuf;
|
||||
class VertBuf;
|
||||
} // namespace gpu
|
||||
struct ModifierData;
|
||||
struct Object;
|
||||
struct ParticleSystem;
|
||||
struct RegionView3D;
|
||||
struct ViewLayer;
|
||||
struct Scene;
|
||||
struct DRWData;
|
||||
namespace draw {
|
||||
class Manager;
|
||||
struct CurvesModule;
|
||||
struct PointCloudModule;
|
||||
struct VolumeModule;
|
||||
class ObjectRef;
|
||||
} // namespace draw
|
||||
|
||||
/* draw_curves.cc */
|
||||
|
||||
namespace draw {
|
||||
|
||||
/* If drw_data is nullptr, DST global is accessed to get it. */
|
||||
void DRW_curves_init(DRWData *drw_data = nullptr);
|
||||
void DRW_curves_begin_sync(DRWData *drw_data);
|
||||
void DRW_curves_module_free(draw::CurvesModule *module);
|
||||
void DRW_curves_update(draw::Manager &manager);
|
||||
|
||||
/* draw_pointcloud.cc */
|
||||
|
||||
/* If drw_data is nullptr, DST global is accessed to get it. */
|
||||
void DRW_pointcloud_init(DRWData *drw_data = nullptr);
|
||||
void DRW_pointcloud_module_free(draw::PointCloudModule *module);
|
||||
|
||||
/* draw_volume.cc */
|
||||
|
||||
/* If drw_data is nullptr, DST global is accessed to get it. */
|
||||
void DRW_volume_init(DRWData *drw_data = nullptr);
|
||||
void DRW_volume_module_free(draw::VolumeModule *module);
|
||||
|
||||
} // namespace draw
|
||||
|
||||
/* `draw_fluid.cc` */
|
||||
|
||||
/* Fluid simulation. */
|
||||
void DRW_smoke_ensure(FluidModifierData *fmd, int highres);
|
||||
void DRW_smoke_ensure_coba_field(FluidModifierData *fmd);
|
||||
void DRW_smoke_ensure_velocity(FluidModifierData *fmd);
|
||||
void DRW_fluid_ensure_flags(FluidModifierData *fmd);
|
||||
void DRW_fluid_ensure_range_field(FluidModifierData *fmd);
|
||||
|
||||
void DRW_smoke_free(FluidModifierData *fmd);
|
||||
|
||||
void DRW_smoke_begin_sync(DRWData *drw_data);
|
||||
void DRW_smoke_exit(DRWData *drw_data);
|
||||
|
||||
} // namespace blender
|
||||
2352
blender-5.2.0/source/blender/draw/intern/draw_context.cc
Normal file
2352
blender-5.2.0/source/blender/draw/intern/draw_context.cc
Normal file
File diff suppressed because it is too large
Load Diff
105
blender-5.2.0/source/blender/draw/intern/draw_context_private.hh
Normal file
105
blender-5.2.0/source/blender/draw/intern/draw_context_private.hh
Normal file
@@ -0,0 +1,105 @@
|
||||
/* SPDX-FileCopyrightText: 2016 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
/* Private functions / structs of the draw manager */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DRW_engine.hh"
|
||||
#include "DRW_render.hh"
|
||||
|
||||
#include "BLI_task.h"
|
||||
#include "BLI_threads.h"
|
||||
#include "BLI_vector_set.hh"
|
||||
|
||||
#include "GPU_batch.hh"
|
||||
#include "GPU_context.hh"
|
||||
#include "GPU_framebuffer.hh"
|
||||
#include "GPU_viewport.hh"
|
||||
|
||||
struct DRWDebugModule;
|
||||
struct DRWUniformChunk;
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct DRWViewData;
|
||||
struct DRWTextStore;
|
||||
struct DupliObject;
|
||||
struct Object;
|
||||
struct Mesh;
|
||||
namespace draw {
|
||||
struct CurvesModule;
|
||||
struct VolumeModule;
|
||||
struct PointCloudModule;
|
||||
struct DRW_MeshCDMask;
|
||||
class CurveRefinePass;
|
||||
class View;
|
||||
} // namespace draw
|
||||
struct GPUMaterial;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Memory Pools
|
||||
* \{ */
|
||||
|
||||
/** Contains memory pools information. */
|
||||
struct DRWData {
|
||||
/** Instance data. */
|
||||
DRWInstanceDataList *idatalist;
|
||||
/** List of smoke textures to free after drawing. */
|
||||
ListBaseT<LinkData> smoke_textures;
|
||||
/** Per stereo view data. Contains engine data and default frame-buffers. */
|
||||
DRWViewData *view_data[2];
|
||||
/** Module storage. */
|
||||
draw::CurvesModule *curves_module;
|
||||
draw::VolumeModule *volume_module;
|
||||
draw::PointCloudModule *pointcloud_module;
|
||||
/** Default view that feeds every engine. */
|
||||
draw::View *default_view;
|
||||
|
||||
/* Ensure modules are created. */
|
||||
void modules_init();
|
||||
/* Callbacks before each sync cycle. */
|
||||
void modules_begin_sync();
|
||||
/* Callbacks after one draw to clear transient data. */
|
||||
void modules_exit();
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Functions
|
||||
* \{ */
|
||||
|
||||
/* Get thread local draw context. */
|
||||
inline DRWContext &drw_get()
|
||||
{
|
||||
return DRWContext::get_active();
|
||||
}
|
||||
|
||||
namespace draw {
|
||||
|
||||
void drw_batch_cache_validate(Object *ob);
|
||||
void drw_batch_cache_generate_requested(Object *ob, TaskGraph &task_graph);
|
||||
|
||||
/**
|
||||
* \warning Only evaluated mesh data is handled by this delayed generation.
|
||||
*/
|
||||
void drw_batch_cache_generate_requested_delayed(Object *ob);
|
||||
void drw_batch_cache_generate_requested_evaluated_mesh_or_curve(Object *ob, TaskGraph &task_graph);
|
||||
|
||||
void DRW_mesh_get_attributes(const Object &object,
|
||||
const Mesh &mesh,
|
||||
Span<const GPUMaterial *> materials,
|
||||
VectorSet<std::string> *r_attrs,
|
||||
DRW_MeshCDMask *r_cd_needed);
|
||||
|
||||
} // namespace draw
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
568
blender-5.2.0/source/blender/draw/intern/draw_curves.cc
Normal file
568
blender-5.2.0/source/blender/draw/intern/draw_curves.cc
Normal file
@@ -0,0 +1,568 @@
|
||||
/* SPDX-FileCopyrightText: 2017 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*
|
||||
* \brief Contains procedural GPU hair drawing methods.
|
||||
*/
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
#include "DNA_curves_types.h"
|
||||
|
||||
#include "BLI_math_base.h"
|
||||
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_curves.hh"
|
||||
|
||||
#include "GPU_batch.hh"
|
||||
#include "GPU_capabilities.hh"
|
||||
#include "GPU_material.hh"
|
||||
#include "GPU_shader.hh"
|
||||
#include "GPU_texture.hh"
|
||||
#include "GPU_vertex_buffer.hh"
|
||||
|
||||
#include "DRW_gpu_wrapper.hh"
|
||||
#include "DRW_render.hh"
|
||||
|
||||
#include "draw_cache_impl.hh"
|
||||
#include "draw_common.hh"
|
||||
#include "draw_context_private.hh"
|
||||
#include "draw_curves_defines.hh"
|
||||
#include "draw_curves_private.hh"
|
||||
#include "draw_hair_private.hh"
|
||||
#include "draw_shader.hh"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
using CurvesInfosBuf = UniformBuffer<CurvesInfos>;
|
||||
|
||||
CurvesInfosBuf &CurvesUniformBufPool::alloc()
|
||||
{
|
||||
CurvesInfosBuf *ptr;
|
||||
if (used >= ubos.size()) {
|
||||
ubos.append(std::make_unique<CurvesInfosBuf>());
|
||||
ptr = ubos.last().get();
|
||||
}
|
||||
else {
|
||||
ptr = ubos[used++].get();
|
||||
}
|
||||
|
||||
memset(ptr->data(), 0, sizeof(CurvesInfos));
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
gpu::VertBuf *CurvesModule::drw_curves_ensure_dummy_vbo()
|
||||
{
|
||||
GPUVertFormat format = {0};
|
||||
uint dummy_id = GPU_vertformat_attr_add(&format, "dummy", gpu::VertAttrType::SFLOAT_32_32_32_32);
|
||||
|
||||
gpu::VertBuf *vbo = GPU_vertbuf_create_with_format_ex(
|
||||
format, GPU_USAGE_STATIC | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY);
|
||||
|
||||
const float vert[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
GPU_vertbuf_data_alloc(*vbo, 1);
|
||||
GPU_vertbuf_attr_fill(vbo, dummy_id, vert);
|
||||
/* Create vbo immediately to bind to texture buffer. */
|
||||
GPU_vertbuf_use(vbo);
|
||||
return vbo;
|
||||
}
|
||||
|
||||
void DRW_curves_init(DRWData *drw_data)
|
||||
{
|
||||
if (drw_data == nullptr) {
|
||||
drw_data = drw_get().data;
|
||||
}
|
||||
if (drw_data->curves_module == nullptr) {
|
||||
drw_data->curves_module = MEM_new<CurvesModule>("CurvesModule");
|
||||
}
|
||||
}
|
||||
|
||||
void DRW_curves_begin_sync(DRWData *drw_data)
|
||||
{
|
||||
drw_data->curves_module->init();
|
||||
}
|
||||
|
||||
void DRW_curves_module_free(CurvesModule *curves_module)
|
||||
{
|
||||
MEM_delete(curves_module);
|
||||
}
|
||||
|
||||
void CurvesModule::dispatch(const int curve_count, PassSimple::Sub &pass)
|
||||
{
|
||||
/* Note that the GPU_max_work_group_count can be INT_MAX.
|
||||
* Promote to 64bit int to avoid overflow. */
|
||||
const int64_t max_strands_per_call = int64_t(GPU_max_work_group_count(0)) *
|
||||
CURVES_PER_THREADGROUP;
|
||||
int strands_start = 0;
|
||||
while (strands_start < curve_count) {
|
||||
int batch_strands_len = std::min(int64_t(curve_count - strands_start), max_strands_per_call);
|
||||
pass.push_constant("curves_start", strands_start);
|
||||
pass.push_constant("curves_count", batch_strands_len);
|
||||
pass.dispatch(divide_ceil_u(batch_strands_len, CURVES_PER_THREADGROUP));
|
||||
strands_start += batch_strands_len;
|
||||
}
|
||||
}
|
||||
|
||||
gpu::VertBufPtr CurvesModule::evaluate_topology_indirection(const int curve_count,
|
||||
const int point_count,
|
||||
CurvesEvalCache &cache,
|
||||
bool is_ribbon,
|
||||
bool has_cyclic)
|
||||
{
|
||||
int element_count = is_ribbon ? (point_count + curve_count) : (point_count - curve_count);
|
||||
if (has_cyclic) {
|
||||
element_count += curve_count;
|
||||
}
|
||||
gpu::VertBufPtr indirection_buf = gpu::VertBuf::device_only<int>(element_count);
|
||||
|
||||
PassSimple::Sub &pass = refine.sub("Topology");
|
||||
pass.shader_set(DRW_shader_curves_topology_get());
|
||||
pass.bind_ssbo("evaluated_offsets_buf", cache.evaluated_points_by_curve_buf);
|
||||
pass.bind_ssbo("curves_cyclic_buf", cache.curves_cyclic_buf);
|
||||
pass.bind_ssbo("indirection_buf", indirection_buf);
|
||||
pass.push_constant("is_ribbon_topology", is_ribbon);
|
||||
pass.push_constant("use_cyclic", has_cyclic);
|
||||
dispatch(curve_count, pass);
|
||||
|
||||
return indirection_buf;
|
||||
}
|
||||
|
||||
void CurvesModule::evaluate_curve_attribute(const bool has_catmull,
|
||||
const bool has_bezier,
|
||||
const bool has_poly,
|
||||
const bool has_nurbs,
|
||||
const bool has_cyclic,
|
||||
const int curve_count,
|
||||
CurvesEvalCache &cache,
|
||||
CurvesEvalShader shader_type,
|
||||
gpu::VertBufPtr input_buf,
|
||||
gpu::VertBufPtr &output_buf,
|
||||
gpu::VertBuf *input2_buf /* = nullptr */,
|
||||
float4x4 transform /* = float4x4::identity() */)
|
||||
{
|
||||
BLI_assert(input_buf != nullptr);
|
||||
BLI_assert(output_buf != nullptr);
|
||||
|
||||
gpu::Shader *shader = DRW_shader_curves_refine_get(shader_type);
|
||||
|
||||
const char *pass_name = nullptr;
|
||||
|
||||
switch (shader_type) {
|
||||
case CURVES_EVAL_POSITION:
|
||||
pass_name = "Position";
|
||||
break;
|
||||
case CURVES_EVAL_FLOAT:
|
||||
pass_name = "Float Attribute";
|
||||
break;
|
||||
case CURVES_EVAL_FLOAT2:
|
||||
pass_name = "Float2 Attribute";
|
||||
break;
|
||||
case CURVES_EVAL_FLOAT3:
|
||||
pass_name = "Float3 Attribute";
|
||||
break;
|
||||
case CURVES_EVAL_FLOAT4:
|
||||
pass_name = "Float4 Attribute";
|
||||
break;
|
||||
case CURVES_EVAL_LENGTH_INTERCEPT:
|
||||
pass_name = "Length-Intercept Attributes";
|
||||
break;
|
||||
}
|
||||
|
||||
PassSimple::Sub &pass = refine.sub(pass_name);
|
||||
pass.bind_ssbo(POINTS_BY_CURVES_SLOT, cache.points_by_curve_buf);
|
||||
pass.bind_ssbo(CURVE_TYPE_SLOT, cache.curves_type_buf);
|
||||
pass.bind_ssbo(CURVE_CYCLIC_SLOT, cache.curves_cyclic_buf);
|
||||
pass.bind_ssbo(CURVE_RESOLUTION_SLOT, cache.curves_resolution_buf);
|
||||
pass.bind_ssbo(EVALUATED_POINT_SLOT, cache.evaluated_points_by_curve_buf);
|
||||
|
||||
switch (shader_type) {
|
||||
case CURVES_EVAL_POSITION:
|
||||
pass.bind_ssbo(POINT_POSITIONS_SLOT, input_buf);
|
||||
pass.bind_ssbo(POINT_RADII_SLOT, input2_buf);
|
||||
pass.bind_ssbo(EVALUATED_POS_RAD_SLOT, cache.evaluated_pos_rad_buf);
|
||||
/* Move ownership of the radius input vbo to the module. */
|
||||
this->transient_buffers.append(gpu::VertBufPtr(input2_buf));
|
||||
break;
|
||||
case CURVES_EVAL_FLOAT:
|
||||
case CURVES_EVAL_FLOAT2:
|
||||
case CURVES_EVAL_FLOAT3:
|
||||
case CURVES_EVAL_FLOAT4:
|
||||
pass.bind_ssbo(POINT_ATTR_SLOT, input_buf);
|
||||
pass.bind_ssbo(EVALUATED_ATTR_SLOT, output_buf);
|
||||
break;
|
||||
case CURVES_EVAL_LENGTH_INTERCEPT:
|
||||
pass.bind_ssbo(EVALUATED_POS_RAD_SLOT, cache.evaluated_pos_rad_buf);
|
||||
pass.bind_ssbo(EVALUATED_TIME_SLOT, cache.evaluated_time_buf);
|
||||
pass.bind_ssbo(CURVES_LENGTH_SLOT, cache.curves_length_buf);
|
||||
/* Synchronize positions reads. */
|
||||
pass.barrier(GPU_BARRIER_SHADER_STORAGE);
|
||||
break;
|
||||
}
|
||||
|
||||
if (has_catmull) {
|
||||
PassSimple::Sub &sub = pass.sub("Catmull-Rom");
|
||||
sub.specialize_constant(shader, "evaluated_type", int(CURVE_TYPE_CATMULL_ROM));
|
||||
sub.shader_set(shader);
|
||||
/* Dummy, not used for Catmull-Rom. */
|
||||
sub.bind_ssbo("handles_positions_left_buf", this->dummy_vbo);
|
||||
sub.bind_ssbo("handles_positions_right_buf", this->dummy_vbo);
|
||||
sub.bind_ssbo("bezier_offsets_buf", this->dummy_vbo);
|
||||
/* Bake object transform for legacy hair particle. */
|
||||
sub.push_constant("transform", transform);
|
||||
sub.push_constant("use_cyclic", has_cyclic);
|
||||
dispatch(curve_count, sub);
|
||||
}
|
||||
|
||||
if (has_bezier) {
|
||||
PassSimple::Sub &sub = pass.sub("Bezier");
|
||||
sub.specialize_constant(shader, "evaluated_type", int(CURVE_TYPE_BEZIER));
|
||||
sub.shader_set(shader);
|
||||
sub.bind_ssbo("handles_positions_left_buf", cache.handles_positions_left_buf);
|
||||
sub.bind_ssbo("handles_positions_right_buf", cache.handles_positions_right_buf);
|
||||
sub.bind_ssbo("bezier_offsets_buf", cache.bezier_offsets_buf);
|
||||
/* Bake object transform for legacy hair particle. */
|
||||
sub.push_constant("transform", transform);
|
||||
sub.push_constant("use_cyclic", has_cyclic);
|
||||
dispatch(curve_count, sub);
|
||||
}
|
||||
|
||||
if (has_nurbs) {
|
||||
PassSimple::Sub &sub = pass.sub("Nurbs");
|
||||
sub.specialize_constant(shader, "evaluated_type", int(CURVE_TYPE_NURBS));
|
||||
sub.shader_set(shader);
|
||||
sub.bind_ssbo("curves_resolution_buf", cache.curves_order_buf);
|
||||
sub.bind_ssbo("handles_positions_left_buf", cache.basis_cache_buf);
|
||||
sub.bind_ssbo("handles_positions_right_buf",
|
||||
cache.control_weights_buf.get() ? cache.control_weights_buf :
|
||||
cache.basis_cache_buf);
|
||||
sub.bind_ssbo("bezier_offsets_buf", cache.basis_cache_offset_buf);
|
||||
sub.push_constant("use_point_weight", cache.control_weights_buf.get() != nullptr);
|
||||
/* Bake object transform for legacy hair particle. */
|
||||
sub.push_constant("transform", transform);
|
||||
sub.push_constant("use_cyclic", has_cyclic);
|
||||
dispatch(curve_count, sub);
|
||||
}
|
||||
|
||||
if (has_poly) {
|
||||
PassSimple::Sub &sub = pass.sub("Poly");
|
||||
sub.specialize_constant(shader, "evaluated_type", int(CURVE_TYPE_POLY));
|
||||
sub.shader_set(shader);
|
||||
/* Dummy, not used for Poly. */
|
||||
sub.bind_ssbo("curves_resolution_buf", this->dummy_vbo);
|
||||
sub.bind_ssbo("handles_positions_left_buf", this->dummy_vbo);
|
||||
sub.bind_ssbo("handles_positions_right_buf", this->dummy_vbo);
|
||||
sub.bind_ssbo("bezier_offsets_buf", this->dummy_vbo);
|
||||
/* Bake object transform for legacy hair particle. */
|
||||
sub.push_constant("transform", transform);
|
||||
sub.push_constant("use_cyclic", has_cyclic);
|
||||
dispatch(curve_count, sub);
|
||||
}
|
||||
|
||||
/* Move ownership of the input vbo to the module. */
|
||||
this->transient_buffers.append(std::move(input_buf));
|
||||
}
|
||||
|
||||
void CurvesModule::evaluate_curve_length_intercept(const bool has_cyclic,
|
||||
const int curve_count,
|
||||
CurvesEvalCache &cache)
|
||||
{
|
||||
gpu::Shader *shader = DRW_shader_curves_refine_get(CURVES_EVAL_LENGTH_INTERCEPT);
|
||||
|
||||
PassSimple::Sub &pass = refine.sub("Length-Intercept Attributes");
|
||||
pass.shader_set(shader);
|
||||
pass.bind_ssbo(POINTS_BY_CURVES_SLOT, cache.points_by_curve_buf);
|
||||
pass.bind_ssbo(CURVE_TYPE_SLOT, cache.curves_type_buf);
|
||||
pass.bind_ssbo(CURVE_CYCLIC_SLOT, cache.curves_cyclic_buf);
|
||||
pass.bind_ssbo(CURVE_RESOLUTION_SLOT, cache.curves_resolution_buf);
|
||||
pass.bind_ssbo(EVALUATED_POINT_SLOT, cache.evaluated_points_by_curve_buf);
|
||||
|
||||
pass.bind_ssbo(EVALUATED_POS_RAD_SLOT, cache.evaluated_pos_rad_buf);
|
||||
pass.bind_ssbo(EVALUATED_TIME_SLOT, cache.evaluated_time_buf);
|
||||
pass.bind_ssbo(CURVES_LENGTH_SLOT, cache.curves_length_buf);
|
||||
pass.barrier(GPU_BARRIER_SHADER_STORAGE);
|
||||
/* Bake object transform for legacy hair particle. */
|
||||
pass.push_constant("use_cyclic", has_cyclic);
|
||||
dispatch(curve_count, pass);
|
||||
}
|
||||
|
||||
static int attribute_index_in_material(const GPUMaterial *gpu_material,
|
||||
const StringRef name,
|
||||
bool is_curve_length = false,
|
||||
bool is_curve_intercept = false)
|
||||
{
|
||||
if (!gpu_material) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
|
||||
ListBaseT<GPUMaterialAttribute> gpu_attrs = GPU_material_attributes(gpu_material);
|
||||
for (GPUMaterialAttribute &gpu_attr : gpu_attrs) {
|
||||
if (is_curve_length) {
|
||||
if (gpu_attr.is_hair_length) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
else if (is_curve_intercept) {
|
||||
if (gpu_attr.is_hair_intercept) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
else if (!gpu_attr.is_hair_intercept && !gpu_attr.is_hair_length && gpu_attr.name == name) {
|
||||
return index;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void DRW_curves_update(draw::Manager &manager)
|
||||
{
|
||||
DRW_submission_start();
|
||||
|
||||
/* TODO(fclem): Remove Global access. */
|
||||
CurvesModule &module = *drw_get().data->curves_module;
|
||||
|
||||
/* NOTE: This also update legacy hairs too as they populate the same pass. */
|
||||
manager.submit(module.refine);
|
||||
GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE);
|
||||
|
||||
module.transient_buffers.clear();
|
||||
|
||||
/* Make sure calling this function again will not subdivide the same data. */
|
||||
module.refine.init();
|
||||
|
||||
DRW_submission_end();
|
||||
}
|
||||
|
||||
/* New Draw Manager. */
|
||||
|
||||
gpu::VertBuf *curves_pos_buffer_get(Object *object)
|
||||
{
|
||||
CurvesModule &module = *drw_get().data->curves_module;
|
||||
Curves &curves = DRW_object_get_data_for_drawing<Curves>(*object);
|
||||
|
||||
CurvesEvalCache &cache = curves_get_eval_cache(curves);
|
||||
cache.ensure_positions(module, curves.geometry.wrap());
|
||||
|
||||
return cache.evaluated_pos_rad_buf.get();
|
||||
}
|
||||
|
||||
static std::optional<StringRef> get_first_uv_name(const bke::AttributeAccessor &attributes)
|
||||
{
|
||||
std::optional<StringRef> name;
|
||||
attributes.foreach_attribute([&](const bke::AttributeIter &iter) {
|
||||
if (iter.data_type == bke::AttrType::Float2) {
|
||||
name = iter.name;
|
||||
iter.stop();
|
||||
}
|
||||
});
|
||||
return name;
|
||||
}
|
||||
|
||||
/* Return true if attribute exists in shader. */
|
||||
static bool set_attribute_type(const GPUMaterial *gpu_material,
|
||||
const StringRef name,
|
||||
CurvesInfosBuf &curves_infos,
|
||||
const bool is_point_domain)
|
||||
{
|
||||
/* Some attributes may not be used in the shader anymore and were not garbage collected yet, so
|
||||
* we need to find the right index for this attribute as uniforms defining the scope of the
|
||||
* attributes are based on attribute loading order, which is itself based on the material's
|
||||
* attributes. */
|
||||
const int index = attribute_index_in_material(gpu_material, name);
|
||||
if (index == -1) {
|
||||
return false;
|
||||
}
|
||||
curves_infos.is_point_attribute[index][0] = is_point_domain;
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename PassT>
|
||||
void curves_bind_resources_implementation(PassT &sub_ps,
|
||||
CurvesModule &module,
|
||||
CurvesEvalCache &cache,
|
||||
const int face_per_segment,
|
||||
GPUMaterial *gpu_material,
|
||||
gpu::VertBufPtr &indirection_buf,
|
||||
const std::optional<StringRef> uv_name)
|
||||
{
|
||||
/* Ensure we have no unbound resources.
|
||||
* Required for Vulkan.
|
||||
* Fixes issues with certain GL drivers not drawing anything. */
|
||||
sub_ps.bind_texture("u", module.dummy_vbo);
|
||||
sub_ps.bind_texture("au", module.dummy_vbo);
|
||||
sub_ps.bind_texture("a", module.dummy_vbo);
|
||||
sub_ps.bind_texture("c", module.dummy_vbo);
|
||||
sub_ps.bind_texture("ac", module.dummy_vbo);
|
||||
sub_ps.bind_texture("l", module.dummy_vbo);
|
||||
sub_ps.bind_texture("i", module.dummy_vbo);
|
||||
if (gpu_material) {
|
||||
ListBaseT<GPUMaterialAttribute> attr_list = GPU_material_attributes(gpu_material);
|
||||
ListBaseWrapper<GPUMaterialAttribute> attrs(attr_list);
|
||||
for (const GPUMaterialAttribute *attr : attrs) {
|
||||
sub_ps.bind_texture(attr->input_name, module.dummy_vbo);
|
||||
}
|
||||
}
|
||||
|
||||
CurvesInfosBuf &curves_infos = module.ubo_pool.alloc();
|
||||
|
||||
{
|
||||
/* TODO(fclem): Compute only if needed. */
|
||||
const int index = attribute_index_in_material(gpu_material, "", true, false);
|
||||
if (index != -1) {
|
||||
sub_ps.bind_texture("l", cache.curves_length_buf);
|
||||
curves_infos.is_point_attribute[index][0] = false;
|
||||
}
|
||||
}
|
||||
{
|
||||
/* TODO(fclem): Compute only if needed. */
|
||||
const int index = attribute_index_in_material(gpu_material, "", false, true);
|
||||
if (index != -1) {
|
||||
sub_ps.bind_texture("i", cache.evaluated_time_buf);
|
||||
curves_infos.is_point_attribute[index][0] = true;
|
||||
}
|
||||
}
|
||||
|
||||
const VectorSet<std::string> &attrs = cache.attr_used;
|
||||
for (const int i : attrs.index_range()) {
|
||||
const StringRef name = attrs[i];
|
||||
char sampler_name[32];
|
||||
drw_curves_get_attribute_sampler_name(name, sampler_name);
|
||||
|
||||
if (cache.attributes_point_domain[i]) {
|
||||
if (!cache.evaluated_attributes_buf[i]) {
|
||||
continue;
|
||||
}
|
||||
if (set_attribute_type(gpu_material, name, curves_infos, true)) {
|
||||
sub_ps.bind_texture(sampler_name, cache.evaluated_attributes_buf[i]);
|
||||
}
|
||||
if (name == uv_name) {
|
||||
if (set_attribute_type(gpu_material, "", curves_infos, true)) {
|
||||
sub_ps.bind_texture("a", cache.evaluated_attributes_buf[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!cache.curve_attributes_buf[i]) {
|
||||
continue;
|
||||
}
|
||||
if (set_attribute_type(gpu_material, name, curves_infos, false)) {
|
||||
sub_ps.bind_texture(sampler_name, cache.curve_attributes_buf[i]);
|
||||
}
|
||||
if (name == uv_name) {
|
||||
if (set_attribute_type(gpu_material, "", curves_infos, false)) {
|
||||
sub_ps.bind_texture("a", cache.curve_attributes_buf[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
curves_infos.half_cylinder_face_count = face_per_segment;
|
||||
curves_infos.vertex_per_segment = face_per_segment < 2 ? (face_per_segment + 1) :
|
||||
((face_per_segment + 1) * 2 + 1);
|
||||
|
||||
curves_infos.push_update();
|
||||
|
||||
sub_ps.bind_ubo("drw_curves", curves_infos);
|
||||
sub_ps.bind_texture("curves_pos_rad_buf", cache.evaluated_pos_rad_buf);
|
||||
sub_ps.bind_texture("curves_indirection_buf", indirection_buf);
|
||||
}
|
||||
|
||||
void curves_bind_resources(PassMain::Sub &sub_ps,
|
||||
CurvesModule &module,
|
||||
CurvesEvalCache &cache,
|
||||
const int face_per_segment,
|
||||
GPUMaterial *gpu_material,
|
||||
gpu::VertBufPtr &indirection_buf,
|
||||
const std::optional<StringRef> active_uv_name)
|
||||
{
|
||||
curves_bind_resources_implementation(
|
||||
sub_ps, module, cache, face_per_segment, gpu_material, indirection_buf, active_uv_name);
|
||||
}
|
||||
|
||||
void curves_bind_resources(PassSimple::Sub &sub_ps,
|
||||
CurvesModule &module,
|
||||
CurvesEvalCache &cache,
|
||||
const int face_per_segment,
|
||||
GPUMaterial *gpu_material,
|
||||
gpu::VertBufPtr &indirection_buf,
|
||||
const std::optional<StringRef> active_uv_name)
|
||||
{
|
||||
curves_bind_resources_implementation(
|
||||
sub_ps, module, cache, face_per_segment, gpu_material, indirection_buf, active_uv_name);
|
||||
}
|
||||
|
||||
template<typename PassT>
|
||||
gpu::Batch *curves_sub_pass_setup_implementation(PassT &sub_ps,
|
||||
const Scene *scene,
|
||||
Object *ob,
|
||||
const char *&r_error,
|
||||
GPUMaterial *gpu_material = nullptr)
|
||||
{
|
||||
BLI_assert(ob->type == OB_CURVES);
|
||||
Curves &curves_id = DRW_object_get_data_for_drawing<Curves>(*ob);
|
||||
const bke::CurvesGeometry &curves = curves_id.geometry.wrap();
|
||||
|
||||
const int face_per_segment = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 0 :
|
||||
(scene->r.hair_type == SCE_HAIR_SHAPE_CYLINDER) ? 3 :
|
||||
1;
|
||||
|
||||
CurvesEvalCache &curves_cache = curves_get_eval_cache(curves_id);
|
||||
|
||||
if (curves.curves_num() == 0) {
|
||||
/* Nothing to draw. Just return an empty drawcall that will be skipped. */
|
||||
bool unused_error = false;
|
||||
return curves_cache.batch_get(0, 0, face_per_segment, false, unused_error);
|
||||
}
|
||||
|
||||
CurvesModule &module = *drw_get().data->curves_module;
|
||||
|
||||
curves_cache.ensure_positions(module, curves);
|
||||
curves_cache.ensure_attributes(module, curves, gpu_material);
|
||||
|
||||
gpu::VertBufPtr &indirection_buf = curves_cache.indirection_buf_get(
|
||||
module, curves, face_per_segment);
|
||||
|
||||
const std::optional<StringRef> uv_name = get_first_uv_name(
|
||||
curves_id.geometry.wrap().attributes());
|
||||
|
||||
curves_bind_resources(
|
||||
sub_ps, module, curves_cache, face_per_segment, gpu_material, indirection_buf, uv_name);
|
||||
|
||||
bool error = false;
|
||||
gpu::Batch *batch = curves_cache.batch_get(curves.evaluated_points_num(),
|
||||
curves.curves_num(),
|
||||
face_per_segment,
|
||||
curves.has_cyclic_curve(),
|
||||
error);
|
||||
if (error) {
|
||||
r_error = RPT_(
|
||||
"Error: Curves object contains too many points. "
|
||||
"Reduce curve resolution or curve count to fix this issue.\n");
|
||||
}
|
||||
return batch;
|
||||
}
|
||||
|
||||
gpu::Batch *curves_sub_pass_setup(PassMain::Sub &ps,
|
||||
const Scene *scene,
|
||||
Object *ob,
|
||||
const char *&r_error,
|
||||
GPUMaterial *gpu_material)
|
||||
{
|
||||
return curves_sub_pass_setup_implementation(ps, scene, ob, r_error, gpu_material);
|
||||
}
|
||||
|
||||
gpu::Batch *curves_sub_pass_setup(PassSimple::Sub &ps,
|
||||
const Scene *scene,
|
||||
Object *ob,
|
||||
const char *&r_error,
|
||||
GPUMaterial *gpu_material)
|
||||
{
|
||||
return curves_sub_pass_setup_implementation(ps, scene, ob, r_error, gpu_material);
|
||||
}
|
||||
|
||||
} // namespace blender::draw
|
||||
@@ -0,0 +1,44 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*
|
||||
* List of defines that are shared with the GPUShaderCreateInfos. We do this to avoid
|
||||
* dragging larger headers into the createInfo pipeline which would cause problems.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/** Curves evaluation. */
|
||||
#define CURVES_PER_THREADGROUP 64
|
||||
|
||||
#define POINTS_BY_CURVES_SLOT 0
|
||||
#define CURVE_TYPE_SLOT 1
|
||||
#define CURVE_RESOLUTION_SLOT 2
|
||||
#define EVALUATED_POINT_SLOT 3
|
||||
#define CURVE_CYCLIC_SLOT 4
|
||||
|
||||
#define HANDLES_POS_LEFT_SLOT 5
|
||||
#define HANDLES_POS_RIGHT_SLOT 6
|
||||
#define BEZIER_OFFSETS_SLOT 7
|
||||
|
||||
/* Nurbs (alias of other buffers). */
|
||||
#define CURVES_ORDER_SLOT CURVE_RESOLUTION_SLOT
|
||||
#define BASIS_CACHE_SLOT HANDLES_POS_LEFT_SLOT
|
||||
#define CONTROL_WEIGHTS_SLOT HANDLES_POS_RIGHT_SLOT
|
||||
#define BASIS_CACHE_OFFSET_SLOT BEZIER_OFFSETS_SLOT
|
||||
|
||||
/* Position evaluation. */
|
||||
#define POINT_POSITIONS_SLOT 8
|
||||
#define POINT_RADII_SLOT 9
|
||||
#define EVALUATED_POS_RAD_SLOT 10
|
||||
|
||||
/* Attribute evaluation. */
|
||||
#define POINT_ATTR_SLOT 8
|
||||
#define EVALUATED_ATTR_SLOT 9
|
||||
|
||||
/* Intercept evaluation. */
|
||||
#define EVALUATED_TIME_SLOT 8
|
||||
#define CURVES_LENGTH_SLOT 9
|
||||
279
blender-5.2.0/source/blender/draw/intern/draw_curves_private.hh
Normal file
279
blender-5.2.0/source/blender/draw/intern/draw_curves_private.hh
Normal file
@@ -0,0 +1,279 @@
|
||||
/* SPDX-FileCopyrightText: 2017 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
|
||||
#include "GPU_shader.hh"
|
||||
#include "GPU_vertex_buffer.hh"
|
||||
|
||||
#include "BLI_vector_set.hh"
|
||||
|
||||
#include "draw_pass.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct Curves;
|
||||
struct Object;
|
||||
struct ParticleSystem;
|
||||
struct PTCacheEdit;
|
||||
struct ModifierData;
|
||||
struct ParticleCacheKey;
|
||||
namespace bke {
|
||||
class CurvesGeometry;
|
||||
} // namespace bke
|
||||
|
||||
namespace gpu {
|
||||
class Batch;
|
||||
class VertBuf;
|
||||
} // namespace gpu
|
||||
struct GPUMaterial;
|
||||
|
||||
namespace draw {
|
||||
|
||||
struct CurvesModule;
|
||||
|
||||
#define MAX_FACE_PER_SEGMENT 5
|
||||
#define MAX_HAIR_SUBDIV 4 /* see hair_subdiv rna */
|
||||
|
||||
enum CurvesEvalShader {
|
||||
CURVES_EVAL_POSITION = 0,
|
||||
CURVES_EVAL_FLOAT = 1,
|
||||
CURVES_EVAL_FLOAT2 = 2,
|
||||
CURVES_EVAL_FLOAT3 = 3,
|
||||
CURVES_EVAL_FLOAT4 = 4,
|
||||
CURVES_EVAL_LENGTH_INTERCEPT = 5,
|
||||
};
|
||||
|
||||
/* Legacy Hair Particle. */
|
||||
|
||||
struct ParticleSpans {
|
||||
Span<ParticleCacheKey *> parent;
|
||||
Span<ParticleCacheKey *> children;
|
||||
|
||||
void foreach_strand(FunctionRef<void(Span<ParticleCacheKey>)> callback);
|
||||
};
|
||||
|
||||
struct ParticleDrawSource {
|
||||
public:
|
||||
Object *object = nullptr;
|
||||
ParticleSystem *psys = nullptr;
|
||||
ModifierData *md = nullptr;
|
||||
PTCacheEdit *edit = nullptr;
|
||||
|
||||
private:
|
||||
Vector<int> &points_by_curve_storage_;
|
||||
Vector<int> &evaluated_points_by_curve_storage_;
|
||||
int additional_subdivision_;
|
||||
|
||||
public:
|
||||
ParticleDrawSource(Vector<int> &points_by_curve_storage,
|
||||
Vector<int> &evaluated_points_by_curve_storage,
|
||||
int additional_subdivision)
|
||||
: points_by_curve_storage_(points_by_curve_storage),
|
||||
evaluated_points_by_curve_storage_(evaluated_points_by_curve_storage),
|
||||
additional_subdivision_(additional_subdivision)
|
||||
{
|
||||
}
|
||||
|
||||
int curves_num()
|
||||
{
|
||||
if (points_by_curve_storage_.is_empty()) {
|
||||
points_by_curve();
|
||||
}
|
||||
return points_by_curve_storage_.size() - 1;
|
||||
}
|
||||
|
||||
int points_num()
|
||||
{
|
||||
if (points_by_curve_storage_.is_empty()) {
|
||||
points_by_curve();
|
||||
}
|
||||
return points_by_curve_storage_.last();
|
||||
}
|
||||
|
||||
int evaluated_points_num()
|
||||
{
|
||||
if (additional_subdivision_ == 0) {
|
||||
return points_num();
|
||||
}
|
||||
evaluated_points_by_curve();
|
||||
return evaluated_points_by_curve_storage_.last();
|
||||
}
|
||||
|
||||
int resolution()
|
||||
{
|
||||
return 1 << additional_subdivision_;
|
||||
}
|
||||
|
||||
OffsetIndices<int> points_by_curve();
|
||||
OffsetIndices<int> evaluated_points_by_curve();
|
||||
ParticleSpans particles_get();
|
||||
};
|
||||
|
||||
#define CURVES_EVAL_SHADER_NUM 5
|
||||
|
||||
/* Curves procedural display: Evaluation is done on the GPU. */
|
||||
struct CurvesEvalCache {
|
||||
/* --- Required attributes. --- */
|
||||
|
||||
/** Position and radius per evaluated point. Always evaluated. */
|
||||
gpu::VertBufPtr evaluated_pos_rad_buf;
|
||||
|
||||
/** Intercept time per evaluated point. */
|
||||
/* TODO(fclem): Move it to generic point domain attributes. */
|
||||
gpu::VertBufPtr evaluated_time_buf;
|
||||
/** Intercept time per curve. */
|
||||
/* TODO(fclem): Move it to generic curve domain attributes. */
|
||||
gpu::VertBufPtr curves_length_buf;
|
||||
|
||||
/* --- Indirection buffers. --- */
|
||||
|
||||
/* Map primitive to point ID and curve ID. Contains restart indices for line and triangle strip
|
||||
* primitive. */
|
||||
gpu::VertBufPtr indirection_ribbon_buf;
|
||||
/* Map primitive to point ID and curve ID. Compacted for cylinder primitive. */
|
||||
gpu::VertBufPtr indirection_cylinder_buf;
|
||||
|
||||
/* --- Buffers common to all curve types. --- */
|
||||
|
||||
/** Buffer containing `CurveGeometry::points_by_curve()`. */
|
||||
gpu::VertBufPtr points_by_curve_buf;
|
||||
/** Buffer containing `CurveGeometry::evaluated_points_by_curve()`. */
|
||||
gpu::VertBufPtr evaluated_points_by_curve_buf;
|
||||
/** Buffer containing `CurveGeometry::curve_types()`. */
|
||||
gpu::VertBufPtr curves_type_buf;
|
||||
/** Buffer containing `CurveGeometry::resolution()`. */
|
||||
gpu::VertBufPtr curves_resolution_buf;
|
||||
/** Buffer containing `CurveGeometry::cyclic_offsets()` or dummy data if not needed. */
|
||||
gpu::VertBufPtr curves_cyclic_buf;
|
||||
|
||||
/* --- Buffers only needed if geometry has Bezier curves. Dummy sized otherwise. --- */
|
||||
|
||||
/** Buffer containing `CurveGeometry::handle_positions_left()`. */
|
||||
gpu::VertBufPtr handles_positions_left_buf;
|
||||
/** Buffer containing `CurveGeometry::handle_positions_right()`. */
|
||||
gpu::VertBufPtr handles_positions_right_buf;
|
||||
/** Buffer containing `EvaluatedOffsets::all_bezier_offsets`. */
|
||||
gpu::VertBufPtr bezier_offsets_buf;
|
||||
|
||||
/* --- Buffers only needed if geometry has Nurbs curves. Dummy sized otherwise. --- */
|
||||
|
||||
/** Buffer containing `CurveGeometry::nurbs_orders()`. */
|
||||
gpu::VertBufPtr curves_order_buf;
|
||||
/** Buffer containing `CurveGeometry::nurbs_weights()`. */
|
||||
gpu::VertBufPtr control_weights_buf;
|
||||
/** Buffer containing all `nurbs::BasisCache` concatenated. */
|
||||
gpu::VertBufPtr basis_cache_buf;
|
||||
/** Buffer containing offsets to the start of each `nurbs::BasisCache` for each curve. */
|
||||
gpu::VertBufPtr basis_cache_offset_buf;
|
||||
|
||||
/* --- Generic Attributes. --- */
|
||||
|
||||
/** Attributes currently being drawn or about to be drawn. */
|
||||
VectorSet<std::string> attr_used;
|
||||
/**
|
||||
* Attributes that were used at some point. This is used for garbage collection, to remove
|
||||
* attributes that are not used in shaders anymore due to user edits.
|
||||
*/
|
||||
VectorSet<std::string> attr_used_over_time;
|
||||
/**
|
||||
* The last time in seconds that the `attr_used` and `attr_used_over_time` were exactly the same.
|
||||
* If the delta between this time and the current scene time is greater than the timeout set in
|
||||
* user preferences (`U.vbotimeout`) then garbage collection is performed.
|
||||
*/
|
||||
int last_attr_matching_time;
|
||||
/* Attributes stored per curve. Nullptr if attribute is not from this domain. */
|
||||
gpu::VertBufPtr curve_attributes_buf[GPU_MAX_ATTR];
|
||||
/* Output of the evaluation stage. This is only used by attributes on point domain. */
|
||||
gpu::VertBufPtr evaluated_attributes_buf[GPU_MAX_ATTR];
|
||||
/* If attribute is point domain, use evaluated_attributes_buf. Otherwise curve_attributes_buf. */
|
||||
std::array<bool, GPU_MAX_ATTR> attributes_point_domain;
|
||||
|
||||
/* --- Procedural Drawcalls. --- */
|
||||
std::array<gpu::Batch *, MAX_FACE_PER_SEGMENT> batch;
|
||||
|
||||
void ensure_attribute(struct CurvesModule &module,
|
||||
const bke::CurvesGeometry &curves,
|
||||
StringRef name,
|
||||
int index);
|
||||
void ensure_attributes(struct CurvesModule &module,
|
||||
const bke::CurvesGeometry &curves,
|
||||
const GPUMaterial *gpu_material);
|
||||
|
||||
void ensure_common(const bke::CurvesGeometry &curves);
|
||||
void ensure_bezier(const bke::CurvesGeometry &curves);
|
||||
void ensure_nurbs(const bke::CurvesGeometry &curves);
|
||||
|
||||
void ensure_positions(CurvesModule &module, const bke::CurvesGeometry &curves);
|
||||
|
||||
gpu::VertBufPtr &indirection_buf_get(CurvesModule &module,
|
||||
const bke::CurvesGeometry &curves,
|
||||
int face_per_segment);
|
||||
|
||||
/* Sets r_over_limit to true if reaching hardware limit for the number of segments. */
|
||||
gpu::Batch *batch_get(int evaluated_point_count,
|
||||
int curve_count,
|
||||
int face_per_segment,
|
||||
bool use_cyclic,
|
||||
bool &r_over_limit);
|
||||
|
||||
void discard_attributes();
|
||||
void clear();
|
||||
|
||||
/* --- Legacy Hair Particle system. --- */
|
||||
|
||||
int resolution = 0;
|
||||
|
||||
void ensure_attribute(CurvesModule &module,
|
||||
ParticleDrawSource &src,
|
||||
const Mesh &mesh,
|
||||
const StringRef name,
|
||||
const int index);
|
||||
void ensure_attributes(CurvesModule &module,
|
||||
ParticleDrawSource &src,
|
||||
const GPUMaterial *gpu_material);
|
||||
|
||||
void ensure_common(ParticleDrawSource &src);
|
||||
|
||||
void ensure_positions(CurvesModule &module, ParticleDrawSource &src);
|
||||
|
||||
gpu::VertBufPtr &indirection_buf_get(CurvesModule &module,
|
||||
ParticleDrawSource &src,
|
||||
int face_per_segment);
|
||||
|
||||
private:
|
||||
/* In the case where there is cyclic curves, add one padding point per curve to ensure easy
|
||||
* indexing in the drawing shader. */
|
||||
int evaluated_point_count_with_cyclic(const bke::CurvesGeometry &curves);
|
||||
};
|
||||
|
||||
CurvesEvalCache &curves_get_eval_cache(Curves &curves_id);
|
||||
|
||||
void drw_curves_get_attribute_sampler_name(StringRef layer_name, char r_sampler_name[32]);
|
||||
|
||||
void curves_bind_resources(draw::PassMain::Sub &sub_ps,
|
||||
CurvesModule &module,
|
||||
CurvesEvalCache &cache,
|
||||
const int face_per_segment,
|
||||
GPUMaterial *gpu_material,
|
||||
gpu::VertBufPtr &indirection_buf,
|
||||
std::optional<StringRef> active_uv_name);
|
||||
|
||||
void curves_bind_resources(draw::PassSimple::Sub &sub_ps,
|
||||
CurvesModule &module,
|
||||
CurvesEvalCache &cache,
|
||||
const int face_per_segment,
|
||||
GPUMaterial *gpu_material,
|
||||
gpu::VertBufPtr &indirection_buf,
|
||||
std::optional<StringRef> active_uv_name);
|
||||
|
||||
} // namespace draw
|
||||
} // namespace blender
|
||||
297
blender-5.2.0/source/blender/draw/intern/draw_debug.cc
Normal file
297
blender-5.2.0/source/blender/draw/intern/draw_debug.cc
Normal file
@@ -0,0 +1,297 @@
|
||||
/* SPDX-FileCopyrightText: 2018 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*
|
||||
* \brief Simple API to draw debug shapes in the viewport.
|
||||
*/
|
||||
|
||||
#include "BKE_object.hh"
|
||||
#include "BLI_math_bits.h"
|
||||
#include "BLI_math_matrix.h"
|
||||
#include "BLI_math_matrix.hh"
|
||||
#include "GPU_batch.hh"
|
||||
#include "GPU_debug.hh"
|
||||
|
||||
#include "draw_context_private.hh"
|
||||
#include "draw_debug.hh"
|
||||
#include "draw_shader.hh"
|
||||
#include "draw_shader_shared.hh"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Init and state
|
||||
* \{ */
|
||||
|
||||
void DebugDraw::reset()
|
||||
{
|
||||
for (int i = 0; i < 2; i++) {
|
||||
vertex_len_.store(0);
|
||||
|
||||
if (cpu_draw_buf_.current() == nullptr) {
|
||||
cpu_draw_buf_.current() = MEM_new<DebugDrawBuf>("DebugDrawBuf-CPU", "DebugDrawBuf-CPU");
|
||||
gpu_draw_buf_.current() = MEM_new<DebugDrawBuf>("DebugDrawBuf-GPU", "DebugDrawBuf-GPU");
|
||||
}
|
||||
|
||||
cpu_draw_buf_.current()->command.array().vertex_len = 0;
|
||||
cpu_draw_buf_.current()->command.array().vertex_first = 0;
|
||||
cpu_draw_buf_.current()->command.array().instance_len = 1;
|
||||
cpu_draw_buf_.current()->command.array().instance_first = 0;
|
||||
|
||||
gpu_draw_buf_.current()->command.array().vertex_len = 0;
|
||||
gpu_draw_buf_.current()->command.array().vertex_first = 0;
|
||||
gpu_draw_buf_.current()->command.array().instance_len = 1;
|
||||
gpu_draw_buf_.current()->command.array().instance_first = 0;
|
||||
gpu_draw_buf_.current()->push_update();
|
||||
|
||||
cpu_draw_buf_.swap();
|
||||
gpu_draw_buf_.swap();
|
||||
}
|
||||
|
||||
gpu_draw_buf_used = false;
|
||||
}
|
||||
|
||||
gpu::StorageBuf *DebugDraw::gpu_draw_buf_get()
|
||||
{
|
||||
#ifdef WITH_DRAW_DEBUG
|
||||
gpu_draw_buf_used = true;
|
||||
return *gpu_draw_buf_.current();
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
void DebugDraw::clear_gpu_data()
|
||||
{
|
||||
for (int i = 0; i < 2; i++) {
|
||||
MEM_SAFE_DELETE(cpu_draw_buf_.current());
|
||||
MEM_SAFE_DELETE(gpu_draw_buf_.current());
|
||||
|
||||
cpu_draw_buf_.swap();
|
||||
gpu_draw_buf_.swap();
|
||||
}
|
||||
}
|
||||
|
||||
void drw_debug_clear()
|
||||
{
|
||||
DebugDraw::get().reset();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Draw functions
|
||||
* \{ */
|
||||
|
||||
void drw_debug_line(const float3 v1, const float3 v2, const float4 color, const uint lifetime)
|
||||
{
|
||||
DebugDraw &dd = DebugDraw::get();
|
||||
dd.draw_line(v1, v2, debug_color_pack(color), lifetime);
|
||||
}
|
||||
|
||||
void drw_debug_polygon(Span<float3> face_verts, const float4 color, const uint lifetime)
|
||||
{
|
||||
BLI_assert(!face_verts.is_empty());
|
||||
DebugDraw &dd = DebugDraw::get();
|
||||
uint col = debug_color_pack(color);
|
||||
float3 v0 = face_verts.last();
|
||||
for (auto vert : face_verts) {
|
||||
float3 v1 = vert;
|
||||
dd.draw_line(v0, v1, col, lifetime);
|
||||
v0 = v1;
|
||||
}
|
||||
}
|
||||
|
||||
void drw_debug_bbox(const BoundBox &bbox, const float4 color, const uint lifetime)
|
||||
{
|
||||
DebugDraw &dd = DebugDraw::get();
|
||||
uint col = debug_color_pack(color);
|
||||
dd.draw_line(bbox.vec[0], bbox.vec[1], col, lifetime);
|
||||
dd.draw_line(bbox.vec[1], bbox.vec[2], col, lifetime);
|
||||
dd.draw_line(bbox.vec[2], bbox.vec[3], col, lifetime);
|
||||
dd.draw_line(bbox.vec[3], bbox.vec[0], col, lifetime);
|
||||
|
||||
dd.draw_line(bbox.vec[4], bbox.vec[5], col, lifetime);
|
||||
dd.draw_line(bbox.vec[5], bbox.vec[6], col, lifetime);
|
||||
dd.draw_line(bbox.vec[6], bbox.vec[7], col, lifetime);
|
||||
dd.draw_line(bbox.vec[7], bbox.vec[4], col, lifetime);
|
||||
|
||||
dd.draw_line(bbox.vec[0], bbox.vec[4], col, lifetime);
|
||||
dd.draw_line(bbox.vec[1], bbox.vec[5], col, lifetime);
|
||||
dd.draw_line(bbox.vec[2], bbox.vec[6], col, lifetime);
|
||||
dd.draw_line(bbox.vec[3], bbox.vec[7], col, lifetime);
|
||||
}
|
||||
|
||||
static Vector<float3> precompute_sphere_points(int circle_resolution)
|
||||
{
|
||||
Vector<float3> result;
|
||||
for (auto axis : IndexRange(3)) {
|
||||
for (auto edge : IndexRange(circle_resolution)) {
|
||||
for (auto vert : IndexRange(2)) {
|
||||
const float angle = (2 * M_PI) * (edge + vert) / float(circle_resolution);
|
||||
const float point[3] = {cosf(angle), sinf(angle), 0.0f};
|
||||
result.append(float3(point[(0 + axis) % 3], point[(1 + axis) % 3], point[(2 + axis) % 3]));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void drw_debug_sphere(const float3 center, float radius, const float4 color, const uint lifetime)
|
||||
{
|
||||
/** Precomputed shapes verts. */
|
||||
static Vector<float3> sphere_verts = precompute_sphere_points(16);
|
||||
|
||||
DebugDraw &dd = DebugDraw::get();
|
||||
uint col = debug_color_pack(color);
|
||||
for (auto i : IndexRange(sphere_verts.size() / 2)) {
|
||||
float3 v0 = sphere_verts[i * 2] * radius + center;
|
||||
float3 v1 = sphere_verts[i * 2 + 1] * radius + center;
|
||||
dd.draw_line(v0, v1, col, lifetime);
|
||||
}
|
||||
}
|
||||
|
||||
void drw_debug_point(const float3 pos, float rad, const float4 col, const uint lifetime)
|
||||
{
|
||||
static Vector<float3> point_verts = precompute_sphere_points(4);
|
||||
|
||||
DebugDraw &dd = DebugDraw::get();
|
||||
uint color = debug_color_pack(col);
|
||||
for (auto i : IndexRange(point_verts.size() / 2)) {
|
||||
float3 v0 = point_verts[i * 2] * rad + pos;
|
||||
float3 v1 = point_verts[i * 2 + 1] * rad + pos;
|
||||
dd.draw_line(v0, v1, color, lifetime);
|
||||
}
|
||||
}
|
||||
|
||||
void drw_debug_matrix(const float4x4 &m4, const uint lifetime)
|
||||
{
|
||||
float3 v0 = math::transform_point(m4, float3(0.0f, 0.0f, 0.0f));
|
||||
float3 v1 = math::transform_point(m4, float3(1.0f, 0.0f, 0.0f));
|
||||
float3 v2 = math::transform_point(m4, float3(0.0f, 1.0f, 0.0f));
|
||||
float3 v3 = math::transform_point(m4, float3(0.0f, 0.0f, 1.0f));
|
||||
|
||||
DebugDraw &dd = DebugDraw::get();
|
||||
dd.draw_line(v0, v1, debug_color_pack(float4(1.0f, 0.0f, 0.0f, 1.0f)), lifetime);
|
||||
dd.draw_line(v0, v2, debug_color_pack(float4(0.0f, 1.0f, 0.0f, 1.0f)), lifetime);
|
||||
dd.draw_line(v0, v3, debug_color_pack(float4(0.0f, 0.0f, 1.0f, 1.0f)), lifetime);
|
||||
}
|
||||
|
||||
void drw_debug_matrix_as_bbox(const float4x4 &mat, const float4 color, const uint lifetime)
|
||||
{
|
||||
BoundBox bb;
|
||||
std::array<float3, 8> corners = bounds::corners(Bounds<float3>(float3(-1), float3(1)));
|
||||
for (auto i : IndexRange(8)) {
|
||||
mul_project_m4_v3(mat.ptr(), corners[i]);
|
||||
}
|
||||
drw_debug_bbox(bb, color, lifetime);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Internals
|
||||
*
|
||||
* \{ */
|
||||
|
||||
void DebugDraw::draw_line(float3 v1, float3 v2, uint color, const uint lifetime)
|
||||
{
|
||||
DebugDrawBuf &buf = *cpu_draw_buf_.current();
|
||||
uint index = vertex_len_.fetch_add(2);
|
||||
if (index + 2 < DRW_DEBUG_DRAW_VERT_MAX) {
|
||||
buf.verts[index / 2] = debug_line_make(float_as_uint(v1.x),
|
||||
float_as_uint(v1.y),
|
||||
float_as_uint(v1.z),
|
||||
float_as_uint(v2.x),
|
||||
float_as_uint(v2.y),
|
||||
float_as_uint(v2.z),
|
||||
color,
|
||||
lifetime);
|
||||
buf.command.array().vertex_len += 2;
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Display
|
||||
* \{ */
|
||||
|
||||
void DebugDraw::display_lines(View &view)
|
||||
{
|
||||
const bool cpu_draw_buf_used = vertex_len_.load() != 0;
|
||||
|
||||
if (!cpu_draw_buf_used && !gpu_draw_buf_used) {
|
||||
return;
|
||||
}
|
||||
|
||||
command::StateSet::set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS);
|
||||
|
||||
float viewport_size[4];
|
||||
GPU_viewport_size_get_f(viewport_size);
|
||||
|
||||
gpu::Batch *batch = GPU_batch_procedural_lines_get();
|
||||
gpu::Shader *shader = DRW_shader_debug_draw_display_get();
|
||||
GPU_batch_set_shader(batch, shader);
|
||||
GPU_shader_uniform_mat4(shader, "persmat", view.persmat().ptr());
|
||||
GPU_shader_uniform_2f(shader, "size_viewport", viewport_size[2], viewport_size[3]);
|
||||
|
||||
if (gpu_draw_buf_used) {
|
||||
GPU_debug_group_begin("GPU");
|
||||
/* Reset buffer. */
|
||||
gpu_draw_buf_.next()->command.array().vertex_len = 0;
|
||||
gpu_draw_buf_.next()->push_update();
|
||||
|
||||
GPU_storagebuf_bind(*gpu_draw_buf_.current(), DRW_DEBUG_DRAW_SLOT);
|
||||
GPU_storagebuf_bind(*gpu_draw_buf_.next(), DRW_DEBUG_DRAW_FEEDBACK_SLOT);
|
||||
GPU_batch_draw_indirect(batch, *gpu_draw_buf_.current(), 0);
|
||||
GPU_storagebuf_unbind(*gpu_draw_buf_.current());
|
||||
GPU_storagebuf_unbind(*gpu_draw_buf_.next());
|
||||
GPU_debug_group_end();
|
||||
}
|
||||
|
||||
{
|
||||
GPU_debug_group_begin("CPU");
|
||||
/* We might have race condition here (a writer thread might still be outputting vertices).
|
||||
* But that is ok. At worse, we will be missing some vertex data and show 1 corrupted line. */
|
||||
cpu_draw_buf_.current()->command.array().vertex_len = vertex_len_.load();
|
||||
cpu_draw_buf_.current()->push_update();
|
||||
/* Reset buffer. */
|
||||
cpu_draw_buf_.next()->command.array().vertex_len = 0;
|
||||
cpu_draw_buf_.next()->push_update();
|
||||
|
||||
GPU_storagebuf_bind(*cpu_draw_buf_.current(), DRW_DEBUG_DRAW_SLOT);
|
||||
GPU_storagebuf_bind(*cpu_draw_buf_.next(), DRW_DEBUG_DRAW_FEEDBACK_SLOT);
|
||||
GPU_batch_draw_indirect(batch, *cpu_draw_buf_.current(), 0);
|
||||
GPU_storagebuf_unbind(*cpu_draw_buf_.current());
|
||||
GPU_storagebuf_unbind(*cpu_draw_buf_.next());
|
||||
|
||||
/* Read result of lifetime management. */
|
||||
cpu_draw_buf_.next()->read();
|
||||
vertex_len_.store(
|
||||
min_ii(DRW_DEBUG_DRAW_VERT_MAX, cpu_draw_buf_.next()->command.array().vertex_len));
|
||||
GPU_debug_group_end();
|
||||
}
|
||||
|
||||
gpu_draw_buf_.swap();
|
||||
cpu_draw_buf_.swap();
|
||||
}
|
||||
|
||||
void DebugDraw::display_to_view(View &view)
|
||||
{
|
||||
/* Display only on the main thread. Avoid concurrent usage of the resource. */
|
||||
BLI_assert(BLI_thread_is_main());
|
||||
|
||||
GPU_debug_group_begin("DebugDraw");
|
||||
|
||||
display_lines(view);
|
||||
|
||||
GPU_debug_group_end();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::draw
|
||||
145
blender-5.2.0/source/blender/draw/intern/draw_debug.hh
Normal file
145
blender-5.2.0/source/blender/draw/intern/draw_debug.hh
Normal file
@@ -0,0 +1,145 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*
|
||||
* \brief Simple API to draw debug shapes and log in the viewport.
|
||||
*
|
||||
* Both CPU and GPU implementation are supported and symmetrical (meaning GPU shader can use it
|
||||
* too, see common_draw_lib.glsl).
|
||||
*
|
||||
* NOTE: CPU logging will overlap GPU logging on screen as it is drawn after.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_mutex.hh"
|
||||
|
||||
#include "DNA_object_types.h"
|
||||
|
||||
#include "draw_shader_shared.hh"
|
||||
|
||||
#include "DRW_gpu_wrapper.hh"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
class View;
|
||||
|
||||
/**
|
||||
* Clear all debug visuals (regardless of visual's lifetime).
|
||||
*
|
||||
* Usually called before populating persistent data to override previous visuals.
|
||||
* Needs an active GPUContext.
|
||||
*/
|
||||
void drw_debug_clear();
|
||||
|
||||
/* Used for virtually infinite lifetime.
|
||||
* Useful for debugging render or baking jobs, or non-modal operators. */
|
||||
constexpr uint drw_debug_persistent_lifetime = ~0u;
|
||||
|
||||
/**
|
||||
* Drawing functions that will draw wire-frames with the given color.
|
||||
*
|
||||
* IMPORTANT: `lifetime` is in unit of **display** and not in unit of time.
|
||||
* One display is defined as one call to `DebugDraw::display_to_view` which happens once
|
||||
* per 3D viewport if overlays are not turned off.
|
||||
*
|
||||
* - The default value of 1 is good for continuous event debugging in one viewport.
|
||||
* - Above 1 is a good value for infrequent events or to compare continuous event history.
|
||||
* Alternatively also allows replicating the display to several viewport.
|
||||
* - drw_debug_persistent_lifetime is a good value for manually triggered event (e.g. an operator).
|
||||
* It is best to clear the display cache (using `drw_debug_clear`) before adding new persistent
|
||||
* visuals.
|
||||
*
|
||||
* All added debug drawing will be shared across viewports. If lifetime is greater than 1 or if a
|
||||
* viewport doesn't display the visuals it produced, the visuals will be displayed onto other
|
||||
* viewport(s).
|
||||
*
|
||||
* These functions are threadsafe and can be called concurrently at anytime, even outside the
|
||||
* UI redraw loop.
|
||||
*/
|
||||
|
||||
void drw_debug_line(float3 v1, float3 v2, float4 color = {1, 0, 0, 1}, uint lifetime = 1);
|
||||
|
||||
void drw_debug_polygon(Span<float3> face_verts, float4 color = {1, 0, 0, 1}, uint lifetime = 1);
|
||||
|
||||
void drw_debug_bbox(const BoundBox &bbox, float4 color = {1, 0, 0, 1}, uint lifetime = 1);
|
||||
|
||||
void drw_debug_sphere(float3 center, float radius, float4 color = {1, 0, 0, 1}, uint lifetime = 1);
|
||||
/** Same as drw_debug_sphere but with small default radius. */
|
||||
void drw_debug_point(float3 pos, float rad = 0.01f, float4 col = {1, 0, 0, 1}, uint lifetime = 1);
|
||||
/** Draw a matrix transform as 3 colored axes. */
|
||||
void drw_debug_matrix(const float4x4 &m4, uint lifetime = 1);
|
||||
/** Draw a matrix as a 2 units length bounding box, centered on origin. */
|
||||
void drw_debug_matrix_as_bbox(const float4x4 &mat, float4 color = {1, 0, 0, 1}, uint lifetime = 1);
|
||||
|
||||
class DebugDraw {
|
||||
private:
|
||||
using DebugDrawBuf = StorageBuffer<DRWDebugDrawBuffer>;
|
||||
|
||||
/**
|
||||
* Ensure thread-safety when adding geometry to the CPU debug buffer.
|
||||
* GPU debug buffer currently expects draw submission to be externally synchronized.
|
||||
*/
|
||||
std::atomic<int> vertex_len_;
|
||||
/** Data buffers containing all verts or chars to draw. */
|
||||
SwapChain<DebugDrawBuf *, 2> cpu_draw_buf_ = {};
|
||||
SwapChain<DebugDrawBuf *, 2> gpu_draw_buf_ = {};
|
||||
/** True if the gpu buffer have been requested and may contain data to draw. */
|
||||
bool gpu_draw_buf_used = false;
|
||||
|
||||
/* Reference counter used by GPUContext to allow freeing of DebugDrawBuf before the last
|
||||
* context is destroyed. */
|
||||
int ref_count_ = 0;
|
||||
Mutex ref_count_mutex_;
|
||||
|
||||
public:
|
||||
void reset();
|
||||
|
||||
/**
|
||||
* Draw all debug shapes to the given current view / frame-buffer.
|
||||
* Draw buffers will be emptied and ready for new debug data.
|
||||
*/
|
||||
void display_to_view(View &view);
|
||||
|
||||
/** Get GPU debug draw buffer. Can, return nullptr if WITH_DRAW_DEBUG is not enabled. */
|
||||
gpu::StorageBuf *gpu_draw_buf_get();
|
||||
|
||||
void acquire()
|
||||
{
|
||||
std::scoped_lock lock(ref_count_mutex_);
|
||||
ref_count_++;
|
||||
if (ref_count_ == 1) {
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
void release()
|
||||
{
|
||||
std::scoped_lock lock(ref_count_mutex_);
|
||||
ref_count_--;
|
||||
if (ref_count_ == 0) {
|
||||
clear_gpu_data();
|
||||
}
|
||||
}
|
||||
|
||||
static DebugDraw &get()
|
||||
{
|
||||
static DebugDraw module;
|
||||
return module;
|
||||
}
|
||||
|
||||
void draw_line(float3 v1, float3 v2, uint color, uint lifetime = 1);
|
||||
|
||||
static uint color_pack(float4 color);
|
||||
|
||||
private:
|
||||
void display_lines(View &view);
|
||||
|
||||
void clear_gpu_data();
|
||||
};
|
||||
|
||||
} // namespace blender::draw
|
||||
50
blender-5.2.0/source/blender/draw/intern/draw_defines.hh
Normal file
50
blender-5.2.0/source/blender/draw/intern/draw_defines.hh
Normal file
@@ -0,0 +1,50 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*
|
||||
* List of defines that are shared with the GPUShaderCreateInfos. We do this to avoid
|
||||
* dragging larger headers into the createInfo pipeline which would cause problems.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/* We target hardware with at least 12 UBO slots (Guaranteed by GL 4.3). */
|
||||
#define DRW_VIEW_UBO_SLOT 11
|
||||
#define DRW_VIEW_CULLING_UBO_SLOT 10
|
||||
#define DRW_OBJ_DATA_INFO_UBO_SLOT 9
|
||||
#define DRW_LAYER_ATTR_UBO_SLOT 7
|
||||
/* Slots 0-6 are reserved for engine use. */
|
||||
/* TODO(fclem): Legacy. To be removed once we remove the old DRW. */
|
||||
#define DRW_OBJ_INFOS_UBO_SLOT 6
|
||||
/* TODO(fclem): Remove in favor of engine-side clipping UBO. */
|
||||
#define DRW_CLIPPING_UBO_SLOT 5
|
||||
|
||||
/* We target hardware with at least 12 SSBO slots (NOT Guaranteed by GL 4.3). */
|
||||
#define DRW_RESOURCE_ID_SLOT 11
|
||||
#define DRW_OBJ_MAT_SLOT 10
|
||||
#define DRW_OBJ_INFOS_SLOT 9
|
||||
#define DRW_OBJ_ATTR_SLOT 8
|
||||
/* Slots 0-7 are reserved for engine use. */
|
||||
/* Debug SSBOs are not counted in the limit [12 - 15+]. */
|
||||
#define DRW_DEBUG_DRAW_SLOT 14
|
||||
#define DRW_DEBUG_DRAW_FEEDBACK_SLOT 15
|
||||
|
||||
#define DRW_COMMAND_GROUP_SIZE 64
|
||||
#define DRW_FINALIZE_GROUP_SIZE 64
|
||||
/* Must be multiple of 32. Set to 32 for shader simplicity. */
|
||||
#define DRW_VISIBILITY_GROUP_SIZE 32
|
||||
|
||||
/**
|
||||
* The maximum of indexable views is dictated by:
|
||||
* - The UBO limit (16KiB) of the ViewMatrices container.
|
||||
* - The maximum resource index supported for shaders using multi-view (see DRW_VIEW_SHIFT).
|
||||
*/
|
||||
#define DRW_VIEW_MAX 64
|
||||
|
||||
#define DRW_POINTCLOUD_STRIP_TILE_SIZE 8
|
||||
|
||||
/* TODO(fclem): Move to overlay. */
|
||||
#define OVERLAY_GLOBALS_SLOT 7
|
||||
610
blender-5.2.0/source/blender/draw/intern/draw_fluid.cc
Normal file
610
blender-5.2.0/source/blender/draw/intern/draw_fluid.cc
Normal file
@@ -0,0 +1,610 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* GPU fluid drawing functions.
|
||||
*/
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "DNA_fluid_types.h"
|
||||
#include "DNA_modifier_types.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BKE_colorband.hh"
|
||||
|
||||
#include "IMB_colormanagement.hh"
|
||||
|
||||
#include "GPU_texture.hh"
|
||||
|
||||
#include "draw_context_private.hh"
|
||||
|
||||
#include "draw_common_c.hh" /* Own include. */
|
||||
|
||||
#ifdef WITH_FLUID
|
||||
# include "manta_fluid_API.h"
|
||||
#endif
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Private API
|
||||
* \{ */
|
||||
|
||||
#ifdef WITH_FLUID
|
||||
|
||||
enum {
|
||||
TFUNC_FLAME_SPECTRUM = 0,
|
||||
TFUNC_COLOR_RAMP = 1,
|
||||
};
|
||||
|
||||
# define TFUNC_WIDTH 256
|
||||
|
||||
static void create_flame_spectrum_texture(float *data)
|
||||
{
|
||||
# define FIRE_THRESH 7
|
||||
# define MAX_FIRE_ALPHA 0.06f
|
||||
# define FULL_ON_FIRE 100
|
||||
|
||||
float *spec_pixels = MEM_new_array_uninitialized<float>(TFUNC_WIDTH * 4 * 16 * 16,
|
||||
"spec_pixels");
|
||||
|
||||
IMB_colormanagement_blackbody_temperature_to_rgb_table(data, TFUNC_WIDTH, 1500, 3000);
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
for (int j = 0; j < 16; j++) {
|
||||
for (int k = 0; k < TFUNC_WIDTH; k++) {
|
||||
int index = (j * TFUNC_WIDTH * 16 + i * TFUNC_WIDTH + k) * 4;
|
||||
if (k >= FIRE_THRESH) {
|
||||
spec_pixels[index] = (data[k * 4]);
|
||||
spec_pixels[index + 1] = (data[k * 4 + 1]);
|
||||
spec_pixels[index + 2] = (data[k * 4 + 2]);
|
||||
spec_pixels[index + 3] = MAX_FIRE_ALPHA *
|
||||
((k > FULL_ON_FIRE) ?
|
||||
1.0f :
|
||||
(k - FIRE_THRESH) / (float(FULL_ON_FIRE) - FIRE_THRESH));
|
||||
}
|
||||
else {
|
||||
zero_v4(&spec_pixels[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(data, spec_pixels, sizeof(float) * 4 * TFUNC_WIDTH);
|
||||
|
||||
MEM_delete(spec_pixels);
|
||||
|
||||
# undef FIRE_THRESH
|
||||
# undef MAX_FIRE_ALPHA
|
||||
# undef FULL_ON_FIRE
|
||||
}
|
||||
|
||||
static void create_color_ramp(const ColorBand *coba, float *data)
|
||||
{
|
||||
for (int i = 0; i < TFUNC_WIDTH; i++) {
|
||||
BKE_colorband_evaluate(coba, float(i) / TFUNC_WIDTH, &data[i * 4]);
|
||||
straight_to_premul_v4(&data[i * 4]);
|
||||
}
|
||||
}
|
||||
|
||||
static gpu::Texture *create_transfer_function(int type, const ColorBand *coba)
|
||||
{
|
||||
float *data = MEM_new_array_uninitialized<float>(4 * TFUNC_WIDTH, __func__);
|
||||
|
||||
switch (type) {
|
||||
case TFUNC_FLAME_SPECTRUM:
|
||||
create_flame_spectrum_texture(data);
|
||||
break;
|
||||
case TFUNC_COLOR_RAMP:
|
||||
create_color_ramp(coba, data);
|
||||
break;
|
||||
}
|
||||
|
||||
gpu::Texture *tex = GPU_texture_create_1d("transf_func",
|
||||
TFUNC_WIDTH,
|
||||
1,
|
||||
gpu::TextureFormat::SRGBA_8_8_8_8,
|
||||
GPU_TEXTURE_USAGE_SHADER_READ,
|
||||
data);
|
||||
|
||||
MEM_delete(data);
|
||||
|
||||
return tex;
|
||||
}
|
||||
|
||||
static void swizzle_texture_channel_single(gpu::Texture *tex)
|
||||
{
|
||||
/* Swizzle texture channels so that we get useful RGBA values when sampling
|
||||
* a texture with fewer channels, e.g. when using density as color. */
|
||||
GPU_texture_swizzle_set(tex, "rrr1");
|
||||
}
|
||||
|
||||
static float *rescale_3d(const int dim[3],
|
||||
const int final_dim[3],
|
||||
int channels,
|
||||
const float *fpixels)
|
||||
{
|
||||
const uint w = dim[0], h = dim[1], d = dim[2];
|
||||
const uint fw = final_dim[0], fh = final_dim[1], fd = final_dim[2];
|
||||
const uint xf = w / fw, yf = h / fh, zf = d / fd;
|
||||
const uint pixel_count = fw * fh * fd;
|
||||
float *nfpixels = MEM_new_array_uninitialized<float>(channels * pixel_count, __func__);
|
||||
|
||||
if (nfpixels) {
|
||||
printf("Performance: You need to scale a 3D texture, feel the pain!\n");
|
||||
|
||||
for (uint k = 0; k < fd; k++) {
|
||||
for (uint j = 0; j < fh; j++) {
|
||||
for (uint i = 0; i < fw; i++) {
|
||||
/* Obviously doing nearest filtering here,
|
||||
* it's going to be slow in any case, let's not make it worse. */
|
||||
float xb = i * xf;
|
||||
float yb = j * yf;
|
||||
float zb = k * zf;
|
||||
uint offset = k * (fw * fh) + i * fh + j;
|
||||
uint offset_orig = (zb) * (w * h) + (xb)*h + (yb);
|
||||
|
||||
if (channels == 4) {
|
||||
nfpixels[offset * 4] = fpixels[offset_orig * 4];
|
||||
nfpixels[offset * 4 + 1] = fpixels[offset_orig * 4 + 1];
|
||||
nfpixels[offset * 4 + 2] = fpixels[offset_orig * 4 + 2];
|
||||
nfpixels[offset * 4 + 3] = fpixels[offset_orig * 4 + 3];
|
||||
}
|
||||
else if (channels == 1) {
|
||||
nfpixels[offset] = fpixels[offset_orig];
|
||||
}
|
||||
else {
|
||||
BLI_assert(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nfpixels;
|
||||
}
|
||||
|
||||
/* Will resize input to fit GL system limits. */
|
||||
static gpu::Texture *create_volume_texture(const int dim[3],
|
||||
gpu::TextureFormat texture_format,
|
||||
eGPUDataFormat data_format,
|
||||
const void *data)
|
||||
{
|
||||
gpu::Texture *tex = nullptr;
|
||||
int3 final_dim = {UNPACK3(dim)};
|
||||
|
||||
if (data == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
tex = GPU_texture_create_3d(
|
||||
"volume", UNPACK3(final_dim), 1, texture_format, GPU_TEXTURE_USAGE_SHADER_READ, nullptr);
|
||||
|
||||
if (tex != nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (final_dim[0] == 1 && final_dim[1] == 1 && final_dim[2] == 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
final_dim[i] = max_ii(1, final_dim[i] / 2);
|
||||
}
|
||||
}
|
||||
|
||||
if (tex == nullptr) {
|
||||
printf("Error: Could not create 3D texture.\n");
|
||||
tex = GPU_texture_create_error(3, false);
|
||||
}
|
||||
else if (int3(dim) == final_dim) {
|
||||
/* No need to resize, just upload the data. */
|
||||
GPU_texture_update_sub(tex, data_format, data, 0, 0, 0, UNPACK3(final_dim));
|
||||
}
|
||||
else if (data_format != GPU_DATA_FLOAT) {
|
||||
printf("Error: Could not allocate 3D texture and not attempting to rescale non-float data.\n");
|
||||
tex = GPU_texture_create_error(3, false);
|
||||
}
|
||||
else {
|
||||
/* We need to resize the input. */
|
||||
int channels = ELEM(texture_format,
|
||||
gpu::TextureFormat::UNORM_8,
|
||||
gpu::TextureFormat::SFLOAT_16,
|
||||
gpu::TextureFormat::SFLOAT_32) ?
|
||||
1 :
|
||||
4;
|
||||
float *rescaled_data = rescale_3d(dim, final_dim, channels, static_cast<const float *>(data));
|
||||
if (rescaled_data) {
|
||||
GPU_texture_update_sub(tex, GPU_DATA_FLOAT, rescaled_data, 0, 0, 0, UNPACK3(final_dim));
|
||||
MEM_delete(rescaled_data);
|
||||
}
|
||||
else {
|
||||
printf("Error: Could not allocate rescaled 3d texture!\n");
|
||||
GPU_texture_free(tex);
|
||||
tex = GPU_texture_create_error(3, false);
|
||||
}
|
||||
}
|
||||
return tex;
|
||||
}
|
||||
|
||||
static gpu::Texture *create_field_texture(FluidDomainSettings *fds, bool single_precision)
|
||||
{
|
||||
void *field = nullptr;
|
||||
eGPUDataFormat data_format = GPU_DATA_FLOAT;
|
||||
gpu::TextureFormat texture_format = gpu::TextureFormat::UNORM_8;
|
||||
|
||||
if (single_precision) {
|
||||
texture_format = gpu::TextureFormat::SFLOAT_32;
|
||||
}
|
||||
|
||||
switch (fds->coba_field) {
|
||||
case FLUID_DOMAIN_FIELD_DENSITY:
|
||||
field = manta_smoke_get_density(fds->fluid);
|
||||
break;
|
||||
case FLUID_DOMAIN_FIELD_HEAT:
|
||||
field = manta_smoke_get_heat(fds->fluid);
|
||||
break;
|
||||
case FLUID_DOMAIN_FIELD_FUEL:
|
||||
field = manta_smoke_get_fuel(fds->fluid);
|
||||
break;
|
||||
case FLUID_DOMAIN_FIELD_REACT:
|
||||
field = manta_smoke_get_react(fds->fluid);
|
||||
break;
|
||||
case FLUID_DOMAIN_FIELD_FLAME:
|
||||
field = manta_smoke_get_flame(fds->fluid);
|
||||
break;
|
||||
case FLUID_DOMAIN_FIELD_VELOCITY_X:
|
||||
field = manta_get_velocity_x(fds->fluid);
|
||||
break;
|
||||
case FLUID_DOMAIN_FIELD_VELOCITY_Y:
|
||||
field = manta_get_velocity_y(fds->fluid);
|
||||
break;
|
||||
case FLUID_DOMAIN_FIELD_VELOCITY_Z:
|
||||
field = manta_get_velocity_z(fds->fluid);
|
||||
break;
|
||||
case FLUID_DOMAIN_FIELD_COLOR_R:
|
||||
field = manta_smoke_get_color_r(fds->fluid);
|
||||
break;
|
||||
case FLUID_DOMAIN_FIELD_COLOR_G:
|
||||
field = manta_smoke_get_color_g(fds->fluid);
|
||||
break;
|
||||
case FLUID_DOMAIN_FIELD_COLOR_B:
|
||||
field = manta_smoke_get_color_b(fds->fluid);
|
||||
break;
|
||||
case FLUID_DOMAIN_FIELD_FORCE_X:
|
||||
field = manta_get_force_x(fds->fluid);
|
||||
break;
|
||||
case FLUID_DOMAIN_FIELD_FORCE_Y:
|
||||
field = manta_get_force_y(fds->fluid);
|
||||
break;
|
||||
case FLUID_DOMAIN_FIELD_FORCE_Z:
|
||||
field = manta_get_force_z(fds->fluid);
|
||||
break;
|
||||
case FLUID_DOMAIN_FIELD_PHI:
|
||||
field = manta_get_phi(fds->fluid);
|
||||
texture_format = gpu::TextureFormat::SFLOAT_16;
|
||||
break;
|
||||
case FLUID_DOMAIN_FIELD_PHI_IN:
|
||||
field = manta_get_phi_in(fds->fluid);
|
||||
texture_format = gpu::TextureFormat::SFLOAT_16;
|
||||
break;
|
||||
case FLUID_DOMAIN_FIELD_PHI_OUT:
|
||||
field = manta_get_phiout_in(fds->fluid);
|
||||
texture_format = gpu::TextureFormat::SFLOAT_16;
|
||||
break;
|
||||
case FLUID_DOMAIN_FIELD_PHI_OBSTACLE:
|
||||
field = manta_get_phiobs_in(fds->fluid);
|
||||
texture_format = gpu::TextureFormat::SFLOAT_16;
|
||||
break;
|
||||
case FLUID_DOMAIN_FIELD_FLAGS:
|
||||
field = manta_smoke_get_flags(fds->fluid);
|
||||
data_format = GPU_DATA_INT;
|
||||
texture_format = gpu::TextureFormat::UINT_8;
|
||||
break;
|
||||
case FLUID_DOMAIN_FIELD_PRESSURE:
|
||||
field = manta_get_pressure(fds->fluid);
|
||||
texture_format = gpu::TextureFormat::SFLOAT_16;
|
||||
break;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (field == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
gpu::Texture *tex = create_volume_texture(fds->res, texture_format, data_format, field);
|
||||
swizzle_texture_channel_single(tex);
|
||||
return tex;
|
||||
}
|
||||
|
||||
static gpu::Texture *create_density_texture(FluidDomainSettings *fds, int highres)
|
||||
{
|
||||
int *dim = (highres) ? fds->res_noise : fds->res;
|
||||
|
||||
float *data;
|
||||
if (highres) {
|
||||
data = manta_noise_get_density(fds->fluid);
|
||||
}
|
||||
else {
|
||||
data = manta_smoke_get_density(fds->fluid);
|
||||
}
|
||||
|
||||
if (data == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
gpu::Texture *tex = create_volume_texture(
|
||||
dim, gpu::TextureFormat::UNORM_8, GPU_DATA_FLOAT, data);
|
||||
swizzle_texture_channel_single(tex);
|
||||
return tex;
|
||||
}
|
||||
|
||||
static gpu::Texture *create_color_texture(FluidDomainSettings *fds, int highres)
|
||||
{
|
||||
const bool has_color = (highres) ? manta_noise_has_colors(fds->fluid) :
|
||||
manta_smoke_has_colors(fds->fluid);
|
||||
|
||||
if (!has_color) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int cell_count = (highres) ? manta_noise_get_cells(fds->fluid) : fds->total_cells;
|
||||
int *dim = (highres) ? fds->res_noise : fds->res;
|
||||
float *data = MEM_new_array_zeroed<float>(cell_count * 4, "smokeColorTexture");
|
||||
|
||||
if (data == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (highres) {
|
||||
manta_noise_get_rgba(fds->fluid, data, 0);
|
||||
}
|
||||
else {
|
||||
manta_smoke_get_rgba(fds->fluid, data, 0);
|
||||
}
|
||||
|
||||
gpu::Texture *tex = create_volume_texture(
|
||||
dim, gpu::TextureFormat::UNORM_8_8_8_8, GPU_DATA_FLOAT, data);
|
||||
|
||||
MEM_delete(data);
|
||||
|
||||
return tex;
|
||||
}
|
||||
|
||||
static gpu::Texture *create_flame_texture(FluidDomainSettings *fds, int highres)
|
||||
{
|
||||
float *source = nullptr;
|
||||
const bool has_fuel = (highres) ? manta_noise_has_fuel(fds->fluid) :
|
||||
manta_smoke_has_fuel(fds->fluid);
|
||||
int *dim = (highres) ? fds->res_noise : fds->res;
|
||||
|
||||
if (!has_fuel) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (highres) {
|
||||
source = manta_noise_get_flame(fds->fluid);
|
||||
}
|
||||
else {
|
||||
source = manta_smoke_get_flame(fds->fluid);
|
||||
}
|
||||
|
||||
gpu::Texture *tex = create_volume_texture(
|
||||
dim, gpu::TextureFormat::UNORM_8, GPU_DATA_FLOAT, source);
|
||||
swizzle_texture_channel_single(tex);
|
||||
return tex;
|
||||
}
|
||||
|
||||
static bool get_smoke_velocity_field(FluidDomainSettings *fds,
|
||||
float **r_velocity_x,
|
||||
float **r_velocity_y,
|
||||
float **r_velocity_z)
|
||||
{
|
||||
const char vector_field = fds->vector_field;
|
||||
switch ((FLUID_DisplayVectorField)vector_field) {
|
||||
case FLUID_DOMAIN_VECTOR_FIELD_VELOCITY:
|
||||
*r_velocity_x = manta_get_velocity_x(fds->fluid);
|
||||
*r_velocity_y = manta_get_velocity_y(fds->fluid);
|
||||
*r_velocity_z = manta_get_velocity_z(fds->fluid);
|
||||
break;
|
||||
case FLUID_DOMAIN_VECTOR_FIELD_GUIDE_VELOCITY:
|
||||
*r_velocity_x = manta_get_guide_velocity_x(fds->fluid);
|
||||
*r_velocity_y = manta_get_guide_velocity_y(fds->fluid);
|
||||
*r_velocity_z = manta_get_guide_velocity_z(fds->fluid);
|
||||
break;
|
||||
case FLUID_DOMAIN_VECTOR_FIELD_FORCE:
|
||||
*r_velocity_x = manta_get_force_x(fds->fluid);
|
||||
*r_velocity_y = manta_get_force_y(fds->fluid);
|
||||
*r_velocity_z = manta_get_force_z(fds->fluid);
|
||||
break;
|
||||
}
|
||||
|
||||
return *r_velocity_x && *r_velocity_y && *r_velocity_z;
|
||||
}
|
||||
|
||||
#endif /* WITH_FLUID */
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Public API
|
||||
* \{ */
|
||||
|
||||
void DRW_smoke_ensure_coba_field(FluidModifierData *fmd)
|
||||
{
|
||||
#ifndef WITH_FLUID
|
||||
UNUSED_VARS(fmd);
|
||||
#else
|
||||
if (fmd->type & MOD_FLUID_TYPE_DOMAIN) {
|
||||
FluidDomainSettings *fds = fmd->domain;
|
||||
|
||||
if (!fds->tex_field) {
|
||||
fds->tex_field = create_field_texture(fds, false);
|
||||
BLI_addtail(&drw_get().data->smoke_textures, BLI_genericNodeN(&fds->tex_field));
|
||||
}
|
||||
if (!fds->tex_coba && !ELEM(fds->coba_field,
|
||||
FLUID_DOMAIN_FIELD_PHI,
|
||||
FLUID_DOMAIN_FIELD_PHI_IN,
|
||||
FLUID_DOMAIN_FIELD_PHI_OUT,
|
||||
FLUID_DOMAIN_FIELD_PHI_OBSTACLE,
|
||||
FLUID_DOMAIN_FIELD_FLAGS,
|
||||
FLUID_DOMAIN_FIELD_PRESSURE))
|
||||
{
|
||||
fds->tex_coba = create_transfer_function(TFUNC_COLOR_RAMP, fds->coba);
|
||||
BLI_addtail(&drw_get().data->smoke_textures, BLI_genericNodeN(&fds->tex_coba));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void DRW_smoke_ensure(FluidModifierData *fmd, int highres)
|
||||
{
|
||||
#ifndef WITH_FLUID
|
||||
UNUSED_VARS(fmd, highres);
|
||||
#else
|
||||
if (fmd->type & MOD_FLUID_TYPE_DOMAIN) {
|
||||
FluidDomainSettings *fds = fmd->domain;
|
||||
|
||||
if (!fds->tex_density) {
|
||||
fds->tex_density = create_density_texture(fds, highres);
|
||||
BLI_addtail(&drw_get().data->smoke_textures, BLI_genericNodeN(&fds->tex_density));
|
||||
}
|
||||
if (!fds->tex_color) {
|
||||
fds->tex_color = create_color_texture(fds, highres);
|
||||
BLI_addtail(&drw_get().data->smoke_textures, BLI_genericNodeN(&fds->tex_color));
|
||||
}
|
||||
if (!fds->tex_flame) {
|
||||
fds->tex_flame = create_flame_texture(fds, highres);
|
||||
BLI_addtail(&drw_get().data->smoke_textures, BLI_genericNodeN(&fds->tex_flame));
|
||||
}
|
||||
if (!fds->tex_flame_coba && fds->tex_flame) {
|
||||
fds->tex_flame_coba = create_transfer_function(TFUNC_FLAME_SPECTRUM, nullptr);
|
||||
BLI_addtail(&drw_get().data->smoke_textures, BLI_genericNodeN(&fds->tex_flame_coba));
|
||||
}
|
||||
if (!fds->tex_shadow) {
|
||||
fds->tex_shadow = create_volume_texture(fds->res,
|
||||
gpu::TextureFormat::UNORM_8,
|
||||
GPU_DATA_FLOAT,
|
||||
manta_smoke_get_shadow(fds->fluid));
|
||||
BLI_addtail(&drw_get().data->smoke_textures, BLI_genericNodeN(&fds->tex_shadow));
|
||||
}
|
||||
}
|
||||
#endif /* WITH_FLUID */
|
||||
}
|
||||
|
||||
void DRW_smoke_ensure_velocity(FluidModifierData *fmd)
|
||||
{
|
||||
#ifndef WITH_FLUID
|
||||
UNUSED_VARS(fmd);
|
||||
#else
|
||||
if (fmd->type & MOD_FLUID_TYPE_DOMAIN) {
|
||||
FluidDomainSettings *fds = fmd->domain;
|
||||
float *vel_x = nullptr, *vel_y = nullptr, *vel_z = nullptr;
|
||||
|
||||
if (!get_smoke_velocity_field(fds, &vel_x, &vel_y, &vel_z)) {
|
||||
fds->vector_field = FLUID_DOMAIN_VECTOR_FIELD_VELOCITY;
|
||||
get_smoke_velocity_field(fds, &vel_x, &vel_y, &vel_z);
|
||||
}
|
||||
|
||||
if (ELEM(nullptr, vel_x, vel_y, vel_z)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!fds->tex_velocity_x) {
|
||||
fds->tex_velocity_x = GPU_texture_create_3d("velx",
|
||||
UNPACK3(fds->res),
|
||||
1,
|
||||
gpu::TextureFormat::SFLOAT_16,
|
||||
GPU_TEXTURE_USAGE_SHADER_READ,
|
||||
vel_x);
|
||||
fds->tex_velocity_y = GPU_texture_create_3d("vely",
|
||||
UNPACK3(fds->res),
|
||||
1,
|
||||
gpu::TextureFormat::SFLOAT_16,
|
||||
GPU_TEXTURE_USAGE_SHADER_READ,
|
||||
vel_y);
|
||||
fds->tex_velocity_z = GPU_texture_create_3d("velz",
|
||||
UNPACK3(fds->res),
|
||||
1,
|
||||
gpu::TextureFormat::SFLOAT_16,
|
||||
GPU_TEXTURE_USAGE_SHADER_READ,
|
||||
vel_z);
|
||||
BLI_addtail(&drw_get().data->smoke_textures, BLI_genericNodeN(&fds->tex_velocity_x));
|
||||
BLI_addtail(&drw_get().data->smoke_textures, BLI_genericNodeN(&fds->tex_velocity_y));
|
||||
BLI_addtail(&drw_get().data->smoke_textures, BLI_genericNodeN(&fds->tex_velocity_z));
|
||||
}
|
||||
}
|
||||
#endif /* WITH_FLUID */
|
||||
}
|
||||
|
||||
void DRW_fluid_ensure_flags(FluidModifierData *fmd)
|
||||
{
|
||||
#ifndef WITH_FLUID
|
||||
UNUSED_VARS(fmd);
|
||||
#else
|
||||
if (fmd->type & MOD_FLUID_TYPE_DOMAIN) {
|
||||
FluidDomainSettings *fds = fmd->domain;
|
||||
if (!fds->tex_flags) {
|
||||
fds->tex_flags = create_volume_texture(
|
||||
fds->res, gpu::TextureFormat::UINT_8, GPU_DATA_INT, manta_smoke_get_flags(fds->fluid));
|
||||
BLI_addtail(&drw_get().data->smoke_textures, BLI_genericNodeN(&fds->tex_flags));
|
||||
|
||||
swizzle_texture_channel_single(fds->tex_flags);
|
||||
}
|
||||
}
|
||||
#endif /* WITH_FLUID */
|
||||
}
|
||||
|
||||
void DRW_fluid_ensure_range_field(FluidModifierData *fmd)
|
||||
{
|
||||
#ifndef WITH_FLUID
|
||||
UNUSED_VARS(fmd);
|
||||
#else
|
||||
if (fmd->type & MOD_FLUID_TYPE_DOMAIN) {
|
||||
FluidDomainSettings *fds = fmd->domain;
|
||||
|
||||
if (!fds->tex_range_field) {
|
||||
fds->tex_range_field = create_field_texture(fds, true);
|
||||
BLI_addtail(&drw_get().data->smoke_textures, BLI_genericNodeN(&fds->tex_range_field));
|
||||
}
|
||||
}
|
||||
#endif /* WITH_FLUID */
|
||||
}
|
||||
|
||||
void DRW_smoke_begin_sync(DRWData *drw_data)
|
||||
{
|
||||
if (drw_data->smoke_textures.is_empty()) {
|
||||
return;
|
||||
}
|
||||
/* See workaround at blender::eevee::Instance::render_sample
|
||||
* Calling exit here as sync can happen multiple times when shaders are still being compiled
|
||||
* during image rendering. */
|
||||
DRW_smoke_exit(drw_data);
|
||||
}
|
||||
|
||||
void DRW_smoke_exit(DRWData *drw_data)
|
||||
{
|
||||
/* Free Smoke Textures after rendering */
|
||||
/* XXX This is a waste of processing and GPU bandwidth if nothing
|
||||
* is updated. But the problem is since Textures are stored in the
|
||||
* modifier we don't want them to take precious VRAM if the
|
||||
* modifier is not used for display. We should share them for
|
||||
* all viewport in a redraw at least. */
|
||||
for (LinkData &link : drw_data->smoke_textures) {
|
||||
GPU_TEXTURE_FREE_SAFE(*(gpu::Texture **)link.data);
|
||||
}
|
||||
drw_data->smoke_textures.free_no_destruct();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
455
blender-5.2.0/source/blender/draw/intern/draw_gpu_context.cc
Normal file
455
blender-5.2.0/source/blender/draw/intern/draw_gpu_context.cc
Normal file
@@ -0,0 +1,455 @@
|
||||
/* SPDX-FileCopyrightText: 2016 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "BKE_global.hh"
|
||||
|
||||
#include "GPU_capabilities.hh"
|
||||
#include "GPU_context.hh"
|
||||
#include "GPU_state.hh"
|
||||
|
||||
#include "RE_engine.h"
|
||||
#include "RE_pipeline.h"
|
||||
|
||||
#include "DRW_engine.hh"
|
||||
#include "DRW_render.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "wm_window.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Submission critical section
|
||||
*
|
||||
* The usage of gpu::Shader objects is currently not thread safe. Since they are shared resources
|
||||
* between render engine instances, we cannot allow pass submissions in a concurrent manner.
|
||||
* \{ */
|
||||
|
||||
static TicketMutex *draw_mutex = nullptr;
|
||||
static TicketMutex *submission_mutex = nullptr;
|
||||
|
||||
void DRW_mutexes_init()
|
||||
{
|
||||
draw_mutex = BLI_ticket_mutex_alloc();
|
||||
submission_mutex = BLI_ticket_mutex_alloc();
|
||||
}
|
||||
|
||||
void DRW_mutexes_exit()
|
||||
{
|
||||
BLI_ticket_mutex_free(draw_mutex);
|
||||
BLI_ticket_mutex_free(submission_mutex);
|
||||
}
|
||||
|
||||
void DRW_lock_start()
|
||||
{
|
||||
bool locked = BLI_ticket_mutex_lock_check_recursive(draw_mutex);
|
||||
BLI_assert(locked);
|
||||
UNUSED_VARS_NDEBUG(locked);
|
||||
}
|
||||
|
||||
void DRW_lock_end()
|
||||
{
|
||||
BLI_ticket_mutex_unlock(draw_mutex);
|
||||
}
|
||||
|
||||
void DRW_submission_start()
|
||||
{
|
||||
bool locked = BLI_ticket_mutex_lock_check_recursive(submission_mutex);
|
||||
BLI_assert(locked);
|
||||
UNUSED_VARS_NDEBUG(locked);
|
||||
GPU_render_begin();
|
||||
}
|
||||
|
||||
void DRW_submission_end()
|
||||
{
|
||||
GPU_render_end();
|
||||
BLI_ticket_mutex_unlock(submission_mutex);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name ContextShared
|
||||
*
|
||||
* \{ */
|
||||
|
||||
/* Context that can be shared across threads. Usage is guarded by a ticket mutex.
|
||||
* Should eventually be moved to GPU module after we get rid of the WM calls. */
|
||||
class ContextShared {
|
||||
/* Should be private but needs to be public for XR workaround. */
|
||||
public:
|
||||
TicketMutex *mutex_ = nullptr;
|
||||
/** Unique ghost context used by Viewports. */
|
||||
GHOST_IContext *system_gpu_context_ = nullptr;
|
||||
/** GPUContext associated to the system_gpu_context. */
|
||||
GPUContext *blender_gpu_context_ = nullptr;
|
||||
|
||||
/* NOTE: This changes the active context. */
|
||||
ContextShared()
|
||||
{
|
||||
mutex_ = BLI_ticket_mutex_alloc();
|
||||
|
||||
system_gpu_context_ = WM_system_gpu_context_create();
|
||||
WM_system_gpu_context_activate(system_gpu_context_);
|
||||
blender_gpu_context_ = GPU_context_create(nullptr, system_gpu_context_);
|
||||
}
|
||||
|
||||
~ContextShared()
|
||||
{
|
||||
WM_system_gpu_context_activate(system_gpu_context_);
|
||||
GPU_context_active_set(blender_gpu_context_);
|
||||
|
||||
GPU_context_discard(blender_gpu_context_);
|
||||
WM_system_gpu_context_dispose(system_gpu_context_);
|
||||
|
||||
BLI_ticket_mutex_free(mutex_);
|
||||
}
|
||||
|
||||
void enable()
|
||||
{
|
||||
DRW_lock_start();
|
||||
/* IMPORTANT: We don't support immediate mode in render mode!
|
||||
* This shall remain in effect until immediate mode supports
|
||||
* multiple threads. */
|
||||
BLI_ticket_mutex_lock(mutex_);
|
||||
|
||||
GPU_render_begin();
|
||||
|
||||
WM_system_gpu_context_activate(system_gpu_context_);
|
||||
GPU_context_active_set(blender_gpu_context_);
|
||||
GPU_context_begin_frame(blender_gpu_context_);
|
||||
}
|
||||
|
||||
bool is_enabled()
|
||||
{
|
||||
return blender_gpu_context_ == GPU_context_active_get();
|
||||
}
|
||||
|
||||
/* Restore window drawable after disabling if restore is true. */
|
||||
void disable(bool restore = false)
|
||||
{
|
||||
GPU_context_end_frame(blender_gpu_context_);
|
||||
|
||||
if (BLI_thread_is_main() && restore) {
|
||||
wm_window_reset_drawable();
|
||||
}
|
||||
else {
|
||||
WM_system_gpu_context_release(system_gpu_context_);
|
||||
GPU_context_active_set(nullptr);
|
||||
}
|
||||
/* Render boundaries are opened and closed here as this may be
|
||||
* called outside of an existing render loop. */
|
||||
GPU_render_end();
|
||||
|
||||
BLI_ticket_mutex_unlock(mutex_);
|
||||
DRW_lock_end();
|
||||
}
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name GPU & System Context
|
||||
*
|
||||
* A global GPUContext is used for rendering every viewports (even on different windows).
|
||||
* This is because some resources cannot be shared between contexts (GPUFramebuffers, GPUBatch).
|
||||
* \{ */
|
||||
|
||||
/** Unique context used by Viewports. */
|
||||
static ContextShared *viewport_context = nullptr;
|
||||
/** Unique context used by Preview jobs. */
|
||||
static ContextShared *preview_context = nullptr;
|
||||
|
||||
void DRW_gpu_context_create()
|
||||
{
|
||||
BLI_assert(viewport_context == nullptr); /* Ensure it's called once */
|
||||
|
||||
DRW_mutexes_init();
|
||||
|
||||
viewport_context = MEM_new<ContextShared>(__func__);
|
||||
preview_context = MEM_new<ContextShared>(__func__);
|
||||
|
||||
viewport_context->enable();
|
||||
}
|
||||
|
||||
void DRW_gpu_context_destroy()
|
||||
{
|
||||
BLI_assert(BLI_thread_is_main());
|
||||
if (viewport_context == nullptr) {
|
||||
return;
|
||||
}
|
||||
DRW_mutexes_exit();
|
||||
|
||||
MEM_SAFE_DELETE(viewport_context);
|
||||
MEM_SAFE_DELETE(preview_context);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Draw GPU Context
|
||||
* \{ */
|
||||
|
||||
void DRW_gpu_context_enable_ex(bool /*restore*/)
|
||||
{
|
||||
if (viewport_context == nullptr) {
|
||||
return;
|
||||
}
|
||||
viewport_context->enable();
|
||||
}
|
||||
|
||||
void DRW_gpu_context_disable_ex(bool restore)
|
||||
{
|
||||
if (viewport_context == nullptr) {
|
||||
return;
|
||||
}
|
||||
viewport_context->disable(restore);
|
||||
}
|
||||
|
||||
static void drw_gpu_preview_context_enable()
|
||||
{
|
||||
if (preview_context == nullptr) {
|
||||
return;
|
||||
}
|
||||
preview_context->enable();
|
||||
}
|
||||
|
||||
static void drw_gpu_preview_context_disable()
|
||||
{
|
||||
if (preview_context == nullptr) {
|
||||
return;
|
||||
}
|
||||
preview_context->disable();
|
||||
}
|
||||
|
||||
void DRW_gpu_context_enable()
|
||||
{
|
||||
/* TODO: should be replace by a more elegant alternative. */
|
||||
|
||||
if (G.background && viewport_context == nullptr) {
|
||||
WM_init_gpu();
|
||||
}
|
||||
DRW_gpu_context_enable_ex(true);
|
||||
}
|
||||
|
||||
bool DRW_gpu_context_try_enable()
|
||||
{
|
||||
if (viewport_context == nullptr) {
|
||||
return false;
|
||||
}
|
||||
DRW_gpu_context_enable_ex(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DRW_gpu_context_is_enabled()
|
||||
{
|
||||
return viewport_context && viewport_context->is_enabled();
|
||||
}
|
||||
|
||||
void DRW_gpu_context_disable()
|
||||
{
|
||||
DRW_gpu_context_disable_ex(true);
|
||||
}
|
||||
|
||||
void DRW_system_gpu_render_context_enable(GHOST_IContext *re_system_gpu_context)
|
||||
{
|
||||
/* If thread is main you should use DRW_gpu_context_enable(). */
|
||||
BLI_assert(!BLI_thread_is_main());
|
||||
|
||||
DRW_lock_start();
|
||||
WM_system_gpu_context_activate(re_system_gpu_context);
|
||||
}
|
||||
|
||||
void DRW_system_gpu_render_context_disable(GHOST_IContext *re_system_gpu_context)
|
||||
{
|
||||
WM_system_gpu_context_release(re_system_gpu_context);
|
||||
DRW_lock_end();
|
||||
}
|
||||
|
||||
void DRW_blender_gpu_render_context_enable(void *re_gpu_context)
|
||||
{
|
||||
/* If thread is main you should use DRW_gpu_context_enable(). */
|
||||
BLI_assert(!BLI_thread_is_main());
|
||||
|
||||
GPU_context_active_set(static_cast<GPUContext *>(re_gpu_context));
|
||||
}
|
||||
|
||||
void DRW_blender_gpu_render_context_disable(void * /*re_gpu_context*/)
|
||||
{
|
||||
GPU_flush();
|
||||
GPU_context_active_set(nullptr);
|
||||
}
|
||||
|
||||
void DRW_render_context_enable(Render *render)
|
||||
{
|
||||
if (G.background && viewport_context == nullptr) {
|
||||
WM_init_gpu();
|
||||
}
|
||||
|
||||
GPU_render_begin();
|
||||
|
||||
if (GPU_use_main_context_workaround()) {
|
||||
GPU_context_main_lock();
|
||||
DRW_gpu_context_enable();
|
||||
return;
|
||||
}
|
||||
|
||||
GHOST_IContext *re_viewport_system_gpu_context = RE_system_gpu_context_get(render);
|
||||
|
||||
/* Changing Context */
|
||||
if (re_viewport_system_gpu_context != nullptr) {
|
||||
DRW_system_gpu_render_context_enable(re_viewport_system_gpu_context);
|
||||
/* We need to query gpu context after a gl context has been bound. */
|
||||
void *re_viewport_context = RE_blender_gpu_context_ensure(render);
|
||||
DRW_blender_gpu_render_context_enable(re_viewport_context);
|
||||
}
|
||||
else {
|
||||
drw_gpu_preview_context_enable();
|
||||
}
|
||||
}
|
||||
|
||||
void DRW_render_context_disable(Render *render)
|
||||
{
|
||||
if (GPU_use_main_context_workaround()) {
|
||||
DRW_gpu_context_disable();
|
||||
GPU_render_end();
|
||||
GPU_context_main_unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
GHOST_IContext *re_viewport_system_gpu_context = RE_system_gpu_context_get(render);
|
||||
|
||||
if (re_viewport_system_gpu_context != nullptr) {
|
||||
void *re_viewport_context = RE_blender_gpu_context_ensure(render);
|
||||
/* GPU rendering may occur during context disable. */
|
||||
DRW_blender_gpu_render_context_disable(re_viewport_context);
|
||||
GPU_render_end();
|
||||
DRW_system_gpu_render_context_disable(re_viewport_system_gpu_context);
|
||||
}
|
||||
else {
|
||||
/* Usually the case for a preview job. The `Render` is created inside the render thread which
|
||||
* is too late to create a GPU context. */
|
||||
drw_gpu_preview_context_disable();
|
||||
GPU_render_end();
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name XR
|
||||
* \{ */
|
||||
|
||||
#ifdef WITH_XR_OPENXR
|
||||
|
||||
GHOST_IContext *DRW_system_gpu_context_get()
|
||||
{
|
||||
/* XXX: There should really be no such getter, but for VR we currently can't easily avoid it.
|
||||
* OpenXR needs some low level info for the GPU context that will be used for submitting the
|
||||
* final frame-buffer. VR could in theory create its own context, but that would mean we have to
|
||||
* switch to it just to submit the final frame, which has notable performance impact.
|
||||
*
|
||||
* We could "inject" a context through DRW_system_gpu_render_context_enable(), but that would
|
||||
* have to work from the main thread, which is tricky to get working too. The preferable solution
|
||||
* would be using a separate thread for VR drawing where a single context can stay active. */
|
||||
|
||||
return viewport_context->system_gpu_context_;
|
||||
}
|
||||
|
||||
void *DRW_xr_blender_gpu_context_get()
|
||||
{
|
||||
/* XXX: See comment on #DRW_system_gpu_context_get(). */
|
||||
|
||||
return viewport_context->blender_gpu_context_;
|
||||
}
|
||||
|
||||
void DRW_xr_drawing_begin()
|
||||
{
|
||||
/* XXX: See comment on #DRW_system_gpu_context_get(). */
|
||||
|
||||
DRW_lock_start();
|
||||
BLI_ticket_mutex_lock(viewport_context->mutex_);
|
||||
}
|
||||
|
||||
void DRW_xr_drawing_end()
|
||||
{
|
||||
/* XXX: See comment on #DRW_system_gpu_context_get(). */
|
||||
|
||||
BLI_ticket_mutex_unlock(viewport_context->mutex_);
|
||||
DRW_lock_end();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Draw manager context release/activation
|
||||
*
|
||||
* These functions are used in cases when an GPU context creation is needed during the draw.
|
||||
* This happens, for example, when an external engine needs to create its own GPU context from
|
||||
* the engine initialization.
|
||||
*
|
||||
* Example of context creation:
|
||||
*
|
||||
* const bool drw_state = DRW_gpu_context_release();
|
||||
* viewport_system_gpu_context = WM_system_gpu_context_create();
|
||||
* DRW_gpu_context_activate(drw_state);
|
||||
*
|
||||
* Example of context destruction:
|
||||
*
|
||||
* const bool drw_state = DRW_gpu_context_release();
|
||||
* WM_system_gpu_context_activate(viewport_system_gpu_context);
|
||||
* WM_system_gpu_context_dispose(viewport_system_gpu_context);
|
||||
* DRW_gpu_context_activate(drw_state);
|
||||
*
|
||||
*
|
||||
* NOTE: Will only perform context modification when on main thread. This way these functions can
|
||||
* be used in an engine without check on whether it is a draw manager which manages GPU context
|
||||
* on the current thread. The downside of this is that if the engine performs GPU creation from
|
||||
* a non-main thread, that thread is supposed to not have GPU context ever bound by Blender.
|
||||
*
|
||||
* \{ */
|
||||
|
||||
bool DRW_gpu_context_release()
|
||||
{
|
||||
if (!BLI_thread_is_main()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (GPU_context_active_get() != viewport_context->blender_gpu_context_) {
|
||||
/* Context release is requested from the outside of the draw manager main draw loop, indicate
|
||||
* this to the `DRW_gpu_context_activate()` so that it restores drawable of the window.
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
GPU_context_active_set(nullptr);
|
||||
WM_system_gpu_context_release(viewport_context->system_gpu_context_);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DRW_gpu_context_activate(bool drw_state)
|
||||
{
|
||||
if (!BLI_thread_is_main()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (drw_state) {
|
||||
WM_system_gpu_context_activate(viewport_context->system_gpu_context_);
|
||||
GPU_context_active_set(viewport_context->blender_gpu_context_);
|
||||
}
|
||||
else {
|
||||
wm_window_reset_drawable();
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
123
blender-5.2.0/source/blender/draw/intern/draw_hair.cc
Normal file
123
blender-5.2.0/source/blender/draw/intern/draw_hair.cc
Normal file
@@ -0,0 +1,123 @@
|
||||
/* SPDX-FileCopyrightText: 2017 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*
|
||||
* \brief Contains procedural GPU hair drawing methods.
|
||||
*/
|
||||
|
||||
#include "BKE_customdata.hh"
|
||||
|
||||
#include "DNA_modifier_types.h"
|
||||
#include "DNA_particle_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "DRW_render.hh"
|
||||
|
||||
#include "GPU_batch.hh"
|
||||
#include "GPU_material.hh"
|
||||
#include "GPU_shader.hh"
|
||||
#include "GPU_texture.hh"
|
||||
#include "GPU_vertex_buffer.hh"
|
||||
|
||||
#include "draw_common_c.hh"
|
||||
#include "draw_context_private.hh"
|
||||
#include "draw_hair_private.hh"
|
||||
#include "draw_manager.hh"
|
||||
#include "draw_shader_shared.hh"
|
||||
|
||||
/* New Draw Manager. */
|
||||
#include "draw_common.hh"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
gpu::VertBuf *hair_pos_buffer_get(Scene *scene,
|
||||
Object *object,
|
||||
ParticleSystem *psys,
|
||||
ModifierData *md)
|
||||
{
|
||||
/* TODO(fclem): Remove Global access. */
|
||||
CurvesModule &module = *drw_get().data->curves_module;
|
||||
|
||||
drw_particle_update_ptcache(object, psys);
|
||||
ParticleDrawSource source = drw_particle_get_hair_source(
|
||||
object, psys, md, nullptr, scene->r.hair_subdiv);
|
||||
|
||||
CurvesEvalCache &cache = hair_particle_get_eval_cache(source);
|
||||
cache.ensure_positions(module, source);
|
||||
|
||||
return cache.evaluated_pos_rad_buf.get();
|
||||
}
|
||||
|
||||
template<typename PassT>
|
||||
gpu::Batch *hair_sub_pass_setup_implementation(PassT &sub_ps,
|
||||
const Scene *scene,
|
||||
const ObjectRef &ob_ref,
|
||||
ParticleSystem *psys,
|
||||
ModifierData *md,
|
||||
GPUMaterial *gpu_material)
|
||||
{
|
||||
/** NOTE: This still relies on the old DRW_hair implementation. */
|
||||
Object *object = ob_ref.object;
|
||||
|
||||
drw_particle_update_ptcache(object, psys);
|
||||
|
||||
ParticleDrawSource source = drw_particle_get_hair_source(
|
||||
object, psys, md, nullptr, scene->r.hair_subdiv);
|
||||
|
||||
CurvesEvalCache &cache = hair_particle_get_eval_cache(source);
|
||||
|
||||
const int face_per_segment = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 0 :
|
||||
(scene->r.hair_type == SCE_HAIR_SHAPE_CYLINDER) ? 3 :
|
||||
1;
|
||||
|
||||
if (source.evaluated_points_num() == 0) {
|
||||
/* Nothing to draw. Just return an empty drawcall that will be skipped. */
|
||||
bool unused_error;
|
||||
return cache.batch_get(0, 0, face_per_segment, false, unused_error);
|
||||
}
|
||||
|
||||
/* TODO(fclem): Remove Global access. */
|
||||
CurvesModule &module = *drw_get().data->curves_module;
|
||||
|
||||
cache.ensure_positions(module, source);
|
||||
cache.ensure_attributes(module, source, gpu_material);
|
||||
|
||||
gpu::VertBufPtr &indirection_buf = cache.indirection_buf_get(module, source, face_per_segment);
|
||||
|
||||
{
|
||||
ParticleSystemModifierData *psmd = reinterpret_cast<ParticleSystemModifierData *>(source.md);
|
||||
Mesh &mesh = *psmd->mesh_final;
|
||||
const StringRef active_uv = mesh.active_uv_map_name();
|
||||
curves_bind_resources(
|
||||
sub_ps, module, cache, face_per_segment, gpu_material, indirection_buf, active_uv);
|
||||
}
|
||||
|
||||
bool unused_error;
|
||||
return cache.batch_get(
|
||||
source.evaluated_points_num(), source.curves_num(), face_per_segment, false, unused_error);
|
||||
}
|
||||
|
||||
gpu::Batch *hair_sub_pass_setup(PassMain::Sub &sub_ps,
|
||||
const Scene *scene,
|
||||
const ObjectRef &ob_ref,
|
||||
ParticleSystem *psys,
|
||||
ModifierData *md,
|
||||
GPUMaterial *gpu_material)
|
||||
{
|
||||
return hair_sub_pass_setup_implementation(sub_ps, scene, ob_ref, psys, md, gpu_material);
|
||||
}
|
||||
|
||||
gpu::Batch *hair_sub_pass_setup(PassSimple::Sub &sub_ps,
|
||||
const Scene *scene,
|
||||
const ObjectRef &ob_ref,
|
||||
ParticleSystem *psys,
|
||||
ModifierData *md,
|
||||
GPUMaterial *gpu_material)
|
||||
{
|
||||
return hair_sub_pass_setup_implementation(sub_ps, scene, ob_ref, psys, md, gpu_material);
|
||||
}
|
||||
|
||||
} // namespace blender::draw
|
||||
159
blender-5.2.0/source/blender/draw/intern/draw_hair_private.hh
Normal file
159
blender-5.2.0/source/blender/draw/intern/draw_hair_private.hh
Normal file
@@ -0,0 +1,159 @@
|
||||
/* SPDX-FileCopyrightText: 2017 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "draw_curves_private.hh"
|
||||
#include "draw_pass.hh"
|
||||
struct ParticleHairCache;
|
||||
namespace blender {
|
||||
|
||||
namespace bke {
|
||||
class CurvesGeometry;
|
||||
}
|
||||
|
||||
namespace draw {
|
||||
|
||||
struct CurvesEvalCache;
|
||||
|
||||
class CurveRefinePass : public PassSimple {
|
||||
public:
|
||||
CurveRefinePass(const char *name) : PassSimple(name) {};
|
||||
};
|
||||
|
||||
using CurvesInfosBuf = UniformBuffer<CurvesInfos>;
|
||||
|
||||
struct CurvesUniformBufPool {
|
||||
Vector<std::unique_ptr<CurvesInfosBuf>> ubos;
|
||||
int used = 0;
|
||||
|
||||
void reset()
|
||||
{
|
||||
used = 0;
|
||||
/* Allocate dummy. */
|
||||
alloc();
|
||||
ubos.first()->push_update();
|
||||
}
|
||||
|
||||
CurvesInfosBuf &dummy_get()
|
||||
{
|
||||
return *ubos.first();
|
||||
}
|
||||
|
||||
CurvesInfosBuf &alloc();
|
||||
};
|
||||
|
||||
struct CurvesModule {
|
||||
CurvesUniformBufPool ubo_pool;
|
||||
CurveRefinePass refine = {"CurvesEvalPass"};
|
||||
/* Contains all transient input buffers contained inside `refine`.
|
||||
* Cleared after update. */
|
||||
Vector<gpu::VertBufPtr> transient_buffers;
|
||||
|
||||
gpu::VertBuf *dummy_vbo = drw_curves_ensure_dummy_vbo();
|
||||
|
||||
~CurvesModule()
|
||||
{
|
||||
GPU_VERTBUF_DISCARD_SAFE(dummy_vbo);
|
||||
}
|
||||
|
||||
void init()
|
||||
{
|
||||
ubo_pool.reset();
|
||||
|
||||
refine.init();
|
||||
refine.state_set(DRW_STATE_NO_DRAW);
|
||||
}
|
||||
|
||||
/* Record evaluation inside `refine`.
|
||||
* Output will be ready once `refine` pass has been submitted. */
|
||||
void evaluate_curve_attribute(bool has_catmull,
|
||||
bool has_bezier,
|
||||
bool has_poly,
|
||||
bool has_nurbs,
|
||||
bool has_cyclic,
|
||||
int curve_count,
|
||||
CurvesEvalCache &cache,
|
||||
CurvesEvalShader shader_type,
|
||||
gpu::VertBufPtr input_buf,
|
||||
gpu::VertBufPtr &output_buf,
|
||||
/* For radius during position evaluation. */
|
||||
gpu::VertBuf *input2_buf = nullptr,
|
||||
/* For baking a transform during position evaluation. */
|
||||
float4x4 transform = float4x4::identity());
|
||||
|
||||
void evaluate_positions(bool has_catmull,
|
||||
bool has_bezier,
|
||||
bool has_poly,
|
||||
bool has_nurbs,
|
||||
bool has_cyclic,
|
||||
int curve_count,
|
||||
CurvesEvalCache &cache,
|
||||
gpu::VertBufPtr input_pos_buf,
|
||||
gpu::VertBufPtr input_rad_buf,
|
||||
gpu::VertBufPtr &output_pos_buf,
|
||||
float4x4 transform = float4x4::identity())
|
||||
{
|
||||
evaluate_curve_attribute(has_catmull,
|
||||
has_bezier,
|
||||
has_poly,
|
||||
has_nurbs,
|
||||
has_cyclic,
|
||||
curve_count,
|
||||
cache,
|
||||
CURVES_EVAL_POSITION,
|
||||
std::move(input_pos_buf),
|
||||
output_pos_buf,
|
||||
/* Transfer ownership through optional argument. */
|
||||
input_rad_buf.release(),
|
||||
transform);
|
||||
}
|
||||
|
||||
void evaluate_curve_length_intercept(bool has_cyclic, int curve_count, CurvesEvalCache &cache);
|
||||
|
||||
gpu::VertBufPtr evaluate_topology_indirection(const int curve_count,
|
||||
const int point_count,
|
||||
CurvesEvalCache &cache,
|
||||
bool is_ribbon,
|
||||
bool has_cyclic);
|
||||
|
||||
private:
|
||||
gpu::VertBuf *drw_curves_ensure_dummy_vbo();
|
||||
|
||||
void dispatch(int curve_count, PassSimple::Sub &pass);
|
||||
};
|
||||
|
||||
} // namespace draw
|
||||
|
||||
#define MAX_LAYER_NAME_CT 4 /* `u0123456789, u, au, a0123456789`. */
|
||||
#define MAX_LAYER_NAME_LEN (GPU_MAX_SAFE_ATTR_NAME + 2)
|
||||
#define MAX_THICKRES 2 /* see eHairType */
|
||||
#define MAX_HAIR_SUBDIV 4 /* see hair_subdiv rna */
|
||||
|
||||
enum ParticleRefineShader {
|
||||
PART_REFINE_CATMULL_ROM = 0,
|
||||
PART_REFINE_MAX_SHADER,
|
||||
};
|
||||
|
||||
struct ModifierData;
|
||||
struct Object;
|
||||
struct ParticleSystem;
|
||||
|
||||
namespace draw {
|
||||
|
||||
void drw_particle_update_ptcache(Object *object_eval, ParticleSystem *psys);
|
||||
ParticleDrawSource drw_particle_get_hair_source(Object *object,
|
||||
ParticleSystem *psys,
|
||||
ModifierData *md,
|
||||
PTCacheEdit *edit,
|
||||
int additional_subdivision);
|
||||
|
||||
CurvesEvalCache &hair_particle_get_eval_cache(ParticleDrawSource &src);
|
||||
|
||||
} // namespace draw
|
||||
} // namespace blender
|
||||
550
blender-5.2.0/source/blender/draw/intern/draw_handle.hh
Normal file
550
blender-5.2.0/source/blender/draw/intern/draw_handle.hh
Normal file
@@ -0,0 +1,550 @@
|
||||
/* 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"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct DupliCacheManager;
|
||||
|
||||
namespace draw {
|
||||
|
||||
/**
|
||||
* Index for getting a specific resource from the Draw Manager resource arrays.
|
||||
* (e.g. object matrices)
|
||||
* Last bit contains handedness.
|
||||
*
|
||||
* NOTE: From the draw_pass and draw_command perspective, the 0 index is still valid and points to
|
||||
* default initialized Manager resources. Valid ResourceHandles start at index 1.
|
||||
*/
|
||||
struct ResourceID {
|
||||
uint32_t raw;
|
||||
|
||||
ResourceID() = default;
|
||||
ResourceID(uint raw_) : raw(raw_) {};
|
||||
ResourceID(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 index() const
|
||||
{
|
||||
return (raw & 0x7FFFFFFFu);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Refers to a range of contiguous indices in the Draw Manager resource arrays.
|
||||
* Typically used to render instances of an object, but can represent a single instance too.
|
||||
* The associated objects must share handedness and state so they can be rendered together.
|
||||
*/
|
||||
struct ResourceIDRange {
|
||||
/* First handle in the range. */
|
||||
ResourceID first = 0;
|
||||
/* Number of handles in the range. */
|
||||
uint32_t count = 1;
|
||||
|
||||
ResourceIDRange() = default;
|
||||
ResourceIDRange(ResourceID id) : first(id) {}
|
||||
ResourceIDRange(ResourceID first_id, uint len) : first(first_id), count(len) {}
|
||||
|
||||
bool has_inverted_handedness() const
|
||||
{
|
||||
return first.has_inverted_handedness();
|
||||
}
|
||||
|
||||
IndexRange id_range() const
|
||||
{
|
||||
BLI_assert(count > 0);
|
||||
BLI_assert(first.raw != 0 || count == 1);
|
||||
return {first.raw, count};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Safety wrapper around ResourceID, meant to be used by engine code.
|
||||
* Valid handles can only be created by the Draw Manager.
|
||||
*
|
||||
* ResourceHandleRange is usually preferred over this class.
|
||||
* ResourceHandle should only be used in paths where handling objects in batches is not possible.
|
||||
*/
|
||||
class ResourceHandle {
|
||||
friend class Manager;
|
||||
friend class ResourceHandleRange;
|
||||
|
||||
ResourceID id_ = {};
|
||||
|
||||
ResourceHandle(uint raw) : id_(raw) {}
|
||||
ResourceHandle(uint index, bool inverted_handedness) : id_(index, inverted_handedness) {}
|
||||
|
||||
public:
|
||||
ResourceHandle() = default;
|
||||
|
||||
bool is_valid() const
|
||||
{
|
||||
return id_.raw != 0;
|
||||
}
|
||||
|
||||
bool has_inverted_handedness() const
|
||||
{
|
||||
return id_.has_inverted_handedness();
|
||||
}
|
||||
|
||||
uint raw() const
|
||||
{
|
||||
return id_.raw;
|
||||
}
|
||||
|
||||
uint index() const
|
||||
{
|
||||
return id_.index();
|
||||
}
|
||||
|
||||
operator ResourceID() const
|
||||
{
|
||||
BLI_assert(is_valid());
|
||||
return id_;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Safety wrapper around ResourceIDRange, meant to be used by engine code.
|
||||
* Valid handles can only be created by the Draw Manager.
|
||||
*/
|
||||
class ResourceHandleRange {
|
||||
friend class Manager;
|
||||
friend class ResourceHandle;
|
||||
|
||||
ResourceIDRange id_ = {};
|
||||
|
||||
ResourceHandleRange(ResourceHandle first_handle, uint len) : id_(first_handle.id_, len) {}
|
||||
|
||||
public:
|
||||
ResourceHandleRange() = default;
|
||||
ResourceHandleRange(ResourceHandle handle) : id_(handle.id_) {}
|
||||
|
||||
bool is_valid() const
|
||||
{
|
||||
return id_.first.raw != 0;
|
||||
}
|
||||
|
||||
bool has_inverted_handedness() const
|
||||
{
|
||||
return id_.has_inverted_handedness();
|
||||
}
|
||||
|
||||
IndexRange id_range() const
|
||||
{
|
||||
return id_.id_range();
|
||||
}
|
||||
|
||||
operator ResourceIDRange() const
|
||||
{
|
||||
BLI_assert(is_valid());
|
||||
return id_;
|
||||
}
|
||||
|
||||
/* Returns a single handle within this range.
|
||||
* May be required for passes that require per-instance setups,
|
||||
* so their drawing can't be batched into a single draw call. */
|
||||
ResourceHandle sub_handle(int index) const
|
||||
{
|
||||
BLI_assert(index < id_.count);
|
||||
return ResourceHandle(id_.first.index() + index, id_.first.has_inverted_handedness());
|
||||
}
|
||||
|
||||
operator ResourceHandle() const
|
||||
{
|
||||
BLI_assert(id_.count == 1);
|
||||
return ResourceHandle(id_.first.raw);
|
||||
}
|
||||
|
||||
uint raw() const
|
||||
{
|
||||
BLI_assert(id_.count == 1);
|
||||
return id_.first.raw;
|
||||
}
|
||||
|
||||
uint index() const
|
||||
{
|
||||
BLI_assert(id_.count == 1);
|
||||
return id_.first.index();
|
||||
}
|
||||
};
|
||||
|
||||
/* 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;
|
||||
|
||||
/** List of (render-compatible) duplis when rendering a ranges. */
|
||||
const VectorList<DupliObject *> *duplis_ = nullptr;
|
||||
|
||||
/** Unique handle per object ref. */
|
||||
ResourceHandleRange handle_ = {};
|
||||
ResourceHandleRange sculpt_handle_ = {};
|
||||
|
||||
/* For ParticleSystems of the main object. */
|
||||
uint sub_key_ = 0;
|
||||
|
||||
public:
|
||||
Object *const object;
|
||||
|
||||
explicit ObjectRef(Object *ob,
|
||||
Object *dupli_parent = nullptr,
|
||||
DupliObject *dupli_object = nullptr);
|
||||
explicit ObjectRef(Object &ob, Object *dupli_parent, const VectorList<DupliObject *> &duplis);
|
||||
explicit ObjectRef(const ObjectRef &ob_ref, uint sub_key) : ObjectRef(ob_ref)
|
||||
{
|
||||
sub_key_ = sub_key;
|
||||
};
|
||||
|
||||
/* Is the object coming from a Dupli system. */
|
||||
bool is_dupli() const
|
||||
{
|
||||
return dupli_parent_ != nullptr;
|
||||
}
|
||||
|
||||
bool is_active(const Object *active_object) const
|
||||
{
|
||||
return (dupli_parent_ ? dupli_parent_ : object) == active_object;
|
||||
}
|
||||
|
||||
bool is_range() const
|
||||
{
|
||||
return duplis_ != nullptr;
|
||||
}
|
||||
|
||||
int instances_count() const
|
||||
{
|
||||
if (duplis_) {
|
||||
return duplis_->size();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
float4x4 object_to_world(int instance_index) const
|
||||
{
|
||||
if (is_range()) {
|
||||
return float4x4((*duplis_)[instance_index]->mat);
|
||||
}
|
||||
else {
|
||||
BLI_assert(instance_index == 0);
|
||||
return object->object_to_world();
|
||||
}
|
||||
}
|
||||
|
||||
float4x4 object_to_world() const
|
||||
{
|
||||
BLI_assert(!is_range());
|
||||
return object_to_world(0);
|
||||
}
|
||||
|
||||
float random(int instance_index) const
|
||||
{
|
||||
if (instance_index != 0) {
|
||||
BLI_assert(is_range());
|
||||
return (*duplis_)[instance_index]->random_id * (1.0f / float(0xFFFFFFFF));
|
||||
}
|
||||
|
||||
if (dupli_parent_ == 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, int instance_index, float r_value[4]) const
|
||||
{
|
||||
if (instance_index != 0) {
|
||||
BLI_assert(is_range());
|
||||
if (attr.use_dupli) {
|
||||
/* If requesting instance data, check the parent particle system and object. */
|
||||
return BKE_object_dupli_find_rgba_attribute(
|
||||
object, (*duplis_)[instance_index], dupli_parent_, attr.name, r_value);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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
|
||||
{
|
||||
return dupli_parent_ ? dupli_parent_->light_linking : object->light_linking;
|
||||
}
|
||||
|
||||
uint 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 bke::ObjectRuntime &runtime) {
|
||||
uint 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;
|
||||
};
|
||||
|
||||
uint 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
|
||||
{
|
||||
/* Objects with particles don't support instancing optimizations yet. */
|
||||
BLI_assert(!is_range());
|
||||
|
||||
/* 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 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_->runtime->sculpt_session &&
|
||||
(dupli_parent_->runtime->sculpt_session->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;
|
||||
default:
|
||||
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 instance_index)
|
||||
{
|
||||
ob_ = DEG_get_original(ob_ref.object);
|
||||
hash_value_ = get_default_hash(ob_);
|
||||
|
||||
if (DupliObject *dupli = instance_index ? (*ob_ref.duplis_)[instance_index] :
|
||||
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 (ob_ref.sub_key_ != 0) {
|
||||
sub_key_ = ob_ref.sub_key_;
|
||||
hash_value_ = get_default_hash(hash_value_, get_default_hash(sub_key_));
|
||||
}
|
||||
}
|
||||
|
||||
ObjectKey(const ObjectRef &ob_ref) : ObjectKey(ob_ref, 0)
|
||||
{
|
||||
BLI_assert(!ob_ref.is_range());
|
||||
}
|
||||
|
||||
/* Special handles that will have nullptr object.
|
||||
* Used for inserting helper items inside the hash-maps without creating a dummy #Object. */
|
||||
explicit ObjectKey(int key)
|
||||
{
|
||||
sub_key_ = key;
|
||||
hash_value_ = get_default_hash(ob_);
|
||||
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 draw
|
||||
|
||||
} // namespace blender
|
||||
421
blender-5.2.0/source/blender/draw/intern/draw_manager.cc
Normal file
421
blender-5.2.0/source/blender/draw/intern/draw_manager.cc
Normal file
@@ -0,0 +1,421 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "DNA_userdef_types.h"
|
||||
|
||||
#include "BKE_paint.hh"
|
||||
#include "BKE_paint_bvh.hh"
|
||||
|
||||
#include "BLI_math_base.h"
|
||||
#include "GPU_compute.hh"
|
||||
|
||||
#include "draw_context_private.hh"
|
||||
#include "draw_debug.hh"
|
||||
#include "draw_defines.hh"
|
||||
#include "draw_manager.hh"
|
||||
#include "draw_pass.hh"
|
||||
#include "draw_shader.hh"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
std::atomic<uint32_t> Manager::global_sync_counter_ = 1;
|
||||
|
||||
Manager::~Manager()
|
||||
{
|
||||
for (gpu::Texture *texture : acquired_textures) {
|
||||
/* Decrease refcount and free if 0. */
|
||||
GPU_texture_free(texture);
|
||||
}
|
||||
}
|
||||
|
||||
void Manager::begin_sync(Object *object_active)
|
||||
{
|
||||
/* Add 2 to always have a non-null number even in case of overflow. */
|
||||
sync_counter_ = (global_sync_counter_ += 2);
|
||||
|
||||
matrix_buf.swap();
|
||||
bounds_buf.swap();
|
||||
infos_buf.swap();
|
||||
|
||||
matrix_buf.current().trim_to_next_power_of_2(resource_len_);
|
||||
bounds_buf.current().trim_to_next_power_of_2(resource_len_);
|
||||
infos_buf.current().trim_to_next_power_of_2(resource_len_);
|
||||
attributes_buf.trim_to_next_power_of_2(attribute_len_);
|
||||
|
||||
/* TODO: This means the reference is kept until further redraw or manager tear-down. Instead,
|
||||
* they should be released after each draw loop. But for now, mimics old DRW behavior. */
|
||||
for (gpu::Texture *texture : acquired_textures) {
|
||||
/* Decrease refcount and free if 0. */
|
||||
GPU_texture_free(texture);
|
||||
}
|
||||
|
||||
acquired_textures.clear();
|
||||
layer_attributes.clear();
|
||||
|
||||
/* For some reason, if this uninitialized data pattern was enabled (ie release asserts enabled),
|
||||
* The viewport just gives up rendering objects on ARM64 devices. Possibly Mesa GLOn12-related. */
|
||||
#if !defined(NDEBUG) && !defined(_M_ARM64)
|
||||
/* Detect uninitialized data. */
|
||||
memset(matrix_buf.current().data(),
|
||||
0xF0,
|
||||
matrix_buf.current().size() * sizeof(*matrix_buf.current().data()));
|
||||
memset(bounds_buf.current().data(),
|
||||
0xF0,
|
||||
matrix_buf.current().size() * sizeof(*bounds_buf.current().data()));
|
||||
memset(infos_buf.current().data(),
|
||||
0xF0,
|
||||
matrix_buf.current().size() * sizeof(*infos_buf.current().data()));
|
||||
#endif
|
||||
resource_len_ = 0;
|
||||
attribute_len_ = 0;
|
||||
/* TODO(fclem): Resize buffers if too big, but with an hysteresis threshold. */
|
||||
|
||||
this->object_active = object_active;
|
||||
|
||||
/* Init the 0 resource. */
|
||||
resource_handle(float4x4::identity());
|
||||
}
|
||||
|
||||
void Manager::sync_layer_attributes()
|
||||
{
|
||||
/* Sort the attribute IDs - the shaders use binary search. */
|
||||
Vector<uint32_t> id_list;
|
||||
|
||||
id_list.reserve(layer_attributes.size());
|
||||
|
||||
for (uint32_t id : layer_attributes.keys()) {
|
||||
id_list.append(id);
|
||||
}
|
||||
|
||||
std::ranges::sort(id_list);
|
||||
|
||||
/* Look up the attributes. */
|
||||
int count = 0, size = layer_attributes_buf.end() - layer_attributes_buf.begin();
|
||||
|
||||
for (uint32_t id : id_list) {
|
||||
if (layer_attributes_buf[count].sync(
|
||||
drw_get().scene, drw_get().view_layer, layer_attributes.lookup(id)))
|
||||
{
|
||||
/* Check if the buffer is full. */
|
||||
if (++count == size) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layer_attributes_buf[0].buffer_length = count;
|
||||
}
|
||||
|
||||
void Manager::end_sync()
|
||||
{
|
||||
GPU_debug_group_begin("Manager.end_sync");
|
||||
|
||||
sync_layer_attributes();
|
||||
|
||||
matrix_buf.current().push_update();
|
||||
bounds_buf.current().push_update();
|
||||
infos_buf.current().push_update();
|
||||
attributes_buf.push_update();
|
||||
layer_attributes_buf.push_update();
|
||||
|
||||
/* Useful for debugging the following resource finalize. But will trigger the drawing of the GPU
|
||||
* debug draw/print buffers for every frame. Not nice for performance. */
|
||||
// debug_bind();
|
||||
|
||||
DRW_submission_start();
|
||||
|
||||
/* Dispatch compute to finalize the resources on GPU. Save a bit of CPU time. */
|
||||
uint thread_groups = divide_ceil_u(resource_len_, DRW_FINALIZE_GROUP_SIZE);
|
||||
gpu::Shader *shader = DRW_shader_draw_resource_finalize_get();
|
||||
GPU_shader_bind(shader);
|
||||
GPU_shader_uniform_1i(shader, "resource_len", resource_len_);
|
||||
GPU_storagebuf_bind(matrix_buf.current(), GPU_shader_get_ssbo_binding(shader, "matrix_buf"));
|
||||
GPU_storagebuf_bind(bounds_buf.current(), GPU_shader_get_ssbo_binding(shader, "bounds_buf"));
|
||||
GPU_storagebuf_bind(infos_buf.current(), GPU_shader_get_ssbo_binding(shader, "infos_buf"));
|
||||
GPU_compute_dispatch(shader, thread_groups, 1, 1);
|
||||
GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE);
|
||||
|
||||
DRW_submission_end();
|
||||
|
||||
GPU_debug_group_end();
|
||||
}
|
||||
|
||||
void Manager::debug_bind()
|
||||
{
|
||||
gpu::StorageBuf *gpu_buf = DebugDraw::get().gpu_draw_buf_get();
|
||||
if (gpu_buf == nullptr) {
|
||||
return;
|
||||
}
|
||||
GPU_storagebuf_bind(gpu_buf, DRW_DEBUG_DRAW_SLOT);
|
||||
#ifndef DISABLE_DEBUG_SHADER_PRINT_BARRIER
|
||||
/* Add a barrier to allow multiple shader writing to the same buffer. */
|
||||
GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Manager::resource_bind()
|
||||
{
|
||||
GPU_storagebuf_bind(matrix_buf.current(), DRW_OBJ_MAT_SLOT);
|
||||
GPU_storagebuf_bind(infos_buf.current(), DRW_OBJ_INFOS_SLOT);
|
||||
GPU_storagebuf_bind(attributes_buf, DRW_OBJ_ATTR_SLOT);
|
||||
GPU_uniformbuf_bind(layer_attributes_buf, DRW_LAYER_ATTR_UBO_SLOT);
|
||||
}
|
||||
|
||||
uint64_t Manager::fingerprint_get()
|
||||
{
|
||||
/* Covers new sync cycle, added resources and different #Manager. */
|
||||
return sync_counter_ | (uint64_t(resource_len_) << 32);
|
||||
}
|
||||
|
||||
ResourceHandleRange Manager::unique_handle_for_sculpt(const ObjectRef &ref)
|
||||
{
|
||||
if (ref.sculpt_handle_.is_valid()) {
|
||||
return ref.sculpt_handle_;
|
||||
}
|
||||
const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(*ref.object);
|
||||
const Bounds<float3> bounds = bke::pbvh::bounds_get(pbvh);
|
||||
const float3 center = math::midpoint(bounds.min, bounds.max);
|
||||
const float3 half_extent = bounds.max - center;
|
||||
/* WORKAROUND: Instead of breaking const correctness everywhere, we only break it for this. */
|
||||
const_cast<ObjectRef &>(ref).sculpt_handle_ = resource_handle(
|
||||
ref, nullptr, ¢er, &half_extent);
|
||||
return ref.sculpt_handle_;
|
||||
}
|
||||
|
||||
void Manager::compute_visibility(View &view)
|
||||
{
|
||||
bool freeze_culling = (USER_DEVELOPER_TOOL_TEST(&U, use_viewport_debug) && drw_get().v3d &&
|
||||
(drw_get().v3d->debug_flag & V3D_DEBUG_FREEZE_CULLING) != 0);
|
||||
|
||||
BLI_assert_msg(view.manager_fingerprint_ != this->fingerprint_get(),
|
||||
"Resources did not changed, no need to update");
|
||||
|
||||
view.manager_fingerprint_ = this->fingerprint_get();
|
||||
|
||||
view.bind();
|
||||
view.compute_visibility(
|
||||
bounds_buf.current(), infos_buf.current(), resource_len_, freeze_culling);
|
||||
}
|
||||
|
||||
void Manager::ensure_visibility(View &view)
|
||||
{
|
||||
if (view.manager_fingerprint_ != this->fingerprint_get()) {
|
||||
compute_visibility(view);
|
||||
}
|
||||
}
|
||||
|
||||
void Manager::generate_commands(PassMain &pass, View &view)
|
||||
{
|
||||
if (pass.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
BLI_assert_msg((pass.manager_fingerprint_ != this->fingerprint_get()) ||
|
||||
(pass.view_fingerprint_ != view.fingerprint_get()),
|
||||
"Resources and view did not changed no need to update");
|
||||
BLI_assert_msg((view.manager_fingerprint_ == this->fingerprint_get()) &&
|
||||
(view.fingerprint_get() != 0),
|
||||
"Resources or view changed, but compute_visibility was not called");
|
||||
|
||||
pass.manager_fingerprint_ = this->fingerprint_get();
|
||||
pass.view_fingerprint_ = view.fingerprint_get();
|
||||
|
||||
pass.draw_commands_buf_.generate_commands(pass.headers_,
|
||||
pass.commands_,
|
||||
view.get_visibility_buffer(),
|
||||
view.visibility_word_per_draw(),
|
||||
view.view_len_,
|
||||
pass.use_custom_ids);
|
||||
}
|
||||
|
||||
void Manager::generate_commands(PassSortable &pass, View &view)
|
||||
{
|
||||
if (pass.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
pass.sort();
|
||||
generate_commands(static_cast<PassMain &>(pass), view);
|
||||
}
|
||||
|
||||
void Manager::generate_commands(PassSimple &pass)
|
||||
{
|
||||
if (pass.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
BLI_assert_msg(pass.manager_fingerprint_ != this->fingerprint_get(),
|
||||
"Resources did not changed since last generate_command, no need to update");
|
||||
pass.manager_fingerprint_ = this->fingerprint_get();
|
||||
|
||||
pass.draw_commands_buf_.generate_commands(pass.headers_, pass.commands_, pass.sub_passes_);
|
||||
}
|
||||
|
||||
void Manager::warm_shader_specialization(PassMain &pass)
|
||||
{
|
||||
if (pass.is_empty()) {
|
||||
return;
|
||||
}
|
||||
command::RecordingState state;
|
||||
pass.warm_shader_specialization(state);
|
||||
}
|
||||
|
||||
void Manager::warm_shader_specialization(PassSimple &pass)
|
||||
{
|
||||
if (pass.is_empty()) {
|
||||
return;
|
||||
}
|
||||
command::RecordingState state;
|
||||
pass.warm_shader_specialization(state);
|
||||
}
|
||||
|
||||
void Manager::submit_only(PassMain &pass, View &view)
|
||||
{
|
||||
if (pass.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
BLI_assert_msg(view.manager_fingerprint_ != 0, "compute_visibility was not called on this view");
|
||||
BLI_assert_msg(view.manager_fingerprint_ == this->fingerprint_get(),
|
||||
"Resources changed since last compute_visibility");
|
||||
BLI_assert_msg(pass.manager_fingerprint_ != 0, "generate_command was not called on this pass");
|
||||
BLI_assert_msg(pass.manager_fingerprint_ == this->fingerprint_get(),
|
||||
"Resources changed since last generate_command");
|
||||
/* The function generate_commands needs to be called for each view this pass is going to be
|
||||
* submitted with. This is because the commands are stored inside the pass and not per view. */
|
||||
BLI_assert_msg(pass.view_fingerprint_ == view.fingerprint_get(),
|
||||
"View have changed since last generate_commands or "
|
||||
"submitting with a different view");
|
||||
|
||||
debug_bind();
|
||||
|
||||
command::RecordingState state;
|
||||
state.inverted_view = view.is_inverted();
|
||||
|
||||
view.bind();
|
||||
pass.draw_commands_buf_.bind(state);
|
||||
|
||||
resource_bind();
|
||||
|
||||
pass.submit(state);
|
||||
|
||||
state.cleanup();
|
||||
}
|
||||
|
||||
void Manager::submit(PassMain &pass, View &view)
|
||||
{
|
||||
if (pass.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (view.manager_fingerprint_ != this->fingerprint_get()) {
|
||||
compute_visibility(view);
|
||||
}
|
||||
|
||||
if (pass.manager_fingerprint_ != this->fingerprint_get() ||
|
||||
pass.view_fingerprint_ != view.fingerprint_get())
|
||||
{
|
||||
generate_commands(pass, view);
|
||||
}
|
||||
|
||||
this->submit_only(pass, view);
|
||||
}
|
||||
|
||||
void Manager::submit(PassSortable &pass, View &view)
|
||||
{
|
||||
if (pass.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
pass.sort();
|
||||
|
||||
this->submit(static_cast<PassMain &>(pass), view);
|
||||
}
|
||||
|
||||
void Manager::submit(PassSimple &pass, bool inverted_view)
|
||||
{
|
||||
if (pass.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pass.has_generated_commands()) {
|
||||
generate_commands(pass);
|
||||
}
|
||||
|
||||
debug_bind();
|
||||
|
||||
command::RecordingState state;
|
||||
state.inverted_view = inverted_view;
|
||||
|
||||
pass.draw_commands_buf_.bind(state);
|
||||
|
||||
resource_bind();
|
||||
|
||||
pass.submit(state);
|
||||
|
||||
state.cleanup();
|
||||
}
|
||||
|
||||
void Manager::submit(PassSimple &pass, View &view)
|
||||
{
|
||||
if (pass.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
debug_bind();
|
||||
|
||||
view.bind();
|
||||
|
||||
this->submit(pass, view.is_inverted());
|
||||
}
|
||||
|
||||
Manager::SubmitDebugOutput Manager::submit_debug(PassSimple &pass, View &view)
|
||||
{
|
||||
submit(pass, view);
|
||||
|
||||
pass.draw_commands_buf_.resource_id_buf_.read();
|
||||
|
||||
Manager::SubmitDebugOutput output;
|
||||
output.resource_id = {pass.draw_commands_buf_.resource_id_buf_.data(),
|
||||
pass.draw_commands_buf_.resource_id_count_};
|
||||
/* There is no visibility data for PassSimple. */
|
||||
output.visibility = {static_cast<uint *>(view.get_visibility_buffer().data()), 0};
|
||||
return output;
|
||||
}
|
||||
|
||||
Manager::SubmitDebugOutput Manager::submit_debug(PassMain &pass, View &view)
|
||||
{
|
||||
submit(pass, view);
|
||||
|
||||
GPU_memory_barrier(GPU_BARRIER_BUFFER_UPDATE);
|
||||
|
||||
pass.draw_commands_buf_.resource_id_buf_.read();
|
||||
view.get_visibility_buffer().read();
|
||||
|
||||
Manager::SubmitDebugOutput output;
|
||||
output.resource_id = {pass.draw_commands_buf_.resource_id_buf_.data(),
|
||||
pass.draw_commands_buf_.resource_id_count_};
|
||||
output.visibility = {static_cast<uint *>(view.get_visibility_buffer().data()),
|
||||
divide_ceil_u(resource_len_, 32)};
|
||||
return output;
|
||||
}
|
||||
|
||||
Manager::DataDebugOutput Manager::data_debug()
|
||||
{
|
||||
matrix_buf.current().read();
|
||||
bounds_buf.current().read();
|
||||
infos_buf.current().read();
|
||||
|
||||
Manager::DataDebugOutput output;
|
||||
output.matrices = {matrix_buf.current().data(), resource_len_};
|
||||
output.bounds = {bounds_buf.current().data(), resource_len_};
|
||||
output.infos = {infos_buf.current().data(), resource_len_};
|
||||
return output;
|
||||
}
|
||||
|
||||
} // namespace blender::draw
|
||||
509
blender-5.2.0/source/blender/draw/intern/draw_manager.hh
Normal file
509
blender-5.2.0/source/blender/draw/intern/draw_manager.hh
Normal file
@@ -0,0 +1,509 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*
|
||||
* `draw::Manager` is the interface between scene data and viewport engines.
|
||||
*
|
||||
* It holds per component data (`ObjectInfo`, `ObjectMatrices`, ...) indexed per `ResourceHandle`.
|
||||
*
|
||||
* \note It is currently work in progress and should replace the old global draw manager.
|
||||
*/
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
#include "GPU_material.hh"
|
||||
|
||||
#include "draw_handle.hh"
|
||||
#include "draw_resource.hh"
|
||||
#include "draw_view.hh"
|
||||
|
||||
#include <atomic>
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace draw {
|
||||
|
||||
/* Forward declarations. */
|
||||
|
||||
namespace detail {
|
||||
template<typename T> class Pass;
|
||||
} // namespace detail
|
||||
|
||||
namespace command {
|
||||
class DrawCommandBuf;
|
||||
class DrawMultiBuf;
|
||||
} // namespace command
|
||||
|
||||
using PassSimple = detail::Pass<command::DrawCommandBuf>;
|
||||
using PassMain = detail::Pass<command::DrawMultiBuf>;
|
||||
class PassSortable;
|
||||
|
||||
class Manager {
|
||||
using ObjectMatricesBuf = StorageArrayBuffer<ObjectMatrices, 128>;
|
||||
using ObjectBoundsBuf = StorageArrayBuffer<ObjectBounds, 128>;
|
||||
using ObjectInfosBuf = StorageArrayBuffer<ObjectInfos, 128>;
|
||||
using ObjectAttributeBuf = StorageArrayBuffer<ObjectAttribute, 128>;
|
||||
using LayerAttributeBuf = UniformArrayBuffer<LayerAttribute, 512>;
|
||||
/**
|
||||
* TODO(@fclem): Remove once we get rid of old EEVEE code-base.
|
||||
* `DRW_RESOURCE_CHUNK_LEN = 512`.
|
||||
*/
|
||||
using ObjectAttributeLegacyBuf = UniformArrayBuffer<float4, 8 * 512>;
|
||||
|
||||
public:
|
||||
struct SubmitDebugOutput {
|
||||
/** Indexed by resource id. */
|
||||
Span<uint32_t> visibility;
|
||||
/** Indexed by drawn instance. */
|
||||
Span<uint32_t> resource_id;
|
||||
};
|
||||
|
||||
struct DataDebugOutput {
|
||||
/** Indexed by resource id. */
|
||||
Span<ObjectMatrices> matrices;
|
||||
/** Indexed by resource id. */
|
||||
Span<ObjectBounds> bounds;
|
||||
/** Indexed by resource id. */
|
||||
Span<ObjectInfos> infos;
|
||||
};
|
||||
|
||||
/**
|
||||
* Buffers containing all object data. Referenced by resource index.
|
||||
* Exposed as public members for shader access after sync.
|
||||
*/
|
||||
SwapChain<ObjectMatricesBuf, 2> matrix_buf;
|
||||
SwapChain<ObjectBoundsBuf, 2> bounds_buf;
|
||||
SwapChain<ObjectInfosBuf, 2> infos_buf;
|
||||
|
||||
/**
|
||||
* Object Attributes are reference by indirection data inside ObjectInfos.
|
||||
* This is because attribute list is arbitrary.
|
||||
*/
|
||||
ObjectAttributeBuf attributes_buf;
|
||||
|
||||
/**
|
||||
* Table of all View Layer attributes required by shaders, used to populate the buffer below.
|
||||
*/
|
||||
Map<uint32_t, GPULayerAttr> layer_attributes;
|
||||
|
||||
/**
|
||||
* Buffer of layer attribute values, indexed and sorted by the hash.
|
||||
*/
|
||||
LayerAttributeBuf layer_attributes_buf;
|
||||
|
||||
/**
|
||||
* List of textures coming from Image data-blocks.
|
||||
* They need to be reference-counted in order to avoid being freed in another thread.
|
||||
*/
|
||||
Vector<gpu::Texture *> acquired_textures;
|
||||
|
||||
private:
|
||||
/** Number of sync done by managers. Used for fingerprint. */
|
||||
static std::atomic<uint32_t> global_sync_counter_;
|
||||
|
||||
/* Local sync counter. Used for fingerprint. Must never be null. */
|
||||
uint32_t sync_counter_ = 1;
|
||||
|
||||
/** Number of resource handle recorded. */
|
||||
uint resource_len_ = 0;
|
||||
/** Number of object attribute recorded. */
|
||||
uint attribute_len_ = 0;
|
||||
|
||||
Object *object_active = nullptr;
|
||||
|
||||
public:
|
||||
Manager() {};
|
||||
~Manager();
|
||||
|
||||
/**
|
||||
* Create a unique resource handle for the given object.
|
||||
* Returns the existing handle if it exists.
|
||||
*/
|
||||
/* WORKAROUND: Instead of breaking const correctness everywhere, we only break it for this. */
|
||||
ResourceHandleRange unique_handle(const ObjectRef &ref);
|
||||
|
||||
ResourceHandleRange unique_handle_for_sculpt(const ObjectRef &ref);
|
||||
|
||||
/**
|
||||
* Create a new resource handle for the given object.
|
||||
*/
|
||||
ResourceHandleRange resource_handle(const ObjectRef &ref, float inflate_bounds = 0.0f);
|
||||
/**
|
||||
* Create a new resource handle for the given object, but optionally override model matrix and
|
||||
* bounds.
|
||||
*/
|
||||
ResourceHandleRange resource_handle(const ObjectRef &ref,
|
||||
const float4x4 *model_matrix,
|
||||
const float3 *bounds_center,
|
||||
const float3 *bounds_half_extent);
|
||||
/**
|
||||
* Get resource id for a loose matrix. The draw-calls for this resource handle won't be culled
|
||||
* and there won't be any associated object info / bounds. Assumes correct handedness / winding.
|
||||
*/
|
||||
ResourceHandle resource_handle(const float4x4 &model_matrix);
|
||||
/**
|
||||
* Get resource id for a loose matrix with bounds. The draw-calls for this resource handle will
|
||||
* be culled but there won't be any associated object info / bounds.
|
||||
* Assumes correct handedness / winding.
|
||||
*/
|
||||
ResourceHandle resource_handle(const float4x4 &model_matrix,
|
||||
const float3 &bounds_center,
|
||||
const float3 &bounds_half_extent);
|
||||
/**
|
||||
* Get resource id for particle system. The draw-calls for this resource won't be culled. The
|
||||
* associated object info will contain the info from its parent object.
|
||||
*/
|
||||
ResourceHandle resource_handle_for_psys(const ObjectRef &ref, const float4x4 &model_matrix);
|
||||
|
||||
/** Update the bounds of an already created handle. */
|
||||
void update_handle_bounds(ResourceHandle handle,
|
||||
const ObjectRef &ref,
|
||||
float inflate_bounds = 0.0f);
|
||||
|
||||
/**
|
||||
* Populate additional per resource data on demand.
|
||||
* IMPORTANT: Should be called only **once** per object.
|
||||
*/
|
||||
void extract_object_attributes(ResourceHandleRange handle,
|
||||
const ObjectRef &ref,
|
||||
const GPUMaterial *material);
|
||||
void extract_object_attributes(ResourceHandleRange handle,
|
||||
const ObjectRef &ref,
|
||||
Span<GPUMaterial *> materials);
|
||||
|
||||
/**
|
||||
* Collect necessary View Layer attributes.
|
||||
*/
|
||||
void register_layer_attributes(GPUMaterial *material);
|
||||
|
||||
/**
|
||||
* Compute <-> Graphic queue transition is quite slow on some backend. To avoid unnecessary
|
||||
* switching, it is better to dispatch all visibility computation as soon as possible before any
|
||||
* graphic work.
|
||||
*
|
||||
* Grouping the calls to `compute_visibility()` together is also beneficial for PSO switching
|
||||
* overhead. Same thing applies to `generate_commands()`.
|
||||
*
|
||||
* IMPORTANT: Generated commands are stored inside #PassMain and overrides commands generated for
|
||||
* a previous view.
|
||||
*
|
||||
* Before:
|
||||
* \code{.cpp}
|
||||
* manager.submit(pass1, view1);
|
||||
* manager.submit(pass2, view1);
|
||||
* manager.submit(pass1, view2);
|
||||
* manager.submit(pass2, view2);
|
||||
* \endcode
|
||||
*
|
||||
* After:
|
||||
* \code{.cpp}
|
||||
* manager.compute_visibility(view1);
|
||||
* manager.compute_visibility(view2);
|
||||
*
|
||||
* manager.generate_commands(pass1, view1);
|
||||
* manager.generate_commands(pass2, view1);
|
||||
*
|
||||
* manager.submit(pass1, view1);
|
||||
* manager.submit(pass2, view1);
|
||||
*
|
||||
* manager.generate_commands(pass1, view2);
|
||||
* manager.generate_commands(pass2, view2);
|
||||
*
|
||||
* manager.submit(pass1, view2);
|
||||
* manager.submit(pass2, view2);
|
||||
* \endcode
|
||||
*/
|
||||
|
||||
/**
|
||||
* Compute visibility of #ResourceHandle for the given #View.
|
||||
* The commands needs to be regenerated for any change inside the #Manager or in the #View.
|
||||
* Avoids just in time computation of visibility.
|
||||
*/
|
||||
void compute_visibility(View &view);
|
||||
/**
|
||||
* Same as compute_visibility but only do it if needed.
|
||||
*/
|
||||
void ensure_visibility(View &view);
|
||||
/**
|
||||
* Generate commands for #ResourceHandle for the given #View and #PassMain.
|
||||
* The commands needs to be regenerated for any change inside the #Manager, the #PassMain or in
|
||||
* the #View. Avoids just in time command generation.
|
||||
*
|
||||
* IMPORTANT: Generated commands are stored inside #PassMain and overrides commands previously
|
||||
* generated for a previous view.
|
||||
*/
|
||||
void generate_commands(PassMain &pass, View &view);
|
||||
void generate_commands(PassSortable &pass, View &view);
|
||||
/**
|
||||
* Generate commands on CPU. Doesn't have the GPU compute dispatch overhead.
|
||||
*/
|
||||
void generate_commands(PassSimple &pass);
|
||||
|
||||
/**
|
||||
* Make sure the shader specialization constants are already compiled.
|
||||
* This avoid stalling the real submission call because of specialization.
|
||||
*/
|
||||
void warm_shader_specialization(PassMain &pass);
|
||||
void warm_shader_specialization(PassSimple &pass);
|
||||
|
||||
/**
|
||||
* Submit a pass for drawing. All resource reference will be dereferenced and commands will be
|
||||
* sent to GPU. Visibility and command generation **must** have already been done explicitly
|
||||
* using `compute_visibility` and `generate_commands`.
|
||||
*/
|
||||
void submit_only(PassMain &pass, View &view);
|
||||
/**
|
||||
* Submit a pass for drawing. All resource reference will be dereferenced and commands will be
|
||||
* sent to GPU. Visibility and command generation are run JIT if needed.
|
||||
*/
|
||||
void submit(PassSimple &pass, View &view);
|
||||
void submit(PassMain &pass, View &view);
|
||||
void submit(PassSortable &pass, View &view);
|
||||
/**
|
||||
* Variant without any view. Must not contain any shader using `draw_view` create info.
|
||||
*/
|
||||
void submit(PassSimple &pass, bool inverted_view = false);
|
||||
|
||||
/**
|
||||
* Submit a pass for drawing but read back all data buffers for inspection.
|
||||
*/
|
||||
SubmitDebugOutput submit_debug(PassSimple &pass, View &view);
|
||||
SubmitDebugOutput submit_debug(PassMain &pass, View &view);
|
||||
|
||||
/**
|
||||
* Check data buffers of the draw manager. Only to be used after end_sync().
|
||||
*/
|
||||
DataDebugOutput data_debug();
|
||||
|
||||
/**
|
||||
* Will acquire the texture using ref counting and release it after drawing. To be used for
|
||||
* texture coming from blender Image.
|
||||
*/
|
||||
void acquire_texture(gpu::Texture *texture)
|
||||
{
|
||||
GPU_texture_ref(texture);
|
||||
acquired_textures.append(texture);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of resource handles allocated.
|
||||
*/
|
||||
uint resource_handle_count() const
|
||||
{
|
||||
return resource_len_;
|
||||
}
|
||||
|
||||
/** TODO(fclem): The following should become private at some point. */
|
||||
void begin_sync(Object *object_active = nullptr);
|
||||
void end_sync();
|
||||
|
||||
void debug_bind();
|
||||
void resource_bind();
|
||||
|
||||
private:
|
||||
void sync_layer_attributes();
|
||||
|
||||
/* Fingerprint of the manager in a certain state. Assured to not be 0.
|
||||
* Not reliable enough for general update detection. Only to be used for debugging assertion. */
|
||||
uint64_t fingerprint_get();
|
||||
};
|
||||
|
||||
inline ResourceHandleRange Manager::unique_handle(const ObjectRef &ref)
|
||||
{
|
||||
if (!ref.handle_.is_valid()) {
|
||||
/* WORKAROUND: Instead of breaking const correctness everywhere, we only break it for this. */
|
||||
const_cast<ObjectRef &>(ref).handle_ = resource_handle(ref);
|
||||
}
|
||||
return ref.handle_;
|
||||
}
|
||||
|
||||
inline ResourceHandleRange Manager::resource_handle(const ObjectRef &ref, float inflate_bounds)
|
||||
{
|
||||
bool is_active_object = ref.is_active(object_active);
|
||||
bool is_active_edit_mode = object_active &&
|
||||
(DRW_object_is_in_edit_mode(object_active) ||
|
||||
ELEM(object_active->mode, OB_MODE_TEXTURE_PAINT, OB_MODE_SCULPT)) &&
|
||||
ref.object->mode == object_active->mode;
|
||||
if (ref.duplis_) {
|
||||
uint start = resource_len_;
|
||||
|
||||
ObjectBounds proto_bounds;
|
||||
proto_bounds.sync(*ref.object, inflate_bounds);
|
||||
|
||||
ObjectInfos proto_info;
|
||||
proto_info.sync(ref, is_active_object, is_active_edit_mode);
|
||||
|
||||
for (const DupliObject *dupli : *ref.duplis_) {
|
||||
matrix_buf.current().get_or_resize(resource_len_).sync(float4x4(dupli->mat));
|
||||
bounds_buf.current().get_or_resize(resource_len_) = proto_bounds;
|
||||
|
||||
ObjectInfos &info = infos_buf.current().get_or_resize(resource_len_);
|
||||
info = proto_info;
|
||||
info.random = dupli->random_id * (1.0f / float(0xFFFFFFFF));
|
||||
|
||||
resource_len_++;
|
||||
}
|
||||
return ResourceHandleRange(ResourceHandle(start, (ref.object->transflag & OB_NEG_SCALE) != 0),
|
||||
resource_len_ - start);
|
||||
}
|
||||
else {
|
||||
matrix_buf.current().get_or_resize(resource_len_).sync(*ref.object);
|
||||
bounds_buf.current().get_or_resize(resource_len_).sync(*ref.object, inflate_bounds);
|
||||
infos_buf.current()
|
||||
.get_or_resize(resource_len_)
|
||||
.sync(ref, is_active_object, is_active_edit_mode);
|
||||
return ResourceHandle(resource_len_++, (ref.object->transflag & OB_NEG_SCALE) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
inline ResourceHandleRange Manager::resource_handle(const ObjectRef &ref,
|
||||
const float4x4 *model_matrix,
|
||||
const float3 *bounds_center,
|
||||
const float3 *bounds_half_extent)
|
||||
{
|
||||
BLI_assert(!ref.duplis_);
|
||||
bool is_active_object = ref.is_active(object_active);
|
||||
bool is_active_edit_mode = object_active &&
|
||||
(DRW_object_is_in_edit_mode(object_active) ||
|
||||
ELEM(object_active->mode, OB_MODE_TEXTURE_PAINT, OB_MODE_SCULPT)) &&
|
||||
ref.object->mode == object_active->mode;
|
||||
if (model_matrix) {
|
||||
matrix_buf.current().get_or_resize(resource_len_).sync(*model_matrix);
|
||||
}
|
||||
else {
|
||||
matrix_buf.current().get_or_resize(resource_len_).sync(*ref.object);
|
||||
}
|
||||
if (bounds_center && bounds_half_extent) {
|
||||
bounds_buf.current().get_or_resize(resource_len_).sync(*bounds_center, *bounds_half_extent);
|
||||
}
|
||||
else {
|
||||
bounds_buf.current().get_or_resize(resource_len_).sync(*ref.object);
|
||||
}
|
||||
infos_buf.current()
|
||||
.get_or_resize(resource_len_)
|
||||
.sync(ref, is_active_object, is_active_edit_mode);
|
||||
return ResourceHandle(resource_len_++, (ref.object->transflag & OB_NEG_SCALE) != 0);
|
||||
}
|
||||
|
||||
inline ResourceHandle Manager::resource_handle(const float4x4 &model_matrix)
|
||||
{
|
||||
matrix_buf.current().get_or_resize(resource_len_).sync(model_matrix);
|
||||
bounds_buf.current().get_or_resize(resource_len_).sync();
|
||||
infos_buf.current().get_or_resize(resource_len_).sync();
|
||||
return ResourceHandle(resource_len_++, false);
|
||||
}
|
||||
|
||||
inline ResourceHandle Manager::resource_handle(const float4x4 &model_matrix,
|
||||
const float3 &bounds_center,
|
||||
const float3 &bounds_half_extent)
|
||||
{
|
||||
matrix_buf.current().get_or_resize(resource_len_).sync(model_matrix);
|
||||
bounds_buf.current().get_or_resize(resource_len_).sync(bounds_center, bounds_half_extent);
|
||||
infos_buf.current().get_or_resize(resource_len_).sync();
|
||||
return ResourceHandle(resource_len_++, false);
|
||||
}
|
||||
|
||||
inline ResourceHandle Manager::resource_handle_for_psys(const ObjectRef &ref,
|
||||
const float4x4 &model_matrix)
|
||||
{
|
||||
BLI_assert(!ref.duplis_);
|
||||
bool is_active_object = ref.is_active(object_active);
|
||||
bool is_active_edit_mode = object_active &&
|
||||
(DRW_object_is_in_edit_mode(object_active) ||
|
||||
ELEM(object_active->mode, OB_MODE_TEXTURE_PAINT, OB_MODE_SCULPT)) &&
|
||||
ref.object->mode == object_active->mode;
|
||||
matrix_buf.current().get_or_resize(resource_len_).sync(model_matrix);
|
||||
bounds_buf.current().get_or_resize(resource_len_).sync();
|
||||
infos_buf.current()
|
||||
.get_or_resize(resource_len_)
|
||||
.sync(ref, is_active_object, is_active_edit_mode);
|
||||
return ResourceHandle(resource_len_++, (ref.object->transflag & OB_NEG_SCALE) != 0);
|
||||
}
|
||||
|
||||
inline void Manager::update_handle_bounds(ResourceHandle handle,
|
||||
const ObjectRef &ref,
|
||||
float inflate_bounds)
|
||||
{
|
||||
bounds_buf.current()[handle.index()].sync(*ref.object, inflate_bounds);
|
||||
}
|
||||
|
||||
inline void Manager::extract_object_attributes(ResourceHandleRange handle,
|
||||
const ObjectRef &ref,
|
||||
const GPUMaterial *material)
|
||||
{
|
||||
const GPUUniformAttrList *attr_list = GPU_material_uniform_attributes(material);
|
||||
if (attr_list == nullptr || attr_list->count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int instance_index = 0;
|
||||
for (ResourceID resource_id : handle.id_range()) {
|
||||
ObjectInfos &infos = infos_buf.current().get_or_resize(resource_id.index());
|
||||
infos.object_attrs_offset = attribute_len_;
|
||||
for (const GPUUniformAttr &attr : attr_list->list) {
|
||||
if (attributes_buf.get_or_resize(attribute_len_).sync(ref, *&attr, instance_index)) {
|
||||
infos.object_attrs_len++;
|
||||
attribute_len_++;
|
||||
}
|
||||
}
|
||||
instance_index++;
|
||||
}
|
||||
}
|
||||
|
||||
inline void Manager::extract_object_attributes(ResourceHandleRange handle,
|
||||
const ObjectRef &ref,
|
||||
Span<GPUMaterial *> materials)
|
||||
{
|
||||
Map<uint32_t, const GPUUniformAttr *, 4> attributes;
|
||||
|
||||
for (const GPUMaterial *mat : materials) {
|
||||
const GPUUniformAttrList *attr_list = GPU_material_uniform_attributes(mat);
|
||||
if (attr_list == nullptr) {
|
||||
continue;
|
||||
}
|
||||
for (const GPUUniformAttr &attr : attr_list->list) {
|
||||
attributes.add(attr.hash_code, &attr);
|
||||
}
|
||||
}
|
||||
|
||||
int instance_index = 0;
|
||||
for (ResourceID resource_id : handle.id_range()) {
|
||||
ObjectInfos &infos = infos_buf.current().get_or_resize(resource_id.index());
|
||||
infos.object_attrs_offset = attribute_len_;
|
||||
for (const GPUUniformAttr *attr : attributes.values()) {
|
||||
if (attributes_buf.get_or_resize(attribute_len_).sync(ref, *attr, instance_index)) {
|
||||
infos.object_attrs_len++;
|
||||
attribute_len_++;
|
||||
}
|
||||
}
|
||||
instance_index++;
|
||||
}
|
||||
}
|
||||
|
||||
inline void Manager::register_layer_attributes(GPUMaterial *material)
|
||||
{
|
||||
const ListBaseT<GPULayerAttr> *attr_list = GPU_material_layer_attributes(material);
|
||||
|
||||
if (attr_list != nullptr) {
|
||||
for (const GPULayerAttr &attr : *attr_list) {
|
||||
/** Since layer attributes are global to the whole render pass,
|
||||
* this only collects a table of their names. */
|
||||
layer_attributes.add(attr.hash_code, *&attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace draw
|
||||
|
||||
/* TODO(@fclem): This is for testing. The manager should be passed to the engine through the
|
||||
* callbacks. */
|
||||
draw::Manager *DRW_manager_get();
|
||||
|
||||
} // namespace blender
|
||||
684
blender-5.2.0/source/blender/draw/intern/draw_manager_text.cc
Normal file
684
blender-5.2.0/source/blender/draw/intern/draw_manager_text.cc
Normal file
@@ -0,0 +1,684 @@
|
||||
/* SPDX-FileCopyrightText: 2016 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_math_color.h"
|
||||
#include "BLI_math_geom.h"
|
||||
#include "BLI_math_matrix.h"
|
||||
#include "BLI_math_matrix.hh"
|
||||
#include "BLI_math_rotation.h"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_memiter.h"
|
||||
#include "BLI_rect.h"
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "BKE_editmesh.hh"
|
||||
#include "BKE_editmesh_cache.hh"
|
||||
#include "BKE_global.hh"
|
||||
#include "BKE_mesh.hh"
|
||||
#include "BKE_mesh_types.hh"
|
||||
#include "BKE_mesh_wrapper.hh"
|
||||
#include "BKE_object.hh"
|
||||
#include "BKE_unit.hh"
|
||||
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_userdef_types.h"
|
||||
#include "DNA_view3d_types.h"
|
||||
|
||||
#include "GPU_matrix.hh"
|
||||
#include "GPU_state.hh"
|
||||
|
||||
#include "ED_view3d.hh"
|
||||
|
||||
#include "UI_interface.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
#include "BLF_api.hh"
|
||||
#include "WM_api.hh"
|
||||
|
||||
#include "draw_manager_text.hh"
|
||||
#include "intern/bmesh_polygon.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct ViewCachedString {
|
||||
float vec[3];
|
||||
union {
|
||||
uchar ub[4];
|
||||
int pack;
|
||||
} col;
|
||||
short sco[2];
|
||||
short xoffs, yoffs;
|
||||
short flag;
|
||||
int str_len;
|
||||
bool shadow;
|
||||
bool align_center;
|
||||
|
||||
/* str is allocated past the end */
|
||||
char str[0];
|
||||
};
|
||||
|
||||
struct DRWTextStore {
|
||||
BLI_memiter *cache_strings;
|
||||
};
|
||||
|
||||
DRWTextStore *DRW_text_cache_create()
|
||||
{
|
||||
DRWTextStore *dt = MEM_new_zeroed<DRWTextStore>(__func__);
|
||||
dt->cache_strings = BLI_memiter_create(1 << 14); /* 16kb */
|
||||
return dt;
|
||||
}
|
||||
|
||||
void DRW_text_cache_destroy(DRWTextStore *dt)
|
||||
{
|
||||
if (dt == nullptr) {
|
||||
return;
|
||||
}
|
||||
BLI_memiter_destroy(dt->cache_strings);
|
||||
MEM_delete(dt);
|
||||
}
|
||||
|
||||
void DRW_text_cache_add(DRWTextStore *dt,
|
||||
const float co[3],
|
||||
const char *str,
|
||||
const int str_len,
|
||||
short xoffs,
|
||||
short yoffs,
|
||||
short flag,
|
||||
const uchar col[4],
|
||||
const bool shadow,
|
||||
const bool align_center)
|
||||
{
|
||||
int alloc_len;
|
||||
ViewCachedString *vos;
|
||||
|
||||
if (flag & DRW_TEXT_CACHE_STRING_PTR) {
|
||||
BLI_assert(str_len == strlen(str));
|
||||
alloc_len = sizeof(void *);
|
||||
}
|
||||
else {
|
||||
alloc_len = str_len + 1;
|
||||
}
|
||||
|
||||
vos = static_cast<ViewCachedString *>(
|
||||
BLI_memiter_alloc(dt->cache_strings, sizeof(ViewCachedString) + alloc_len));
|
||||
|
||||
copy_v3_v3(vos->vec, co);
|
||||
copy_v4_v4_uchar(vos->col.ub, col);
|
||||
vos->xoffs = xoffs;
|
||||
vos->yoffs = yoffs;
|
||||
vos->flag = flag;
|
||||
vos->str_len = str_len;
|
||||
vos->shadow = shadow;
|
||||
vos->align_center = align_center;
|
||||
|
||||
/* allocate past the end */
|
||||
if (flag & DRW_TEXT_CACHE_STRING_PTR) {
|
||||
memcpy(vos->str, &str, alloc_len);
|
||||
}
|
||||
else {
|
||||
memcpy(vos->str, str, alloc_len);
|
||||
}
|
||||
}
|
||||
|
||||
static void drw_text_cache_draw_ex(const DRWTextStore *dt, const ARegion *region)
|
||||
{
|
||||
ViewCachedString *vos;
|
||||
BLI_memiter_handle it;
|
||||
int col_pack_prev = 0;
|
||||
|
||||
float original_proj[4][4];
|
||||
GPU_matrix_projection_get(original_proj);
|
||||
wmOrtho2_region_pixelspace(region);
|
||||
|
||||
GPU_matrix_push();
|
||||
GPU_matrix_identity_set();
|
||||
|
||||
BLF_default_size(ui::style_get()->widget.points);
|
||||
const int font_id = BLF_set_default();
|
||||
|
||||
float outline_dark_color[4] = {0, 0, 0, 0.8f};
|
||||
float outline_light_color[4] = {1, 1, 1, 0.8f};
|
||||
bool outline_is_dark = true;
|
||||
|
||||
BLI_memiter_iter_init(dt->cache_strings, &it);
|
||||
while ((vos = static_cast<ViewCachedString *>(BLI_memiter_iter_step(&it)))) {
|
||||
if (vos->sco[0] != IS_CLIPPED) {
|
||||
if (col_pack_prev != vos->col.pack) {
|
||||
BLF_color4ubv(font_id, vos->col.ub);
|
||||
const uchar lightness = srgb_to_grayscale_byte(vos->col.ub);
|
||||
outline_is_dark = lightness > 96;
|
||||
col_pack_prev = vos->col.pack;
|
||||
}
|
||||
|
||||
if (vos->align_center) {
|
||||
/* Measure the size of the string, then offset to align to the vertex. */
|
||||
float width, height;
|
||||
BLF_width_and_height(font_id,
|
||||
(vos->flag & DRW_TEXT_CACHE_STRING_PTR) ?
|
||||
*(reinterpret_cast<const char **>(vos->str)) :
|
||||
vos->str,
|
||||
vos->str_len,
|
||||
&width,
|
||||
&height);
|
||||
vos->xoffs -= short(width / 2.0f);
|
||||
vos->yoffs -= short(height / 2.0f);
|
||||
}
|
||||
|
||||
const int font_id = BLF_default();
|
||||
if (vos->shadow) {
|
||||
BLF_enable(font_id, BLF_SHADOW);
|
||||
BLF_shadow(font_id,
|
||||
FontShadowType::Outline,
|
||||
outline_is_dark ? outline_dark_color : outline_light_color);
|
||||
BLF_shadow_offset(font_id, 0, 0);
|
||||
}
|
||||
else {
|
||||
BLF_disable(font_id, BLF_SHADOW);
|
||||
}
|
||||
BLF_draw_default(float(vos->sco[0] + vos->xoffs),
|
||||
float(vos->sco[1] + vos->yoffs),
|
||||
2.0f,
|
||||
(vos->flag & DRW_TEXT_CACHE_STRING_PTR) ?
|
||||
*(reinterpret_cast<const char **>(vos->str)) :
|
||||
vos->str,
|
||||
vos->str_len);
|
||||
}
|
||||
}
|
||||
|
||||
GPU_matrix_pop();
|
||||
GPU_matrix_projection_set(original_proj);
|
||||
}
|
||||
|
||||
void DRW_text_cache_draw(const DRWTextStore *dt, const ARegion *region, const View3D *v3d)
|
||||
{
|
||||
ViewCachedString *vos;
|
||||
if (v3d) {
|
||||
RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
|
||||
int tot = 0;
|
||||
/* project first and test */
|
||||
BLI_memiter_handle it;
|
||||
BLI_memiter_iter_init(dt->cache_strings, &it);
|
||||
while ((vos = static_cast<ViewCachedString *>(BLI_memiter_iter_step(&it)))) {
|
||||
if (ED_view3d_project_short_ex(
|
||||
region,
|
||||
(vos->flag & DRW_TEXT_CACHE_GLOBALSPACE) ? rv3d->persmat : rv3d->persmatob,
|
||||
(vos->flag & DRW_TEXT_CACHE_LOCALCLIP) != 0,
|
||||
vos->vec,
|
||||
vos->sco,
|
||||
V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_WIN | V3D_PROJ_TEST_CLIP_NEAR) ==
|
||||
V3D_PROJ_RET_OK)
|
||||
{
|
||||
tot++;
|
||||
}
|
||||
else {
|
||||
vos->sco[0] = IS_CLIPPED;
|
||||
}
|
||||
}
|
||||
|
||||
if (tot) {
|
||||
/* Disable clipping for text */
|
||||
const bool rv3d_clipping_enabled = RV3D_CLIPPING_ENABLED(v3d, rv3d);
|
||||
if (rv3d_clipping_enabled) {
|
||||
GPU_clip_distances(0);
|
||||
}
|
||||
|
||||
drw_text_cache_draw_ex(dt, region);
|
||||
|
||||
if (rv3d_clipping_enabled) {
|
||||
GPU_clip_distances(6);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* project first */
|
||||
BLI_memiter_handle it;
|
||||
BLI_memiter_iter_init(dt->cache_strings, &it);
|
||||
const View2D *v2d = ®ion->v2d;
|
||||
float viewmat[4][4];
|
||||
rctf region_space = {0.0f, float(region->winx), 0.0f, float(region->winy)};
|
||||
BLI_rctf_transform_calc_m4_pivot_min(&v2d->cur, ®ion_space, viewmat);
|
||||
|
||||
while ((vos = static_cast<ViewCachedString *>(BLI_memiter_iter_step(&it)))) {
|
||||
float p[3];
|
||||
copy_v3_v3(p, vos->vec);
|
||||
mul_m4_v3(viewmat, p);
|
||||
|
||||
vos->sco[0] = p[0];
|
||||
vos->sco[1] = p[1];
|
||||
}
|
||||
|
||||
drw_text_cache_draw_ex(dt, region);
|
||||
}
|
||||
}
|
||||
|
||||
void DRW_text_edit_mesh_measure_stats(const ARegion *region,
|
||||
const View3D *v3d,
|
||||
const Object *ob,
|
||||
const UnitSettings &unit,
|
||||
DRWTextStore *dt)
|
||||
{
|
||||
/* Do not use ASCII when using non-default unit system, some unit chars are UTF8
|
||||
* (micro, square, etc.). See #36090. */
|
||||
const short txt_flag = DRW_TEXT_CACHE_GLOBALSPACE;
|
||||
const Mesh *mesh = BKE_object_get_editmesh_eval_cage(ob);
|
||||
if (!mesh) {
|
||||
return;
|
||||
}
|
||||
const BMEditMesh *em = mesh->runtime->edit_mesh.get();
|
||||
if (!BKE_editmesh_eval_orig_map_available(*mesh, BKE_object_get_pre_modified_mesh(ob))) {
|
||||
return;
|
||||
}
|
||||
char numstr[32]; /* Stores the measurement display text here */
|
||||
const char *conv_float; /* Use a float conversion matching the grid size */
|
||||
uchar4 col = {0, 0, 0, 255}; /* color of the text to draw */
|
||||
const float grid = unit.system ? unit.scale_length : v3d->grid;
|
||||
const bool do_global = (v3d->flag & V3D_GLOBAL_STATS) != 0;
|
||||
const bool do_moving = (G.moving & G_TRANSFORM_EDIT) != 0;
|
||||
float4x4 clip_planes;
|
||||
/* allow for displaying shape keys and deform mods */
|
||||
BMIter iter;
|
||||
const Span<float3> vert_positions = BKE_mesh_wrapper_vert_coords(mesh);
|
||||
const bool use_coords = !vert_positions.is_empty();
|
||||
|
||||
/* when 2 or more edge-info options are enabled, space apart */
|
||||
short edge_tex_count = 0;
|
||||
if (v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_EDGE_LEN) {
|
||||
edge_tex_count += 1;
|
||||
}
|
||||
if (v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_EDGE_ANG) {
|
||||
edge_tex_count += 1;
|
||||
}
|
||||
if ((v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_INDICES) && (em->selectmode & SCE_SELECT_EDGE)) {
|
||||
edge_tex_count += 1;
|
||||
}
|
||||
const short edge_tex_sep = short((edge_tex_count - 1) * 5.0f * UI_SCALE_FAC);
|
||||
|
||||
/* Make the precision of the display value proportionate to the grid-size. */
|
||||
|
||||
if (grid <= 0.01f) {
|
||||
conv_float = "%.6g";
|
||||
}
|
||||
else if (grid <= 0.1f) {
|
||||
conv_float = "%.5g";
|
||||
}
|
||||
else if (grid <= 1.0f) {
|
||||
conv_float = "%.4g";
|
||||
}
|
||||
else if (grid <= 10.0f) {
|
||||
conv_float = "%.3g";
|
||||
}
|
||||
else {
|
||||
conv_float = "%.2g";
|
||||
}
|
||||
|
||||
if (v3d->overlay.edit_flag &
|
||||
(V3D_OVERLAY_EDIT_EDGE_LEN | V3D_OVERLAY_EDIT_EDGE_ANG | V3D_OVERLAY_EDIT_INDICES))
|
||||
{
|
||||
BoundBox bb;
|
||||
const rcti rect = {0, region->winx, 0, region->winy};
|
||||
|
||||
ED_view3d_clipping_calc(&bb, clip_planes.ptr(), region, ob, &rect);
|
||||
}
|
||||
|
||||
if (v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_EDGE_LEN) {
|
||||
BMEdge *eed;
|
||||
|
||||
ui::theme::get_color_3ubv(TH_DRAWEXTRA_EDGELEN, col);
|
||||
|
||||
if (use_coords) {
|
||||
BM_mesh_elem_index_ensure(em->bm, BM_VERT);
|
||||
}
|
||||
|
||||
BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
|
||||
/* draw selected edges, or edges next to selected verts while dragging */
|
||||
if (BM_elem_flag_test(eed, BM_ELEM_SELECT) ||
|
||||
(do_moving && (BM_elem_flag_test(eed->v1, BM_ELEM_SELECT) ||
|
||||
BM_elem_flag_test(eed->v2, BM_ELEM_SELECT))))
|
||||
{
|
||||
float3 v1, v2;
|
||||
float3 v1_clip, v2_clip;
|
||||
|
||||
if (use_coords) {
|
||||
v1 = vert_positions[BM_elem_index_get(eed->v1)];
|
||||
v2 = vert_positions[BM_elem_index_get(eed->v2)];
|
||||
}
|
||||
else {
|
||||
v1 = eed->v1->co;
|
||||
v2 = eed->v2->co;
|
||||
}
|
||||
|
||||
if (clip_segment_v3_plane_n(v1, v2, clip_planes.ptr(), 4, v1_clip, v2_clip)) {
|
||||
const float3 co = math::transform_point(ob->object_to_world(),
|
||||
0.5 * (v1_clip + v2_clip));
|
||||
|
||||
if (do_global) {
|
||||
v1 = ob->object_to_world().view<3, 3>() * v1;
|
||||
v2 = ob->object_to_world().view<3, 3>() * v2;
|
||||
}
|
||||
|
||||
const size_t numstr_len = unit.system ?
|
||||
BKE_unit_value_as_string_scaled(numstr,
|
||||
sizeof(numstr),
|
||||
len_v3v3(v1, v2),
|
||||
3,
|
||||
B_UNIT_LENGTH,
|
||||
unit,
|
||||
false,
|
||||
true) :
|
||||
SNPRINTF_RLEN(numstr, conv_float, len_v3v3(v1, v2));
|
||||
|
||||
DRW_text_cache_add(dt, co, numstr, numstr_len, 0, edge_tex_sep, txt_flag, col);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_EDGE_ANG) {
|
||||
const bool is_rad = (unit.system_rotation == USER_UNIT_ROT_RADIANS);
|
||||
BMEdge *eed;
|
||||
|
||||
ui::theme::get_color_3ubv(TH_DRAWEXTRA_EDGEANG, col);
|
||||
|
||||
Span<float3> face_normals;
|
||||
if (use_coords) {
|
||||
BM_mesh_elem_index_ensure(em->bm, BM_VERT | BM_FACE);
|
||||
/* TODO: This is not const correct for wrapper meshes, but it should be okay because
|
||||
* every evaluated object gets its own evaluated cage mesh (they are not shared). */
|
||||
face_normals = BKE_mesh_wrapper_face_normals(const_cast<Mesh *>(mesh));
|
||||
}
|
||||
|
||||
BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
|
||||
BMLoop *l_a, *l_b;
|
||||
if (BM_edge_loop_pair(eed, &l_a, &l_b)) {
|
||||
/* Draw selected edges, or edges next to selected verts while dragging. */
|
||||
if (BM_elem_flag_test(eed, BM_ELEM_SELECT) ||
|
||||
(do_moving && (BM_elem_flag_test(eed->v1, BM_ELEM_SELECT) ||
|
||||
BM_elem_flag_test(eed->v2, BM_ELEM_SELECT) ||
|
||||
/* Special case, this is useful to show when verts connected
|
||||
* to this edge via a face are being transformed. */
|
||||
BM_elem_flag_test(l_a->next->next->v, BM_ELEM_SELECT) ||
|
||||
BM_elem_flag_test(l_a->prev->v, BM_ELEM_SELECT) ||
|
||||
BM_elem_flag_test(l_b->next->next->v, BM_ELEM_SELECT) ||
|
||||
BM_elem_flag_test(l_b->prev->v, BM_ELEM_SELECT))))
|
||||
{
|
||||
float3 v1, v2;
|
||||
float3 v1_clip, v2_clip;
|
||||
|
||||
if (use_coords) {
|
||||
v1 = vert_positions[BM_elem_index_get(eed->v1)];
|
||||
v2 = vert_positions[BM_elem_index_get(eed->v2)];
|
||||
}
|
||||
else {
|
||||
v1 = eed->v1->co;
|
||||
v2 = eed->v2->co;
|
||||
}
|
||||
|
||||
if (clip_segment_v3_plane_n(v1, v2, clip_planes.ptr(), 4, v1_clip, v2_clip)) {
|
||||
float3 no_a, no_b;
|
||||
|
||||
const float3 co = math::transform_point(ob->object_to_world(),
|
||||
0.5 * (v1_clip + v2_clip));
|
||||
|
||||
if (use_coords) {
|
||||
no_a = face_normals[BM_elem_index_get(l_a->f)];
|
||||
no_b = face_normals[BM_elem_index_get(l_b->f)];
|
||||
}
|
||||
else {
|
||||
no_a = l_a->f->no;
|
||||
no_b = l_b->f->no;
|
||||
}
|
||||
|
||||
if (do_global) {
|
||||
no_a = math::normalize(ob->world_to_object().view<3, 3>() * no_a);
|
||||
no_b = math::normalize(ob->world_to_object().view<3, 3>() * no_b);
|
||||
}
|
||||
|
||||
const float angle = angle_normalized_v3v3(no_a, no_b);
|
||||
|
||||
const size_t numstr_len = SNPRINTF_RLEN(numstr,
|
||||
"%.3f%s",
|
||||
(is_rad) ? angle : RAD2DEGF(angle),
|
||||
(is_rad) ? "r" : BLI_STR_UTF8_DEGREE_SIGN);
|
||||
|
||||
DRW_text_cache_add(dt, co, numstr, numstr_len, 0, -edge_tex_sep, txt_flag, col);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_FACE_AREA) {
|
||||
/* would be nice to use BM_face_calc_area, but that is for 2d faces
|
||||
* so instead add up tessellation triangle areas */
|
||||
|
||||
ui::theme::get_color_3ubv(TH_DRAWEXTRA_FACEAREA, col);
|
||||
|
||||
int i;
|
||||
BMFace *f = nullptr;
|
||||
/* Alternative to using `poly_to_tri_count(i, BM_elem_index_get(f->l_first))`
|
||||
* without having to add an extra loop. */
|
||||
int tri_index = 0;
|
||||
BM_ITER_MESH_INDEX (f, &iter, em->bm, BM_FACES_OF_MESH, i) {
|
||||
const int f_corner_tris_len = f->len - 2;
|
||||
if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
|
||||
int n = 0;
|
||||
float area = 0; /* area of the face */
|
||||
float3 vmid(0.0f);
|
||||
const std::array<BMLoop *, 3> *ltri_array = &em->looptris[tri_index];
|
||||
for (int j = 0; j < f_corner_tris_len; j++) {
|
||||
float3 v1, v2, v3;
|
||||
|
||||
if (use_coords) {
|
||||
v1 = vert_positions[BM_elem_index_get(ltri_array[j][0]->v)];
|
||||
v2 = vert_positions[BM_elem_index_get(ltri_array[j][1]->v)];
|
||||
v3 = vert_positions[BM_elem_index_get(ltri_array[j][2]->v)];
|
||||
}
|
||||
else {
|
||||
v1 = ltri_array[j][0]->v->co;
|
||||
v2 = ltri_array[j][1]->v->co;
|
||||
v3 = ltri_array[j][2]->v->co;
|
||||
}
|
||||
|
||||
vmid += v1;
|
||||
vmid += v2;
|
||||
vmid += v3;
|
||||
n += 3;
|
||||
|
||||
if (do_global) {
|
||||
v1 = ob->object_to_world().view<3, 3>() * v1;
|
||||
v2 = ob->object_to_world().view<3, 3>() * v2;
|
||||
v3 = ob->object_to_world().view<3, 3>() * v3;
|
||||
}
|
||||
|
||||
area += area_tri_v3(v1, v2, v3);
|
||||
}
|
||||
|
||||
vmid *= 1.0f / float(n);
|
||||
vmid = math::transform_point(ob->object_to_world(), vmid);
|
||||
|
||||
const size_t numstr_len =
|
||||
unit.system ? BKE_unit_value_as_string_scaled(
|
||||
numstr, sizeof(numstr), area, 3, B_UNIT_AREA, unit, false, true) :
|
||||
SNPRINTF_RLEN(numstr, conv_float, area);
|
||||
|
||||
DRW_text_cache_add(dt, vmid, numstr, numstr_len, 0, 0, txt_flag, col);
|
||||
}
|
||||
tri_index += f_corner_tris_len;
|
||||
}
|
||||
}
|
||||
|
||||
if (v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_FACE_ANG) {
|
||||
BMFace *efa;
|
||||
const bool is_rad = (unit.system_rotation == USER_UNIT_ROT_RADIANS);
|
||||
|
||||
ui::theme::get_color_3ubv(TH_DRAWEXTRA_FACEANG, col);
|
||||
|
||||
if (use_coords) {
|
||||
BM_mesh_elem_index_ensure(em->bm, BM_VERT);
|
||||
}
|
||||
|
||||
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
|
||||
const bool is_face_sel = BM_elem_flag_test_bool(efa, BM_ELEM_SELECT);
|
||||
|
||||
if (is_face_sel || do_moving) {
|
||||
BMIter liter;
|
||||
BMLoop *loop;
|
||||
bool is_first = true;
|
||||
|
||||
BM_ITER_ELEM (loop, &liter, efa, BM_LOOPS_OF_FACE) {
|
||||
if (is_face_sel || (do_moving && (BM_elem_flag_test(loop->v, BM_ELEM_SELECT) ||
|
||||
BM_elem_flag_test(loop->prev->v, BM_ELEM_SELECT) ||
|
||||
BM_elem_flag_test(loop->next->v, BM_ELEM_SELECT))))
|
||||
{
|
||||
float3 v1, v2, v3;
|
||||
float3 vmid;
|
||||
|
||||
/* lazy init center calc */
|
||||
if (is_first) {
|
||||
if (use_coords) {
|
||||
BM_face_calc_center_bounds_vcos(em->bm, efa, vmid, vert_positions);
|
||||
}
|
||||
else {
|
||||
BM_face_calc_center_bounds(efa, vmid);
|
||||
}
|
||||
is_first = false;
|
||||
}
|
||||
if (use_coords) {
|
||||
v1 = vert_positions[BM_elem_index_get(loop->prev->v)];
|
||||
v2 = vert_positions[BM_elem_index_get(loop->v)];
|
||||
v3 = vert_positions[BM_elem_index_get(loop->next->v)];
|
||||
}
|
||||
else {
|
||||
v1 = loop->prev->v->co;
|
||||
v2 = loop->v->co;
|
||||
v3 = loop->next->v->co;
|
||||
}
|
||||
|
||||
const float3 v2_local = v2;
|
||||
|
||||
if (do_global) {
|
||||
v1 = ob->object_to_world().view<3, 3>() * v1;
|
||||
v2 = ob->object_to_world().view<3, 3>() * v2;
|
||||
v3 = ob->object_to_world().view<3, 3>() * v3;
|
||||
}
|
||||
|
||||
const float angle = angle_v3v3v3(v1, v2, v3);
|
||||
|
||||
const size_t numstr_len = SNPRINTF_RLEN(numstr,
|
||||
"%.3f%s",
|
||||
(is_rad) ? angle : RAD2DEGF(angle),
|
||||
(is_rad) ? "r" : BLI_STR_UTF8_DEGREE_SIGN);
|
||||
const float3 co = math::transform_point(ob->object_to_world(),
|
||||
math::interpolate(vmid, v2_local, 0.8f));
|
||||
DRW_text_cache_add(dt, co, numstr, numstr_len, 0, 0, txt_flag, col);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* This option is for mesh ops and addons debugging; only available in UI if Blender starts
|
||||
* with
|
||||
* --debug */
|
||||
if (v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_INDICES) {
|
||||
int i;
|
||||
|
||||
ui::theme::get_color_4ubv(TH_TEXT_HI, col);
|
||||
|
||||
if (em->selectmode & SCE_SELECT_VERTEX) {
|
||||
BMVert *v;
|
||||
|
||||
if (use_coords) {
|
||||
BM_mesh_elem_index_ensure(em->bm, BM_VERT);
|
||||
}
|
||||
BM_ITER_MESH_INDEX (v, &iter, em->bm, BM_VERTS_OF_MESH, i) {
|
||||
if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
|
||||
const float3 co = math::transform_point(
|
||||
ob->object_to_world(), use_coords ? vert_positions[BM_elem_index_get(v)] : v->co);
|
||||
|
||||
const size_t numstr_len = SNPRINTF_RLEN(numstr, "%d", i);
|
||||
DRW_text_cache_add(dt, co, numstr, numstr_len, 0, 0, txt_flag, col, true, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (em->selectmode & SCE_SELECT_EDGE) {
|
||||
BMEdge *eed;
|
||||
|
||||
const bool use_edge_tex_sep = (edge_tex_count == 2);
|
||||
const bool use_edge_tex_len = (v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_EDGE_LEN);
|
||||
|
||||
BM_ITER_MESH_INDEX (eed, &iter, em->bm, BM_EDGES_OF_MESH, i) {
|
||||
if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
|
||||
float3 v1, v2;
|
||||
float3 v1_clip, v2_clip;
|
||||
|
||||
if (use_coords) {
|
||||
v1 = vert_positions[BM_elem_index_get(eed->v1)];
|
||||
v2 = vert_positions[BM_elem_index_get(eed->v2)];
|
||||
}
|
||||
else {
|
||||
v1 = eed->v1->co;
|
||||
v2 = eed->v2->co;
|
||||
}
|
||||
|
||||
if (clip_segment_v3_plane_n(v1, v2, clip_planes.ptr(), 4, v1_clip, v2_clip)) {
|
||||
const float3 co = math::transform_point(ob->object_to_world(),
|
||||
0.5 * (v1_clip + v2_clip));
|
||||
|
||||
const size_t numstr_len = SNPRINTF_RLEN(numstr, "%d", i);
|
||||
DRW_text_cache_add(
|
||||
dt,
|
||||
co,
|
||||
numstr,
|
||||
numstr_len,
|
||||
0,
|
||||
(use_edge_tex_sep) ? (use_edge_tex_len) ? -edge_tex_sep : edge_tex_sep : 0,
|
||||
txt_flag,
|
||||
col,
|
||||
true,
|
||||
false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (em->selectmode & SCE_SELECT_FACE) {
|
||||
BMFace *f;
|
||||
|
||||
if (use_coords) {
|
||||
BM_mesh_elem_index_ensure(em->bm, BM_VERT);
|
||||
}
|
||||
|
||||
BM_ITER_MESH_INDEX (f, &iter, em->bm, BM_FACES_OF_MESH, i) {
|
||||
if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
|
||||
float3 co;
|
||||
|
||||
if (use_coords) {
|
||||
BM_face_calc_center_median_vcos(em->bm, f, co, vert_positions);
|
||||
}
|
||||
else {
|
||||
BM_face_calc_center_median(f, co);
|
||||
}
|
||||
|
||||
co = math::transform_point(ob->object_to_world(), co);
|
||||
|
||||
const size_t numstr_len = SNPRINTF_RLEN(numstr, "%d", i);
|
||||
DRW_text_cache_add(dt, co, numstr, numstr_len, 0, 0, txt_flag, col, true, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,54 @@
|
||||
/* SPDX-FileCopyrightText: 2016 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
#pragma once
|
||||
|
||||
struct ARegion;
|
||||
struct DRWTextStore;
|
||||
struct Object;
|
||||
struct UnitSettings;
|
||||
struct View3D;
|
||||
|
||||
DRWTextStore *DRW_text_cache_create();
|
||||
void DRW_text_cache_destroy(DRWTextStore *dt);
|
||||
|
||||
/* `draw_manager.cc` */
|
||||
DRWTextStore *DRW_text_cache_ensure();
|
||||
|
||||
void DRW_text_cache_add(DRWTextStore *dt,
|
||||
const float co[3],
|
||||
const char *str,
|
||||
int str_len,
|
||||
short xoffs,
|
||||
short yoffs,
|
||||
short flag,
|
||||
const uchar col[4],
|
||||
const bool shadow = false,
|
||||
const bool align_center = false);
|
||||
|
||||
void DRW_text_cache_draw(const DRWTextStore *dt, const ARegion *region, const View3D *v3d);
|
||||
|
||||
void DRW_text_edit_mesh_measure_stats(const ARegion *region,
|
||||
const View3D *v3d,
|
||||
const Object *ob,
|
||||
const UnitSettings &unit,
|
||||
DRWTextStore *dt = DRW_text_cache_ensure());
|
||||
|
||||
enum {
|
||||
// DRW_UNUSED_1 = (1 << 0), /* dirty */
|
||||
DRW_TEXT_CACHE_GLOBALSPACE = (1 << 1),
|
||||
DRW_TEXT_CACHE_LOCALCLIP = (1 << 2),
|
||||
/* reference the string by pointer */
|
||||
DRW_TEXT_CACHE_STRING_PTR = (1 << 3),
|
||||
};
|
||||
|
||||
} // namespace blender
|
||||
1678
blender-5.2.0/source/blender/draw/intern/draw_pass.hh
Normal file
1678
blender-5.2.0/source/blender/draw/intern/draw_pass.hh
Normal file
File diff suppressed because it is too large
Load Diff
1975
blender-5.2.0/source/blender/draw/intern/draw_pbvh.cc
Normal file
1975
blender-5.2.0/source/blender/draw/intern/draw_pbvh.cc
Normal file
File diff suppressed because it is too large
Load Diff
125
blender-5.2.0/source/blender/draw/intern/draw_pointcloud.cc
Normal file
125
blender-5.2.0/source/blender/draw/intern/draw_pointcloud.cc
Normal file
@@ -0,0 +1,125 @@
|
||||
/* SPDX-FileCopyrightText: 2017 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "DNA_pointcloud_types.h"
|
||||
|
||||
#include "GPU_batch.hh"
|
||||
#include "GPU_material.hh"
|
||||
#include "GPU_shader.hh"
|
||||
#include "GPU_texture.hh"
|
||||
#include "GPU_vertex_buffer.hh"
|
||||
|
||||
#include "DRW_render.hh"
|
||||
|
||||
#include "draw_cache_impl.hh"
|
||||
#include "draw_common.hh"
|
||||
#include "draw_common_c.hh"
|
||||
#include "draw_context_private.hh"
|
||||
#include "draw_pointcloud_private.hh"
|
||||
/* For drw_curves_get_attribute_sampler_name. */
|
||||
#include "draw_curves_private.hh"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
struct PointCloudModule {
|
||||
gpu::VertBuf *dummy_vbo = create_dummy_vbo();
|
||||
|
||||
~PointCloudModule()
|
||||
{
|
||||
GPU_VERTBUF_DISCARD_SAFE(dummy_vbo);
|
||||
}
|
||||
|
||||
private:
|
||||
gpu::VertBuf *create_dummy_vbo()
|
||||
{
|
||||
GPUVertFormat format = {0};
|
||||
uint dummy_id = GPU_vertformat_attr_add(
|
||||
&format, "dummy", gpu::VertAttrType::SFLOAT_32_32_32_32);
|
||||
|
||||
gpu::VertBuf *vbo = GPU_vertbuf_create_with_format_ex(
|
||||
format, GPU_USAGE_STATIC | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY);
|
||||
|
||||
const float vert[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
GPU_vertbuf_data_alloc(*vbo, 1);
|
||||
GPU_vertbuf_attr_fill(vbo, dummy_id, vert);
|
||||
return vbo;
|
||||
}
|
||||
};
|
||||
|
||||
void DRW_pointcloud_init(DRWData *drw_data)
|
||||
{
|
||||
if (drw_data == nullptr) {
|
||||
drw_data = drw_get().data;
|
||||
}
|
||||
if (drw_data->pointcloud_module == nullptr) {
|
||||
drw_data->pointcloud_module = MEM_new<PointCloudModule>("PointCloudModule");
|
||||
}
|
||||
}
|
||||
|
||||
void DRW_pointcloud_module_free(PointCloudModule *pointcloud_module)
|
||||
{
|
||||
MEM_delete(pointcloud_module);
|
||||
}
|
||||
|
||||
template<typename PassT>
|
||||
gpu::Batch *pointcloud_sub_pass_setup_implementation(PassT &sub_ps,
|
||||
Object *object,
|
||||
GPUMaterial *gpu_material)
|
||||
{
|
||||
BLI_assert(object->type == OB_POINTCLOUD);
|
||||
PointCloud &pointcloud = DRW_object_get_data_for_drawing<PointCloud>(*object);
|
||||
/* An empty point cloud should never result in a draw-call. However, the buffer binding commands
|
||||
* will still be executed. In this case, in order to avoid assertion, we bind dummy VBOs. */
|
||||
bool is_empty = pointcloud.totpoint == 0;
|
||||
|
||||
PointCloudModule &module = *drw_get().data->pointcloud_module;
|
||||
/* Ensure we have no unbound resources.
|
||||
* Required for Vulkan.
|
||||
* Fixes issues with certain GL drivers not drawing anything. */
|
||||
sub_ps.bind_texture("u", module.dummy_vbo);
|
||||
sub_ps.bind_texture("au", module.dummy_vbo);
|
||||
sub_ps.bind_texture("a", module.dummy_vbo);
|
||||
sub_ps.bind_texture("c", module.dummy_vbo);
|
||||
sub_ps.bind_texture("ac", module.dummy_vbo);
|
||||
|
||||
gpu::VertBuf *pos_rad_buf = pointcloud_position_and_radius_get(&pointcloud);
|
||||
sub_ps.bind_texture("ptcloud_pos_rad_tx", is_empty ? module.dummy_vbo : pos_rad_buf);
|
||||
|
||||
if (gpu_material != nullptr) {
|
||||
ListBaseT<GPUMaterialAttribute> gpu_attrs = GPU_material_attributes(gpu_material);
|
||||
for (GPUMaterialAttribute &gpu_attr : gpu_attrs) {
|
||||
char sampler_name[32];
|
||||
/** NOTE: Reusing curve attribute function. */
|
||||
drw_curves_get_attribute_sampler_name(gpu_attr.name, sampler_name);
|
||||
|
||||
gpu::VertBuf **attribute_buf = DRW_pointcloud_evaluated_attribute(&pointcloud,
|
||||
gpu_attr.name);
|
||||
sub_ps.bind_texture(sampler_name,
|
||||
(attribute_buf && !is_empty) ? attribute_buf : &module.dummy_vbo);
|
||||
}
|
||||
}
|
||||
|
||||
gpu::Batch *geom = pointcloud_surface_get(&pointcloud);
|
||||
return geom;
|
||||
}
|
||||
|
||||
gpu::Batch *pointcloud_sub_pass_setup(PassMain::Sub &sub_ps,
|
||||
Object *object,
|
||||
GPUMaterial *gpu_material)
|
||||
{
|
||||
return pointcloud_sub_pass_setup_implementation(sub_ps, object, gpu_material);
|
||||
}
|
||||
|
||||
gpu::Batch *pointcloud_sub_pass_setup(PassSimple::Sub &sub_ps,
|
||||
Object *object,
|
||||
GPUMaterial *gpu_material)
|
||||
{
|
||||
return pointcloud_sub_pass_setup_implementation(sub_ps, object, gpu_material);
|
||||
}
|
||||
|
||||
} // namespace blender::draw
|
||||
@@ -0,0 +1,29 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct PointCloud;
|
||||
namespace gpu {
|
||||
class Batch;
|
||||
class VertBuf;
|
||||
} // namespace gpu
|
||||
struct GPUMaterial;
|
||||
|
||||
namespace draw {
|
||||
|
||||
gpu::VertBuf *pointcloud_position_and_radius_get(PointCloud *pointcloud);
|
||||
gpu::Batch **pointcloud_surface_shaded_get(PointCloud *pointcloud,
|
||||
GPUMaterial **gpu_materials,
|
||||
int mat_len);
|
||||
gpu::Batch *pointcloud_surface_get(PointCloud *pointcloud);
|
||||
|
||||
} // namespace draw
|
||||
} // namespace blender
|
||||
45
blender-5.2.0/source/blender/draw/intern/draw_resource.cc
Normal file
45
blender-5.2.0/source/blender/draw/intern/draw_resource.cc
Normal file
@@ -0,0 +1,45 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "BKE_duplilist.hh"
|
||||
#include "GPU_material.hh"
|
||||
|
||||
#include "draw_handle.hh"
|
||||
#include "draw_shader_shared.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name ObjectAttributes
|
||||
* \{ */
|
||||
|
||||
bool ObjectAttribute::sync(const draw::ObjectRef &ref,
|
||||
const GPUUniformAttr &attr,
|
||||
int instance_index)
|
||||
{
|
||||
/* This function mirrors `lookup_instance_property` in `cycles/blender/blender_object.cpp`. */
|
||||
hash_code = attr.hash_code;
|
||||
return ref.find_rgba_attribute(attr, instance_index, &data_x);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name LayerAttributes
|
||||
* \{ */
|
||||
|
||||
bool LayerAttribute::sync(const Scene *scene, const ViewLayer *layer, const GPULayerAttr &attr)
|
||||
{
|
||||
hash_code = attr.hash_code;
|
||||
|
||||
return BKE_view_layer_find_rgba_attribute(scene, layer, attr.name, &data.x);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
271
blender-5.2.0/source/blender/draw/intern/draw_resource.hh
Normal file
271
blender-5.2.0/source/blender/draw/intern/draw_resource.hh
Normal file
@@ -0,0 +1,271 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*
|
||||
* Component / Object level resources like object attributes, matrices, visibility etc...
|
||||
* Each of them are reference by resource index (#ResourceHandle).
|
||||
*/
|
||||
|
||||
#include "BLI_bounds.hh"
|
||||
#include "BLI_math_base.h"
|
||||
#include "BLI_math_matrix.hh"
|
||||
|
||||
#include "BKE_curve.hh"
|
||||
#include "BKE_duplilist.hh"
|
||||
#include "BKE_mesh.h"
|
||||
#include "BKE_object.hh"
|
||||
#include "BKE_volume.hh"
|
||||
#include "BLI_hash.h"
|
||||
#include "DNA_curve_types.h"
|
||||
#include "DNA_layer_types.h"
|
||||
#include "DNA_meta_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
|
||||
#include "DRW_render.hh"
|
||||
#include "draw_handle.hh"
|
||||
#include "draw_shader_shared.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name ObjectMatrices
|
||||
* \{ */
|
||||
|
||||
inline void ObjectMatrices::sync(const Object &object)
|
||||
{
|
||||
model = object.object_to_world();
|
||||
model_inverse = object.world_to_object();
|
||||
}
|
||||
|
||||
inline void ObjectMatrices::sync(const float4x4 &model_matrix)
|
||||
{
|
||||
model = model_matrix;
|
||||
model_inverse = math::invert(model_matrix);
|
||||
}
|
||||
|
||||
inline std::ostream &operator<<(std::ostream &stream, const ObjectMatrices &matrices)
|
||||
{
|
||||
stream << "ObjectMatrices(" << std::endl;
|
||||
stream << "model=" << matrices.model << ", " << std::endl;
|
||||
stream << "model_inverse=" << matrices.model_inverse << ")" << std::endl;
|
||||
return stream;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name ObjectInfos
|
||||
* \{ */
|
||||
|
||||
inline void ObjectInfos::sync()
|
||||
{
|
||||
object_attrs_len = 0;
|
||||
object_attrs_offset = 0;
|
||||
/* Zero-Initialize since this data might still be accessible (See #154105). */
|
||||
orco_add = float3(0.0f);
|
||||
orco_mul = float3(0.0f);
|
||||
ob_color = float4(0.0f);
|
||||
index = 0;
|
||||
light_and_shadow_set_membership = 0;
|
||||
random = 0.0f;
|
||||
flag = eObjectInfoFlag::OBJECT_NO_INFO;
|
||||
shadow_terminator_normal_offset = 0.0f;
|
||||
shadow_terminator_geometry_offset = 0.0f;
|
||||
}
|
||||
|
||||
inline void ObjectInfos::sync(const draw::ObjectRef ref,
|
||||
bool is_active_object,
|
||||
bool is_active_edit_mode)
|
||||
{
|
||||
object_attrs_len = 0;
|
||||
object_attrs_offset = 0;
|
||||
light_and_shadow_set_membership = 0;
|
||||
|
||||
LightLinking *light_linking = ref.light_linking();
|
||||
if (light_linking) {
|
||||
light_and_shadow_set_membership |= light_linking->runtime.receiver_light_set;
|
||||
light_and_shadow_set_membership |= light_linking->runtime.blocker_shadow_set << 8;
|
||||
}
|
||||
|
||||
bool is_holdout = (ref.object->base_flag & BASE_HOLDOUT) ||
|
||||
(ref.object->visibility_flag & OB_HOLDOUT);
|
||||
|
||||
ob_color = ref.object->color;
|
||||
index = ref.object->index;
|
||||
SET_FLAG_FROM_TEST(flag, is_active_object, eObjectInfoFlag::OBJECT_ACTIVE);
|
||||
SET_FLAG_FROM_TEST(
|
||||
flag, ref.object->base_flag & BASE_SELECTED, eObjectInfoFlag::OBJECT_SELECTED);
|
||||
SET_FLAG_FROM_TEST(
|
||||
flag, ref.object->base_flag & BASE_FROM_DUPLI, eObjectInfoFlag::OBJECT_FROM_DUPLI);
|
||||
SET_FLAG_FROM_TEST(
|
||||
flag, ref.object->base_flag & BASE_FROM_SET, eObjectInfoFlag::OBJECT_FROM_SET);
|
||||
SET_FLAG_FROM_TEST(
|
||||
flag, ref.object->transflag & OB_NEG_SCALE, eObjectInfoFlag::OBJECT_NEGATIVE_SCALE);
|
||||
SET_FLAG_FROM_TEST(flag, is_holdout, eObjectInfoFlag::OBJECT_HOLDOUT);
|
||||
SET_FLAG_FROM_TEST(flag, is_active_edit_mode, eObjectInfoFlag::OBJECT_ACTIVE_EDIT_MODE);
|
||||
|
||||
if (ref.object->shadow_terminator_normal_offset > 0.0f) {
|
||||
using namespace blender::math;
|
||||
shadow_terminator_geometry_offset = ref.object->shadow_terminator_geometry_offset;
|
||||
shadow_terminator_normal_offset = ref.object->shadow_terminator_normal_offset *
|
||||
reduce_max(to_scale(ref.object->object_to_world()));
|
||||
}
|
||||
else {
|
||||
shadow_terminator_geometry_offset = 0.0f;
|
||||
shadow_terminator_normal_offset = 0.0f;
|
||||
}
|
||||
|
||||
random = ref.random(0);
|
||||
|
||||
if (ref.object->data == nullptr) {
|
||||
orco_add = float3(0.0f);
|
||||
orco_mul = float3(1.0f);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (GS(reinterpret_cast<ID *>(ref.object->data)->name)) {
|
||||
case ID_VO: {
|
||||
std::optional<const Bounds<float3>> bounds = BKE_volume_min_max(
|
||||
&DRW_object_get_data_for_drawing<const Volume>(*ref.object));
|
||||
if (bounds) {
|
||||
orco_add = math::midpoint(bounds->min, bounds->max);
|
||||
orco_mul = (bounds->max - bounds->min) * 0.5f;
|
||||
}
|
||||
else {
|
||||
orco_add = float3(0.0f);
|
||||
orco_mul = float3(1.0f);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ID_ME: {
|
||||
BKE_mesh_texspace_get(
|
||||
&DRW_object_get_data_for_drawing<Mesh>(*ref.object), orco_add, orco_mul);
|
||||
break;
|
||||
}
|
||||
case ID_CU_LEGACY: {
|
||||
Curve &cu = DRW_object_get_data_for_drawing<Curve>(*ref.object);
|
||||
BKE_curve_texspace_ensure(&cu);
|
||||
orco_add = cu.texspace_location;
|
||||
orco_mul = cu.texspace_size;
|
||||
break;
|
||||
}
|
||||
case ID_MB: {
|
||||
MetaBall &mb = DRW_object_get_data_for_drawing<MetaBall>(*ref.object);
|
||||
orco_add = mb.texspace_location;
|
||||
orco_mul = mb.texspace_size;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
orco_add = float3(0.0f);
|
||||
orco_mul = float3(1.0f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
inline std::ostream &operator<<(std::ostream &stream, const ObjectInfos &infos)
|
||||
{
|
||||
stream << "ObjectInfos(";
|
||||
if (infos.flag == eObjectInfoFlag::OBJECT_NO_INFO) {
|
||||
stream << "skipped)" << std::endl;
|
||||
return stream;
|
||||
}
|
||||
stream << "orco_add=" << infos.orco_add << ", ";
|
||||
stream << "orco_mul=" << infos.orco_mul << ", ";
|
||||
stream << "ob_color=" << infos.ob_color << ", ";
|
||||
stream << "index=" << infos.index << ", ";
|
||||
stream << "random=" << infos.random << ", ";
|
||||
stream << "flag=" << infos.flag << ")" << std::endl;
|
||||
return stream;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name ObjectBounds
|
||||
* \{ */
|
||||
|
||||
inline void ObjectBounds::sync()
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
/* Initialize to NaN for easier debugging of uninitialized data usage. */
|
||||
bounding_corners[0] = float4(NAN_FLT);
|
||||
bounding_corners[1] = float4(NAN_FLT);
|
||||
bounding_corners[2] = float4(NAN_FLT);
|
||||
bounding_corners[3] = float4(NAN_FLT);
|
||||
bounding_sphere = float4(NAN_FLT);
|
||||
#endif
|
||||
bounding_sphere.w = -1.0f; /* Disable test. */
|
||||
}
|
||||
|
||||
inline void ObjectBounds::sync(const Object &ob, float inflate_bounds)
|
||||
{
|
||||
const std::optional<Bounds<float3>> bounds = BKE_object_boundbox_get(&ob);
|
||||
if (!bounds) {
|
||||
#ifndef NDEBUG
|
||||
/* Initialize to NaN for easier debugging of uninitialized data usage. */
|
||||
bounding_corners[0] = float4(NAN_FLT);
|
||||
bounding_corners[1] = float4(NAN_FLT);
|
||||
bounding_corners[2] = float4(NAN_FLT);
|
||||
bounding_corners[3] = float4(NAN_FLT);
|
||||
bounding_sphere = float4(NAN_FLT);
|
||||
#endif
|
||||
bounding_sphere.w = -1.0f; /* Disable test. */
|
||||
return;
|
||||
}
|
||||
const std::array<float3, 8> corners = bounds::corners(*bounds);
|
||||
*reinterpret_cast<float3 *>(&bounding_corners[0]) = corners[0];
|
||||
*reinterpret_cast<float3 *>(&bounding_corners[1]) = corners[4];
|
||||
*reinterpret_cast<float3 *>(&bounding_corners[2]) = corners[3];
|
||||
*reinterpret_cast<float3 *>(&bounding_corners[3]) = corners[1];
|
||||
bounding_sphere.w = 0.0f; /* Enable test. */
|
||||
|
||||
if (inflate_bounds != 0.0f) {
|
||||
BLI_assert(inflate_bounds >= 0.0f);
|
||||
float p = inflate_bounds;
|
||||
float n = -inflate_bounds;
|
||||
bounding_corners[0] += float4(n, n, n, 0.0f);
|
||||
bounding_corners[1] += float4(p, n, n, 0.0f);
|
||||
bounding_corners[2] += float4(n, p, n, 0.0f);
|
||||
bounding_corners[3] += float4(n, n, p, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
inline void ObjectBounds::sync(const float3 ¢er, const float3 &size)
|
||||
{
|
||||
*reinterpret_cast<float3 *>(&bounding_corners[0]) = center - size;
|
||||
*reinterpret_cast<float3 *>(&bounding_corners[1]) = center + float3(+size.x, -size.y, -size.z);
|
||||
*reinterpret_cast<float3 *>(&bounding_corners[2]) = center + float3(-size.x, +size.y, -size.z);
|
||||
*reinterpret_cast<float3 *>(&bounding_corners[3]) = center + float3(-size.x, -size.y, +size.z);
|
||||
bounding_sphere.w = 0.0; /* Enable test. */
|
||||
}
|
||||
|
||||
inline std::ostream &operator<<(std::ostream &stream, const ObjectBounds &bounds)
|
||||
{
|
||||
stream << "ObjectBounds(";
|
||||
if (bounds.bounding_sphere.w == -1.0f) {
|
||||
stream << "skipped)" << std::endl;
|
||||
return stream;
|
||||
}
|
||||
stream << std::endl;
|
||||
stream << ".bounding_corners[0]"
|
||||
<< *reinterpret_cast<const float3 *>(&bounds.bounding_corners[0]) << std::endl;
|
||||
stream << ".bounding_corners[1]"
|
||||
<< *reinterpret_cast<const float3 *>(&bounds.bounding_corners[1]) << std::endl;
|
||||
stream << ".bounding_corners[2]"
|
||||
<< *reinterpret_cast<const float3 *>(&bounds.bounding_corners[2]) << std::endl;
|
||||
stream << ".bounding_corners[3]"
|
||||
<< *reinterpret_cast<const float3 *>(&bounds.bounding_corners[3]) << std::endl;
|
||||
stream << ".sphere=(pos=" << float3(bounds.bounding_sphere)
|
||||
<< ", rad=" << bounds.bounding_sphere.w << std::endl;
|
||||
stream << ")" << std::endl;
|
||||
return stream;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
191
blender-5.2.0/source/blender/draw/intern/draw_sculpt.cc
Normal file
191
blender-5.2.0/source/blender/draw/intern/draw_sculpt.cc
Normal file
@@ -0,0 +1,191 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "draw_sculpt.hh"
|
||||
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "draw_attributes.hh"
|
||||
#include "draw_context_private.hh"
|
||||
#include "draw_view.hh"
|
||||
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_attribute_legacy_convert.hh"
|
||||
#include "BKE_customdata.hh"
|
||||
#include "BKE_material.hh"
|
||||
#include "BKE_object.hh"
|
||||
#include "BKE_object_types.hh"
|
||||
#include "BKE_paint.hh"
|
||||
|
||||
#include "BLI_math_matrix.hh"
|
||||
|
||||
#include "bmesh_class.hh"
|
||||
|
||||
#include "DRW_pbvh.hh"
|
||||
#include "DRW_render.hh"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
float3 SculptBatch::debug_color()
|
||||
{
|
||||
static float3 colors[9] = {
|
||||
{1.0f, 0.2f, 0.2f},
|
||||
{0.2f, 1.0f, 0.2f},
|
||||
{0.2f, 0.2f, 1.0f},
|
||||
{1.0f, 1.0f, 0.2f},
|
||||
{0.2f, 1.0f, 1.0f},
|
||||
{1.0f, 0.2f, 1.0f},
|
||||
{1.0f, 0.7f, 0.2f},
|
||||
{0.2f, 1.0f, 0.7f},
|
||||
{0.7f, 0.2f, 1.0f},
|
||||
};
|
||||
|
||||
return colors[debug_index % 9];
|
||||
}
|
||||
|
||||
static Vector<SculptBatch> sculpt_batches_get_ex(const Object *ob,
|
||||
const bool use_wire,
|
||||
const Span<pbvh::AttributeRequest> attrs)
|
||||
{
|
||||
/* pbvh::Tree should always exist for non-empty meshes, created by depsgraph eval. */
|
||||
bke::pbvh::Tree *pbvh = ob->runtime->sculpt_session ?
|
||||
const_cast<bke::pbvh::Tree *>(bke::object::pbvh_get(*ob)) :
|
||||
nullptr;
|
||||
if (!pbvh) {
|
||||
return {};
|
||||
}
|
||||
|
||||
/* TODO(Miguel Pozo): Don't use global context. */
|
||||
const DRWContext *drwctx = DRW_context_get();
|
||||
RegionView3D *rv3d = drwctx->rv3d;
|
||||
const bool navigating = rv3d && (rv3d->rflag & RV3D_NAVIGATING);
|
||||
|
||||
Paint *paint = nullptr;
|
||||
if (drwctx->evil_C != nullptr) {
|
||||
paint = BKE_paint_get_active_from_context(drwctx->evil_C);
|
||||
}
|
||||
|
||||
/* TODO: take into account partial redraw for clipping planes. */
|
||||
/* Frustum planes to show only visible pbvh::Tree nodes. */
|
||||
std::array<float4, 6> draw_frustum_planes = View::default_get().frustum_planes_get();
|
||||
/* Transform clipping planes to object space. Transforming a plane with a
|
||||
* 4x4 matrix is done by multiplying with the transpose inverse.
|
||||
* The inverse cancels out here since we transform by inverse(obmat). */
|
||||
float4x4 tmat = math::transpose(ob->object_to_world());
|
||||
for (int i : IndexRange(draw_frustum_planes.size())) {
|
||||
draw_frustum_planes[i] = tmat * draw_frustum_planes[i];
|
||||
}
|
||||
|
||||
/* Fast mode to show low poly multires while navigating. */
|
||||
bool fast_mode = false;
|
||||
if (paint && (paint->flags & PAINT_FAST_NAVIGATE)) {
|
||||
fast_mode = navigating;
|
||||
}
|
||||
|
||||
/* Update draw buffers only for visible nodes while painting.
|
||||
* But do update them otherwise so navigating stays smooth. */
|
||||
bool update_only_visible = rv3d && !(rv3d->rflag & RV3D_PAINTING);
|
||||
if (paint && (paint->flags & PAINT_SCULPT_DELAY_UPDATES)) {
|
||||
update_only_visible = true;
|
||||
}
|
||||
|
||||
bke::pbvh::update_normals_from_eval(*const_cast<Object *>(ob), *pbvh);
|
||||
|
||||
pbvh::DrawCache &draw_data = pbvh::ensure_draw_data(pbvh->draw_data);
|
||||
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask visible_nodes = bke::pbvh::search_nodes(
|
||||
*pbvh, memory, [&](const bke::pbvh::Node &node) {
|
||||
return !BKE_pbvh_node_fully_hidden_get(node) &&
|
||||
bke::pbvh::node_frustum_contain_aabb(node, draw_frustum_planes);
|
||||
});
|
||||
|
||||
const IndexMask nodes_to_update = update_only_visible ? visible_nodes :
|
||||
bke::pbvh::all_leaf_nodes(*pbvh, memory);
|
||||
|
||||
Span<gpu::Batch *> batches;
|
||||
if (use_wire) {
|
||||
batches = draw_data.ensure_lines_batches(*ob, {{}, fast_mode}, nodes_to_update);
|
||||
}
|
||||
else {
|
||||
batches = draw_data.ensure_tris_batches(*ob, {attrs, fast_mode}, nodes_to_update);
|
||||
}
|
||||
|
||||
const Span<int> material_indices = draw_data.ensure_material_indices(*ob);
|
||||
|
||||
const int max_material = std::max(0, BKE_object_material_count_eval(ob) - 1);
|
||||
Vector<SculptBatch> result_batches(visible_nodes.size());
|
||||
visible_nodes.foreach_index([&](const int i, const int pos) {
|
||||
result_batches[pos] = {};
|
||||
result_batches[pos].batch = batches[i];
|
||||
result_batches[pos].material_slot = material_indices.is_empty() ?
|
||||
0 :
|
||||
std::clamp(material_indices[i], 0, max_material);
|
||||
result_batches[pos].debug_index = pos;
|
||||
});
|
||||
|
||||
return result_batches;
|
||||
}
|
||||
|
||||
Vector<SculptBatch> sculpt_batches_get(const Object *ob, SculptBatchFeature features)
|
||||
{
|
||||
Vector<pbvh::AttributeRequest, 16> attrs;
|
||||
|
||||
attrs.append(pbvh::CustomRequest::Position);
|
||||
attrs.append(pbvh::CustomRequest::Normal);
|
||||
if (features & SCULPT_BATCH_MASK) {
|
||||
attrs.append(pbvh::CustomRequest::Mask);
|
||||
}
|
||||
if (features & SCULPT_BATCH_FACE_SET) {
|
||||
attrs.append(pbvh::CustomRequest::FaceSet);
|
||||
}
|
||||
|
||||
const Mesh *mesh = BKE_object_get_original_mesh(ob);
|
||||
if (features & SCULPT_BATCH_VERTEX_COLOR) {
|
||||
if (const char *name = mesh->active_color_attribute) {
|
||||
attrs.append(pbvh::GenericRequest(name));
|
||||
}
|
||||
}
|
||||
|
||||
if (features & SCULPT_BATCH_UV) {
|
||||
const StringRef uv_name = mesh->active_uv_map_name();
|
||||
if (!uv_name.is_empty()) {
|
||||
attrs.append(pbvh::GenericRequest(uv_name));
|
||||
}
|
||||
}
|
||||
|
||||
return sculpt_batches_get_ex(ob, features & SCULPT_BATCH_WIREFRAME, attrs);
|
||||
}
|
||||
|
||||
Vector<SculptBatch> sculpt_batches_per_material_get(const Object *ob,
|
||||
Span<const GPUMaterial *> materials)
|
||||
{
|
||||
BLI_assert(ob->type == OB_MESH);
|
||||
const Mesh &mesh = DRW_object_get_data_for_drawing<Mesh>(*ob);
|
||||
|
||||
VectorSet<std::string> draw_attrs;
|
||||
DRW_MeshCDMask cd_needed;
|
||||
DRW_mesh_get_attributes(*ob, mesh, materials, &draw_attrs, &cd_needed);
|
||||
|
||||
Vector<pbvh::AttributeRequest, 16> attrs;
|
||||
|
||||
attrs.append(pbvh::CustomRequest::Position);
|
||||
attrs.append(pbvh::CustomRequest::Normal);
|
||||
|
||||
for (const StringRef name : draw_attrs) {
|
||||
attrs.append(pbvh::GenericRequest(name));
|
||||
}
|
||||
|
||||
for (const StringRef name : cd_needed.uv) {
|
||||
attrs.append(pbvh::GenericRequest(name));
|
||||
}
|
||||
|
||||
return sculpt_batches_get_ex(ob, false, attrs);
|
||||
}
|
||||
|
||||
} // namespace blender::draw
|
||||
50
blender-5.2.0/source/blender/draw/intern/draw_sculpt.hh
Normal file
50
blender-5.2.0/source/blender/draw/intern/draw_sculpt.hh
Normal file
@@ -0,0 +1,50 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_enum_flags.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct GPUMaterial;
|
||||
struct Object;
|
||||
namespace gpu {
|
||||
class Batch;
|
||||
}
|
||||
|
||||
namespace draw {
|
||||
|
||||
struct SculptBatch {
|
||||
gpu::Batch *batch;
|
||||
int material_slot;
|
||||
int debug_index;
|
||||
float3 debug_color();
|
||||
};
|
||||
|
||||
enum SculptBatchFeature {
|
||||
SCULPT_BATCH_DEFAULT = 0,
|
||||
SCULPT_BATCH_WIREFRAME = 1 << 0,
|
||||
SCULPT_BATCH_MASK = 1 << 1,
|
||||
SCULPT_BATCH_FACE_SET = 1 << 2,
|
||||
SCULPT_BATCH_VERTEX_COLOR = 1 << 3,
|
||||
SCULPT_BATCH_UV = 1 << 4
|
||||
};
|
||||
ENUM_OPERATORS(SculptBatchFeature);
|
||||
|
||||
/** Used by engines that don't use GPUMaterials, like the Workbench and Overlay engines. */
|
||||
Vector<SculptBatch> sculpt_batches_get(const Object *ob, SculptBatchFeature features);
|
||||
|
||||
/** Used by EEVEE. */
|
||||
Vector<SculptBatch> sculpt_batches_per_material_get(const Object *ob,
|
||||
Span<const GPUMaterial *> materials);
|
||||
|
||||
} // namespace draw
|
||||
} // namespace blender
|
||||
484
blender-5.2.0/source/blender/draw/intern/draw_select_buffer.cc
Normal file
484
blender-5.2.0/source/blender/draw/intern/draw_select_buffer.cc
Normal file
@@ -0,0 +1,484 @@
|
||||
/* SPDX-FileCopyrightText: 2019 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*
|
||||
* Utilities to read id buffer created in select_engine.
|
||||
*/
|
||||
|
||||
#include <cfloat>
|
||||
|
||||
#include "BLI_math_matrix.hh"
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_array_utils.h"
|
||||
#include "BLI_bitmap.h"
|
||||
#include "BLI_bitmap_draw_2d.h"
|
||||
#include "BLI_math_matrix.h"
|
||||
#include "BLI_rect.h"
|
||||
|
||||
#include "DNA_layer_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_view3d_types.h"
|
||||
|
||||
#include "GPU_framebuffer.hh"
|
||||
#include "GPU_select.hh"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
|
||||
#include "DRW_engine.hh"
|
||||
#include "DRW_render.hh"
|
||||
#include "DRW_select_buffer.hh"
|
||||
|
||||
#include "../engines/select/select_engine.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
bool SELECTID_Context::is_dirty(Depsgraph *depsgraph, RegionView3D *rv3d)
|
||||
{
|
||||
uint64_t last_update = this->depsgraph_last_update;
|
||||
this->depsgraph_last_update = DEG_get_update_count(depsgraph);
|
||||
|
||||
/* Check if the viewport has changed.
|
||||
* This can happen when triggering the selection operator *while* playing back animation and
|
||||
* looking through an animated camera. */
|
||||
if (!math::is_equal(this->persmat, float4x4(rv3d->persmat), FLT_EPSILON)) {
|
||||
return true;
|
||||
}
|
||||
/* Check if any of the drawn objects have been transformed.
|
||||
* This can happen when triggering the selection operator *while* playing back animation on an
|
||||
* edited mesh. */
|
||||
for (Object *obj_eval : this->objects) {
|
||||
if (obj_eval->runtime->last_update_transform > last_update) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Buffer of select ID's
|
||||
* \{ */
|
||||
|
||||
uint *DRW_select_buffer_read(
|
||||
Depsgraph *depsgraph, ARegion *region, View3D *v3d, const rcti *rect, uint *r_buf_len)
|
||||
{
|
||||
uint *buf = nullptr;
|
||||
uint buf_len = 0;
|
||||
|
||||
/* Clamp rect. */
|
||||
rcti r{};
|
||||
r.xmin = 0;
|
||||
r.xmax = region->winx;
|
||||
r.ymin = 0;
|
||||
r.ymax = region->winy;
|
||||
|
||||
/* Make sure that the rect is within the bounds of the viewport.
|
||||
* Some GPUs have problems reading pixels off limits. */
|
||||
rcti rect_clamp = *rect;
|
||||
if (BLI_rcti_isect(&r, &rect_clamp, &rect_clamp) && !BLI_rcti_is_empty(&rect_clamp)) {
|
||||
SELECTID_Context *select_ctx = DRW_select_engine_context_get();
|
||||
RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
|
||||
|
||||
DRW_gpu_context_enable();
|
||||
|
||||
if (select_ctx->is_dirty(depsgraph, rv3d)) {
|
||||
/* Update drawing. */
|
||||
DRW_draw_select_id(depsgraph, region, v3d);
|
||||
}
|
||||
|
||||
if (select_ctx->max_index_drawn_len > 1) {
|
||||
BLI_assert(region->winx == GPU_texture_width(DRW_engine_select_texture_get()) &&
|
||||
region->winy == GPU_texture_height(DRW_engine_select_texture_get()));
|
||||
|
||||
/* Read the UI32 pixels. */
|
||||
buf_len = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect);
|
||||
buf = MEM_new_array_uninitialized<uint>(buf_len, __func__);
|
||||
|
||||
gpu::FrameBuffer *select_id_fb = DRW_engine_select_framebuffer_get();
|
||||
GPU_framebuffer_bind(select_id_fb);
|
||||
GPU_framebuffer_read_color(select_id_fb,
|
||||
rect_clamp.xmin,
|
||||
rect_clamp.ymin,
|
||||
BLI_rcti_size_x(&rect_clamp),
|
||||
BLI_rcti_size_y(&rect_clamp),
|
||||
1,
|
||||
0,
|
||||
GPU_DATA_UINT,
|
||||
buf);
|
||||
|
||||
if (!BLI_rcti_compare(rect, &rect_clamp)) {
|
||||
/* The rect has been clamped so we need to realign the buffer and fill in the blanks */
|
||||
GPU_select_buffer_stride_realign(rect, &rect_clamp, buf);
|
||||
}
|
||||
}
|
||||
|
||||
GPU_framebuffer_restore();
|
||||
DRW_gpu_context_disable();
|
||||
}
|
||||
|
||||
if (r_buf_len) {
|
||||
*r_buf_len = buf_len;
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Select Bitmap from ID's
|
||||
*
|
||||
* Given a buffer of select ID's, fill in a booleans (true/false) per index.
|
||||
* #BLI_bitmap is used for memory efficiency.
|
||||
*
|
||||
* \{ */
|
||||
|
||||
uint *DRW_select_buffer_bitmap_from_rect(
|
||||
Depsgraph *depsgraph, ARegion *region, View3D *v3d, const rcti *rect, uint *r_bitmap_len)
|
||||
{
|
||||
SELECTID_Context *select_ctx = DRW_select_engine_context_get();
|
||||
|
||||
rcti rect_px = *rect;
|
||||
rect_px.xmax += 1;
|
||||
rect_px.ymax += 1;
|
||||
|
||||
uint buf_len;
|
||||
uint *buf = DRW_select_buffer_read(depsgraph, region, v3d, &rect_px, &buf_len);
|
||||
if (buf == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BLI_assert(select_ctx->max_index_drawn_len > 0);
|
||||
const uint bitmap_len = select_ctx->max_index_drawn_len - 1;
|
||||
|
||||
BLI_bitmap *bitmap_buf = BLI_BITMAP_NEW(bitmap_len, __func__);
|
||||
const uint *buf_iter = buf;
|
||||
while (buf_len--) {
|
||||
const uint index = *buf_iter - 1;
|
||||
if (index < bitmap_len) {
|
||||
BLI_BITMAP_ENABLE(bitmap_buf, index);
|
||||
}
|
||||
buf_iter++;
|
||||
}
|
||||
MEM_delete(buf);
|
||||
|
||||
if (r_bitmap_len) {
|
||||
*r_bitmap_len = bitmap_len;
|
||||
}
|
||||
|
||||
return bitmap_buf;
|
||||
}
|
||||
|
||||
uint *DRW_select_buffer_bitmap_from_circle(Depsgraph *depsgraph,
|
||||
ARegion *region,
|
||||
View3D *v3d,
|
||||
const int center[2],
|
||||
const int radius,
|
||||
uint *r_bitmap_len)
|
||||
{
|
||||
SELECTID_Context *select_ctx = DRW_select_engine_context_get();
|
||||
|
||||
rcti rect{};
|
||||
rect.xmin = center[0] - radius;
|
||||
rect.xmax = center[0] + radius + 1;
|
||||
rect.ymin = center[1] - radius;
|
||||
rect.ymax = center[1] + radius + 1;
|
||||
|
||||
const uint *buf = DRW_select_buffer_read(depsgraph, region, v3d, &rect, nullptr);
|
||||
|
||||
if (buf == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BLI_assert(select_ctx->max_index_drawn_len > 0);
|
||||
const uint bitmap_len = select_ctx->max_index_drawn_len - 1;
|
||||
|
||||
BLI_bitmap *bitmap_buf = BLI_BITMAP_NEW(bitmap_len, __func__);
|
||||
const uint *buf_iter = buf;
|
||||
const int radius_sq = radius * radius;
|
||||
for (int yc = -radius; yc <= radius; yc++) {
|
||||
for (int xc = -radius; xc <= radius; xc++, buf_iter++) {
|
||||
if (xc * xc + yc * yc < radius_sq) {
|
||||
/* Intentionally wrap to max value if this is zero. */
|
||||
const uint index = *buf_iter - 1;
|
||||
if (index < bitmap_len) {
|
||||
BLI_BITMAP_ENABLE(bitmap_buf, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MEM_delete(buf);
|
||||
|
||||
if (r_bitmap_len) {
|
||||
*r_bitmap_len = bitmap_len;
|
||||
}
|
||||
|
||||
return bitmap_buf;
|
||||
}
|
||||
|
||||
struct PolyMaskData {
|
||||
BLI_bitmap *px;
|
||||
int width;
|
||||
};
|
||||
|
||||
static void drw_select_mask_px_cb(int x, int x_end, int y, void *user_data)
|
||||
{
|
||||
PolyMaskData *data = static_cast<PolyMaskData *>(user_data);
|
||||
BLI_bitmap *px = data->px;
|
||||
int i = (y * data->width) + x;
|
||||
do {
|
||||
BLI_BITMAP_ENABLE(px, i);
|
||||
i++;
|
||||
} while (++x != x_end);
|
||||
}
|
||||
|
||||
uint *DRW_select_buffer_bitmap_from_poly(Depsgraph *depsgraph,
|
||||
ARegion *region,
|
||||
View3D *v3d,
|
||||
const Span<int2> poly,
|
||||
const rcti *rect,
|
||||
uint *r_bitmap_len)
|
||||
{
|
||||
SELECTID_Context *select_ctx = DRW_select_engine_context_get();
|
||||
|
||||
rcti rect_px = *rect;
|
||||
rect_px.xmax += 1;
|
||||
rect_px.ymax += 1;
|
||||
|
||||
uint buf_len;
|
||||
uint *buf = DRW_select_buffer_read(depsgraph, region, v3d, &rect_px, &buf_len);
|
||||
if (buf == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BLI_bitmap *buf_mask = BLI_BITMAP_NEW(buf_len, __func__);
|
||||
|
||||
PolyMaskData poly_mask_data;
|
||||
poly_mask_data.px = buf_mask;
|
||||
poly_mask_data.width = (rect->xmax - rect->xmin) + 1;
|
||||
|
||||
BLI_bitmap_draw_2d_poly_v2i_n(rect_px.xmin,
|
||||
rect_px.ymin,
|
||||
rect_px.xmax,
|
||||
rect_px.ymax,
|
||||
poly,
|
||||
drw_select_mask_px_cb,
|
||||
&poly_mask_data);
|
||||
|
||||
BLI_assert(select_ctx->max_index_drawn_len > 0);
|
||||
const uint bitmap_len = select_ctx->max_index_drawn_len - 1;
|
||||
|
||||
BLI_bitmap *bitmap_buf = BLI_BITMAP_NEW(bitmap_len, __func__);
|
||||
const uint *buf_iter = buf;
|
||||
int i = 0;
|
||||
while (buf_len--) {
|
||||
const uint index = *buf_iter - 1;
|
||||
if (index < bitmap_len && BLI_BITMAP_TEST(buf_mask, i)) {
|
||||
BLI_BITMAP_ENABLE(bitmap_buf, index);
|
||||
}
|
||||
buf_iter++;
|
||||
i++;
|
||||
}
|
||||
MEM_delete(buf);
|
||||
MEM_delete(buf_mask);
|
||||
|
||||
if (r_bitmap_len) {
|
||||
*r_bitmap_len = bitmap_len;
|
||||
}
|
||||
|
||||
return bitmap_buf;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Find Single Select ID's
|
||||
*
|
||||
* Given a buffer of select ID's, find the a single select id.
|
||||
*
|
||||
* \{ */
|
||||
|
||||
uint DRW_select_buffer_sample_point(Depsgraph *depsgraph,
|
||||
ARegion *region,
|
||||
View3D *v3d,
|
||||
const int center[2])
|
||||
{
|
||||
uint ret = 0;
|
||||
|
||||
rcti rect{};
|
||||
rect.xmin = center[0];
|
||||
rect.xmax = center[0] + 1;
|
||||
rect.ymin = center[1];
|
||||
rect.ymax = center[1] + 1;
|
||||
|
||||
uint buf_len;
|
||||
uint *buf = DRW_select_buffer_read(depsgraph, region, v3d, &rect, &buf_len);
|
||||
if (buf) {
|
||||
BLI_assert(0 != buf_len);
|
||||
ret = buf[0];
|
||||
MEM_delete(buf);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct SelectReadData {
|
||||
const void *val_ptr;
|
||||
uint id_min;
|
||||
uint id_max;
|
||||
uint r_index;
|
||||
};
|
||||
|
||||
static bool select_buffer_test_fn(const void *__restrict value, void *__restrict userdata)
|
||||
{
|
||||
SelectReadData *data = static_cast<SelectReadData *>(userdata);
|
||||
uint hit_id = *static_cast<uint *>(const_cast<void *>(value));
|
||||
if (hit_id && hit_id >= data->id_min && hit_id < data->id_max) {
|
||||
/* Start at 1 to confirm. */
|
||||
data->val_ptr = value;
|
||||
data->r_index = (hit_id - data->id_min) + 1;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint DRW_select_buffer_find_nearest_to_point(Depsgraph *depsgraph,
|
||||
ARegion *region,
|
||||
View3D *v3d,
|
||||
const int center[2],
|
||||
const uint id_min,
|
||||
const uint id_max,
|
||||
uint *dist)
|
||||
{
|
||||
/* Create region around center (typically the mouse cursor).
|
||||
* This must be square and have an odd width. */
|
||||
|
||||
rcti rect;
|
||||
BLI_rcti_init_pt_radius(&rect, center, *dist);
|
||||
rect.xmax += 1;
|
||||
rect.ymax += 1;
|
||||
|
||||
int width = BLI_rcti_size_x(&rect);
|
||||
int height = width;
|
||||
|
||||
/* Read from selection framebuffer. */
|
||||
|
||||
uint buf_len;
|
||||
const uint *buf = DRW_select_buffer_read(depsgraph, region, v3d, &rect, &buf_len);
|
||||
|
||||
if (buf == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const int shape[2] = {height, width};
|
||||
const int center_yx[2] = {(height - 1) / 2, (width - 1) / 2};
|
||||
SelectReadData data = {nullptr, id_min, id_max, 0};
|
||||
BLI_array_iter_spiral_square(buf, shape, center_yx, select_buffer_test_fn, &data);
|
||||
|
||||
if (data.val_ptr) {
|
||||
size_t offset = (size_t(data.val_ptr) - size_t(buf)) / sizeof(*buf);
|
||||
int hit_x = offset % width;
|
||||
int hit_y = offset / width;
|
||||
*dist = uint(abs(hit_y - center_yx[0]) + abs(hit_x - center_yx[1]));
|
||||
}
|
||||
|
||||
MEM_delete(buf);
|
||||
return data.r_index;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Object Utils
|
||||
* \{ */
|
||||
|
||||
bool DRW_select_buffer_elem_get(const uint sel_id,
|
||||
uint &r_elem,
|
||||
uint &r_base_index,
|
||||
char &r_elem_type)
|
||||
{
|
||||
SELECTID_Context *select_ctx = DRW_select_engine_context_get();
|
||||
|
||||
for (const auto &item : select_ctx->elem_ranges.items()) {
|
||||
const ElemIndexRanges &ranges = item.value;
|
||||
Object *ob = item.key;
|
||||
if (!ranges.total.contains(sel_id)) {
|
||||
continue;
|
||||
}
|
||||
if (ranges.face.contains(sel_id)) {
|
||||
r_elem = sel_id - ranges.face.start();
|
||||
r_elem_type = SCE_SELECT_FACE;
|
||||
r_base_index = select_ctx->objects.first_index_of_try(ob);
|
||||
return r_base_index != -1;
|
||||
}
|
||||
if (ranges.edge.contains(sel_id)) {
|
||||
r_elem = sel_id - ranges.edge.start();
|
||||
r_elem_type = SCE_SELECT_EDGE;
|
||||
r_base_index = select_ctx->objects.first_index_of_try(ob);
|
||||
return r_base_index != -1;
|
||||
}
|
||||
if (ranges.vert.contains(sel_id)) {
|
||||
r_elem = sel_id - ranges.vert.start();
|
||||
r_elem_type = SCE_SELECT_VERTEX;
|
||||
r_base_index = select_ctx->objects.first_index_of_try(ob);
|
||||
return r_base_index != -1;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint DRW_select_buffer_context_offset_for_object_elem(Depsgraph *depsgraph,
|
||||
Object *object,
|
||||
char elem_type)
|
||||
{
|
||||
SELECTID_Context *select_ctx = DRW_select_engine_context_get();
|
||||
|
||||
Object *ob_eval = DEG_get_evaluated(depsgraph, object);
|
||||
|
||||
const ElemIndexRanges base_ofs = select_ctx->elem_ranges.lookup_default(ob_eval,
|
||||
ElemIndexRanges{});
|
||||
|
||||
if (elem_type == SCE_SELECT_VERTEX) {
|
||||
return base_ofs.vert.start();
|
||||
}
|
||||
if (elem_type == SCE_SELECT_EDGE) {
|
||||
return base_ofs.edge.start();
|
||||
}
|
||||
if (elem_type == SCE_SELECT_FACE) {
|
||||
return base_ofs.face.start();
|
||||
}
|
||||
BLI_assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Context
|
||||
* \{ */
|
||||
|
||||
void DRW_select_buffer_context_create(Depsgraph *depsgraph,
|
||||
const Span<Base *> bases,
|
||||
short select_mode)
|
||||
{
|
||||
SELECTID_Context *select_ctx = DRW_select_engine_context_get();
|
||||
|
||||
select_ctx->objects.reinitialize(bases.size());
|
||||
|
||||
for (const int i : bases.index_range()) {
|
||||
Object *obj = bases[i]->object;
|
||||
select_ctx->objects[i] = DEG_get_evaluated(depsgraph, obj);
|
||||
}
|
||||
|
||||
select_ctx->select_mode = select_mode;
|
||||
select_ctx->persmat = float4x4::zero();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
238
blender-5.2.0/source/blender/draw/intern/draw_shader.cc
Normal file
238
blender-5.2.0/source/blender/draw/intern/draw_shader.cc
Normal file
@@ -0,0 +1,238 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*/
|
||||
|
||||
#include "draw_shader.hh"
|
||||
|
||||
#include "GPU_batch.hh"
|
||||
#include "GPU_capabilities.hh"
|
||||
|
||||
#include "opensubdiv_capi_type.hh"
|
||||
#include "opensubdiv_evaluator_capi.hh"
|
||||
|
||||
#include "DRW_render.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
#define SHADER_CUSTOM_DATA_INTERP_MAX_DIMENSIONS 4
|
||||
|
||||
static StringRefNull get_subdiv_shader_info_name(SubdivShaderType shader_type)
|
||||
{
|
||||
switch (shader_type) {
|
||||
case SubdivShaderType::BUFFER_LINES:
|
||||
return "subdiv_lines";
|
||||
|
||||
case SubdivShaderType::BUFFER_LINES_LOOSE:
|
||||
return "subdiv_lines_loose";
|
||||
|
||||
case SubdivShaderType::BUFFER_TRIS:
|
||||
return "subdiv_tris_single_material";
|
||||
|
||||
case SubdivShaderType::BUFFER_TRIS_MULTIPLE_MATERIALS:
|
||||
return "subdiv_tris_multiple_materials";
|
||||
|
||||
case SubdivShaderType::BUFFER_EDGE_FAC:
|
||||
return "subdiv_edge_fac";
|
||||
|
||||
case SubdivShaderType::BUFFER_SCULPT_DATA:
|
||||
return "subdiv_sculpt_data";
|
||||
|
||||
case SubdivShaderType::PATCH_EVALUATION:
|
||||
return "subdiv_patch_evaluation_verts";
|
||||
|
||||
case SubdivShaderType::PATCH_EVALUATION_FVAR:
|
||||
return "subdiv_patch_evaluation_fvar";
|
||||
|
||||
case SubdivShaderType::PATCH_EVALUATION_FACE_DOTS:
|
||||
return "subdiv_patch_evaluation_fdots";
|
||||
|
||||
case SubdivShaderType::PATCH_EVALUATION_FACE_DOTS_WITH_NORMALS:
|
||||
return "subdiv_patch_evaluation_fdots_normals";
|
||||
|
||||
case SubdivShaderType::PATCH_EVALUATION_ORCO:
|
||||
return "subdiv_patch_evaluation_verts_orcos";
|
||||
|
||||
case SubdivShaderType::BUFFER_UV_STRETCH_ANGLE:
|
||||
return "subdiv_edituv_stretch_angle";
|
||||
|
||||
case SubdivShaderType::BUFFER_UV_STRETCH_AREA:
|
||||
return "subdiv_edituv_stretch_area";
|
||||
|
||||
case SubdivShaderType::BUFFER_NORMALS_ACCUMULATE:
|
||||
return "subdiv_normals_accumulate";
|
||||
|
||||
case SubdivShaderType::BUFFER_PAINT_OVERLAY_FLAG:
|
||||
return "subdiv_paint_overlay_flag";
|
||||
|
||||
case SubdivShaderType::BUFFER_LNOR:
|
||||
return "subdiv_loop_normals";
|
||||
|
||||
case SubdivShaderType::COMP_CUSTOM_DATA_INTERP:
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return "";
|
||||
}
|
||||
|
||||
namespace draw::Shader {
|
||||
|
||||
class ShaderCache {
|
||||
static gpu::StaticShaderCache<ShaderCache> &get_static_cache()
|
||||
{
|
||||
static gpu::StaticShaderCache<ShaderCache> static_cache;
|
||||
return static_cache;
|
||||
}
|
||||
|
||||
public:
|
||||
static ShaderCache &get()
|
||||
{
|
||||
return get_static_cache().get();
|
||||
}
|
||||
static void release()
|
||||
{
|
||||
get_static_cache().release();
|
||||
}
|
||||
|
||||
gpu::StaticShader curves_topology = {"draw_curves_topology"};
|
||||
gpu::StaticShader curves_evaluate_position = {"draw_curves_interpolate_position"};
|
||||
gpu::StaticShader curves_evaluate_float4 = {"draw_curves_interpolate_float4_attribute"};
|
||||
gpu::StaticShader curves_evaluate_float3 = {"draw_curves_interpolate_float3_attribute"};
|
||||
gpu::StaticShader curves_evaluate_float2 = {"draw_curves_interpolate_float2_attribute"};
|
||||
gpu::StaticShader curves_evaluate_float = {"draw_curves_interpolate_float_attribute"};
|
||||
gpu::StaticShader curves_evaluate_length_intercept = {"draw_curves_evaluate_length_intercept"};
|
||||
gpu::StaticShader debug_draw_display = {"draw_debug_draw_display"};
|
||||
gpu::StaticShader draw_visibility_compute = {"draw_visibility_compute"};
|
||||
gpu::StaticShader draw_view_finalize = {"draw_view_finalize"};
|
||||
gpu::StaticShader draw_resource_finalize = {"draw_resource_finalize"};
|
||||
gpu::StaticShader draw_command_generate = {"draw_command_generate"};
|
||||
|
||||
gpu::StaticShader subdiv_sh[SUBDIVISION_MAX_SHADERS];
|
||||
gpu::StaticShader subdiv_custom_data_sh[SHADER_CUSTOM_DATA_INTERP_MAX_DIMENSIONS][GPU_COMP_MAX];
|
||||
gpu::StaticShader subdiv_interp_corner_normals_sh = {
|
||||
"subdiv_custom_data_interp_3d_f32_normalize"};
|
||||
|
||||
ShaderCache()
|
||||
{
|
||||
for (int i : IndexRange(SUBDIVISION_MAX_SHADERS)) {
|
||||
if (SubdivShaderType(i) == SubdivShaderType::COMP_CUSTOM_DATA_INTERP) {
|
||||
continue;
|
||||
}
|
||||
subdiv_sh[i] = {get_subdiv_shader_info_name(SubdivShaderType(i)).c_str()};
|
||||
}
|
||||
|
||||
for (int dimension : IndexRange(SHADER_CUSTOM_DATA_INTERP_MAX_DIMENSIONS)) {
|
||||
for (GPUVertCompType comp_type : {GPU_COMP_U16, GPU_COMP_I32, GPU_COMP_F32}) {
|
||||
std::string info_name = "subdiv_custom_data_interp";
|
||||
const char *dimension_names[] = {"_1d", "_2d", "_3d", "_4d"};
|
||||
info_name += dimension_names[dimension];
|
||||
|
||||
switch (comp_type) {
|
||||
case GPU_COMP_U16:
|
||||
info_name += "_u16";
|
||||
break;
|
||||
case GPU_COMP_I32:
|
||||
info_name += "_i32";
|
||||
break;
|
||||
case GPU_COMP_F32:
|
||||
info_name += "_f32";
|
||||
break;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
|
||||
subdiv_custom_data_sh[dimension][comp_type] = {info_name};
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace draw::Shader
|
||||
|
||||
using namespace blender::draw::Shader;
|
||||
|
||||
gpu::Shader *DRW_shader_curves_topology_get()
|
||||
{
|
||||
return ShaderCache::get().curves_topology.get();
|
||||
}
|
||||
|
||||
gpu::Shader *DRW_shader_curves_refine_get(draw::CurvesEvalShader type)
|
||||
{
|
||||
switch (type) {
|
||||
case draw::CURVES_EVAL_POSITION:
|
||||
return ShaderCache::get().curves_evaluate_position.get();
|
||||
case draw::CURVES_EVAL_FLOAT4:
|
||||
return ShaderCache::get().curves_evaluate_float4.get();
|
||||
case draw::CURVES_EVAL_FLOAT3:
|
||||
return ShaderCache::get().curves_evaluate_float3.get();
|
||||
case draw::CURVES_EVAL_FLOAT2:
|
||||
return ShaderCache::get().curves_evaluate_float2.get();
|
||||
case draw::CURVES_EVAL_FLOAT:
|
||||
return ShaderCache::get().curves_evaluate_float.get();
|
||||
case draw::CURVES_EVAL_LENGTH_INTERCEPT:
|
||||
return ShaderCache::get().curves_evaluate_length_intercept.get();
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
gpu::Shader *DRW_shader_debug_draw_display_get()
|
||||
{
|
||||
return ShaderCache::get().debug_draw_display.get();
|
||||
}
|
||||
|
||||
gpu::Shader *DRW_shader_draw_visibility_compute_get()
|
||||
{
|
||||
return ShaderCache::get().draw_visibility_compute.get();
|
||||
}
|
||||
|
||||
gpu::Shader *DRW_shader_draw_view_finalize_get()
|
||||
{
|
||||
return ShaderCache::get().draw_view_finalize.get();
|
||||
}
|
||||
|
||||
gpu::Shader *DRW_shader_draw_resource_finalize_get()
|
||||
{
|
||||
return ShaderCache::get().draw_resource_finalize.get();
|
||||
}
|
||||
|
||||
gpu::Shader *DRW_shader_draw_command_generate_get()
|
||||
{
|
||||
return ShaderCache::get().draw_command_generate.get();
|
||||
}
|
||||
|
||||
gpu::Shader *DRW_shader_subdiv_get(SubdivShaderType shader_type)
|
||||
{
|
||||
BLI_assert(!ELEM(shader_type, SubdivShaderType::COMP_CUSTOM_DATA_INTERP));
|
||||
return ShaderCache::get().subdiv_sh[uint(shader_type)].get();
|
||||
}
|
||||
|
||||
gpu::Shader *DRW_shader_subdiv_custom_data_get(GPUVertCompType comp_type, int dimensions)
|
||||
{
|
||||
BLI_assert(dimensions >= 1 && dimensions <= SHADER_CUSTOM_DATA_INTERP_MAX_DIMENSIONS);
|
||||
if (comp_type == GPU_COMP_U16) {
|
||||
BLI_assert(dimensions == 4);
|
||||
}
|
||||
BLI_assert(ELEM(comp_type, GPU_COMP_U16, GPU_COMP_I32, GPU_COMP_F32));
|
||||
|
||||
return ShaderCache::get().subdiv_custom_data_sh[dimensions - 1][comp_type].get();
|
||||
}
|
||||
|
||||
gpu::Shader *DRW_shader_subdiv_interp_corner_normals_get()
|
||||
{
|
||||
return ShaderCache::get().subdiv_interp_corner_normals_sh.get();
|
||||
}
|
||||
|
||||
void DRW_shaders_free()
|
||||
{
|
||||
GPU_shader_unbind();
|
||||
ShaderCache::release();
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
59
blender-5.2.0/source/blender/draw/intern/draw_shader.hh
Normal file
59
blender-5.2.0/source/blender/draw/intern/draw_shader.hh
Normal file
@@ -0,0 +1,59 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "draw_curves_private.hh"
|
||||
#include "draw_hair_private.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace gpu {
|
||||
class Shader;
|
||||
} // namespace gpu
|
||||
|
||||
/* draw_shader.cc */
|
||||
|
||||
gpu::Shader *DRW_shader_curves_topology_get();
|
||||
gpu::Shader *DRW_shader_curves_refine_get(draw::CurvesEvalShader type);
|
||||
|
||||
gpu::Shader *DRW_shader_debug_draw_display_get();
|
||||
gpu::Shader *DRW_shader_draw_visibility_compute_get();
|
||||
gpu::Shader *DRW_shader_draw_view_finalize_get();
|
||||
gpu::Shader *DRW_shader_draw_resource_finalize_get();
|
||||
gpu::Shader *DRW_shader_draw_command_generate_get();
|
||||
|
||||
/* Subdivision */
|
||||
enum class SubdivShaderType {
|
||||
BUFFER_LINES = 0,
|
||||
BUFFER_LINES_LOOSE = 1,
|
||||
BUFFER_EDGE_FAC = 2,
|
||||
BUFFER_LNOR = 3,
|
||||
BUFFER_TRIS = 4,
|
||||
BUFFER_TRIS_MULTIPLE_MATERIALS = 5,
|
||||
BUFFER_NORMALS_ACCUMULATE = 6,
|
||||
BUFFER_PAINT_OVERLAY_FLAG = 7,
|
||||
PATCH_EVALUATION = 8,
|
||||
PATCH_EVALUATION_FVAR = 9,
|
||||
PATCH_EVALUATION_FACE_DOTS = 10,
|
||||
PATCH_EVALUATION_FACE_DOTS_WITH_NORMALS = 11,
|
||||
PATCH_EVALUATION_ORCO = 12,
|
||||
COMP_CUSTOM_DATA_INTERP = 13,
|
||||
BUFFER_SCULPT_DATA = 14,
|
||||
BUFFER_UV_STRETCH_ANGLE = 15,
|
||||
BUFFER_UV_STRETCH_AREA = 16,
|
||||
};
|
||||
constexpr int SUBDIVISION_MAX_SHADERS = 17;
|
||||
|
||||
gpu::Shader *DRW_shader_subdiv_get(SubdivShaderType shader_type);
|
||||
gpu::Shader *DRW_shader_subdiv_custom_data_get(GPUVertCompType comp_type, int dimensions);
|
||||
gpu::Shader *DRW_shader_subdiv_interp_corner_normals_get();
|
||||
|
||||
void DRW_shaders_free();
|
||||
|
||||
} // namespace blender
|
||||
713
blender-5.2.0/source/blender/draw/intern/draw_shader_shared.hh
Normal file
713
blender-5.2.0/source/blender/draw/intern/draw_shader_shared.hh
Normal file
@@ -0,0 +1,713 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GPU_shader_shared_utils.hh"
|
||||
|
||||
#ifndef GPU_SHADER
|
||||
# include "BLI_math_vector.hh"
|
||||
# include "GPU_shader.hh"
|
||||
# include "draw_defines.hh"
|
||||
|
||||
# if defined(__cplusplus) && !defined(GPU_SHADER)
|
||||
|
||||
/* Defined on windows in `windows.h`. */
|
||||
# undef far
|
||||
# undef near
|
||||
|
||||
namespace blender {
|
||||
# endif
|
||||
|
||||
struct ViewCullingData;
|
||||
struct ViewMatrices;
|
||||
struct ObjectMatrices;
|
||||
struct ObjectInfos;
|
||||
struct ObjectBounds;
|
||||
struct VolumeInfos;
|
||||
struct CurvesInfos;
|
||||
struct ObjectAttribute;
|
||||
struct LayerAttribute;
|
||||
struct DrawCommand;
|
||||
struct DispatchCommand;
|
||||
struct DRWDebugPrintBuffer;
|
||||
struct DRWDebugVertPair;
|
||||
struct DRWDebugDrawBuffer;
|
||||
struct FrustumCorners;
|
||||
struct FrustumPlanes;
|
||||
|
||||
/* __cplusplus is true when compiling with MSL. */
|
||||
# if defined(__cplusplus) && !defined(GPU_SHADER)
|
||||
/* C++ only forward declarations. */
|
||||
struct Object;
|
||||
struct Scene;
|
||||
struct ViewLayer;
|
||||
struct GPUUniformAttr;
|
||||
struct GPULayerAttr;
|
||||
|
||||
namespace draw {
|
||||
|
||||
class ObjectRef;
|
||||
|
||||
} // namespace draw
|
||||
|
||||
using namespace math;
|
||||
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#define DRW_SHADER_SHARED_H
|
||||
|
||||
#define DRW_RESOURCE_CHUNK_LEN 512
|
||||
|
||||
/* Define the maximum number of grid we allow in a volume UBO. */
|
||||
#define DRW_GRID_PER_VOLUME_MAX 16
|
||||
|
||||
/* Define the maximum number of attribute we allow in a curves UBO.
|
||||
* This should be kept in sync with `GPU_ATTR_MAX` */
|
||||
#define DRW_ATTRIBUTE_PER_CURVES_MAX 15
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Views
|
||||
* \{ */
|
||||
|
||||
#if !defined(DRW_VIEW_LEN) && !defined(GLSL_CPP_STUBS)
|
||||
/* Single-view case (default). */
|
||||
# define drw_view_id 0
|
||||
# define DRW_VIEW_LEN 1
|
||||
# define DRW_VIEW_SHIFT 0
|
||||
# define DRW_VIEW_FROM_RESOURCE_ID
|
||||
#else
|
||||
|
||||
/* Multi-view case. */
|
||||
/** This should be already defined at shaderCreateInfo level. */
|
||||
// # define DRW_VIEW_LEN 64
|
||||
/** Global that needs to be set correctly in each shader stage. */
|
||||
uint drw_view_id = 0;
|
||||
/**
|
||||
* In order to reduce the memory requirements, the view id is merged with resource id to avoid
|
||||
* doubling the memory required only for view indexing.
|
||||
*/
|
||||
/** \note This is simply log2(DRW_VIEW_LEN) but making sure it is optimized out. */
|
||||
# define DRW_VIEW_SHIFT \
|
||||
((DRW_VIEW_LEN > 32) ? 6 : \
|
||||
(DRW_VIEW_LEN > 16) ? 5 : \
|
||||
(DRW_VIEW_LEN > 8) ? 4 : \
|
||||
(DRW_VIEW_LEN > 4) ? 3 : \
|
||||
(DRW_VIEW_LEN > 2) ? 2 : \
|
||||
1)
|
||||
# define DRW_VIEW_MASK ~(0xFFFFFFFFu << DRW_VIEW_SHIFT)
|
||||
# define DRW_VIEW_FROM_RESOURCE_ID drw_view_id = (drw_resource_id_raw() & DRW_VIEW_MASK)
|
||||
#endif
|
||||
|
||||
struct [[host_shared]] FrustumCorners {
|
||||
float4 corners[8];
|
||||
};
|
||||
|
||||
struct [[host_shared]] FrustumPlanes {
|
||||
/* [0] left
|
||||
* [1] right
|
||||
* [2] bottom
|
||||
* [3] top
|
||||
* [4] near
|
||||
* [5] far */
|
||||
float4 planes[6];
|
||||
};
|
||||
|
||||
struct [[host_shared]] ViewCullingData {
|
||||
/** \note float3 array padded to float4. */
|
||||
/** Frustum corners. */
|
||||
struct FrustumCorners frustum_corners;
|
||||
struct FrustumPlanes frustum_planes;
|
||||
float4 bound_sphere;
|
||||
};
|
||||
|
||||
struct [[host_shared]] ViewMatrices {
|
||||
float4x4 viewmat;
|
||||
float4x4 viewinv;
|
||||
float4x4 winmat;
|
||||
float4x4 wininv;
|
||||
|
||||
/* Returns true if the current view has a perspective projection matrix. */
|
||||
bool is_perspective() const
|
||||
{
|
||||
return winmat[3][3] == 0.0f;
|
||||
}
|
||||
|
||||
/* Returns the view forward vector, going towards the viewer. */
|
||||
float3 forward() const
|
||||
{
|
||||
return viewinv[2].xyz();
|
||||
}
|
||||
|
||||
/* Returns the view up vector. */
|
||||
float3 up() const
|
||||
{
|
||||
return viewinv[1].xyz();
|
||||
}
|
||||
|
||||
/* Returns the view origin. */
|
||||
float3 position() const
|
||||
{
|
||||
return viewinv[3].xyz();
|
||||
}
|
||||
|
||||
/* Positive Z distance from the view origin. Faster than using `drw_point_world_to_view`. */
|
||||
float z_distance(float3 P) const
|
||||
{
|
||||
return dot(P - position(), -forward());
|
||||
}
|
||||
|
||||
/* Returns the projection matrix far clip distance. */
|
||||
float far() const
|
||||
{
|
||||
if (is_perspective()) {
|
||||
return -winmat[3][2] / (winmat[2][2] + 1.0f);
|
||||
}
|
||||
return -(winmat[3][2] - 1.0f) / winmat[2][2];
|
||||
}
|
||||
|
||||
/* Returns the projection matrix near clip distance. */
|
||||
float near() const
|
||||
{
|
||||
if (is_perspective()) {
|
||||
return -winmat[3][2] / (winmat[2][2] - 1.0f);
|
||||
}
|
||||
return -(winmat[3][2] + 1.0f) / winmat[2][2];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the world incident vector `V` (going towards the viewer)
|
||||
* from the world position `P` and the current view.
|
||||
*/
|
||||
float3 world_incident_vector(float3 P) const
|
||||
{
|
||||
return is_perspective() ? normalize(position() - P) : forward();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the view incident vector `vV` (going towards the viewer)
|
||||
* from the view position `vP` and the current view.
|
||||
*/
|
||||
float3 view_incident_vector(float3 vP) const
|
||||
{
|
||||
return is_perspective() ? normalize(-vP) : float3(0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform position on screen UV space [0..1] to Normalized Device Coordinate space [-1..1].
|
||||
*/
|
||||
template<typename T> T screen_to_ndc(T ss_P) const
|
||||
{
|
||||
return ss_P * 2.0f - 1.0f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform position in Normalized Device Coordinate [-1..1] to screen UV space [0..1].
|
||||
*/
|
||||
template<typename T> T ndc_to_screen(T ndc_P) const
|
||||
{
|
||||
return ndc_P * 0.5f + 0.5f;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Transform Normal
|
||||
* \{ */
|
||||
|
||||
#ifdef GPU_SHADER /* `to_float3x3` doesn't exist on CPU side. */
|
||||
float3 normal_view_to_world(float3 vN) const
|
||||
{
|
||||
return (to_float3x3(viewinv) * vN);
|
||||
}
|
||||
|
||||
float3 normal_world_to_view(float3 N) const
|
||||
{
|
||||
return (to_float3x3(viewmat) * N);
|
||||
}
|
||||
#endif
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Transform Position
|
||||
* \{ */
|
||||
|
||||
float3 perspective_divide(float4 hs_P) const
|
||||
{
|
||||
return hs_P.xyz() / hs_P.w;
|
||||
}
|
||||
|
||||
float3 point_view_to_world(float3 vP) const
|
||||
{
|
||||
return (viewinv * float4(vP, 1.0f)).xyz();
|
||||
}
|
||||
|
||||
float4 point_view_to_homogenous(float3 vP) const
|
||||
{
|
||||
return (winmat * float4(vP, 1.0f));
|
||||
}
|
||||
|
||||
float3 point_view_to_ndc(float3 vP) const
|
||||
{
|
||||
return perspective_divide(point_view_to_homogenous(vP));
|
||||
}
|
||||
|
||||
float3 point_world_to_view(float3 P) const
|
||||
{
|
||||
return (viewmat * float4(P, 1.0f)).xyz();
|
||||
}
|
||||
|
||||
float4 point_world_to_homogenous(float3 P) const
|
||||
{
|
||||
return (winmat * (viewmat * float4(P, 1.0f)));
|
||||
}
|
||||
|
||||
float3 point_world_to_ndc(float3 P) const
|
||||
{
|
||||
return perspective_divide(point_world_to_homogenous(P));
|
||||
}
|
||||
|
||||
float3 point_ndc_to_view(float3 ssP) const
|
||||
{
|
||||
return perspective_divide(wininv * float4(ssP, 1.0f));
|
||||
}
|
||||
|
||||
float3 point_ndc_to_world(float3 ssP) const
|
||||
{
|
||||
return point_view_to_world(point_ndc_to_view(ssP));
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Transform Screen Positions
|
||||
* \{ */
|
||||
|
||||
float3 point_view_to_screen(float3 vP) const
|
||||
{
|
||||
return ndc_to_screen(point_view_to_ndc(vP));
|
||||
}
|
||||
|
||||
float3 point_world_to_screen(float3 vP) const
|
||||
{
|
||||
return ndc_to_screen(point_world_to_ndc(vP));
|
||||
}
|
||||
|
||||
float3 point_screen_to_view(float3 ssP) const
|
||||
{
|
||||
return point_ndc_to_view(screen_to_ndc(ssP));
|
||||
}
|
||||
|
||||
float3 point_screen_to_world(float3 ssP) const
|
||||
{
|
||||
return point_view_to_world(point_screen_to_view(ssP));
|
||||
}
|
||||
|
||||
float depth_view_to_screen(float v_depth) const
|
||||
{
|
||||
return point_view_to_screen(float3(0.0f, 0.0f, v_depth)).z;
|
||||
}
|
||||
|
||||
float depth_screen_to_view(float ss_depth) const
|
||||
{
|
||||
return point_screen_to_view(float3(0.0f, 0.0f, ss_depth)).z;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
};
|
||||
|
||||
template float ViewMatrices::screen_to_ndc<float>(float ss_P) const;
|
||||
template float2 ViewMatrices::screen_to_ndc<float2>(float2 ss_P) const;
|
||||
template float3 ViewMatrices::screen_to_ndc<float3>(float3 ss_P) const;
|
||||
template float ViewMatrices::ndc_to_screen<float>(float ss_P) const;
|
||||
template float2 ViewMatrices::ndc_to_screen<float2>(float2 ss_P) const;
|
||||
template float3 ViewMatrices::ndc_to_screen<float3>(float3 ss_P) const;
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Debug draw shapes
|
||||
* \{ */
|
||||
|
||||
struct [[host_shared]] ObjectMatrices {
|
||||
float4x4 model;
|
||||
float4x4 model_inverse;
|
||||
|
||||
#ifndef GPU_SHADER
|
||||
void sync(const Object &object);
|
||||
void sync(const float4x4 &model_matrix);
|
||||
#endif
|
||||
|
||||
#ifdef GPU_SHADER /* `to_float3x3` doesn't exist on CPU side. */
|
||||
/**
|
||||
* Usually Normal matrix is `transpose(inverse(ViewMatrix * ModelMatrix))`.
|
||||
*
|
||||
* But since it is slow to multiply matrices we decompose it. Decomposing
|
||||
* inversion and transposition both invert the product order leaving us with
|
||||
* the same original order:
|
||||
* transpose(ViewMatrixInverse) * transpose(ModelMatrixInverse)
|
||||
*
|
||||
* Knowing that the view matrix is orthogonal, the transpose is also the inverse.
|
||||
* NOTE: This is only valid because we are only using the mat3 of the ViewMatrixInverse.
|
||||
* ViewMatrix * transpose(ModelMatrixInverse)
|
||||
*/
|
||||
float3x3 normal() const
|
||||
{
|
||||
return transpose(to_float3x3(model_inverse));
|
||||
}
|
||||
float3x3 normal_inverse() const
|
||||
{
|
||||
return transpose(to_float3x3(model));
|
||||
}
|
||||
#endif
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Transform Normal
|
||||
*
|
||||
* Space conversion helpers for normal vectors.
|
||||
* \{ */
|
||||
|
||||
#ifdef GPU_SHADER /* `to_float3x3` doesn't exist on CPU side. */
|
||||
float3 normal_object_to_world(float3 lN) const
|
||||
{
|
||||
return normal() * lN;
|
||||
}
|
||||
|
||||
float3 normal_world_to_object(float3 N) const
|
||||
{
|
||||
return normal_inverse() * N;
|
||||
}
|
||||
|
||||
float3 normal_object_to_view(ViewMatrices view, float3 lN) const
|
||||
{
|
||||
return to_float3x3(view.viewmat) * (normal() * lN);
|
||||
}
|
||||
|
||||
float3 normal_view_to_object(ViewMatrices view, float3 vN) const
|
||||
{
|
||||
return normal_inverse() * (to_float3x3(view.viewinv) * vN);
|
||||
}
|
||||
#endif
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Transform Point
|
||||
*
|
||||
* Space conversion helpers for points (coordinates).
|
||||
* \{ */
|
||||
|
||||
float3 point_object_to_world(float3 lP) const
|
||||
{
|
||||
return (model * float4(lP, 1.0f)).xyz();
|
||||
}
|
||||
|
||||
float3 point_world_to_object(float3 P) const
|
||||
{
|
||||
return (model_inverse * float4(P, 1.0f)).xyz();
|
||||
}
|
||||
|
||||
float3 point_object_to_view(ViewMatrices view, float3 lP) const
|
||||
{
|
||||
return (view.viewmat * (model * float4(lP, 1.0f))).xyz();
|
||||
}
|
||||
|
||||
float3 point_view_to_object(ViewMatrices view, float3 vP) const
|
||||
{
|
||||
return (model_inverse * (view.viewinv * float4(vP, 1.0f))).xyz();
|
||||
}
|
||||
|
||||
float4 point_object_to_homogenous(ViewMatrices view, float3 lP) const
|
||||
{
|
||||
return view.winmat * (view.viewmat * (model * float4(lP, 1.0f)));
|
||||
}
|
||||
|
||||
float3 point_object_to_ndc(ViewMatrices view, float3 lP) const
|
||||
{
|
||||
return view.perspective_divide(point_object_to_homogenous(view, lP));
|
||||
}
|
||||
|
||||
/** \} */
|
||||
};
|
||||
|
||||
enum [[host_shared]] eObjectInfoFlag : uint32_t {
|
||||
OBJECT_SELECTED = (1u << 0u),
|
||||
OBJECT_FROM_DUPLI = (1u << 1u),
|
||||
OBJECT_FROM_SET = (1u << 2u),
|
||||
OBJECT_ACTIVE = (1u << 3u),
|
||||
OBJECT_NEGATIVE_SCALE = (1u << 4u),
|
||||
OBJECT_HOLDOUT = (1u << 5u),
|
||||
/* Implies all objects that match the current active object's mode and able to be edited
|
||||
* simultaneously. Currently only applicable for edit mode. */
|
||||
OBJECT_ACTIVE_EDIT_MODE = (1u << 6u),
|
||||
/* Avoid skipped info to change culling. */
|
||||
OBJECT_NO_INFO = ~OBJECT_HOLDOUT
|
||||
};
|
||||
|
||||
#ifndef GPU_SHADER
|
||||
ENUM_OPERATORS(eObjectInfoFlag);
|
||||
#endif
|
||||
|
||||
struct [[host_shared]] ObjectInfos {
|
||||
/** Uploaded as center + size. Converted to mul+bias to local coord. */
|
||||
packed_float3 orco_add;
|
||||
uint object_attrs_offset;
|
||||
packed_float3 orco_mul;
|
||||
uint object_attrs_len;
|
||||
|
||||
float4 ob_color;
|
||||
uint index;
|
||||
/** Used for Light Linking in EEVEE */
|
||||
uint light_and_shadow_set_membership;
|
||||
float random;
|
||||
enum eObjectInfoFlag flag;
|
||||
float shadow_terminator_normal_offset;
|
||||
float shadow_terminator_geometry_offset;
|
||||
float _pad1;
|
||||
float _pad2;
|
||||
|
||||
#ifndef GPU_SHADER
|
||||
void sync();
|
||||
void sync(const draw::ObjectRef ref, bool is_active_object, bool is_active_edit_mode);
|
||||
#endif
|
||||
};
|
||||
|
||||
inline uint receiver_light_set_get(ObjectInfos object_infos)
|
||||
{
|
||||
return object_infos.light_and_shadow_set_membership & 0xFFu;
|
||||
}
|
||||
|
||||
inline uint blocker_shadow_set_get(ObjectInfos object_infos)
|
||||
{
|
||||
return (object_infos.light_and_shadow_set_membership >> 8u) & 0xFFu;
|
||||
}
|
||||
|
||||
struct [[host_shared]] ObjectBounds {
|
||||
/**
|
||||
* Uploaded as vertex (0, 4, 3, 1) of the bbox in local space, matching XYZ axis order.
|
||||
* Then processed by GPU and stored as (0, 4-0, 3-0, 1-0) in world space for faster culling.
|
||||
*/
|
||||
float4 bounding_corners[4];
|
||||
/** Bounding sphere derived from the bounding corner. Computed on GPU. */
|
||||
float4 bounding_sphere;
|
||||
/** Radius of the inscribed sphere derived from the bounding corner. Computed on GPU. */
|
||||
#define _inner_sphere_radius bounding_corners[3].w
|
||||
|
||||
#ifndef GPU_SHADER
|
||||
void sync();
|
||||
void sync(const Object &ob, float inflate_bounds = 0.0f);
|
||||
void sync(const float3 ¢er, const float3 &size);
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Return true if `bounding_corners` are valid. Should be checked before accessing them.
|
||||
* Does not guarantee that `bounding_sphere` is valid.
|
||||
* Converting these bounds to an `IsectBox` may generate invalid clip planes.
|
||||
* For safe `IsectBox` generation check `drw_bounds_are_valid`. */
|
||||
inline bool drw_bounds_corners_are_valid(ObjectBounds bounds)
|
||||
{
|
||||
return bounds.bounding_sphere.w != -1.0f;
|
||||
}
|
||||
|
||||
/* Return true if bounds are ready for culling.
|
||||
* In this case, both `bounding_corners` and `bounding_sphere` are valid.
|
||||
* These bounds can be safely converted to an `IsectBox` with valid clip planes. */
|
||||
inline bool drw_bounds_are_valid(ObjectBounds bounds)
|
||||
{
|
||||
return bounds.bounding_sphere.w >= 0.0f;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Object attributes
|
||||
* \{ */
|
||||
|
||||
struct [[host_shared]] VolumeInfos {
|
||||
/** Object to grid-space. */
|
||||
float4x4 grids_xform[DRW_GRID_PER_VOLUME_MAX];
|
||||
/** \note float4 for alignment. Only float3 needed. */
|
||||
float4 color_mul;
|
||||
float density_scale;
|
||||
float temperature_mul;
|
||||
float temperature_bias;
|
||||
float _pad;
|
||||
};
|
||||
|
||||
struct [[host_shared]] CurvesInfos {
|
||||
/* TODO(fclem): Make it a single uint. */
|
||||
/** Per attribute scope, follows loading order.
|
||||
* \note uint as bool in GLSL is 4 bytes.
|
||||
* \note GLSL pad arrays of scalar to 16 bytes (std140). */
|
||||
uint4 is_point_attribute[DRW_ATTRIBUTE_PER_CURVES_MAX];
|
||||
|
||||
/* Number of vertex in a segment (including restart vertex for cylinder). */
|
||||
uint vertex_per_segment;
|
||||
/* Edge count for the visible half cylinder. Equal to face count + 1. */
|
||||
uint half_cylinder_face_count;
|
||||
uint _pad0;
|
||||
uint _pad1;
|
||||
};
|
||||
|
||||
#pragma pack(push, 4)
|
||||
struct [[host_shared]] ObjectAttribute {
|
||||
/* Workaround the padding cost from alignment requirements.
|
||||
* (see GL spec : 7.6.2.2 Standard Uniform Block Layout) */
|
||||
float data_x;
|
||||
float data_y;
|
||||
float data_z;
|
||||
float data_w;
|
||||
uint hash_code;
|
||||
|
||||
#ifndef GPU_SHADER
|
||||
/**
|
||||
* Go through all possible source of the given object uniform attribute.
|
||||
* Returns true if the attribute was correctly filled.
|
||||
*
|
||||
* \param instance_index: The index of synced the instance within the ObjectRef::duplis_,
|
||||
* or just 0 if the ObjectRef doesn't contain any instances.
|
||||
*/
|
||||
bool sync(const draw::ObjectRef &ref, const GPUUniformAttr &attr, int instance_index);
|
||||
#endif
|
||||
};
|
||||
#pragma pack(pop)
|
||||
/** \note we only align to 4 bytes and fetch data manually so make sure
|
||||
* C++ compiler gives us the same size. */
|
||||
BLI_STATIC_ASSERT_ALIGN(ObjectAttribute, 20)
|
||||
|
||||
struct [[host_shared]] LayerAttribute {
|
||||
float4 data;
|
||||
uint hash_code;
|
||||
uint buffer_length; /* Only in the first record. */
|
||||
uint _pad1;
|
||||
uint _pad2;
|
||||
|
||||
#ifndef GPU_SHADER
|
||||
bool sync(const Scene *scene, const ViewLayer *layer, const GPULayerAttr &attr);
|
||||
#endif
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Indirect commands structures.
|
||||
* \{ */
|
||||
|
||||
/* Regular draw commands (no index buffer). */
|
||||
struct [[host_shared]] DrawCommandArray {
|
||||
uint vertex_len;
|
||||
uint instance_len;
|
||||
uint vertex_first;
|
||||
uint instance_first;
|
||||
|
||||
uint _pad0;
|
||||
uint _pad1;
|
||||
uint _pad2;
|
||||
uint _pad3;
|
||||
};
|
||||
|
||||
/* Indexed draw commands (with index buffer). */
|
||||
struct [[host_shared]] DrawCommandIndexed {
|
||||
uint vertex_len;
|
||||
uint instance_len;
|
||||
uint vertex_first;
|
||||
uint base_index;
|
||||
|
||||
uint instance_first;
|
||||
uint _pad0;
|
||||
uint _pad1;
|
||||
uint _pad2;
|
||||
};
|
||||
|
||||
struct [[host_shared]] DrawCommand {
|
||||
union {
|
||||
union_t<DrawCommandArray> array;
|
||||
union_t<DrawCommandIndexed> indexed;
|
||||
};
|
||||
};
|
||||
|
||||
struct [[host_shared]] DispatchCommand {
|
||||
uint num_groups_x;
|
||||
uint num_groups_y;
|
||||
uint num_groups_z;
|
||||
uint _pad0;
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Debug draw shapes
|
||||
* \{ */
|
||||
|
||||
struct [[host_shared]] DRWDebugVertPair {
|
||||
/* This is a weird layout, but needed to be able to use DRWDebugVertPair as
|
||||
* a DrawCommand and avoid alignment issues. See drw_debug_lines_buf[] definition. */
|
||||
uint pos1_x;
|
||||
uint pos1_y;
|
||||
uint pos1_z;
|
||||
/* Named vert_color to avoid global namespace collision with uniform color. */
|
||||
uint vert_color;
|
||||
|
||||
uint pos2_x;
|
||||
uint pos2_y;
|
||||
uint pos2_z;
|
||||
/* Number of time this line is supposed to be displayed. Decremented by one on display. */
|
||||
uint lifetime;
|
||||
};
|
||||
|
||||
inline DRWDebugVertPair debug_line_make(uint in_pos1_x,
|
||||
uint in_pos1_y,
|
||||
uint in_pos1_z,
|
||||
uint in_pos2_x,
|
||||
uint in_pos2_y,
|
||||
uint in_pos2_z,
|
||||
uint in_vert_color,
|
||||
uint in_lifetime)
|
||||
{
|
||||
DRWDebugVertPair debug_vert;
|
||||
debug_vert.pos1_x = in_pos1_x;
|
||||
debug_vert.pos1_y = in_pos1_y;
|
||||
debug_vert.pos1_z = in_pos1_z;
|
||||
debug_vert.pos2_x = in_pos2_x;
|
||||
debug_vert.pos2_y = in_pos2_y;
|
||||
debug_vert.pos2_z = in_pos2_z;
|
||||
debug_vert.vert_color = in_vert_color;
|
||||
debug_vert.lifetime = in_lifetime;
|
||||
return debug_vert;
|
||||
}
|
||||
|
||||
inline uint debug_color_pack(float4 v_color)
|
||||
{
|
||||
v_color = clamp(v_color, 0.0f, 1.0f);
|
||||
uint result = 0;
|
||||
result |= uint(v_color.x * 255.0) << 0u;
|
||||
result |= uint(v_color.y * 255.0) << 8u;
|
||||
result |= uint(v_color.z * 255.0) << 16u;
|
||||
result |= uint(v_color.w * 255.0) << 24u;
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Take the header (DrawCommand) into account. */
|
||||
#define DRW_DEBUG_DRAW_VERT_MAX (2 * 1024) - 1
|
||||
|
||||
/* The debug draw buffer is laid-out as the following struct.
|
||||
* But we use plain array in shader code instead because of driver issues. */
|
||||
struct [[host_shared]] DRWDebugDrawBuffer {
|
||||
struct DrawCommand command;
|
||||
struct DRWDebugVertPair verts[DRW_DEBUG_DRAW_VERT_MAX];
|
||||
};
|
||||
|
||||
/* Equivalent to `DRWDebugDrawBuffer.command.v_count`. */
|
||||
#define drw_debug_draw_v_count(buf) buf[0].pos1_x
|
||||
/**
|
||||
* Offset to the first data. Equal to: `sizeof(DrawCommand) / sizeof(DRWDebugVertPair)`.
|
||||
* This is needed because we bind the whole buffer as a `DRWDebugVertPair` array.
|
||||
*/
|
||||
#define drw_debug_draw_offset 1
|
||||
|
||||
/** \} */
|
||||
|
||||
#if !defined(GPU_SHADER)
|
||||
} // namespace blender
|
||||
#endif
|
||||
225
blender-5.2.0/source/blender/draw/intern/draw_state.hh
Normal file
225
blender-5.2.0/source/blender/draw/intern/draw_state.hh
Normal file
@@ -0,0 +1,225 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GPU_state.hh"
|
||||
|
||||
#include "BLI_enum_flags.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*
|
||||
* Internal Pipeline State tracking. It is higher level than GPU state as everything fits a single
|
||||
* enum.
|
||||
*/
|
||||
|
||||
/**
|
||||
* DRWState is a bit-mask that stores the current render state and the desired render state. Based
|
||||
* on the differences the minimum state changes can be invoked to setup the desired render state.
|
||||
*
|
||||
* The Write Stencil, Stencil test, Depth test and Blend state options are mutual exclusive
|
||||
* therefore they aren't ordered as a bit mask.
|
||||
*/
|
||||
enum DRWState : uint32_t {
|
||||
/** To be used for compute passes. */
|
||||
DRW_STATE_NO_DRAW = 0,
|
||||
/** Write mask */
|
||||
DRW_STATE_WRITE_DEPTH = (1 << 0),
|
||||
DRW_STATE_WRITE_COLOR = (1 << 1),
|
||||
/* Write Stencil. These options are mutual exclusive and packed into 2 bits */
|
||||
DRW_STATE_WRITE_STENCIL = (1 << 2),
|
||||
DRW_STATE_WRITE_STENCIL_SHADOW_PASS = (2 << 2),
|
||||
DRW_STATE_WRITE_STENCIL_SHADOW_FAIL = (3 << 2),
|
||||
/** Depth test. These options are mutual exclusive and packed into 3 bits */
|
||||
DRW_STATE_DEPTH_ALWAYS = (1 << 4),
|
||||
DRW_STATE_DEPTH_LESS = (2 << 4),
|
||||
DRW_STATE_DEPTH_LESS_EQUAL = (3 << 4),
|
||||
DRW_STATE_DEPTH_EQUAL = (4 << 4),
|
||||
DRW_STATE_DEPTH_GREATER = (5 << 4),
|
||||
DRW_STATE_DEPTH_GREATER_EQUAL = (6 << 4),
|
||||
/** Culling test */
|
||||
DRW_STATE_CULL_BACK = (1 << 7),
|
||||
DRW_STATE_CULL_FRONT = (1 << 8),
|
||||
/** Stencil test. These options are mutually exclusive and packed into 2 bits. */
|
||||
DRW_STATE_STENCIL_ALWAYS = (1 << 9),
|
||||
DRW_STATE_STENCIL_EQUAL = (2 << 9),
|
||||
DRW_STATE_STENCIL_NEQUAL = (3 << 9),
|
||||
|
||||
/** Blend state. These options are mutual exclusive and packed into 4 bits */
|
||||
DRW_STATE_BLEND_ADD = (1 << 11),
|
||||
/** Same as additive but let alpha accumulate without pre-multiply. */
|
||||
DRW_STATE_BLEND_ADD_FULL = (2 << 11),
|
||||
/** Standard alpha blending. */
|
||||
DRW_STATE_BLEND_ALPHA = (3 << 11),
|
||||
/** Use that if color is already pre-multiply by alpha. */
|
||||
DRW_STATE_BLEND_ALPHA_PREMUL = (4 << 11),
|
||||
DRW_STATE_BLEND_BACKGROUND = (5 << 11),
|
||||
DRW_STATE_BLEND_OIT = (6 << 11),
|
||||
DRW_STATE_BLEND_MUL = (7 << 11),
|
||||
DRW_STATE_BLEND_SUB = (8 << 11),
|
||||
/** Use dual source blending. WARNING: Only one color buffer allowed. */
|
||||
DRW_STATE_BLEND_CUSTOM = (9 << 11),
|
||||
DRW_STATE_LOGIC_INVERT = (10 << 11),
|
||||
DRW_STATE_BLEND_ALPHA_UNDER_PREMUL = (11 << 11),
|
||||
DRW_STATE_BLEND_TRANSPARENCY = (12 << 11),
|
||||
|
||||
/* See GPU_clip_control_unit_range. */
|
||||
DRW_STATE_CLIP_CONTROL_UNIT_RANGE = (1 << 28),
|
||||
DRW_STATE_CLIP_PLANES = (1 << 29),
|
||||
DRW_STATE_FIRST_VERTEX_CONVENTION = (1 << 30),
|
||||
/** DO NOT USE. Assumed always enabled. Only used internally. */
|
||||
DRW_STATE_PROGRAM_POINT_SIZE = (1u << 31),
|
||||
};
|
||||
ENUM_OPERATORS(DRWState);
|
||||
|
||||
#define DRW_STATE_DEFAULT \
|
||||
(DRW_STATE_WRITE_DEPTH | DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL)
|
||||
#define DRW_STATE_BLEND_ENABLED \
|
||||
(DRW_STATE_BLEND_ADD | DRW_STATE_BLEND_ADD_FULL | DRW_STATE_BLEND_ALPHA | \
|
||||
DRW_STATE_BLEND_ALPHA_PREMUL | DRW_STATE_BLEND_BACKGROUND | DRW_STATE_BLEND_OIT | \
|
||||
DRW_STATE_BLEND_MUL | DRW_STATE_BLEND_SUB | DRW_STATE_BLEND_CUSTOM | DRW_STATE_LOGIC_INVERT | \
|
||||
DRW_STATE_BLEND_ALPHA_UNDER_PREMUL | DRW_STATE_BLEND_TRANSPARENCY)
|
||||
#define DRW_STATE_RASTERIZER_ENABLED \
|
||||
(DRW_STATE_WRITE_DEPTH | DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_STENCIL | \
|
||||
DRW_STATE_WRITE_STENCIL_SHADOW_PASS | DRW_STATE_WRITE_STENCIL_SHADOW_FAIL)
|
||||
#define DRW_STATE_DEPTH_TEST_ENABLED \
|
||||
(DRW_STATE_DEPTH_ALWAYS | DRW_STATE_DEPTH_LESS | DRW_STATE_DEPTH_LESS_EQUAL | \
|
||||
DRW_STATE_DEPTH_EQUAL | DRW_STATE_DEPTH_GREATER | DRW_STATE_DEPTH_GREATER_EQUAL)
|
||||
#define DRW_STATE_STENCIL_TEST_ENABLED \
|
||||
(DRW_STATE_STENCIL_ALWAYS | DRW_STATE_STENCIL_EQUAL | DRW_STATE_STENCIL_NEQUAL)
|
||||
#define DRW_STATE_WRITE_STENCIL_ENABLED \
|
||||
(DRW_STATE_WRITE_STENCIL | DRW_STATE_WRITE_STENCIL_SHADOW_PASS | \
|
||||
DRW_STATE_WRITE_STENCIL_SHADOW_FAIL)
|
||||
|
||||
namespace draw {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name DRWState to GPU state conversion
|
||||
* \{ */
|
||||
|
||||
static inline GPUWriteMask to_write_mask(DRWState state)
|
||||
{
|
||||
GPUWriteMask write_mask = GPU_WRITE_NONE;
|
||||
if (state & DRW_STATE_WRITE_DEPTH) {
|
||||
write_mask |= GPU_WRITE_DEPTH;
|
||||
}
|
||||
if (state & DRW_STATE_WRITE_COLOR) {
|
||||
write_mask |= GPU_WRITE_COLOR;
|
||||
}
|
||||
if (state & DRW_STATE_WRITE_STENCIL_ENABLED) {
|
||||
write_mask |= GPU_WRITE_STENCIL;
|
||||
}
|
||||
return write_mask;
|
||||
}
|
||||
|
||||
static inline GPUFaceCullTest to_face_cull_test(DRWState state)
|
||||
{
|
||||
switch (state & (DRW_STATE_CULL_BACK | DRW_STATE_CULL_FRONT)) {
|
||||
case DRW_STATE_CULL_BACK:
|
||||
return GPU_CULL_BACK;
|
||||
case DRW_STATE_CULL_FRONT:
|
||||
return GPU_CULL_FRONT;
|
||||
default:
|
||||
return GPU_CULL_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
static inline GPUDepthTest to_depth_test(DRWState state)
|
||||
{
|
||||
switch (state & DRW_STATE_DEPTH_TEST_ENABLED) {
|
||||
case DRW_STATE_DEPTH_LESS:
|
||||
return GPU_DEPTH_LESS;
|
||||
case DRW_STATE_DEPTH_LESS_EQUAL:
|
||||
return GPU_DEPTH_LESS_EQUAL;
|
||||
case DRW_STATE_DEPTH_EQUAL:
|
||||
return GPU_DEPTH_EQUAL;
|
||||
case DRW_STATE_DEPTH_GREATER:
|
||||
return GPU_DEPTH_GREATER;
|
||||
case DRW_STATE_DEPTH_GREATER_EQUAL:
|
||||
return GPU_DEPTH_GREATER_EQUAL;
|
||||
case DRW_STATE_DEPTH_ALWAYS:
|
||||
return GPU_DEPTH_ALWAYS;
|
||||
default:
|
||||
return GPU_DEPTH_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
static inline GPUStencilOp to_stencil_op(DRWState state)
|
||||
{
|
||||
switch (state & DRW_STATE_WRITE_STENCIL_ENABLED) {
|
||||
case DRW_STATE_WRITE_STENCIL:
|
||||
return GPU_STENCIL_OP_REPLACE;
|
||||
case DRW_STATE_WRITE_STENCIL_SHADOW_PASS:
|
||||
return GPU_STENCIL_OP_COUNT_DEPTH_PASS;
|
||||
case DRW_STATE_WRITE_STENCIL_SHADOW_FAIL:
|
||||
return GPU_STENCIL_OP_COUNT_DEPTH_FAIL;
|
||||
default:
|
||||
return GPU_STENCIL_OP_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
static inline GPUStencilTest to_stencil_test(DRWState state)
|
||||
{
|
||||
switch (state & DRW_STATE_STENCIL_TEST_ENABLED) {
|
||||
case DRW_STATE_STENCIL_ALWAYS:
|
||||
return GPU_STENCIL_ALWAYS;
|
||||
case DRW_STATE_STENCIL_EQUAL:
|
||||
return GPU_STENCIL_EQUAL;
|
||||
case DRW_STATE_STENCIL_NEQUAL:
|
||||
return GPU_STENCIL_NEQUAL;
|
||||
default:
|
||||
return GPU_STENCIL_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
static inline GPUBlend to_blend(DRWState state)
|
||||
{
|
||||
switch (state & DRW_STATE_BLEND_ENABLED) {
|
||||
case DRW_STATE_BLEND_ADD:
|
||||
return GPU_BLEND_ADDITIVE;
|
||||
case DRW_STATE_BLEND_ADD_FULL:
|
||||
return GPU_BLEND_ADDITIVE_PREMULT;
|
||||
case DRW_STATE_BLEND_ALPHA:
|
||||
return GPU_BLEND_ALPHA;
|
||||
case DRW_STATE_BLEND_ALPHA_PREMUL:
|
||||
return GPU_BLEND_ALPHA_PREMULT;
|
||||
case DRW_STATE_BLEND_BACKGROUND:
|
||||
return GPU_BLEND_BACKGROUND;
|
||||
case DRW_STATE_BLEND_OIT:
|
||||
return GPU_BLEND_OIT;
|
||||
case DRW_STATE_BLEND_MUL:
|
||||
return GPU_BLEND_MULTIPLY;
|
||||
case DRW_STATE_BLEND_SUB:
|
||||
return GPU_BLEND_SUBTRACT;
|
||||
case DRW_STATE_BLEND_CUSTOM:
|
||||
return GPU_BLEND_CUSTOM;
|
||||
case DRW_STATE_LOGIC_INVERT:
|
||||
return GPU_BLEND_INVERT;
|
||||
case DRW_STATE_BLEND_ALPHA_UNDER_PREMUL:
|
||||
return GPU_BLEND_ALPHA_UNDER_PREMUL;
|
||||
case DRW_STATE_BLEND_TRANSPARENCY:
|
||||
return GPU_BLEND_TRANSPARENCY;
|
||||
default:
|
||||
return GPU_BLEND_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
static inline GPUProvokingVertex to_provoking_vertex(DRWState state)
|
||||
{
|
||||
switch (state & DRW_STATE_FIRST_VERTEX_CONVENTION) {
|
||||
case DRW_STATE_FIRST_VERTEX_CONVENTION:
|
||||
return GPU_VERTEX_FIRST;
|
||||
default:
|
||||
return GPU_VERTEX_LAST;
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
}; // namespace draw
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,81 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define SUBDIV_GROUP_SIZE 64
|
||||
|
||||
/* Uniform buffer bindings */
|
||||
#define SHADER_DATA_BUF_SLOT 0
|
||||
|
||||
/* Storage buffer bindings */
|
||||
#define SUBDIV_FACE_OFFSET_BUF_SLOT 0
|
||||
|
||||
#define LINES_INPUT_EDGE_DRAW_FLAG_BUF_SLOT 1
|
||||
#define LINES_EXTRA_COARSE_FACE_DATA_BUF_SLOT 2
|
||||
#define LINES_OUTPUT_LINES_BUF_SLOT 3
|
||||
#define LINES_LINES_LOOSE_FLAGS 4
|
||||
|
||||
#define TRIS_EXTRA_COARSE_FACE_DATA_BUF_SLOT 1
|
||||
#define TRIS_OUTPUT_TRIS_BUF_SLOT 2
|
||||
#define TRIS_FACE_MAT_OFFSET 3
|
||||
|
||||
#define EDGE_FAC_POS_BUF_SLOT 0
|
||||
#define EDGE_FAC_EDGE_DRAW_FLAG_BUF_SLOT 1
|
||||
#define EDGE_FAC_POLY_OTHER_MAP_BUF_SLOT 2
|
||||
#define EDGE_FAC_EDGE_FAC_BUF_SLOT 3
|
||||
|
||||
#define PATCH_EVALUATION_SOURCE_VERTEX_BUFFER_BUF_SLOT 0
|
||||
#define PATCH_EVALUATION_INPUT_PATCH_HANDLES_BUF_SLOT 1
|
||||
#define PATCH_EVALUATION_QUAD_NODES_BUF_SLOT 2
|
||||
#define PATCH_EVALUATION_PATCH_COORDS_BUF_SLOT 3
|
||||
#define PATCH_EVALUATION_INPUT_VERTEX_ORIG_INDEX_BUF_SLOT 4
|
||||
#define PATCH_EVALUATION_PATCH_ARRAY_BUFFER_BUF_SLOT 5
|
||||
#define PATCH_EVALUATION_PATCH_INDEX_BUFFER_BUF_SLOT 6
|
||||
#define PATCH_EVALUATION_PATCH_PARAM_BUFFER_BUF_SLOT 7
|
||||
#define PATCH_EVALUATION_OUTPUT_FVAR_BUF_SLOT 8
|
||||
#define PATCH_EVALUATION_OUTPUT_FDOTS_VERTEX_BUFFER_BUF_SLOT 8
|
||||
#define PATCH_EVALUATION_OUTPUT_NORMALS_BUF_SLOT 9
|
||||
#define PATCH_EVALUATION_OUTPUT_INDICES_BUF_SLOT 10
|
||||
#define PATCH_EVALUATION_EXTRA_COARSE_FACE_DATA_BUF_SLOT 11
|
||||
#define PATCH_EVALUATION_OUTPUT_POS_BUF_SLOT 9
|
||||
#define PATCH_EVALUATION_SOURCE_EXTRA_VERTEX_BUFFER_BUF_SLOT 10
|
||||
#define PATCH_EVALUATION_OUTPUT_ORCOS_BUF_SLOT 11
|
||||
|
||||
#define CUSTOM_DATA_SOURCE_DATA_BUF_SLOT 1
|
||||
#define CUSTOM_DATA_FACE_PTEX_OFFSET_BUF_SLOT 2
|
||||
#define CUSTOM_DATA_PATCH_COORDS_BUF_SLOT 3
|
||||
#define CUSTOM_DATA_EXTRA_COARSE_FACE_DATA_BUF_SLOT 4
|
||||
#define CUSTOM_DATA_DESTINATION_DATA_BUF_SLOT 5
|
||||
|
||||
#define SCULPT_DATA_SCULPT_MASK_BUF_SLOT 0
|
||||
#define SCULPT_DATA_SCULPT_FACE_SET_COLOR_BUF_SLOT 1
|
||||
#define SCULPT_DATA_SCULPT_DATA_BUF_SLOT 2
|
||||
|
||||
#define STRETCH_ANGLE_POS_BUF_SLOT 0
|
||||
#define STRETCH_ANGLE_UVS_BUF_SLOT 1
|
||||
#define STRETCH_ANGLE_UV_STRETCHES_BUF_SLOT 2
|
||||
|
||||
#define STRETCH_AREA_COARSE_STRETCH_AREA_BUF_SLOT 1
|
||||
#define STRETCH_AREA_SUBDIV_STRETCH_AREA_BUF_SLOT 2
|
||||
|
||||
#define NORMALS_ACCUMULATE_POS_BUF_SLOT 0
|
||||
#define NORMALS_ACCUMULATE_FACE_ADJACENCY_OFFSETS_BUF_SLOT 1
|
||||
#define NORMALS_ACCUMULATE_FACE_ADJACENCY_LISTS_BUF_SLOT 2
|
||||
#define NORMALS_ACCUMULATE_VERTEX_LOOP_MAP_BUF_SLOT 3
|
||||
#define NORMALS_ACCUMULATE_NORMALS_BUF_SLOT 4
|
||||
|
||||
#define PAINT_OVERLAY_EXTRA_COARSE_FACE_DATA_BUF_SLOT 1
|
||||
#define PAINT_OVERLAY_EXTRA_INPUT_VERT_ORIG_INDEX_SLOT 2
|
||||
#define PAINT_OVERLAY_OUTPUT_FLAG_SLOT 3
|
||||
|
||||
#define LOOP_NORMALS_POS_SLOT 1
|
||||
#define LOOP_NORMALS_EXTRA_COARSE_FACE_DATA_BUF_SLOT 2
|
||||
#define LOOP_NORMALS_VERT_NORMALS_BUF_SLOT 3
|
||||
#define LOOP_NORMALS_VERTEX_LOOP_MAP_BUF_SLOT 4
|
||||
#define LOOP_NORMALS_OUTPUT_LNOR_BUF_SLOT 5
|
||||
@@ -0,0 +1,121 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "draw_subdiv_defines.hh"
|
||||
|
||||
#include "GPU_shader_shared_utils.hh"
|
||||
|
||||
struct [[host_shared]] DRWSubdivUboStorage {
|
||||
/* Offsets in the buffers data where the source and destination data start. */
|
||||
int src_offset;
|
||||
int dst_offset;
|
||||
|
||||
/* Parameters for the DRWPatchMap. */
|
||||
int min_patch_face;
|
||||
int max_patch_face;
|
||||
int max_depth;
|
||||
bool32_t patches_are_triangular;
|
||||
|
||||
/* Coarse topology information. */
|
||||
int coarse_face_count;
|
||||
uint edge_loose_offset;
|
||||
|
||||
/* Refined topology information. */
|
||||
uint num_subdiv_loops;
|
||||
|
||||
/* The sculpt mask data layer may be null. */
|
||||
bool32_t has_sculpt_mask;
|
||||
|
||||
/* Masks for the extra coarse face data. */
|
||||
uint coarse_face_select_mask;
|
||||
uint coarse_face_smooth_mask;
|
||||
uint coarse_face_active_mask;
|
||||
uint coarse_face_hidden_mask;
|
||||
uint coarse_face_loopstart_mask;
|
||||
|
||||
/* Number of elements to process in the compute shader (can be the coarse quad count, or the
|
||||
* final vertex count, depending on which compute pass we do). This is used to early out in case
|
||||
* of out of bond accesses as compute dispatch are of fixed size. */
|
||||
uint total_dispatch_size;
|
||||
|
||||
bool32_t is_edit_mode;
|
||||
bool32_t use_hide;
|
||||
int _pad3;
|
||||
int _pad4;
|
||||
};
|
||||
|
||||
struct [[host_shared]] SculptData {
|
||||
uint face_set_color;
|
||||
float mask;
|
||||
};
|
||||
|
||||
/* Mirror of #UVStretchAngle in the C++ code, but using floats until proper data compression
|
||||
* is implemented for all subdivision data. */
|
||||
struct [[host_shared]] UVStretchAngle {
|
||||
float angle;
|
||||
float uv_angle0;
|
||||
float uv_angle1;
|
||||
};
|
||||
|
||||
struct [[host_shared]] Position {
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
};
|
||||
|
||||
struct [[host_shared]] Normal {
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
};
|
||||
|
||||
/* Structure for #CompressedPatchCoord. */
|
||||
struct [[host_shared]] BlenderPatchCoord {
|
||||
int patch_index;
|
||||
uint encoded_uv;
|
||||
};
|
||||
|
||||
/* Patch evaluation - F-dots. */
|
||||
/* float3 is padded to float4, but the format used for face-dots does not have any padding. */
|
||||
struct [[host_shared]] FDotVert {
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
};
|
||||
|
||||
/* Same here, do not use float3. */
|
||||
struct [[host_shared]] FDotNor {
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
float flag;
|
||||
};
|
||||
|
||||
/* This structure is a carbon copy of OpenSubDiv's #PatchTable::PatchHandle. */
|
||||
struct [[host_shared]] PatchHandle {
|
||||
int array_index;
|
||||
int patch_index;
|
||||
int vertex_index;
|
||||
};
|
||||
|
||||
/* This structure is a carbon copy of OpenSubDiv's #PatchCoord. */
|
||||
struct [[host_shared]] PatchCoord {
|
||||
int array_index;
|
||||
int patch_index;
|
||||
int vertex_index;
|
||||
float u;
|
||||
float v;
|
||||
};
|
||||
|
||||
/* This structure is a carbon copy of OpenSubDiv's #PatchCoord.QuadNode.
|
||||
* Each child is a bit-field. */
|
||||
struct [[host_shared]] QuadNode {
|
||||
uint4 child;
|
||||
};
|
||||
280
blender-5.2.0/source/blender/draw/intern/draw_subdivision.hh
Normal file
280
blender-5.2.0/source/blender/draw/intern/draw_subdivision.hh
Normal file
@@ -0,0 +1,280 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_math_matrix_types.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_span.hh"
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
#include "mesh_extractors/extract_mesh.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct BMesh;
|
||||
namespace gpu {
|
||||
class IndexBuf;
|
||||
class UniformBuf;
|
||||
class VertBuf;
|
||||
} // namespace gpu
|
||||
struct GPUVertFormat;
|
||||
struct Mesh;
|
||||
struct Object;
|
||||
namespace bke::subdiv {
|
||||
struct Subdiv;
|
||||
}
|
||||
struct ToolSettings;
|
||||
|
||||
namespace draw {
|
||||
|
||||
struct MeshBatchCache;
|
||||
struct MeshBufferCache;
|
||||
struct MeshRenderData;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name DRWPatchMap
|
||||
*
|
||||
* This is a GPU version of the OpenSubDiv PatchMap. The quad tree and the patch handles are copied
|
||||
* to GPU buffers in order to lookup the right patch for a given set of patch coordinates.
|
||||
* \{ */
|
||||
|
||||
struct DRWPatchMap {
|
||||
gpu::VertBuf *patch_map_handles;
|
||||
gpu::VertBuf *patch_map_quadtree;
|
||||
int min_patch_face;
|
||||
int max_patch_face;
|
||||
int max_depth;
|
||||
bool patches_are_triangular;
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name DRWSubdivCache
|
||||
*
|
||||
* This holds the various buffers used to evaluate and render subdivision through OpenGL.
|
||||
* \{ */
|
||||
|
||||
struct DRWSubdivCache {
|
||||
const Mesh *mesh;
|
||||
BMesh *bm;
|
||||
bke::subdiv::Subdiv *subdiv;
|
||||
bool optimal_display;
|
||||
bool hide_unmapped_edges;
|
||||
bool use_custom_loop_normals;
|
||||
|
||||
/* Coordinates used to evaluate patches for positions and normals. */
|
||||
gpu::VertBuf *patch_coords;
|
||||
/* Coordinates used to evaluate patches for attributes. */
|
||||
gpu::VertBuf *corner_patch_coords;
|
||||
/* Coordinates used to evaluate patches for the face centers (or face dots) in edit-mode. */
|
||||
gpu::VertBuf *fdots_patch_coords;
|
||||
|
||||
/* Resolution used to generate the patch coordinates. */
|
||||
int resolution;
|
||||
|
||||
/* Number of subdivided loops, also the number of patch coordinates since we have one coordinate
|
||||
* but quad corner/vertex. */
|
||||
uint num_subdiv_loops;
|
||||
uint num_subdiv_edges;
|
||||
uint num_subdiv_triangles;
|
||||
uint num_subdiv_verts;
|
||||
uint num_subdiv_quads;
|
||||
|
||||
/* We only do the subdivision traversal for full faces, however we may have geometries that only
|
||||
* have loose edges (e.g. a custom bone shape). This flag is used to detect those cases, as the
|
||||
* counters above will all be set to zero if we do not have subdivision loops. */
|
||||
bool may_have_loose_geom;
|
||||
|
||||
/* Number of faces in the coarse mesh, notably used to compute a coarse face index given a
|
||||
* subdivision loop index. */
|
||||
int num_coarse_faces;
|
||||
|
||||
/* Maps subdivision loop to subdivided vertex index. */
|
||||
int *subdiv_loop_subdiv_vert_index;
|
||||
/* Maps subdivision loop to subdivided edge index. */
|
||||
int *subdiv_loop_subdiv_edge_index;
|
||||
/* Maps subdivision loop to original coarse face index. */
|
||||
int *subdiv_loop_face_index;
|
||||
|
||||
/* Indices of faces adjacent to the vertices, ordered by vertex index, with no particular
|
||||
* winding. */
|
||||
gpu::VertBuf *subdiv_vert_face_adjacency;
|
||||
/* The difference between value (i + 1) and (i) gives the number of faces adjacent to vertex (i).
|
||||
*/
|
||||
gpu::VertBuf *subdiv_vert_face_adjacency_offsets;
|
||||
|
||||
/* Maps subdivision loop to original coarse vertex index, only really useful for edit mode. */
|
||||
gpu::VertBuf *verts_orig_index;
|
||||
/* Maps subdivision loop to original coarse edge index, only really useful for edit mode. */
|
||||
gpu::VertBuf *edges_orig_index;
|
||||
/* Indicates if edge should be drawn in optimal display mode. */
|
||||
gpu::VertBuf *edges_draw_flag;
|
||||
|
||||
/* Owned by #Subdiv. Indexed by coarse face index, difference between value (i + 1) and (i)
|
||||
* gives the number of ptex faces for coarse face (i). */
|
||||
Span<int> face_ptex_offset;
|
||||
/* Vertex buffer for face_ptex_offset. */
|
||||
gpu::VertBuf *face_ptex_offset_buffer;
|
||||
|
||||
Array<int> subdiv_face_offset;
|
||||
gpu::VertBuf *subdiv_face_offset_buffer;
|
||||
|
||||
/* Contains the start loop index and the smooth flag for each coarse face. */
|
||||
gpu::VertBuf *extra_coarse_face_data;
|
||||
|
||||
/* Material offsets. */
|
||||
int *mat_start;
|
||||
int *mat_end;
|
||||
gpu::VertBuf *face_mat_offset;
|
||||
|
||||
DRWPatchMap gpu_patch_map;
|
||||
|
||||
/**
|
||||
* Subdivided vertices of loose edges. The size of this array is the number of loose edges
|
||||
* multiplied with the resolution. For storage in the VBO the data is duplicated for each edge.
|
||||
*/
|
||||
Array<float3> loose_edge_positions;
|
||||
|
||||
/* UBO to store settings for the various compute shaders. */
|
||||
gpu::UniformBuf *ubo;
|
||||
|
||||
/* Extra flags, passed to the UBO. */
|
||||
bool is_edit_mode;
|
||||
bool use_hide;
|
||||
};
|
||||
|
||||
/* Only frees the data of the cache, caller is responsible to free the cache itself if necessary.
|
||||
*/
|
||||
void draw_subdiv_cache_free(DRWSubdivCache &cache);
|
||||
|
||||
/** \} */
|
||||
|
||||
void DRW_create_subdivision(Object &ob,
|
||||
Mesh &mesh,
|
||||
MeshBatchCache &batch_cache,
|
||||
MeshBufferCache &mbc,
|
||||
Span<IBOType> ibo_requests,
|
||||
Span<VBOType> vbo_requests,
|
||||
bool is_editmode,
|
||||
bool is_paint_mode,
|
||||
bool do_final,
|
||||
bool do_uvedit,
|
||||
bool do_cage,
|
||||
const ToolSettings *ts,
|
||||
bool use_hide);
|
||||
|
||||
void DRW_subdivide_loose_geom(DRWSubdivCache &subdiv_cache, const MeshBufferCache &cache);
|
||||
|
||||
void DRW_subdiv_cache_free(bke::subdiv::Subdiv *subdiv);
|
||||
|
||||
gpu::VertBufPtr draw_subdiv_init_origindex_buffer(int32_t *vert_origindex,
|
||||
uint num_loops,
|
||||
uint loose_len);
|
||||
|
||||
gpu::VertBuf *draw_subdiv_build_origindex_buffer(int *vert_origindex, uint num_loops);
|
||||
gpu::VertBufPtr draw_subdiv_init_origindex_buffer(Span<int32_t> vert_origindex, uint loose_len);
|
||||
gpu::VertBuf *draw_subdiv_build_origindex_buffer(Span<int> vert_origindex);
|
||||
|
||||
/* Compute shader functions. */
|
||||
|
||||
void draw_subdiv_build_sculpt_data_buffer(const DRWSubdivCache &cache,
|
||||
gpu::VertBuf *mask_vbo,
|
||||
gpu::VertBuf *face_set_vbo,
|
||||
gpu::VertBuf *sculpt_data);
|
||||
|
||||
void draw_subdiv_accumulate_normals(const DRWSubdivCache &cache,
|
||||
gpu::VertBuf *pos,
|
||||
gpu::VertBuf *face_adjacency_offsets,
|
||||
gpu::VertBuf *face_adjacency_lists,
|
||||
gpu::VertBuf *vert_loop_map,
|
||||
gpu::VertBuf *vert_normals);
|
||||
|
||||
void draw_subdiv_extract_pos(const DRWSubdivCache &cache, gpu::VertBuf *pos, gpu::VertBuf *orco);
|
||||
|
||||
void draw_subdiv_interp_custom_data(const DRWSubdivCache &cache,
|
||||
gpu::VertBuf &src_data,
|
||||
gpu::VertBuf &dst_data,
|
||||
GPUVertCompType comp_type,
|
||||
int dimensions,
|
||||
int dst_offset);
|
||||
|
||||
void draw_subdiv_interp_corner_normals(const DRWSubdivCache &cache,
|
||||
gpu::VertBuf &src_data,
|
||||
gpu::VertBuf &dst_data);
|
||||
|
||||
void draw_subdiv_extract_uvs(const DRWSubdivCache &cache,
|
||||
gpu::VertBuf *uvs,
|
||||
int face_varying_channel,
|
||||
int dst_offset);
|
||||
|
||||
void draw_subdiv_build_edge_fac_buffer(const DRWSubdivCache &cache,
|
||||
gpu::VertBuf *pos,
|
||||
gpu::VertBuf *edge_draw_flag,
|
||||
gpu::VertBuf *poly_other_map,
|
||||
gpu::VertBuf *edge_fac);
|
||||
|
||||
void draw_subdiv_build_tris_buffer(const DRWSubdivCache &cache,
|
||||
gpu::IndexBuf *subdiv_tris,
|
||||
int material_count);
|
||||
|
||||
void draw_subdiv_build_lines_buffer(const DRWSubdivCache &cache, gpu::IndexBuf *lines_indices);
|
||||
|
||||
void draw_subdiv_build_lines_loose_buffer(const DRWSubdivCache &cache,
|
||||
gpu::IndexBuf *lines_indices,
|
||||
gpu::VertBuf *lines_flags,
|
||||
uint edge_loose_offset,
|
||||
uint num_loose_edges);
|
||||
|
||||
void draw_subdiv_build_fdots_buffers(const DRWSubdivCache &cache,
|
||||
gpu::VertBuf *fdots_pos,
|
||||
gpu::VertBuf *fdots_nor,
|
||||
gpu::IndexBuf *fdots_indices);
|
||||
|
||||
void draw_subdiv_build_lnor_buffer(const DRWSubdivCache &cache,
|
||||
gpu::VertBuf *pos,
|
||||
gpu::VertBuf *vert_normals,
|
||||
gpu::VertBuf *subdiv_corner_verts,
|
||||
gpu::VertBuf *lnor);
|
||||
|
||||
void draw_subdiv_build_paint_overlay_flag_buffer(const DRWSubdivCache &cache, gpu::VertBuf &flags);
|
||||
|
||||
void draw_subdiv_build_edituv_stretch_area_buffer(const DRWSubdivCache &cache,
|
||||
gpu::VertBuf *coarse_data,
|
||||
gpu::VertBuf *subdiv_data);
|
||||
|
||||
void draw_subdiv_build_edituv_stretch_angle_buffer(const DRWSubdivCache &cache,
|
||||
gpu::VertBuf *pos,
|
||||
gpu::VertBuf *uvs,
|
||||
int uvs_offset,
|
||||
gpu::VertBuf *stretch_angles);
|
||||
|
||||
/** For every coarse edge, there are `resolution - 1` subdivided edges. */
|
||||
inline int subdiv_edges_per_coarse_edge(const DRWSubdivCache &cache)
|
||||
{
|
||||
return cache.resolution - 1;
|
||||
}
|
||||
|
||||
/** For every subdivided edge, there are two coarse vertices stored in vertex buffers. */
|
||||
inline int subdiv_verts_per_coarse_edge(const DRWSubdivCache &cache)
|
||||
{
|
||||
return subdiv_edges_per_coarse_edge(cache) * 2;
|
||||
}
|
||||
|
||||
/** The number of subdivided edges from base mesh loose edges. */
|
||||
inline int subdiv_loose_edges_num(const MeshRenderData &mr, const DRWSubdivCache &cache)
|
||||
{
|
||||
return mr.loose_edges.size() * subdiv_edges_per_coarse_edge(cache);
|
||||
}
|
||||
|
||||
/** Size of vertex buffers including all face corners, loose edges, and loose vertices. */
|
||||
inline int subdiv_full_vbo_size(const MeshRenderData &mr, const DRWSubdivCache &cache)
|
||||
{
|
||||
return cache.num_subdiv_loops + subdiv_loose_edges_num(mr, cache) * 2 + mr.loose_verts.size();
|
||||
}
|
||||
|
||||
} // namespace draw
|
||||
} // namespace blender
|
||||
349
blender-5.2.0/source/blender/draw/intern/draw_view.cc
Normal file
349
blender-5.2.0/source/blender/draw/intern/draw_view.cc
Normal file
@@ -0,0 +1,349 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "BLI_math_geom.h"
|
||||
#include "BLI_math_matrix.h"
|
||||
#include "BLI_math_matrix.hh"
|
||||
|
||||
#include "DRW_render.hh"
|
||||
#include "GPU_compute.hh"
|
||||
#include "GPU_debug.hh"
|
||||
|
||||
#include "draw_context_private.hh"
|
||||
#include "draw_shader.hh"
|
||||
#include "draw_view.hh"
|
||||
|
||||
#include "draw_debug.hh"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
std::atomic<uint32_t> View::global_sync_counter_ = 1;
|
||||
|
||||
void View::sync(const float4x4 &view_mat, const float4x4 &win_mat, int view_id)
|
||||
{
|
||||
data_[view_id].viewmat = view_mat;
|
||||
data_[view_id].viewinv = math::invert(view_mat);
|
||||
data_[view_id].winmat = win_mat;
|
||||
data_[view_id].wininv = math::invert(win_mat);
|
||||
|
||||
is_inverted_ = (is_negative_m4(view_mat.ptr()) == is_negative_m4(win_mat.ptr()));
|
||||
|
||||
frustum_boundbox_calc(view_id);
|
||||
frustum_culling_planes_calc(view_id);
|
||||
frustum_culling_sphere_calc(view_id);
|
||||
|
||||
dirty_ = true;
|
||||
manager_fingerprint_ = 0;
|
||||
/* Add 2 to always have a non-null number even in case of overflow. */
|
||||
sync_counter_ = (global_sync_counter_ += 2);
|
||||
}
|
||||
|
||||
void View::frustum_boundbox_calc(int view_id)
|
||||
{
|
||||
/* Extract the 8 corners from a Projection Matrix. */
|
||||
#if 0 /* Equivalent to this but it has accuracy problems. */
|
||||
std::array<float3, 8> box = bounds::corners(Bounds<float3>(float3 (-1), float3 (1)));
|
||||
for (int i = 0; i < 8; i++) {
|
||||
mul_project_m4_v3(data_.wininv.ptr(), box[i]);
|
||||
}
|
||||
#endif
|
||||
|
||||
MutableSpan<float4> corners = {culling_[view_id].frustum_corners.corners,
|
||||
int64_t(ARRAY_SIZE(culling_[view_id].frustum_corners.corners))};
|
||||
|
||||
float left, right, bottom, top, near, far;
|
||||
bool is_persp = data_[view_id].winmat[3][3] == 0.0f;
|
||||
|
||||
projmat_dimensions(data_[view_id].winmat.ptr(), &left, &right, &bottom, &top, &near, &far);
|
||||
|
||||
corners[0][2] = corners[3][2] = corners[7][2] = corners[4][2] = -near;
|
||||
corners[0][0] = corners[3][0] = left;
|
||||
corners[4][0] = corners[7][0] = right;
|
||||
corners[0][1] = corners[4][1] = bottom;
|
||||
corners[7][1] = corners[3][1] = top;
|
||||
|
||||
/* Get the coordinates of the far plane. */
|
||||
if (is_persp) {
|
||||
float sca_far = far / near;
|
||||
left *= sca_far;
|
||||
right *= sca_far;
|
||||
bottom *= sca_far;
|
||||
top *= sca_far;
|
||||
}
|
||||
|
||||
corners[1][2] = corners[2][2] = corners[6][2] = corners[5][2] = -far;
|
||||
corners[1][0] = corners[2][0] = left;
|
||||
corners[6][0] = corners[5][0] = right;
|
||||
corners[1][1] = corners[5][1] = bottom;
|
||||
corners[2][1] = corners[6][1] = top;
|
||||
|
||||
const float4x4 &view_inv = data_[view_id].viewinv;
|
||||
/* Transform into world space. */
|
||||
for (float4 &corner : corners) {
|
||||
corner = float4(math::transform_point(view_inv, float3(corner)), 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
void View::frustum_culling_planes_calc(int view_id)
|
||||
{
|
||||
float4x4 persmat = data_[view_id].winmat * data_[view_id].viewmat;
|
||||
planes_from_projmat(persmat.ptr(),
|
||||
culling_[view_id].frustum_planes.planes[0],
|
||||
culling_[view_id].frustum_planes.planes[5],
|
||||
culling_[view_id].frustum_planes.planes[1],
|
||||
culling_[view_id].frustum_planes.planes[3],
|
||||
culling_[view_id].frustum_planes.planes[4],
|
||||
culling_[view_id].frustum_planes.planes[2]);
|
||||
/* Normalize. */
|
||||
for (float4 &plane : culling_[view_id].frustum_planes.planes) {
|
||||
float len = math::length(plane.xyz());
|
||||
if (len != 0.0f) {
|
||||
plane /= len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void View::frustum_culling_sphere_calc(int view_id)
|
||||
{
|
||||
BoundSphere &bsphere = *reinterpret_cast<BoundSphere *>(&culling_[view_id].bound_sphere);
|
||||
Span<float4> corners = {culling_[view_id].frustum_corners.corners,
|
||||
int64_t(ARRAY_SIZE(culling_[view_id].frustum_corners.corners))};
|
||||
|
||||
/* Extract Bounding Sphere */
|
||||
if (data_[view_id].winmat[3][3] != 0.0f) {
|
||||
/* Orthographic */
|
||||
/* The most extreme points on the near and far plane. (normalized device coords). */
|
||||
const float *nearpoint = corners[0];
|
||||
const float *farpoint = corners[6];
|
||||
|
||||
/* just use median point */
|
||||
mid_v3_v3v3(bsphere.center, farpoint, nearpoint);
|
||||
bsphere.radius = len_v3v3(bsphere.center, farpoint);
|
||||
}
|
||||
else if (data_[view_id].winmat[2][0] == 0.0f && data_[view_id].winmat[2][1] == 0.0f) {
|
||||
/* Perspective with symmetrical frustum. */
|
||||
|
||||
/* We obtain the center and radius of the circumscribed circle of the
|
||||
* isosceles trapezoid composed by the diagonals of the near and far clipping plane */
|
||||
|
||||
/* center of each clipping plane */
|
||||
float mid_min[3], mid_max[3];
|
||||
mid_v3_v3v3(mid_min, corners[3], corners[4]);
|
||||
mid_v3_v3v3(mid_max, corners[2], corners[5]);
|
||||
|
||||
/* square length of the diagonals of each clipping plane */
|
||||
float a_sq = len_squared_v3v3(corners[3], corners[4]);
|
||||
float b_sq = len_squared_v3v3(corners[2], corners[5]);
|
||||
|
||||
/* distance squared between clipping planes */
|
||||
float h_sq = len_squared_v3v3(mid_min, mid_max);
|
||||
|
||||
float fac = (4 * h_sq + b_sq - a_sq) / (8 * h_sq);
|
||||
|
||||
/* The goal is to get the smallest sphere,
|
||||
* not the sphere that passes through each corner */
|
||||
CLAMP(fac, 0.0f, 1.0f);
|
||||
|
||||
interp_v3_v3v3(bsphere.center, mid_min, mid_max, fac);
|
||||
|
||||
/* distance from the center to one of the points of the far plane (1, 2, 5, 6) */
|
||||
bsphere.radius = len_v3v3(bsphere.center, corners[1]);
|
||||
}
|
||||
else {
|
||||
/* Perspective with asymmetrical frustum. */
|
||||
|
||||
/* We put the sphere center on the line that goes from origin
|
||||
* to the center of the far clipping plane. */
|
||||
|
||||
/* Detect which of the corner of the far clipping plane is the farthest to the origin */
|
||||
float nfar[4]; /* most extreme far point in NDC space */
|
||||
float farxy[2]; /* far-point projection onto the near plane */
|
||||
float farpoint[3] = {0.0f}; /* most extreme far point in camera coordinate */
|
||||
float nearpoint[3]; /* most extreme near point in camera coordinate */
|
||||
float farcenter[3] = {0.0f}; /* center of far clipping plane in camera coordinate */
|
||||
float F = -1.0f, N; /* square distance of far and near point to origin */
|
||||
float f, n; /* distance of far and near point to z axis. f is always > 0 but n can be < 0 */
|
||||
float e, s; /* far and near clipping distance (<0) */
|
||||
float c; /* slope of center line = distance of far clipping center
|
||||
* to z axis / far clipping distance. */
|
||||
float z; /* projection of sphere center on z axis (<0) */
|
||||
|
||||
/* Find farthest corner and center of far clip plane. */
|
||||
float corner[3] = {1.0f, 1.0f, 1.0f}; /* in clip space */
|
||||
for (int i = 0; i < 4; i++) {
|
||||
float point[3];
|
||||
mul_v3_project_m4_v3(point, data_[view_id].wininv.ptr(), corner);
|
||||
float len = len_squared_v3(point);
|
||||
if (len > F) {
|
||||
copy_v3_v3(nfar, corner);
|
||||
copy_v3_v3(farpoint, point);
|
||||
F = len;
|
||||
}
|
||||
add_v3_v3(farcenter, point);
|
||||
/* rotate by 90 degree to walk through the 4 points of the far clip plane */
|
||||
float tmp = corner[0];
|
||||
corner[0] = -corner[1];
|
||||
corner[1] = tmp;
|
||||
}
|
||||
|
||||
/* the far center is the average of the far clipping points */
|
||||
mul_v3_fl(farcenter, 0.25f);
|
||||
/* the extreme near point is the opposite point on the near clipping plane */
|
||||
copy_v3_fl3(nfar, -nfar[0], -nfar[1], -1.0f);
|
||||
mul_v3_project_m4_v3(nearpoint, data_[view_id].wininv.ptr(), nfar);
|
||||
/* this is a frustum projection */
|
||||
N = len_squared_v3(nearpoint);
|
||||
e = farpoint[2];
|
||||
s = nearpoint[2];
|
||||
/* distance to view Z axis */
|
||||
f = len_v2(farpoint);
|
||||
/* get corresponding point on the near plane */
|
||||
mul_v2_v2fl(farxy, farpoint, s / e);
|
||||
/* this formula preserve the sign of n */
|
||||
sub_v2_v2(nearpoint, farxy);
|
||||
n = f * s / e - len_v2(nearpoint);
|
||||
c = len_v2(farcenter) / e;
|
||||
/* the big formula, it simplifies to (F-N)/(2(e-s)) for the symmetric case */
|
||||
z = (F - N) / (2.0f * (e - s + c * (f - n)));
|
||||
|
||||
bsphere.center[0] = farcenter[0] * z / e;
|
||||
bsphere.center[1] = farcenter[1] * z / e;
|
||||
bsphere.center[2] = z;
|
||||
|
||||
/* For XR, the view matrix may contain a scale factor. Then, transforming only the center
|
||||
* into world space after calculating the radius will result in incorrect behavior. */
|
||||
mul_m4_v3(data_[view_id].viewinv.ptr(), bsphere.center); /* Transform to world space. */
|
||||
mul_m4_v3(data_[view_id].viewinv.ptr(), farpoint);
|
||||
bsphere.radius = len_v3v3(bsphere.center, farpoint);
|
||||
}
|
||||
}
|
||||
|
||||
void View::bind()
|
||||
{
|
||||
if (dirty_ && !procedural_) {
|
||||
dirty_ = false;
|
||||
data_.push_update();
|
||||
culling_.push_update();
|
||||
}
|
||||
|
||||
GPU_uniformbuf_bind(data_, DRW_VIEW_UBO_SLOT);
|
||||
GPU_uniformbuf_bind(culling_, DRW_VIEW_CULLING_UBO_SLOT);
|
||||
}
|
||||
|
||||
void View::compute_procedural_bounds()
|
||||
{
|
||||
/* Sync happens on the GPU. This is called after each sync. */
|
||||
manager_fingerprint_ = 0;
|
||||
/* Add 2 to always have a non-null number even in case of overflow. */
|
||||
sync_counter_ = (global_sync_counter_ += 2);
|
||||
|
||||
GPU_debug_group_begin("View.compute_procedural_bounds");
|
||||
|
||||
gpu::Shader *shader = DRW_shader_draw_view_finalize_get();
|
||||
GPU_shader_bind(shader);
|
||||
GPU_uniformbuf_bind_as_ssbo(culling_, GPU_shader_get_ssbo_binding(shader, "view_culling_buf"));
|
||||
GPU_uniformbuf_bind(data_, DRW_VIEW_UBO_SLOT);
|
||||
GPU_compute_dispatch(shader, 1, 1, 1);
|
||||
GPU_memory_barrier(GPU_BARRIER_UNIFORM);
|
||||
|
||||
GPU_debug_group_end();
|
||||
}
|
||||
|
||||
void View::compute_visibility(ObjectBoundsBuf &bounds,
|
||||
ObjectInfosBuf & /*infos*/,
|
||||
uint resource_len,
|
||||
bool debug_freeze)
|
||||
{
|
||||
if (debug_freeze && frozen_ == false) {
|
||||
data_freeze_[0] = static_cast<ViewMatrices>(data_[0]);
|
||||
data_freeze_.push_update();
|
||||
culling_freeze_[0] = static_cast<ViewCullingData>(culling_[0]);
|
||||
culling_freeze_.push_update();
|
||||
}
|
||||
if (debug_freeze) {
|
||||
float4x4 persmat = data_freeze_[0].winmat * data_freeze_[0].viewmat;
|
||||
drw_debug_matrix_as_bbox(math::invert(persmat), float4(0, 1, 0, 1));
|
||||
}
|
||||
frozen_ = debug_freeze;
|
||||
|
||||
GPU_debug_group_begin("View.compute_visibility");
|
||||
|
||||
/* TODO(fclem): Early out if visibility hasn't changed. */
|
||||
|
||||
uint word_per_draw = this->visibility_word_per_draw();
|
||||
/* Switch between tightly packed and set of whole word per instance. */
|
||||
uint words_len = (view_len_ == 1) ? divide_ceil_u(resource_len, 32) :
|
||||
resource_len * word_per_draw;
|
||||
words_len = ceil_to_multiple_u(max_ii(1, words_len), 4);
|
||||
/* TODO(fclem): Resize to nearest pow2 to reduce fragmentation. */
|
||||
visibility_buf_.resize(words_len);
|
||||
|
||||
const uint32_t data = 0xFFFFFFFFu;
|
||||
GPU_storagebuf_clear(visibility_buf_, data);
|
||||
|
||||
if (do_visibility_) {
|
||||
gpu::Shader *shader = DRW_shader_draw_visibility_compute_get();
|
||||
GPU_shader_bind(shader);
|
||||
GPU_shader_uniform_1i(shader, "resource_len", resource_len);
|
||||
GPU_shader_uniform_1i(shader, "view_len", view_len_);
|
||||
GPU_shader_uniform_1i(shader, "visibility_word_per_draw", word_per_draw);
|
||||
GPU_storagebuf_bind(bounds, GPU_shader_get_ssbo_binding(shader, "bounds_buf"));
|
||||
GPU_storagebuf_bind(visibility_buf_, GPU_shader_get_ssbo_binding(shader, "visibility_buf"));
|
||||
GPU_uniformbuf_bind(frozen_ ? data_freeze_ : data_, DRW_VIEW_UBO_SLOT);
|
||||
GPU_uniformbuf_bind(frozen_ ? culling_freeze_ : culling_, DRW_VIEW_CULLING_UBO_SLOT);
|
||||
GPU_compute_dispatch(shader, divide_ceil_u(resource_len, DRW_VISIBILITY_GROUP_SIZE), 1, 1);
|
||||
GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE);
|
||||
}
|
||||
|
||||
if (frozen_) {
|
||||
/* Bind back the non frozen data. */
|
||||
GPU_uniformbuf_bind(data_, DRW_VIEW_UBO_SLOT);
|
||||
GPU_uniformbuf_bind(culling_, DRW_VIEW_CULLING_UBO_SLOT);
|
||||
}
|
||||
|
||||
GPU_debug_group_end();
|
||||
}
|
||||
|
||||
VisibilityBuf &View::get_visibility_buffer()
|
||||
{
|
||||
return visibility_buf_;
|
||||
}
|
||||
|
||||
draw::View &View::default_get()
|
||||
{
|
||||
return *drw_get().data->default_view;
|
||||
}
|
||||
|
||||
void View::default_set(const float4x4 &view_mat, const float4x4 &win_mat)
|
||||
{
|
||||
drw_get().data->default_view->sync(view_mat, win_mat);
|
||||
}
|
||||
|
||||
std::array<float4, 6> View::frustum_planes_get(int view_id) const
|
||||
{
|
||||
return {culling_[view_id].frustum_planes.planes[0],
|
||||
culling_[view_id].frustum_planes.planes[1],
|
||||
culling_[view_id].frustum_planes.planes[2],
|
||||
culling_[view_id].frustum_planes.planes[3],
|
||||
culling_[view_id].frustum_planes.planes[4],
|
||||
culling_[view_id].frustum_planes.planes[5]};
|
||||
}
|
||||
|
||||
std::array<float3, 8> View::frustum_corners_get(int view_id) const
|
||||
{
|
||||
return {culling_[view_id].frustum_corners.corners[0].xyz(),
|
||||
culling_[view_id].frustum_corners.corners[1].xyz(),
|
||||
culling_[view_id].frustum_corners.corners[2].xyz(),
|
||||
culling_[view_id].frustum_corners.corners[3].xyz(),
|
||||
culling_[view_id].frustum_corners.corners[4].xyz(),
|
||||
culling_[view_id].frustum_corners.corners[5].xyz(),
|
||||
culling_[view_id].frustum_corners.corners[6].xyz(),
|
||||
culling_[view_id].frustum_corners.corners[7].xyz()};
|
||||
}
|
||||
|
||||
} // namespace blender::draw
|
||||
264
blender-5.2.0/source/blender/draw/intern/draw_view.hh
Normal file
264
blender-5.2.0/source/blender/draw/intern/draw_view.hh
Normal file
@@ -0,0 +1,264 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*
|
||||
* View description and states.
|
||||
*
|
||||
* A `draw::View` object is required for drawing geometry using the DRW API and its internal
|
||||
* culling system.
|
||||
*
|
||||
* One `View` object can actually contain multiple view matrices if the template parameter
|
||||
* `view_len` is greater than 1. This is called multi-view rendering and the vertex shader must
|
||||
* setting `drw_view_id` accordingly.
|
||||
*/
|
||||
|
||||
#include "DNA_view3d_types.h"
|
||||
#include "DRW_gpu_wrapper.hh"
|
||||
#include "GPU_matrix.hh"
|
||||
|
||||
#include "draw_shader_shared.hh"
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
class Manager;
|
||||
|
||||
/* TODO: de-duplicate. */
|
||||
using ObjectBoundsBuf = StorageArrayBuffer<ObjectBounds, 128>;
|
||||
using ObjectInfosBuf = StorageArrayBuffer<ObjectInfos, 128>;
|
||||
using VisibilityBuf = StorageArrayBuffer<uint, 4, true>;
|
||||
|
||||
class View {
|
||||
friend Manager;
|
||||
|
||||
/** Number of sync done by views. Used for fingerprint. */
|
||||
static std::atomic<uint32_t> global_sync_counter_;
|
||||
|
||||
/* Local sync counter. Used for fingerprint. */
|
||||
uint32_t sync_counter_ = 0;
|
||||
|
||||
protected:
|
||||
/** TODO(fclem): Maybe try to reduce the minimum cost if the number of view is lower. */
|
||||
|
||||
UniformArrayBuffer<ViewMatrices, DRW_VIEW_MAX> data_;
|
||||
UniformArrayBuffer<ViewCullingData, DRW_VIEW_MAX> culling_;
|
||||
/** Frozen version of data_ used for debugging culling. */
|
||||
UniformArrayBuffer<ViewMatrices, DRW_VIEW_MAX> data_freeze_;
|
||||
UniformArrayBuffer<ViewCullingData, DRW_VIEW_MAX> culling_freeze_;
|
||||
/** Result of the visibility computation. 1 bit or 1 or 2 word per resource ID per view. */
|
||||
VisibilityBuf visibility_buf_;
|
||||
/* Fingerprint of the manager state when visibility was computed. */
|
||||
uint64_t manager_fingerprint_ = 0;
|
||||
|
||||
const char *debug_name_;
|
||||
|
||||
int view_len_ = 0;
|
||||
|
||||
bool is_inverted_ = false;
|
||||
bool do_visibility_ = true;
|
||||
bool dirty_ = true;
|
||||
bool frozen_ = false;
|
||||
bool procedural_ = false;
|
||||
|
||||
public:
|
||||
View(const char *name, int view_len = 1, bool procedural = false)
|
||||
: visibility_buf_(name), debug_name_(name), view_len_(view_len), procedural_(procedural)
|
||||
{
|
||||
BLI_assert(view_len <= DRW_VIEW_MAX);
|
||||
}
|
||||
|
||||
virtual ~View() = default;
|
||||
|
||||
void sync(const float4x4 &view_mat, const float4x4 &win_mat, int view_id = 0);
|
||||
|
||||
/** Enable or disable every visibility test (frustum culling, HiZ culling). */
|
||||
void visibility_test(bool enable)
|
||||
{
|
||||
do_visibility_ = enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update culling data using a compute shader.
|
||||
* This is to be used if the matrices were updated externally
|
||||
* on the GPU (not using the `sync()` method).
|
||||
*/
|
||||
void compute_procedural_bounds();
|
||||
|
||||
bool is_persp(int view_id = 0) const
|
||||
{
|
||||
BLI_assert(view_id < view_len_);
|
||||
return data_[view_id].winmat[3][3] == 0.0f;
|
||||
}
|
||||
|
||||
bool is_inverted(int view_id = 0) const
|
||||
{
|
||||
BLI_assert(view_id < view_len_);
|
||||
UNUSED_VARS_NDEBUG(view_id);
|
||||
return is_inverted_;
|
||||
}
|
||||
|
||||
float far_clip(int view_id = 0) const
|
||||
{
|
||||
BLI_assert(view_id < view_len_);
|
||||
if (is_persp(view_id)) {
|
||||
return -data_[view_id].winmat[3][2] / (data_[view_id].winmat[2][2] + 1.0f);
|
||||
}
|
||||
return -(data_[view_id].winmat[3][2] - 1.0f) / data_[view_id].winmat[2][2];
|
||||
}
|
||||
|
||||
float near_clip(int view_id = 0) const
|
||||
{
|
||||
BLI_assert(view_id < view_len_);
|
||||
if (is_persp(view_id)) {
|
||||
return -data_[view_id].winmat[3][2] / (data_[view_id].winmat[2][2] - 1.0f);
|
||||
}
|
||||
return -(data_[view_id].winmat[3][2] + 1.0f) / data_[view_id].winmat[2][2];
|
||||
}
|
||||
|
||||
const float3 &location(int view_id = 0) const
|
||||
{
|
||||
BLI_assert(view_id < view_len_);
|
||||
return data_[view_id].viewinv.location();
|
||||
}
|
||||
|
||||
const float3 &forward(int view_id = 0) const
|
||||
{
|
||||
BLI_assert(view_id < view_len_);
|
||||
return data_[view_id].viewinv.z_axis();
|
||||
}
|
||||
|
||||
const float4x4 &viewmat(int view_id = 0) const
|
||||
{
|
||||
BLI_assert(view_id < view_len_);
|
||||
return data_[view_id].viewmat;
|
||||
}
|
||||
|
||||
const float4x4 &viewinv(int view_id = 0) const
|
||||
{
|
||||
BLI_assert(view_id < view_len_);
|
||||
return data_[view_id].viewinv;
|
||||
}
|
||||
|
||||
const float4x4 &winmat(int view_id = 0) const
|
||||
{
|
||||
BLI_assert(view_id < view_len_);
|
||||
return data_[view_id].winmat;
|
||||
}
|
||||
|
||||
const float4x4 &wininv(int view_id = 0) const
|
||||
{
|
||||
BLI_assert(view_id < view_len_);
|
||||
return data_[view_id].wininv;
|
||||
}
|
||||
|
||||
/* Compute and return the perspective matrix. */
|
||||
const float4x4 persmat(int view_id = 0) const
|
||||
{
|
||||
BLI_assert(view_id < view_len_);
|
||||
return data_[view_id].winmat * data_[view_id].viewmat;
|
||||
}
|
||||
|
||||
int visibility_word_per_draw() const
|
||||
{
|
||||
return (view_len_ == 1) ? 0 : divide_ceil_u(view_len_, 32);
|
||||
}
|
||||
|
||||
UniformArrayBuffer<ViewMatrices, DRW_VIEW_MAX> &matrices_ubo_get()
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
/* TODO(fclem): Remove. Global DST access. */
|
||||
static View &default_get();
|
||||
static void default_set(const float4x4 &view_mat, const float4x4 &win_mat);
|
||||
|
||||
/* Data to save per overlay to not rely on rv3d for rendering.
|
||||
* TODO(fclem): Compute offset directly from the view. */
|
||||
struct OffsetData {
|
||||
/* Copy of rv3d->dist. */
|
||||
float dist = 0.0f;
|
||||
/* Copy of rv3d->persp. */
|
||||
char persp = 0;
|
||||
/* Copy of rv3d->is_persp. */
|
||||
bool is_persp = false;
|
||||
|
||||
OffsetData() = default;
|
||||
OffsetData(const RegionView3D &rv3d)
|
||||
: dist(rv3d.dist), persp(rv3d.persp), is_persp(rv3d.is_persp != 0)
|
||||
{
|
||||
}
|
||||
|
||||
float4x4 winmat_polygon_offset(const float4x4 &winmat, float offset)
|
||||
{
|
||||
float view_dist = dist;
|
||||
/* Special exception for orthographic camera:
|
||||
* `view_dist` isn't used as the depth range isn't the same. */
|
||||
if (persp == RV3D_CAMOB && is_persp == false) {
|
||||
view_dist = 1.0f / max_ff(fabsf(winmat[0][0]), fabsf(winmat[1][1]));
|
||||
}
|
||||
|
||||
float4x4 result = winmat;
|
||||
result[3][2] -= GPU_polygon_offset_calc(winmat.ptr(), view_dist, offset);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Return unit offset to apply to `gl_Position.z`. To be scaled depending on purpose. */
|
||||
float polygon_offset_factor(const float4x4 &winmat)
|
||||
{
|
||||
float view_dist = dist;
|
||||
/* Special exception for orthographic camera:
|
||||
* `view_dist` isn't used as the depth range isn't the same. */
|
||||
if (persp == RV3D_CAMOB && is_persp == false) {
|
||||
view_dist = 1.0f / max_ff(fabsf(winmat[0][0]), fabsf(winmat[1][1]));
|
||||
}
|
||||
|
||||
return GPU_polygon_offset_calc(winmat.ptr(), view_dist, 1.0);
|
||||
}
|
||||
};
|
||||
|
||||
/* Returns frustum planes equations. Available only after sync. */
|
||||
std::array<float4, 6> frustum_planes_get(int view_id = 0) const;
|
||||
/* Returns frustum corners positions in world space. Available only after sync.
|
||||
* Follow bounding box corner order. */
|
||||
std::array<float3, 8> frustum_corners_get(int view_id = 0) const;
|
||||
|
||||
protected:
|
||||
/** Called from draw manager. */
|
||||
void bind();
|
||||
virtual void compute_visibility(ObjectBoundsBuf &bounds,
|
||||
ObjectInfosBuf &infos,
|
||||
uint resource_len,
|
||||
bool debug_freeze);
|
||||
virtual VisibilityBuf &get_visibility_buffer();
|
||||
|
||||
bool has_computed_visibility() const
|
||||
{
|
||||
/* NOTE: Even though manager fingerprint is not enough to check for update, it is still
|
||||
* guaranteed to not be 0. So we can check weather or not this view has computed visibility
|
||||
* after sync. Asserts will catch invalid usage . */
|
||||
return manager_fingerprint_ != 0;
|
||||
}
|
||||
|
||||
/* Fingerprint of the view for the current state.
|
||||
* Not reliable enough for general update detection. Only to be used for debugging assertion. */
|
||||
uint64_t fingerprint_get() const
|
||||
{
|
||||
BLI_assert_msg(sync_counter_ != 0, "View should be synced at least once before use");
|
||||
return sync_counter_;
|
||||
}
|
||||
|
||||
void update_viewport_size();
|
||||
|
||||
/* WARNING: These 3 functions must be called in order */
|
||||
void frustum_boundbox_calc(int view_id);
|
||||
void frustum_culling_planes_calc(int view_id);
|
||||
void frustum_culling_sphere_calc(int view_id);
|
||||
};
|
||||
|
||||
} // namespace blender::draw
|
||||
60
blender-5.2.0/source/blender/draw/intern/draw_view_c.cc
Normal file
60
blender-5.2.0/source/blender/draw/intern/draw_view_c.cc
Normal file
@@ -0,0 +1,60 @@
|
||||
/* SPDX-FileCopyrightText: 2016 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*
|
||||
* Contains dynamic drawing using immediate mode
|
||||
*/
|
||||
|
||||
#include "DNA_screen_types.h"
|
||||
|
||||
#include "ED_screen.hh"
|
||||
#include "ED_view3d.hh"
|
||||
|
||||
#include "GPU_debug.hh"
|
||||
#include "GPU_state.hh"
|
||||
|
||||
#include "UI_view2d.hh"
|
||||
|
||||
#include "WM_types.hh"
|
||||
|
||||
#include "BKE_paint.hh"
|
||||
#include "BKE_screen.hh"
|
||||
|
||||
#include "DRW_engine.hh"
|
||||
#include "DRW_render.hh"
|
||||
|
||||
#include "draw_view_c.hh"
|
||||
|
||||
#include "view3d_intern.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* ******************** region info ***************** */
|
||||
|
||||
void DRW_draw_region_info(const bContext *C, ARegion *region)
|
||||
{
|
||||
GPU_debug_group_begin("RegionInfo");
|
||||
view3d_draw_region_info(C, region);
|
||||
GPU_debug_group_end();
|
||||
}
|
||||
|
||||
/* **************************** 3D Gizmo ******************************** */
|
||||
|
||||
void DRW_draw_gizmo_3d(const bContext *C, ARegion *region)
|
||||
{
|
||||
/* draw depth culled gizmos - gizmos need to be updated *after* view matrix was set up */
|
||||
/* TODO: depth culling gizmos is not yet supported, just drawing _3D here, should
|
||||
* later become _IN_SCENE (and draw _3D separate) */
|
||||
WM_gizmomap_draw(region->runtime->gizmo_map, C, WM_GIZMOMAP_DRAWSTEP_3D);
|
||||
}
|
||||
|
||||
void DRW_draw_gizmo_2d(const bContext *C, ARegion *region)
|
||||
{
|
||||
WM_gizmomap_draw(region->runtime->gizmo_map, C, WM_GIZMOMAP_DRAWSTEP_2D);
|
||||
GPU_depth_mask(true);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
20
blender-5.2.0/source/blender/draw/intern/draw_view_c.hh
Normal file
20
blender-5.2.0/source/blender/draw/intern/draw_view_c.hh
Normal file
@@ -0,0 +1,20 @@
|
||||
/* SPDX-FileCopyrightText: 2016 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct bContext;
|
||||
struct ARegion;
|
||||
|
||||
void DRW_draw_region_info(const bContext *C, ARegion *region);
|
||||
void DRW_draw_gizmo_3d(const bContext *C, ARegion *region);
|
||||
void DRW_draw_gizmo_2d(const bContext *C, ARegion *region);
|
||||
|
||||
} // namespace blender
|
||||
138
blender-5.2.0/source/blender/draw/intern/draw_view_data.cc
Normal file
138
blender-5.2.0/source/blender/draw/intern/draw_view_data.cc
Normal file
@@ -0,0 +1,138 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "GPU_viewport.hh"
|
||||
|
||||
#include "DRW_gpu_wrapper.hh"
|
||||
#include "DRW_render.hh"
|
||||
|
||||
#include "draw_manager_text.hh"
|
||||
|
||||
#include "draw_context_private.hh"
|
||||
#include "draw_manager.hh"
|
||||
#include "draw_view_data.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
DRWViewData::DRWViewData()
|
||||
{
|
||||
manager = new draw::Manager();
|
||||
};
|
||||
|
||||
DRWViewData::~DRWViewData()
|
||||
{
|
||||
this->clear(true);
|
||||
delete manager;
|
||||
};
|
||||
|
||||
void DRW_view_data_default_lists_from_viewport(DRWViewData *view_data, GPUViewport *viewport)
|
||||
{
|
||||
int active_view = GPU_viewport_active_view_get(viewport);
|
||||
view_data->from_viewport = true;
|
||||
|
||||
DefaultFramebufferList *dfbl = &view_data->dfbl;
|
||||
DefaultTextureList *dtxl = &view_data->dtxl;
|
||||
/* Depth texture is shared between both stereo views. */
|
||||
dtxl->depth = GPU_viewport_depth_texture(viewport);
|
||||
dtxl->color = GPU_viewport_color_texture(viewport, active_view);
|
||||
dtxl->color_overlay = GPU_viewport_overlay_texture(viewport, active_view);
|
||||
|
||||
GPU_framebuffer_ensure_config(&dfbl->default_fb,
|
||||
{
|
||||
GPU_ATTACHMENT_TEXTURE(dtxl->depth),
|
||||
GPU_ATTACHMENT_TEXTURE(dtxl->color),
|
||||
});
|
||||
GPU_framebuffer_ensure_config(&dfbl->overlay_fb,
|
||||
{
|
||||
GPU_ATTACHMENT_TEXTURE(dtxl->depth),
|
||||
GPU_ATTACHMENT_TEXTURE(dtxl->color_overlay),
|
||||
});
|
||||
GPU_framebuffer_ensure_config(&dfbl->depth_only_fb,
|
||||
{
|
||||
GPU_ATTACHMENT_TEXTURE(dtxl->depth),
|
||||
GPU_ATTACHMENT_NONE,
|
||||
});
|
||||
GPU_framebuffer_ensure_config(&dfbl->color_only_fb,
|
||||
{
|
||||
GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_TEXTURE(dtxl->color),
|
||||
});
|
||||
GPU_framebuffer_ensure_config(&dfbl->overlay_only_fb,
|
||||
{
|
||||
GPU_ATTACHMENT_NONE,
|
||||
GPU_ATTACHMENT_TEXTURE(dtxl->color_overlay),
|
||||
});
|
||||
}
|
||||
|
||||
void DRWViewData::clear(bool free_instance_data)
|
||||
{
|
||||
GPU_FRAMEBUFFER_FREE_SAFE(this->dfbl.default_fb);
|
||||
GPU_FRAMEBUFFER_FREE_SAFE(this->dfbl.overlay_fb);
|
||||
GPU_FRAMEBUFFER_FREE_SAFE(this->dfbl.in_front_fb);
|
||||
GPU_FRAMEBUFFER_FREE_SAFE(this->dfbl.color_only_fb);
|
||||
GPU_FRAMEBUFFER_FREE_SAFE(this->dfbl.depth_only_fb);
|
||||
GPU_FRAMEBUFFER_FREE_SAFE(this->dfbl.overlay_only_fb);
|
||||
|
||||
if (!this->from_viewport) {
|
||||
GPU_TEXTURE_FREE_SAFE(this->dtxl.color);
|
||||
GPU_TEXTURE_FREE_SAFE(this->dtxl.color_overlay);
|
||||
GPU_TEXTURE_FREE_SAFE(this->dtxl.depth);
|
||||
}
|
||||
GPU_TEXTURE_FREE_SAFE(this->dtxl.depth_in_front);
|
||||
|
||||
if (free_instance_data) {
|
||||
foreach_engine([&](DrawEngine::Pointer &ptr) {
|
||||
if (ptr.instance) {
|
||||
ptr.free_instance();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void DRWViewData::texture_list_size_validate(const int2 &size)
|
||||
{
|
||||
if (this->texture_list_size != size) {
|
||||
this->clear(false);
|
||||
copy_v2_v2_int(this->texture_list_size, size);
|
||||
}
|
||||
}
|
||||
|
||||
void DRW_view_data_reset(DRWViewData *view_data)
|
||||
{
|
||||
view_data->foreach_enabled_engine([&](DrawEngine &instance) { instance.used = false; });
|
||||
|
||||
for (std::unique_ptr<draw::TextureFromPool> &texture :
|
||||
view_data->viewport_compositor_passes.values())
|
||||
{
|
||||
texture->release();
|
||||
}
|
||||
view_data->viewport_compositor_passes.clear();
|
||||
}
|
||||
|
||||
void DRW_view_data_free_unused(DRWViewData *view_data)
|
||||
{
|
||||
view_data->foreach_engine([&](DrawEngine::Pointer &ptr) {
|
||||
if (ptr.instance && ptr.instance->used == false) {
|
||||
ptr.free_instance();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
draw::Manager *DRW_manager_get()
|
||||
{
|
||||
BLI_assert(drw_get().view_data_active->manager);
|
||||
return drw_get().view_data_active->manager;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
146
blender-5.2.0/source/blender/draw/intern/draw_view_data.hh
Normal file
146
blender-5.2.0/source/blender/draw/intern/draw_view_data.hh
Normal file
@@ -0,0 +1,146 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*
|
||||
* Engine data
|
||||
* Structure containing each draw engine instance data.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
|
||||
#include "DRW_render.hh"
|
||||
#include "draw_context_private.hh"
|
||||
|
||||
#include "engines/compositor/compositor_engine.h"
|
||||
#include "engines/eevee/eevee_engine.h"
|
||||
#include "engines/external/external_engine.h"
|
||||
#include "engines/gpencil/gpencil_engine.hh"
|
||||
#include "engines/image/image_engine.h"
|
||||
#include "engines/overlay/overlay_engine.h"
|
||||
#include "engines/select/select_engine.hh"
|
||||
#include "engines/workbench/workbench_engine.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
#define GPU_INFO_SIZE 512 /* IMA_MAX_RENDER_TEXT_SIZE */
|
||||
|
||||
namespace draw {
|
||||
class TextureFromPool;
|
||||
class Manager;
|
||||
} // namespace draw
|
||||
|
||||
struct DRWTextStore;
|
||||
namespace gpu {
|
||||
class FrameBuffer;
|
||||
class Texture;
|
||||
} // namespace gpu
|
||||
struct GPUViewport;
|
||||
|
||||
/** Buffer and textures used by the viewport by default. */
|
||||
struct DefaultFramebufferList {
|
||||
gpu::FrameBuffer *default_fb;
|
||||
gpu::FrameBuffer *overlay_fb;
|
||||
gpu::FrameBuffer *in_front_fb;
|
||||
gpu::FrameBuffer *color_only_fb;
|
||||
gpu::FrameBuffer *depth_only_fb;
|
||||
gpu::FrameBuffer *overlay_only_fb;
|
||||
};
|
||||
|
||||
struct DefaultTextureList {
|
||||
gpu::Texture *color;
|
||||
gpu::Texture *color_overlay;
|
||||
gpu::Texture *depth;
|
||||
gpu::Texture *depth_in_front;
|
||||
};
|
||||
|
||||
struct DRWViewData {
|
||||
public:
|
||||
DefaultFramebufferList dfbl = {};
|
||||
DefaultTextureList dtxl = {};
|
||||
/** True indicates the textures inside dtxl are from the viewport and should not be freed. */
|
||||
bool from_viewport = false;
|
||||
/** Common size for texture in the engines texture list.
|
||||
* We free all texture lists if it changes. */
|
||||
int2 texture_list_size = {0, 0};
|
||||
|
||||
/** Engines running for this viewport. nullptr if not enabled. */
|
||||
eevee::Engine eevee;
|
||||
workbench::Engine workbench;
|
||||
draw::external::Engine external;
|
||||
image_engine::Engine image;
|
||||
draw::gpencil::Engine grease_pencil;
|
||||
draw::overlay::Engine overlay;
|
||||
draw::select::Engine object_select;
|
||||
draw::edit_select::Engine edit_select;
|
||||
#ifdef WITH_DRAW_DEBUG
|
||||
draw::edit_select_debug::Engine edit_select_debug;
|
||||
#endif
|
||||
draw::compositor_engine::Engine compositor;
|
||||
|
||||
/**
|
||||
* Stores passes needed by the viewport compositor. Engines are expected to populate those in
|
||||
* every redraw using calls to the #DRW_viewport_pass_texture_get function. The compositor can
|
||||
* then call the same function to retrieve the passes it needs, which are expected to be
|
||||
* initialized. Those textures are release when view data is reset.
|
||||
*/
|
||||
Map<std::string, std::unique_ptr<draw::TextureFromPool>> viewport_compositor_passes;
|
||||
|
||||
/** New per view/viewport manager. Null if not supported by current hardware. */
|
||||
draw::Manager *manager = nullptr;
|
||||
|
||||
public:
|
||||
DRWViewData();
|
||||
~DRWViewData();
|
||||
|
||||
void texture_list_size_validate(const int2 &size);
|
||||
|
||||
template<typename CallbackT> void foreach_engine(CallbackT callback)
|
||||
{
|
||||
/* IMPORTANT: Order here defines the draw order. */
|
||||
|
||||
/* Render engines. Output to the render result frame-buffer. Mutually exclusive. */
|
||||
|
||||
callback(eevee);
|
||||
callback(workbench);
|
||||
callback(external);
|
||||
callback(image);
|
||||
#ifdef WITH_DRAW_DEBUG
|
||||
callback(edit_select_debug);
|
||||
#endif
|
||||
/** Grease pencil. Merge its output to the render result frame-buffer. */
|
||||
callback(grease_pencil);
|
||||
/** GPU compositor. Processes render result and output to the render result frame-buffer. */
|
||||
callback(compositor);
|
||||
/** Overlays. Draw on a separate overlay frame-buffer. Can read render result. */
|
||||
callback(overlay);
|
||||
|
||||
/** Selection. Are always enabled alone and have no interaction with other engines. */
|
||||
callback(object_select);
|
||||
callback(edit_select);
|
||||
}
|
||||
|
||||
template<typename CallbackT> void foreach_enabled_engine(CallbackT callback)
|
||||
{
|
||||
foreach_engine([&](DrawEngine::Pointer &ptr) {
|
||||
if (ptr.instance == nullptr || ptr.instance->used == false) {
|
||||
return;
|
||||
}
|
||||
callback(*ptr.instance);
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
void clear(bool free_instance_data);
|
||||
};
|
||||
|
||||
void DRW_view_data_default_lists_from_viewport(DRWViewData *view_data, GPUViewport *viewport);
|
||||
void DRW_view_data_reset(DRWViewData *view_data);
|
||||
void DRW_view_data_free_unused(DRWViewData *view_data);
|
||||
|
||||
} // namespace blender
|
||||
297
blender-5.2.0/source/blender/draw/intern/draw_volume.cc
Normal file
297
blender-5.2.0/source/blender/draw/intern/draw_volume.cc
Normal file
@@ -0,0 +1,297 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*
|
||||
* \brief Contains Volume object GPU attributes configuration.
|
||||
*/
|
||||
|
||||
#include "DRW_gpu_wrapper.hh"
|
||||
#include "DRW_render.hh"
|
||||
|
||||
#include "DNA_fluid_types.h"
|
||||
#include "DNA_volume_types.h"
|
||||
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "BKE_global.hh"
|
||||
#include "BKE_mesh.hh"
|
||||
#include "BKE_modifier.hh"
|
||||
#include "BKE_volume.hh"
|
||||
#include "BKE_volume_render.hh"
|
||||
|
||||
#include "GPU_material.hh"
|
||||
|
||||
#include "draw_cache.hh"
|
||||
#include "draw_common_c.hh"
|
||||
#include "draw_context_private.hh"
|
||||
|
||||
#include "draw_common.hh"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
using VolumeInfosBuf = draw::UniformBuffer<VolumeInfos>;
|
||||
|
||||
struct VolumeUniformBufPool {
|
||||
Vector<VolumeInfosBuf *> ubos;
|
||||
uint used = 0;
|
||||
|
||||
~VolumeUniformBufPool()
|
||||
{
|
||||
for (VolumeInfosBuf *ubo : ubos) {
|
||||
delete ubo;
|
||||
}
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
used = 0;
|
||||
}
|
||||
|
||||
VolumeInfosBuf *alloc()
|
||||
{
|
||||
if (used >= ubos.size()) {
|
||||
VolumeInfosBuf *buf = new VolumeInfosBuf();
|
||||
ubos.append(buf);
|
||||
}
|
||||
return ubos[used++];
|
||||
}
|
||||
};
|
||||
|
||||
struct VolumeModule {
|
||||
VolumeUniformBufPool ubo_pool;
|
||||
|
||||
draw::Texture dummy_zero;
|
||||
draw::Texture dummy_one;
|
||||
|
||||
VolumeModule()
|
||||
{
|
||||
const float zero[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
const float one[4] = {1.0f, 1.0f, 1.0f, 1.0f};
|
||||
const eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ;
|
||||
dummy_zero.ensure_3d(gpu::TextureFormat::SFLOAT_32_32_32_32, int3(1), usage, zero);
|
||||
dummy_one.ensure_3d(gpu::TextureFormat::SFLOAT_32_32_32_32, int3(1), usage, one);
|
||||
GPU_texture_extend_mode(dummy_zero, GPU_SAMPLER_EXTEND_MODE_REPEAT);
|
||||
GPU_texture_extend_mode(dummy_one, GPU_SAMPLER_EXTEND_MODE_REPEAT);
|
||||
}
|
||||
|
||||
gpu::Texture *grid_default_texture(GPUDefaultValue default_value)
|
||||
{
|
||||
switch (default_value) {
|
||||
case GPU_DEFAULT_0:
|
||||
return dummy_zero;
|
||||
case GPU_DEFAULT_1:
|
||||
return dummy_one;
|
||||
}
|
||||
return dummy_zero;
|
||||
}
|
||||
};
|
||||
|
||||
void DRW_volume_init(DRWData *drw_data)
|
||||
{
|
||||
if (drw_data == nullptr) {
|
||||
drw_data = drw_get().data;
|
||||
}
|
||||
if (drw_data->volume_module == nullptr) {
|
||||
drw_data->volume_module = MEM_new<VolumeModule>("VolumeModule");
|
||||
}
|
||||
drw_data->volume_module->ubo_pool.reset();
|
||||
}
|
||||
|
||||
void DRW_volume_module_free(draw::VolumeModule *module)
|
||||
{
|
||||
MEM_delete(module);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Public API for render engines.
|
||||
* \{ */
|
||||
|
||||
template<typename PassType>
|
||||
PassType *volume_world_grids_init(PassType &ps, ListBaseWrapper<GPUMaterialAttribute> &attrs)
|
||||
{
|
||||
VolumeModule &module = *drw_get().data->volume_module;
|
||||
|
||||
PassType *sub = &ps.sub("World Volume");
|
||||
for (const GPUMaterialAttribute *attr : attrs) {
|
||||
sub->bind_texture(attr->input_name, module.grid_default_texture(attr->default_value));
|
||||
}
|
||||
|
||||
return sub;
|
||||
}
|
||||
|
||||
template<typename PassType>
|
||||
PassType *volume_object_grids_init(PassType &ps,
|
||||
Object *ob,
|
||||
ListBaseWrapper<GPUMaterialAttribute> &attrs)
|
||||
{
|
||||
Volume &volume = DRW_object_get_data_for_drawing<Volume>(*ob);
|
||||
BKE_volume_load(&volume, G.main);
|
||||
|
||||
/* Render nothing if there is no attribute. */
|
||||
if (BKE_volume_num_grids(&volume) == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
VolumeModule &module = *drw_get().data->volume_module;
|
||||
VolumeInfosBuf &volume_infos = *module.ubo_pool.alloc();
|
||||
|
||||
volume_infos.density_scale = BKE_volume_density_scale(&volume, ob->object_to_world().ptr());
|
||||
volume_infos.color_mul = float4(1.0f);
|
||||
volume_infos.temperature_mul = 1.0f;
|
||||
volume_infos.temperature_bias = 0.0f;
|
||||
|
||||
PassType *sub = &ps.sub("Volume Object SubPass");
|
||||
|
||||
/* Bind volume grid textures. */
|
||||
int grid_id = 0;
|
||||
for (const GPUMaterialAttribute *attr : attrs) {
|
||||
const bke::VolumeGridData *volume_grid = BKE_volume_grid_find(&volume, attr->name);
|
||||
const DRWVolumeGrid *drw_grid = (volume_grid) ?
|
||||
DRW_volume_batch_cache_get_grid(&volume, volume_grid) :
|
||||
nullptr;
|
||||
/* Handle 3 cases here:
|
||||
* - Grid exists and texture was loaded -> use texture.
|
||||
* - Grid exists but has zero size or failed to load -> use zero.
|
||||
* - Grid does not exist -> use default value. */
|
||||
const gpu::Texture *grid_tex = (drw_grid) ? drw_grid->texture :
|
||||
(volume_grid) ?
|
||||
module.dummy_zero :
|
||||
module.grid_default_texture(attr->default_value);
|
||||
/* TODO(@pragma37): bind_texture const support ? */
|
||||
sub->bind_texture(attr->input_name, const_cast<gpu::Texture *>(grid_tex));
|
||||
|
||||
volume_infos.grids_xform[grid_id++] = drw_grid ? float4x4(drw_grid->object_to_texture) :
|
||||
float4x4::identity();
|
||||
}
|
||||
|
||||
volume_infos.push_update();
|
||||
|
||||
sub->bind_ubo("drw_volume", volume_infos);
|
||||
|
||||
return sub;
|
||||
}
|
||||
|
||||
template<typename PassType>
|
||||
PassType *drw_volume_object_mesh_init(PassType &ps,
|
||||
Scene *scene,
|
||||
Object *ob,
|
||||
ListBaseWrapper<GPUMaterialAttribute> &attrs)
|
||||
{
|
||||
VolumeModule &module = *drw_get().data->volume_module;
|
||||
VolumeInfosBuf &volume_infos = *module.ubo_pool.alloc();
|
||||
|
||||
ModifierData *md = nullptr;
|
||||
|
||||
volume_infos.density_scale = 1.0f;
|
||||
volume_infos.color_mul = float4(1.0f);
|
||||
volume_infos.temperature_mul = 1.0f;
|
||||
volume_infos.temperature_bias = 0.0f;
|
||||
|
||||
bool has_fluid_modifier = (md = BKE_modifiers_findby_type(ob, eModifierType_Fluid)) &&
|
||||
BKE_modifier_is_enabled(scene, md, eModifierMode_Realtime) &&
|
||||
(reinterpret_cast<FluidModifierData *>(md))->domain != nullptr;
|
||||
FluidModifierData *fmd = has_fluid_modifier ? reinterpret_cast<FluidModifierData *>(md) :
|
||||
nullptr;
|
||||
FluidDomainSettings *fds = has_fluid_modifier ? fmd->domain : nullptr;
|
||||
|
||||
PassType *sub = nullptr;
|
||||
|
||||
if (!has_fluid_modifier || (fds->type != FLUID_DOMAIN_TYPE_GAS)) {
|
||||
/* No volume attributes or fluid domain. */
|
||||
sub = &ps.sub("Volume Mesh SubPass");
|
||||
int grid_id = 0;
|
||||
for (const GPUMaterialAttribute *attr : attrs) {
|
||||
sub->bind_texture(attr->input_name, module.grid_default_texture(attr->default_value));
|
||||
volume_infos.grids_xform[grid_id++] = float4x4::identity();
|
||||
}
|
||||
}
|
||||
else if (fds->fluid) {
|
||||
/* Smoke Simulation. */
|
||||
DRW_smoke_ensure(fmd, fds->flags & FLUID_DOMAIN_USE_NOISE);
|
||||
|
||||
sub = &ps.sub("Volume Modifier SubPass");
|
||||
|
||||
float3 location, scale;
|
||||
BKE_mesh_texspace_get(&DRW_object_get_data_for_drawing<Mesh>(*ob), location, scale);
|
||||
float3 orco_mul = math::safe_rcp(scale * 2.0);
|
||||
float3 orco_add = (location - scale) * -orco_mul;
|
||||
/* Replace OrcoTexCoFactors with a matrix multiplication. */
|
||||
float4x4 orco_mat = math::from_scale<float4x4>(orco_mul);
|
||||
orco_mat.location() = orco_add;
|
||||
|
||||
int grid_id = 0;
|
||||
for (const GPUMaterialAttribute *attr : attrs) {
|
||||
if (STREQ(attr->name, "density")) {
|
||||
sub->bind_texture(attr->input_name,
|
||||
fds->tex_density ? &fds->tex_density : &module.dummy_one);
|
||||
}
|
||||
else if (STREQ(attr->name, "color")) {
|
||||
sub->bind_texture(attr->input_name, fds->tex_color ? &fds->tex_color : &module.dummy_one);
|
||||
}
|
||||
else if (STR_ELEM(attr->name, "flame", "temperature")) {
|
||||
sub->bind_texture(attr->input_name, fds->tex_flame ? &fds->tex_flame : &module.dummy_zero);
|
||||
}
|
||||
else {
|
||||
sub->bind_texture(attr->input_name, module.grid_default_texture(attr->default_value));
|
||||
}
|
||||
volume_infos.grids_xform[grid_id++] = orco_mat;
|
||||
}
|
||||
|
||||
bool use_constant_color = ((fds->active_fields & FLUID_DOMAIN_ACTIVE_COLORS) == 0 &&
|
||||
(fds->active_fields & FLUID_DOMAIN_ACTIVE_COLOR_SET) != 0);
|
||||
if (use_constant_color) {
|
||||
volume_infos.color_mul = float4(UNPACK3(fds->active_color), 1.0f);
|
||||
}
|
||||
|
||||
/* Output is such that 0..1 maps to 0..1000K. */
|
||||
volume_infos.temperature_mul = fds->flame_max_temp - fds->flame_ignition;
|
||||
volume_infos.temperature_bias = fds->flame_ignition;
|
||||
}
|
||||
|
||||
if (sub) {
|
||||
volume_infos.push_update();
|
||||
sub->bind_ubo("drw_volume", volume_infos);
|
||||
}
|
||||
|
||||
return sub;
|
||||
}
|
||||
|
||||
template<typename PassType>
|
||||
PassType *volume_sub_pass_implementation(PassType &ps,
|
||||
Scene *scene,
|
||||
Object *ob,
|
||||
GPUMaterial *gpu_material)
|
||||
{
|
||||
ListBaseT<GPUMaterialAttribute> attr_list = GPU_material_attributes(gpu_material);
|
||||
ListBaseWrapper<GPUMaterialAttribute> attrs(attr_list);
|
||||
if (ob == nullptr) {
|
||||
return volume_world_grids_init(ps, attrs);
|
||||
}
|
||||
if (ob->type == OB_VOLUME) {
|
||||
return volume_object_grids_init(ps, ob, attrs);
|
||||
}
|
||||
return drw_volume_object_mesh_init(ps, scene, ob, attrs);
|
||||
}
|
||||
|
||||
PassMain::Sub *volume_sub_pass(PassMain::Sub &ps,
|
||||
Scene *scene,
|
||||
Object *ob,
|
||||
GPUMaterial *gpu_material)
|
||||
{
|
||||
return volume_sub_pass_implementation(ps, scene, ob, gpu_material);
|
||||
}
|
||||
|
||||
PassSimple::Sub *volume_sub_pass(PassSimple::Sub &ps,
|
||||
Scene *scene,
|
||||
Object *ob,
|
||||
GPUMaterial *gpu_material)
|
||||
{
|
||||
return volume_sub_pass_implementation(ps, scene, ob, gpu_material);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::draw
|
||||
@@ -0,0 +1,87 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*
|
||||
* \brief Extraction of Mesh data into VBO to feed to GPU.
|
||||
*/
|
||||
|
||||
#include "DNA_meshdata_types.h"
|
||||
|
||||
#include "ED_uvedit.hh"
|
||||
|
||||
#include "extract_mesh.hh"
|
||||
|
||||
#include "draw_cache_impl.hh"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name Extract Edit Flag Utils
|
||||
* \{ */
|
||||
|
||||
void mesh_render_data_face_flag(const MeshRenderData &mr,
|
||||
const BMFace *efa,
|
||||
const BMUVOffsets &offsets,
|
||||
EditLoopData &eattr)
|
||||
{
|
||||
if (efa == mr.efa_act) {
|
||||
eattr.v_flag |= VFLAG_FACE_ACTIVE;
|
||||
}
|
||||
if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
|
||||
eattr.v_flag |= VFLAG_FACE_SELECTED;
|
||||
}
|
||||
|
||||
if (efa == mr.efa_act_uv) {
|
||||
eattr.v_flag |= VFLAG_FACE_UV_ACTIVE;
|
||||
}
|
||||
if ((offsets.uv != -1) && (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) &&
|
||||
uvedit_face_select_test_ex(mr.toolsettings, mr.bm, efa))
|
||||
{
|
||||
eattr.v_flag |= VFLAG_FACE_UV_SELECT;
|
||||
}
|
||||
|
||||
#ifdef WITH_FREESTYLE
|
||||
if (mr.freestyle_face_ofs != -1) {
|
||||
if (BM_ELEM_CD_GET_BOOL(efa, mr.freestyle_face_ofs)) {
|
||||
eattr.v_flag |= VFLAG_FACE_FREESTYLE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void mesh_render_data_loop_flag(const MeshRenderData &mr,
|
||||
const BMLoop *l,
|
||||
const BMUVOffsets &offsets,
|
||||
EditLoopData &eattr)
|
||||
{
|
||||
if (offsets.uv == -1) {
|
||||
return;
|
||||
}
|
||||
if (BM_ELEM_CD_GET_BOOL(l, offsets.pin)) {
|
||||
eattr.v_flag |= VFLAG_VERT_UV_PINNED;
|
||||
}
|
||||
if (uvedit_uv_select_test_ex(mr.toolsettings, mr.bm, l, offsets)) {
|
||||
eattr.v_flag |= VFLAG_VERT_UV_SELECT;
|
||||
}
|
||||
}
|
||||
|
||||
void mesh_render_data_loop_edge_flag(const MeshRenderData &mr,
|
||||
const BMLoop *l,
|
||||
const BMUVOffsets &offsets,
|
||||
EditLoopData &eattr)
|
||||
{
|
||||
if (offsets.uv == -1) {
|
||||
return;
|
||||
}
|
||||
if (uvedit_edge_select_test_ex(mr.toolsettings, mr.bm, l, offsets)) {
|
||||
eattr.v_flag |= VFLAG_EDGE_UV_SELECT;
|
||||
eattr.v_flag |= VFLAG_VERT_UV_SELECT;
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::draw
|
||||
@@ -0,0 +1,401 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*
|
||||
* \brief Extraction of Mesh data into VBO to feed to GPU.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_math_matrix_types.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_task.hh"
|
||||
#include "BLI_virtual_array.hh"
|
||||
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "BKE_mesh.hh"
|
||||
#include "BKE_object.hh"
|
||||
|
||||
#include "bmesh.hh"
|
||||
|
||||
#include "GPU_vertex_buffer.hh"
|
||||
|
||||
#include "draw_cache_extract.hh"
|
||||
|
||||
struct DRWSubdivCache;
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct BMVert;
|
||||
struct BMEdge;
|
||||
struct BMEditMesh;
|
||||
struct BMFace;
|
||||
struct BMLoop;
|
||||
|
||||
namespace draw {
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name Mesh Render Data
|
||||
* \{ */
|
||||
|
||||
enum class MeshExtractType {
|
||||
BMesh,
|
||||
Mesh,
|
||||
};
|
||||
|
||||
struct MeshRenderData {
|
||||
MeshExtractType extract_type;
|
||||
|
||||
int verts_num;
|
||||
int edges_num;
|
||||
int faces_num;
|
||||
int corners_num;
|
||||
|
||||
int loose_edges_num;
|
||||
int loose_verts_num;
|
||||
int loose_indices_num;
|
||||
|
||||
int corner_tris_num;
|
||||
int materials_num;
|
||||
|
||||
bool use_hide;
|
||||
bool use_subsurf_fdots;
|
||||
bool hide_unmapped_edges;
|
||||
bool use_simplify_normals;
|
||||
|
||||
const ToolSettings *toolsettings;
|
||||
/** Edit Mesh */
|
||||
BMEditMesh *edit_bmesh;
|
||||
BMesh *bm;
|
||||
bke::EditMeshData *edit_data;
|
||||
|
||||
/* For deformed edit-mesh data. */
|
||||
/* Use for #ME_WRAPPER_TYPE_BMESH. */
|
||||
Span<float3> bm_vert_coords;
|
||||
Span<float3> bm_vert_normals;
|
||||
Span<float3> bm_face_normals;
|
||||
Array<float3> bm_loop_normals;
|
||||
int bm_free_normal_offset_vert = -1;
|
||||
int bm_free_normal_offset_face = -1;
|
||||
int bm_free_normal_offset_corner = -1;
|
||||
|
||||
const int *orig_index_vert;
|
||||
const int *orig_index_edge;
|
||||
const int *orig_index_face;
|
||||
int edge_crease_ofs;
|
||||
int vert_crease_ofs;
|
||||
int bweight_ofs;
|
||||
int freestyle_edge_ofs;
|
||||
int freestyle_face_ofs;
|
||||
/** Mesh */
|
||||
const Mesh *mesh;
|
||||
Span<float3> vert_positions;
|
||||
Span<int2> edges;
|
||||
OffsetIndices<int> faces;
|
||||
Span<int> corner_verts;
|
||||
Span<int> corner_edges;
|
||||
|
||||
BMVert *eve_act;
|
||||
BMEdge *eed_act;
|
||||
BMFace *efa_act;
|
||||
BMFace *efa_act_uv;
|
||||
VArraySpan<int> material_indices;
|
||||
|
||||
bke::MeshNormalDomain normals_domain;
|
||||
Span<float3> face_normals;
|
||||
Span<float3> corner_normals;
|
||||
|
||||
VArraySpan<bool> hide_vert;
|
||||
VArraySpan<bool> hide_edge;
|
||||
VArraySpan<bool> hide_poly;
|
||||
VArraySpan<bool> select_vert;
|
||||
VArraySpan<bool> select_edge;
|
||||
VArraySpan<bool> select_poly;
|
||||
VArraySpan<bool> sharp_faces;
|
||||
|
||||
IndexMask loose_verts;
|
||||
IndexMask loose_edges;
|
||||
|
||||
const char *active_color_name;
|
||||
const char *default_color_name;
|
||||
};
|
||||
|
||||
inline const Mesh &editmesh_final_or_this(const Object &object, const Mesh &mesh)
|
||||
{
|
||||
if (mesh.runtime->edit_mesh != nullptr) {
|
||||
if (const Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(&object)) {
|
||||
return *editmesh_eval_final;
|
||||
}
|
||||
}
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
BLI_INLINE BMFace *bm_original_face_get(const MeshRenderData &mr, int idx)
|
||||
{
|
||||
return ((mr.orig_index_face != nullptr) && (mr.orig_index_face[idx] != ORIGINDEX_NONE) &&
|
||||
mr.bm) ?
|
||||
BM_face_at_index(mr.bm, mr.orig_index_face[idx]) :
|
||||
nullptr;
|
||||
}
|
||||
|
||||
BLI_INLINE BMEdge *bm_original_edge_get(const MeshRenderData &mr, int idx)
|
||||
{
|
||||
return ((mr.orig_index_edge != nullptr) && (mr.orig_index_edge[idx] != ORIGINDEX_NONE) &&
|
||||
mr.bm) ?
|
||||
BM_edge_at_index(mr.bm, mr.orig_index_edge[idx]) :
|
||||
nullptr;
|
||||
}
|
||||
|
||||
BLI_INLINE BMVert *bm_original_vert_get(const MeshRenderData &mr, int idx)
|
||||
{
|
||||
return ((mr.orig_index_vert != nullptr) && (mr.orig_index_vert[idx] != ORIGINDEX_NONE) &&
|
||||
mr.bm) ?
|
||||
BM_vert_at_index(mr.bm, mr.orig_index_vert[idx]) :
|
||||
nullptr;
|
||||
}
|
||||
|
||||
BLI_INLINE const float *bm_vert_co_get(const MeshRenderData &mr, const BMVert *eve)
|
||||
{
|
||||
if (!mr.bm_vert_coords.is_empty()) {
|
||||
return mr.bm_vert_coords[BM_elem_index_get(eve)];
|
||||
}
|
||||
return eve->co;
|
||||
}
|
||||
|
||||
BLI_INLINE const float *bm_vert_no_get(const MeshRenderData &mr, const BMVert *eve)
|
||||
{
|
||||
if (mr.bm_free_normal_offset_vert != -1) {
|
||||
return BM_ELEM_CD_GET_FLOAT_P(eve, mr.bm_free_normal_offset_vert);
|
||||
}
|
||||
if (!mr.bm_vert_normals.is_empty()) {
|
||||
return mr.bm_vert_normals[BM_elem_index_get(eve)];
|
||||
}
|
||||
return eve->no;
|
||||
}
|
||||
|
||||
BLI_INLINE const float *bm_face_no_get(const MeshRenderData &mr, const BMFace *efa)
|
||||
{
|
||||
if (mr.bm_free_normal_offset_face != -1) {
|
||||
return BM_ELEM_CD_GET_FLOAT_P(efa, mr.bm_free_normal_offset_face);
|
||||
}
|
||||
if (!mr.bm_face_normals.is_empty()) {
|
||||
return mr.bm_face_normals[BM_elem_index_get(efa)];
|
||||
}
|
||||
return efa->no;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* `draw_cache_extract_mesh_render_data.cc` */
|
||||
|
||||
/**
|
||||
* \param is_editmode: When true, use the modifiers from the edit-data,
|
||||
* otherwise don't use modifiers as they are not from this object.
|
||||
*/
|
||||
MeshRenderData mesh_render_data_create(Object &object,
|
||||
Mesh &mesh,
|
||||
bool is_editmode,
|
||||
bool is_paint_mode,
|
||||
bool do_final,
|
||||
bool do_uvedit,
|
||||
bool use_hide,
|
||||
const ToolSettings *ts);
|
||||
void mesh_render_data_update_corner_normals(MeshRenderData &mr);
|
||||
void mesh_render_data_update_face_normals(MeshRenderData &mr);
|
||||
void mesh_render_data_update_loose_geom(MeshRenderData &mr, MeshBufferCache &cache);
|
||||
const SortedFaceData &mesh_render_data_faces_sorted_ensure(const MeshRenderData &mr,
|
||||
MeshBufferCache &cache);
|
||||
|
||||
/* draw_cache_extract_mesh_extractors.c */
|
||||
|
||||
struct EditLoopData {
|
||||
uchar v_flag;
|
||||
uchar e_flag;
|
||||
/* This is used for both vertex and edge creases. The edge crease value is stored in the bottom 4
|
||||
* bits, while the vertex crease is stored in the upper 4 bits. */
|
||||
uchar crease;
|
||||
uchar bweight;
|
||||
};
|
||||
|
||||
void mesh_render_data_face_flag(const MeshRenderData &mr,
|
||||
const BMFace *efa,
|
||||
const BMUVOffsets &offsets,
|
||||
EditLoopData &eattr);
|
||||
void mesh_render_data_loop_flag(const MeshRenderData &mr,
|
||||
const BMLoop *l,
|
||||
const BMUVOffsets &offsets,
|
||||
EditLoopData &eattr);
|
||||
void mesh_render_data_loop_edge_flag(const MeshRenderData &mr,
|
||||
const BMLoop *l,
|
||||
const BMUVOffsets &offsets,
|
||||
EditLoopData &eattr);
|
||||
|
||||
/* In the GPU vertex buffers, the value for each vertex is duplicated to each of its vertex
|
||||
* corners. So the edges on the GPU connect face corners rather than vertices. */
|
||||
inline uint2 edge_from_corners(const IndexRange face, const int corner)
|
||||
{
|
||||
const int corner_next = bke::mesh::face_corner_next(face, corner);
|
||||
return uint2(corner, corner_next);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void extract_mesh_loose_edge_data(const Span<T> vert_data,
|
||||
const Span<int2> edges,
|
||||
const IndexMask &loose_edges,
|
||||
MutableSpan<T> gpu_data)
|
||||
{
|
||||
loose_edges.foreach_index_optimized<int>(
|
||||
[&](const int i, const int pos) {
|
||||
const int2 edge = edges[i];
|
||||
gpu_data[pos * 2 + 0] = vert_data[edge[0]];
|
||||
gpu_data[pos * 2 + 1] = vert_data[edge[1]];
|
||||
},
|
||||
exec_mode::grain_size(4096));
|
||||
}
|
||||
|
||||
gpu::VertBufPtr extract_positions(const MeshRenderData &mr);
|
||||
gpu::VertBufPtr extract_positions_subdiv(const DRWSubdivCache &subdiv_cache,
|
||||
const MeshRenderData &mr,
|
||||
gpu::VertBufPtr *orco_vbo);
|
||||
|
||||
gpu::VertBufPtr extract_face_dots_position(const MeshRenderData &mr);
|
||||
void extract_face_dots_subdiv(const DRWSubdivCache &subdiv_cache,
|
||||
gpu::VertBufPtr &fdots_pos,
|
||||
gpu::VertBufPtr *fdots_nor,
|
||||
gpu::IndexBufPtr &fdots);
|
||||
|
||||
gpu::VertBufPtr extract_normals(const MeshRenderData &mr, bool use_hq);
|
||||
gpu::VertBufPtr extract_normals_subdiv(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache,
|
||||
gpu::VertBuf &pos);
|
||||
gpu::VertBufPtr extract_vert_normals(const MeshRenderData &mr);
|
||||
gpu::VertBufPtr extract_face_dot_normals(const MeshRenderData &mr, bool use_hq);
|
||||
gpu::VertBufPtr extract_edge_factor(const MeshRenderData &mr);
|
||||
gpu::VertBufPtr extract_edge_factor_subdiv(const DRWSubdivCache &subdiv_cache,
|
||||
const MeshRenderData &mr,
|
||||
gpu::VertBuf &pos);
|
||||
|
||||
gpu::IndexBufPtr extract_tris(const MeshRenderData &mr, const SortedFaceData &face_sorted);
|
||||
void create_material_subranges(const SortedFaceData &face_sorted,
|
||||
gpu::IndexBuf &tris_ibo,
|
||||
MutableSpan<gpu::IndexBufPtr> ibos);
|
||||
gpu::IndexBufPtr extract_tris_subdiv(const DRWSubdivCache &subdiv_cache, MeshBatchCache &cache);
|
||||
|
||||
void extract_lines(const MeshRenderData &mr,
|
||||
gpu::IndexBufPtr *lines,
|
||||
gpu::IndexBufPtr *lines_loose,
|
||||
bool &no_loose_wire);
|
||||
void extract_lines_subdiv(const DRWSubdivCache &subdiv_cache,
|
||||
const MeshRenderData &mr,
|
||||
gpu::IndexBufPtr *lines,
|
||||
gpu::IndexBufPtr *lines_loose,
|
||||
bool &no_loose_wire);
|
||||
|
||||
gpu::IndexBufPtr extract_points(const MeshRenderData &mr);
|
||||
gpu::IndexBufPtr extract_points_subdiv(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache);
|
||||
|
||||
gpu::VertBufPtr extract_edit_data(const MeshRenderData &mr);
|
||||
gpu::VertBufPtr extract_edit_data_subdiv(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache);
|
||||
|
||||
gpu::VertBufPtr extract_tangents(const MeshRenderData &mr,
|
||||
const MeshBatchCache &cache,
|
||||
bool use_hq);
|
||||
gpu::VertBufPtr extract_tangents_subdiv(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache,
|
||||
const MeshBatchCache &cache);
|
||||
|
||||
gpu::VertBufPtr extract_vert_index(const MeshRenderData &mr);
|
||||
gpu::VertBufPtr extract_edge_index(const MeshRenderData &mr);
|
||||
gpu::VertBufPtr extract_face_index(const MeshRenderData &mr);
|
||||
gpu::VertBufPtr extract_face_dot_index(const MeshRenderData &mr);
|
||||
|
||||
gpu::VertBufPtr extract_vert_index_subdiv(const DRWSubdivCache &subdiv_cache,
|
||||
const MeshRenderData &mr);
|
||||
gpu::VertBufPtr extract_edge_index_subdiv(const DRWSubdivCache &subdiv_cache,
|
||||
const MeshRenderData &mr);
|
||||
gpu::VertBufPtr extract_face_index_subdiv(const DRWSubdivCache &subdiv_cache,
|
||||
const MeshRenderData &mr);
|
||||
|
||||
gpu::VertBufPtr extract_weights(const MeshRenderData &mr, const MeshBatchCache &cache);
|
||||
gpu::VertBufPtr extract_weights_subdiv(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache,
|
||||
const MeshBatchCache &cache);
|
||||
|
||||
gpu::IndexBufPtr extract_face_dots(const MeshRenderData &mr);
|
||||
|
||||
gpu::VertBufPtr extract_face_dots_uv(const MeshRenderData &mr);
|
||||
gpu::VertBufPtr extract_face_dots_edituv_data(const MeshRenderData &mr);
|
||||
|
||||
gpu::IndexBufPtr extract_lines_paint_mask(const MeshRenderData &mr);
|
||||
gpu::IndexBufPtr extract_lines_paint_mask_subdiv(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache);
|
||||
|
||||
gpu::IndexBufPtr extract_lines_adjacency(const MeshRenderData &mr, bool &r_is_manifold);
|
||||
gpu::IndexBufPtr extract_lines_adjacency_subdiv(const DRWSubdivCache &subdiv_cache,
|
||||
bool &r_is_manifold);
|
||||
|
||||
gpu::VertBufPtr extract_uv_maps(const MeshRenderData &mr, const MeshBatchCache &cache);
|
||||
gpu::VertBufPtr extract_uv_maps_subdiv(const DRWSubdivCache &subdiv_cache,
|
||||
const MeshBatchCache &cache);
|
||||
gpu::VertBufPtr extract_edituv_stretch_area(const MeshRenderData &mr,
|
||||
float &tot_area,
|
||||
float &tot_uv_area);
|
||||
gpu::VertBufPtr extract_edituv_stretch_area_subdiv(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache,
|
||||
float &tot_area,
|
||||
float &tot_uv_area);
|
||||
gpu::VertBufPtr extract_edituv_stretch_angle(const MeshRenderData &mr);
|
||||
gpu::VertBufPtr extract_edituv_stretch_angle_subdiv(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache,
|
||||
const MeshBatchCache &cache);
|
||||
gpu::VertBufPtr extract_edituv_data(const MeshRenderData &mr);
|
||||
gpu::VertBufPtr extract_edituv_data_subdiv(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache);
|
||||
gpu::IndexBufPtr extract_edituv_tris(const MeshRenderData &mr, bool edit_uvs);
|
||||
gpu::IndexBufPtr extract_edituv_tris_subdiv(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache);
|
||||
|
||||
enum class UvExtractionMode : int8_t {
|
||||
Edit,
|
||||
Selection,
|
||||
All,
|
||||
};
|
||||
|
||||
gpu::IndexBufPtr extract_edituv_lines(const MeshRenderData &mr, UvExtractionMode mode);
|
||||
gpu::IndexBufPtr extract_edituv_lines_subdiv(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache,
|
||||
UvExtractionMode mode);
|
||||
gpu::IndexBufPtr extract_edituv_points(const MeshRenderData &mr);
|
||||
gpu::IndexBufPtr extract_edituv_points_subdiv(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache);
|
||||
gpu::IndexBufPtr extract_edituv_face_dots(const MeshRenderData &mr);
|
||||
|
||||
gpu::VertBufPtr extract_mesh_analysis(const MeshRenderData &mr, const float4x4 &object_to_world);
|
||||
|
||||
gpu::VertBufPtr extract_skin_roots(const MeshRenderData &mr);
|
||||
|
||||
gpu::VertBufPtr extract_sculpt_data(const MeshRenderData &mr);
|
||||
gpu::VertBufPtr extract_sculpt_data_subdiv(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache);
|
||||
|
||||
gpu::VertBufPtr extract_orco(const MeshRenderData &mr);
|
||||
|
||||
gpu::VertBufPtr extract_attribute(const MeshRenderData &mr, StringRef name);
|
||||
gpu::VertBufPtr extract_attribute_subdiv(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache,
|
||||
StringRef name);
|
||||
gpu::VertBufPtr extract_attr_viewer(const MeshRenderData &mr);
|
||||
|
||||
gpu::VertBufPtr extract_paint_overlay_flags(const MeshRenderData &mr);
|
||||
gpu::VertBufPtr extract_paint_overlay_flags_subdiv(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache);
|
||||
|
||||
} // namespace draw
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,703 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "BKE_editmesh.hh"
|
||||
|
||||
#include "BLI_math_geom.h"
|
||||
|
||||
#include "extract_mesh.hh"
|
||||
|
||||
#include "GPU_index_buffer.hh"
|
||||
|
||||
#include "draw_subdivision.hh"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name Extract Edit UV Triangles Indices
|
||||
* \{ */
|
||||
|
||||
inline bool skip_bm_face(const BMFace &face, const bool sync_selection)
|
||||
{
|
||||
if (BM_elem_flag_test(&face, BM_ELEM_HIDDEN)) {
|
||||
return true;
|
||||
}
|
||||
if (!sync_selection) {
|
||||
if (!BM_elem_flag_test_bool(&face, BM_ELEM_SELECT)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static OffsetIndices<int> build_bmesh_face_offsets(const BMesh &bm, Array<int> &r_offset_data)
|
||||
{
|
||||
r_offset_data.reinitialize(bm.totface + 1);
|
||||
threading::parallel_for(IndexRange(bm.totface), 4096, [&](const IndexRange range) {
|
||||
for (const int face : range) {
|
||||
r_offset_data[face] = BM_face_at_index(&const_cast<BMesh &>(bm), face)->len;
|
||||
}
|
||||
});
|
||||
return offset_indices::accumulate_counts_to_offsets(r_offset_data);
|
||||
}
|
||||
|
||||
static gpu::IndexBufPtr extract_edituv_tris_bm(const MeshRenderData &mr, const bool sync_selection)
|
||||
{
|
||||
const Span<std::array<BMLoop *, 3>> looptris = mr.edit_bmesh->looptris;
|
||||
const BMesh &bm = *mr.bm;
|
||||
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask selection = IndexMask::from_predicate(
|
||||
IndexRange(bm.totface), memory, [&](const int face) {
|
||||
return !skip_bm_face(*BM_face_at_index(&const_cast<BMesh &>(bm), face), sync_selection);
|
||||
});
|
||||
|
||||
if (selection.size() == bm.totface) {
|
||||
GPUIndexBufBuilder builder;
|
||||
GPU_indexbuf_init(&builder, GPU_PRIM_TRIS, looptris.size(), mr.corners_num);
|
||||
MutableSpan<uint3> data = GPU_indexbuf_get_data(&builder).cast<uint3>();
|
||||
threading::parallel_for(looptris.index_range(), 4096, [&](const IndexRange range) {
|
||||
for (const int i : range) {
|
||||
data[i] = uint3(BM_elem_index_get(looptris[i][0]),
|
||||
BM_elem_index_get(looptris[i][1]),
|
||||
BM_elem_index_get(looptris[i][2]));
|
||||
}
|
||||
});
|
||||
return gpu::IndexBufPtr(GPU_indexbuf_build_ex(&builder, 0, mr.corners_num, false));
|
||||
}
|
||||
|
||||
Array<int> face_offset_data;
|
||||
const OffsetIndices faces = build_bmesh_face_offsets(bm, face_offset_data);
|
||||
|
||||
Array<int> selected_face_offset_data(selection.size() + 1);
|
||||
const OffsetIndices selected_faces = offset_indices::gather_selected_offsets(
|
||||
faces, selection, selected_face_offset_data);
|
||||
|
||||
const int tris_num = poly_to_tri_count(selected_faces.size(), selected_faces.total_size());
|
||||
|
||||
GPUIndexBufBuilder builder;
|
||||
GPU_indexbuf_init(&builder, GPU_PRIM_TRIS, tris_num, mr.corners_num);
|
||||
MutableSpan<uint3> data = GPU_indexbuf_get_data(&builder).cast<uint3>();
|
||||
|
||||
selection.foreach_index(
|
||||
[&](const int face, const int mask) {
|
||||
const IndexRange tris = bke::mesh::face_triangles_range(faces, face);
|
||||
const IndexRange ibo_tris = bke::mesh::face_triangles_range(selected_faces, mask);
|
||||
for (const int i : tris.index_range()) {
|
||||
data[ibo_tris[i]] = uint3(BM_elem_index_get(looptris[tris[i]][0]),
|
||||
BM_elem_index_get(looptris[tris[i]][1]),
|
||||
BM_elem_index_get(looptris[tris[i]][2]));
|
||||
}
|
||||
},
|
||||
exec_mode::grain_size(4096));
|
||||
|
||||
return gpu::IndexBufPtr(GPU_indexbuf_build_ex(&builder, 0, mr.corners_num, false));
|
||||
}
|
||||
|
||||
static gpu::IndexBufPtr extract_edituv_tris_mesh(const MeshRenderData &mr,
|
||||
const bool sync_selection)
|
||||
{
|
||||
const OffsetIndices faces = mr.faces;
|
||||
const Span<int3> corner_tris = mr.mesh->corner_tris();
|
||||
|
||||
IndexMaskMemory memory;
|
||||
IndexMask selection;
|
||||
if (mr.bm) {
|
||||
selection = IndexMask::from_predicate(faces.index_range(), memory, [&](const int face) {
|
||||
const BMFace *face_orig = bm_original_face_get(mr, face);
|
||||
if (!face_orig) {
|
||||
return false;
|
||||
}
|
||||
if (skip_bm_face(*face_orig, sync_selection)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
else {
|
||||
if (mr.hide_poly.is_empty()) {
|
||||
selection = faces.index_range();
|
||||
}
|
||||
else {
|
||||
selection = IndexMask::from_bools_inverse(faces.index_range(), mr.hide_poly, memory);
|
||||
}
|
||||
|
||||
if (!sync_selection && !mr.select_poly.is_empty()) {
|
||||
selection = IndexMask::from_bools(selection, mr.select_poly, memory);
|
||||
}
|
||||
}
|
||||
|
||||
if (selection.size() == faces.size()) {
|
||||
return gpu::IndexBufPtr(GPU_indexbuf_build_from_memory(GPU_PRIM_TRIS,
|
||||
corner_tris.cast<uint32_t>().data(),
|
||||
corner_tris.size(),
|
||||
0,
|
||||
mr.corners_num,
|
||||
false));
|
||||
}
|
||||
|
||||
Array<int> selected_face_offset_data(selection.size() + 1);
|
||||
const OffsetIndices selected_faces = offset_indices::gather_selected_offsets(
|
||||
faces, selection, selected_face_offset_data);
|
||||
|
||||
const int tris_num = poly_to_tri_count(selected_faces.size(), selected_faces.total_size());
|
||||
|
||||
GPUIndexBufBuilder builder;
|
||||
GPU_indexbuf_init(&builder, GPU_PRIM_TRIS, tris_num, mr.corners_num);
|
||||
MutableSpan<uint3> data = GPU_indexbuf_get_data(&builder).cast<uint3>();
|
||||
|
||||
selection.foreach_index(
|
||||
[&](const int face, const int mask) {
|
||||
const IndexRange tris = bke::mesh::face_triangles_range(faces, face);
|
||||
const IndexRange ibo_tris = bke::mesh::face_triangles_range(selected_faces, mask);
|
||||
for (const int i : tris.index_range()) {
|
||||
data[ibo_tris[i]] = uint3(
|
||||
corner_tris[tris[i]][0], corner_tris[tris[i]][1], corner_tris[tris[i]][2]);
|
||||
}
|
||||
},
|
||||
exec_mode::grain_size(4096));
|
||||
|
||||
return gpu::IndexBufPtr(GPU_indexbuf_build_ex(&builder, 0, mr.corners_num, false));
|
||||
}
|
||||
|
||||
gpu::IndexBufPtr extract_edituv_tris(const MeshRenderData &mr, const bool edit_uvs)
|
||||
{
|
||||
const bool sync_selection = edit_uvs ? (mr.toolsettings->uv_flag & UV_FLAG_SELECT_SYNC) != 0 :
|
||||
false;
|
||||
if (mr.extract_type == MeshExtractType::BMesh) {
|
||||
return extract_edituv_tris_bm(mr, sync_selection);
|
||||
}
|
||||
return extract_edituv_tris_mesh(mr, sync_selection);
|
||||
}
|
||||
|
||||
static gpu::IndexBufPtr build_tris_from_subdiv_quad_selection(const DRWSubdivCache &subdiv_cache,
|
||||
const IndexMask &selection)
|
||||
{
|
||||
const int tris_num = selection.size() * 2;
|
||||
|
||||
GPUIndexBufBuilder builder;
|
||||
GPU_indexbuf_init(&builder, GPU_PRIM_TRIS, tris_num, subdiv_cache.num_subdiv_loops);
|
||||
MutableSpan<uint3> data = GPU_indexbuf_get_data(&builder).cast<uint3>();
|
||||
|
||||
selection.foreach_index(
|
||||
[&](const int subdiv_quad_index, const int mask) {
|
||||
const uint corner_start = subdiv_quad_index * 4;
|
||||
data[mask * 2 + 0] = uint3(corner_start, corner_start + 1, corner_start + 2);
|
||||
data[mask * 2 + 1] = uint3(corner_start, corner_start + 2, corner_start + 3);
|
||||
},
|
||||
exec_mode::grain_size(4096));
|
||||
|
||||
return gpu::IndexBufPtr(
|
||||
GPU_indexbuf_build_ex(&builder, 0, subdiv_cache.num_subdiv_loops, false));
|
||||
}
|
||||
|
||||
static gpu::IndexBufPtr extract_edituv_tris_subdiv_bm(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache,
|
||||
const bool sync_selection)
|
||||
{
|
||||
const BMesh &bm = *mr.bm;
|
||||
const Span<int> subdiv_loop_face_index(subdiv_cache.subdiv_loop_face_index,
|
||||
subdiv_cache.num_subdiv_loops);
|
||||
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask selection = IndexMask::from_predicate(
|
||||
IndexRange(subdiv_cache.num_subdiv_quads), memory, [&](const int subdiv_quad_index) {
|
||||
const uint corner_start = subdiv_quad_index * 4;
|
||||
const int coarse_face = subdiv_loop_face_index[corner_start];
|
||||
const BMFace &bm_face = *BM_face_at_index(&const_cast<BMesh &>(bm), coarse_face);
|
||||
return !skip_bm_face(bm_face, sync_selection);
|
||||
});
|
||||
|
||||
return build_tris_from_subdiv_quad_selection(subdiv_cache, selection);
|
||||
}
|
||||
|
||||
static gpu::IndexBufPtr extract_edituv_tris_subdiv_mesh(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache,
|
||||
const bool sync_selection)
|
||||
{
|
||||
const Span<int> subdiv_loop_face_index(subdiv_cache.subdiv_loop_face_index,
|
||||
subdiv_cache.num_subdiv_loops);
|
||||
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask selection = IndexMask::from_predicate(
|
||||
IndexRange(subdiv_cache.num_subdiv_quads), memory, [&](const int subdiv_quad_index) {
|
||||
const uint corner_start = subdiv_quad_index * 4;
|
||||
const int coarse_face = subdiv_loop_face_index[corner_start];
|
||||
const BMFace *face_orig = bm_original_face_get(mr, coarse_face);
|
||||
if (!face_orig) {
|
||||
return false;
|
||||
}
|
||||
if (skip_bm_face(*face_orig, sync_selection)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
return build_tris_from_subdiv_quad_selection(subdiv_cache, selection);
|
||||
}
|
||||
|
||||
gpu::IndexBufPtr extract_edituv_tris_subdiv(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache)
|
||||
{
|
||||
const bool sync_selection = (mr.toolsettings->uv_flag & UV_FLAG_SELECT_SYNC) != 0;
|
||||
if (mr.extract_type == MeshExtractType::BMesh) {
|
||||
return extract_edituv_tris_subdiv_bm(mr, subdiv_cache, sync_selection);
|
||||
}
|
||||
return extract_edituv_tris_subdiv_mesh(mr, subdiv_cache, sync_selection);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name Extract Edit UV Line Indices around faces
|
||||
* \{ */
|
||||
|
||||
static gpu::IndexBufPtr extract_edituv_lines_bm(const MeshRenderData &mr,
|
||||
const bool sync_selection)
|
||||
{
|
||||
GPUIndexBufBuilder builder;
|
||||
GPU_indexbuf_init(&builder, GPU_PRIM_LINES, mr.corners_num, mr.corners_num);
|
||||
MutableSpan<uint2> data = GPU_indexbuf_get_data(&builder).cast<uint2>();
|
||||
int line_index = 0;
|
||||
|
||||
const BMFace *face;
|
||||
BMIter f_iter;
|
||||
BM_ITER_MESH (face, &f_iter, mr.bm, BM_FACES_OF_MESH) {
|
||||
if (skip_bm_face(*face, sync_selection)) {
|
||||
continue;
|
||||
}
|
||||
const BMLoop *loop = BM_FACE_FIRST_LOOP(face);
|
||||
for ([[maybe_unused]] const int i : IndexRange(face->len)) {
|
||||
data[line_index++] = uint2(BM_elem_index_get(loop), BM_elem_index_get(loop->next));
|
||||
loop = loop->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* Only upload the part of the index buffer that is used. Alternatively it might be beneficial to
|
||||
* count the number of visible edges first, especially if that allows parallelizing filling the
|
||||
* data array. */
|
||||
builder.index_len = line_index * 2;
|
||||
builder.index_min = 0;
|
||||
builder.index_max = mr.corners_num;
|
||||
builder.uses_restart_indices = false;
|
||||
gpu::IndexBufPtr result = gpu::IndexBufPtr(GPU_indexbuf_calloc());
|
||||
GPU_indexbuf_build_in_place(&builder, result.get());
|
||||
return result;
|
||||
}
|
||||
|
||||
static gpu::IndexBufPtr extract_edituv_lines_mesh(const MeshRenderData &mr,
|
||||
const bool sync_selection)
|
||||
{
|
||||
const OffsetIndices faces = mr.faces;
|
||||
const Span<int> corner_edges = mr.corner_edges;
|
||||
const Span<int> orig_index_edge = mr.orig_index_edge ?
|
||||
Span<int>(mr.orig_index_edge, mr.edges_num) :
|
||||
Span<int>();
|
||||
|
||||
GPUIndexBufBuilder builder;
|
||||
GPU_indexbuf_init(&builder, GPU_PRIM_LINES, mr.corners_num, mr.corners_num);
|
||||
MutableSpan<uint2> data = GPU_indexbuf_get_data(&builder).cast<uint2>();
|
||||
int line_index = 0;
|
||||
|
||||
if (mr.bm) {
|
||||
for (const int face_index : faces.index_range()) {
|
||||
const IndexRange face = faces[face_index];
|
||||
const BMFace *face_orig = bm_original_face_get(mr, face_index);
|
||||
if (!face_orig) {
|
||||
continue;
|
||||
}
|
||||
if (skip_bm_face(*face_orig, sync_selection)) {
|
||||
continue;
|
||||
}
|
||||
for (const int corner : face) {
|
||||
const int edge = corner_edges[corner];
|
||||
if (!orig_index_edge.is_empty() && orig_index_edge[edge] == ORIGINDEX_NONE) {
|
||||
continue;
|
||||
}
|
||||
data[line_index++] = edge_from_corners(face, corner);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
IndexMaskMemory memory;
|
||||
IndexMask visible = faces.index_range();
|
||||
if (!mr.hide_poly.is_empty()) {
|
||||
visible = IndexMask::from_bools_inverse(visible, mr.hide_poly, memory);
|
||||
}
|
||||
if (!sync_selection) {
|
||||
if (mr.select_poly.is_empty()) {
|
||||
visible = {};
|
||||
}
|
||||
else {
|
||||
visible = IndexMask::from_bools(visible, mr.select_poly, memory);
|
||||
}
|
||||
}
|
||||
visible.foreach_index([&](const int face_index) {
|
||||
const IndexRange face = faces[face_index];
|
||||
for (const int corner : face) {
|
||||
const int edge = corner_edges[corner];
|
||||
if (!orig_index_edge.is_empty() && orig_index_edge[edge] == ORIGINDEX_NONE) {
|
||||
continue;
|
||||
}
|
||||
data[line_index++] = edge_from_corners(face, corner);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* Only upload the part of the index buffer that is used. Alternatively it might be beneficial to
|
||||
* count the number of visible edges first, especially if that allows parallelizing filling the
|
||||
* data array. */
|
||||
builder.index_len = line_index * 2;
|
||||
builder.index_min = 0;
|
||||
builder.index_max = mr.corners_num;
|
||||
builder.uses_restart_indices = false;
|
||||
gpu::IndexBufPtr result = gpu::IndexBufPtr(GPU_indexbuf_calloc());
|
||||
GPU_indexbuf_build_in_place(&builder, result.get());
|
||||
return result;
|
||||
}
|
||||
|
||||
gpu::IndexBufPtr extract_edituv_lines(const MeshRenderData &mr, const UvExtractionMode mode)
|
||||
{
|
||||
bool sync_selection = false;
|
||||
switch (mode) {
|
||||
case UvExtractionMode::All:
|
||||
sync_selection = true;
|
||||
break;
|
||||
case UvExtractionMode::Edit:
|
||||
sync_selection = ((mr.toolsettings->uv_flag & UV_FLAG_SELECT_SYNC) != 0);
|
||||
break;
|
||||
case UvExtractionMode::Selection:
|
||||
sync_selection = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (mr.extract_type == MeshExtractType::BMesh) {
|
||||
return extract_edituv_lines_bm(mr, sync_selection);
|
||||
}
|
||||
return extract_edituv_lines_mesh(mr, sync_selection);
|
||||
}
|
||||
|
||||
static gpu::IndexBufPtr extract_edituv_lines_subdiv_bm(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache,
|
||||
const bool sync_selection)
|
||||
{
|
||||
const BMesh &bm = *mr.bm;
|
||||
const Span<int> subdiv_loop_edge_index = subdiv_cache.edges_orig_index->data<int>();
|
||||
const Span<int> subdiv_loop_face_index(subdiv_cache.subdiv_loop_face_index,
|
||||
subdiv_cache.num_subdiv_loops);
|
||||
|
||||
GPUIndexBufBuilder builder;
|
||||
GPU_indexbuf_init(
|
||||
&builder, GPU_PRIM_LINES, subdiv_cache.num_subdiv_loops, subdiv_cache.num_subdiv_loops);
|
||||
MutableSpan<uint2> data = GPU_indexbuf_get_data(&builder).cast<uint2>();
|
||||
int line_index = 0;
|
||||
|
||||
for (const int subdiv_quad : IndexRange(subdiv_cache.num_subdiv_quads)) {
|
||||
const int coarse_face = subdiv_loop_face_index[subdiv_quad * 4];
|
||||
const BMFace &face_orig = *BM_face_at_index(&const_cast<BMesh &>(bm), coarse_face);
|
||||
if (skip_bm_face(face_orig, sync_selection)) {
|
||||
continue;
|
||||
}
|
||||
const IndexRange subdiv_face(subdiv_quad * 4, 4);
|
||||
for (const int subdiv_corner : subdiv_face) {
|
||||
const int coarse_edge = subdiv_loop_edge_index[subdiv_corner];
|
||||
if (coarse_edge == -1) {
|
||||
continue;
|
||||
}
|
||||
data[line_index++] = edge_from_corners(subdiv_face, subdiv_corner);
|
||||
}
|
||||
}
|
||||
|
||||
return gpu::IndexBufPtr(
|
||||
GPU_indexbuf_build_ex(&builder, 0, subdiv_cache.num_subdiv_loops, false));
|
||||
}
|
||||
|
||||
static gpu::IndexBufPtr extract_edituv_lines_subdiv_mesh(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache,
|
||||
const bool sync_selection)
|
||||
{
|
||||
GPUIndexBufBuilder builder;
|
||||
GPU_indexbuf_init(
|
||||
&builder, GPU_PRIM_LINES, subdiv_cache.num_subdiv_loops, subdiv_cache.num_subdiv_loops);
|
||||
MutableSpan<uint2> data = GPU_indexbuf_get_data(&builder).cast<uint2>();
|
||||
int line_index = 0;
|
||||
|
||||
/* NOTE: #subdiv_loop_edge_index already has the #CD_ORIGINDEX layer baked in. */
|
||||
const Span<int> subdiv_loop_edge_index = subdiv_cache.edges_orig_index->data<int>();
|
||||
const Span<int> subdiv_loop_face_index(subdiv_cache.subdiv_loop_face_index,
|
||||
subdiv_cache.num_subdiv_loops);
|
||||
/* TODO: Replace subdiv quad iteration with coarse face iteration. */
|
||||
for (const int subdiv_quad : IndexRange(subdiv_cache.num_subdiv_quads)) {
|
||||
const int coarse_face = subdiv_loop_face_index[subdiv_quad * 4];
|
||||
if (const BMesh *bm = mr.bm) {
|
||||
const int orig_coarse_face = mr.orig_index_face ? mr.orig_index_face[coarse_face] :
|
||||
coarse_face;
|
||||
const BMFace &face_orig = *BM_face_at_index(const_cast<BMesh *>(bm), orig_coarse_face);
|
||||
if (skip_bm_face(face_orig, sync_selection)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!mr.hide_poly.is_empty() && mr.hide_poly[coarse_face]) {
|
||||
continue;
|
||||
}
|
||||
if (!sync_selection) {
|
||||
if (mr.select_poly.is_empty() || !mr.select_poly[coarse_face]) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
const IndexRange subdiv_face(subdiv_quad * 4, 4);
|
||||
for (const int subdiv_corner : subdiv_face) {
|
||||
const int coarse_edge = subdiv_loop_edge_index[subdiv_corner];
|
||||
if (coarse_edge == -1) {
|
||||
continue;
|
||||
}
|
||||
data[line_index++] = edge_from_corners(subdiv_face, subdiv_corner);
|
||||
}
|
||||
}
|
||||
|
||||
/* Only upload the part of the index buffer that is used. Alternatively it might be beneficial to
|
||||
* count the number of visible edges first, especially if that allows parallelizing filling the
|
||||
* data array. */
|
||||
builder.index_len = line_index * 2;
|
||||
builder.index_min = 0;
|
||||
builder.index_max = subdiv_cache.num_subdiv_loops;
|
||||
builder.uses_restart_indices = false;
|
||||
gpu::IndexBufPtr result = gpu::IndexBufPtr(GPU_indexbuf_calloc());
|
||||
GPU_indexbuf_build_in_place(&builder, result.get());
|
||||
return result;
|
||||
}
|
||||
|
||||
gpu::IndexBufPtr extract_edituv_lines_subdiv(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache,
|
||||
const UvExtractionMode mode)
|
||||
{
|
||||
bool sync_selection = false;
|
||||
switch (mode) {
|
||||
case UvExtractionMode::All:
|
||||
sync_selection = true;
|
||||
break;
|
||||
case UvExtractionMode::Edit:
|
||||
sync_selection = ((mr.toolsettings->uv_flag & UV_FLAG_SELECT_SYNC) != 0);
|
||||
break;
|
||||
case UvExtractionMode::Selection:
|
||||
sync_selection = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (mr.extract_type == MeshExtractType::BMesh) {
|
||||
return extract_edituv_lines_subdiv_bm(mr, subdiv_cache, sync_selection);
|
||||
}
|
||||
return extract_edituv_lines_subdiv_mesh(mr, subdiv_cache, sync_selection);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name Extract Edit UV Points Indices
|
||||
* \{ */
|
||||
|
||||
static void extract_edituv_points_bm(const MeshRenderData &mr,
|
||||
const bool sync_selection,
|
||||
GPUIndexBufBuilder &builder)
|
||||
{
|
||||
const BMesh &bm = *mr.bm;
|
||||
const BMFace *face;
|
||||
BMIter f_iter;
|
||||
BM_ITER_MESH (face, &f_iter, &const_cast<BMesh &>(bm), BM_FACES_OF_MESH) {
|
||||
if (skip_bm_face(*face, sync_selection)) {
|
||||
continue;
|
||||
}
|
||||
const BMLoop *loop = BM_FACE_FIRST_LOOP(face);
|
||||
for ([[maybe_unused]] const int i : IndexRange(face->len)) {
|
||||
GPU_indexbuf_add_point_vert(&builder, BM_elem_index_get(loop));
|
||||
loop = loop->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void extract_edituv_points_mesh(const MeshRenderData &mr,
|
||||
const bool sync_selection,
|
||||
GPUIndexBufBuilder &builder)
|
||||
{
|
||||
const OffsetIndices faces = mr.faces;
|
||||
const Span<int> corner_verts = mr.corner_verts;
|
||||
const Span<int> orig_index_vert = mr.orig_index_vert ?
|
||||
Span<int>(mr.orig_index_vert, mr.verts_num) :
|
||||
Span<int>();
|
||||
for (const int face_index : faces.index_range()) {
|
||||
const BMFace *face_orig = bm_original_face_get(mr, face_index);
|
||||
if (!face_orig) {
|
||||
continue;
|
||||
}
|
||||
if (skip_bm_face(*face_orig, sync_selection)) {
|
||||
continue;
|
||||
}
|
||||
for (const int corner : faces[face_index]) {
|
||||
const int vert = corner_verts[corner];
|
||||
if (!orig_index_vert.is_empty() && orig_index_vert[vert] == ORIGINDEX_NONE) {
|
||||
continue;
|
||||
}
|
||||
GPU_indexbuf_add_point_vert(&builder, corner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gpu::IndexBufPtr extract_edituv_points(const MeshRenderData &mr)
|
||||
{
|
||||
const bool sync_selection = (mr.toolsettings->uv_flag & UV_FLAG_SELECT_SYNC) != 0;
|
||||
|
||||
GPUIndexBufBuilder builder;
|
||||
GPU_indexbuf_init(&builder, GPU_PRIM_POINTS, mr.corners_num, mr.corners_num);
|
||||
if (mr.extract_type == MeshExtractType::BMesh) {
|
||||
extract_edituv_points_bm(mr, sync_selection, builder);
|
||||
}
|
||||
else {
|
||||
extract_edituv_points_mesh(mr, sync_selection, builder);
|
||||
}
|
||||
return gpu::IndexBufPtr(GPU_indexbuf_build(&builder));
|
||||
}
|
||||
|
||||
static void extract_edituv_points_subdiv_bm(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache,
|
||||
const bool sync_selection,
|
||||
GPUIndexBufBuilder &builder)
|
||||
{
|
||||
const BMesh &bm = *mr.bm;
|
||||
const Span<int> subdiv_loop_vert_index = subdiv_cache.verts_orig_index->data<int>();
|
||||
const Span<int> subdiv_loop_face_index(subdiv_cache.subdiv_loop_face_index,
|
||||
subdiv_cache.num_subdiv_loops);
|
||||
|
||||
for (const int subdiv_quad : IndexRange(subdiv_cache.num_subdiv_quads)) {
|
||||
const int coarse_face = subdiv_loop_face_index[subdiv_quad * 4];
|
||||
const BMFace &face_orig = *BM_face_at_index(&const_cast<BMesh &>(bm), coarse_face);
|
||||
if (skip_bm_face(face_orig, sync_selection)) {
|
||||
continue;
|
||||
}
|
||||
for (const int subdiv_corner : IndexRange(subdiv_quad * 4, 4)) {
|
||||
const int coarse_vert = subdiv_loop_vert_index[subdiv_corner];
|
||||
if (coarse_vert == -1) {
|
||||
continue;
|
||||
}
|
||||
GPU_indexbuf_add_point_vert(&builder, subdiv_corner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void extract_edituv_points_subdiv_mesh(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache,
|
||||
const bool sync_selection,
|
||||
GPUIndexBufBuilder &builder)
|
||||
{
|
||||
const Span<int> subdiv_loop_vert_index = subdiv_cache.verts_orig_index->data<int>();
|
||||
const Span<int> subdiv_loop_face_index(subdiv_cache.subdiv_loop_face_index,
|
||||
subdiv_cache.num_subdiv_loops);
|
||||
|
||||
for (const int subdiv_quad : IndexRange(subdiv_cache.num_subdiv_quads)) {
|
||||
const int coarse_face = subdiv_loop_face_index[subdiv_quad * 4];
|
||||
const BMFace *face_orig = bm_original_face_get(mr, coarse_face);
|
||||
if (!face_orig) {
|
||||
continue;
|
||||
}
|
||||
if (skip_bm_face(*face_orig, sync_selection)) {
|
||||
continue;
|
||||
}
|
||||
for (const int subdiv_corner : IndexRange(subdiv_quad * 4, 4)) {
|
||||
const int coarse_vert = subdiv_loop_vert_index[subdiv_corner];
|
||||
if (coarse_vert == -1) {
|
||||
continue;
|
||||
}
|
||||
GPU_indexbuf_add_point_vert(&builder, subdiv_corner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gpu::IndexBufPtr extract_edituv_points_subdiv(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache)
|
||||
{
|
||||
const bool sync_selection = (mr.toolsettings->uv_flag & UV_FLAG_SELECT_SYNC) != 0;
|
||||
|
||||
GPUIndexBufBuilder builder;
|
||||
GPU_indexbuf_init(
|
||||
&builder, GPU_PRIM_POINTS, subdiv_cache.num_subdiv_loops, subdiv_cache.num_subdiv_loops);
|
||||
if (mr.extract_type == MeshExtractType::BMesh) {
|
||||
extract_edituv_points_subdiv_bm(mr, subdiv_cache, sync_selection, builder);
|
||||
}
|
||||
else {
|
||||
extract_edituv_points_subdiv_mesh(mr, subdiv_cache, sync_selection, builder);
|
||||
}
|
||||
return gpu::IndexBufPtr(GPU_indexbuf_build(&builder));
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name Extract Edit UV Face-dots Indices
|
||||
* \{ */
|
||||
|
||||
static gpu::IndexBufPtr extract_edituv_face_dots_bm(const MeshRenderData &mr,
|
||||
const bool sync_selection)
|
||||
{
|
||||
const BMesh &bm = *mr.bm;
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask visible = IndexMask::from_predicate(
|
||||
IndexMask(bm.totface), memory, [&](const int i) {
|
||||
return !skip_bm_face(*BM_face_at_index(&const_cast<BMesh &>(bm), i), sync_selection);
|
||||
});
|
||||
|
||||
GPUIndexBufBuilder builder;
|
||||
GPU_indexbuf_init(&builder, GPU_PRIM_POINTS, visible.size(), bm.totface);
|
||||
visible.to_indices(GPU_indexbuf_get_data(&builder).cast<int>());
|
||||
return gpu::IndexBufPtr(GPU_indexbuf_build_ex(&builder, 0, bm.totface, false));
|
||||
}
|
||||
|
||||
static gpu::IndexBufPtr extract_edituv_face_dots_mesh(const MeshRenderData &mr,
|
||||
const bool sync_selection)
|
||||
{
|
||||
const OffsetIndices faces = mr.faces;
|
||||
IndexMaskMemory memory;
|
||||
IndexMask visible = IndexMask::from_predicate(faces.index_range(), memory, [&](const int i) {
|
||||
const BMFace *face_orig = bm_original_face_get(mr, i);
|
||||
if (!face_orig) {
|
||||
return false;
|
||||
}
|
||||
if (skip_bm_face(*face_orig, sync_selection)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (mr.use_subsurf_fdots) {
|
||||
const BitSpan facedot_tags = mr.mesh->runtime->subsurf_face_dot_tags;
|
||||
const Span<int> corner_verts = mr.corner_verts;
|
||||
visible = IndexMask::from_predicate(visible, memory, [&](const int i) {
|
||||
const Span<int> face_verts = corner_verts.slice(faces[i]);
|
||||
return std::any_of(face_verts.begin(), face_verts.end(), [&](const int vert) {
|
||||
return facedot_tags[vert];
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
GPUIndexBufBuilder builder;
|
||||
GPU_indexbuf_init(&builder, GPU_PRIM_POINTS, visible.size(), faces.size());
|
||||
visible.to_indices(GPU_indexbuf_get_data(&builder).cast<int>());
|
||||
return gpu::IndexBufPtr(GPU_indexbuf_build_ex(&builder, 0, faces.size(), false));
|
||||
}
|
||||
|
||||
gpu::IndexBufPtr extract_edituv_face_dots(const MeshRenderData &mr)
|
||||
{
|
||||
const bool sync_selection = (mr.toolsettings->uv_flag & UV_FLAG_SELECT_SYNC) != 0;
|
||||
if (mr.extract_type == MeshExtractType::BMesh) {
|
||||
return extract_edituv_face_dots_bm(mr, sync_selection);
|
||||
}
|
||||
return extract_edituv_face_dots_mesh(mr, sync_selection);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::draw
|
||||
@@ -0,0 +1,71 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "GPU_index_buffer.hh"
|
||||
|
||||
#include "extract_mesh.hh"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
static IndexMask calc_face_visibility_mesh(const MeshRenderData &mr, IndexMaskMemory &memory)
|
||||
{
|
||||
IndexMask visible(mr.faces_num);
|
||||
if (!mr.hide_poly.is_empty()) {
|
||||
visible = IndexMask::from_bools_inverse(visible, mr.hide_poly, memory);
|
||||
}
|
||||
if (mr.use_subsurf_fdots) {
|
||||
const OffsetIndices faces = mr.faces;
|
||||
const Span<int> corner_verts = mr.corner_verts;
|
||||
const BitSpan facedot_tags = mr.mesh->runtime->subsurf_face_dot_tags;
|
||||
visible = IndexMask::from_predicate(visible, memory, [&](const int i) {
|
||||
const Span<int> face_verts = corner_verts.slice(faces[i]);
|
||||
return std::any_of(face_verts.begin(), face_verts.end(), [&](const int vert) {
|
||||
return facedot_tags[vert];
|
||||
});
|
||||
});
|
||||
}
|
||||
return visible;
|
||||
}
|
||||
|
||||
static gpu::IndexBufPtr index_mask_to_ibo(const IndexMask &mask)
|
||||
{
|
||||
const int max_index = mask.min_array_size();
|
||||
GPUIndexBufBuilder builder;
|
||||
GPU_indexbuf_init(&builder, GPU_PRIM_POINTS, mask.size(), max_index);
|
||||
MutableSpan<uint> data = GPU_indexbuf_get_data(&builder);
|
||||
mask.to_indices<int>(data.cast<int>());
|
||||
return gpu::IndexBufPtr(GPU_indexbuf_build_ex(&builder, 0, max_index, false));
|
||||
}
|
||||
|
||||
static gpu::IndexBufPtr extract_face_dots_mesh(const MeshRenderData &mr)
|
||||
{
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask visible_faces = calc_face_visibility_mesh(mr, memory);
|
||||
return index_mask_to_ibo(visible_faces);
|
||||
}
|
||||
|
||||
static gpu::IndexBufPtr extract_face_dots_bm(const MeshRenderData &mr)
|
||||
{
|
||||
BMesh &bm = *mr.bm;
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask visible_faces = IndexMask::from_predicate(
|
||||
IndexRange(bm.totface), memory, [&](const int i) {
|
||||
return !BM_elem_flag_test_bool(BM_face_at_index(&bm, i), BM_ELEM_HIDDEN);
|
||||
});
|
||||
return index_mask_to_ibo(visible_faces);
|
||||
}
|
||||
|
||||
gpu::IndexBufPtr extract_face_dots(const MeshRenderData &mr)
|
||||
{
|
||||
if (mr.extract_type == MeshExtractType::Mesh) {
|
||||
return extract_face_dots_mesh(mr);
|
||||
}
|
||||
return extract_face_dots_bm(mr);
|
||||
}
|
||||
|
||||
} // namespace blender::draw
|
||||
@@ -0,0 +1,358 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "BLI_array_utils.hh"
|
||||
|
||||
#include "GPU_index_buffer.hh"
|
||||
|
||||
#include "extract_mesh.hh"
|
||||
|
||||
#include "draw_cache_inline.hh"
|
||||
#include "draw_subdivision.hh"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
static IndexMask calc_mesh_edge_visibility(const MeshRenderData &mr,
|
||||
const IndexMask &mask,
|
||||
IndexMaskMemory &memory)
|
||||
{
|
||||
IndexMask visible = mask;
|
||||
if (!mr.mesh->runtime->subsurf_optimal_display_edges.is_empty()) {
|
||||
const BoundedBitSpan visible_bits = mr.mesh->runtime->subsurf_optimal_display_edges;
|
||||
visible = IndexMask::from_bits(visible, visible_bits, memory);
|
||||
}
|
||||
if (!mr.hide_edge.is_empty()) {
|
||||
visible = IndexMask::from_bools_inverse(visible, mr.hide_edge, memory);
|
||||
}
|
||||
if (mr.hide_unmapped_edges && mr.orig_index_edge != nullptr) {
|
||||
const int *orig_index = mr.orig_index_edge;
|
||||
static_assert(ORIGINDEX_NONE == -1);
|
||||
visible = array_utils::indices_non_negative(visible, Span(orig_index, mr.edges_num), memory);
|
||||
}
|
||||
return visible;
|
||||
}
|
||||
|
||||
static void fill_loose_lines_ibo(const MeshRenderData &mr,
|
||||
const IndexMask &visible,
|
||||
MutableSpan<uint2> data)
|
||||
{
|
||||
/* Vertices for loose edges are not shared in the GPU vertex buffers, so the indices are simply
|
||||
* an increasing contiguous range. Ideally this would be generated on the GPU itself, or just
|
||||
* unnecessary, but a large number of loose edges isn't expected to be a common performance
|
||||
* bottleneck either. */
|
||||
threading::memory_bandwidth_bound_task(data.size_in_bytes(), [&]() {
|
||||
const uint loose_start = mr.corners_num;
|
||||
if (visible.size() == mr.loose_edges.size()) {
|
||||
array_utils::fill_index_range(data.cast<uint>(), loose_start);
|
||||
}
|
||||
else {
|
||||
visible.foreach_index_optimized<int>(
|
||||
[&](const int i, const int pos) {
|
||||
data[pos] = loose_start + uint2(i * 2 + 0, i * 2 + 1);
|
||||
},
|
||||
exec_mode::grain_size(4096));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static IndexMask calc_visible_loose_edge_indices(const MeshRenderData &mr, IndexMaskMemory &memory)
|
||||
{
|
||||
/* Since we're building a mask of the full loose edge data arrays (i.e. rather than a mask
|
||||
* of the mesh's edges) we need random access to the IndexMask indices. */
|
||||
Array<int, 64> loose_edges(mr.loose_edges.size());
|
||||
mr.loose_edges.to_indices(loose_edges.as_mutable_span());
|
||||
IndexMask visible(loose_edges.size());
|
||||
if (!mr.hide_edge.is_empty()) {
|
||||
const Span<bool> hide_edge = mr.hide_edge;
|
||||
visible = IndexMask::from_predicate(
|
||||
visible, memory, [&](const int i) { return !hide_edge[loose_edges[i]]; });
|
||||
}
|
||||
if (mr.hide_unmapped_edges && mr.orig_index_edge != nullptr) {
|
||||
const int *orig_index = mr.orig_index_edge;
|
||||
visible = IndexMask::from_predicate(visible, memory, [&](const int64_t i) {
|
||||
return orig_index[loose_edges[i]] != ORIGINDEX_NONE;
|
||||
});
|
||||
}
|
||||
return visible;
|
||||
}
|
||||
|
||||
static void extract_lines_mesh(const MeshRenderData &mr,
|
||||
gpu::IndexBufPtr *lines,
|
||||
gpu::IndexBufPtr *lines_loose,
|
||||
bool &no_loose_wire)
|
||||
{
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask visible_loose_edges = calc_visible_loose_edge_indices(mr, memory);
|
||||
const int max_index = mr.corners_num + mr.loose_edges.size() * 2;
|
||||
|
||||
no_loose_wire = visible_loose_edges.is_empty();
|
||||
|
||||
if (lines_loose && !lines) {
|
||||
GPUIndexBufBuilder builder;
|
||||
GPU_indexbuf_init(&builder, GPU_PRIM_LINES, visible_loose_edges.size(), max_index);
|
||||
MutableSpan<uint2> data = GPU_indexbuf_get_data(&builder).cast<uint2>();
|
||||
fill_loose_lines_ibo(mr, visible_loose_edges, data);
|
||||
*lines_loose = gpu::IndexBufPtr(GPU_indexbuf_build_ex(&builder, 0, max_index, false));
|
||||
return;
|
||||
}
|
||||
|
||||
const IndexMask non_loose_edges = mr.loose_edges.complement(IndexRange(mr.edges_num), memory);
|
||||
const IndexMask visible_non_loose_edges = calc_mesh_edge_visibility(mr, non_loose_edges, memory);
|
||||
|
||||
GPUIndexBufBuilder builder;
|
||||
GPU_indexbuf_init(&builder,
|
||||
GPU_PRIM_LINES,
|
||||
visible_non_loose_edges.size() + visible_loose_edges.size(),
|
||||
max_index);
|
||||
MutableSpan<uint2> data = GPU_indexbuf_get_data(&builder).cast<uint2>();
|
||||
|
||||
/* This code fills the index buffer in a non-deterministic way, with non-atomic access to `used`.
|
||||
* This is okay because any of the possible face corner indices are correct, since they all
|
||||
* correspond to the same #Mesh vertex. `used` exists here as a performance optimization to
|
||||
* avoid writing to the VBO. */
|
||||
const OffsetIndices faces = mr.faces;
|
||||
const Span<int> corner_edges = mr.corner_edges;
|
||||
if (visible_non_loose_edges.size() == mr.edges_num) {
|
||||
/* All edges in the mesh are visible. The edges in the GPU buffer will have the same order as
|
||||
* the mesh's edges, so any remapping is unnecessary. Use a boolean array to avoid writing to
|
||||
* the same indices again multiple times from different threads. This is slightly beneficial
|
||||
* because booleans are 8 times smaller than the `uint2` for each edge. */
|
||||
Array<bool> used(mr.edges_num, false);
|
||||
threading::memory_bandwidth_bound_task(
|
||||
used.as_span().size_in_bytes() + data.size_in_bytes() + corner_edges.size_in_bytes(),
|
||||
[&]() {
|
||||
threading::parallel_for(faces.index_range(), 2048, [&](const IndexRange range) {
|
||||
for (const int face_index : range) {
|
||||
const IndexRange face = faces[face_index];
|
||||
for (const int corner : face) {
|
||||
const int edge = corner_edges[corner];
|
||||
if (!used[edge]) {
|
||||
used[edge] = true;
|
||||
data[edge] = edge_from_corners(face, corner);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
else {
|
||||
Array<int> map(mr.edges_num, -1);
|
||||
threading::memory_bandwidth_bound_task(
|
||||
map.as_span().size_in_bytes() + data.size_in_bytes() + corner_edges.size_in_bytes(),
|
||||
[&]() {
|
||||
index_mask::build_reverse_map(visible_non_loose_edges, map.as_mutable_span());
|
||||
threading::parallel_for(faces.index_range(), 2048, [&](const IndexRange range) {
|
||||
for (const int face_index : range) {
|
||||
const IndexRange face = faces[face_index];
|
||||
for (const int corner : face) {
|
||||
const int edge = corner_edges[corner];
|
||||
const int vbo_index = map[edge];
|
||||
if (vbo_index != -1) {
|
||||
map[edge] = -1;
|
||||
data[vbo_index] = edge_from_corners(face, corner);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
fill_loose_lines_ibo(mr, visible_loose_edges, data.take_back(visible_loose_edges.size()));
|
||||
|
||||
*lines = gpu::IndexBufPtr(GPU_indexbuf_build_ex(&builder, 0, max_index, false));
|
||||
if (lines_loose) {
|
||||
*lines_loose = gpu::IndexBufPtr(GPU_indexbuf_create_subrange(
|
||||
lines->get(), visible_non_loose_edges.size() * 2, visible_loose_edges.size() * 2));
|
||||
}
|
||||
}
|
||||
|
||||
static void extract_lines_bm(const MeshRenderData &mr,
|
||||
gpu::IndexBufPtr *lines,
|
||||
gpu::IndexBufPtr *lines_loose,
|
||||
bool &no_loose_wire)
|
||||
{
|
||||
const BMesh &bm = *mr.bm;
|
||||
Array<int, 64> loose_edges(mr.loose_edges.size());
|
||||
mr.loose_edges.to_indices(loose_edges.as_mutable_span());
|
||||
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask visible_loose_edges = IndexMask::from_predicate(
|
||||
loose_edges.index_range(), memory, [&](const int i) {
|
||||
const BMEdge &edge = *BM_edge_at_index(&const_cast<BMesh &>(bm), loose_edges[i]);
|
||||
return !BM_elem_flag_test_bool(&edge, BM_ELEM_HIDDEN);
|
||||
});
|
||||
const int max_index = mr.corners_num + loose_edges.size() * 2;
|
||||
|
||||
no_loose_wire = visible_loose_edges.is_empty();
|
||||
|
||||
if (lines_loose && !lines) {
|
||||
GPUIndexBufBuilder builder;
|
||||
GPU_indexbuf_init(&builder, GPU_PRIM_LINES, visible_loose_edges.size(), max_index);
|
||||
MutableSpan<uint2> data = GPU_indexbuf_get_data(&builder).cast<uint2>();
|
||||
fill_loose_lines_ibo(mr, visible_loose_edges, data);
|
||||
*lines_loose = gpu::IndexBufPtr(GPU_indexbuf_build_ex(&builder, 0, max_index, false));
|
||||
return;
|
||||
}
|
||||
|
||||
const IndexMask non_loose_edges = mr.loose_edges.complement(IndexRange(bm.totedge), memory);
|
||||
const IndexMask visible_non_loose_edges = IndexMask::from_predicate(
|
||||
non_loose_edges, memory, [&](const int i) {
|
||||
const BMEdge &edge = *BM_edge_at_index(&const_cast<BMesh &>(bm), i);
|
||||
return !BM_elem_flag_test_bool(&edge, BM_ELEM_HIDDEN);
|
||||
});
|
||||
|
||||
GPUIndexBufBuilder builder;
|
||||
GPU_indexbuf_init(&builder,
|
||||
GPU_PRIM_LINES,
|
||||
visible_non_loose_edges.size() + visible_loose_edges.size(),
|
||||
max_index);
|
||||
MutableSpan<uint2> data = GPU_indexbuf_get_data(&builder).cast<uint2>();
|
||||
|
||||
/* Make use of BMesh's edge to loop topology knowledge to iterate over edges instead of
|
||||
* iterating over faces and defining edges implicitly as done in the #Mesh extraction. */
|
||||
visible_non_loose_edges.foreach_index(
|
||||
[&](const int i, const int pos) {
|
||||
const BMEdge &edge = *BM_edge_at_index(&const_cast<BMesh &>(bm), i);
|
||||
data[pos] = uint2(BM_elem_index_get(edge.l), BM_elem_index_get(edge.l->next));
|
||||
},
|
||||
exec_mode::grain_size(4096));
|
||||
|
||||
fill_loose_lines_ibo(mr, visible_loose_edges, data.take_back(visible_loose_edges.size()));
|
||||
|
||||
*lines = gpu::IndexBufPtr(GPU_indexbuf_build_ex(&builder, 0, max_index, false));
|
||||
if (lines_loose) {
|
||||
*lines_loose = gpu::IndexBufPtr(GPU_indexbuf_create_subrange(
|
||||
lines->get(), visible_non_loose_edges.size() * 2, visible_loose_edges.size() * 2));
|
||||
}
|
||||
}
|
||||
|
||||
void extract_lines(const MeshRenderData &mr,
|
||||
gpu::IndexBufPtr *lines,
|
||||
gpu::IndexBufPtr *lines_loose,
|
||||
bool &no_loose_wire)
|
||||
{
|
||||
if (mr.extract_type == MeshExtractType::Mesh) {
|
||||
extract_lines_mesh(mr, lines, lines_loose, no_loose_wire);
|
||||
}
|
||||
else {
|
||||
extract_lines_bm(mr, lines, lines_loose, no_loose_wire);
|
||||
}
|
||||
}
|
||||
|
||||
static void extract_lines_loose_geom_subdiv(const DRWSubdivCache &subdiv_cache,
|
||||
const MeshRenderData &mr,
|
||||
const int edge_loose_offset,
|
||||
gpu::IndexBuf *ibo)
|
||||
{
|
||||
Array<int, 64> loose_edges(mr.loose_edges.size());
|
||||
mr.loose_edges.to_indices(loose_edges.as_mutable_span());
|
||||
if (loose_edges.is_empty()) {
|
||||
return;
|
||||
}
|
||||
const int edges_per_edge = subdiv_edges_per_coarse_edge(subdiv_cache);
|
||||
const int loose_edges_num = subdiv_loose_edges_num(mr, subdiv_cache);
|
||||
|
||||
/* Update flags for loose edges, points are already handled. */
|
||||
static const GPUVertFormat format = GPU_vertformat_from_attribute("data",
|
||||
gpu::VertAttrType::UINT_32);
|
||||
|
||||
gpu::VertBuf *flags = GPU_vertbuf_calloc();
|
||||
GPU_vertbuf_init_with_format(*flags, format);
|
||||
|
||||
GPU_vertbuf_data_alloc(*flags, loose_edges_num);
|
||||
|
||||
MutableSpan<uint> flags_data = flags->data<uint>();
|
||||
|
||||
switch (mr.extract_type) {
|
||||
case MeshExtractType::Mesh: {
|
||||
const int *orig_index_edge = (mr.hide_unmapped_edges) ? mr.orig_index_edge : nullptr;
|
||||
if (orig_index_edge == nullptr) {
|
||||
const Span<bool> hide_edge = mr.hide_edge;
|
||||
if (!hide_edge.is_empty()) {
|
||||
for (const int i : loose_edges.index_range()) {
|
||||
const bool value = hide_edge[loose_edges[i]];
|
||||
flags_data.slice(i * edges_per_edge, edges_per_edge).fill(value);
|
||||
}
|
||||
}
|
||||
else {
|
||||
flags_data.fill(0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (mr.bm) {
|
||||
for (const int i : loose_edges.index_range()) {
|
||||
const BMEdge *bm_edge = bm_original_edge_get(mr, loose_edges[i]);
|
||||
const int value = (bm_edge) ? BM_elem_flag_test_bool(bm_edge, BM_ELEM_HIDDEN) : true;
|
||||
flags_data.slice(i * edges_per_edge, edges_per_edge).fill(value);
|
||||
}
|
||||
}
|
||||
else {
|
||||
const Span<bool> hide_edge = mr.hide_edge;
|
||||
if (!hide_edge.is_empty()) {
|
||||
for (const int i : loose_edges.index_range()) {
|
||||
const bool value = (orig_index_edge[loose_edges[i]] == ORIGINDEX_NONE) ?
|
||||
false :
|
||||
hide_edge[loose_edges[i]];
|
||||
flags_data.slice(i * edges_per_edge, edges_per_edge).fill(value);
|
||||
}
|
||||
}
|
||||
else {
|
||||
flags_data.fill(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MeshExtractType::BMesh: {
|
||||
BMesh *bm = mr.bm;
|
||||
for (const int i : loose_edges.index_range()) {
|
||||
const BMEdge *bm_edge = BM_edge_at_index(bm, loose_edges[i]);
|
||||
const bool value = BM_elem_flag_test_bool(bm_edge, BM_ELEM_HIDDEN);
|
||||
flags_data.slice(i * edges_per_edge, edges_per_edge).fill(value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
draw_subdiv_build_lines_loose_buffer(
|
||||
subdiv_cache, ibo, flags, uint(edge_loose_offset), loose_edges_num);
|
||||
|
||||
GPU_vertbuf_discard(flags);
|
||||
}
|
||||
|
||||
void extract_lines_subdiv(const DRWSubdivCache &subdiv_cache,
|
||||
const MeshRenderData &mr,
|
||||
gpu::IndexBufPtr *lines,
|
||||
gpu::IndexBufPtr *lines_loose,
|
||||
bool &no_loose_wire)
|
||||
{
|
||||
const int loose_ibo_size = subdiv_loose_edges_num(mr, subdiv_cache) * 2;
|
||||
no_loose_wire = loose_ibo_size == 0;
|
||||
|
||||
if (lines_loose && !lines) {
|
||||
*lines_loose = gpu::IndexBufPtr(GPU_indexbuf_build_on_device(loose_ibo_size));
|
||||
extract_lines_loose_geom_subdiv(subdiv_cache, mr, 0, lines_loose->get());
|
||||
return;
|
||||
}
|
||||
|
||||
const int non_loose_ibo_size = subdiv_cache.num_subdiv_loops * 2;
|
||||
|
||||
*lines = gpu::IndexBufPtr(GPU_indexbuf_build_on_device(non_loose_ibo_size + loose_ibo_size));
|
||||
if (non_loose_ibo_size > 0) {
|
||||
draw_subdiv_build_lines_buffer(subdiv_cache, lines->get());
|
||||
}
|
||||
extract_lines_loose_geom_subdiv(subdiv_cache, mr, non_loose_ibo_size, lines->get());
|
||||
|
||||
if (lines_loose) {
|
||||
/* Multiply by 2 because these are edges indices. */
|
||||
*lines_loose = gpu::IndexBufPtr(
|
||||
GPU_indexbuf_create_subrange(lines->get(), non_loose_ibo_size, loose_ibo_size));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::draw
|
||||
@@ -0,0 +1,269 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include <climits>
|
||||
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_ordered_edge.hh"
|
||||
|
||||
#include "BKE_editmesh.hh"
|
||||
|
||||
#include "GPU_index_buffer.hh"
|
||||
|
||||
#include "draw_subdivision.hh"
|
||||
#include "extract_mesh.hh"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
/** Set when the edge has not yet been initialized. */
|
||||
#define EDGE_IS_UNSET INT_MAX
|
||||
/**
|
||||
* Set when the edge has been initialized and handled.
|
||||
* This is set instead of #EDGE_IS_UNSET so the edge isn't handled again
|
||||
* which can write past the buffer bounds, see: #150841.
|
||||
*
|
||||
* \note this isn't ideal as the winding checks may match
|
||||
* or not depending on the order triangles are handled.
|
||||
* Typically we try to avoid different behavior based on the order of data
|
||||
* however enforcing exactly matching behavior seems fairly involved without
|
||||
* much benefit, so accept this shortcoming.
|
||||
*/
|
||||
#define EDGE_IS_HANDLED (INT_MAX - 1)
|
||||
#define EDGE_IS_UNSET_OR_HANDLED(i) ((i) >= EDGE_IS_HANDLED)
|
||||
|
||||
static void create_lines_for_remaining_edges(MutableSpan<int> vert_to_corner,
|
||||
Map<OrderedEdge, int> &edge_hash,
|
||||
GPUIndexBufBuilder &elb,
|
||||
bool &is_manifold)
|
||||
{
|
||||
for (const auto item : edge_hash.items()) {
|
||||
int v_data = item.value;
|
||||
if (EDGE_IS_UNSET_OR_HANDLED(v_data)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int v2 = item.key.v_low;
|
||||
int v3 = item.key.v_high;
|
||||
|
||||
int l1 = uint(abs(v_data)) - 1;
|
||||
if (v_data < 0) { /* `inv_opposite`. */
|
||||
std::swap(v2, v3);
|
||||
}
|
||||
int l2 = vert_to_corner[v2];
|
||||
int l3 = vert_to_corner[v3];
|
||||
GPU_indexbuf_add_line_adj_verts(&elb, l1, l2, l3, l1);
|
||||
is_manifold = false;
|
||||
}
|
||||
}
|
||||
|
||||
inline void rotate_vector(uint3 &value)
|
||||
{
|
||||
const uint tmp = value[0];
|
||||
value[0] = value[2];
|
||||
value[2] = value[1];
|
||||
value[1] = tmp;
|
||||
}
|
||||
|
||||
/** \param vert_to_corner: Array to convert vert index to any corner of this vert. */
|
||||
inline void lines_adjacency_triangle(uint3 vert_tri,
|
||||
uint3 corner_tri,
|
||||
MutableSpan<int> vert_to_corner,
|
||||
Map<OrderedEdge, int> &edge_hash,
|
||||
GPUIndexBufBuilder &elb,
|
||||
bool &is_manifold)
|
||||
{
|
||||
/* Iterate around the triangle's edges. */
|
||||
for (int e = 0; e < 3; e++) {
|
||||
rotate_vector(vert_tri);
|
||||
rotate_vector(corner_tri);
|
||||
|
||||
bool inv_indices = (vert_tri[1] > vert_tri[2]);
|
||||
edge_hash.add_or_modify(
|
||||
{vert_tri[1], vert_tri[2]},
|
||||
[&](int *value) {
|
||||
int new_value = int(corner_tri[0]) + 1; /* 0 cannot be signed so add one. */
|
||||
*value = inv_indices ? -new_value : new_value;
|
||||
/* Store loop indices for remaining non-manifold edges. */
|
||||
vert_to_corner[vert_tri[1]] = corner_tri[1];
|
||||
vert_to_corner[vert_tri[2]] = corner_tri[2];
|
||||
},
|
||||
[&](int *value) {
|
||||
int v_data = *value;
|
||||
if (v_data == EDGE_IS_UNSET) {
|
||||
int new_value = int(corner_tri[0]) + 1; /* 0 cannot be signed so add one. */
|
||||
*value = inv_indices ? -new_value : new_value;
|
||||
/* Store loop indices for remaining non-manifold edges. */
|
||||
vert_to_corner[vert_tri[1]] = corner_tri[1];
|
||||
vert_to_corner[vert_tri[2]] = corner_tri[2];
|
||||
}
|
||||
else if (UNLIKELY(v_data == EDGE_IS_HANDLED)) {
|
||||
/* Ignore additional faces once this has been handled
|
||||
* as this may exceed the pre-sized index buffer: see #150841 & !151084 for details. */
|
||||
|
||||
/* When there are 3+ users of an edge the mesh is not manifold (by definition). */
|
||||
is_manifold = false;
|
||||
}
|
||||
else {
|
||||
/* HACK Tag as not used.
|
||||
* Prevent overhead of removing from the hash as well as potentially adding
|
||||
* the value back in the case more than 2 triangles use this edge. */
|
||||
*value = EDGE_IS_HANDLED;
|
||||
bool inv_opposite = (v_data < 0);
|
||||
const int corner_opposite = abs(v_data) - 1;
|
||||
/* TODO: Make this part thread-safe. */
|
||||
if (inv_opposite == inv_indices) {
|
||||
/* Don't share edge if triangles have non matching winding. */
|
||||
GPU_indexbuf_add_line_adj_verts(
|
||||
&elb, corner_tri[0], corner_tri[1], corner_tri[2], corner_tri[0]);
|
||||
GPU_indexbuf_add_line_adj_verts(
|
||||
&elb, corner_opposite, corner_tri[1], corner_tri[2], corner_opposite);
|
||||
is_manifold = false;
|
||||
}
|
||||
else {
|
||||
GPU_indexbuf_add_line_adj_verts(
|
||||
&elb, corner_tri[0], corner_tri[1], corner_tri[2], corner_opposite);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static void calc_adjacency_bm(const MeshRenderData &mr,
|
||||
MutableSpan<int> vert_to_corner,
|
||||
Map<OrderedEdge, int> &edge_hash,
|
||||
GPUIndexBufBuilder &elb,
|
||||
bool &is_manifold)
|
||||
{
|
||||
const Span<std::array<BMLoop *, 3>> looptris = mr.edit_bmesh->looptris;
|
||||
for (const int i : looptris.index_range()) {
|
||||
const std::array<BMLoop *, 3> &tri = looptris[i];
|
||||
if (BM_elem_flag_test(tri[0]->f, BM_ELEM_HIDDEN)) {
|
||||
continue;
|
||||
}
|
||||
lines_adjacency_triangle(
|
||||
uint3(BM_elem_index_get(tri[0]->v),
|
||||
BM_elem_index_get(tri[1]->v),
|
||||
BM_elem_index_get(tri[2]->v)),
|
||||
uint3(BM_elem_index_get(tri[0]), BM_elem_index_get(tri[1]), BM_elem_index_get(tri[2])),
|
||||
vert_to_corner,
|
||||
edge_hash,
|
||||
elb,
|
||||
is_manifold);
|
||||
}
|
||||
}
|
||||
|
||||
static void calc_adjacency_mesh(const MeshRenderData &mr,
|
||||
MutableSpan<int> vert_to_corner,
|
||||
Map<OrderedEdge, int> &edge_hash,
|
||||
GPUIndexBufBuilder &elb,
|
||||
bool &is_manifold)
|
||||
{
|
||||
const OffsetIndices faces = mr.faces;
|
||||
const Span<int> corner_verts = mr.corner_verts;
|
||||
const Span<bool> hide_poly = mr.hide_poly;
|
||||
const Span<int3> corner_tris = mr.mesh->corner_tris();
|
||||
for (const int face : faces.index_range()) {
|
||||
if (!hide_poly.is_empty() && hide_poly[face]) {
|
||||
continue;
|
||||
}
|
||||
const IndexRange tris = bke::mesh::face_triangles_range(faces, face);
|
||||
for (const int3 &tri : corner_tris.slice(tris)) {
|
||||
lines_adjacency_triangle(
|
||||
uint3(corner_verts[tri[0]], corner_verts[tri[1]], corner_verts[tri[2]]),
|
||||
uint3(tri),
|
||||
vert_to_corner,
|
||||
edge_hash,
|
||||
elb,
|
||||
is_manifold);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gpu::IndexBufPtr extract_lines_adjacency(const MeshRenderData &mr, bool &r_is_manifold)
|
||||
{
|
||||
/* Similar to poly_to_tri_count().
|
||||
* There is always (loop + triangle - 1) edges inside a face.
|
||||
* Accumulate for all faces and you get : */
|
||||
const int tess_edge_len = mr.corners_num + mr.corner_tris_num - mr.faces_num;
|
||||
Map<OrderedEdge, int> edge_hash;
|
||||
edge_hash.reserve(tess_edge_len);
|
||||
Array<int> vert_to_corner(mr.verts_num, 0);
|
||||
bool is_manifold = true;
|
||||
|
||||
GPUIndexBufBuilder builder;
|
||||
GPU_indexbuf_init(&builder, GPU_PRIM_LINES_ADJ, tess_edge_len, mr.corners_num);
|
||||
|
||||
if (mr.extract_type == MeshExtractType::Mesh) {
|
||||
calc_adjacency_mesh(mr, vert_to_corner, edge_hash, builder, is_manifold);
|
||||
}
|
||||
else {
|
||||
calc_adjacency_bm(mr, vert_to_corner, edge_hash, builder, is_manifold);
|
||||
}
|
||||
|
||||
create_lines_for_remaining_edges(vert_to_corner, edge_hash, builder, is_manifold);
|
||||
|
||||
r_is_manifold = is_manifold;
|
||||
|
||||
return gpu::IndexBufPtr(GPU_indexbuf_build(&builder));
|
||||
}
|
||||
|
||||
gpu::IndexBufPtr extract_lines_adjacency_subdiv(const DRWSubdivCache &subdiv_cache,
|
||||
bool &r_is_manifold)
|
||||
{
|
||||
/* For each face there is (loop + triangle - 1) edges. Since we only have quads, and a quad
|
||||
* is split into 2 triangles, we have (loop + 2 - 1) = (loop + 1) edges for each quad, or in
|
||||
* total: (number_of_loops + number_of_quads). */
|
||||
const uint tess_edge_len = subdiv_cache.num_subdiv_loops + subdiv_cache.num_subdiv_quads;
|
||||
|
||||
Array<int> vert_to_corner(subdiv_cache.num_subdiv_verts, 0);
|
||||
Map<OrderedEdge, int> edge_hash;
|
||||
edge_hash.reserve(tess_edge_len);
|
||||
|
||||
bool is_manifold = true;
|
||||
|
||||
GPUIndexBufBuilder builder;
|
||||
GPU_indexbuf_init(&builder, GPU_PRIM_LINES_ADJ, tess_edge_len, subdiv_cache.num_subdiv_loops);
|
||||
|
||||
for (const int subdiv_quad_index : IndexRange(subdiv_cache.num_subdiv_quads)) {
|
||||
const uint loop_index = subdiv_quad_index * 4;
|
||||
const uint corner_0 = loop_index + 0;
|
||||
const uint corner_1 = loop_index + 1;
|
||||
const uint corner_2 = loop_index + 2;
|
||||
const uint corner_3 = loop_index + 3;
|
||||
|
||||
const uint vert_0 = subdiv_cache.subdiv_loop_subdiv_vert_index[corner_0];
|
||||
const uint vert_1 = subdiv_cache.subdiv_loop_subdiv_vert_index[corner_1];
|
||||
const uint vert_2 = subdiv_cache.subdiv_loop_subdiv_vert_index[corner_2];
|
||||
const uint vert_3 = subdiv_cache.subdiv_loop_subdiv_vert_index[corner_3];
|
||||
|
||||
lines_adjacency_triangle({vert_0, vert_1, vert_2},
|
||||
{corner_0, corner_1, corner_2},
|
||||
vert_to_corner,
|
||||
edge_hash,
|
||||
builder,
|
||||
is_manifold);
|
||||
lines_adjacency_triangle({vert_0, vert_2, vert_3},
|
||||
{corner_0, corner_2, corner_3},
|
||||
vert_to_corner,
|
||||
edge_hash,
|
||||
builder,
|
||||
is_manifold);
|
||||
}
|
||||
|
||||
create_lines_for_remaining_edges(vert_to_corner, edge_hash, builder, is_manifold);
|
||||
|
||||
r_is_manifold = is_manifold;
|
||||
|
||||
return gpu::IndexBufPtr(GPU_indexbuf_build(&builder));
|
||||
}
|
||||
|
||||
#undef EDGE_IS_UNSET
|
||||
#undef EDGE_IS_HANDLED
|
||||
#undef EDGE_IS_CHECK
|
||||
|
||||
} // namespace blender::draw
|
||||
@@ -0,0 +1,139 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "BLI_bitmap.h"
|
||||
#include "atomic_ops.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "GPU_index_buffer.hh"
|
||||
|
||||
#include "draw_subdivision.hh"
|
||||
#include "extract_mesh.hh"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
gpu::IndexBufPtr extract_lines_paint_mask(const MeshRenderData &mr)
|
||||
{
|
||||
const OffsetIndices faces = mr.faces;
|
||||
const Span<int> corner_edges = mr.corner_edges;
|
||||
const Span<bool> hide_edge = mr.hide_edge;
|
||||
const Span<bool> select_poly = mr.select_poly;
|
||||
const Span<int> orig_index_edge = mr.orig_index_edge ?
|
||||
Span<int>(mr.orig_index_edge, mr.edges_num) :
|
||||
Span<int>();
|
||||
|
||||
GPUIndexBufBuilder builder;
|
||||
const int max_index = mr.corners_num;
|
||||
GPU_indexbuf_init(&builder, GPU_PRIM_LINES, mr.edges_num, max_index);
|
||||
MutableSpan<uint2> data = GPU_indexbuf_get_data(&builder).cast<uint2>();
|
||||
|
||||
BLI_bitmap *select_map = BLI_BITMAP_NEW(mr.edges_num, __func__);
|
||||
threading::parallel_for(faces.index_range(), 1024, [&](const IndexRange range) {
|
||||
for (const int face_index : range) {
|
||||
const IndexRange face = faces[face_index];
|
||||
for (const int corner : face) {
|
||||
const int edge = corner_edges[corner];
|
||||
if ((!hide_edge.is_empty() && hide_edge[edge]) ||
|
||||
(!orig_index_edge.is_empty() && (orig_index_edge[edge] == ORIGINDEX_NONE)))
|
||||
{
|
||||
data[edge] = uint2(gpu::RESTART_INDEX);
|
||||
continue;
|
||||
}
|
||||
|
||||
const int corner_next = bke::mesh::face_corner_next(face, corner);
|
||||
if (!select_poly.is_empty() && select_poly[face_index]) {
|
||||
if (BLI_BITMAP_TEST_AND_SET_ATOMIC(select_map, edge)) {
|
||||
/* Hide edge as it has more than 2 selected loop. */
|
||||
data[edge] = uint2(gpu::RESTART_INDEX);
|
||||
}
|
||||
else {
|
||||
/* First selected loop. Set edge visible, overwriting any unselected loop. */
|
||||
data[edge] = uint2(corner, corner_next);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Set these unselected loop only if this edge has no other selected loop. */
|
||||
if (!BLI_BITMAP_TEST(select_map, edge)) {
|
||||
data[edge] = uint2(corner, corner_next);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
MEM_delete(select_map);
|
||||
return gpu::IndexBufPtr(GPU_indexbuf_build_ex(&builder, 0, max_index, true));
|
||||
}
|
||||
|
||||
gpu::IndexBufPtr extract_lines_paint_mask_subdiv(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache)
|
||||
{
|
||||
const Span<bool> hide_edge = mr.hide_edge;
|
||||
const Span<bool> select_poly = mr.select_poly;
|
||||
const Span<int> orig_index_edge = mr.orig_index_edge ?
|
||||
Span<int>(mr.orig_index_edge, mr.edges_num) :
|
||||
Span<int>();
|
||||
const Span<int> subdiv_loop_face_index(subdiv_cache.subdiv_loop_face_index,
|
||||
subdiv_cache.num_subdiv_loops);
|
||||
const Span<int> subdiv_loop_subdiv_edge_index(subdiv_cache.subdiv_loop_subdiv_edge_index,
|
||||
subdiv_cache.num_subdiv_loops);
|
||||
const Span<int> subdiv_loop_edge_index = subdiv_cache.edges_orig_index->data<int>();
|
||||
|
||||
GPUIndexBufBuilder builder;
|
||||
const int max_index = subdiv_cache.num_subdiv_loops;
|
||||
GPU_indexbuf_init(&builder, GPU_PRIM_LINES, subdiv_cache.num_subdiv_edges, max_index);
|
||||
MutableSpan<uint2> data = GPU_indexbuf_get_data(&builder).cast<uint2>();
|
||||
|
||||
BLI_bitmap *select_map = BLI_BITMAP_NEW(mr.edges_num, __func__);
|
||||
const int quads_num = subdiv_cache.num_subdiv_quads;
|
||||
threading::parallel_for(IndexRange(quads_num), 4096, [&](const IndexRange range) {
|
||||
for (const int subdiv_quad_index : range) {
|
||||
const int coarse_quad_index = subdiv_loop_face_index[subdiv_quad_index * 4];
|
||||
const IndexRange subdiv_face(subdiv_quad_index * 4, 4);
|
||||
for (const int corner : subdiv_face) {
|
||||
const uint coarse_edge_index = uint(subdiv_loop_edge_index[corner]);
|
||||
const uint subdiv_edge_index = uint(subdiv_loop_subdiv_edge_index[corner]);
|
||||
if (coarse_edge_index == -1u) {
|
||||
data[subdiv_edge_index] = uint2(gpu::RESTART_INDEX);
|
||||
continue;
|
||||
}
|
||||
if ((!hide_edge.is_empty() && hide_edge[coarse_edge_index]) ||
|
||||
(!orig_index_edge.is_empty() &&
|
||||
(orig_index_edge[coarse_edge_index] == ORIGINDEX_NONE)))
|
||||
{
|
||||
data[subdiv_edge_index] = uint2(gpu::RESTART_INDEX);
|
||||
continue;
|
||||
}
|
||||
|
||||
const int corner_next = bke::mesh::face_corner_next(subdiv_face, corner);
|
||||
if (!select_poly.is_empty() && select_poly[coarse_quad_index]) {
|
||||
if (BLI_BITMAP_TEST_AND_SET_ATOMIC(select_map, coarse_edge_index)) {
|
||||
/* Hide edge as it has more than 2 selected loop. */
|
||||
data[subdiv_edge_index] = uint2(gpu::RESTART_INDEX);
|
||||
}
|
||||
else {
|
||||
/* First selected loop. Set edge visible, overwriting any unselected loop. */
|
||||
data[subdiv_edge_index] = uint2(corner, corner_next);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Set these unselected loop only if this edge has no other selected loop. */
|
||||
if (!BLI_BITMAP_TEST(select_map, coarse_edge_index)) {
|
||||
data[subdiv_edge_index] = uint2(corner, corner_next);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
MEM_delete(select_map);
|
||||
return gpu::IndexBufPtr(GPU_indexbuf_build_ex(&builder, 0, max_index, true));
|
||||
}
|
||||
|
||||
} // namespace blender::draw
|
||||
@@ -0,0 +1,330 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "GPU_index_buffer.hh"
|
||||
|
||||
#include "draw_subdivision.hh"
|
||||
#include "extract_mesh.hh"
|
||||
|
||||
#include "BLI_array_utils.hh"
|
||||
#include "BLI_timeit.hh"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
static IndexMask calc_vert_visibility_mesh(const MeshRenderData &mr,
|
||||
const IndexMask &mask,
|
||||
IndexMaskMemory &memory)
|
||||
{
|
||||
IndexMask visible = mask;
|
||||
if (!mr.hide_vert.is_empty()) {
|
||||
visible = IndexMask::from_bools_inverse(visible, mr.hide_vert, memory);
|
||||
}
|
||||
if (mr.orig_index_vert != nullptr) {
|
||||
const int *orig_index = mr.orig_index_vert;
|
||||
static_assert(ORIGINDEX_NONE == -1);
|
||||
visible = array_utils::indices_non_negative(visible, Span(orig_index, mr.verts_num), memory);
|
||||
}
|
||||
return visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the index buffer in a parallel non-deterministic fashion. This is okay because any of the
|
||||
* possible face corner indices are correct, since they all correspond to the same #Mesh vertex.
|
||||
* The separate arrays exist as a performance optimization to avoid writing to the VBO.
|
||||
*/
|
||||
template<typename Fn>
|
||||
static void process_ibo_verts_mesh(const MeshRenderData &mr, const Fn &process_vert_fn)
|
||||
{
|
||||
const Span<int> corner_verts = mr.corner_verts;
|
||||
threading::parallel_for(corner_verts.index_range(), 2048, [&](const IndexRange range) {
|
||||
for (const int corner : range) {
|
||||
process_vert_fn(corner, corner_verts[corner]);
|
||||
}
|
||||
});
|
||||
|
||||
const int loose_edges_start = mr.corners_num;
|
||||
const Span<int2> edges = mr.edges;
|
||||
mr.loose_edges.foreach_index_optimized<int>(
|
||||
[&](const int i, const int pos) {
|
||||
const int2 edge = edges[i];
|
||||
process_vert_fn(loose_edges_start + pos * 2 + 0, edge[0]);
|
||||
process_vert_fn(loose_edges_start + pos * 2 + 1, edge[1]);
|
||||
},
|
||||
exec_mode::grain_size(8196));
|
||||
|
||||
const int loose_verts_start = mr.corners_num + mr.loose_edges.size() * 2;
|
||||
mr.loose_verts.foreach_index_optimized<int>(
|
||||
[&](const int i, const int pos) { process_vert_fn(loose_verts_start + pos, i); },
|
||||
exec_mode::grain_size(8196));
|
||||
}
|
||||
|
||||
static gpu::IndexBufPtr extract_points_mesh(const MeshRenderData &mr)
|
||||
{
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask visible_verts = calc_vert_visibility_mesh(mr, IndexMask(mr.verts_num), memory);
|
||||
|
||||
const int max_index = mr.corners_num + mr.loose_edges.size() * 2 + mr.loose_verts.size();
|
||||
GPUIndexBufBuilder builder;
|
||||
GPU_indexbuf_init(&builder, GPU_PRIM_POINTS, visible_verts.size(), max_index);
|
||||
MutableSpan<uint> data = GPU_indexbuf_get_data(&builder);
|
||||
|
||||
threading::memory_bandwidth_bound_task(mr.corner_verts.size_in_bytes(), [&]() {
|
||||
if (visible_verts.size() == mr.verts_num) {
|
||||
Array<bool> used(mr.verts_num, false);
|
||||
process_ibo_verts_mesh(mr, [&](const int ibo_index, const int vert) {
|
||||
if (!used[vert]) {
|
||||
used[vert] = true;
|
||||
data[vert] = ibo_index;
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
/* Compress the vertex indices into the smaller range of visible vertices in the IBO. */
|
||||
Array<int> map(mr.verts_num, -1);
|
||||
index_mask::build_reverse_map(visible_verts, map.as_mutable_span());
|
||||
process_ibo_verts_mesh(mr, [&](const int ibo_index, const int vert) {
|
||||
const int index = map[vert];
|
||||
if (index != -1) {
|
||||
map[vert] = -1;
|
||||
data[index] = ibo_index;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return gpu::IndexBufPtr(GPU_indexbuf_build_ex(&builder, 0, max_index, false));
|
||||
}
|
||||
|
||||
template<typename Fn>
|
||||
static void process_ibo_verts_bm(const MeshRenderData &mr, const Fn &process_vert_fn)
|
||||
{
|
||||
BMesh &bm = *mr.bm;
|
||||
|
||||
threading::parallel_for(IndexRange(mr.verts_num), 4096, [&](const IndexRange range) {
|
||||
for (const int i : range) {
|
||||
BMVert &vert = *BM_vert_at_index(&bm, i);
|
||||
if (const BMLoop *loop = BM_vert_find_first_loop(&vert)) {
|
||||
process_vert_fn(BM_elem_index_get(loop), i);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const int loose_edges_start = mr.corners_num;
|
||||
mr.loose_edges.foreach_index(
|
||||
[&](const int i, const int pos) {
|
||||
const BMEdge &edge = *BM_edge_at_index(&bm, i);
|
||||
process_vert_fn(loose_edges_start + pos * 2 + 0, BM_elem_index_get(edge.v1));
|
||||
process_vert_fn(loose_edges_start + pos * 2 + 1, BM_elem_index_get(edge.v2));
|
||||
},
|
||||
exec_mode::grain_size(4096));
|
||||
|
||||
const int loose_verts_start = mr.corners_num + mr.loose_edges.size() * 2;
|
||||
mr.loose_verts.foreach_index_optimized<int>(
|
||||
[&](const int i, const int pos) { process_vert_fn(loose_verts_start + pos, i); },
|
||||
exec_mode::grain_size(4096));
|
||||
}
|
||||
|
||||
static gpu::IndexBufPtr extract_points_bm(const MeshRenderData &mr)
|
||||
{
|
||||
BMesh &bm = *mr.bm;
|
||||
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask visible_verts = IndexMask::from_predicate(
|
||||
IndexRange(bm.totvert), memory, [&](const int i) {
|
||||
return !BM_elem_flag_test_bool(BM_vert_at_index(&const_cast<BMesh &>(bm), i),
|
||||
BM_ELEM_HIDDEN);
|
||||
});
|
||||
|
||||
const int max_index = mr.corners_num + mr.loose_edges.size() * 2 + mr.loose_verts.size();
|
||||
GPUIndexBufBuilder builder;
|
||||
GPU_indexbuf_init(&builder, GPU_PRIM_POINTS, visible_verts.size(), max_index);
|
||||
MutableSpan<uint> data = GPU_indexbuf_get_data(&builder);
|
||||
|
||||
if (mr.loose_verts.is_empty() && mr.loose_edges.is_empty()) {
|
||||
/* Make use of BMesh's vertex to loop topology knowledge to iterate over verts instead of
|
||||
* iterating over faces and defining points implicitly as done in the #Mesh extraction. */
|
||||
visible_verts.foreach_index(
|
||||
[&](const int i, const int pos) {
|
||||
BMVert &vert = *BM_vert_at_index(&bm, i);
|
||||
data[pos] = BM_elem_index_get(BM_vert_find_first_loop(&vert));
|
||||
},
|
||||
exec_mode::grain_size(4096));
|
||||
}
|
||||
else if (visible_verts.size() == bm.totvert) {
|
||||
Array<bool> used(mr.verts_num, false);
|
||||
process_ibo_verts_bm(mr, [&](const int ibo_index, const int vert) {
|
||||
if (!used[vert]) {
|
||||
used[vert] = true;
|
||||
data[vert] = ibo_index;
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
/* Compress the vertex indices into the smaller range of visible vertices in the IBO. */
|
||||
Array<int> map(mr.verts_num, -1);
|
||||
index_mask::build_reverse_map(visible_verts, map.as_mutable_span());
|
||||
process_ibo_verts_bm(mr, [&](const int ibo_index, const int vert) {
|
||||
const int index = map[vert];
|
||||
if (index != -1) {
|
||||
map[vert] = -1;
|
||||
data[index] = ibo_index;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return gpu::IndexBufPtr(GPU_indexbuf_build_ex(&builder, 0, max_index, false));
|
||||
}
|
||||
|
||||
gpu::IndexBufPtr extract_points(const MeshRenderData &mr)
|
||||
{
|
||||
if (mr.extract_type == MeshExtractType::Mesh) {
|
||||
return extract_points_mesh(mr);
|
||||
}
|
||||
return extract_points_bm(mr);
|
||||
}
|
||||
|
||||
static IndexMask calc_vert_visibility_mapped_mesh(const MeshRenderData &mr,
|
||||
const IndexMask &mask,
|
||||
const Span<int> map,
|
||||
IndexMaskMemory &memory)
|
||||
{
|
||||
IndexMask visible = mask;
|
||||
if (!mr.hide_vert.is_empty()) {
|
||||
const Span<bool> hide_vert = mr.hide_vert;
|
||||
visible = IndexMask::from_predicate(
|
||||
visible, memory, [&](const int i) { return !hide_vert[map[i]]; });
|
||||
}
|
||||
if (mr.orig_index_vert != nullptr) {
|
||||
const int *orig_index = mr.orig_index_vert;
|
||||
visible = IndexMask::from_predicate(
|
||||
visible, memory, [&](const int i) { return orig_index[map[i]] != ORIGINDEX_NONE; });
|
||||
}
|
||||
return visible;
|
||||
}
|
||||
|
||||
static gpu::IndexBufPtr extract_points_subdiv_mesh(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache)
|
||||
{
|
||||
const Span<int2> coarse_edges = mr.edges;
|
||||
const int verts_per_edge = subdiv_verts_per_coarse_edge(subdiv_cache);
|
||||
const int loose_edge_verts_num = verts_per_edge * mr.loose_edges.size();
|
||||
|
||||
const Span<bool> hide_vert = mr.hide_vert;
|
||||
const Span<int> corner_orig_verts = subdiv_cache.verts_orig_index->data<int>();
|
||||
|
||||
IndexMaskMemory memory;
|
||||
IndexMask visible_corners = IndexMask::from_predicate(
|
||||
corner_orig_verts.index_range(), memory, [&](const int i) {
|
||||
return corner_orig_verts[i] != -1;
|
||||
});
|
||||
visible_corners = calc_vert_visibility_mapped_mesh(
|
||||
mr, visible_corners, corner_orig_verts, memory);
|
||||
|
||||
Array<int, 64> loose_verts(mr.loose_verts.size());
|
||||
mr.loose_verts.to_indices(loose_verts.as_mutable_span());
|
||||
const IndexMask visible_loose = calc_vert_visibility_mapped_mesh(
|
||||
mr, IndexMask(loose_verts.size()), loose_verts, memory);
|
||||
|
||||
const int max_index = subdiv_cache.num_subdiv_loops + loose_edge_verts_num + loose_verts.size();
|
||||
GPUIndexBufBuilder builder;
|
||||
GPU_indexbuf_init(&builder,
|
||||
GPU_PRIM_POINTS,
|
||||
visible_corners.size() + mr.loose_edges.size() * 2 + visible_loose.size(),
|
||||
max_index);
|
||||
MutableSpan<uint> data = GPU_indexbuf_get_data(&builder);
|
||||
|
||||
visible_corners.to_indices<int32_t>(data.take_front(visible_corners.size()).cast<int32_t>());
|
||||
|
||||
const auto show_vert = [&](const int vert) {
|
||||
if (!hide_vert.is_empty() && hide_vert[vert]) {
|
||||
return false;
|
||||
}
|
||||
if (mr.orig_index_vert && mr.orig_index_vert[vert] == ORIGINDEX_NONE) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
MutableSpan loose_edge_data = data.slice(visible_corners.size(), mr.loose_edges.size() * 2);
|
||||
const int loose_geom_start = subdiv_cache.num_subdiv_loops;
|
||||
mr.loose_edges.foreach_index([&](const int i, const int pos) {
|
||||
const int2 edge = coarse_edges[i];
|
||||
const IndexRange edge_range(loose_geom_start + pos * verts_per_edge, verts_per_edge);
|
||||
loose_edge_data[pos * 2 + 0] = show_vert(edge[0]) ? edge_range.first() : gpu::RESTART_INDEX;
|
||||
loose_edge_data[pos * 2 + 1] = show_vert(edge[1]) ? edge_range.last() : gpu::RESTART_INDEX;
|
||||
});
|
||||
|
||||
MutableSpan loose_vert_data = data.take_back(visible_loose.size()).cast<int32_t>();
|
||||
const int loose_verts_start = loose_geom_start + loose_edge_verts_num;
|
||||
visible_loose.shift(loose_verts_start, memory).to_indices<int32_t>(loose_vert_data);
|
||||
|
||||
return gpu::IndexBufPtr(GPU_indexbuf_build_ex(&builder, 0, max_index, true));
|
||||
}
|
||||
|
||||
static gpu::IndexBufPtr extract_points_subdiv_bm(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache)
|
||||
{
|
||||
const Span<int2> coarse_edges = mr.edges;
|
||||
const IndexMask &loose_verts = mr.loose_verts;
|
||||
const IndexMask &loose_edges = mr.loose_edges;
|
||||
const int verts_per_edge = subdiv_verts_per_coarse_edge(subdiv_cache);
|
||||
const int loose_edge_verts_num = verts_per_edge * loose_edges.size();
|
||||
|
||||
const Span<int> corner_orig_verts = subdiv_cache.verts_orig_index->data<int>();
|
||||
|
||||
const auto show_vert_bm = [&](const int vert_index) {
|
||||
const BMVert *vert = mr.orig_index_vert ? bm_original_vert_get(mr, vert_index) :
|
||||
BM_vert_at_index(mr.bm, vert_index);
|
||||
return !BM_elem_flag_test_bool(vert, BM_ELEM_HIDDEN);
|
||||
};
|
||||
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask visible_corners = IndexMask::from_predicate(
|
||||
corner_orig_verts.index_range(), memory, [&](const int i) {
|
||||
return corner_orig_verts[i] != -1 && show_vert_bm(corner_orig_verts[i]);
|
||||
});
|
||||
|
||||
const IndexMask visible_loose = IndexMask::from_predicate(
|
||||
loose_verts.index_range(), memory, [&](const int vert) { return show_vert_bm(vert); });
|
||||
|
||||
const int max_index = subdiv_cache.num_subdiv_loops + loose_edge_verts_num + loose_verts.size();
|
||||
GPUIndexBufBuilder builder;
|
||||
GPU_indexbuf_init(&builder,
|
||||
GPU_PRIM_POINTS,
|
||||
visible_corners.size() + loose_edges.size() * 2 + visible_loose.size(),
|
||||
max_index);
|
||||
MutableSpan<uint> data = GPU_indexbuf_get_data(&builder);
|
||||
|
||||
visible_corners.to_indices<int32_t>(data.take_front(visible_corners.size()).cast<int32_t>());
|
||||
|
||||
MutableSpan loose_edge_data = data.slice(visible_corners.size(), loose_edges.size() * 2);
|
||||
const int loose_geom_start = subdiv_cache.num_subdiv_loops;
|
||||
for (const int i : loose_edges.index_range()) {
|
||||
const int2 edge = coarse_edges[loose_edges[i]];
|
||||
const IndexRange edge_range(loose_geom_start + i * verts_per_edge, verts_per_edge);
|
||||
loose_edge_data[i * 2 + 0] = show_vert_bm(edge[0]) ? edge_range.first() : gpu::RESTART_INDEX;
|
||||
loose_edge_data[i * 2 + 1] = show_vert_bm(edge[1]) ? edge_range.last() : gpu::RESTART_INDEX;
|
||||
}
|
||||
|
||||
MutableSpan loose_vert_data = data.take_back(visible_loose.size()).cast<int32_t>();
|
||||
const int loose_verts_start = loose_geom_start + loose_edge_verts_num;
|
||||
visible_loose.shift(loose_verts_start, memory).to_indices<int32_t>(loose_vert_data);
|
||||
|
||||
return gpu::IndexBufPtr(GPU_indexbuf_build_ex(&builder, 0, max_index, true));
|
||||
}
|
||||
|
||||
gpu::IndexBufPtr extract_points_subdiv(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache)
|
||||
{
|
||||
if (mr.extract_type == MeshExtractType::Mesh) {
|
||||
return extract_points_subdiv_mesh(mr, subdiv_cache);
|
||||
}
|
||||
return extract_points_subdiv_bm(mr, subdiv_cache);
|
||||
}
|
||||
|
||||
} // namespace blender::draw
|
||||
@@ -0,0 +1,139 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "BKE_editmesh.hh"
|
||||
|
||||
#include "BLI_math_geom.h"
|
||||
|
||||
#include "GPU_index_buffer.hh"
|
||||
|
||||
#include "extract_mesh.hh"
|
||||
|
||||
#include "draw_subdivision.hh"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
static gpu::IndexBufPtr extract_tris_mesh(const MeshRenderData &mr,
|
||||
const SortedFaceData &face_sorted)
|
||||
{
|
||||
const Span<int3> corner_tris = mr.mesh->corner_tris();
|
||||
if (!face_sorted.face_tri_offsets) {
|
||||
/* There are no hidden faces and no reordering is necessary to group triangles with the same
|
||||
* material. The corner indices from #Mesh::corner_tris() can be copied directly to the GPU. */
|
||||
BLI_assert(face_sorted.visible_tris_num == corner_tris.size());
|
||||
return gpu::IndexBufPtr(GPU_indexbuf_build_from_memory(GPU_PRIM_TRIS,
|
||||
corner_tris.cast<uint32_t>().data(),
|
||||
corner_tris.size(),
|
||||
0,
|
||||
mr.corners_num,
|
||||
false));
|
||||
}
|
||||
|
||||
const OffsetIndices faces = mr.faces;
|
||||
const Span<bool> hide_poly = mr.hide_poly;
|
||||
|
||||
GPUIndexBufBuilder builder;
|
||||
GPU_indexbuf_init(&builder, GPU_PRIM_TRIS, face_sorted.visible_tris_num, mr.corners_num);
|
||||
MutableSpan<uint3> data = GPU_indexbuf_get_data(&builder).cast<uint3>();
|
||||
|
||||
const Span<int> face_tri_offsets = face_sorted.face_tri_offsets->as_span();
|
||||
threading::parallel_for(faces.index_range(), 2048, [&](const IndexRange range) {
|
||||
for (const int face : range) {
|
||||
if (!hide_poly.is_empty() && hide_poly[face]) {
|
||||
continue;
|
||||
}
|
||||
const IndexRange mesh_range = bke::mesh::face_triangles_range(faces, face);
|
||||
const Span<uint3> mesh_tris = corner_tris.slice(mesh_range).cast<uint3>();
|
||||
MutableSpan<uint3> ibo_tris = data.slice(face_tri_offsets[face], mesh_tris.size());
|
||||
ibo_tris.copy_from(mesh_tris);
|
||||
}
|
||||
});
|
||||
|
||||
return gpu::IndexBufPtr(GPU_indexbuf_build_ex(&builder, 0, mr.corners_num, false));
|
||||
}
|
||||
|
||||
static gpu::IndexBufPtr extract_tris_bmesh(const MeshRenderData &mr,
|
||||
const SortedFaceData &face_sorted)
|
||||
{
|
||||
GPUIndexBufBuilder builder;
|
||||
GPU_indexbuf_init(&builder, GPU_PRIM_TRIS, face_sorted.visible_tris_num, mr.corners_num);
|
||||
MutableSpan<uint3> data = GPU_indexbuf_get_data(&builder).cast<uint3>();
|
||||
|
||||
BMesh &bm = *mr.bm;
|
||||
const Span<std::array<BMLoop *, 3>> looptris = mr.edit_bmesh->looptris;
|
||||
const Span<int> face_tri_offsets = *face_sorted.face_tri_offsets;
|
||||
threading::parallel_for(IndexRange(bm.totface), 1024, [&](const IndexRange range) {
|
||||
for (const int face_index : range) {
|
||||
const BMFace &face = *BM_face_at_index(&bm, face_index);
|
||||
if (BM_elem_flag_test(&face, BM_ELEM_HIDDEN)) {
|
||||
continue;
|
||||
}
|
||||
const int corner_index = BM_elem_index_get(BM_FACE_FIRST_LOOP(&face));
|
||||
const IndexRange bm_tris(poly_to_tri_count(face_index, corner_index),
|
||||
bke::mesh::face_triangles_num(face.len));
|
||||
const IndexRange ibo_tris(face_tri_offsets[face_index], bm_tris.size());
|
||||
for (const int i : bm_tris.index_range()) {
|
||||
data[ibo_tris[i]] = uint3(BM_elem_index_get(looptris[bm_tris[i]][0]),
|
||||
BM_elem_index_get(looptris[bm_tris[i]][1]),
|
||||
BM_elem_index_get(looptris[bm_tris[i]][2]));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return gpu::IndexBufPtr(GPU_indexbuf_build_ex(&builder, 0, bm.totloop, false));
|
||||
}
|
||||
|
||||
void create_material_subranges(const SortedFaceData &face_sorted,
|
||||
gpu::IndexBuf &tris_ibo,
|
||||
MutableSpan<gpu::IndexBufPtr> ibos)
|
||||
{
|
||||
/* Create ibo sub-ranges. Always do this to avoid error when the standard surface batch
|
||||
* is created before the surfaces-per-material. */
|
||||
int mat_start = 0;
|
||||
for (const int i : face_sorted.tris_num_by_material.index_range()) {
|
||||
/* These IBOs have not been queried yet but we create them just in case they are needed
|
||||
* later since they are not tracked by mesh_buffer_cache_create_requested(). */
|
||||
|
||||
const int mat_tri_len = face_sorted.tris_num_by_material[i];
|
||||
/* Multiply by 3 because these are triangle indices. */
|
||||
const int start = mat_start * 3;
|
||||
const int len = mat_tri_len * 3;
|
||||
ibos[i] = gpu::IndexBufPtr(GPU_indexbuf_create_subrange(&tris_ibo, start, len));
|
||||
mat_start += mat_tri_len;
|
||||
}
|
||||
}
|
||||
|
||||
gpu::IndexBufPtr extract_tris(const MeshRenderData &mr, const SortedFaceData &face_sorted)
|
||||
{
|
||||
if (mr.extract_type == MeshExtractType::Mesh) {
|
||||
return extract_tris_mesh(mr, face_sorted);
|
||||
}
|
||||
return extract_tris_bmesh(mr, face_sorted);
|
||||
}
|
||||
|
||||
gpu::IndexBufPtr extract_tris_subdiv(const DRWSubdivCache &subdiv_cache, MeshBatchCache &cache)
|
||||
{
|
||||
/* Initialize the index buffer, it was already allocated, it will be filled on the device. */
|
||||
gpu::IndexBufPtr ibo = gpu::IndexBufPtr(
|
||||
GPU_indexbuf_build_on_device(subdiv_cache.num_subdiv_triangles * 3));
|
||||
|
||||
if (!cache.tris_per_mat.is_empty()) {
|
||||
for (int i = 0; i < cache.mat_len; i++) {
|
||||
/* Multiply by 6 since we have 2 triangles per quad. */
|
||||
const int start = subdiv_cache.mat_start[i] * 6;
|
||||
const int len = (subdiv_cache.mat_end[i] - subdiv_cache.mat_start[i]) * 6;
|
||||
cache.tris_per_mat[i] = gpu::IndexBufPtr(
|
||||
GPU_indexbuf_create_subrange(ibo.get(), start, len));
|
||||
}
|
||||
}
|
||||
|
||||
draw_subdiv_build_tris_buffer(subdiv_cache, ibo.get(), cache.mat_len);
|
||||
return ibo;
|
||||
}
|
||||
|
||||
} // namespace blender::draw
|
||||
@@ -0,0 +1,341 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "BLI_array_utils.hh"
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_attribute_legacy_convert.hh"
|
||||
#include "BKE_attribute_math.hh"
|
||||
#include "BKE_mesh.hh"
|
||||
|
||||
#include "attribute_convert.hh"
|
||||
#include "draw_attributes.hh"
|
||||
#include "draw_subdivision.hh"
|
||||
#include "extract_mesh.hh"
|
||||
|
||||
#include "GPU_vertex_buffer.hh"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name Extract Attributes
|
||||
* \{ */
|
||||
|
||||
static void init_vbo_for_attribute(const MeshRenderData &mr,
|
||||
gpu::VertBuf &vbo,
|
||||
const StringRef name,
|
||||
const bke::AttrType type,
|
||||
bool build_on_device,
|
||||
uint32_t len)
|
||||
{
|
||||
char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
|
||||
GPU_vertformat_safe_attr_name(name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME);
|
||||
/* Attributes use auto-name. */
|
||||
SNPRINTF(attr_name, "a%s", attr_safe_name);
|
||||
|
||||
GPUVertFormat format = init_format_for_attribute(type, attr_name);
|
||||
GPU_vertformat_deinterleave(&format);
|
||||
|
||||
if (mr.active_color_name && name == mr.active_color_name) {
|
||||
GPU_vertformat_alias_add(&format, "ac");
|
||||
}
|
||||
if (mr.default_color_name && name == mr.default_color_name) {
|
||||
GPU_vertformat_alias_add(&format, "c");
|
||||
}
|
||||
|
||||
if (build_on_device) {
|
||||
GPU_vertbuf_init_build_on_device(vbo, format, len);
|
||||
}
|
||||
else {
|
||||
GPU_vertbuf_init_with_format(vbo, format);
|
||||
GPU_vertbuf_data_alloc(vbo, len);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void extract_data_mesh_mapped_corner(const Span<T> attribute,
|
||||
const Span<int> indices,
|
||||
gpu::VertBuf &vbo)
|
||||
{
|
||||
using Converter = AttributeConverter<T>;
|
||||
using VBOType = typename Converter::VBOType;
|
||||
MutableSpan data = vbo.data<VBOType>();
|
||||
|
||||
if constexpr (std::is_same_v<T, VBOType>) {
|
||||
array_utils::gather(attribute, indices, data);
|
||||
}
|
||||
else {
|
||||
threading::parallel_for(indices.index_range(), 8192, [&](const IndexRange range) {
|
||||
for (const int i : range) {
|
||||
data[i] = Converter::convert(attribute[indices[i]]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void extract_data_mesh_face(const OffsetIndices<int> faces,
|
||||
const Span<T> attribute,
|
||||
gpu::VertBuf &vbo)
|
||||
{
|
||||
using Converter = AttributeConverter<T>;
|
||||
using VBOType = typename Converter::VBOType;
|
||||
MutableSpan data = vbo.data<VBOType>();
|
||||
|
||||
threading::parallel_for(faces.index_range(), 2048, [&](const IndexRange range) {
|
||||
for (const int i : range) {
|
||||
data.slice(faces[i]).fill(Converter::convert(attribute[i]));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void extract_data_bmesh_vert(const BMesh &bm, const int cd_offset, gpu::VertBuf &vbo)
|
||||
{
|
||||
using Converter = AttributeConverter<T>;
|
||||
using VBOType = typename Converter::VBOType;
|
||||
VBOType *data = vbo.data<VBOType>().data();
|
||||
|
||||
const BMFace *face;
|
||||
BMIter f_iter;
|
||||
BM_ITER_MESH (face, &f_iter, &const_cast<BMesh &>(bm), BM_FACES_OF_MESH) {
|
||||
const BMLoop *loop = BM_FACE_FIRST_LOOP(face);
|
||||
for ([[maybe_unused]] const int i : IndexRange(face->len)) {
|
||||
const T *src = static_cast<const T *>(POINTER_OFFSET(loop->v->head.data, cd_offset));
|
||||
*data = Converter::convert(*src);
|
||||
loop = loop->next;
|
||||
data++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void extract_data_bmesh_edge(const BMesh &bm, const int cd_offset, gpu::VertBuf &vbo)
|
||||
{
|
||||
using Converter = AttributeConverter<T>;
|
||||
using VBOType = typename Converter::VBOType;
|
||||
VBOType *data = vbo.data<VBOType>().data();
|
||||
|
||||
const BMFace *face;
|
||||
BMIter f_iter;
|
||||
BM_ITER_MESH (face, &f_iter, &const_cast<BMesh &>(bm), BM_FACES_OF_MESH) {
|
||||
const BMLoop *loop = BM_FACE_FIRST_LOOP(face);
|
||||
for ([[maybe_unused]] const int i : IndexRange(face->len)) {
|
||||
const T &src = *static_cast<const T *>(POINTER_OFFSET(loop->e->head.data, cd_offset));
|
||||
*data = Converter::convert(src);
|
||||
loop = loop->next;
|
||||
data++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void extract_data_bmesh_face(const BMesh &bm, const int cd_offset, gpu::VertBuf &vbo)
|
||||
{
|
||||
using Converter = AttributeConverter<T>;
|
||||
using VBOType = typename Converter::VBOType;
|
||||
VBOType *data = vbo.data<VBOType>().data();
|
||||
|
||||
const BMFace *face;
|
||||
BMIter f_iter;
|
||||
BM_ITER_MESH (face, &f_iter, &const_cast<BMesh &>(bm), BM_FACES_OF_MESH) {
|
||||
const T &src = *static_cast<const T *>(POINTER_OFFSET(face->head.data, cd_offset));
|
||||
std::fill_n(data, face->len, Converter::convert(src));
|
||||
data += face->len;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void extract_data_bmesh_loop(const BMesh &bm, const int cd_offset, gpu::VertBuf &vbo)
|
||||
{
|
||||
using Converter = AttributeConverter<T>;
|
||||
using VBOType = typename Converter::VBOType;
|
||||
VBOType *data = vbo.data<VBOType>().data();
|
||||
|
||||
const BMFace *face;
|
||||
BMIter f_iter;
|
||||
BM_ITER_MESH (face, &f_iter, &const_cast<BMesh &>(bm), BM_FACES_OF_MESH) {
|
||||
const BMLoop *loop = BM_FACE_FIRST_LOOP(face);
|
||||
for ([[maybe_unused]] const int i : IndexRange(face->len)) {
|
||||
const T &src = *static_cast<const T *>(POINTER_OFFSET(loop->head.data, cd_offset));
|
||||
*data = Converter::convert(src);
|
||||
loop = loop->next;
|
||||
data++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void extract_attribute_data(const MeshRenderData &mr,
|
||||
const BMDataLayerLookup &attr,
|
||||
gpu::VertBuf &vbo)
|
||||
{
|
||||
bke::attribute_math::to_static_type(attr.type, [&]<typename T>() {
|
||||
if constexpr (!std::is_void_v<typename AttributeConverter<T>::VBOType>) {
|
||||
switch (attr.domain) {
|
||||
case bke::AttrDomain::Point:
|
||||
extract_data_bmesh_vert<T>(*mr.bm, attr.offset, vbo);
|
||||
break;
|
||||
case bke::AttrDomain::Edge:
|
||||
extract_data_bmesh_edge<T>(*mr.bm, attr.offset, vbo);
|
||||
break;
|
||||
case bke::AttrDomain::Face:
|
||||
extract_data_bmesh_face<T>(*mr.bm, attr.offset, vbo);
|
||||
break;
|
||||
case bke::AttrDomain::Corner:
|
||||
extract_data_bmesh_loop<T>(*mr.bm, attr.offset, vbo);
|
||||
break;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void extract_attribute_data(const MeshRenderData &mr,
|
||||
const bke::GAttributeReader &attr,
|
||||
gpu::VertBuf &vbo)
|
||||
{
|
||||
if (attr.varray.is_single()) {
|
||||
bke::attribute_math::to_static_type(attr.varray.type(), [&]<typename T>() {
|
||||
const VArray<T> &src = attr.varray.typed<T>();
|
||||
if constexpr (!std::is_void_v<typename AttributeConverter<T>::VBOType>) {
|
||||
using Converter = AttributeConverter<T>;
|
||||
using VBOType = typename Converter::VBOType;
|
||||
MutableSpan data = vbo.data<VBOType>();
|
||||
data.fill(Converter::convert(src.get_internal_single()));
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
bke::attribute_math::to_static_type(attr.varray.type(), [&]<typename T>() {
|
||||
if constexpr (!std::is_void_v<typename AttributeConverter<T>::VBOType>) {
|
||||
switch (attr.domain) {
|
||||
case bke::AttrDomain::Point:
|
||||
extract_data_mesh_mapped_corner(GVArraySpan(*attr).typed<T>(), mr.corner_verts, vbo);
|
||||
break;
|
||||
case bke::AttrDomain::Edge:
|
||||
extract_data_mesh_mapped_corner(GVArraySpan(*attr).typed<T>(), mr.corner_edges, vbo);
|
||||
break;
|
||||
case bke::AttrDomain::Face:
|
||||
extract_data_mesh_face(mr.faces, GVArraySpan(*attr).typed<T>(), vbo);
|
||||
break;
|
||||
case bke::AttrDomain::Corner:
|
||||
vertbuf_data_extract_direct(GVArraySpan(*attr).typed<T>(), vbo);
|
||||
break;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
gpu::VertBufPtr extract_attribute(const MeshRenderData &mr, const StringRef name)
|
||||
{
|
||||
gpu::VertBuf *vbo = GPU_vertbuf_calloc();
|
||||
if (mr.extract_type == MeshExtractType::BMesh) {
|
||||
const BMDataLayerLookup attr = BM_data_layer_lookup(*mr.bm, name);
|
||||
if (!attr) {
|
||||
return {};
|
||||
}
|
||||
const bke::AttrType type = attr.type;
|
||||
init_vbo_for_attribute(mr, *vbo, name, type, false, uint32_t(mr.corners_num));
|
||||
extract_attribute_data(mr, attr, *vbo);
|
||||
}
|
||||
else {
|
||||
const bke::AttributeAccessor attributes = mr.mesh->attributes();
|
||||
const bke::GAttributeReader attr = attributes.lookup(name);
|
||||
if (!attr) {
|
||||
return {};
|
||||
}
|
||||
const bke::AttrType type = bke::cpp_type_to_attribute_type(attr.varray.type());
|
||||
init_vbo_for_attribute(mr, *vbo, name, type, false, uint32_t(mr.corners_num));
|
||||
extract_attribute_data(mr, attr, *vbo);
|
||||
}
|
||||
return gpu::VertBufPtr(vbo);
|
||||
}
|
||||
|
||||
static gpu::VertBufPtr init_coarse_data(const bke::AttrType type, const int coarse_corners_num)
|
||||
{
|
||||
gpu::VertBuf *vbo = GPU_vertbuf_calloc();
|
||||
GPUVertFormat coarse_format = draw::init_format_for_attribute(type, "data");
|
||||
GPU_vertbuf_init_with_format_ex(*vbo, coarse_format, GPU_USAGE_STATIC);
|
||||
GPU_vertbuf_data_alloc(*vbo, uint32_t(coarse_corners_num));
|
||||
return gpu::VertBufPtr(vbo);
|
||||
}
|
||||
|
||||
gpu::VertBufPtr extract_attribute_subdiv(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache,
|
||||
const StringRef name)
|
||||
{
|
||||
|
||||
const Mesh *coarse_mesh = subdiv_cache.mesh;
|
||||
|
||||
/* Prepare VBO for coarse data. The compute shader only expects floats. */
|
||||
gpu::VertBufPtr coarse_vbo;
|
||||
bke::AttrType type;
|
||||
if (mr.extract_type == MeshExtractType::BMesh) {
|
||||
const BMDataLayerLookup attr = BM_data_layer_lookup(*mr.bm, name);
|
||||
if (!attr) {
|
||||
return {};
|
||||
}
|
||||
type = attr.type;
|
||||
coarse_vbo = init_coarse_data(type, coarse_mesh->corners_num);
|
||||
extract_attribute_data(mr, attr, *coarse_vbo);
|
||||
}
|
||||
else {
|
||||
const bke::AttributeAccessor attributes = mr.mesh->attributes();
|
||||
const bke::GAttributeReader attr = attributes.lookup(name);
|
||||
if (!attr) {
|
||||
return {};
|
||||
}
|
||||
type = bke::cpp_type_to_attribute_type(attr.varray.type());
|
||||
coarse_vbo = init_coarse_data(type, coarse_mesh->corners_num);
|
||||
extract_attribute_data(mr, attr, *coarse_vbo);
|
||||
}
|
||||
|
||||
gpu::VertBuf *vbo = GPU_vertbuf_calloc();
|
||||
init_vbo_for_attribute(mr, *vbo, name, type, true, subdiv_cache.num_subdiv_loops);
|
||||
|
||||
/* Ensure data is uploaded properly. */
|
||||
GPU_vertbuf_tag_dirty(coarse_vbo.get());
|
||||
bke::attribute_math::to_static_type(type, [&]<typename T>() {
|
||||
using Converter = AttributeConverter<T>;
|
||||
if constexpr (!std::is_void_v<typename Converter::VBOType>) {
|
||||
draw_subdiv_interp_custom_data(subdiv_cache,
|
||||
*coarse_vbo,
|
||||
*vbo,
|
||||
Converter::gpu_component_type,
|
||||
Converter::gpu_component_len,
|
||||
0);
|
||||
}
|
||||
});
|
||||
|
||||
return gpu::VertBufPtr(vbo);
|
||||
}
|
||||
|
||||
gpu::VertBufPtr extract_attr_viewer(const MeshRenderData &mr)
|
||||
{
|
||||
static const GPUVertFormat format = GPU_vertformat_from_attribute(
|
||||
"attribute_value", gpu::VertAttrType::SFLOAT_32_32_32_32);
|
||||
|
||||
gpu::VertBufPtr vbo = gpu::VertBufPtr(GPU_vertbuf_create_with_format(format));
|
||||
GPU_vertbuf_data_alloc(*vbo, mr.corners_num);
|
||||
MutableSpan vbo_data = vbo->data<ColorGeometry4f>();
|
||||
|
||||
const StringRefNull attr_name = ".viewer";
|
||||
const bke::AttributeAccessor attributes = mr.mesh->attributes();
|
||||
const bke::AttributeReader attribute = attributes.lookup_or_default<ColorGeometry4f>(
|
||||
attr_name, bke::AttrDomain::Corner, {1.0f, 0.0f, 1.0f, 1.0f});
|
||||
attribute.varray.materialize(vbo_data);
|
||||
return vbo;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::draw
|
||||
@@ -0,0 +1,212 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "BLI_math_vector.hh"
|
||||
|
||||
#include "GPU_capabilities.hh"
|
||||
|
||||
#include "draw_subdivision.hh"
|
||||
#include "extract_mesh.hh"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
struct MEdgeDataPrev {
|
||||
int corner_a;
|
||||
|
||||
/* Data that represents:
|
||||
* - the index of the face of `corner_a` before the 2nd loop is found
|
||||
* - the index of the next radial corner after the 2nd loop is found */
|
||||
int data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculates a factor that is used to identify the minimum angle in the shader to display an edge.
|
||||
* NOTE: Keep in sync with `subdiv_vbo_edge_fac_comp.glsl`.
|
||||
*/
|
||||
|
||||
inline float edge_factor_calc(const float3 &a, const float3 &b)
|
||||
{
|
||||
const float cosine = math::dot(a, b);
|
||||
|
||||
/* Re-scale to the slider range. */
|
||||
float fac = (200 * (cosine - 1.0f)) + 1.0f;
|
||||
CLAMP(fac, 0.0f, 1.0f);
|
||||
/* 1.0 is a reserved value to force hide the wire. */
|
||||
constexpr float factor = 254.0f / 255.0f;
|
||||
return fac * factor;
|
||||
}
|
||||
|
||||
static void extract_edge_factor_mesh(const MeshRenderData &mr, MutableSpan<float> vbo_data)
|
||||
{
|
||||
const OffsetIndices faces = mr.faces;
|
||||
const Span<int> corner_edges = mr.corner_edges;
|
||||
const Span<float3> face_normals = mr.face_normals;
|
||||
const BitSpan optimal_display_edges = mr.mesh->runtime->subsurf_optimal_display_edges;
|
||||
|
||||
Array<int8_t> edge_face_count(mr.edges_num, 0);
|
||||
Array<MEdgeDataPrev> edge_data(mr.edges_num);
|
||||
|
||||
for (const int face : faces.index_range()) {
|
||||
for (const int corner : faces[face]) {
|
||||
const int edge = corner_edges[corner];
|
||||
if (!optimal_display_edges.is_empty() && !optimal_display_edges[edge]) {
|
||||
vbo_data[corner] = 1.0f;
|
||||
continue;
|
||||
}
|
||||
|
||||
MEdgeDataPrev *medata = &edge_data[edge];
|
||||
|
||||
const int8_t face_count = edge_face_count[edge];
|
||||
vbo_data[corner] = 0;
|
||||
if (face_count < 4) {
|
||||
if (face_count == 0) {
|
||||
/* Prepare to calculate the factor. */
|
||||
medata->corner_a = corner;
|
||||
medata->data = face;
|
||||
}
|
||||
else if (face_count == 1) {
|
||||
/* Calculate the factor for both corners. */
|
||||
const int other_face = medata->data;
|
||||
const float factor = edge_factor_calc(face_normals[other_face], face_normals[face]);
|
||||
vbo_data[medata->corner_a] = factor;
|
||||
vbo_data[corner] = factor;
|
||||
|
||||
/* If the count still changes, use this `data` member to inform the corner. */
|
||||
medata->data = corner;
|
||||
}
|
||||
else {
|
||||
/* Non-manifold edge. Always visible. */
|
||||
const int corner_a = medata->corner_a;
|
||||
const int corner_b = medata->data;
|
||||
vbo_data[corner_a] = 0;
|
||||
vbo_data[corner_b] = 0;
|
||||
}
|
||||
|
||||
edge_face_count[edge]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void extract_edge_factor_bm(const MeshRenderData &mr, MutableSpan<float> vbo_data)
|
||||
{
|
||||
BMesh &bm = *mr.bm;
|
||||
threading::parallel_for(IndexRange(bm.totface), 2048, [&](const IndexRange range) {
|
||||
for (const int face_index : range) {
|
||||
const BMFace &face = *BM_face_at_index(&bm, face_index);
|
||||
const BMLoop *loop = BM_FACE_FIRST_LOOP(&face);
|
||||
for ([[maybe_unused]] const int i : IndexRange(face.len)) {
|
||||
const int index = BM_elem_index_get(loop);
|
||||
if (BM_edge_is_manifold(loop->e)) {
|
||||
const BMFace *other_face = loop->radial_next->f;
|
||||
vbo_data[index] = edge_factor_calc(float3(bm_face_no_get(mr, &face)),
|
||||
float3(bm_face_no_get(mr, other_face)));
|
||||
}
|
||||
else {
|
||||
vbo_data[index] = 0.0f;
|
||||
}
|
||||
loop = loop->next;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
gpu::VertBufPtr extract_edge_factor(const MeshRenderData &mr)
|
||||
{
|
||||
static const GPUVertFormat format = GPU_vertformat_from_attribute("wd",
|
||||
gpu::VertAttrType::SFLOAT_32);
|
||||
gpu::VertBufPtr vbo = gpu::VertBufPtr(GPU_vertbuf_create_with_format(format));
|
||||
GPU_vertbuf_data_alloc(*vbo, mr.corners_num + mr.loose_indices_num);
|
||||
MutableSpan vbo_data = vbo->data<float>();
|
||||
if (mr.extract_type == MeshExtractType::Mesh) {
|
||||
extract_edge_factor_mesh(mr, vbo_data);
|
||||
}
|
||||
else {
|
||||
extract_edge_factor_bm(mr, vbo_data);
|
||||
}
|
||||
vbo_data.take_back(mr.loose_indices_num).fill(0.0f);
|
||||
return vbo;
|
||||
}
|
||||
|
||||
static gpu::VertBuf *build_poly_other_map_vbo(const DRWSubdivCache &subdiv_cache)
|
||||
{
|
||||
gpu::VertBuf *vbo = GPU_vertbuf_calloc();
|
||||
|
||||
static const GPUVertFormat format = GPU_vertformat_from_attribute("poly_other",
|
||||
gpu::VertAttrType::SINT_32);
|
||||
|
||||
GPU_vertbuf_init_with_format(*vbo, format);
|
||||
GPU_vertbuf_data_alloc(*vbo, subdiv_cache.num_subdiv_loops);
|
||||
MutableSpan vbo_data = vbo->data<int>();
|
||||
|
||||
Array<MEdgeDataPrev> edge_data(subdiv_cache.num_subdiv_edges);
|
||||
Array<int> tmp_edge_corner_count(subdiv_cache.num_subdiv_edges, 0);
|
||||
int *subdiv_loop_subdiv_edge_index = subdiv_cache.subdiv_loop_subdiv_edge_index;
|
||||
|
||||
for (int corner : IndexRange(subdiv_cache.num_subdiv_loops)) {
|
||||
const int edge = subdiv_loop_subdiv_edge_index[corner];
|
||||
const int quad = corner / 4;
|
||||
const int corner_count = tmp_edge_corner_count[edge]++;
|
||||
|
||||
vbo_data[corner] = -1;
|
||||
if (corner_count == 0) {
|
||||
edge_data[edge].corner_a = corner;
|
||||
edge_data[edge].data = quad;
|
||||
}
|
||||
else if (corner_count == 1) {
|
||||
const int corner_a = edge_data[edge].corner_a;
|
||||
const int quad_a = edge_data[edge].data;
|
||||
vbo_data[corner_a] = quad;
|
||||
vbo_data[corner] = quad_a;
|
||||
edge_data[edge].data = corner;
|
||||
}
|
||||
else if (corner_count == 2) {
|
||||
const int corner_a = edge_data[edge].corner_a;
|
||||
const int corner_b = edge_data[edge].data;
|
||||
vbo_data[corner_a] = -1;
|
||||
vbo_data[corner_b] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
return vbo;
|
||||
}
|
||||
|
||||
gpu::VertBufPtr extract_edge_factor_subdiv(const DRWSubdivCache &subdiv_cache,
|
||||
const MeshRenderData &mr,
|
||||
gpu::VertBuf &pos_nor)
|
||||
{
|
||||
gpu::VertBufPtr vbo = gpu::VertBufPtr(GPU_vertbuf_create_on_device(
|
||||
GPU_vertformat_from_attribute("wd", gpu::VertAttrType::SFLOAT_32),
|
||||
subdiv_cache.num_subdiv_loops + subdiv_loose_edges_num(mr, subdiv_cache) * 2));
|
||||
|
||||
if (mr.faces_num > 0) {
|
||||
gpu::VertBuf *poly_other_map = build_poly_other_map_vbo(subdiv_cache);
|
||||
|
||||
draw_subdiv_build_edge_fac_buffer(
|
||||
subdiv_cache, &pos_nor, subdiv_cache.edges_draw_flag, poly_other_map, vbo.get());
|
||||
|
||||
GPU_vertbuf_discard(poly_other_map);
|
||||
}
|
||||
|
||||
const int loose_edges_num = subdiv_loose_edges_num(mr, subdiv_cache);
|
||||
if (loose_edges_num == 0) {
|
||||
return vbo;
|
||||
}
|
||||
|
||||
/* Make sure buffer is active for sending loose data. */
|
||||
GPU_vertbuf_use(vbo.get());
|
||||
|
||||
const int offset = subdiv_cache.num_subdiv_loops;
|
||||
const float values[2] = {1.0f, 1.0f};
|
||||
for (const int i : IndexRange(loose_edges_num)) {
|
||||
GPU_vertbuf_update_sub(vbo.get(), (offset + i * 2) * sizeof(float), sizeof(values), values);
|
||||
}
|
||||
return vbo;
|
||||
}
|
||||
|
||||
} // namespace blender::draw
|
||||
@@ -0,0 +1,399 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "DNA_meshdata_types.h"
|
||||
|
||||
#include "extract_mesh.hh"
|
||||
|
||||
#include "draw_cache_impl.hh"
|
||||
|
||||
#include "draw_subdivision.hh"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
static void mesh_render_data_edge_flag(const MeshRenderData &mr,
|
||||
const BMEdge *eed,
|
||||
EditLoopData &eattr)
|
||||
{
|
||||
const ToolSettings *ts = mr.toolsettings;
|
||||
const bool is_vertex_select_mode = (ts != nullptr) && (ts->selectmode & SCE_SELECT_VERTEX) != 0;
|
||||
const bool is_face_only_select_mode = (ts != nullptr) && (ts->selectmode == SCE_SELECT_FACE);
|
||||
|
||||
if (eed == mr.eed_act) {
|
||||
eattr.e_flag |= VFLAG_EDGE_ACTIVE;
|
||||
}
|
||||
if (!is_vertex_select_mode && BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
|
||||
eattr.e_flag |= VFLAG_EDGE_SELECTED;
|
||||
}
|
||||
if (is_vertex_select_mode && BM_elem_flag_test(eed->v1, BM_ELEM_SELECT) &&
|
||||
BM_elem_flag_test(eed->v2, BM_ELEM_SELECT))
|
||||
{
|
||||
eattr.e_flag |= VFLAG_EDGE_SELECTED;
|
||||
eattr.e_flag |= VFLAG_VERT_SELECTED;
|
||||
}
|
||||
if (BM_elem_flag_test(eed, BM_ELEM_SEAM)) {
|
||||
eattr.e_flag |= VFLAG_EDGE_SEAM;
|
||||
}
|
||||
if (!BM_elem_flag_test(eed, BM_ELEM_SMOOTH)) {
|
||||
eattr.e_flag |= VFLAG_EDGE_SHARP;
|
||||
}
|
||||
|
||||
/* Use active edge color for active face edges because
|
||||
* specular highlights make it hard to see #55456#510873.
|
||||
*
|
||||
* This isn't ideal since it can't be used when mixing edge/face modes
|
||||
* but it's still better than not being able to see the active face. */
|
||||
if (is_face_only_select_mode) {
|
||||
if (mr.efa_act != nullptr) {
|
||||
if (BM_edge_in_face(eed, mr.efa_act)) {
|
||||
eattr.e_flag |= VFLAG_EDGE_ACTIVE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Use half a byte for value range */
|
||||
if (mr.edge_crease_ofs != -1) {
|
||||
float crease = BM_ELEM_CD_GET_FLOAT(eed, mr.edge_crease_ofs);
|
||||
if (crease > 0) {
|
||||
eattr.crease = uchar(ceil(crease * 15.0f));
|
||||
}
|
||||
}
|
||||
/* Use a byte for value range */
|
||||
if (mr.bweight_ofs != -1) {
|
||||
float bweight = BM_ELEM_CD_GET_FLOAT(eed, mr.bweight_ofs);
|
||||
if (bweight > 0) {
|
||||
eattr.bweight = uchar(bweight * 255.0f);
|
||||
}
|
||||
}
|
||||
#ifdef WITH_FREESTYLE
|
||||
if (mr.freestyle_edge_ofs != -1) {
|
||||
if (BM_ELEM_CD_GET_BOOL(eed, mr.freestyle_edge_ofs)) {
|
||||
eattr.e_flag |= VFLAG_EDGE_FREESTYLE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void mesh_render_data_vert_flag(const MeshRenderData &mr,
|
||||
const BMVert *eve,
|
||||
EditLoopData &eattr)
|
||||
{
|
||||
if (eve == mr.eve_act) {
|
||||
eattr.e_flag |= VFLAG_VERT_ACTIVE;
|
||||
}
|
||||
if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
|
||||
eattr.e_flag |= VFLAG_VERT_SELECTED;
|
||||
}
|
||||
/* Use half a byte for value range */
|
||||
if (mr.vert_crease_ofs != -1) {
|
||||
float crease = BM_ELEM_CD_GET_FLOAT(eve, mr.vert_crease_ofs);
|
||||
if (crease > 0) {
|
||||
eattr.crease |= uchar(ceil(crease * 15.0f)) << 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const GPUVertFormat &get_edit_data_format()
|
||||
{
|
||||
static const GPUVertFormat format = []() {
|
||||
GPUVertFormat format{};
|
||||
/* WARNING: Adjust #EditLoopData struct accordingly. */
|
||||
GPU_vertformat_attr_add(&format, "data", gpu::VertAttrType::UINT_8_8_8_8);
|
||||
GPU_vertformat_alias_add(&format, "flag");
|
||||
return format;
|
||||
}();
|
||||
return format;
|
||||
}
|
||||
|
||||
static void extract_edit_data_mesh(const MeshRenderData &mr, MutableSpan<EditLoopData> vbo_data)
|
||||
{
|
||||
MutableSpan corners_data = vbo_data.take_front(mr.corners_num);
|
||||
MutableSpan loose_edge_data = vbo_data.slice(mr.corners_num, mr.loose_edges.size() * 2);
|
||||
MutableSpan loose_vert_data = vbo_data.take_back(mr.loose_verts.size());
|
||||
|
||||
const BMUVOffsets uv_offsets_none = BMUVOFFSETS_NONE;
|
||||
const OffsetIndices faces = mr.faces;
|
||||
const Span<int> corner_verts = mr.corner_verts;
|
||||
const Span<int> corner_edges = mr.corner_edges;
|
||||
threading::parallel_for(faces.index_range(), 2048, [&](const IndexRange range) {
|
||||
for (const int face : range) {
|
||||
EditLoopData face_value = {};
|
||||
if (const BMFace *bm_face = bm_original_face_get(mr, face)) {
|
||||
mesh_render_data_face_flag(mr, bm_face, uv_offsets_none, face_value);
|
||||
}
|
||||
for (const int corner : faces[face]) {
|
||||
EditLoopData &value = corners_data[corner];
|
||||
value = face_value;
|
||||
if (const BMVert *bm_vert = bm_original_vert_get(mr, corner_verts[corner])) {
|
||||
mesh_render_data_vert_flag(mr, bm_vert, value);
|
||||
}
|
||||
if (const BMEdge *bm_edge = bm_original_edge_get(mr, corner_edges[corner])) {
|
||||
mesh_render_data_edge_flag(mr, bm_edge, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const Span<int2> edges = mr.edges;
|
||||
mr.loose_edges.foreach_index(
|
||||
[&](const int edge_i, const int pos) {
|
||||
EditLoopData &value_1 = loose_edge_data[pos * 2 + 0];
|
||||
EditLoopData &value_2 = loose_edge_data[pos * 2 + 1];
|
||||
if (const BMEdge *bm_edge = bm_original_edge_get(mr, edge_i)) {
|
||||
value_1 = {};
|
||||
mesh_render_data_edge_flag(mr, bm_edge, value_1);
|
||||
value_2 = value_1;
|
||||
}
|
||||
else {
|
||||
value_2 = value_1 = {};
|
||||
}
|
||||
const int2 edge = edges[edge_i];
|
||||
if (const BMVert *bm_vert = bm_original_vert_get(mr, edge[0])) {
|
||||
mesh_render_data_vert_flag(mr, bm_vert, value_1);
|
||||
}
|
||||
if (const BMVert *bm_vert = bm_original_vert_get(mr, edge[1])) {
|
||||
mesh_render_data_vert_flag(mr, bm_vert, value_2);
|
||||
}
|
||||
},
|
||||
exec_mode::grain_size(2048));
|
||||
|
||||
mr.loose_verts.foreach_index(
|
||||
[&](const int vert, const int pos) {
|
||||
loose_vert_data[pos] = {};
|
||||
if (const BMVert *eve = bm_original_vert_get(mr, vert)) {
|
||||
mesh_render_data_vert_flag(mr, eve, loose_vert_data[pos]);
|
||||
}
|
||||
},
|
||||
exec_mode::grain_size(2048));
|
||||
}
|
||||
|
||||
static void extract_edit_data_bm(const MeshRenderData &mr, MutableSpan<EditLoopData> vbo_data)
|
||||
{
|
||||
MutableSpan corners_data = vbo_data.take_front(mr.corners_num);
|
||||
MutableSpan loose_edge_data = vbo_data.slice(mr.corners_num, mr.loose_edges.size() * 2);
|
||||
MutableSpan loose_vert_data = vbo_data.take_back(mr.loose_verts.size());
|
||||
|
||||
const BMesh &bm = *mr.bm;
|
||||
const BMUVOffsets uv_offsets_none = BMUVOFFSETS_NONE;
|
||||
|
||||
threading::parallel_for(IndexRange(bm.totface), 2048, [&](const IndexRange range) {
|
||||
for (const int face_index : range) {
|
||||
const BMFace &face = *BM_face_at_index(&const_cast<BMesh &>(bm), face_index);
|
||||
EditLoopData face_value = {};
|
||||
mesh_render_data_face_flag(mr, &face, uv_offsets_none, face_value);
|
||||
const BMLoop *loop = BM_FACE_FIRST_LOOP(&face);
|
||||
for ([[maybe_unused]] const int i : IndexRange(face.len)) {
|
||||
const int index = BM_elem_index_get(loop);
|
||||
EditLoopData &value = corners_data[index];
|
||||
value = face_value;
|
||||
mesh_render_data_edge_flag(mr, loop->e, value);
|
||||
mesh_render_data_vert_flag(mr, loop->v, value);
|
||||
loop = loop->next;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mr.loose_edges.foreach_index(
|
||||
[&](const int edge_i, const int pos) {
|
||||
EditLoopData &value_1 = loose_edge_data[pos * 2 + 0];
|
||||
EditLoopData &value_2 = loose_edge_data[pos * 2 + 1];
|
||||
const BMEdge &edge = *BM_edge_at_index(&const_cast<BMesh &>(bm), edge_i);
|
||||
value_1 = {};
|
||||
mesh_render_data_edge_flag(mr, &edge, value_1);
|
||||
value_2 = value_1;
|
||||
mesh_render_data_vert_flag(mr, edge.v1, value_1);
|
||||
mesh_render_data_vert_flag(mr, edge.v2, value_2);
|
||||
},
|
||||
exec_mode::grain_size(2048));
|
||||
|
||||
mr.loose_verts.foreach_index(
|
||||
[&](const int vert_i, const int pos) {
|
||||
loose_vert_data[pos] = {};
|
||||
const BMVert &vert = *BM_vert_at_index(&const_cast<BMesh &>(bm), vert_i);
|
||||
mesh_render_data_vert_flag(mr, &vert, loose_vert_data[pos]);
|
||||
},
|
||||
exec_mode::grain_size(2048));
|
||||
}
|
||||
|
||||
gpu::VertBufPtr extract_edit_data(const MeshRenderData &mr)
|
||||
{
|
||||
gpu::VertBufPtr vbo = gpu::VertBufPtr(GPU_vertbuf_create_with_format(get_edit_data_format()));
|
||||
const int size = mr.corners_num + mr.loose_indices_num;
|
||||
GPU_vertbuf_data_alloc(*vbo, size);
|
||||
MutableSpan vbo_data = vbo->data<EditLoopData>();
|
||||
if (mr.extract_type == MeshExtractType::Mesh) {
|
||||
extract_edit_data_mesh(mr, vbo_data);
|
||||
}
|
||||
else {
|
||||
extract_edit_data_bm(mr, vbo_data);
|
||||
}
|
||||
return vbo;
|
||||
}
|
||||
|
||||
static void extract_edit_subdiv_data_mesh(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache,
|
||||
MutableSpan<EditLoopData> vbo_data)
|
||||
{
|
||||
const BMUVOffsets uv_offsets_none = BMUVOFFSETS_NONE;
|
||||
const int corners_num = subdiv_cache.num_subdiv_loops;
|
||||
const int loose_edges_num = mr.loose_edges.size();
|
||||
const int verts_per_edge = subdiv_verts_per_coarse_edge(subdiv_cache);
|
||||
const Span<int> subdiv_loop_face_index(subdiv_cache.subdiv_loop_face_index, corners_num);
|
||||
const Span<int> subdiv_loop_vert_index = subdiv_cache.verts_orig_index->data<int>();
|
||||
/* NOTE: #subdiv_loop_edge_index already has the origindex layer baked in. */
|
||||
const Span<int> subdiv_loop_edge_index = subdiv_cache.edges_orig_index->data<int>();
|
||||
|
||||
MutableSpan corners_data = vbo_data.take_front(corners_num);
|
||||
MutableSpan loose_edge_data = vbo_data.slice(corners_num, loose_edges_num * verts_per_edge);
|
||||
MutableSpan loose_vert_data = vbo_data.take_back(mr.loose_verts.size());
|
||||
|
||||
threading::parallel_for(IndexRange(subdiv_cache.num_subdiv_quads), 2048, [&](IndexRange range) {
|
||||
for (const int subdiv_quad : range) {
|
||||
const int coarse_face = subdiv_loop_face_index[subdiv_quad * 4];
|
||||
|
||||
EditLoopData face_value = {};
|
||||
if (const BMFace *bm_face = bm_original_face_get(mr, coarse_face)) {
|
||||
mesh_render_data_face_flag(mr, bm_face, uv_offsets_none, face_value);
|
||||
}
|
||||
for (const int subdiv_corner : IndexRange(subdiv_quad * 4, 4)) {
|
||||
EditLoopData &value = corners_data[subdiv_corner];
|
||||
value = face_value;
|
||||
|
||||
const int vert_origindex = subdiv_loop_vert_index[subdiv_corner];
|
||||
if (vert_origindex != -1) {
|
||||
if (const BMVert *bm_vert = bm_original_vert_get(mr, vert_origindex)) {
|
||||
mesh_render_data_vert_flag(mr, bm_vert, value);
|
||||
}
|
||||
}
|
||||
|
||||
const int edge_origindex = subdiv_loop_edge_index[subdiv_corner];
|
||||
if (edge_origindex != -1) {
|
||||
if (const BMEdge *bm_edge = BM_edge_at_index(mr.bm, edge_origindex)) {
|
||||
mesh_render_data_edge_flag(mr, bm_edge, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const Span<int2> edges = mr.edges;
|
||||
mr.loose_edges.foreach_index(
|
||||
[&](const int edge_i, const int pos) {
|
||||
MutableSpan<EditLoopData> data = loose_edge_data.slice(pos * verts_per_edge,
|
||||
verts_per_edge);
|
||||
if (const BMEdge *edge = bm_original_edge_get(mr, edge_i)) {
|
||||
EditLoopData value{};
|
||||
mesh_render_data_edge_flag(mr, edge, value);
|
||||
data.fill(value);
|
||||
}
|
||||
else {
|
||||
data.fill({});
|
||||
}
|
||||
const int2 edge = edges[edge_i];
|
||||
if (const BMVert *bm_vert = bm_original_vert_get(mr, edge[0])) {
|
||||
mesh_render_data_vert_flag(mr, bm_vert, data.first());
|
||||
}
|
||||
if (const BMVert *bm_vert = bm_original_vert_get(mr, edge[1])) {
|
||||
mesh_render_data_vert_flag(mr, bm_vert, data.last());
|
||||
}
|
||||
},
|
||||
exec_mode::grain_size(2048));
|
||||
|
||||
mr.loose_verts.foreach_index(
|
||||
[&](const int vert, const int pos) {
|
||||
loose_vert_data[pos] = {};
|
||||
if (const BMVert *eve = bm_original_vert_get(mr, vert)) {
|
||||
mesh_render_data_vert_flag(mr, eve, loose_vert_data[pos]);
|
||||
}
|
||||
},
|
||||
exec_mode::grain_size(2048));
|
||||
}
|
||||
|
||||
static void extract_edit_subdiv_data_bm(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache,
|
||||
MutableSpan<EditLoopData> vbo_data)
|
||||
{
|
||||
const BMUVOffsets uv_offsets_none = BMUVOFFSETS_NONE;
|
||||
const int corners_num = subdiv_cache.num_subdiv_loops;
|
||||
const int loose_edges_num = mr.loose_edges.size();
|
||||
const int verts_per_edge = subdiv_verts_per_coarse_edge(subdiv_cache);
|
||||
const Span<int> subdiv_loop_face_index(subdiv_cache.subdiv_loop_face_index, corners_num);
|
||||
const Span<int> subdiv_loop_vert_index = subdiv_cache.verts_orig_index->data<int>();
|
||||
const Span<int> subdiv_loop_edge_index = subdiv_cache.edges_orig_index->data<int>();
|
||||
|
||||
MutableSpan corners_data = vbo_data.take_front(corners_num);
|
||||
MutableSpan loose_edge_data = vbo_data.slice(corners_num, loose_edges_num * verts_per_edge);
|
||||
MutableSpan loose_vert_data = vbo_data.take_back(mr.loose_verts.size());
|
||||
|
||||
BMesh &bm = *mr.bm;
|
||||
threading::parallel_for(IndexRange(subdiv_cache.num_subdiv_quads), 2048, [&](IndexRange range) {
|
||||
for (const int subdiv_quad : range) {
|
||||
const int coarse_face = subdiv_loop_face_index[subdiv_quad * 4];
|
||||
const BMFace *bm_face = BM_face_at_index(&bm, coarse_face);
|
||||
|
||||
EditLoopData face_value = {};
|
||||
mesh_render_data_face_flag(mr, bm_face, uv_offsets_none, face_value);
|
||||
|
||||
for (const int subdiv_corner : IndexRange(subdiv_quad * 4, 4)) {
|
||||
EditLoopData &value = corners_data[subdiv_corner];
|
||||
value = face_value;
|
||||
|
||||
const int vert_origindex = subdiv_loop_vert_index[subdiv_corner];
|
||||
if (vert_origindex != -1) {
|
||||
const BMVert *bm_vert = BM_vert_at_index(mr.bm, vert_origindex);
|
||||
mesh_render_data_vert_flag(mr, bm_vert, value);
|
||||
}
|
||||
|
||||
const int edge_origindex = subdiv_loop_edge_index[subdiv_corner];
|
||||
if (edge_origindex != -1) {
|
||||
const BMEdge *bm_edge = BM_edge_at_index(mr.bm, edge_origindex);
|
||||
mesh_render_data_edge_flag(mr, bm_edge, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mr.loose_edges.foreach_index(
|
||||
[&](const int edge_i, const int pos) {
|
||||
MutableSpan<EditLoopData> data = loose_edge_data.slice(pos * verts_per_edge,
|
||||
verts_per_edge);
|
||||
const BMEdge *edge = BM_edge_at_index(&bm, edge_i);
|
||||
EditLoopData value{};
|
||||
mesh_render_data_edge_flag(mr, edge, value);
|
||||
data.fill(value);
|
||||
mesh_render_data_vert_flag(mr, edge->v1, data.first());
|
||||
mesh_render_data_vert_flag(mr, edge->v2, data.last());
|
||||
},
|
||||
exec_mode::grain_size(2048));
|
||||
|
||||
mr.loose_verts.foreach_index(
|
||||
[&](const int vert_i, const int pos) {
|
||||
loose_vert_data[pos] = {};
|
||||
const BMVert *vert = BM_vert_at_index(&bm, vert_i);
|
||||
mesh_render_data_vert_flag(mr, vert, loose_vert_data[pos]);
|
||||
},
|
||||
exec_mode::grain_size(2048));
|
||||
}
|
||||
|
||||
gpu::VertBufPtr extract_edit_data_subdiv(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache)
|
||||
{
|
||||
gpu::VertBufPtr vbo = gpu::VertBufPtr(GPU_vertbuf_create_with_format(get_edit_data_format()));
|
||||
const int size = subdiv_full_vbo_size(mr, subdiv_cache);
|
||||
GPU_vertbuf_data_alloc(*vbo, size);
|
||||
MutableSpan vbo_data = vbo->data<EditLoopData>();
|
||||
if (mr.extract_type == MeshExtractType::Mesh) {
|
||||
extract_edit_subdiv_data_mesh(mr, subdiv_cache, vbo_data);
|
||||
}
|
||||
else {
|
||||
extract_edit_subdiv_data_bm(mr, subdiv_cache, vbo_data);
|
||||
}
|
||||
return vbo;
|
||||
}
|
||||
|
||||
} // namespace blender::draw
|
||||
@@ -0,0 +1,229 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "BKE_mesh.hh"
|
||||
|
||||
#include "extract_mesh.hh"
|
||||
|
||||
#include "draw_cache_impl.hh"
|
||||
|
||||
#include "draw_subdivision.hh"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name Extract Edit UV Data / Flags
|
||||
* \{ */
|
||||
|
||||
static const GPUVertFormat &edituv_data_format()
|
||||
{
|
||||
static const GPUVertFormat format = []() {
|
||||
GPUVertFormat format{};
|
||||
/* WARNING: Adjust #EditLoopData struct accordingly. */
|
||||
GPU_vertformat_attr_add(&format, "data", gpu::VertAttrType::UINT_8_8_8_8);
|
||||
GPU_vertformat_alias_add(&format, "flag");
|
||||
return format;
|
||||
}();
|
||||
return format;
|
||||
}
|
||||
|
||||
static void extract_edituv_data_bm(const MeshRenderData &mr, MutableSpan<EditLoopData> vbo_data)
|
||||
{
|
||||
const BMesh &bm = *mr.bm;
|
||||
const BMUVOffsets offsets = BM_uv_map_offsets_get(&bm);
|
||||
threading::parallel_for(IndexRange(bm.totface), 2048, [&](const IndexRange range) {
|
||||
for (const int face_index : range) {
|
||||
const BMFace &face = *BM_face_at_index(&const_cast<BMesh &>(bm), face_index);
|
||||
EditLoopData face_value = {};
|
||||
mesh_render_data_face_flag(mr, &face, offsets, face_value);
|
||||
const BMLoop *loop = BM_FACE_FIRST_LOOP(&face);
|
||||
for ([[maybe_unused]] const int i : IndexRange(face.len)) {
|
||||
const int index = BM_elem_index_get(loop);
|
||||
EditLoopData &value = vbo_data[index];
|
||||
value = face_value;
|
||||
mesh_render_data_loop_flag(mr, loop, offsets, value);
|
||||
mesh_render_data_loop_edge_flag(mr, loop, offsets, value);
|
||||
loop = loop->next;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void extract_edituv_data_mesh(const MeshRenderData &mr, MutableSpan<EditLoopData> vbo_data)
|
||||
{
|
||||
const BMesh &bm = *mr.bm;
|
||||
const BMUVOffsets offsets = BM_uv_map_offsets_get(&bm);
|
||||
const OffsetIndices faces = mr.faces;
|
||||
const Span<int> corner_verts = mr.corner_verts;
|
||||
const Span<int> corner_edges = mr.corner_edges;
|
||||
threading::parallel_for(faces.index_range(), 2048, [&](const IndexRange range) {
|
||||
for (const int face_index : range) {
|
||||
const IndexRange face = faces[face_index];
|
||||
BMFace *face_orig = bm_original_face_get(mr, face_index);
|
||||
if (!face_orig) {
|
||||
vbo_data.slice(face).fill({});
|
||||
continue;
|
||||
}
|
||||
for (const int corner : face) {
|
||||
EditLoopData &value = vbo_data[corner];
|
||||
value = {};
|
||||
BMVert *vert = bm_original_vert_get(mr, corner_verts[corner]);
|
||||
BMEdge *edge = bm_original_edge_get(mr, corner_edges[corner]);
|
||||
if (edge && vert) {
|
||||
/* Loop on an edge endpoint. */
|
||||
BMLoop *l = BM_face_edge_share_loop(face_orig, edge);
|
||||
mesh_render_data_loop_flag(mr, l, offsets, value);
|
||||
mesh_render_data_loop_edge_flag(mr, l, offsets, value);
|
||||
}
|
||||
else {
|
||||
if (edge == nullptr) {
|
||||
/* Find if the loop's vert is not part of an edit edge.
|
||||
* For this, we check if the previous loop was on an edge. */
|
||||
const int corner_prev = bke::mesh::face_corner_prev(face, corner);
|
||||
edge = bm_original_edge_get(mr, corner_edges[corner_prev]);
|
||||
}
|
||||
if (edge) {
|
||||
/* Mapped points on an edge between two edit verts. */
|
||||
BMLoop *l = BM_face_edge_share_loop(face_orig, edge);
|
||||
mesh_render_data_loop_edge_flag(mr, l, offsets, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
gpu::VertBufPtr extract_edituv_data(const MeshRenderData &mr)
|
||||
{
|
||||
gpu::VertBufPtr vbo = gpu::VertBufPtr(GPU_vertbuf_create_with_format(edituv_data_format()));
|
||||
GPU_vertbuf_data_alloc(*vbo, mr.corners_num);
|
||||
MutableSpan vbo_data = vbo->data<EditLoopData>();
|
||||
|
||||
if (mr.extract_type == MeshExtractType::BMesh) {
|
||||
extract_edituv_data_bm(mr, vbo_data);
|
||||
}
|
||||
else {
|
||||
extract_edituv_data_mesh(mr, vbo_data);
|
||||
}
|
||||
return vbo;
|
||||
}
|
||||
|
||||
static void extract_edituv_data_iter_subdiv_bm(const MeshRenderData &mr,
|
||||
const BMUVOffsets &offsets,
|
||||
const Span<int> subdiv_loop_vert_index,
|
||||
const Span<int> subdiv_loop_edge_index,
|
||||
const int subdiv_quad_index,
|
||||
const BMFace *coarse_quad,
|
||||
MutableSpan<EditLoopData> vbo_data)
|
||||
{
|
||||
|
||||
uint start_loop_idx = subdiv_quad_index * 4;
|
||||
uint end_loop_idx = (subdiv_quad_index + 1) * 4;
|
||||
EditLoopData edit_loop_data_face = {};
|
||||
mesh_render_data_face_flag(mr, coarse_quad, offsets, edit_loop_data_face);
|
||||
for (uint i = start_loop_idx; i < end_loop_idx; i++) {
|
||||
const int vert_origindex = subdiv_loop_vert_index[i];
|
||||
int edge_origindex = subdiv_loop_edge_index[i];
|
||||
|
||||
EditLoopData *edit_loop_data = &vbo_data[i];
|
||||
*edit_loop_data = edit_loop_data_face;
|
||||
|
||||
if (vert_origindex != -1 && edge_origindex != -1) {
|
||||
BMEdge *eed = BM_edge_at_index(mr.bm, edge_origindex);
|
||||
/* Loop on an edge endpoint. */
|
||||
BMLoop *l = BM_face_edge_share_loop(const_cast<BMFace *>(coarse_quad), eed);
|
||||
mesh_render_data_loop_flag(mr, l, offsets, *edit_loop_data);
|
||||
mesh_render_data_loop_edge_flag(mr, l, offsets, *edit_loop_data);
|
||||
}
|
||||
else {
|
||||
if (edge_origindex == -1) {
|
||||
/* Find if the loop's vert is not part of an edit edge.
|
||||
* For this, we check if the previous loop was on an edge. */
|
||||
const uint loop_index_last = (i == start_loop_idx) ? end_loop_idx - 1 : i - 1;
|
||||
edge_origindex = subdiv_loop_edge_index[loop_index_last];
|
||||
}
|
||||
if (edge_origindex != -1) {
|
||||
/* Mapped points on an edge between two edit verts. */
|
||||
BMEdge *eed = BM_edge_at_index(mr.bm, edge_origindex);
|
||||
BMLoop *l = BM_face_edge_share_loop(const_cast<BMFace *>(coarse_quad), eed);
|
||||
mesh_render_data_loop_edge_flag(mr, l, offsets, *edit_loop_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void extract_edituv_subdiv_data_bm(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache,
|
||||
MutableSpan<EditLoopData> vbo_data)
|
||||
{
|
||||
const int corners_num = subdiv_cache.num_subdiv_loops;
|
||||
const Span<int> subdiv_loop_face_index(subdiv_cache.subdiv_loop_face_index, corners_num);
|
||||
const Span<int> subdiv_loop_vert_index = subdiv_cache.verts_orig_index->data<int>();
|
||||
/* NOTE: #subdiv_loop_edge_index already has the origindex layer baked in. */
|
||||
const Span<int> subdiv_loop_edge_index = subdiv_cache.edges_orig_index->data<int>();
|
||||
|
||||
const BMUVOffsets offsets = BM_uv_map_offsets_get(mr.bm);
|
||||
threading::parallel_for(IndexRange(subdiv_cache.num_subdiv_quads), 2048, [&](IndexRange range) {
|
||||
for (const int subdiv_quad : range) {
|
||||
const int coarse_face = subdiv_loop_face_index[subdiv_quad * 4];
|
||||
extract_edituv_data_iter_subdiv_bm(mr,
|
||||
offsets,
|
||||
subdiv_loop_vert_index,
|
||||
subdiv_loop_edge_index,
|
||||
subdiv_quad,
|
||||
BM_face_at_index(mr.bm, coarse_face),
|
||||
vbo_data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void extract_edituv_subdiv_data_mesh(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache,
|
||||
MutableSpan<EditLoopData> vbo_data)
|
||||
{
|
||||
const int corners_num = subdiv_cache.num_subdiv_loops;
|
||||
const Span<int> subdiv_loop_face_index(subdiv_cache.subdiv_loop_face_index, corners_num);
|
||||
const Span<int> subdiv_loop_vert_index = subdiv_cache.verts_orig_index->data<int>();
|
||||
/* NOTE: #subdiv_loop_edge_index already has the origindex layer baked in. */
|
||||
const Span<int> subdiv_loop_edge_index = subdiv_cache.edges_orig_index->data<int>();
|
||||
|
||||
const BMUVOffsets offsets = BM_uv_map_offsets_get(mr.bm);
|
||||
threading::parallel_for(IndexRange(subdiv_cache.num_subdiv_quads), 2048, [&](IndexRange range) {
|
||||
for (const int subdiv_quad : range) {
|
||||
const int coarse_face = subdiv_loop_face_index[subdiv_quad * 4];
|
||||
extract_edituv_data_iter_subdiv_bm(mr,
|
||||
offsets,
|
||||
subdiv_loop_vert_index,
|
||||
subdiv_loop_edge_index,
|
||||
subdiv_quad,
|
||||
bm_original_face_get(mr, coarse_face),
|
||||
vbo_data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
gpu::VertBufPtr extract_edituv_data_subdiv(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache)
|
||||
{
|
||||
gpu::VertBufPtr vbo = gpu::VertBufPtr(GPU_vertbuf_create_with_format(edituv_data_format()));
|
||||
const int size = subdiv_cache.num_subdiv_loops;
|
||||
GPU_vertbuf_data_alloc(*vbo, size);
|
||||
MutableSpan vbo_data = vbo->data<EditLoopData>();
|
||||
|
||||
if (mr.extract_type == MeshExtractType::BMesh) {
|
||||
extract_edituv_subdiv_data_bm(mr, subdiv_cache, vbo_data);
|
||||
}
|
||||
else {
|
||||
extract_edituv_subdiv_data_mesh(mr, subdiv_cache, vbo_data);
|
||||
}
|
||||
return vbo;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::draw
|
||||
@@ -0,0 +1,268 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "BLI_math_vector.h"
|
||||
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_mesh.hh"
|
||||
|
||||
#include "extract_mesh.hh"
|
||||
|
||||
#include "draw_subdivision.hh"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
struct UVStretchAngle {
|
||||
float angle;
|
||||
int16_t uv_angles[2];
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(UVStretchAngle, 4)
|
||||
|
||||
struct MeshExtract_StretchAngle_Data {
|
||||
UVStretchAngle *vbo_data;
|
||||
const float2 *uv;
|
||||
float auv[2][2], last_auv[2];
|
||||
float av[2][3], last_av[3];
|
||||
int cd_ofs;
|
||||
};
|
||||
|
||||
static void compute_normalize_edge_vectors(float auv[2][2],
|
||||
float av[2][3],
|
||||
const float uv[2],
|
||||
const float uv_prev[2],
|
||||
const float co[3],
|
||||
const float co_prev[3])
|
||||
{
|
||||
/* Move previous edge. */
|
||||
copy_v2_v2(auv[0], auv[1]);
|
||||
copy_v3_v3(av[0], av[1]);
|
||||
/* 2d edge */
|
||||
sub_v2_v2v2(auv[1], uv_prev, uv);
|
||||
normalize_v2(auv[1]);
|
||||
/* 3d edge */
|
||||
sub_v3_v3v3(av[1], co_prev, co);
|
||||
normalize_v3(av[1]);
|
||||
}
|
||||
|
||||
static short v2_to_short_angle(const float v[2])
|
||||
{
|
||||
return atan2f(v[1], v[0]) * float(M_1_PI) * SHRT_MAX;
|
||||
}
|
||||
|
||||
static void edituv_get_edituv_stretch_angle(float auv[2][2],
|
||||
const float av[2][3],
|
||||
UVStretchAngle *r_stretch)
|
||||
{
|
||||
/* Send UVs to the shader and let it compute the aspect corrected angle. */
|
||||
r_stretch->uv_angles[0] = v2_to_short_angle(auv[0]);
|
||||
r_stretch->uv_angles[1] = v2_to_short_angle(auv[1]);
|
||||
/* Compute 3D angle here. */
|
||||
r_stretch->angle = angle_normalized_v3v3(av[0], av[1]) * float(M_1_PI);
|
||||
|
||||
#if 0 /* here for reference, this is done in shader now. */
|
||||
float uvang = angle_normalized_v2v2(auv0, auv1);
|
||||
float ang = angle_normalized_v3v3(av0, av1);
|
||||
float stretch = fabsf(uvang - ang) / float(M_PI);
|
||||
return 1.0f - pow2f(1.0f - stretch);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void extract_uv_stretch_angle_bm(const MeshRenderData &mr,
|
||||
MutableSpan<UVStretchAngle> vbo_data)
|
||||
{
|
||||
const BMesh &bm = *mr.bm;
|
||||
const StringRef active_name = mr.mesh->active_uv_map_name();
|
||||
const int uv_offset = CustomData_get_offset_named(&bm.ldata, CD_PROP_FLOAT2, active_name);
|
||||
|
||||
float auv[2][2], last_auv[2];
|
||||
float av[2][3], last_av[3];
|
||||
|
||||
const BMFace *face;
|
||||
BMIter f_iter;
|
||||
BM_ITER_MESH (face, &f_iter, &const_cast<BMesh &>(bm), BM_FACES_OF_MESH) {
|
||||
BMLoop *l_iter, *l_first;
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(face);
|
||||
do {
|
||||
const int l_index = BM_elem_index_get(l_iter);
|
||||
|
||||
const float (*luv)[2], (*luv_next)[2];
|
||||
BMLoop *l_next = l_iter->next;
|
||||
if (l_iter == BM_FACE_FIRST_LOOP(face)) {
|
||||
/* First loop in face. */
|
||||
BMLoop *l_tmp = l_iter->prev;
|
||||
BMLoop *l_next_tmp = l_iter;
|
||||
luv = BM_ELEM_CD_GET_FLOAT2_P(l_tmp, uv_offset);
|
||||
luv_next = BM_ELEM_CD_GET_FLOAT2_P(l_next_tmp, uv_offset);
|
||||
compute_normalize_edge_vectors(auv,
|
||||
av,
|
||||
*luv,
|
||||
*luv_next,
|
||||
bm_vert_co_get(mr, l_tmp->v),
|
||||
bm_vert_co_get(mr, l_next_tmp->v));
|
||||
/* Save last edge. */
|
||||
copy_v2_v2(last_auv, auv[1]);
|
||||
copy_v3_v3(last_av, av[1]);
|
||||
}
|
||||
if (l_next == BM_FACE_FIRST_LOOP(face)) {
|
||||
/* Move previous edge. */
|
||||
copy_v2_v2(auv[0], auv[1]);
|
||||
copy_v3_v3(av[0], av[1]);
|
||||
/* Copy already calculated last edge. */
|
||||
copy_v2_v2(auv[1], last_auv);
|
||||
copy_v3_v3(av[1], last_av);
|
||||
}
|
||||
else {
|
||||
luv = BM_ELEM_CD_GET_FLOAT2_P(l_iter, uv_offset);
|
||||
luv_next = BM_ELEM_CD_GET_FLOAT2_P(l_next, uv_offset);
|
||||
compute_normalize_edge_vectors(auv,
|
||||
av,
|
||||
*luv,
|
||||
*luv_next,
|
||||
bm_vert_co_get(mr, l_iter->v),
|
||||
bm_vert_co_get(mr, l_next->v));
|
||||
}
|
||||
edituv_get_edituv_stretch_angle(auv, av, &vbo_data[l_index]);
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
}
|
||||
}
|
||||
|
||||
static void extract_uv_stretch_angle_mesh(const MeshRenderData &mr,
|
||||
MutableSpan<UVStretchAngle> vbo_data)
|
||||
{
|
||||
const Span<float3> positions = mr.vert_positions;
|
||||
const OffsetIndices faces = mr.faces;
|
||||
const Span<int> corner_verts = mr.corner_verts;
|
||||
const Mesh &mesh = *mr.mesh;
|
||||
const bke::AttributeAccessor attributes = mesh.attributes();
|
||||
const StringRef name = mesh.active_uv_map_name();
|
||||
const VArraySpan uv_map = *attributes.lookup<float2>(name, bke::AttrDomain::Corner);
|
||||
|
||||
float auv[2][2], last_auv[2];
|
||||
float av[2][3], last_av[3];
|
||||
|
||||
for (const int face_index : faces.index_range()) {
|
||||
const IndexRange face = faces[face_index];
|
||||
const int corner_end = face.start() + face.size();
|
||||
for (int corner = face.start(); corner < corner_end; corner += 1) {
|
||||
int l_next = corner + 1;
|
||||
if (corner == face.start()) {
|
||||
/* First loop in face. */
|
||||
const int corner_last = corner_end - 1;
|
||||
const int l_next_tmp = face.start();
|
||||
compute_normalize_edge_vectors(auv,
|
||||
av,
|
||||
uv_map[corner_last],
|
||||
uv_map[l_next_tmp],
|
||||
positions[corner_verts[corner_last]],
|
||||
positions[corner_verts[l_next_tmp]]);
|
||||
/* Save last edge. */
|
||||
copy_v2_v2(last_auv, auv[1]);
|
||||
copy_v3_v3(last_av, av[1]);
|
||||
}
|
||||
if (l_next == corner_end) {
|
||||
l_next = face.start();
|
||||
/* Move previous edge. */
|
||||
copy_v2_v2(auv[0], auv[1]);
|
||||
copy_v3_v3(av[0], av[1]);
|
||||
/* Copy already calculated last edge. */
|
||||
copy_v2_v2(auv[1], last_auv);
|
||||
copy_v3_v3(av[1], last_av);
|
||||
}
|
||||
else {
|
||||
compute_normalize_edge_vectors(auv,
|
||||
av,
|
||||
uv_map[corner],
|
||||
uv_map[l_next],
|
||||
positions[corner_verts[corner]],
|
||||
positions[corner_verts[l_next]]);
|
||||
}
|
||||
edituv_get_edituv_stretch_angle(auv, av, &vbo_data[corner]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gpu::VertBufPtr extract_edituv_stretch_angle(const MeshRenderData &mr)
|
||||
{
|
||||
static const GPUVertFormat format = []() {
|
||||
GPUVertFormat format{};
|
||||
/* Waning: adjust #UVStretchAngle struct accordingly. */
|
||||
GPU_vertformat_attr_add(&format, "angle", gpu::VertAttrType::SFLOAT_32);
|
||||
GPU_vertformat_attr_add(&format, "uv_angles", gpu::VertAttrType::SNORM_16_16);
|
||||
return format;
|
||||
}();
|
||||
|
||||
gpu::VertBufPtr vbo = gpu::VertBufPtr(GPU_vertbuf_create_with_format(format));
|
||||
GPU_vertbuf_data_alloc(*vbo, mr.corners_num);
|
||||
MutableSpan vbo_data = vbo->data<UVStretchAngle>();
|
||||
|
||||
if (mr.extract_type == MeshExtractType::BMesh) {
|
||||
extract_uv_stretch_angle_bm(mr, vbo_data);
|
||||
}
|
||||
else {
|
||||
extract_uv_stretch_angle_mesh(mr, vbo_data);
|
||||
}
|
||||
return vbo;
|
||||
}
|
||||
|
||||
static const GPUVertFormat &get_edituv_stretch_angle_format_subdiv()
|
||||
{
|
||||
static const GPUVertFormat format = []() {
|
||||
GPUVertFormat format{};
|
||||
/* Waning: adjust #UVStretchAngle struct accordingly. */
|
||||
GPU_vertformat_attr_add(&format, "angle", gpu::VertAttrType::SFLOAT_32);
|
||||
GPU_vertformat_attr_add(&format, "uv_angles", gpu::VertAttrType::SFLOAT_32_32);
|
||||
return format;
|
||||
}();
|
||||
return format;
|
||||
}
|
||||
|
||||
gpu::VertBufPtr extract_edituv_stretch_angle_subdiv(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache,
|
||||
const MeshBatchCache &cache)
|
||||
{
|
||||
gpu::VertBufPtr vbo = gpu::VertBufPtr(GPU_vertbuf_create_on_device(
|
||||
get_edituv_stretch_angle_format_subdiv(), subdiv_cache.num_subdiv_loops));
|
||||
|
||||
gpu::VertBuf *pos = cache.final.buff.vbos.lookup(VBOType::Position).get();
|
||||
gpu::VertBuf *uvs = cache.final.buff.vbos.lookup(VBOType::UVs).get();
|
||||
|
||||
/* It may happen that the data for the UV editor is requested before (as a separate draw update)
|
||||
* the data for the mesh when switching to the `UV Editing` workspace, and therefore the position
|
||||
* buffer might not be created yet. In this case, create a buffer it locally, the subdivision
|
||||
* data should already be evaluated if we are here. This can happen if the subsurf modifier is
|
||||
* only enabled in edit-mode. See #96338. */
|
||||
if (!pos) {
|
||||
pos = GPU_vertbuf_calloc();
|
||||
static const GPUVertFormat pos_format = GPU_vertformat_from_attribute(
|
||||
"pos", gpu::VertAttrType::SFLOAT_32_32_32);
|
||||
GPU_vertbuf_init_build_on_device(*pos, pos_format, subdiv_full_vbo_size(mr, subdiv_cache));
|
||||
draw_subdiv_extract_pos(subdiv_cache, pos, nullptr);
|
||||
}
|
||||
|
||||
/* UVs are stored contiguously so we need to compute the offset in the UVs buffer for the active
|
||||
* UV layer. */
|
||||
|
||||
VectorSet<std::string> uv_layers = cache.cd_used.uv;
|
||||
/* HACK to fix #68857 */
|
||||
if (mr.extract_type == MeshExtractType::BMesh && cache.cd_used.edit_uv == 1) {
|
||||
const StringRef active_name = mr.mesh->active_uv_map_name();
|
||||
if (!active_name.is_empty()) {
|
||||
uv_layers.add_as(active_name);
|
||||
}
|
||||
}
|
||||
|
||||
int uvs_offset = uv_layers.index_of(mr.mesh->active_uv_map_name());
|
||||
|
||||
/* The data is at `offset * num loops`, and we have 2 values per index. */
|
||||
uvs_offset *= subdiv_cache.num_subdiv_loops * 2;
|
||||
|
||||
draw_subdiv_build_edituv_stretch_angle_buffer(subdiv_cache, pos, uvs, uvs_offset, vbo.get());
|
||||
return vbo;
|
||||
}
|
||||
|
||||
} // namespace blender::draw
|
||||
@@ -0,0 +1,158 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "BLI_math_geom.h"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_mesh.hh"
|
||||
|
||||
#include "extract_mesh.hh"
|
||||
|
||||
#include "draw_subdivision.hh"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
BLI_INLINE float area_ratio_get(float area, float uvarea)
|
||||
{
|
||||
if (area >= FLT_EPSILON && uvarea >= FLT_EPSILON) {
|
||||
return uvarea / area;
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
BLI_INLINE float area_ratio_to_stretch(float ratio, float tot_ratio)
|
||||
{
|
||||
ratio *= tot_ratio;
|
||||
return (ratio > 1.0f) ? (1.0f / ratio) : ratio;
|
||||
}
|
||||
|
||||
struct AreaInfo {
|
||||
float tot_area = 0.0f;
|
||||
float tot_uv_area = 0.0f;
|
||||
};
|
||||
static AreaInfo compute_area_ratio(const MeshRenderData &mr, MutableSpan<float> r_area_ratio)
|
||||
{
|
||||
if (mr.extract_type == MeshExtractType::BMesh) {
|
||||
const BMesh &bm = *mr.bm;
|
||||
const StringRef active_name = mr.mesh->active_uv_map_name();
|
||||
const int uv_offset = CustomData_get_offset_named(&bm.ldata, CD_PROP_FLOAT2, active_name);
|
||||
return threading::parallel_reduce(
|
||||
IndexRange(bm.totface),
|
||||
1024,
|
||||
AreaInfo{},
|
||||
[&](const IndexRange range, AreaInfo info) {
|
||||
for (const int face_index : range) {
|
||||
const BMFace &face = *BM_face_at_index(&const_cast<BMesh &>(bm), face_index);
|
||||
const float area = BM_face_calc_area(&face);
|
||||
const float uvarea = BM_face_calc_area_uv(&face, uv_offset);
|
||||
info.tot_area += area;
|
||||
info.tot_uv_area += uvarea;
|
||||
r_area_ratio[face_index] = area_ratio_get(area, uvarea);
|
||||
}
|
||||
return info;
|
||||
},
|
||||
[](const AreaInfo &a, const AreaInfo &b) {
|
||||
return AreaInfo{a.tot_area + b.tot_area, a.tot_uv_area + b.tot_uv_area};
|
||||
});
|
||||
}
|
||||
|
||||
const Span<float3> positions = mr.vert_positions;
|
||||
const OffsetIndices<int> faces = mr.faces;
|
||||
const Span<int> corner_verts = mr.corner_verts;
|
||||
const Mesh &mesh = *mr.mesh;
|
||||
const bke::AttributeAccessor attributes = mesh.attributes();
|
||||
const StringRef name = mesh.active_uv_map_name();
|
||||
const VArraySpan uv_map = *attributes.lookup<float2>(name, bke::AttrDomain::Corner);
|
||||
|
||||
return threading::parallel_reduce(
|
||||
faces.index_range(),
|
||||
1024,
|
||||
AreaInfo{},
|
||||
[&](const IndexRange range, AreaInfo info) {
|
||||
for (const int face_index : range) {
|
||||
const IndexRange face = faces[face_index];
|
||||
const float area = bke::mesh::face_area_calc(positions, corner_verts.slice(face));
|
||||
float uvarea = area_poly_v2(reinterpret_cast<const float (*)[2]>(&uv_map[face.start()]),
|
||||
face.size());
|
||||
info.tot_area += area;
|
||||
info.tot_uv_area += uvarea;
|
||||
r_area_ratio[face_index] = area_ratio_get(area, uvarea);
|
||||
}
|
||||
return info;
|
||||
},
|
||||
[](const AreaInfo &a, const AreaInfo &b) {
|
||||
return AreaInfo{a.tot_area + b.tot_area, a.tot_uv_area + b.tot_uv_area};
|
||||
});
|
||||
}
|
||||
|
||||
gpu::VertBufPtr extract_edituv_stretch_area(const MeshRenderData &mr,
|
||||
float &tot_area,
|
||||
float &tot_uv_area)
|
||||
{
|
||||
Array<float> area_ratio(mr.faces_num);
|
||||
const AreaInfo info = compute_area_ratio(mr, area_ratio);
|
||||
tot_area = info.tot_area;
|
||||
tot_uv_area = info.tot_uv_area;
|
||||
|
||||
static const GPUVertFormat format = GPU_vertformat_from_attribute("ratio",
|
||||
gpu::VertAttrType::SFLOAT_32);
|
||||
gpu::VertBufPtr vbo = gpu::VertBufPtr(GPU_vertbuf_create_with_format(format));
|
||||
GPU_vertbuf_data_alloc(*vbo, mr.corners_num);
|
||||
MutableSpan<float> vbo_data = vbo->data<float>();
|
||||
|
||||
const int64_t bytes = area_ratio.as_span().size_in_bytes() + vbo_data.size_in_bytes();
|
||||
threading::memory_bandwidth_bound_task(bytes, [&]() {
|
||||
if (mr.extract_type == MeshExtractType::BMesh) {
|
||||
const BMesh &bm = *mr.bm;
|
||||
threading::parallel_for(IndexRange(bm.totface), 2048, [&](const IndexRange range) {
|
||||
for (const int face_index : range) {
|
||||
const BMFace &face = *BM_face_at_index(&const_cast<BMesh &>(bm), face_index);
|
||||
const IndexRange face_range(BM_elem_index_get(BM_FACE_FIRST_LOOP(&face)), face.len);
|
||||
vbo_data.slice(face_range).fill(area_ratio[face_index]);
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
BLI_assert(mr.extract_type == MeshExtractType::Mesh);
|
||||
const OffsetIndices<int> faces = mr.faces;
|
||||
threading::parallel_for(faces.index_range(), 2048, [&](const IndexRange range) {
|
||||
for (const int face : range) {
|
||||
vbo_data.slice(faces[face]).fill(area_ratio[face]);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
return vbo;
|
||||
}
|
||||
|
||||
gpu::VertBufPtr extract_edituv_stretch_area_subdiv(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache,
|
||||
float &tot_area,
|
||||
float &tot_uv_area)
|
||||
{
|
||||
static const GPUVertFormat format = GPU_vertformat_from_attribute("ratio",
|
||||
gpu::VertAttrType::SFLOAT_32);
|
||||
gpu::VertBufPtr vbo = gpu::VertBufPtr(
|
||||
GPU_vertbuf_create_on_device(format, subdiv_cache.num_subdiv_loops));
|
||||
|
||||
gpu::VertBuf *coarse_vbo = GPU_vertbuf_calloc();
|
||||
GPU_vertbuf_init_with_format(*coarse_vbo, format);
|
||||
GPU_vertbuf_data_alloc(*coarse_vbo, mr.faces_num);
|
||||
MutableSpan coarse_vbo_data = coarse_vbo->data<float>();
|
||||
const AreaInfo info = compute_area_ratio(mr, coarse_vbo_data);
|
||||
tot_area = info.tot_area;
|
||||
tot_uv_area = info.tot_uv_area;
|
||||
|
||||
draw_subdiv_build_edituv_stretch_area_buffer(subdiv_cache, coarse_vbo, vbo.get());
|
||||
|
||||
GPU_vertbuf_discard(coarse_vbo);
|
||||
return vbo;
|
||||
}
|
||||
|
||||
} // namespace blender::draw
|
||||
@@ -0,0 +1,58 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "extract_mesh.hh"
|
||||
|
||||
#include "draw_cache_impl.hh"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
gpu::VertBufPtr extract_face_dots_edituv_data(const MeshRenderData &mr)
|
||||
{
|
||||
static const GPUVertFormat format = []() {
|
||||
GPUVertFormat format{};
|
||||
GPU_vertformat_attr_add(&format, "data", gpu::VertAttrType::UINT_8_8_8_8);
|
||||
GPU_vertformat_alias_add(&format, "flag");
|
||||
return format;
|
||||
}();
|
||||
gpu::VertBufPtr vbo = gpu::VertBufPtr(GPU_vertbuf_create_with_format(format));
|
||||
GPU_vertbuf_data_alloc(*vbo, mr.faces_num);
|
||||
MutableSpan vbo_data = vbo->data<EditLoopData>();
|
||||
const BMesh &bm = *mr.bm;
|
||||
const BMUVOffsets offsets = BM_uv_map_offsets_get(&bm);
|
||||
if (mr.extract_type == MeshExtractType::BMesh) {
|
||||
threading::parallel_for(IndexRange(bm.totface), 2048, [&](const IndexRange range) {
|
||||
for (const int face_index : range) {
|
||||
const BMFace &face = *BM_face_at_index(&const_cast<BMesh &>(bm), face_index);
|
||||
vbo_data[face_index] = {};
|
||||
mesh_render_data_face_flag(mr, &face, offsets, vbo_data[face_index]);
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
if (mr.orig_index_face) {
|
||||
const Span<int> orig_index_face(mr.orig_index_face, mr.faces_num);
|
||||
threading::parallel_for(IndexRange(mr.faces_num), 4096, [&](const IndexRange range) {
|
||||
for (const int face : range) {
|
||||
vbo_data[face] = {};
|
||||
if (orig_index_face[face] == ORIGINDEX_NONE) {
|
||||
continue;
|
||||
}
|
||||
const BMFace *orig_face = bm_original_face_get(mr, face);
|
||||
mesh_render_data_face_flag(mr, orig_face, offsets, vbo_data[face]);
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
vbo_data.fill({});
|
||||
}
|
||||
}
|
||||
return vbo;
|
||||
}
|
||||
|
||||
} // namespace blender::draw
|
||||
@@ -0,0 +1,92 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "extract_mesh.hh"
|
||||
|
||||
#include "GPU_attribute_convert.hh"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
#define NOR_AND_FLAG_DEFAULT 0
|
||||
#define NOR_AND_FLAG_SELECT 1
|
||||
#define NOR_AND_FLAG_ACTIVE -1
|
||||
#define NOR_AND_FLAG_HIDDEN -2
|
||||
|
||||
template<typename GPUType>
|
||||
static void extract_face_dot_normals_mesh(const MeshRenderData &mr, MutableSpan<GPUType> normals)
|
||||
{
|
||||
gpu::convert_normals(mr.face_normals, normals);
|
||||
const GPUType invalid_normal = gpu::convert_normal<GPUType>(float3(0));
|
||||
threading::parallel_for(IndexRange(mr.faces_num), 4096, [&](const IndexRange range) {
|
||||
for (const int i : range) {
|
||||
const BMFace *face = bm_original_face_get(mr, i);
|
||||
if (!face || BM_elem_flag_test(face, BM_ELEM_HIDDEN)) {
|
||||
normals[i] = invalid_normal;
|
||||
normals[i].w = NOR_AND_FLAG_HIDDEN;
|
||||
}
|
||||
else if (BM_elem_flag_test(face, BM_ELEM_SELECT)) {
|
||||
normals[i].w = (face == mr.efa_act) ? NOR_AND_FLAG_ACTIVE : NOR_AND_FLAG_SELECT;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template<typename GPUType>
|
||||
void extract_face_dot_normals_bm(const MeshRenderData &mr, MutableSpan<GPUType> normals)
|
||||
{
|
||||
const GPUType invalid_normal = gpu::convert_normal<GPUType>(float3(0));
|
||||
threading::parallel_for(IndexRange(mr.faces_num), 4096, [&](const IndexRange range) {
|
||||
for (const int i : range) {
|
||||
BMFace *face = BM_face_at_index(mr.bm, i);
|
||||
if (BM_elem_flag_test(face, BM_ELEM_HIDDEN)) {
|
||||
normals[i] = invalid_normal;
|
||||
normals[i].w = NOR_AND_FLAG_HIDDEN;
|
||||
}
|
||||
else {
|
||||
normals[i] = gpu::convert_normal<GPUType>(bm_face_no_get(mr, face));
|
||||
normals[i].w = (BM_elem_flag_test(face, BM_ELEM_SELECT) ?
|
||||
((face == mr.efa_act) ? NOR_AND_FLAG_ACTIVE : NOR_AND_FLAG_SELECT) :
|
||||
NOR_AND_FLAG_DEFAULT);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
gpu::VertBufPtr extract_face_dot_normals(const MeshRenderData &mr, const bool use_hq)
|
||||
{
|
||||
if (use_hq) {
|
||||
static const GPUVertFormat format = GPU_vertformat_from_attribute(
|
||||
"norAndFlag", gpu::VertAttrType::SNORM_16_16_16_16);
|
||||
gpu::VertBufPtr vbo = gpu::VertBufPtr(GPU_vertbuf_create_with_format(format));
|
||||
GPU_vertbuf_data_alloc(*vbo, mr.faces_num);
|
||||
MutableSpan vbo_data = vbo->data<short4>();
|
||||
|
||||
if (mr.extract_type == MeshExtractType::Mesh) {
|
||||
extract_face_dot_normals_mesh(mr, vbo_data);
|
||||
}
|
||||
else {
|
||||
extract_face_dot_normals_bm(mr, vbo_data);
|
||||
}
|
||||
return vbo;
|
||||
}
|
||||
static const GPUVertFormat format = GPU_vertformat_from_attribute(
|
||||
"norAndFlag", gpu::VertAttrType::SNORM_10_10_10_2);
|
||||
gpu::VertBufPtr vbo = gpu::VertBufPtr(GPU_vertbuf_create_with_format(format));
|
||||
GPU_vertbuf_data_alloc(*vbo, mr.faces_num);
|
||||
MutableSpan vbo_data = vbo->data<gpu::PackedNormal>();
|
||||
|
||||
if (mr.extract_type == MeshExtractType::Mesh) {
|
||||
extract_face_dot_normals_mesh(mr, vbo_data);
|
||||
}
|
||||
else {
|
||||
extract_face_dot_normals_bm(mr, vbo_data);
|
||||
}
|
||||
return vbo;
|
||||
}
|
||||
|
||||
} // namespace blender::draw
|
||||
@@ -0,0 +1,110 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "GPU_index_buffer.hh"
|
||||
|
||||
#include "extract_mesh.hh"
|
||||
|
||||
#include "draw_subdivision.hh"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
static const GPUVertFormat &get_fdots_pos_format()
|
||||
{
|
||||
static const GPUVertFormat format = GPU_vertformat_from_attribute(
|
||||
"pos", gpu::VertAttrType::SFLOAT_32_32_32);
|
||||
return format;
|
||||
}
|
||||
|
||||
static const GPUVertFormat &get_fdots_nor_format_subdiv()
|
||||
{
|
||||
static const GPUVertFormat format = GPU_vertformat_from_attribute(
|
||||
"norAndFlag", gpu::VertAttrType::SFLOAT_32_32_32_32);
|
||||
return format;
|
||||
}
|
||||
|
||||
static void extract_face_dot_positions_mesh(const MeshRenderData &mr, MutableSpan<float3> vbo_data)
|
||||
{
|
||||
const Span<float3> positions = mr.vert_positions;
|
||||
const OffsetIndices faces = mr.faces;
|
||||
const Span<int> corner_verts = mr.corner_verts;
|
||||
if (mr.use_subsurf_fdots) {
|
||||
const BitSpan facedot_tags = mr.mesh->runtime->subsurf_face_dot_tags;
|
||||
threading::parallel_for(faces.index_range(), 4096, [&](const IndexRange range) {
|
||||
for (const int face : range) {
|
||||
const Span<int> face_verts = corner_verts.slice(faces[face]);
|
||||
const int *vert = std::find_if(face_verts.begin(), face_verts.end(), [&](const int vert) {
|
||||
return facedot_tags[vert].test();
|
||||
});
|
||||
if (vert == face_verts.end()) {
|
||||
vbo_data[face] = float3(0);
|
||||
}
|
||||
else {
|
||||
vbo_data[face] = positions[*vert];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
threading::parallel_for(faces.index_range(), 4096, [&](const IndexRange range) {
|
||||
for (const int face : range) {
|
||||
vbo_data[face] = bke::mesh::face_center_calc(positions, corner_verts.slice(faces[face]));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static void extract_face_dot_positions_bm(const MeshRenderData &mr, MutableSpan<float3> vbo_data)
|
||||
{
|
||||
const BMesh &bm = *mr.bm;
|
||||
threading::parallel_for(IndexRange(bm.totface), 2048, [&](const IndexRange range) {
|
||||
for (const int face_index : range) {
|
||||
const BMFace &face = *BM_face_at_index(&const_cast<BMesh &>(bm), face_index);
|
||||
if (mr.bm_vert_coords.is_empty()) {
|
||||
BM_face_calc_center_median(&face, vbo_data[face_index]);
|
||||
}
|
||||
else {
|
||||
BM_face_calc_center_median_vcos(&bm, &face, vbo_data[face_index], mr.bm_vert_coords);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
gpu::VertBufPtr extract_face_dots_position(const MeshRenderData &mr)
|
||||
{
|
||||
gpu::VertBufPtr vbo = gpu::VertBufPtr(GPU_vertbuf_create_with_format(get_fdots_pos_format()));
|
||||
GPU_vertbuf_data_alloc(*vbo, mr.corners_num + mr.loose_indices_num);
|
||||
MutableSpan vbo_data = vbo->data<float3>();
|
||||
if (mr.extract_type == MeshExtractType::Mesh) {
|
||||
extract_face_dot_positions_mesh(mr, vbo_data);
|
||||
}
|
||||
else {
|
||||
extract_face_dot_positions_bm(mr, vbo_data);
|
||||
}
|
||||
return vbo;
|
||||
}
|
||||
|
||||
void extract_face_dots_subdiv(const DRWSubdivCache &subdiv_cache,
|
||||
gpu::VertBufPtr &fdots_pos,
|
||||
gpu::VertBufPtr *fdots_nor,
|
||||
gpu::IndexBufPtr &fdots)
|
||||
{
|
||||
/* We "extract" positions, normals, and indices at once. */
|
||||
/* The normals may not be requested. */
|
||||
if (fdots_nor) {
|
||||
*fdots_nor = gpu::VertBufPtr(GPU_vertbuf_create_on_device(get_fdots_nor_format_subdiv(),
|
||||
subdiv_cache.num_coarse_faces));
|
||||
}
|
||||
fdots_pos = gpu::VertBufPtr(
|
||||
GPU_vertbuf_create_on_device(get_fdots_pos_format(), subdiv_cache.num_coarse_faces));
|
||||
fdots = gpu::IndexBufPtr(GPU_indexbuf_build_on_device(subdiv_cache.num_coarse_faces));
|
||||
draw_subdiv_build_fdots_buffers(
|
||||
subdiv_cache, fdots_pos.get(), fdots_nor ? fdots_nor->get() : nullptr, fdots.get());
|
||||
}
|
||||
|
||||
} // namespace blender::draw
|
||||
@@ -0,0 +1,88 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "BKE_attribute.hh"
|
||||
|
||||
#include "extract_mesh.hh"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
static void extract_face_dots_uv_mesh(const MeshRenderData &mr, MutableSpan<float2> vbo_data)
|
||||
{
|
||||
const Mesh &mesh = *mr.mesh;
|
||||
const StringRef name = mesh.active_uv_map_name();
|
||||
const bke::AttributeAccessor attributes = mesh.attributes();
|
||||
if (mr.use_subsurf_fdots) {
|
||||
const BitSpan facedot_tags = mesh.runtime->subsurf_face_dot_tags;
|
||||
const OffsetIndices faces = mr.faces;
|
||||
const Span<int> corner_verts = mr.corner_verts;
|
||||
const VArraySpan uv_map = *attributes.lookup<float2>(name, bke::AttrDomain::Corner);
|
||||
threading::parallel_for(faces.index_range(), 4096, [&](const IndexRange range) {
|
||||
for (const int face_index : range) {
|
||||
const IndexRange face = faces[face_index];
|
||||
const auto corner = std::find_if(face.begin(), face.end(), [&](const int corner) {
|
||||
return facedot_tags[corner_verts[corner]].test();
|
||||
});
|
||||
if (corner == face.end()) {
|
||||
vbo_data[face_index] = float2(0);
|
||||
}
|
||||
else {
|
||||
vbo_data[face_index] = uv_map[*corner];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
/* Use the attribute API to average the attribute on the face domain. */
|
||||
const VArray uv_map = *attributes.lookup<float2>(name, bke::AttrDomain::Face);
|
||||
uv_map.materialize(vbo_data);
|
||||
}
|
||||
}
|
||||
|
||||
static void extract_face_dots_uv_bm(const MeshRenderData &mr, MutableSpan<float2> vbo_data)
|
||||
{
|
||||
const BMesh &bm = *mr.bm;
|
||||
const int offset = CustomData_get_offset(&bm.ldata, CD_PROP_FLOAT2);
|
||||
|
||||
threading::parallel_for(IndexRange(bm.totface), 2048, [&](const IndexRange range) {
|
||||
for (const int face_index : range) {
|
||||
const BMFace &face = *BM_face_at_index(&const_cast<BMesh &>(bm), face_index);
|
||||
const BMLoop *loop = BM_FACE_FIRST_LOOP(&face);
|
||||
vbo_data[face_index] = float2(0);
|
||||
for ([[maybe_unused]] const int i : IndexRange(face.len)) {
|
||||
vbo_data[face_index] += *BM_ELEM_CD_GET_FLOAT2_P(loop, offset);
|
||||
loop = loop->next;
|
||||
}
|
||||
vbo_data[face_index] /= face.len;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
gpu::VertBufPtr extract_face_dots_uv(const MeshRenderData &mr)
|
||||
{
|
||||
static const GPUVertFormat format = []() {
|
||||
GPUVertFormat format{};
|
||||
GPU_vertformat_attr_add(&format, "u", gpu::VertAttrType::SFLOAT_32_32);
|
||||
GPU_vertformat_alias_add(&format, "au");
|
||||
GPU_vertformat_alias_add(&format, "pos");
|
||||
return format;
|
||||
}();
|
||||
gpu::VertBufPtr vbo = gpu::VertBufPtr(GPU_vertbuf_create_with_format(format));
|
||||
GPU_vertbuf_data_alloc(*vbo, mr.faces_num);
|
||||
MutableSpan<float2> vbo_data = vbo->data<float2>();
|
||||
|
||||
if (mr.extract_type == MeshExtractType::Mesh) {
|
||||
extract_face_dots_uv_mesh(mr, vbo_data);
|
||||
}
|
||||
else {
|
||||
extract_face_dots_uv_bm(mr, vbo_data);
|
||||
}
|
||||
return vbo;
|
||||
}
|
||||
|
||||
} // namespace blender::draw
|
||||
@@ -0,0 +1,364 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "BLI_array_utils.hh"
|
||||
|
||||
#include "GPU_attribute_convert.hh"
|
||||
|
||||
#include "extract_mesh.hh"
|
||||
|
||||
#include "draw_subdivision.hh"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
template<typename GPUType>
|
||||
static void extract_vert_normals(const Span<int> corner_verts,
|
||||
const Span<float3> vert_normals,
|
||||
MutableSpan<GPUType> normals)
|
||||
{
|
||||
Array<GPUType> vert_normals_converted(vert_normals.size());
|
||||
gpu::convert_normals(vert_normals, vert_normals_converted.as_mutable_span());
|
||||
array_utils::gather(vert_normals_converted.as_span(), corner_verts, normals);
|
||||
}
|
||||
|
||||
template<typename GPUType>
|
||||
static void extract_face_normals(const MeshRenderData &mr, MutableSpan<GPUType> normals)
|
||||
{
|
||||
const OffsetIndices faces = mr.faces;
|
||||
const Span<float3> face_normals = mr.face_normals;
|
||||
threading::parallel_for(faces.index_range(), 4096, [&](const IndexRange range) {
|
||||
for (const int face : range) {
|
||||
normals.slice(faces[face]).fill(gpu::convert_normal<GPUType>(face_normals[face]));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template<typename GPUType>
|
||||
static void extract_normals_mesh(const MeshRenderData &mr, MutableSpan<GPUType> normals)
|
||||
{
|
||||
const auto get_vert_normals = [&]() {
|
||||
return mr.use_simplify_normals ? mr.mesh->vert_normals_true() : mr.mesh->vert_normals();
|
||||
};
|
||||
if (mr.normals_domain == bke::MeshNormalDomain::Face) {
|
||||
extract_face_normals(mr, normals);
|
||||
}
|
||||
else if (mr.normals_domain == bke::MeshNormalDomain::Point) {
|
||||
extract_vert_normals(mr.corner_verts, get_vert_normals(), normals);
|
||||
}
|
||||
else if (!mr.corner_normals.is_empty()) {
|
||||
gpu::convert_normals(mr.corner_normals, normals);
|
||||
}
|
||||
else if (mr.sharp_faces.is_empty()) {
|
||||
extract_vert_normals(mr.corner_verts, get_vert_normals(), normals);
|
||||
}
|
||||
else {
|
||||
const OffsetIndices faces = mr.faces;
|
||||
const Span<int> corner_verts = mr.corner_verts;
|
||||
const Span<bool> sharp_faces = mr.sharp_faces;
|
||||
const Span<float3> vert_normals = get_vert_normals();
|
||||
const Span<float3> face_normals = mr.face_normals;
|
||||
threading::parallel_for(faces.index_range(), 2048, [&](const IndexRange range) {
|
||||
for (const int face : range) {
|
||||
if (sharp_faces[face]) {
|
||||
normals.slice(faces[face]).fill(gpu::convert_normal<GPUType>(face_normals[face]));
|
||||
}
|
||||
else {
|
||||
for (const int corner : faces[face]) {
|
||||
normals[corner] = gpu::convert_normal<GPUType>(vert_normals[corner_verts[corner]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
template<typename GPUType>
|
||||
static void extract_vert_normals_bm(const MeshRenderData &mr, MutableSpan<GPUType> normals)
|
||||
{
|
||||
const BMesh &bm = *mr.bm;
|
||||
if (mr.bm_free_normal_offset_vert != -1) {
|
||||
threading::parallel_for(IndexRange(bm.totface), 2048, [&](const IndexRange range) {
|
||||
for (const int face_index : range) {
|
||||
const BMFace &face = *BM_face_at_index(&const_cast<BMesh &>(bm), face_index);
|
||||
const BMLoop *loop = BM_FACE_FIRST_LOOP(&face);
|
||||
const IndexRange face_range(BM_elem_index_get(loop), face.len);
|
||||
for (const int corner : face_range) {
|
||||
normals[corner] = gpu::convert_normal<GPUType>(
|
||||
BM_ELEM_CD_GET_FLOAT_P(loop->v, mr.bm_free_normal_offset_vert));
|
||||
loop = loop->next;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (!mr.bm_vert_normals.is_empty()) {
|
||||
Array<GPUType> vert_normals_converted(mr.bm_vert_normals.size());
|
||||
gpu::convert_normals(mr.bm_vert_normals, vert_normals_converted.as_mutable_span());
|
||||
threading::parallel_for(IndexRange(bm.totface), 2048, [&](const IndexRange range) {
|
||||
for (const int face_index : range) {
|
||||
const BMFace &face = *BM_face_at_index(&const_cast<BMesh &>(bm), face_index);
|
||||
const BMLoop *loop = BM_FACE_FIRST_LOOP(&face);
|
||||
const IndexRange face_range(BM_elem_index_get(loop), face.len);
|
||||
for (const int corner : face_range) {
|
||||
normals[corner] = vert_normals_converted[BM_elem_index_get(loop->v)];
|
||||
loop = loop->next;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
threading::parallel_for(IndexRange(bm.totface), 2048, [&](const IndexRange range) {
|
||||
for (const int face_index : range) {
|
||||
const BMFace &face = *BM_face_at_index(&const_cast<BMesh &>(bm), face_index);
|
||||
const BMLoop *loop = BM_FACE_FIRST_LOOP(&face);
|
||||
const IndexRange face_range(BM_elem_index_get(loop), face.len);
|
||||
for (const int corner : face_range) {
|
||||
normals[corner] = gpu::convert_normal<GPUType>(loop->v->no);
|
||||
loop = loop->next;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
template<typename GPUType>
|
||||
static void extract_face_normals_bm(const MeshRenderData &mr, MutableSpan<GPUType> normals)
|
||||
{
|
||||
const BMesh &bm = *mr.bm;
|
||||
if (mr.bm_free_normal_offset_face != -1) {
|
||||
threading::parallel_for(IndexRange(bm.totface), 2048, [&](const IndexRange range) {
|
||||
for (const int face_index : range) {
|
||||
const BMFace &face = *BM_face_at_index(&const_cast<BMesh &>(bm), face_index);
|
||||
const IndexRange face_range(BM_elem_index_get(BM_FACE_FIRST_LOOP(&face)), face.len);
|
||||
normals.slice(face_range)
|
||||
.fill(gpu::convert_normal<GPUType>(
|
||||
BM_ELEM_CD_GET_FLOAT_P(&face, mr.bm_free_normal_offset_face)));
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (!mr.bm_face_normals.is_empty()) {
|
||||
threading::parallel_for(IndexRange(bm.totface), 2048, [&](const IndexRange range) {
|
||||
for (const int face_index : range) {
|
||||
const BMFace &face = *BM_face_at_index(&const_cast<BMesh &>(bm), face_index);
|
||||
const IndexRange face_range(BM_elem_index_get(BM_FACE_FIRST_LOOP(&face)), face.len);
|
||||
normals.slice(face_range)
|
||||
.fill(gpu::convert_normal<GPUType>(mr.bm_face_normals[face_index]));
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
threading::parallel_for(IndexRange(bm.totface), 2048, [&](const IndexRange range) {
|
||||
for (const int face_index : range) {
|
||||
const BMFace &face = *BM_face_at_index(&const_cast<BMesh &>(bm), face_index);
|
||||
const IndexRange face_range(BM_elem_index_get(BM_FACE_FIRST_LOOP(&face)), face.len);
|
||||
normals.slice(face_range).fill(gpu::convert_normal<GPUType>(face.no));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
template<typename GPUType>
|
||||
static void extract_normals_bm(const MeshRenderData &mr, MutableSpan<GPUType> normals)
|
||||
{
|
||||
const BMesh &bm = *mr.bm;
|
||||
if (mr.normals_domain == bke::MeshNormalDomain::Face) {
|
||||
extract_face_normals_bm(mr, normals);
|
||||
}
|
||||
else if (mr.normals_domain == bke::MeshNormalDomain::Point) {
|
||||
extract_vert_normals_bm(mr, normals);
|
||||
}
|
||||
else if (mr.bm_free_normal_offset_corner != -1) {
|
||||
threading::parallel_for(IndexRange(bm.totface), 2048, [&](const IndexRange range) {
|
||||
for (const int face_index : range) {
|
||||
const BMFace &face = *BM_face_at_index(&const_cast<BMesh &>(bm), face_index);
|
||||
const BMLoop *loop = BM_FACE_FIRST_LOOP(&face);
|
||||
const IndexRange face_range(BM_elem_index_get(loop), face.len);
|
||||
for (const int corner : face_range) {
|
||||
normals[corner] = gpu::convert_normal<GPUType>(
|
||||
BM_ELEM_CD_GET_FLOAT_P(loop, mr.bm_free_normal_offset_corner));
|
||||
loop = loop->next;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (!mr.bm_loop_normals.is_empty()) {
|
||||
gpu::convert_normals(mr.bm_loop_normals, normals);
|
||||
}
|
||||
else {
|
||||
threading::parallel_for(IndexRange(bm.totface), 2048, [&](const IndexRange range) {
|
||||
for (const int face_index : range) {
|
||||
const BMFace &face = *BM_face_at_index(&const_cast<BMesh &>(bm), face_index);
|
||||
const BMLoop *loop = BM_FACE_FIRST_LOOP(&face);
|
||||
const IndexRange face_range(BM_elem_index_get(loop), face.len);
|
||||
|
||||
if (!BM_elem_flag_test(&face, BM_ELEM_SMOOTH)) {
|
||||
if (!mr.bm_face_normals.is_empty()) {
|
||||
normals.slice(face_range)
|
||||
.fill(gpu::convert_normal<GPUType>(mr.bm_face_normals[face_index]));
|
||||
}
|
||||
else {
|
||||
normals.slice(face_range).fill(gpu::convert_normal<GPUType>(face.no));
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!mr.bm_vert_normals.is_empty()) {
|
||||
for (const int corner : face_range) {
|
||||
normals[corner] = gpu::convert_normal<GPUType>(
|
||||
mr.bm_vert_normals[BM_elem_index_get(loop->v)]);
|
||||
loop = loop->next;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (const int corner : face_range) {
|
||||
normals[corner] = gpu::convert_normal<GPUType>(loop->v->no);
|
||||
loop = loop->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
gpu::VertBufPtr extract_normals(const MeshRenderData &mr, const bool use_hq)
|
||||
{
|
||||
const int size = mr.corners_num + mr.loose_indices_num;
|
||||
if (use_hq) {
|
||||
static const GPUVertFormat format = []() {
|
||||
GPUVertFormat format{};
|
||||
GPU_vertformat_attr_add(&format, "nor", gpu::VertAttrType::SNORM_16_16_16_16);
|
||||
GPU_vertformat_alias_add(&format, "lnor");
|
||||
GPU_vertformat_alias_add(&format, "vnor");
|
||||
return format;
|
||||
}();
|
||||
gpu::VertBufPtr vbo = gpu::VertBufPtr(GPU_vertbuf_create_with_format(format));
|
||||
GPU_vertbuf_data_alloc(*vbo, size);
|
||||
MutableSpan vbo_data = vbo->data<short4>();
|
||||
MutableSpan corners_data = vbo_data.take_front(mr.corners_num);
|
||||
MutableSpan loose_data = vbo_data.take_back(mr.loose_indices_num);
|
||||
|
||||
if (mr.extract_type == MeshExtractType::Mesh) {
|
||||
extract_normals_mesh(mr, corners_data);
|
||||
}
|
||||
else {
|
||||
extract_normals_bm(mr, corners_data);
|
||||
}
|
||||
|
||||
loose_data.fill(short4(0));
|
||||
return vbo;
|
||||
}
|
||||
static const GPUVertFormat format = []() {
|
||||
GPUVertFormat format{};
|
||||
GPU_vertformat_attr_add(&format, "nor", gpu::VertAttrType::SNORM_10_10_10_2);
|
||||
GPU_vertformat_alias_add(&format, "lnor");
|
||||
GPU_vertformat_alias_add(&format, "vnor");
|
||||
return format;
|
||||
}();
|
||||
gpu::VertBufPtr vbo = gpu::VertBufPtr(GPU_vertbuf_create_with_format(format));
|
||||
GPU_vertbuf_data_alloc(*vbo, size);
|
||||
MutableSpan vbo_data = vbo->data<gpu::PackedNormal>();
|
||||
MutableSpan corners_data = vbo_data.take_front(mr.corners_num);
|
||||
MutableSpan loose_data = vbo_data.take_back(mr.loose_indices_num);
|
||||
|
||||
if (mr.extract_type == MeshExtractType::Mesh) {
|
||||
extract_normals_mesh(mr, corners_data);
|
||||
}
|
||||
else {
|
||||
extract_normals_bm(mr, corners_data);
|
||||
}
|
||||
|
||||
loose_data.fill(gpu::PackedNormal{});
|
||||
return vbo;
|
||||
}
|
||||
|
||||
static const GPUVertFormat &get_normals_format()
|
||||
{
|
||||
static const GPUVertFormat format = []() {
|
||||
GPUVertFormat format{};
|
||||
GPU_vertformat_attr_add(&format, "nor", gpu::VertAttrType::SFLOAT_32_32_32);
|
||||
GPU_vertformat_alias_add(&format, "lnor");
|
||||
GPU_vertformat_alias_add(&format, "vnor");
|
||||
return format;
|
||||
}();
|
||||
return format;
|
||||
}
|
||||
|
||||
static void update_loose_normals(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache,
|
||||
gpu::VertBuf &lnor)
|
||||
{
|
||||
const int vbo_size = subdiv_full_vbo_size(mr, subdiv_cache);
|
||||
const int loose_geom_start = subdiv_cache.num_subdiv_loops;
|
||||
|
||||
/* Push VBO content to the GPU and bind the VBO so that #GPU_vertbuf_update_sub can work. */
|
||||
GPU_vertbuf_use(&lnor);
|
||||
|
||||
/* Default to zeroed attribute. The overlay shader should expect this and render engines should
|
||||
* never draw loose geometry. */
|
||||
const float3 default_normal(0.0f, 0.0f, 0.0f);
|
||||
for (const int i : IndexRange::from_begin_end(loose_geom_start, vbo_size)) {
|
||||
/* TODO(fclem): This has HORRENDOUS performance. Prefer clearing the buffer on device with
|
||||
* something like glClearBufferSubData. */
|
||||
GPU_vertbuf_update_sub(&lnor, i * sizeof(float3), sizeof(float3), &default_normal);
|
||||
}
|
||||
}
|
||||
|
||||
gpu::VertBufPtr extract_normals_subdiv(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache,
|
||||
gpu::VertBuf &pos)
|
||||
{
|
||||
const int vbo_size = subdiv_full_vbo_size(mr, subdiv_cache);
|
||||
|
||||
gpu::VertBufPtr lnor = gpu::VertBufPtr(
|
||||
GPU_vertbuf_create_on_device(get_normals_format(), vbo_size));
|
||||
if (subdiv_cache.num_subdiv_loops == 0) {
|
||||
update_loose_normals(mr, subdiv_cache, *lnor);
|
||||
return lnor;
|
||||
}
|
||||
|
||||
if (subdiv_cache.use_custom_loop_normals) {
|
||||
const Mesh *coarse_mesh = subdiv_cache.mesh;
|
||||
static GPUVertFormat src_normals_format = GPU_vertformat_from_attribute(
|
||||
"vnor", gpu::VertAttrType::SFLOAT_32_32_32);
|
||||
gpu::VertBufPtr src = gpu::VertBufPtr(GPU_vertbuf_create_with_format(src_normals_format));
|
||||
GPU_vertbuf_data_alloc(*src, coarse_mesh->corners_num);
|
||||
src->data<float3>().copy_from(coarse_mesh->corner_normals());
|
||||
draw_subdiv_interp_corner_normals(subdiv_cache, *src, *lnor);
|
||||
|
||||
update_loose_normals(mr, subdiv_cache, *lnor);
|
||||
return lnor;
|
||||
}
|
||||
|
||||
gpu::VertBufPtr subdiv_corner_verts = gpu::VertBufPtr(draw_subdiv_build_origindex_buffer(
|
||||
subdiv_cache.subdiv_loop_subdiv_vert_index, subdiv_cache.num_subdiv_loops));
|
||||
|
||||
/* Calculate vertex normals (stored here per subdivided vertex rather than per subdivided face
|
||||
* corner). The values are used for smooth shaded faces later. */
|
||||
static GPUVertFormat vert_normals_format = GPU_vertformat_from_attribute(
|
||||
"vnor", gpu::VertAttrType::SFLOAT_32_32_32);
|
||||
gpu::VertBufPtr vert_normals = gpu::VertBufPtr(
|
||||
GPU_vertbuf_create_on_device(vert_normals_format, subdiv_cache.num_subdiv_verts));
|
||||
draw_subdiv_accumulate_normals(subdiv_cache,
|
||||
&pos,
|
||||
subdiv_cache.subdiv_vert_face_adjacency_offsets,
|
||||
subdiv_cache.subdiv_vert_face_adjacency,
|
||||
subdiv_corner_verts.get(),
|
||||
vert_normals.get());
|
||||
|
||||
/* Compute final normals for face corners, either using the vertex normal corresponding to the
|
||||
* corner, or by calculating the face normal.
|
||||
*
|
||||
* TODO: Avoid using face normals or vertex normals if possible, using `mr.normals_domain`. */
|
||||
draw_subdiv_build_lnor_buffer(
|
||||
subdiv_cache, &pos, vert_normals.get(), subdiv_corner_verts.get(), lnor.get());
|
||||
|
||||
update_loose_normals(mr, subdiv_cache, *lnor);
|
||||
|
||||
return lnor;
|
||||
}
|
||||
|
||||
} // namespace blender::draw
|
||||
@@ -0,0 +1,602 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_jitter_2d.h"
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_math_geom.h"
|
||||
#include "BLI_math_matrix.h"
|
||||
#include "BLI_math_rotation.h"
|
||||
#include "BLI_ordered_edge.hh"
|
||||
|
||||
#include "BKE_bvhutils.hh"
|
||||
#include "BKE_editmesh.hh"
|
||||
#include "BKE_editmesh_bvh.hh"
|
||||
#include "BKE_editmesh_cache.hh"
|
||||
|
||||
#include "extract_mesh.hh"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
static void axis_from_enum_v3(float v[3], const char axis)
|
||||
{
|
||||
zero_v3(v);
|
||||
if (axis < 3) {
|
||||
v[axis] = 1.0f;
|
||||
}
|
||||
else {
|
||||
v[axis - 3] = -1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
BLI_INLINE float overhang_remap(float fac, float min, float max, float minmax_irange)
|
||||
{
|
||||
if (fac < min) {
|
||||
fac = 1.0f;
|
||||
}
|
||||
else if (fac > max) {
|
||||
fac = -1.0f;
|
||||
}
|
||||
else {
|
||||
fac = (fac - min) * minmax_irange;
|
||||
fac = 1.0f - fac;
|
||||
CLAMP(fac, 0.0f, 1.0f);
|
||||
}
|
||||
return fac;
|
||||
}
|
||||
|
||||
static void statvis_calc_overhang(const MeshRenderData &mr,
|
||||
const float4x4 &object_to_world,
|
||||
MutableSpan<float> r_overhang)
|
||||
{
|
||||
const MeshStatVis *statvis = &mr.toolsettings->statvis;
|
||||
const float min = statvis->overhang_min / float(M_PI);
|
||||
const float max = statvis->overhang_max / float(M_PI);
|
||||
const char axis = statvis->overhang_axis;
|
||||
float dir[3];
|
||||
const float minmax_irange = 1.0f / (max - min);
|
||||
|
||||
BLI_assert(min <= max);
|
||||
|
||||
axis_from_enum_v3(dir, axis);
|
||||
|
||||
/* now convert into global space */
|
||||
mul_transposed_mat3_m4_v3(object_to_world.ptr(), dir);
|
||||
normalize_v3(dir);
|
||||
|
||||
if (mr.extract_type == MeshExtractType::BMesh) {
|
||||
BMEditMesh *em = mr.edit_bmesh;
|
||||
BMIter iter;
|
||||
BMesh *bm = em->bm;
|
||||
BMFace *f;
|
||||
int l_index = 0;
|
||||
BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
|
||||
float fac = angle_normalized_v3v3(bm_face_no_get(mr, f), dir) / float(M_PI);
|
||||
fac = overhang_remap(fac, min, max, minmax_irange);
|
||||
for (int i = 0; i < f->len; i++, l_index++) {
|
||||
r_overhang[l_index] = fac;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (const int face_i : mr.faces.index_range()) {
|
||||
float fac = angle_normalized_v3v3(mr.face_normals[face_i], dir) / float(M_PI);
|
||||
fac = overhang_remap(fac, min, max, minmax_irange);
|
||||
for (const int loop_i : mr.faces[face_i]) {
|
||||
r_overhang[loop_i] = fac;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Needed so we can use jitter values for face interpolation.
|
||||
*/
|
||||
static void uv_from_jitter_v2(float uv[2])
|
||||
{
|
||||
uv[0] += 0.5f;
|
||||
uv[1] += 0.5f;
|
||||
if (uv[0] + uv[1] > 1.0f) {
|
||||
uv[0] = 1.0f - uv[0];
|
||||
uv[1] = 1.0f - uv[1];
|
||||
}
|
||||
|
||||
clamp_v2(uv, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
BLI_INLINE float thickness_remap(float fac, float min, float max, float minmax_irange)
|
||||
{
|
||||
/* important not '<=' */
|
||||
if (fac < max) {
|
||||
fac = (fac - min) * minmax_irange;
|
||||
fac = 1.0f - fac;
|
||||
CLAMP(fac, 0.0f, 1.0f);
|
||||
}
|
||||
else {
|
||||
fac = -1.0f;
|
||||
}
|
||||
return fac;
|
||||
}
|
||||
|
||||
static void statvis_calc_thickness(const MeshRenderData &mr,
|
||||
const float4x4 &object_to_world,
|
||||
MutableSpan<float> r_thickness)
|
||||
{
|
||||
const float eps_offset = 0.00002f; /* values <= 0.00001 give errors */
|
||||
/* cheating to avoid another allocation */
|
||||
float *face_dists = r_thickness.data() + (mr.corners_num - mr.faces_num);
|
||||
const float scale = 1.0f / mat4_to_scale(object_to_world.ptr());
|
||||
const MeshStatVis *statvis = &mr.toolsettings->statvis;
|
||||
const float min = statvis->thickness_min * scale;
|
||||
const float max = statvis->thickness_max * scale;
|
||||
const float minmax_irange = 1.0f / (max - min);
|
||||
const int samples = statvis->thickness_samples;
|
||||
float jit_ofs[32][2];
|
||||
BLI_assert(samples <= 32);
|
||||
BLI_assert(min <= max);
|
||||
|
||||
std::fill_n(face_dists, mr.faces_num, max);
|
||||
|
||||
BLI_jitter_init(jit_ofs, samples);
|
||||
for (int j = 0; j < samples; j++) {
|
||||
uv_from_jitter_v2(jit_ofs[j]);
|
||||
}
|
||||
|
||||
if (mr.extract_type == MeshExtractType::BMesh) {
|
||||
BMEditMesh *em = mr.edit_bmesh;
|
||||
BMesh *bm = em->bm;
|
||||
BM_mesh_elem_index_ensure(bm, BM_FACE);
|
||||
|
||||
BMBVHTree *bmtree = BKE_bmbvh_new_from_editmesh(em, 0, nullptr, false);
|
||||
const Span<std::array<BMLoop *, 3>> looptris = em->looptris;
|
||||
for (int i = 0; i < mr.corner_tris_num; i++) {
|
||||
const BMLoop *const *ltri = looptris[i].data();
|
||||
const int index = BM_elem_index_get(ltri[0]->f);
|
||||
const float *cos[3] = {
|
||||
bm_vert_co_get(mr, ltri[0]->v),
|
||||
bm_vert_co_get(mr, ltri[1]->v),
|
||||
bm_vert_co_get(mr, ltri[2]->v),
|
||||
};
|
||||
float ray_co[3];
|
||||
float ray_no[3];
|
||||
|
||||
normal_tri_v3(ray_no, cos[2], cos[1], cos[0]);
|
||||
|
||||
for (int j = 0; j < samples; j++) {
|
||||
float dist = face_dists[index];
|
||||
interp_v3_v3v3v3_uv(ray_co, cos[0], cos[1], cos[2], jit_ofs[j]);
|
||||
madd_v3_v3fl(ray_co, ray_no, eps_offset);
|
||||
|
||||
BMFace *f_hit = BKE_bmbvh_ray_cast(bmtree, ray_co, ray_no, 0.0f, &dist, nullptr, nullptr);
|
||||
if (f_hit && dist < face_dists[index]) {
|
||||
float angle_fac = fabsf(
|
||||
dot_v3v3(bm_face_no_get(mr, ltri[0]->f), bm_face_no_get(mr, f_hit)));
|
||||
angle_fac = 1.0f - angle_fac;
|
||||
angle_fac = angle_fac * angle_fac * angle_fac;
|
||||
angle_fac = 1.0f - angle_fac;
|
||||
dist /= angle_fac;
|
||||
face_dists[index] = std::min(dist, face_dists[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
BKE_bmbvh_free(bmtree);
|
||||
|
||||
BMIter iter;
|
||||
BMFace *f;
|
||||
int l_index = 0;
|
||||
BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
|
||||
float fac = face_dists[BM_elem_index_get(f)];
|
||||
fac = thickness_remap(fac, min, max, minmax_irange);
|
||||
for (int i = 0; i < f->len; i++, l_index++) {
|
||||
r_thickness[l_index] = fac;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
bke::BVHTreeFromMesh treeData = mr.mesh->bvh_corner_tris();
|
||||
const BVHTree *tree = treeData.tree;
|
||||
if (tree == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const Span<int3> corner_tris = mr.mesh->corner_tris();
|
||||
const Span<int> tri_faces = mr.mesh->corner_tri_faces();
|
||||
for (const int i : corner_tris.index_range()) {
|
||||
const int index = tri_faces[i];
|
||||
const float *cos[3] = {mr.vert_positions[mr.corner_verts[corner_tris[i][0]]],
|
||||
mr.vert_positions[mr.corner_verts[corner_tris[i][1]]],
|
||||
mr.vert_positions[mr.corner_verts[corner_tris[i][2]]]};
|
||||
float ray_co[3];
|
||||
float ray_no[3];
|
||||
|
||||
normal_tri_v3(ray_no, cos[2], cos[1], cos[0]);
|
||||
|
||||
for (int j = 0; j < samples; j++) {
|
||||
interp_v3_v3v3v3_uv(ray_co, cos[0], cos[1], cos[2], jit_ofs[j]);
|
||||
madd_v3_v3fl(ray_co, ray_no, eps_offset);
|
||||
|
||||
BVHTreeRayHit hit;
|
||||
hit.index = -1;
|
||||
hit.dist = face_dists[index];
|
||||
if ((BLI_bvhtree_ray_cast(
|
||||
tree, ray_co, ray_no, 0.0f, &hit, treeData.raycast_callback, &treeData) != -1) &&
|
||||
hit.dist < face_dists[index])
|
||||
{
|
||||
float angle_fac = fabsf(dot_v3v3(mr.face_normals[index], hit.no));
|
||||
angle_fac = 1.0f - angle_fac;
|
||||
angle_fac = angle_fac * angle_fac * angle_fac;
|
||||
angle_fac = 1.0f - angle_fac;
|
||||
hit.dist /= angle_fac;
|
||||
face_dists[index] = std::min(hit.dist, face_dists[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const int face_i : mr.faces.index_range()) {
|
||||
float fac = face_dists[face_i];
|
||||
fac = thickness_remap(fac, min, max, minmax_irange);
|
||||
for (const int loop_i : mr.faces[face_i]) {
|
||||
r_thickness[loop_i] = fac;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct BVHTree_OverlapData {
|
||||
Span<float3> positions;
|
||||
Span<int> corner_verts;
|
||||
Span<int3> corner_tris;
|
||||
Span<int> tri_faces;
|
||||
float epsilon;
|
||||
};
|
||||
|
||||
static bool bvh_overlap_cb(void *userdata, int index_a, int index_b, int /*thread*/)
|
||||
{
|
||||
BVHTree_OverlapData *data = static_cast<BVHTree_OverlapData *>(userdata);
|
||||
|
||||
if (UNLIKELY(data->tri_faces[index_a] == data->tri_faces[index_b])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const int3 tri_a = data->corner_tris[index_a];
|
||||
const int3 tri_b = data->corner_tris[index_b];
|
||||
|
||||
const float *tri_a_co[3] = {data->positions[data->corner_verts[tri_a[0]]],
|
||||
data->positions[data->corner_verts[tri_a[1]]],
|
||||
data->positions[data->corner_verts[tri_a[2]]]};
|
||||
const float *tri_b_co[3] = {data->positions[data->corner_verts[tri_b[0]]],
|
||||
data->positions[data->corner_verts[tri_b[1]]],
|
||||
data->positions[data->corner_verts[tri_b[2]]]};
|
||||
float ix_pair[2][3];
|
||||
int verts_shared = 0;
|
||||
|
||||
verts_shared = (ELEM(tri_a_co[0], UNPACK3(tri_b_co)) + ELEM(tri_a_co[1], UNPACK3(tri_b_co)) +
|
||||
ELEM(tri_a_co[2], UNPACK3(tri_b_co)));
|
||||
|
||||
/* if 2 points are shared, bail out */
|
||||
if (verts_shared >= 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (isect_tri_tri_v3(UNPACK3(tri_a_co), UNPACK3(tri_b_co), ix_pair[0], ix_pair[1]) &&
|
||||
/* if we share a vertex, check the intersection isn't a 'point' */
|
||||
((verts_shared == 0) || (len_squared_v3v3(ix_pair[0], ix_pair[1]) > data->epsilon)));
|
||||
}
|
||||
|
||||
static void statvis_calc_intersect(const MeshRenderData &mr, MutableSpan<float> r_intersect)
|
||||
{
|
||||
for (int l_index = 0; l_index < mr.corners_num; l_index++) {
|
||||
r_intersect[l_index] = -1.0f;
|
||||
}
|
||||
|
||||
if (mr.extract_type == MeshExtractType::BMesh) {
|
||||
BMEditMesh *em = mr.edit_bmesh;
|
||||
uint overlap_len;
|
||||
BMesh *bm = em->bm;
|
||||
|
||||
BM_mesh_elem_index_ensure(bm, BM_FACE);
|
||||
|
||||
BMBVHTree *bmtree = BKE_bmbvh_new_from_editmesh(em, 0, nullptr, false);
|
||||
BVHTreeOverlap *overlap = BKE_bmbvh_overlap_self(bmtree, &overlap_len);
|
||||
|
||||
if (overlap) {
|
||||
for (int i = 0; i < overlap_len; i++) {
|
||||
BMFace *f_hit_pair[2] = {
|
||||
em->looptris[overlap[i].indexA][0]->f,
|
||||
em->looptris[overlap[i].indexB][0]->f,
|
||||
};
|
||||
for (int j = 0; j < 2; j++) {
|
||||
BMFace *f_hit = f_hit_pair[j];
|
||||
BMLoop *l_first = BM_FACE_FIRST_LOOP(f_hit);
|
||||
int l_index = BM_elem_index_get(l_first);
|
||||
for (int k = 0; k < f_hit->len; k++, l_index++) {
|
||||
r_intersect[l_index] = 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
MEM_delete(overlap);
|
||||
}
|
||||
|
||||
BKE_bmbvh_free(bmtree);
|
||||
}
|
||||
else {
|
||||
uint overlap_len;
|
||||
bke::BVHTreeFromMesh treeData = mr.mesh->bvh_corner_tris();
|
||||
const BVHTree *tree = treeData.tree;
|
||||
if (tree == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
BVHTree_OverlapData data = {};
|
||||
data.positions = mr.vert_positions;
|
||||
data.corner_verts = mr.corner_verts;
|
||||
data.corner_tris = mr.mesh->corner_tris();
|
||||
data.tri_faces = mr.mesh->corner_tri_faces();
|
||||
data.epsilon = BLI_bvhtree_get_epsilon(tree);
|
||||
|
||||
BVHTreeOverlap *overlap = BLI_bvhtree_overlap_self(tree, &overlap_len, bvh_overlap_cb, &data);
|
||||
if (overlap) {
|
||||
for (int i = 0; i < overlap_len; i++) {
|
||||
|
||||
for (const IndexRange f_hit : {mr.faces[data.tri_faces[overlap[i].indexA]],
|
||||
mr.faces[data.tri_faces[overlap[i].indexB]]})
|
||||
{
|
||||
int l_index = f_hit.start();
|
||||
for (int k = 0; k < f_hit.size(); k++, l_index++) {
|
||||
r_intersect[l_index] = 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
MEM_delete(overlap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BLI_INLINE float distort_remap(float fac, float min, float /*max*/, float minmax_irange)
|
||||
{
|
||||
if (fac >= min) {
|
||||
fac = (fac - min) * minmax_irange;
|
||||
CLAMP(fac, 0.0f, 1.0f);
|
||||
}
|
||||
else {
|
||||
/* fallback */
|
||||
fac = -1.0f;
|
||||
}
|
||||
return fac;
|
||||
}
|
||||
|
||||
static void statvis_calc_distort(const MeshRenderData &mr, MutableSpan<float> r_distort)
|
||||
{
|
||||
const MeshStatVis *statvis = &mr.toolsettings->statvis;
|
||||
const float min = statvis->distort_min;
|
||||
const float max = statvis->distort_max;
|
||||
const float minmax_irange = 1.0f / (max - min);
|
||||
|
||||
if (mr.extract_type == MeshExtractType::BMesh) {
|
||||
BMEditMesh *em = mr.edit_bmesh;
|
||||
BMIter iter;
|
||||
BMesh *bm = em->bm;
|
||||
BMFace *f;
|
||||
|
||||
int l_index = 0;
|
||||
int f_index = 0;
|
||||
BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, f_index) {
|
||||
float fac = -1.0f;
|
||||
|
||||
if (f->len > 3) {
|
||||
BMLoop *l_iter, *l_first;
|
||||
|
||||
fac = 0.0f;
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
||||
do {
|
||||
const float *no_face;
|
||||
float no_corner[3];
|
||||
if (!mr.bm_vert_coords.is_empty()) {
|
||||
no_face = mr.bm_face_normals[f_index];
|
||||
BM_loop_calc_face_normal_safe_vcos(
|
||||
l_iter,
|
||||
no_face,
|
||||
reinterpret_cast<const float (*)[3]>(mr.bm_vert_coords.data()),
|
||||
no_corner);
|
||||
}
|
||||
else {
|
||||
no_face = f->no;
|
||||
BM_loop_calc_face_normal_safe(l_iter, no_corner);
|
||||
}
|
||||
|
||||
/* simple way to detect (what is most likely) concave */
|
||||
if (dot_v3v3(no_face, no_corner) < 0.0f) {
|
||||
negate_v3(no_corner);
|
||||
}
|
||||
fac = max_ff(fac, angle_normalized_v3v3(no_face, no_corner));
|
||||
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
fac *= 2.0f;
|
||||
}
|
||||
|
||||
fac = distort_remap(fac, min, max, minmax_irange);
|
||||
for (int i = 0; i < f->len; i++, l_index++) {
|
||||
r_distort[l_index] = fac;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (const int face_index : mr.faces.index_range()) {
|
||||
const IndexRange face = mr.faces[face_index];
|
||||
float fac = -1.0f;
|
||||
|
||||
if (face.size() > 3) {
|
||||
const float *f_no = mr.face_normals[face_index];
|
||||
fac = 0.0f;
|
||||
|
||||
for (const int corner : face.drop_front(1)) {
|
||||
const int corner_prev = bke::mesh::face_corner_prev(face, corner);
|
||||
const int corner_next = bke::mesh::face_corner_next(face, corner);
|
||||
float no_corner[3];
|
||||
normal_tri_v3(no_corner,
|
||||
mr.vert_positions[mr.corner_verts[corner_prev]],
|
||||
mr.vert_positions[mr.corner_verts[corner]],
|
||||
mr.vert_positions[mr.corner_verts[corner_next]]);
|
||||
/* simple way to detect (what is most likely) concave */
|
||||
if (dot_v3v3(f_no, no_corner) < 0.0f) {
|
||||
negate_v3(no_corner);
|
||||
}
|
||||
fac = max_ff(fac, angle_normalized_v3v3(f_no, no_corner));
|
||||
}
|
||||
fac *= 2.0f;
|
||||
}
|
||||
|
||||
fac = distort_remap(fac, min, max, minmax_irange);
|
||||
for (const int corner : face) {
|
||||
r_distort[corner] = fac;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BLI_INLINE float sharp_remap(float fac, float min, float /*max*/, float minmax_irange)
|
||||
{
|
||||
/* important not '>=' */
|
||||
if (fac > min) {
|
||||
fac = (fac - min) * minmax_irange;
|
||||
CLAMP(fac, 0.0f, 1.0f);
|
||||
}
|
||||
else {
|
||||
/* fallback */
|
||||
fac = -1.0f;
|
||||
}
|
||||
return fac;
|
||||
}
|
||||
|
||||
static void statvis_calc_sharp(const MeshRenderData &mr, MutableSpan<float> r_sharp)
|
||||
{
|
||||
const MeshStatVis *statvis = &mr.toolsettings->statvis;
|
||||
const float min = statvis->sharp_min;
|
||||
const float max = statvis->sharp_max;
|
||||
const float minmax_irange = 1.0f / (max - min);
|
||||
|
||||
/* Can we avoid this extra allocation? */
|
||||
float *vert_angles = MEM_new_array_uninitialized<float>(mr.verts_num, __func__);
|
||||
std::fill_n(vert_angles, mr.verts_num, -M_PI);
|
||||
|
||||
if (mr.extract_type == MeshExtractType::BMesh) {
|
||||
BMEditMesh *em = mr.edit_bmesh;
|
||||
BMIter iter;
|
||||
BMesh *bm = em->bm;
|
||||
BMFace *efa;
|
||||
BMEdge *e;
|
||||
/* first assign float values to verts */
|
||||
BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
|
||||
float angle = BM_edge_calc_face_angle_signed(e);
|
||||
float *col1 = &vert_angles[BM_elem_index_get(e->v1)];
|
||||
float *col2 = &vert_angles[BM_elem_index_get(e->v2)];
|
||||
*col1 = max_ff(*col1, angle);
|
||||
*col2 = max_ff(*col2, angle);
|
||||
}
|
||||
/* Copy vert value to loops. */
|
||||
BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
|
||||
BMLoop *l_iter, *l_first;
|
||||
l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
|
||||
do {
|
||||
int l_index = BM_elem_index_get(l_iter);
|
||||
int v_index = BM_elem_index_get(l_iter->v);
|
||||
r_sharp[l_index] = sharp_remap(vert_angles[v_index], min, max, minmax_irange);
|
||||
} while ((l_iter = l_iter->next) != l_first);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* first assign float values to verts */
|
||||
|
||||
Map<OrderedEdge, int> eh;
|
||||
eh.reserve(mr.edges_num);
|
||||
|
||||
for (int face_index = 0; face_index < mr.faces_num; face_index++) {
|
||||
const IndexRange face = mr.faces[face_index];
|
||||
for (const int corner : face) {
|
||||
const int vert_curr = mr.corner_verts[corner];
|
||||
const int vert_next = mr.corner_verts[bke::mesh::face_corner_next(face, corner)];
|
||||
float angle;
|
||||
eh.add_or_modify(
|
||||
{vert_curr, vert_next},
|
||||
[&](int *value) { *value = face_index; },
|
||||
[&](int *value) {
|
||||
const int other_face_index = *value;
|
||||
if (other_face_index == -1) {
|
||||
/* non-manifold edge */
|
||||
angle = DEG2RADF(90.0f);
|
||||
return;
|
||||
}
|
||||
const float *f1_no = mr.face_normals[face_index];
|
||||
const float *f2_no = mr.face_normals[other_face_index];
|
||||
angle = angle_normalized_v3v3(f1_no, f2_no);
|
||||
angle = is_edge_convex_v3(mr.vert_positions[vert_curr],
|
||||
mr.vert_positions[vert_next],
|
||||
f1_no,
|
||||
f2_no) ?
|
||||
angle :
|
||||
-angle;
|
||||
/* Tag as manifold. */
|
||||
*value = -1;
|
||||
});
|
||||
float *col1 = &vert_angles[vert_curr];
|
||||
float *col2 = &vert_angles[vert_next];
|
||||
*col1 = max_ff(*col1, angle);
|
||||
*col2 = max_ff(*col2, angle);
|
||||
}
|
||||
}
|
||||
/* Remaining non manifold edges. */
|
||||
for (const OrderedEdge &edge : eh.keys()) {
|
||||
const float angle = DEG2RADF(90.0f);
|
||||
float *col1 = &vert_angles[edge.v_low];
|
||||
float *col2 = &vert_angles[edge.v_high];
|
||||
*col1 = max_ff(*col1, angle);
|
||||
*col2 = max_ff(*col2, angle);
|
||||
}
|
||||
|
||||
for (int l_index = 0; l_index < mr.corners_num; l_index++) {
|
||||
const int vert = mr.corner_verts[l_index];
|
||||
r_sharp[l_index] = sharp_remap(vert_angles[vert], min, max, minmax_irange);
|
||||
}
|
||||
}
|
||||
|
||||
MEM_delete(vert_angles);
|
||||
}
|
||||
|
||||
gpu::VertBufPtr extract_mesh_analysis(const MeshRenderData &mr, const float4x4 &object_to_world)
|
||||
{
|
||||
static const GPUVertFormat format = GPU_vertformat_from_attribute("weight",
|
||||
gpu::VertAttrType::SFLOAT_32);
|
||||
|
||||
gpu::VertBufPtr vbo = gpu::VertBufPtr(GPU_vertbuf_create_with_format(format));
|
||||
GPU_vertbuf_data_alloc(*vbo, mr.corners_num);
|
||||
MutableSpan<float> vbo_data = vbo->data<float>();
|
||||
|
||||
switch (mr.toolsettings->statvis.type) {
|
||||
case SCE_STATVIS_OVERHANG:
|
||||
statvis_calc_overhang(mr, object_to_world, vbo_data);
|
||||
break;
|
||||
case SCE_STATVIS_THICKNESS:
|
||||
statvis_calc_thickness(mr, object_to_world, vbo_data);
|
||||
break;
|
||||
case SCE_STATVIS_INTERSECT:
|
||||
statvis_calc_intersect(mr, vbo_data);
|
||||
break;
|
||||
case SCE_STATVIS_DISTORT:
|
||||
statvis_calc_distort(mr, vbo_data);
|
||||
break;
|
||||
case SCE_STATVIS_SHARP:
|
||||
statvis_calc_sharp(mr, vbo_data);
|
||||
break;
|
||||
}
|
||||
return vbo;
|
||||
}
|
||||
|
||||
} // namespace blender::draw
|
||||
@@ -0,0 +1,58 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "extract_mesh.hh"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
gpu::VertBufPtr extract_orco(const MeshRenderData &mr)
|
||||
{
|
||||
const Span<float3> orco_data(
|
||||
static_cast<const float3 *>(CustomData_get_layer(&mr.mesh->vert_data, CD_ORCO)),
|
||||
mr.corners_num);
|
||||
|
||||
/* FIXME(fclem): We use the last component as a way to differentiate from generic vertex
|
||||
* attributes. This is a substantial waste of video-ram and should be done another way.
|
||||
* Unfortunately, at the time of writing, I did not found any other "non disruptive"
|
||||
* alternative. */
|
||||
static const GPUVertFormat format = GPU_vertformat_from_attribute(
|
||||
"orco", gpu::VertAttrType::SFLOAT_32_32_32_32);
|
||||
|
||||
gpu::VertBufPtr vbo = gpu::VertBufPtr(GPU_vertbuf_create_with_format(format));
|
||||
GPU_vertbuf_data_alloc(*vbo, mr.corners_num);
|
||||
MutableSpan vbo_data = vbo->data<float4>();
|
||||
|
||||
const int64_t bytes = orco_data.size_in_bytes() + vbo_data.size_in_bytes();
|
||||
threading::memory_bandwidth_bound_task(bytes, [&]() {
|
||||
if (mr.extract_type == MeshExtractType::BMesh) {
|
||||
const BMesh &bm = *mr.bm;
|
||||
threading::parallel_for(IndexRange(bm.totface), 2048, [&](const IndexRange range) {
|
||||
for (const int face_index : range) {
|
||||
const BMFace &face = *BM_face_at_index(&const_cast<BMesh &>(bm), face_index);
|
||||
const BMLoop *loop = BM_FACE_FIRST_LOOP(&face);
|
||||
for ([[maybe_unused]] const int i : IndexRange(face.len)) {
|
||||
const int index = BM_elem_index_get(loop);
|
||||
vbo_data[index] = float4(orco_data[BM_elem_index_get(loop->v)], 0.0f);
|
||||
loop = loop->next;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
const Span<int> corner_verts = mr.corner_verts;
|
||||
threading::parallel_for(corner_verts.index_range(), 4096, [&](const IndexRange range) {
|
||||
for (const int corner : range) {
|
||||
vbo_data[corner] = float4(orco_data[corner_verts[corner]], 0.0f);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
return vbo;
|
||||
}
|
||||
|
||||
} // namespace blender::draw
|
||||
@@ -0,0 +1,142 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "extract_mesh.hh"
|
||||
|
||||
#include "draw_subdivision.hh"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
static void extract_paint_overlay_flags(const MeshRenderData &mr, MutableSpan<int> flags)
|
||||
{
|
||||
const bool use_face_select = (mr.mesh->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
|
||||
Span<bool> selection;
|
||||
if (mr.mesh->editflag & ME_EDIT_PAINT_FACE_SEL) {
|
||||
selection = mr.select_poly;
|
||||
}
|
||||
else if (mr.mesh->editflag & ME_EDIT_PAINT_VERT_SEL) {
|
||||
selection = mr.select_vert;
|
||||
}
|
||||
if (selection.is_empty() && mr.hide_poly.is_empty() && (!mr.edit_bmesh || !mr.orig_index_vert)) {
|
||||
flags.fill(0);
|
||||
return;
|
||||
}
|
||||
const OffsetIndices faces = mr.faces;
|
||||
threading::parallel_for(faces.index_range(), 1024, [&](const IndexRange range) {
|
||||
if (selection.is_empty()) {
|
||||
flags.fill(0);
|
||||
}
|
||||
else {
|
||||
if (use_face_select) {
|
||||
for (const int face : range) {
|
||||
flags.slice(faces[face]).fill(selection[face] ? 1 : 0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
const Span<int> corner_verts = mr.corner_verts;
|
||||
for (const int face : range) {
|
||||
for (const int corner : faces[face]) {
|
||||
flags[corner] = selection[corner_verts[corner]] ? 1 : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!mr.hide_poly.is_empty()) {
|
||||
const Span<bool> hide_poly = mr.hide_poly;
|
||||
for (const int face : range) {
|
||||
if (hide_poly[face]) {
|
||||
flags.slice(faces[face]).fill(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mr.edit_bmesh && mr.orig_index_vert) {
|
||||
const Span<int> corner_verts = mr.corner_verts;
|
||||
const Span<int> orig_indices(mr.orig_index_vert, mr.verts_num);
|
||||
for (const int face : range) {
|
||||
for (const int corner : faces[face]) {
|
||||
if (orig_indices[corner_verts[corner]] == ORIGINDEX_NONE) {
|
||||
flags[corner] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void extract_edit_flags_bm(const MeshRenderData &mr, MutableSpan<int> flags)
|
||||
{
|
||||
/* TODO: Return early if there are no hidden faces. */
|
||||
const BMesh &bm = *mr.bm;
|
||||
threading::parallel_for(IndexRange(bm.totface), 2048, [&](const IndexRange range) {
|
||||
for (const int face_index : range) {
|
||||
const BMFace &face = *BM_face_at_index(&const_cast<BMesh &>(bm), face_index);
|
||||
if (BM_elem_flag_test(&face, BM_ELEM_HIDDEN)) {
|
||||
const IndexRange face_range(BM_elem_index_get(BM_FACE_FIRST_LOOP(&face)), face.len);
|
||||
flags.slice(face_range).fill(-1);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static const GPUVertFormat &get_paint_overlay_flag_format()
|
||||
{
|
||||
static const GPUVertFormat format = GPU_vertformat_from_attribute("paint_overlay_flag",
|
||||
gpu::VertAttrType::SINT_32);
|
||||
return format;
|
||||
}
|
||||
|
||||
gpu::VertBufPtr extract_paint_overlay_flags(const MeshRenderData &mr)
|
||||
{
|
||||
const int size = mr.corners_num + mr.loose_indices_num;
|
||||
gpu::VertBufPtr vbo = gpu::VertBufPtr(
|
||||
GPU_vertbuf_create_with_format(get_paint_overlay_flag_format()));
|
||||
GPU_vertbuf_data_alloc(*vbo, size);
|
||||
MutableSpan vbo_data = vbo->data<int>();
|
||||
MutableSpan corners_data = vbo_data.take_front(mr.corners_num);
|
||||
MutableSpan loose_data = vbo_data.take_back(mr.loose_indices_num);
|
||||
|
||||
if (mr.extract_type == MeshExtractType::Mesh) {
|
||||
extract_paint_overlay_flags(mr, corners_data);
|
||||
}
|
||||
else {
|
||||
extract_edit_flags_bm(mr, corners_data);
|
||||
}
|
||||
|
||||
loose_data.fill(0);
|
||||
return vbo;
|
||||
}
|
||||
|
||||
static void update_loose_flags(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache,
|
||||
gpu::VertBuf &flags)
|
||||
{
|
||||
const int vbo_size = subdiv_full_vbo_size(mr, subdiv_cache);
|
||||
const int loose_geom_start = subdiv_cache.num_subdiv_loops;
|
||||
|
||||
/* Push VBO content to the GPU and bind the VBO so that #GPU_vertbuf_update_sub can work. */
|
||||
GPU_vertbuf_use(&flags);
|
||||
|
||||
/* Default to zeroed attribute. The overlay shader should expect this and render engines should
|
||||
* never draw loose geometry. */
|
||||
const int default_value = 0;
|
||||
for (const int i : IndexRange::from_begin_end(loose_geom_start, vbo_size)) {
|
||||
/* TODO(fclem): This has HORRENDOUS performance. Prefer clearing the buffer on device with
|
||||
* something like glClearBufferSubData. */
|
||||
GPU_vertbuf_update_sub(&flags, i * sizeof(int), sizeof(int), &default_value);
|
||||
}
|
||||
}
|
||||
|
||||
gpu::VertBufPtr extract_paint_overlay_flags_subdiv(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache)
|
||||
{
|
||||
gpu::VertBufPtr flags = gpu::VertBufPtr(GPU_vertbuf_create_on_device(
|
||||
get_paint_overlay_flag_format(), subdiv_full_vbo_size(mr, subdiv_cache)));
|
||||
|
||||
draw_subdiv_build_paint_overlay_flag_buffer(subdiv_cache, *flags);
|
||||
|
||||
update_loose_flags(mr, subdiv_cache, *flags);
|
||||
return flags;
|
||||
}
|
||||
|
||||
} // namespace blender::draw
|
||||
@@ -0,0 +1,158 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "BLI_array_utils.hh"
|
||||
#include "BLI_math_vector.h"
|
||||
|
||||
#include "extract_mesh.hh"
|
||||
|
||||
#include "draw_subdivision.hh"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
static void extract_positions_mesh(const MeshRenderData &mr, MutableSpan<float3> vbo_data)
|
||||
{
|
||||
MutableSpan corners_data = vbo_data.take_front(mr.corners_num);
|
||||
MutableSpan loose_edge_data = vbo_data.slice(mr.corners_num, mr.loose_edges.size() * 2);
|
||||
MutableSpan loose_vert_data = vbo_data.take_back(mr.loose_verts.size());
|
||||
|
||||
threading::memory_bandwidth_bound_task(
|
||||
mr.vert_positions.size_in_bytes() + mr.corner_verts.size_in_bytes() +
|
||||
vbo_data.size_in_bytes() + mr.loose_edges.size(),
|
||||
[&]() {
|
||||
array_utils::gather(mr.vert_positions, mr.corner_verts, corners_data);
|
||||
extract_mesh_loose_edge_data(mr.vert_positions, mr.edges, mr.loose_edges, loose_edge_data);
|
||||
array_utils::gather(mr.vert_positions, mr.loose_verts, loose_vert_data);
|
||||
});
|
||||
}
|
||||
|
||||
static void extract_positions_bm(const MeshRenderData &mr, MutableSpan<float3> vbo_data)
|
||||
{
|
||||
const BMesh &bm = *mr.bm;
|
||||
MutableSpan corners_data = vbo_data.take_front(mr.corners_num);
|
||||
MutableSpan loose_edge_data = vbo_data.slice(mr.corners_num, mr.loose_edges.size() * 2);
|
||||
MutableSpan loose_vert_data = vbo_data.take_back(mr.loose_verts.size());
|
||||
|
||||
threading::parallel_for(IndexRange(bm.totface), 2048, [&](const IndexRange range) {
|
||||
for (const int face_index : range) {
|
||||
const BMFace &face = *BM_face_at_index(&const_cast<BMesh &>(bm), face_index);
|
||||
const BMLoop *loop = BM_FACE_FIRST_LOOP(&face);
|
||||
for ([[maybe_unused]] const int i : IndexRange(face.len)) {
|
||||
const int index = BM_elem_index_get(loop);
|
||||
corners_data[index] = bm_vert_co_get(mr, loop->v);
|
||||
loop = loop->next;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mr.loose_edges.foreach_index(
|
||||
[&](const int i, const int pos) {
|
||||
const BMEdge &edge = *BM_edge_at_index(&const_cast<BMesh &>(bm), i);
|
||||
loose_edge_data[pos * 2 + 0] = bm_vert_co_get(mr, edge.v1);
|
||||
loose_edge_data[pos * 2 + 1] = bm_vert_co_get(mr, edge.v2);
|
||||
},
|
||||
exec_mode::grain_size(4096));
|
||||
|
||||
mr.loose_verts.foreach_index(
|
||||
[&](const int i, const int pos) {
|
||||
const BMVert &vert = *BM_vert_at_index(&const_cast<BMesh &>(bm), i);
|
||||
loose_vert_data[pos] = bm_vert_co_get(mr, &vert);
|
||||
},
|
||||
exec_mode::grain_size(2048));
|
||||
}
|
||||
|
||||
gpu::VertBufPtr extract_positions(const MeshRenderData &mr)
|
||||
{
|
||||
static const GPUVertFormat format = GPU_vertformat_from_attribute(
|
||||
"pos", gpu::VertAttrType::SFLOAT_32_32_32);
|
||||
gpu::VertBufPtr vbo = gpu::VertBufPtr(GPU_vertbuf_create_with_format(format));
|
||||
GPU_vertbuf_data_alloc(*vbo, mr.corners_num + mr.loose_indices_num);
|
||||
|
||||
MutableSpan vbo_data = vbo->data<float3>();
|
||||
if (mr.extract_type == MeshExtractType::Mesh) {
|
||||
extract_positions_mesh(mr, vbo_data);
|
||||
}
|
||||
else {
|
||||
extract_positions_bm(mr, vbo_data);
|
||||
}
|
||||
|
||||
return vbo;
|
||||
}
|
||||
|
||||
static void extract_loose_positions_subdiv(const DRWSubdivCache &subdiv_cache,
|
||||
const MeshRenderData &mr,
|
||||
gpu::VertBuf &vbo)
|
||||
{
|
||||
const IndexMask &loose_verts = mr.loose_verts;
|
||||
const int loose_edges_num = mr.loose_edges.size();
|
||||
if (loose_verts.is_empty() && loose_edges_num == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Make sure buffer is active for sending loose data. */
|
||||
GPU_vertbuf_use(&vbo);
|
||||
|
||||
const int resolution = subdiv_cache.resolution;
|
||||
const Span<float3> cached_positions = subdiv_cache.loose_edge_positions;
|
||||
const int verts_per_edge = subdiv_verts_per_coarse_edge(subdiv_cache);
|
||||
const int edges_per_edge = subdiv_edges_per_coarse_edge(subdiv_cache);
|
||||
|
||||
const int loose_geom_start = subdiv_cache.num_subdiv_loops;
|
||||
|
||||
float3 edge_data[2];
|
||||
for (const int i : IndexRange(loose_edges_num)) {
|
||||
const int edge_offset = loose_geom_start + i * verts_per_edge;
|
||||
const Span<float3> positions = cached_positions.slice(i * resolution, resolution);
|
||||
for (const int edge : IndexRange(edges_per_edge)) {
|
||||
edge_data[0] = positions[edge + 0];
|
||||
edge_data[1] = positions[edge + 1];
|
||||
GPU_vertbuf_update_sub(
|
||||
&vbo, (edge_offset + edge * 2) * sizeof(float3), sizeof(float3) * 2, &edge_data);
|
||||
}
|
||||
}
|
||||
|
||||
const int loose_verts_start = loose_geom_start + loose_edges_num * verts_per_edge;
|
||||
const Span<float3> positions = mr.vert_positions;
|
||||
loose_verts.foreach_index([&](const int i, const int pos) {
|
||||
GPU_vertbuf_update_sub(
|
||||
&vbo, (loose_verts_start + pos) * sizeof(float3), sizeof(float3), &positions[i]);
|
||||
});
|
||||
}
|
||||
|
||||
gpu::VertBufPtr extract_positions_subdiv(const DRWSubdivCache &subdiv_cache,
|
||||
const MeshRenderData &mr,
|
||||
gpu::VertBufPtr *orco_vbo)
|
||||
{
|
||||
static const GPUVertFormat format = GPU_vertformat_from_attribute(
|
||||
"pos", gpu::VertAttrType::SFLOAT_32_32_32);
|
||||
gpu::VertBufPtr vbo = gpu::VertBufPtr(
|
||||
GPU_vertbuf_create_on_device(format, subdiv_full_vbo_size(mr, subdiv_cache)));
|
||||
|
||||
if (subdiv_cache.num_subdiv_loops == 0) {
|
||||
extract_loose_positions_subdiv(subdiv_cache, mr, *vbo);
|
||||
return vbo;
|
||||
}
|
||||
|
||||
if (orco_vbo) {
|
||||
/* FIXME(fclem): We use the last component as a way to differentiate from generic vertex
|
||||
* attributes. This is a substantial waste of video-ram and should be done another way.
|
||||
* Unfortunately, at the time of writing, I did not found any other "non disruptive"
|
||||
* alternative. */
|
||||
static const GPUVertFormat format = GPU_vertformat_from_attribute(
|
||||
"orco", gpu::VertAttrType::SFLOAT_32_32_32_32);
|
||||
*orco_vbo = gpu::VertBufPtr(
|
||||
GPU_vertbuf_create_on_device(format, subdiv_cache.num_subdiv_loops));
|
||||
}
|
||||
|
||||
draw_subdiv_extract_pos(subdiv_cache, vbo.get(), orco_vbo ? orco_vbo->get() : nullptr);
|
||||
|
||||
extract_loose_positions_subdiv(subdiv_cache, mr, *vbo);
|
||||
return vbo;
|
||||
}
|
||||
|
||||
} // namespace blender::draw
|
||||
@@ -0,0 +1,187 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "BLI_array_utils.hh"
|
||||
#include "BLI_math_vector.h"
|
||||
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_mesh.hh"
|
||||
#include "BKE_paint.hh"
|
||||
|
||||
#include "draw_subdivision.hh"
|
||||
#include "extract_mesh.hh"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
static const GPUVertFormat &get_sculpt_data_format()
|
||||
{
|
||||
static const GPUVertFormat format = []() {
|
||||
GPUVertFormat format{};
|
||||
GPU_vertformat_attr_add(&format, "fset", gpu::VertAttrType::UNORM_8_8_8_8);
|
||||
GPU_vertformat_attr_add(&format, "msk", gpu::VertAttrType::SFLOAT_32);
|
||||
return format;
|
||||
}();
|
||||
return format;
|
||||
}
|
||||
|
||||
gpu::VertBufPtr extract_sculpt_data(const MeshRenderData &mr)
|
||||
{
|
||||
gpu::VertBufPtr vbo = gpu::VertBufPtr(GPU_vertbuf_create_with_format(get_sculpt_data_format()));
|
||||
GPU_vertbuf_data_alloc(*vbo, mr.corners_num);
|
||||
|
||||
struct gpuSculptData {
|
||||
uchar4 face_set_color;
|
||||
float mask;
|
||||
};
|
||||
|
||||
MutableSpan vbo_data = vbo->data<gpuSculptData>();
|
||||
|
||||
const int default_face_set = mr.mesh->face_sets_color_default;
|
||||
const int face_set_seed = mr.mesh->face_sets_color_seed;
|
||||
if (mr.extract_type == MeshExtractType::BMesh) {
|
||||
const BMesh &bm = *mr.bm;
|
||||
const int mask_offset = CustomData_get_offset_named(
|
||||
&mr.bm->vdata, CD_PROP_FLOAT, ".sculpt_mask");
|
||||
const int face_set_offset = CustomData_get_offset_named(
|
||||
&mr.bm->pdata, CD_PROP_INT32, ".sculpt_face_set");
|
||||
threading::parallel_for(IndexRange(bm.totface), 2048, [&](const IndexRange range) {
|
||||
for (const int face_index : range) {
|
||||
const BMFace &face = *BM_face_at_index(&const_cast<BMesh &>(bm), face_index);
|
||||
const IndexRange face_range(BM_elem_index_get(BM_FACE_FIRST_LOOP(&face)), face.len);
|
||||
|
||||
uchar4 face_set_color(UCHAR_MAX);
|
||||
if (face_set_offset != -1) {
|
||||
const int face_set_id = BM_ELEM_CD_GET_INT(&face, face_set_offset);
|
||||
if (face_set_id != default_face_set) {
|
||||
BKE_paint_face_set_overlay_color_get(face_set_id, face_set_seed, face_set_color);
|
||||
}
|
||||
}
|
||||
vbo_data.slice(face_range).fill(gpuSculptData{face_set_color, 0.0f});
|
||||
|
||||
if (mask_offset != -1) {
|
||||
const BMLoop *loop = BM_FACE_FIRST_LOOP(&face);
|
||||
for ([[maybe_unused]] const int i : IndexRange(face.len)) {
|
||||
const int index = BM_elem_index_get(loop);
|
||||
vbo_data[index].mask = BM_ELEM_CD_GET_FLOAT(loop->v, mask_offset);
|
||||
loop = loop->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
const OffsetIndices faces = mr.faces;
|
||||
const Span<int> corner_verts = mr.corner_verts;
|
||||
const bke::AttributeAccessor attributes = mr.mesh->attributes();
|
||||
const VArraySpan mask = *attributes.lookup<float>(".sculpt_mask", bke::AttrDomain::Point);
|
||||
const VArraySpan face_set = *attributes.lookup<int>(".sculpt_face_set", bke::AttrDomain::Face);
|
||||
threading::parallel_for(faces.index_range(), 1024, [&](const IndexRange range) {
|
||||
for (const int face_index : range) {
|
||||
const IndexRange face = faces[face_index];
|
||||
uchar4 face_set_color(UCHAR_MAX);
|
||||
if (!face_set.is_empty()) {
|
||||
const int face_set_id = face_set[face_index];
|
||||
if (face_set_id != default_face_set) {
|
||||
BKE_paint_face_set_overlay_color_get(face_set_id, face_set_seed, face_set_color);
|
||||
}
|
||||
}
|
||||
vbo_data.slice(face).fill(gpuSculptData{face_set_color, 0.0f});
|
||||
|
||||
if (!mask.is_empty()) {
|
||||
for (const int corner : face) {
|
||||
vbo_data[corner].mask = mask[corner_verts[corner]];
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return vbo;
|
||||
}
|
||||
|
||||
gpu::VertBufPtr extract_sculpt_data_subdiv(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache)
|
||||
{
|
||||
const Mesh &coarse_mesh = *mr.mesh;
|
||||
const int subdiv_corners_num = subdiv_cache.num_subdiv_loops;
|
||||
|
||||
/* First, interpolate mask if available. */
|
||||
gpu::VertBuf *mask_vbo = nullptr;
|
||||
gpu::VertBuf *subdiv_mask_vbo = nullptr;
|
||||
|
||||
const bke::AttributeAccessor attributes = coarse_mesh.attributes();
|
||||
|
||||
if (const VArray mask = *attributes.lookup<float>(".sculpt_mask", bke::AttrDomain::Point)) {
|
||||
GPUVertFormat mask_format = {0};
|
||||
GPU_vertformat_attr_add(&mask_format, "msk", gpu::VertAttrType::SFLOAT_32);
|
||||
const Span<int> corner_verts = coarse_mesh.corner_verts();
|
||||
mask_vbo = GPU_vertbuf_calloc();
|
||||
GPU_vertbuf_init_with_format(*mask_vbo, mask_format);
|
||||
GPU_vertbuf_data_alloc(*mask_vbo, corner_verts.size());
|
||||
|
||||
MutableSpan mask_vbo_data = mask_vbo->data<float>();
|
||||
array_utils::gather(mask, corner_verts, mask_vbo_data);
|
||||
|
||||
subdiv_mask_vbo = GPU_vertbuf_calloc();
|
||||
GPU_vertbuf_init_build_on_device(*subdiv_mask_vbo, mask_format, subdiv_corners_num);
|
||||
|
||||
draw_subdiv_interp_custom_data(subdiv_cache, *mask_vbo, *subdiv_mask_vbo, GPU_COMP_F32, 1, 0);
|
||||
}
|
||||
|
||||
/* Then, gather face sets. */
|
||||
GPUVertFormat face_set_format = {0};
|
||||
GPU_vertformat_attr_add(&face_set_format, "msk", gpu::VertAttrType::UNORM_8_8_8_8);
|
||||
|
||||
gpu::VertBuf *face_set_vbo = GPU_vertbuf_calloc();
|
||||
GPU_vertbuf_init_with_format(*face_set_vbo, face_set_format);
|
||||
GPU_vertbuf_data_alloc(*face_set_vbo, subdiv_corners_num);
|
||||
|
||||
struct gpuFaceSet {
|
||||
uchar4 color;
|
||||
};
|
||||
|
||||
MutableSpan face_set_vbo_data = face_set_vbo->data<gpuFaceSet>();
|
||||
const VArraySpan face_sets = *attributes.lookup<int>(".sculpt_face_set", bke::AttrDomain::Face);
|
||||
if (face_sets.is_empty()) {
|
||||
face_set_vbo_data.fill({uchar4{UCHAR_MAX}});
|
||||
}
|
||||
else {
|
||||
const Span<int> subdiv_loop_face_index(subdiv_cache.subdiv_loop_face_index,
|
||||
subdiv_corners_num);
|
||||
const int default_face_set = coarse_mesh.face_sets_color_default;
|
||||
const int face_set_seed = coarse_mesh.face_sets_color_seed;
|
||||
threading::parallel_for(IndexRange(subdiv_corners_num), 4096, [&](const IndexRange range) {
|
||||
for (const int i : range) {
|
||||
const int face_index = subdiv_loop_face_index[i];
|
||||
|
||||
uchar4 face_set_color(UCHAR_MAX);
|
||||
const int face_set_id = face_sets[face_index];
|
||||
/* Skip for the default color face set to render it white. */
|
||||
if (face_set_id != default_face_set) {
|
||||
BKE_paint_face_set_overlay_color_get(face_set_id, face_set_seed, face_set_color);
|
||||
}
|
||||
|
||||
copy_v3_v3_uchar(face_set_vbo_data[i].color, face_set_color);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* Finally, interleave mask and face sets. */
|
||||
gpu::VertBufPtr vbo = gpu::VertBufPtr(
|
||||
GPU_vertbuf_create_on_device(get_sculpt_data_format(), subdiv_corners_num));
|
||||
|
||||
draw_subdiv_build_sculpt_data_buffer(subdiv_cache, subdiv_mask_vbo, face_set_vbo, vbo.get());
|
||||
|
||||
if (mask_vbo) {
|
||||
GPU_vertbuf_discard(mask_vbo);
|
||||
GPU_vertbuf_discard(subdiv_mask_vbo);
|
||||
}
|
||||
GPU_vertbuf_discard(face_set_vbo);
|
||||
return vbo;
|
||||
}
|
||||
|
||||
} // namespace blender::draw
|
||||
@@ -0,0 +1,324 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "BLI_array_utils.hh"
|
||||
|
||||
#include "draw_subdivision.hh"
|
||||
#include "extract_mesh.hh"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
static gpu::VertBufPtr create_vbo(const int size)
|
||||
{
|
||||
static GPUVertFormat format = GPU_vertformat_from_attribute("index", gpu::VertAttrType::SINT_32);
|
||||
gpu::VertBufPtr vbo = gpu::VertBufPtr(GPU_vertbuf_create_with_format(format));
|
||||
GPU_vertbuf_data_alloc(*vbo, size);
|
||||
return vbo;
|
||||
}
|
||||
|
||||
/* TODO: Use #glVertexID to get loop index and use the data structure on the CPU to retrieve the
|
||||
* select element associated with this loop ID. This would remove the need for this separate
|
||||
* index VBO's. We could upload the p/e/orig_index_vert as a buffer texture and sample it inside
|
||||
* the shader to output original index. */
|
||||
|
||||
static void extract_vert_index_mesh(const MeshRenderData &mr, MutableSpan<int> vbo_data)
|
||||
{
|
||||
MutableSpan corners_data = vbo_data.take_front(mr.corners_num);
|
||||
MutableSpan loose_edge_data = vbo_data.slice(mr.corners_num, mr.loose_edges.size() * 2);
|
||||
MutableSpan loose_vert_data = vbo_data.take_back(mr.loose_verts.size());
|
||||
|
||||
if (mr.orig_index_vert) {
|
||||
const Span<int> orig_index_vert(mr.orig_index_vert, mr.verts_num);
|
||||
array_utils::gather(orig_index_vert, mr.corner_verts, corners_data);
|
||||
extract_mesh_loose_edge_data(orig_index_vert, mr.edges, mr.loose_edges, loose_edge_data);
|
||||
array_utils::gather(orig_index_vert, mr.loose_verts, loose_vert_data);
|
||||
}
|
||||
else {
|
||||
array_utils::copy(mr.corner_verts, corners_data);
|
||||
const Span<int2> edges = mr.edges;
|
||||
mr.loose_edges.foreach_index_optimized<int>(
|
||||
[&](const int i, const int pos) {
|
||||
loose_edge_data[pos * 2 + 0] = edges[i][0];
|
||||
loose_edge_data[pos * 2 + 1] = edges[i][1];
|
||||
},
|
||||
exec_mode::grain_size(4096));
|
||||
mr.loose_verts.to_indices(loose_vert_data);
|
||||
}
|
||||
}
|
||||
|
||||
static void extract_vert_index_bm(const MeshRenderData &mr, MutableSpan<int> vbo_data)
|
||||
{
|
||||
MutableSpan corners_data = vbo_data.take_front(mr.corners_num);
|
||||
MutableSpan loose_edge_data = vbo_data.slice(mr.corners_num, mr.loose_edges.size() * 2);
|
||||
MutableSpan loose_vert_data = vbo_data.take_back(mr.loose_verts.size());
|
||||
|
||||
const BMesh &bm = *mr.bm;
|
||||
threading::parallel_for(IndexRange(bm.totface), 2048, [&](const IndexRange range) {
|
||||
for (const int face_index : range) {
|
||||
const BMFace &face = *BM_face_at_index(&const_cast<BMesh &>(bm), face_index);
|
||||
const BMLoop *loop = BM_FACE_FIRST_LOOP(&face);
|
||||
for ([[maybe_unused]] const int i : IndexRange(face.len)) {
|
||||
const int index = BM_elem_index_get(loop);
|
||||
corners_data[index] = BM_elem_index_get(loop->v);
|
||||
loop = loop->next;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mr.loose_edges.foreach_index(
|
||||
[&](const int i, const int pos) {
|
||||
const BMEdge &edge = *BM_edge_at_index(&const_cast<BMesh &>(bm), i);
|
||||
loose_edge_data[pos * 2 + 0] = BM_elem_index_get(edge.v1);
|
||||
loose_edge_data[pos * 2 + 1] = BM_elem_index_get(edge.v2);
|
||||
},
|
||||
exec_mode::grain_size(4096));
|
||||
|
||||
mr.loose_verts.to_indices(loose_vert_data);
|
||||
}
|
||||
|
||||
gpu::VertBufPtr extract_vert_index(const MeshRenderData &mr)
|
||||
{
|
||||
gpu::VertBufPtr vbo = create_vbo(mr.corners_num + mr.loose_indices_num);
|
||||
if (mr.extract_type == MeshExtractType::Mesh) {
|
||||
extract_vert_index_mesh(mr, vbo->data<int>());
|
||||
}
|
||||
else {
|
||||
extract_vert_index_bm(mr, vbo->data<int>());
|
||||
}
|
||||
return vbo;
|
||||
}
|
||||
|
||||
static void extract_edge_index_mesh(const MeshRenderData &mr, MutableSpan<int> vbo_data)
|
||||
{
|
||||
MutableSpan corners_data = vbo_data.take_front(mr.corners_num);
|
||||
MutableSpan loose_edge_data = vbo_data.slice(mr.corners_num, mr.loose_edges.size() * 2);
|
||||
|
||||
if (mr.orig_index_edge) {
|
||||
const Span<int> orig_index_edge(mr.orig_index_edge, mr.edges_num);
|
||||
array_utils::gather(orig_index_edge, mr.corner_edges, corners_data);
|
||||
mr.loose_edges.foreach_index_optimized<int>(
|
||||
[&](const int i, const int pos) {
|
||||
loose_edge_data[pos * 2 + 0] = orig_index_edge[i];
|
||||
loose_edge_data[pos * 2 + 1] = orig_index_edge[i];
|
||||
},
|
||||
exec_mode::grain_size(4096));
|
||||
}
|
||||
else {
|
||||
array_utils::copy(mr.corner_edges, corners_data);
|
||||
mr.loose_edges.foreach_index_optimized<int>(
|
||||
[&](const int i, const int pos) {
|
||||
loose_edge_data[pos * 2 + 0] = i;
|
||||
loose_edge_data[pos * 2 + 1] = i;
|
||||
},
|
||||
exec_mode::grain_size(4096));
|
||||
}
|
||||
}
|
||||
|
||||
static void extract_edge_index_bm(const MeshRenderData &mr, MutableSpan<int> vbo_data)
|
||||
{
|
||||
MutableSpan corners_data = vbo_data.take_front(mr.corners_num);
|
||||
MutableSpan loose_edge_data = vbo_data.slice(mr.corners_num, mr.loose_edges.size() * 2);
|
||||
|
||||
const BMesh &bm = *mr.bm;
|
||||
threading::parallel_for(IndexRange(bm.totface), 2048, [&](const IndexRange range) {
|
||||
for (const int face_index : range) {
|
||||
const BMFace &face = *BM_face_at_index(&const_cast<BMesh &>(bm), face_index);
|
||||
const BMLoop *loop = BM_FACE_FIRST_LOOP(&face);
|
||||
for ([[maybe_unused]] const int i : IndexRange(face.len)) {
|
||||
const int index = BM_elem_index_get(loop);
|
||||
corners_data[index] = BM_elem_index_get(loop->e);
|
||||
loop = loop->next;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mr.loose_edges.foreach_index_optimized<int>(
|
||||
[&](const int i, const int pos) {
|
||||
loose_edge_data[pos * 2 + 0] = i;
|
||||
loose_edge_data[pos * 2 + 1] = i;
|
||||
},
|
||||
exec_mode::grain_size(4096));
|
||||
}
|
||||
|
||||
gpu::VertBufPtr extract_edge_index(const MeshRenderData &mr)
|
||||
{
|
||||
gpu::VertBufPtr vbo = create_vbo(mr.corners_num + mr.loose_edges.size() * 2);
|
||||
if (mr.extract_type == MeshExtractType::Mesh) {
|
||||
extract_edge_index_mesh(mr, vbo->data<int>());
|
||||
}
|
||||
else {
|
||||
extract_edge_index_bm(mr, vbo->data<int>());
|
||||
}
|
||||
return vbo;
|
||||
}
|
||||
|
||||
static void extract_face_index_mesh(const MeshRenderData &mr, MutableSpan<int> vbo_data)
|
||||
{
|
||||
const OffsetIndices faces = mr.faces;
|
||||
if (mr.orig_index_face) {
|
||||
const Span<int> orig_index_face(mr.orig_index_face, mr.edges_num);
|
||||
threading::parallel_for(faces.index_range(), 2048, [&](const IndexRange range) {
|
||||
for (const int face : range) {
|
||||
vbo_data.slice(faces[face]).fill(orig_index_face[face]);
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
offset_indices::build_reverse_map(faces, vbo_data);
|
||||
}
|
||||
}
|
||||
|
||||
static void extract_face_index_bm(const MeshRenderData &mr, MutableSpan<int> vbo_data)
|
||||
{
|
||||
const BMesh &bm = *mr.bm;
|
||||
threading::parallel_for(IndexRange(bm.totface), 2048, [&](const IndexRange range) {
|
||||
for (const int face_index : range) {
|
||||
const BMFace &face = *BM_face_at_index(&const_cast<BMesh &>(bm), face_index);
|
||||
const IndexRange face_range(BM_elem_index_get(BM_FACE_FIRST_LOOP(&face)), face.len);
|
||||
vbo_data.slice(face_range).fill(face_index);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
gpu::VertBufPtr extract_face_index(const MeshRenderData &mr)
|
||||
{
|
||||
gpu::VertBufPtr vbo = create_vbo(mr.corners_num);
|
||||
if (mr.extract_type == MeshExtractType::Mesh) {
|
||||
extract_face_index_mesh(mr, vbo->data<int>());
|
||||
}
|
||||
else {
|
||||
extract_face_index_bm(mr, vbo->data<int>());
|
||||
}
|
||||
return vbo;
|
||||
}
|
||||
|
||||
static void extract_vert_idx_loose_geom_subdiv(const DRWSubdivCache &subdiv_cache,
|
||||
const MeshRenderData &mr,
|
||||
MutableSpan<int32_t> vbo_data)
|
||||
{
|
||||
const IndexMask &loose_verts = mr.loose_verts;
|
||||
const IndexMask &loose_edges = mr.loose_edges;
|
||||
if (loose_edges.is_empty() && loose_verts.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const Span<int2> coarse_edges = mr.edges;
|
||||
const int verts_per_edge = subdiv_verts_per_coarse_edge(subdiv_cache);
|
||||
MutableSpan<int32_t> edge_data = vbo_data.slice(subdiv_cache.num_subdiv_loops,
|
||||
loose_edges.size() * verts_per_edge);
|
||||
loose_edges.foreach_index([&](const int i, const int pos) {
|
||||
const int2 edge = coarse_edges[i];
|
||||
MutableSpan data = edge_data.slice(pos * verts_per_edge, verts_per_edge);
|
||||
data.first() = mr.orig_index_vert ? mr.orig_index_vert[edge[0]] : edge[0];
|
||||
data.last() = mr.orig_index_vert ? mr.orig_index_vert[edge[1]] : edge[1];
|
||||
});
|
||||
|
||||
MutableSpan<int32_t> loose_vert_data = vbo_data.take_back(loose_verts.size());
|
||||
if (mr.orig_index_vert) {
|
||||
array_utils::gather(Span(mr.orig_index_vert, mr.verts_num), loose_verts, loose_vert_data);
|
||||
}
|
||||
else {
|
||||
loose_verts.to_indices(loose_vert_data);
|
||||
}
|
||||
}
|
||||
|
||||
gpu::VertBufPtr extract_vert_index_subdiv(const DRWSubdivCache &subdiv_cache,
|
||||
const MeshRenderData &mr)
|
||||
{
|
||||
const int loose_len = subdiv_loose_edges_num(mr, subdiv_cache) * 2 + mr.loose_verts.size();
|
||||
/* Each element points to an element in the `ibo.points`. */
|
||||
gpu::VertBufPtr vbo = draw_subdiv_init_origindex_buffer(
|
||||
subdiv_cache.verts_orig_index->data<int32_t>().data(),
|
||||
subdiv_cache.num_subdiv_loops,
|
||||
loose_len);
|
||||
if (!mr.orig_index_vert) {
|
||||
return vbo;
|
||||
}
|
||||
|
||||
/* Remap the vertex indices to those pointed by the origin indices layer. At this point, the
|
||||
* VBO data is a copy of #verts_orig_index which contains the coarse vertices indices, so
|
||||
* the memory can both be accessed for lookup and immediately overwritten. */
|
||||
MutableSpan vbo_data = vbo->data<int32_t>();
|
||||
for (int i = 0; i < subdiv_cache.num_subdiv_loops; i++) {
|
||||
if (vbo_data[i] == -1) {
|
||||
continue;
|
||||
}
|
||||
vbo_data[i] = mr.orig_index_vert[vbo_data[i]];
|
||||
}
|
||||
extract_vert_idx_loose_geom_subdiv(subdiv_cache, mr, vbo_data);
|
||||
return vbo;
|
||||
}
|
||||
|
||||
static void extract_edge_idx_loose_geom_subdiv(const DRWSubdivCache &subdiv_cache,
|
||||
const MeshRenderData &mr,
|
||||
MutableSpan<int32_t> vbo_data)
|
||||
{
|
||||
const IndexMask &loose_edges = mr.loose_edges;
|
||||
if (loose_edges.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int verts_per_edge = subdiv_verts_per_coarse_edge(subdiv_cache);
|
||||
MutableSpan data = vbo_data.slice(subdiv_cache.num_subdiv_loops,
|
||||
loose_edges.size() * verts_per_edge);
|
||||
loose_edges.foreach_index([&](const int edge, const int i) {
|
||||
const int index = mr.orig_index_edge ? mr.orig_index_edge[edge] : edge;
|
||||
data.slice(i * verts_per_edge, verts_per_edge).fill(index);
|
||||
});
|
||||
}
|
||||
|
||||
gpu::VertBufPtr extract_edge_index_subdiv(const DRWSubdivCache &subdiv_cache,
|
||||
const MeshRenderData &mr)
|
||||
{
|
||||
gpu::VertBufPtr vbo = draw_subdiv_init_origindex_buffer(
|
||||
subdiv_cache.edges_orig_index->data<int32_t>().data(),
|
||||
subdiv_cache.num_subdiv_loops,
|
||||
subdiv_loose_edges_num(mr, subdiv_cache) * 2);
|
||||
extract_edge_idx_loose_geom_subdiv(subdiv_cache, mr, vbo->data<int32_t>());
|
||||
return vbo;
|
||||
}
|
||||
|
||||
gpu::VertBufPtr extract_face_index_subdiv(const DRWSubdivCache &subdiv_cache,
|
||||
const MeshRenderData &mr)
|
||||
{
|
||||
gpu::VertBufPtr vbo = draw_subdiv_init_origindex_buffer(
|
||||
subdiv_cache.subdiv_loop_face_index, subdiv_cache.num_subdiv_loops, 0);
|
||||
|
||||
if (!mr.orig_index_face) {
|
||||
return vbo;
|
||||
}
|
||||
|
||||
/* Remap the face indices to those pointed by the origin indices layer. At this point, the
|
||||
* VBO data is a copy of #subdiv_loop_face_index which contains the coarse face indices, so
|
||||
* the memory can both be accessed for lookup and immediately overwritten. */
|
||||
MutableSpan vbo_data = vbo->data<int32_t>();
|
||||
for (int i = 0; i < subdiv_cache.num_subdiv_loops; i++) {
|
||||
vbo_data[i] = mr.orig_index_face[vbo_data[i]];
|
||||
}
|
||||
return vbo;
|
||||
}
|
||||
|
||||
gpu::VertBufPtr extract_face_dot_index(const MeshRenderData &mr)
|
||||
{
|
||||
gpu::VertBufPtr vbo = create_vbo(mr.faces_num);
|
||||
if (mr.extract_type == MeshExtractType::Mesh) {
|
||||
if (mr.orig_index_face) {
|
||||
const Span<int> orig_index_face(mr.orig_index_face, mr.faces_num);
|
||||
array_utils::copy(orig_index_face, vbo->data<int>());
|
||||
}
|
||||
else {
|
||||
array_utils::fill_index_range(vbo->data<int>());
|
||||
}
|
||||
}
|
||||
else {
|
||||
array_utils::fill_index_range(vbo->data<int>());
|
||||
}
|
||||
return vbo;
|
||||
}
|
||||
|
||||
} // namespace blender::draw
|
||||
@@ -0,0 +1,49 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "DNA_meshdata_types.h"
|
||||
|
||||
#include "extract_mesh.hh"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
struct SkinRootData {
|
||||
float size;
|
||||
float3 local_pos;
|
||||
};
|
||||
|
||||
gpu::VertBufPtr extract_skin_roots(const MeshRenderData &mr)
|
||||
{
|
||||
/* Exclusively for edit mode. */
|
||||
BLI_assert(mr.bm);
|
||||
|
||||
static const GPUVertFormat format = []() {
|
||||
GPUVertFormat format{};
|
||||
GPU_vertformat_attr_add(&format, "size", gpu::VertAttrType::SFLOAT_32);
|
||||
GPU_vertformat_attr_add(&format, "local_pos", gpu::VertAttrType::SFLOAT_32_32_32);
|
||||
return format;
|
||||
}();
|
||||
|
||||
Vector<SkinRootData> skin_roots;
|
||||
const int offset = CustomData_get_offset(&mr.bm->vdata, CD_MVERT_SKIN);
|
||||
BMIter iter;
|
||||
BMVert *vert;
|
||||
BM_ITER_MESH (vert, &iter, mr.bm, BM_VERTS_OF_MESH) {
|
||||
const MVertSkin *vs = static_cast<const MVertSkin *> BM_ELEM_CD_GET_VOID_P(vert, offset);
|
||||
if (vs->flag & MVERT_SKIN_ROOT) {
|
||||
skin_roots.append({(vs->radius[0] + vs->radius[1]) * 0.5f, bm_vert_co_get(mr, vert)});
|
||||
}
|
||||
}
|
||||
|
||||
gpu::VertBufPtr vbo = gpu::VertBufPtr(GPU_vertbuf_create_with_format(format));
|
||||
GPU_vertbuf_data_alloc(*vbo, skin_roots.size());
|
||||
vbo->data<SkinRootData>().copy_from(skin_roots);
|
||||
return vbo;
|
||||
}
|
||||
|
||||
} // namespace blender::draw
|
||||
@@ -0,0 +1,236 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include <climits>
|
||||
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "GPU_attribute_convert.hh"
|
||||
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_editmesh_tangent.hh"
|
||||
#include "BKE_mesh.hh"
|
||||
#include "BKE_mesh_tangent.hh"
|
||||
|
||||
#include "extract_mesh.hh"
|
||||
|
||||
#include "draw_subdivision.hh"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
static Array<Array<float4>> extract_tan_init_common(const MeshRenderData &mr,
|
||||
const MeshBatchCache &cache,
|
||||
GPUVertFormat *format,
|
||||
gpu::VertAttrType gpu_attr_type)
|
||||
{
|
||||
GPU_vertformat_deinterleave(format);
|
||||
|
||||
VectorSet<std::string> tan_layers = cache.cd_used.tan;
|
||||
bool use_orco_tan = cache.cd_used.tan_orco;
|
||||
|
||||
const StringRef active_name = mr.mesh->active_uv_map_name();
|
||||
const StringRef default_name = mr.mesh->default_uv_map_name();
|
||||
|
||||
/* FIXME(#91838): This is to avoid a crash when orco tangent was requested but there are valid
|
||||
* uv layers. It would be better to fix the root cause. */
|
||||
if (tan_layers.is_empty() && use_orco_tan && !default_name.is_empty()) {
|
||||
tan_layers.add(default_name);
|
||||
use_orco_tan = false;
|
||||
}
|
||||
|
||||
if (use_orco_tan) {
|
||||
Array<float4> tangents;
|
||||
if (mr.extract_type == MeshExtractType::BMesh) {
|
||||
Array<float3> positions = BM_mesh_vert_coords_alloc(mr.bm);
|
||||
tangents = BKE_editmesh_orco_tangents_calc(
|
||||
mr.edit_bmesh, mr.bm_face_normals, mr.bm_loop_normals, positions);
|
||||
}
|
||||
else {
|
||||
Span<float3> orco;
|
||||
Array<float3> orco_allocated;
|
||||
if (const float3 *orco_ptr = static_cast<const float3 *>(
|
||||
CustomData_get_layer(&mr.mesh->vert_data, CD_ORCO)))
|
||||
{
|
||||
orco = Span(orco_ptr, mr.verts_num);
|
||||
}
|
||||
else {
|
||||
orco_allocated = mr.vert_positions;
|
||||
/* TODO: This is not thread-safe. Draw extraction should not modify the mesh. */
|
||||
BKE_mesh_orco_verts_transform(const_cast<Mesh *>(mr.mesh), orco_allocated, false);
|
||||
orco = orco_allocated;
|
||||
}
|
||||
tangents = bke::mesh::calc_orco_tangents(mr.vert_positions,
|
||||
mr.faces,
|
||||
mr.corner_verts,
|
||||
mr.mesh->corner_tris(),
|
||||
mr.mesh->corner_tri_faces(),
|
||||
mr.sharp_faces,
|
||||
mr.mesh->vert_normals(),
|
||||
mr.face_normals,
|
||||
mr.corner_normals,
|
||||
orco);
|
||||
}
|
||||
|
||||
char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
|
||||
GPU_vertformat_safe_attr_name("orco", attr_safe_name, GPU_MAX_SAFE_ATTR_NAME);
|
||||
SNPRINTF(attr_name, "t%s", attr_safe_name);
|
||||
GPU_vertformat_attr_add(format, attr_name, gpu_attr_type);
|
||||
GPU_vertformat_alias_add(format, "t");
|
||||
GPU_vertformat_alias_add(format, "at");
|
||||
|
||||
return {std::move(tangents)};
|
||||
}
|
||||
|
||||
Vector<StringRef> uv_names;
|
||||
for (const StringRef name : tan_layers.as_span().take_front(MAX_MTFACE)) {
|
||||
if (tan_layers.contains(name)) {
|
||||
char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
|
||||
GPU_vertformat_safe_attr_name(name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME);
|
||||
/* Tangent layer name. */
|
||||
SNPRINTF(attr_name, "t%s", attr_safe_name);
|
||||
GPU_vertformat_attr_add(format, attr_name, gpu_attr_type);
|
||||
/* Active render layer name. */
|
||||
if (name == default_name) {
|
||||
GPU_vertformat_alias_add(format, "t");
|
||||
}
|
||||
/* Active display layer name. */
|
||||
if (name == active_name) {
|
||||
GPU_vertformat_alias_add(format, "at");
|
||||
}
|
||||
|
||||
uv_names.append(name);
|
||||
}
|
||||
}
|
||||
|
||||
if (uv_names.is_empty()) {
|
||||
GPU_vertformat_attr_add(format, "dummy", gpu::VertAttrType::SFLOAT_32);
|
||||
return {};
|
||||
}
|
||||
|
||||
Array<Array<float4>> results;
|
||||
if (mr.extract_type == MeshExtractType::BMesh) {
|
||||
results = BKE_editmesh_uv_tangents_calc(
|
||||
mr.edit_bmesh, mr.bm_face_normals, mr.bm_loop_normals, uv_names);
|
||||
}
|
||||
else {
|
||||
Array<VArraySpan<float2>> uv_maps(uv_names.size());
|
||||
Array<Span<float2>> uv_map_spans(uv_names.size());
|
||||
const bke::AttributeAccessor attributes = mr.mesh->attributes();
|
||||
for (const int i : uv_names.index_range()) {
|
||||
uv_maps[i] = *attributes.lookup<float2>(uv_names[i], bke::AttrDomain::Corner);
|
||||
uv_map_spans[i] = uv_maps[i];
|
||||
}
|
||||
results = bke::mesh::calc_uv_tangents(mr.vert_positions,
|
||||
mr.faces,
|
||||
mr.corner_verts,
|
||||
mr.mesh->corner_tris(),
|
||||
mr.mesh->corner_tri_faces(),
|
||||
mr.sharp_faces,
|
||||
mr.mesh->vert_normals(),
|
||||
mr.face_normals,
|
||||
mr.corner_normals,
|
||||
uv_map_spans);
|
||||
}
|
||||
|
||||
if (format->attr_len == 0) {
|
||||
GPU_vertformat_attr_add(format, "dummy", gpu::VertAttrType::SFLOAT_32);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
gpu::VertBufPtr extract_tangents(const MeshRenderData &mr,
|
||||
const MeshBatchCache &cache,
|
||||
const bool use_hq)
|
||||
{
|
||||
gpu::VertAttrType gpu_attr_type = use_hq ? gpu::VertAttrType::SNORM_16_16_16_16 :
|
||||
gpu::VertAttrType::SNORM_10_10_10_2;
|
||||
|
||||
GPUVertFormat format = {0};
|
||||
const Array<Array<float4>> tangents = extract_tan_init_common(mr, cache, &format, gpu_attr_type);
|
||||
|
||||
gpu::VertBufPtr vbo = gpu::VertBufPtr(GPU_vertbuf_create_with_format(format));
|
||||
GPU_vertbuf_data_alloc(*vbo, mr.corners_num);
|
||||
|
||||
if (use_hq) {
|
||||
MutableSpan tan_data = vbo->data<short4>();
|
||||
int vbo_index = 0;
|
||||
for (const int i : tangents.index_range()) {
|
||||
const Span<float4> layer_data = tangents[i];
|
||||
for (int corner = 0; corner < mr.corners_num; corner++) {
|
||||
tan_data[vbo_index] = gpu::convert_normal<short4>(float3(layer_data[corner]));
|
||||
tan_data[vbo_index].w = (layer_data[corner][3] > 0.0f) ? SHRT_MAX : SHRT_MIN;
|
||||
vbo_index++;
|
||||
}
|
||||
}
|
||||
BLI_assert(vbo_index == tan_data.size());
|
||||
}
|
||||
else {
|
||||
MutableSpan tan_data = vbo->data<gpu::PackedNormal>();
|
||||
int vbo_index = 0;
|
||||
for (const int i : tangents.index_range()) {
|
||||
const Span<float4> layer_data = tangents[i];
|
||||
for (int corner = 0; corner < mr.corners_num; corner++) {
|
||||
tan_data[vbo_index] = gpu::convert_normal<gpu::PackedNormal>(float3(layer_data[corner]));
|
||||
tan_data[vbo_index].w = (layer_data[corner][3] > 0.0f) ? 1 : -2;
|
||||
vbo_index++;
|
||||
}
|
||||
}
|
||||
BLI_assert(vbo_index == tan_data.size());
|
||||
}
|
||||
|
||||
return vbo;
|
||||
}
|
||||
|
||||
static const GPUVertFormat &get_coarse_tan_format()
|
||||
{
|
||||
static GPUVertFormat format = GPU_vertformat_from_attribute(
|
||||
"tan", gpu::VertAttrType::SFLOAT_32_32_32_32);
|
||||
return format;
|
||||
}
|
||||
|
||||
gpu::VertBufPtr extract_tangents_subdiv(const MeshRenderData &mr,
|
||||
const DRWSubdivCache &subdiv_cache,
|
||||
const MeshBatchCache &cache)
|
||||
{
|
||||
gpu::VertAttrType gpu_attr_type = gpu::VertAttrType::SFLOAT_32_32_32_32;
|
||||
GPUVertFormat format = {0};
|
||||
const Array<Array<float4>> tangents = extract_tan_init_common(mr, cache, &format, gpu_attr_type);
|
||||
|
||||
gpu::VertBufPtr vbo = gpu::VertBufPtr(
|
||||
GPU_vertbuf_create_on_device(format, subdiv_cache.num_subdiv_loops));
|
||||
|
||||
gpu::VertBuf *coarse_vbo = GPU_vertbuf_calloc();
|
||||
/* Dynamic as we upload and interpolate layers one at a time. */
|
||||
GPU_vertbuf_init_with_format_ex(*coarse_vbo, get_coarse_tan_format(), GPU_USAGE_DYNAMIC);
|
||||
GPU_vertbuf_data_alloc(*coarse_vbo, mr.corners_num);
|
||||
|
||||
/* Index of the tangent layer in the compact buffer. Used layers are stored in a single buffer.
|
||||
*/
|
||||
int pack_layer_index = 0;
|
||||
for (const int i : tangents.index_range()) {
|
||||
float4 *tan_data = coarse_vbo->data<float4>().data();
|
||||
const Span<float4> values = tangents[i];
|
||||
for (int corner = 0; corner < mr.corners_num; corner++) {
|
||||
*tan_data = values[corner];
|
||||
(*tan_data)[3] = (values[corner][3] > 0.0f) ? 1.0f : -1.0f;
|
||||
tan_data++;
|
||||
}
|
||||
|
||||
/* Ensure data is uploaded properly. */
|
||||
GPU_vertbuf_tag_dirty(coarse_vbo);
|
||||
/* Include stride in offset. */
|
||||
const int dst_offset = int(subdiv_cache.num_subdiv_loops) * 4 * pack_layer_index++;
|
||||
draw_subdiv_interp_custom_data(subdiv_cache, *coarse_vbo, *vbo, GPU_COMP_F32, 4, dst_offset);
|
||||
}
|
||||
|
||||
GPU_vertbuf_discard(coarse_vbo);
|
||||
return vbo;
|
||||
}
|
||||
|
||||
} // namespace blender::draw
|
||||
@@ -0,0 +1,175 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "BLI_array_utils.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_string_utf8.h"
|
||||
|
||||
#include "BKE_anonymous_attribute_id.hh"
|
||||
#include "BKE_attribute.hh"
|
||||
|
||||
#include "draw_subdivision.hh"
|
||||
#include "extract_mesh.hh"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
/* Initialize the vertex format to be used for UVs. Return true if any UV layer is
|
||||
* found, false otherwise. */
|
||||
static VectorSet<StringRef> mesh_extract_uv_format_init(GPUVertFormat *format,
|
||||
const MeshBatchCache &cache,
|
||||
const Mesh &mesh,
|
||||
const MeshExtractType extract_type)
|
||||
{
|
||||
GPU_vertformat_deinterleave(format);
|
||||
|
||||
VectorSet<StringRef> uv_layers;
|
||||
for (const StringRef name : cache.cd_used.uv.as_span().take_front(MAX_MTFACE)) {
|
||||
uv_layers.add_new(name);
|
||||
}
|
||||
|
||||
const StringRef active_name = mesh.active_or_default_uv_map_name();
|
||||
const StringRef default_name = mesh.default_uv_map_name();
|
||||
|
||||
/* HACK to fix #68857 */
|
||||
if (extract_type == MeshExtractType::BMesh && cache.cd_used.edit_uv == 1) {
|
||||
if (!bke::attribute_name_is_anonymous(default_name)) {
|
||||
uv_layers.add(default_name);
|
||||
}
|
||||
}
|
||||
|
||||
const StringRef stencil_name = [&]() -> StringRef {
|
||||
StringRef name = mesh.stencil_uv_map_attribute;
|
||||
if (name.is_empty()) {
|
||||
name = mesh.active_uv_map_name();
|
||||
}
|
||||
return name;
|
||||
}();
|
||||
|
||||
for (const StringRef name : uv_layers) {
|
||||
char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
|
||||
GPU_vertformat_safe_attr_name(name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME);
|
||||
SNPRINTF_UTF8(attr_name, "a%s", attr_safe_name);
|
||||
GPU_vertformat_attr_add(format, attr_name, gpu::VertAttrType::SFLOAT_32_32);
|
||||
if (name == default_name) {
|
||||
GPU_vertformat_alias_add(format, "a");
|
||||
}
|
||||
if (name == active_name) {
|
||||
GPU_vertformat_alias_add(format, "au");
|
||||
/* Alias to `pos` for edit uvs. */
|
||||
GPU_vertformat_alias_add(format, "pos");
|
||||
}
|
||||
if (name == stencil_name) {
|
||||
GPU_vertformat_alias_add(format, "mu");
|
||||
}
|
||||
}
|
||||
|
||||
if (format->attr_len == 0) {
|
||||
GPU_vertformat_attr_add(format, "dummy", gpu::VertAttrType::SFLOAT_32_32);
|
||||
}
|
||||
|
||||
return uv_layers;
|
||||
}
|
||||
|
||||
gpu::VertBufPtr extract_uv_maps(const MeshRenderData &mr, const MeshBatchCache &cache)
|
||||
{
|
||||
GPUVertFormat format = {0};
|
||||
|
||||
int v_len = mr.corners_num;
|
||||
const VectorSet<StringRef> uv_layers = mesh_extract_uv_format_init(
|
||||
&format, cache, *mr.mesh, mr.extract_type);
|
||||
if (uv_layers.is_empty()) {
|
||||
/* VBO will not be used, only allocate minimum of memory. */
|
||||
v_len = 1;
|
||||
}
|
||||
|
||||
gpu::VertBufPtr vbo = gpu::VertBufPtr(GPU_vertbuf_create_with_format(format));
|
||||
GPU_vertbuf_data_alloc(*vbo, v_len);
|
||||
|
||||
MutableSpan<float2> uv_data = vbo->data<float2>();
|
||||
threading::memory_bandwidth_bound_task(uv_data.size_in_bytes() * 2, [&]() {
|
||||
if (mr.extract_type == MeshExtractType::BMesh) {
|
||||
const BMesh &bm = *mr.bm;
|
||||
for (const int i : uv_layers.index_range()) {
|
||||
MutableSpan<float2> data = uv_data.slice(i * bm.totloop, bm.totloop);
|
||||
const int offset = CustomData_get_offset_named(&bm.ldata, CD_PROP_FLOAT2, uv_layers[i]);
|
||||
threading::parallel_for(IndexRange(bm.totface), 2048, [&](const IndexRange range) {
|
||||
for (const int face_index : range) {
|
||||
const BMFace &face = *BM_face_at_index(&const_cast<BMesh &>(bm), face_index);
|
||||
const BMLoop *loop = BM_FACE_FIRST_LOOP(&face);
|
||||
for ([[maybe_unused]] const int i : IndexRange(face.len)) {
|
||||
const int index = BM_elem_index_get(loop);
|
||||
data[index] = BM_ELEM_CD_GET_FLOAT_P(loop, offset);
|
||||
loop = loop->next;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
const bke::AttributeAccessor attributes = mr.mesh->attributes();
|
||||
for (const int i : uv_layers.index_range()) {
|
||||
const VArray uv_map = *attributes.lookup_or_default<float2>(
|
||||
uv_layers[i], bke::AttrDomain::Corner, float2(0));
|
||||
array_utils::copy(uv_map, uv_data.slice(i * mr.corners_num, mr.corners_num));
|
||||
}
|
||||
}
|
||||
});
|
||||
return vbo;
|
||||
}
|
||||
|
||||
static VectorSet<StringRef> all_uv_map_attributes(const Mesh &mesh)
|
||||
{
|
||||
const bke::AttributeAccessor attributes = mesh.attributes();
|
||||
VectorSet<StringRef> result;
|
||||
attributes.foreach_attribute([&](const bke::AttributeIter &iter) {
|
||||
if (iter.domain == bke::AttrDomain::Corner && iter.data_type == bke::AttrType::Float2) {
|
||||
result.add(iter.name);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
gpu::VertBufPtr extract_uv_maps_subdiv(const DRWSubdivCache &subdiv_cache,
|
||||
const MeshBatchCache &cache)
|
||||
{
|
||||
const Mesh *coarse_mesh = subdiv_cache.mesh;
|
||||
GPUVertFormat format = {0};
|
||||
|
||||
const VectorSet<StringRef> uv_layers = mesh_extract_uv_format_init(
|
||||
&format, cache, *coarse_mesh, MeshExtractType::Mesh);
|
||||
|
||||
uint v_len = subdiv_cache.num_subdiv_loops;
|
||||
if (uv_layers.is_empty()) {
|
||||
/* TODO(kevindietrich): handle this more gracefully. */
|
||||
v_len = 1;
|
||||
}
|
||||
|
||||
gpu::VertBufPtr vbo = gpu::VertBufPtr(GPU_vertbuf_create_on_device(format, v_len));
|
||||
|
||||
if (uv_layers.is_empty()) {
|
||||
return vbo;
|
||||
}
|
||||
|
||||
const VectorSet<StringRef> all_uv_maps = all_uv_map_attributes(*coarse_mesh);
|
||||
|
||||
/* Index of the UV layer in the compact buffer. Used UV layers are stored in a single buffer. */
|
||||
for (const int pack_layer_index : uv_layers.index_range()) {
|
||||
const StringRef name = uv_layers[pack_layer_index];
|
||||
/* This lookup may fail because some modifiers (e.g. Skin)
|
||||
* do not generate UV maps. See: #151787. */
|
||||
const int i = all_uv_maps.index_of_try(name);
|
||||
if (i == -1) {
|
||||
continue;
|
||||
}
|
||||
const int offset = int(subdiv_cache.num_subdiv_loops) * pack_layer_index;
|
||||
draw_subdiv_extract_uvs(subdiv_cache, vbo.get(), i, offset);
|
||||
}
|
||||
return vbo;
|
||||
}
|
||||
|
||||
} // namespace blender::draw
|
||||
@@ -0,0 +1,92 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup draw
|
||||
*/
|
||||
|
||||
#include "BLI_array_utils.hh"
|
||||
|
||||
#include "GPU_attribute_convert.hh"
|
||||
|
||||
#include "extract_mesh.hh"
|
||||
|
||||
namespace blender::draw {
|
||||
|
||||
static void extract_vert_normals_mesh(const MeshRenderData &mr,
|
||||
MutableSpan<gpu::PackedNormal> vbo_data)
|
||||
{
|
||||
MutableSpan corners_data = vbo_data.take_front(mr.corners_num);
|
||||
MutableSpan loose_edge_data = vbo_data.slice(mr.corners_num, mr.loose_edges.size() * 2);
|
||||
MutableSpan loose_vert_data = vbo_data.take_back(mr.loose_verts.size());
|
||||
|
||||
const Span<float3> vert_normals = mr.mesh->vert_normals();
|
||||
|
||||
Array<gpu::PackedNormal> converted(vert_normals.size());
|
||||
convert_normals(vert_normals, converted.as_mutable_span());
|
||||
static_assert(sizeof(gpu::PackedNormal) == sizeof(int32_t));
|
||||
array_utils::gather(
|
||||
converted.as_span().cast<int32_t>(), mr.corner_verts, corners_data.cast<int32_t>());
|
||||
extract_mesh_loose_edge_data(converted.as_span(), mr.edges, mr.loose_edges, loose_edge_data);
|
||||
array_utils::gather(
|
||||
converted.as_span().cast<int32_t>(), mr.loose_verts, loose_vert_data.cast<int32_t>());
|
||||
}
|
||||
|
||||
static void extract_vert_normals_bm(const MeshRenderData &mr,
|
||||
MutableSpan<gpu::PackedNormal> vbo_data)
|
||||
{
|
||||
const BMesh &bm = *mr.bm;
|
||||
MutableSpan corners_data = vbo_data.take_front(mr.corners_num);
|
||||
MutableSpan loose_edge_data = vbo_data.slice(mr.corners_num, mr.loose_edges.size() * 2);
|
||||
MutableSpan loose_vert_data = vbo_data.take_back(mr.loose_verts.size());
|
||||
|
||||
threading::parallel_for(IndexRange(bm.totface), 2048, [&](const IndexRange range) {
|
||||
for (const int face_index : range) {
|
||||
const BMFace &face = *BM_face_at_index(&const_cast<BMesh &>(bm), face_index);
|
||||
const BMLoop *loop = BM_FACE_FIRST_LOOP(&face);
|
||||
for ([[maybe_unused]] const int i : IndexRange(face.len)) {
|
||||
const int index = BM_elem_index_get(loop);
|
||||
corners_data[index] = gpu::convert_normal<gpu::PackedNormal>(bm_vert_no_get(mr, loop->v));
|
||||
loop = loop->next;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mr.loose_edges.foreach_index(
|
||||
[&](const int i, const int pos) {
|
||||
const BMEdge &edge = *BM_edge_at_index(&const_cast<BMesh &>(bm), i);
|
||||
loose_edge_data[pos * 2 + 0] = gpu::convert_normal<gpu::PackedNormal>(
|
||||
bm_vert_no_get(mr, edge.v1));
|
||||
loose_edge_data[pos * 2 + 1] = gpu::convert_normal<gpu::PackedNormal>(
|
||||
bm_vert_no_get(mr, edge.v2));
|
||||
},
|
||||
exec_mode::grain_size(2048));
|
||||
|
||||
mr.loose_verts.foreach_index(
|
||||
[&](const int i, const int pos) {
|
||||
const BMVert &vert = *BM_vert_at_index(&const_cast<BMesh &>(bm), i);
|
||||
loose_vert_data[pos] = gpu::convert_normal<gpu::PackedNormal>(bm_vert_no_get(mr, &vert));
|
||||
},
|
||||
exec_mode::grain_size(2048));
|
||||
}
|
||||
|
||||
gpu::VertBufPtr extract_vert_normals(const MeshRenderData &mr)
|
||||
{
|
||||
static GPUVertFormat format = GPU_vertformat_from_attribute("vnor",
|
||||
gpu::VertAttrType::SNORM_10_10_10_2);
|
||||
|
||||
gpu::VertBufPtr vbo = gpu::VertBufPtr(GPU_vertbuf_create_with_format(format));
|
||||
GPU_vertbuf_data_alloc(*vbo, mr.corners_num + mr.loose_indices_num);
|
||||
MutableSpan vbo_data = vbo->data<gpu::PackedNormal>();
|
||||
|
||||
if (mr.extract_type == MeshExtractType::Mesh) {
|
||||
extract_vert_normals_mesh(mr, vbo_data);
|
||||
}
|
||||
else {
|
||||
extract_vert_normals_bm(mr, vbo_data);
|
||||
}
|
||||
return vbo;
|
||||
}
|
||||
|
||||
} // namespace blender::draw
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user