Fix #122714: Workbench: Missing textures not shown in Texture Paint mode with magenta
Missing feature from the Workbench Next port. Pull Request: https://projects.blender.org/blender/blender/pulls/123034
This commit is contained in:
@@ -121,7 +121,7 @@ class Instance {
|
||||
return;
|
||||
}
|
||||
|
||||
const ObjectState object_state = ObjectState(scene_state, ob);
|
||||
const ObjectState object_state = ObjectState(scene_state, resources, ob);
|
||||
|
||||
/* Needed for mesh cache validation, to prevent two copies of
|
||||
* of vertex color arrays from being sent to the GPU (e.g.
|
||||
@@ -232,16 +232,18 @@ class Instance {
|
||||
Material &material,
|
||||
gpu::Batch *batch,
|
||||
ResourceHandle handle,
|
||||
::Image *image = nullptr,
|
||||
GPUSamplerState sampler_state = GPUSamplerState::default_sampler(),
|
||||
ImageUser *iuser = nullptr)
|
||||
const MaterialTexture *texture = nullptr,
|
||||
bool show_missing_texture = false)
|
||||
{
|
||||
resources.material_buf.append(material);
|
||||
int material_index = resources.material_buf.size() - 1;
|
||||
|
||||
if (show_missing_texture && (!texture || !texture->gpu.texture)) {
|
||||
texture = &resources.missing_texture;
|
||||
}
|
||||
|
||||
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);
|
||||
mesh_pass.get_subpass(eGeometryType::MESH, texture).draw(batch, handle, material_index);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -271,14 +273,12 @@ class Instance {
|
||||
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();
|
||||
MaterialTexture texture;
|
||||
if (object_state.color_type == V3D_SHADING_TEXTURE_COLOR) {
|
||||
get_material_image(ob_ref.object, material_slot, image, iuser, sampler_state);
|
||||
texture = MaterialTexture(ob_ref.object, material_slot);
|
||||
}
|
||||
|
||||
draw_mesh(ob_ref, mat, batches[i], handle, image, sampler_state, iuser);
|
||||
draw_mesh(ob_ref, mat, batches[i], handle, &texture, object_state.show_missing_texture);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -303,12 +303,7 @@ class Instance {
|
||||
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);
|
||||
draw_mesh(ob_ref, mat, batch, handle, &object_state.image_paint_override);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -334,14 +329,12 @@ class Instance {
|
||||
mat.base_color = batch.debug_color();
|
||||
}
|
||||
|
||||
::Image *image = nullptr;
|
||||
ImageUser *iuser = nullptr;
|
||||
GPUSamplerState sampler_state = GPUSamplerState::default_sampler();
|
||||
MaterialTexture texture;
|
||||
if (object_state.color_type == V3D_SHADING_TEXTURE_COLOR) {
|
||||
get_material_image(ob_ref.object, batch.material_slot, image, iuser, sampler_state);
|
||||
texture = MaterialTexture(ob_ref.object, batch.material_slot);
|
||||
}
|
||||
|
||||
draw_mesh(ob_ref, mat, batch.batch, handle, image, sampler_state);
|
||||
draw_mesh(ob_ref, mat, batch.batch, handle, &texture, object_state.show_missing_texture);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -351,12 +344,7 @@ class Instance {
|
||||
mat.base_color = batch.debug_color();
|
||||
}
|
||||
|
||||
draw_mesh(ob_ref,
|
||||
mat,
|
||||
batch.batch,
|
||||
handle,
|
||||
object_state.image_paint_override,
|
||||
object_state.override_sampler_state);
|
||||
draw_mesh(ob_ref, mat, batch.batch, handle, &object_state.image_paint_override);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -388,19 +376,16 @@ class Instance {
|
||||
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();
|
||||
MaterialTexture texture;
|
||||
if (object_state.color_type == V3D_SHADING_TEXTURE_COLOR) {
|
||||
get_material_image(ob_ref.object, psys->part->omat - 1, image, iuser, sampler_state);
|
||||
texture = MaterialTexture(ob_ref.object, psys->part->omat - 1);
|
||||
}
|
||||
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");
|
||||
PassMain::Sub &pass =
|
||||
mesh_pass.get_subpass(eGeometryType::CURVES, &texture).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);
|
||||
|
||||
@@ -60,52 +60,65 @@ uint32_t Material::pack_data(float metallic, float roughness, float alpha)
|
||||
return (packed_alpha << 16u) | (packed_roughness << 8u) | packed_metallic;
|
||||
}
|
||||
|
||||
void get_material_image(Object *ob,
|
||||
int material_slot,
|
||||
::Image *&image,
|
||||
ImageUser *&iuser,
|
||||
GPUSamplerState &sampler_state)
|
||||
MaterialTexture::MaterialTexture(Object *ob, int material_index)
|
||||
{
|
||||
const ::bNode *node = nullptr;
|
||||
|
||||
ED_object_get_active_image(ob, material_slot + 1, &image, &iuser, &node, nullptr);
|
||||
if (node && image) {
|
||||
switch (node->type) {
|
||||
case SH_NODE_TEX_IMAGE: {
|
||||
const NodeTexImage *storage = static_cast<NodeTexImage *>(node->storage);
|
||||
const bool use_filter = (storage->interpolation != SHD_INTERP_CLOSEST);
|
||||
sampler_state.set_filtering_flag_from_test(GPU_SAMPLER_FILTERING_LINEAR, use_filter);
|
||||
switch (storage->extension) {
|
||||
case SHD_IMAGE_EXTENSION_EXTEND:
|
||||
default:
|
||||
sampler_state.extend_x = GPU_SAMPLER_EXTEND_MODE_EXTEND;
|
||||
sampler_state.extend_yz = GPU_SAMPLER_EXTEND_MODE_EXTEND;
|
||||
break;
|
||||
case SHD_IMAGE_EXTENSION_REPEAT:
|
||||
sampler_state.extend_x = GPU_SAMPLER_EXTEND_MODE_REPEAT;
|
||||
sampler_state.extend_yz = GPU_SAMPLER_EXTEND_MODE_REPEAT;
|
||||
break;
|
||||
case SHD_IMAGE_EXTENSION_MIRROR:
|
||||
sampler_state.extend_x = GPU_SAMPLER_EXTEND_MODE_MIRRORED_REPEAT;
|
||||
sampler_state.extend_yz = GPU_SAMPLER_EXTEND_MODE_MIRRORED_REPEAT;
|
||||
break;
|
||||
case SHD_IMAGE_EXTENSION_CLIP:
|
||||
sampler_state.extend_x = GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER;
|
||||
sampler_state.extend_yz = GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SH_NODE_TEX_ENVIRONMENT: {
|
||||
const NodeTexEnvironment *storage = static_cast<NodeTexEnvironment *>(node->storage);
|
||||
const bool use_filter = (storage->interpolation != SHD_INTERP_CLOSEST);
|
||||
sampler_state.set_filtering_flag_from_test(GPU_SAMPLER_FILTERING_LINEAR, use_filter);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
BLI_assert_msg(0, "Node type not supported by workbench");
|
||||
}
|
||||
::Image *image = nullptr;
|
||||
ImageUser *user = nullptr;
|
||||
ED_object_get_active_image(ob, material_index + 1, &image, &user, &node, nullptr);
|
||||
if (!node || !image) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (node->type) {
|
||||
case SH_NODE_TEX_IMAGE: {
|
||||
const NodeTexImage *storage = static_cast<NodeTexImage *>(node->storage);
|
||||
const bool use_filter = (storage->interpolation != SHD_INTERP_CLOSEST);
|
||||
sampler_state.set_filtering_flag_from_test(GPU_SAMPLER_FILTERING_LINEAR, use_filter);
|
||||
switch (storage->extension) {
|
||||
case SHD_IMAGE_EXTENSION_EXTEND:
|
||||
default:
|
||||
sampler_state.extend_x = GPU_SAMPLER_EXTEND_MODE_EXTEND;
|
||||
sampler_state.extend_yz = GPU_SAMPLER_EXTEND_MODE_EXTEND;
|
||||
break;
|
||||
case SHD_IMAGE_EXTENSION_REPEAT:
|
||||
sampler_state.extend_x = GPU_SAMPLER_EXTEND_MODE_REPEAT;
|
||||
sampler_state.extend_yz = GPU_SAMPLER_EXTEND_MODE_REPEAT;
|
||||
break;
|
||||
case SHD_IMAGE_EXTENSION_MIRROR:
|
||||
sampler_state.extend_x = GPU_SAMPLER_EXTEND_MODE_MIRRORED_REPEAT;
|
||||
sampler_state.extend_yz = GPU_SAMPLER_EXTEND_MODE_MIRRORED_REPEAT;
|
||||
break;
|
||||
case SHD_IMAGE_EXTENSION_CLIP:
|
||||
sampler_state.extend_x = GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER;
|
||||
sampler_state.extend_yz = GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SH_NODE_TEX_ENVIRONMENT: {
|
||||
const NodeTexEnvironment *storage = static_cast<NodeTexEnvironment *>(node->storage);
|
||||
const bool use_filter = (storage->interpolation != SHD_INTERP_CLOSEST);
|
||||
sampler_state.set_filtering_flag_from_test(GPU_SAMPLER_FILTERING_LINEAR, use_filter);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
BLI_assert_msg(0, "Node type not supported by workbench");
|
||||
}
|
||||
|
||||
gpu = BKE_image_get_gpu_material_texture(image, user, true);
|
||||
premultiplied = image->alpha_mode == IMA_ALPHA_PREMUL;
|
||||
alpha_cutoff = !ELEM(image->alpha_mode, IMA_ALPHA_IGNORE, IMA_ALPHA_CHANNEL_PACKED);
|
||||
name = image->id.name;
|
||||
}
|
||||
|
||||
MaterialTexture::MaterialTexture(::Image *image, ImageUser *user /* = nullptr */)
|
||||
{
|
||||
gpu = BKE_image_get_gpu_material_texture(image, user, true);
|
||||
premultiplied = image->alpha_mode == IMA_ALPHA_PREMUL;
|
||||
alpha_cutoff = !ELEM(image->alpha_mode, IMA_ALPHA_IGNORE, IMA_ALPHA_CHANNEL_PACKED);
|
||||
name = image->id.name;
|
||||
}
|
||||
|
||||
} // namespace blender::workbench
|
||||
|
||||
@@ -54,43 +54,34 @@ void MeshPass::init_subpasses(ePipelineType pipeline, eLightingType lighting, bo
|
||||
}
|
||||
}
|
||||
|
||||
PassMain::Sub &MeshPass::get_subpass(
|
||||
eGeometryType geometry_type,
|
||||
::Image *image /* = nullptr */,
|
||||
GPUSamplerState sampler_state /* = GPUSamplerState::default_sampler() */,
|
||||
ImageUser *iuser /* = nullptr */)
|
||||
PassMain::Sub &MeshPass::get_subpass(eGeometryType geometry_type,
|
||||
const MaterialTexture *texture /* = nullptr */)
|
||||
{
|
||||
is_empty_ = false;
|
||||
|
||||
if (image) {
|
||||
ImageGPUTextures gputex = BKE_image_get_gpu_material_texture(image, iuser, true);
|
||||
if (gputex.texture) {
|
||||
auto add_cb = [&] {
|
||||
PassMain::Sub *sub_pass = passes_[int(geometry_type)][int(eShaderType::TEXTURE)];
|
||||
sub_pass = &sub_pass->sub(image->id.name);
|
||||
if (gputex.tile_mapping) {
|
||||
sub_pass->bind_texture(WB_TILE_ARRAY_SLOT, gputex.texture, sampler_state);
|
||||
sub_pass->bind_texture(WB_TILE_DATA_SLOT, gputex.tile_mapping);
|
||||
}
|
||||
else {
|
||||
sub_pass->bind_texture(WB_TEXTURE_SLOT, gputex.texture, sampler_state);
|
||||
}
|
||||
sub_pass->push_constant("isImageTile", gputex.tile_mapping != nullptr);
|
||||
sub_pass->push_constant("imagePremult", image->alpha_mode == IMA_ALPHA_PREMUL);
|
||||
/* TODO(@pragma37): This setting should be exposed on the user side,
|
||||
* either as a global parameter (and set it here)
|
||||
* or by reading the Material Clipping Threshold (and set it per material) */
|
||||
float alpha_cutoff = 0.1f;
|
||||
if (ELEM(image->alpha_mode, IMA_ALPHA_IGNORE, IMA_ALPHA_CHANNEL_PACKED)) {
|
||||
alpha_cutoff = -FLT_MAX;
|
||||
}
|
||||
sub_pass->push_constant("imageTransparencyCutoff", alpha_cutoff);
|
||||
return sub_pass;
|
||||
};
|
||||
if (texture && texture->gpu.texture) {
|
||||
auto add_cb = [&] {
|
||||
PassMain::Sub *sub_pass = passes_[int(geometry_type)][int(eShaderType::TEXTURE)];
|
||||
sub_pass = &sub_pass->sub(texture->name);
|
||||
if (texture->gpu.tile_mapping) {
|
||||
sub_pass->bind_texture(WB_TILE_ARRAY_SLOT, texture->gpu.texture, texture->sampler_state);
|
||||
sub_pass->bind_texture(WB_TILE_DATA_SLOT, texture->gpu.tile_mapping);
|
||||
}
|
||||
else {
|
||||
sub_pass->bind_texture(WB_TEXTURE_SLOT, texture->gpu.texture, texture->sampler_state);
|
||||
}
|
||||
sub_pass->push_constant("isImageTile", texture->gpu.tile_mapping != nullptr);
|
||||
sub_pass->push_constant("imagePremult", texture->premultiplied);
|
||||
/* TODO(@pragma37): This setting should be exposed on the user side,
|
||||
* either as a global parameter (and set it here)
|
||||
* or by reading the Material Clipping Threshold (and set it per material) */
|
||||
float alpha_cutoff = texture->alpha_cutoff ? 0.1f : -FLT_MAX;
|
||||
sub_pass->push_constant("imageTransparencyCutoff", alpha_cutoff);
|
||||
return sub_pass;
|
||||
};
|
||||
|
||||
return *texture_subpass_map_.lookup_or_add_cb(
|
||||
TextureSubPassKey(gputex.texture, geometry_type), add_cb);
|
||||
}
|
||||
return *texture_subpass_map_.lookup_or_add_cb(
|
||||
TextureSubPassKey(texture->gpu.texture, geometry_type), add_cb);
|
||||
}
|
||||
|
||||
return *passes_[int(geometry_type)][int(eShaderType::MATERIAL)];
|
||||
|
||||
@@ -137,11 +137,7 @@ struct Material {
|
||||
bool is_transparent();
|
||||
};
|
||||
|
||||
void get_material_image(Object *ob,
|
||||
int material_index,
|
||||
::Image *&image,
|
||||
ImageUser *&iuser,
|
||||
GPUSamplerState &sampler_state);
|
||||
ImageGPUTextures get_material_texture(GPUSamplerState &sampler_state);
|
||||
|
||||
struct SceneState {
|
||||
Scene *scene = nullptr;
|
||||
@@ -186,19 +182,31 @@ struct SceneState {
|
||||
void init(Object *camera_ob = nullptr);
|
||||
};
|
||||
|
||||
struct ObjectState {
|
||||
eV3DShadingColorType color_type = V3D_SHADING_SINGLE_COLOR;
|
||||
bool sculpt_pbvh = false;
|
||||
::Image *image_paint_override = nullptr;
|
||||
GPUSamplerState override_sampler_state = GPUSamplerState::default_sampler();
|
||||
bool draw_shadow = false;
|
||||
bool use_per_material_batches = false;
|
||||
struct MaterialTexture {
|
||||
const char *name = nullptr;
|
||||
ImageGPUTextures gpu = {};
|
||||
GPUSamplerState sampler_state = GPUSamplerState::default_sampler();
|
||||
bool premultiplied = false;
|
||||
bool alpha_cutoff = false;
|
||||
|
||||
ObjectState(const SceneState &scene_state, Object *ob);
|
||||
MaterialTexture() = default;
|
||||
MaterialTexture(Object *ob, int material_index);
|
||||
MaterialTexture(::Image *image, ImageUser *user = nullptr);
|
||||
};
|
||||
|
||||
struct SceneResources;
|
||||
|
||||
struct ObjectState {
|
||||
eV3DShadingColorType color_type = V3D_SHADING_SINGLE_COLOR;
|
||||
MaterialTexture image_paint_override = {};
|
||||
bool show_missing_texture = false;
|
||||
bool draw_shadow = false;
|
||||
bool use_per_material_batches = false;
|
||||
bool sculpt_pbvh = false;
|
||||
|
||||
ObjectState(const SceneState &scene_state, const SceneResources &resources, Object *ob);
|
||||
};
|
||||
|
||||
class CavityEffect {
|
||||
private:
|
||||
/* This value must be kept in sync with the one declared at
|
||||
@@ -285,6 +293,9 @@ struct SceneResources {
|
||||
|
||||
StencilViewWorkaround stencil_view;
|
||||
|
||||
Texture missing_tx = "missing_tx";
|
||||
MaterialTexture missing_texture;
|
||||
|
||||
void init(const SceneState &scene_state);
|
||||
void load_jitter_tx(int total_samples);
|
||||
};
|
||||
@@ -309,9 +320,7 @@ class MeshPass : public PassMain {
|
||||
void init_subpasses(ePipelineType pipeline, eLightingType lighting, bool clip);
|
||||
|
||||
PassMain::Sub &get_subpass(eGeometryType geometry_type,
|
||||
::Image *image = nullptr,
|
||||
GPUSamplerState sampler_state = GPUSamplerState::default_sampler(),
|
||||
ImageUser *iuser = nullptr);
|
||||
const MaterialTexture *texture = nullptr);
|
||||
};
|
||||
|
||||
enum class StencilBits : uint8_t {
|
||||
|
||||
@@ -174,6 +174,11 @@ void SceneResources::init(const SceneState &scene_state)
|
||||
}
|
||||
|
||||
clip_planes_buf.push_update();
|
||||
|
||||
missing_tx.ensure_2d(
|
||||
GPU_RGBA8, int2(1), GPU_TEXTURE_USAGE_SHADER_READ, float4(1.0f, 0.0f, 1.0f, 1.0f));
|
||||
missing_texture.gpu.texture = missing_tx;
|
||||
missing_texture.name = "Missing Texture";
|
||||
}
|
||||
|
||||
} // namespace blender::workbench
|
||||
|
||||
@@ -206,13 +206,13 @@ static const CustomData *get_vert_custom_data(const Mesh *mesh)
|
||||
return &mesh->vert_data;
|
||||
}
|
||||
|
||||
ObjectState::ObjectState(const SceneState &scene_state, Object *ob)
|
||||
ObjectState::ObjectState(const SceneState &scene_state,
|
||||
const SceneResources &resources,
|
||||
Object *ob)
|
||||
{
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
const bool is_active = (ob == draw_ctx->obact);
|
||||
|
||||
image_paint_override = nullptr;
|
||||
override_sampler_state = GPUSamplerState::default_sampler();
|
||||
sculpt_pbvh = BKE_sculptsession_use_pbvh_draw(ob, draw_ctx->rv3d) &&
|
||||
!DRW_state_is_image_render();
|
||||
draw_shadow = scene_state.draw_shadows && (ob->dtx & OB_DRAW_NO_SHADOW_CAST) == 0 &&
|
||||
@@ -267,21 +267,28 @@ ObjectState::ObjectState(const SceneState &scene_state, Object *ob)
|
||||
}
|
||||
else if (is_texpaint_mode && has_uv) {
|
||||
color_type = V3D_SHADING_TEXTURE_COLOR;
|
||||
show_missing_texture = true;
|
||||
const ImagePaintSettings *imapaint = &scene_state.scene->toolsettings->imapaint;
|
||||
if (imapaint->mode == IMAGEPAINT_MODE_IMAGE) {
|
||||
image_paint_override = imapaint->canvas;
|
||||
override_sampler_state.extend_x = GPU_SAMPLER_EXTEND_MODE_REPEAT;
|
||||
override_sampler_state.extend_yz = GPU_SAMPLER_EXTEND_MODE_REPEAT;
|
||||
const bool use_linear_filter = imapaint->interp == IMAGEPAINT_INTERP_LINEAR;
|
||||
override_sampler_state.set_filtering_flag_from_test(GPU_SAMPLER_FILTERING_LINEAR,
|
||||
use_linear_filter);
|
||||
if (imapaint->canvas) {
|
||||
image_paint_override = MaterialTexture(imapaint->canvas);
|
||||
image_paint_override.sampler_state.extend_x = GPU_SAMPLER_EXTEND_MODE_REPEAT;
|
||||
image_paint_override.sampler_state.extend_yz = GPU_SAMPLER_EXTEND_MODE_REPEAT;
|
||||
const bool use_linear_filter = imapaint->interp == IMAGEPAINT_INTERP_LINEAR;
|
||||
image_paint_override.sampler_state.set_filtering_flag_from_test(
|
||||
GPU_SAMPLER_FILTERING_LINEAR, use_linear_filter);
|
||||
}
|
||||
else {
|
||||
image_paint_override = resources.missing_texture;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use_per_material_batches = image_paint_override == nullptr && ELEM(color_type,
|
||||
V3D_SHADING_TEXTURE_COLOR,
|
||||
V3D_SHADING_MATERIAL_COLOR);
|
||||
use_per_material_batches = image_paint_override.gpu.texture == nullptr &&
|
||||
ELEM(color_type,
|
||||
V3D_SHADING_TEXTURE_COLOR,
|
||||
V3D_SHADING_MATERIAL_COLOR);
|
||||
}
|
||||
|
||||
} // namespace blender::workbench
|
||||
|
||||
Reference in New Issue
Block a user