Files
test/source/blender/draw/engines/workbench/workbench_engine.cc
2024-04-29 22:21:23 +02:00

852 lines
29 KiB
C++

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BKE_editmesh.hh"
#include "BKE_modifier.hh"
#include "BKE_object.hh"
#include "BKE_paint.hh"
#include "BKE_particle.h"
#include "BKE_pbvh_api.hh"
#include "BKE_report.hh"
#include "DEG_depsgraph_query.hh"
#include "DNA_fluid_types.h"
#include "ED_paint.hh"
#include "ED_view3d.hh"
#include "GPU_capabilities.hh"
#include "IMB_imbuf_types.hh"
#include "draw_common.hh"
#include "draw_sculpt.hh"
#include "workbench_private.hh"
#include "workbench_engine.h" /* Own include. */
namespace blender::workbench {
using namespace draw;
class Instance {
public:
View view = {"DefaultView"};
SceneState scene_state;
SceneResources resources;
OpaquePass opaque_ps;
TransparentPass transparent_ps;
TransparentDepthPass transparent_depth_ps;
ShadowPass shadow_ps;
VolumePass volume_ps;
OutlinePass outline_ps;
DofPass dof_ps;
AntiAliasingPass anti_aliasing_ps;
/* An array of nullptr GPUMaterial pointers so we can call DRW_cache_object_surface_material_get.
* They never get actually used. */
Vector<GPUMaterial *> dummy_gpu_materials = {1, nullptr, {}};
GPUMaterial **get_dummy_gpu_materials(int material_count)
{
if (material_count > dummy_gpu_materials.size()) {
dummy_gpu_materials.resize(material_count, nullptr);
}
return dummy_gpu_materials.begin();
};
void init(Object *camera_ob = nullptr)
{
scene_state.init(camera_ob);
shadow_ps.init(scene_state, resources);
resources.init(scene_state);
outline_ps.init(scene_state);
dof_ps.init(scene_state);
anti_aliasing_ps.init(scene_state);
}
void begin_sync()
{
resources.material_buf.clear_and_trim();
opaque_ps.sync(scene_state, resources);
transparent_ps.sync(scene_state, resources);
transparent_depth_ps.sync(scene_state, resources);
shadow_ps.sync();
volume_ps.sync(resources);
outline_ps.sync(resources);
dof_ps.sync(resources);
anti_aliasing_ps.sync(scene_state, resources);
}
void end_sync()
{
resources.material_buf.push_update();
}
Material get_material(ObjectRef ob_ref, eV3DShadingColorType color_type, int slot = 0)
{
switch (color_type) {
case V3D_SHADING_OBJECT_COLOR:
return Material(*ob_ref.object);
case V3D_SHADING_RANDOM_COLOR:
return Material(*ob_ref.object, true);
case V3D_SHADING_SINGLE_COLOR:
return scene_state.material_override;
case V3D_SHADING_VERTEX_COLOR:
return scene_state.material_attribute_color;
case V3D_SHADING_TEXTURE_COLOR:
ATTR_FALLTHROUGH;
case V3D_SHADING_MATERIAL_COLOR:
if (::Material *_mat = BKE_object_material_get_eval(ob_ref.object, slot + 1)) {
return Material(*_mat);
}
ATTR_FALLTHROUGH;
default:
return Material(*BKE_material_default_empty());
}
}
void object_sync(Manager &manager, ObjectRef &ob_ref)
{
if (scene_state.render_finished) {
return;
}
Object *ob = ob_ref.object;
if (!DRW_object_is_renderable(ob)) {
return;
}
const ObjectState object_state = ObjectState(scene_state, ob);
/* Needed for mesh cache validation, to prevent two copies of
* of vertex color arrays from being sent to the GPU (e.g.
* when switching from eevee to workbench).
*/
if (ob_ref.object->sculpt && ob_ref.object->sculpt->pbvh) {
/* TODO(Miguel Pozo): Could this me moved to sculpt_batches_get()? */
BKE_pbvh_is_drawing_set(*ob_ref.object->sculpt->pbvh, object_state.sculpt_pbvh);
}
bool is_object_data_visible = (DRW_object_visibility_in_active_context(ob) &
OB_VISIBLE_SELF) &&
(ob->dt >= OB_SOLID || DRW_state_is_scene_render());
if (!(ob->base_flag & BASE_FROM_DUPLI)) {
ModifierData *md = BKE_modifiers_findby_type(ob, eModifierType_Fluid);
if (md && BKE_modifier_is_enabled(scene_state.scene, md, eModifierMode_Realtime)) {
FluidModifierData *fmd = (FluidModifierData *)md;
if (fmd->domain) {
volume_ps.object_sync_modifier(manager, resources, scene_state, ob_ref, md);
if (fmd->domain->type == FLUID_DOMAIN_TYPE_GAS) {
/* Do not draw solid in this case. */
is_object_data_visible = false;
}
}
}
}
ResourceHandle emitter_handle(0);
if (is_object_data_visible) {
if (object_state.sculpt_pbvh) {
const Bounds<float3> bounds = bke::pbvh::bounds_get(*ob_ref.object->sculpt->pbvh);
const float3 center = math::midpoint(bounds.min, bounds.max);
const float3 half_extent = bounds.max - center;
ResourceHandle handle = manager.resource_handle(ob_ref, nullptr, &center, &half_extent);
sculpt_sync(ob_ref, handle, object_state);
emitter_handle = handle;
}
else if (ob->type == OB_MESH) {
ResourceHandle handle = manager.resource_handle(ob_ref);
mesh_sync(ob_ref, handle, object_state);
emitter_handle = handle;
}
else if (ob->type == OB_POINTCLOUD) {
point_cloud_sync(manager, ob_ref, object_state);
}
else if (ob->type == OB_CURVES) {
curves_sync(manager, ob_ref, object_state);
}
else if (ob->type == OB_VOLUME) {
if (scene_state.shading.type != OB_WIRE) {
volume_ps.object_sync_volume(manager,
resources,
scene_state,
ob_ref,
get_material(ob_ref, object_state.color_type).base_color);
}
}
}
if (ob->type == OB_MESH && ob->modifiers.first != nullptr) {
LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
if (md->type != eModifierType_ParticleSystem) {
continue;
}
ParticleSystem *psys = ((ParticleSystemModifierData *)md)->psys;
if (!DRW_object_is_visible_psys_in_active_context(ob, psys)) {
continue;
}
ParticleSettings *part = psys->part;
const int draw_as = (part->draw_as == PART_DRAW_REND) ? part->ren_as : part->draw_as;
if (draw_as == PART_DRAW_PATH) {
hair_sync(manager, ob_ref, emitter_handle, object_state, psys, md);
}
}
}
}
template<typename F>
void draw_to_mesh_pass(ObjectRef &ob_ref, bool is_transparent, F draw_callback)
{
const bool in_front = (ob_ref.object->dtx & OB_DRAW_IN_FRONT) != 0;
if (scene_state.xray_mode || is_transparent) {
if (in_front) {
draw_callback(transparent_ps.accumulation_in_front_ps_);
draw_callback(transparent_depth_ps.in_front_ps_);
}
else {
draw_callback(transparent_ps.accumulation_ps_);
draw_callback(transparent_depth_ps.main_ps_);
}
}
else {
if (in_front) {
draw_callback(opaque_ps.gbuffer_in_front_ps_);
}
else {
draw_callback(opaque_ps.gbuffer_ps_);
}
}
}
void draw_mesh(ObjectRef &ob_ref,
Material &material,
gpu::Batch *batch,
ResourceHandle handle,
::Image *image = nullptr,
GPUSamplerState sampler_state = GPUSamplerState::default_sampler(),
ImageUser *iuser = nullptr)
{
resources.material_buf.append(material);
int material_index = resources.material_buf.size() - 1;
draw_to_mesh_pass(ob_ref, material.is_transparent(), [&](MeshPass &mesh_pass) {
mesh_pass.get_subpass(eGeometryType::MESH, image, sampler_state, iuser)
.draw(batch, handle, material_index);
});
}
void mesh_sync(ObjectRef &ob_ref, ResourceHandle handle, const ObjectState &object_state)
{
bool has_transparent_material = false;
if (object_state.use_per_material_batches) {
const int material_count = DRW_cache_object_material_count_get(ob_ref.object);
gpu::Batch **batches;
if (object_state.color_type == V3D_SHADING_TEXTURE_COLOR) {
batches = DRW_cache_mesh_surface_texpaint_get(ob_ref.object);
}
else {
batches = DRW_cache_object_surface_material_get(
ob_ref.object, get_dummy_gpu_materials(material_count), material_count);
}
if (batches) {
for (auto i : IndexRange(material_count)) {
if (batches[i] == nullptr) {
continue;
}
int material_slot = i;
Material mat = get_material(ob_ref, object_state.color_type, material_slot);
has_transparent_material = has_transparent_material || mat.is_transparent();
::Image *image = nullptr;
ImageUser *iuser = nullptr;
GPUSamplerState sampler_state = GPUSamplerState::default_sampler();
if (object_state.color_type == V3D_SHADING_TEXTURE_COLOR) {
get_material_image(ob_ref.object, material_slot, image, iuser, sampler_state);
}
draw_mesh(ob_ref, mat, batches[i], handle, image, sampler_state, iuser);
}
}
}
else {
gpu::Batch *batch;
if (object_state.color_type == V3D_SHADING_TEXTURE_COLOR) {
batch = DRW_cache_mesh_surface_texpaint_single_get(ob_ref.object);
}
else if (object_state.color_type == V3D_SHADING_VERTEX_COLOR) {
if (ob_ref.object->mode & OB_MODE_VERTEX_PAINT) {
batch = DRW_cache_mesh_surface_vertpaint_get(ob_ref.object);
}
else {
batch = DRW_cache_mesh_surface_sculptcolors_get(ob_ref.object);
}
}
else {
batch = DRW_cache_object_surface_get(ob_ref.object);
}
if (batch) {
Material mat = get_material(ob_ref, object_state.color_type);
has_transparent_material = has_transparent_material || mat.is_transparent();
draw_mesh(ob_ref,
mat,
batch,
handle,
object_state.image_paint_override,
object_state.override_sampler_state);
}
}
if (object_state.draw_shadow) {
shadow_ps.object_sync(scene_state, ob_ref, handle, has_transparent_material);
}
}
void sculpt_sync(ObjectRef &ob_ref, ResourceHandle handle, const ObjectState &object_state)
{
SculptBatchFeature features = SCULPT_BATCH_DEFAULT;
if (object_state.color_type == V3D_SHADING_VERTEX_COLOR) {
features = SCULPT_BATCH_VERTEX_COLOR;
}
else if (object_state.color_type == V3D_SHADING_TEXTURE_COLOR) {
features = SCULPT_BATCH_UV;
}
if (object_state.use_per_material_batches) {
for (SculptBatch &batch : sculpt_batches_get(ob_ref.object, features)) {
Material mat = get_material(ob_ref, object_state.color_type, batch.material_slot);
if (SCULPT_DEBUG_DRAW) {
mat.base_color = batch.debug_color();
}
::Image *image = nullptr;
ImageUser *iuser = nullptr;
GPUSamplerState sampler_state = GPUSamplerState::default_sampler();
if (object_state.color_type == V3D_SHADING_TEXTURE_COLOR) {
get_material_image(ob_ref.object, batch.material_slot, image, iuser, sampler_state);
}
draw_mesh(ob_ref, mat, batch.batch, handle, image, sampler_state);
}
}
else {
Material mat = get_material(ob_ref, object_state.color_type);
for (SculptBatch &batch : sculpt_batches_get(ob_ref.object, features)) {
if (SCULPT_DEBUG_DRAW) {
mat.base_color = batch.debug_color();
}
draw_mesh(ob_ref,
mat,
batch.batch,
handle,
object_state.image_paint_override,
object_state.override_sampler_state);
}
}
}
void point_cloud_sync(Manager &manager, ObjectRef &ob_ref, const ObjectState &object_state)
{
ResourceHandle handle = manager.resource_handle(ob_ref);
Material mat = get_material(ob_ref, object_state.color_type);
resources.material_buf.append(mat);
int material_index = resources.material_buf.size() - 1;
draw_to_mesh_pass(ob_ref, mat.is_transparent(), [&](MeshPass &mesh_pass) {
PassMain::Sub &pass =
mesh_pass.get_subpass(eGeometryType::POINTCLOUD).sub("Point Cloud SubPass");
gpu::Batch *batch = point_cloud_sub_pass_setup(pass, ob_ref.object);
pass.draw(batch, handle, material_index);
});
}
void hair_sync(Manager &manager,
ObjectRef &ob_ref,
ResourceHandle emitter_handle,
const ObjectState &object_state,
ParticleSystem *psys,
ModifierData *md)
{
/* Skip frustum culling. */
ResourceHandle handle = manager.resource_handle(ob_ref.object->object_to_world());
Material mat = get_material(ob_ref, object_state.color_type, psys->part->omat - 1);
::Image *image = nullptr;
ImageUser *iuser = nullptr;
GPUSamplerState sampler_state = GPUSamplerState::default_sampler();
if (object_state.color_type == V3D_SHADING_TEXTURE_COLOR) {
get_material_image(ob_ref.object, psys->part->omat - 1, image, iuser, sampler_state);
}
resources.material_buf.append(mat);
int material_index = resources.material_buf.size() - 1;
draw_to_mesh_pass(ob_ref, mat.is_transparent(), [&](MeshPass &mesh_pass) {
PassMain::Sub &pass = mesh_pass
.get_subpass(eGeometryType::CURVES, image, sampler_state, iuser)
.sub("Hair SubPass");
pass.push_constant("emitter_object_id", int(emitter_handle.raw));
gpu::Batch *batch = hair_sub_pass_setup(pass, scene_state.scene, ob_ref.object, psys, md);
pass.draw(batch, handle, material_index);
});
}
void curves_sync(Manager &manager, ObjectRef &ob_ref, const ObjectState &object_state)
{
/* Skip frustum culling. */
ResourceHandle handle = manager.resource_handle(ob_ref.object->object_to_world());
Material mat = get_material(ob_ref, object_state.color_type);
resources.material_buf.append(mat);
int material_index = resources.material_buf.size() - 1;
draw_to_mesh_pass(ob_ref, mat.is_transparent(), [&](MeshPass &mesh_pass) {
PassMain::Sub &pass = mesh_pass.get_subpass(eGeometryType::CURVES).sub("Curves SubPass");
gpu::Batch *batch = curves_sub_pass_setup(pass, scene_state.scene, ob_ref.object);
pass.draw(batch, handle, material_index);
});
}
void draw(Manager &manager,
GPUTexture *depth_tx,
GPUTexture *depth_in_front_tx,
GPUTexture *color_tx)
{
view.sync(DRW_view_default_get());
int2 resolution = scene_state.resolution;
/** Always setup in-front depth, since Overlays can be updated without causing a Workbench
* re-sync (See #113580). */
bool needs_depth_in_front = !transparent_ps.accumulation_in_front_ps_.is_empty() ||
(!opaque_ps.gbuffer_in_front_ps_.is_empty() &&
scene_state.overlays_enabled && scene_state.sample == 0);
resources.depth_in_front_tx.wrap(needs_depth_in_front ? depth_in_front_tx : nullptr);
if ((!needs_depth_in_front && scene_state.overlays_enabled) ||
(needs_depth_in_front && opaque_ps.gbuffer_in_front_ps_.is_empty()))
{
resources.clear_in_front_fb.ensure(GPU_ATTACHMENT_TEXTURE(depth_in_front_tx));
resources.clear_in_front_fb.bind();
GPU_framebuffer_clear_depth_stencil(resources.clear_in_front_fb, 1.0f, 0x00);
}
if (scene_state.render_finished) {
/* Just copy back the already rendered result */
anti_aliasing_ps.draw(manager, view, scene_state, resources, depth_in_front_tx);
return;
}
anti_aliasing_ps.setup_view(view, scene_state);
resources.depth_tx.wrap(depth_tx);
resources.color_tx.wrap(color_tx);
GPUAttachment id_attachment = GPU_ATTACHMENT_NONE;
if (scene_state.draw_object_id) {
resources.object_id_tx.acquire(
resolution, GPU_R16UI, GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT);
id_attachment = GPU_ATTACHMENT_TEXTURE(resources.object_id_tx);
}
resources.clear_fb.ensure(GPU_ATTACHMENT_TEXTURE(resources.depth_tx),
GPU_ATTACHMENT_TEXTURE(resources.color_tx),
id_attachment);
resources.clear_fb.bind();
float4 clear_colors[2] = {scene_state.background_color, float4(0.0f)};
GPU_framebuffer_multi_clear(resources.clear_fb, reinterpret_cast<float(*)[4]>(clear_colors));
GPU_framebuffer_clear_depth_stencil(resources.clear_fb, 1.0f, 0x00);
opaque_ps.draw(
manager, view, resources, resolution, scene_state.draw_shadows ? &shadow_ps : nullptr);
transparent_ps.draw(manager, view, resources, resolution);
transparent_depth_ps.draw(manager, view, resources);
volume_ps.draw(manager, view, resources);
outline_ps.draw(manager, resources);
dof_ps.draw(manager, view, resources, resolution);
anti_aliasing_ps.draw(manager, view, scene_state, resources, depth_in_front_tx);
resources.object_id_tx.release();
}
void draw_viewport(Manager &manager,
GPUTexture *depth_tx,
GPUTexture *depth_in_front_tx,
GPUTexture *color_tx)
{
this->draw(manager, depth_tx, depth_in_front_tx, color_tx);
if (scene_state.sample + 1 < scene_state.samples_len) {
DRW_viewport_request_redraw();
}
}
void draw_image_render(Manager &manager,
GPUTexture *depth_tx,
GPUTexture *depth_in_front_tx,
GPUTexture *color_tx,
RenderEngine *engine = nullptr)
{
BLI_assert(scene_state.sample == 0);
for (auto i : IndexRange(scene_state.samples_len)) {
if (engine && RE_engine_test_break(engine)) {
break;
}
if (i != 0) {
scene_state.sample = i;
/* Re-sync anything dependent on scene_state.sample. */
resources.init(scene_state);
dof_ps.init(scene_state);
anti_aliasing_ps.sync(scene_state, resources);
}
this->draw(manager, depth_tx, depth_in_front_tx, color_tx);
/* Perform render step between samples to allow
* flushing of freed GPUBackend resources. */
if (GPU_backend_get_type() == GPU_BACKEND_METAL) {
GPU_flush();
}
GPU_render_step();
}
}
};
} // namespace blender::workbench
/* -------------------------------------------------------------------- */
/** \name Interface with legacy C DRW manager
* \{ */
using namespace blender;
struct WORKBENCH_Data {
DrawEngineType *engine_type;
DRWViewportEmptyList *fbl;
DRWViewportEmptyList *txl;
DRWViewportEmptyList *psl;
DRWViewportEmptyList *stl;
workbench::Instance *instance;
char info[GPU_INFO_SIZE];
};
static void workbench_engine_init(void *vedata)
{
WORKBENCH_Data *ved = reinterpret_cast<WORKBENCH_Data *>(vedata);
if (ved->instance == nullptr) {
ved->instance = new workbench::Instance();
}
ved->instance->init();
}
static void workbench_cache_init(void *vedata)
{
reinterpret_cast<WORKBENCH_Data *>(vedata)->instance->begin_sync();
}
static void workbench_cache_populate(void *vedata, Object *object)
{
draw::Manager *manager = DRW_manager_get();
draw::ObjectRef ref;
ref.object = object;
ref.dupli_object = DRW_object_get_dupli(object);
ref.dupli_parent = DRW_object_get_dupli_parent(object);
reinterpret_cast<WORKBENCH_Data *>(vedata)->instance->object_sync(*manager, ref);
}
static void workbench_cache_finish(void *vedata)
{
reinterpret_cast<WORKBENCH_Data *>(vedata)->instance->end_sync();
}
static void workbench_draw_scene(void *vedata)
{
WORKBENCH_Data *ved = reinterpret_cast<WORKBENCH_Data *>(vedata);
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
draw::Manager *manager = DRW_manager_get();
if (DRW_state_is_viewport_image_render()) {
ved->instance->draw_image_render(*manager, dtxl->depth, dtxl->depth_in_front, dtxl->color);
}
else {
ved->instance->draw_viewport(*manager, dtxl->depth, dtxl->depth_in_front, dtxl->color);
}
}
static void workbench_instance_free(void *instance)
{
delete reinterpret_cast<workbench::Instance *>(instance);
}
static void workbench_engine_free()
{
workbench::ShaderCache::release();
}
static void workbench_view_update(void *vedata)
{
WORKBENCH_Data *ved = reinterpret_cast<WORKBENCH_Data *>(vedata);
if (ved->instance) {
ved->instance->scene_state.reset_taa_next_sample = true;
}
}
static void workbench_id_update(void *vedata, ID *id)
{
UNUSED_VARS(vedata, id);
}
/* RENDER */
static bool workbench_render_framebuffers_init()
{
/* For image render, allocate own buffers because we don't have a viewport. */
const float2 viewport_size = DRW_viewport_size_get();
const int2 size = {int(viewport_size.x), int(viewport_size.y)};
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
/* When doing a multi view rendering the first view will allocate the buffers
* the other views will reuse these buffers */
if (dtxl->color == nullptr) {
BLI_assert(dtxl->depth == nullptr);
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_GENERAL;
dtxl->color = GPU_texture_create_2d(
"txl.color", size.x, size.y, 1, GPU_RGBA16F, usage, nullptr);
dtxl->depth = GPU_texture_create_2d(
"txl.depth", size.x, size.y, 1, GPU_DEPTH24_STENCIL8, usage, nullptr);
dtxl->depth_in_front = GPU_texture_create_2d(
"txl.depth_in_front", size.x, size.y, 1, GPU_DEPTH24_STENCIL8, usage, nullptr);
}
if (!(dtxl->depth && dtxl->color && dtxl->depth_in_front)) {
return false;
}
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
GPU_framebuffer_ensure_config(
&dfbl->default_fb,
{GPU_ATTACHMENT_TEXTURE(dtxl->depth), GPU_ATTACHMENT_TEXTURE(dtxl->color)});
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)});
return GPU_framebuffer_check_valid(dfbl->default_fb, nullptr) &&
GPU_framebuffer_check_valid(dfbl->color_only_fb, nullptr) &&
GPU_framebuffer_check_valid(dfbl->depth_only_fb, nullptr);
}
static void write_render_color_output(RenderLayer *layer,
const char *viewname,
GPUFrameBuffer *fb,
const rcti *rect)
{
RenderPass *rp = RE_pass_find_by_name(layer, RE_PASSNAME_COMBINED, viewname);
if (rp) {
GPU_framebuffer_bind(fb);
GPU_framebuffer_read_color(fb,
rect->xmin,
rect->ymin,
BLI_rcti_size_x(rect),
BLI_rcti_size_y(rect),
4,
0,
GPU_DATA_FLOAT,
rp->ibuf->float_buffer.data);
}
}
static void write_render_z_output(RenderLayer *layer,
const char *viewname,
GPUFrameBuffer *fb,
const rcti *rect,
const float4x4 &winmat)
{
RenderPass *rp = RE_pass_find_by_name(layer, RE_PASSNAME_Z, viewname);
if (rp) {
GPU_framebuffer_bind(fb);
GPU_framebuffer_read_depth(fb,
rect->xmin,
rect->ymin,
BLI_rcti_size_x(rect),
BLI_rcti_size_y(rect),
GPU_DATA_FLOAT,
rp->ibuf->float_buffer.data);
int pix_num = BLI_rcti_size_x(rect) * BLI_rcti_size_y(rect);
/* Convert GPU depth [0..1] to view Z [near..far] */
if (DRW_view_is_persp_get(nullptr)) {
for (float &z : MutableSpan(rp->ibuf->float_buffer.data, pix_num)) {
if (z == 1.0f) {
z = 1e10f; /* Background */
}
else {
z = z * 2.0f - 1.0f;
z = winmat[3][2] / (z + winmat[2][2]);
}
}
}
else {
/* Keep in mind, near and far distance are negatives. */
float near = DRW_view_near_distance_get(nullptr);
float far = DRW_view_far_distance_get(nullptr);
float range = fabsf(far - near);
for (float &z : MutableSpan(rp->ibuf->float_buffer.data, pix_num)) {
if (z == 1.0f) {
z = 1e10f; /* Background */
}
else {
z = z * range - near;
}
}
}
}
}
static void workbench_render_to_image(void *vedata,
RenderEngine *engine,
RenderLayer *layer,
const rcti *rect)
{
using namespace blender::draw;
if (!workbench_render_framebuffers_init()) {
RE_engine_report(engine, RPT_ERROR, "Failed to allocate GPU buffers");
return;
}
/* Setup */
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
const DRWContextState *draw_ctx = DRW_context_state_get();
Depsgraph *depsgraph = draw_ctx->depsgraph;
WORKBENCH_Data *ved = reinterpret_cast<WORKBENCH_Data *>(vedata);
if (ved->instance == nullptr) {
ved->instance = new workbench::Instance();
}
/* TODO(sergey): Shall render hold pointer to an evaluated camera instead? */
Object *camera_ob = DEG_get_evaluated_object(depsgraph, RE_GetCamera(engine->re));
/* Set the perspective, view and window matrix. */
float4x4 winmat, viewmat, viewinv;
RE_GetCameraWindow(engine->re, camera_ob, winmat.ptr());
RE_GetCameraModelMatrix(engine->re, camera_ob, viewinv.ptr());
viewmat = math::invert(viewinv);
/* Render */
/* TODO: Remove old draw manager calls. */
DRW_cache_restart();
DRWView *view = DRW_view_create(viewmat.ptr(), winmat.ptr(), nullptr, nullptr, nullptr);
DRW_view_default_set(view);
DRW_view_set_active(view);
ved->instance->init(camera_ob);
draw::Manager &manager = *DRW_manager_get();
manager.begin_sync();
workbench_cache_init(vedata);
auto workbench_render_cache =
[](void *vedata, Object *ob, RenderEngine * /*engine*/, Depsgraph * /*depsgraph*/) {
workbench_cache_populate(vedata, ob);
};
DRW_render_object_iter(vedata, engine, depsgraph, workbench_render_cache);
workbench_cache_finish(vedata);
manager.end_sync();
/* TODO: Remove old draw manager calls. */
DRW_render_instance_buffer_finish();
DRW_curves_update();
DefaultTextureList &dtxl = *DRW_viewport_texture_list_get();
ved->instance->draw_image_render(manager, dtxl.depth, dtxl.depth_in_front, dtxl.color, engine);
/* Write image */
const char *viewname = RE_GetActiveRenderView(engine->re);
write_render_color_output(layer, viewname, dfbl->default_fb, rect);
write_render_z_output(layer, viewname, dfbl->default_fb, rect, winmat);
}
static void workbench_render_update_passes(RenderEngine *engine,
Scene *scene,
ViewLayer *view_layer)
{
if (view_layer->passflag & SCE_PASS_COMBINED) {
RE_engine_register_pass(engine, scene, view_layer, RE_PASSNAME_COMBINED, 4, "RGBA", SOCK_RGBA);
}
if (view_layer->passflag & SCE_PASS_Z) {
RE_engine_register_pass(engine, scene, view_layer, RE_PASSNAME_Z, 1, "Z", SOCK_FLOAT);
}
}
extern "C" {
static const DrawEngineDataSize workbench_data_size = DRW_VIEWPORT_DATA_SIZE(WORKBENCH_Data);
DrawEngineType draw_engine_workbench = {
/*next*/ nullptr,
/*prev*/ nullptr,
/*idname*/ N_("Workbench"),
/*vedata_size*/ &workbench_data_size,
/*engine_init*/ &workbench_engine_init,
/*engine_free*/ &workbench_engine_free,
/*instance_free*/ &workbench_instance_free,
/*cache_init*/ &workbench_cache_init,
/*cache_populate*/ &workbench_cache_populate,
/*cache_finish*/ &workbench_cache_finish,
/*draw_scene*/ &workbench_draw_scene,
/*view_update*/ &workbench_view_update,
/*id_update*/ &workbench_id_update,
/*render_to_image*/ &workbench_render_to_image,
/*store_metadata*/ nullptr,
};
RenderEngineType DRW_engine_viewport_workbench_type = {
/*next*/ nullptr,
/*prev*/ nullptr,
/*idname*/ "BLENDER_WORKBENCH",
/*name*/ N_("Workbench"),
/*flag*/ RE_INTERNAL | RE_USE_STEREO_VIEWPORT | RE_USE_GPU_CONTEXT,
/*update*/ nullptr,
/*render*/ &DRW_render_to_image,
/*render_frame_finish*/ nullptr,
/*draw*/ nullptr,
/*bake*/ nullptr,
/*view_update*/ nullptr,
/*view_draw*/ nullptr,
/*update_script_node*/ nullptr,
/*update_render_passes*/ &workbench_render_update_passes,
/*draw_engine*/ &draw_engine_workbench,
/*rna_ext*/
{
/*data*/ nullptr,
/*srna*/ nullptr,
/*call*/ nullptr,
},
};
}
/** \} */