Workbench: Support node texture "closest" interpolation option
authorClément Foucault <foucault.clem@gmail.com>
Sun, 23 Dec 2018 14:20:06 +0000 (15:20 +0100)
committerClément Foucault <foucault.clem@gmail.com>
Fri, 11 Jan 2019 15:00:23 +0000 (16:00 +0100)
This makes it possible to paint pixel art using the workbench.

Cubic interpolation is not supported but could be added if needed.

source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl
source/blender/draw/engines/workbench/shaders/workbench_forward_transparent_accum_frag.glsl
source/blender/draw/engines/workbench/shaders/workbench_prepass_frag.glsl
source/blender/draw/engines/workbench/workbench_deferred.c
source/blender/draw/engines/workbench/workbench_forward.c
source/blender/draw/engines/workbench/workbench_materials.c
source/blender/draw/engines/workbench/workbench_private.h

index 25942b1..ce8988d 100644 (file)
@@ -159,3 +159,13 @@ vec4 srgb_to_linearrgb(vec4 col_from)
        col_to.a = col_from.a;
        return col_to;
 }
+
+vec4 workbench_sample_texture(sampler2D image, vec2 coord, bool srgb, bool nearest_sampling)
+{
+       vec2 tex_size = vec2(textureSize(image, 0).xy);
+       /* TODO(fclem) We could do the same with sampler objects.
+        * But this is a quick workaround instead of messing with the GPUTexture itself. */
+       vec2 uv = nearest_sampling ? (floor(coord * tex_size) + 0.5) / tex_size : coord;
+       vec4 color = texture(image, uv);
+       return (srgb) ? srgb_to_linearrgb(color) : color;
+}
index aed8693..93da32d 100644 (file)
@@ -2,6 +2,7 @@
 uniform float ImageTransparencyCutoff = 0.1;
 uniform sampler2D image;
 uniform bool imageSrgb;
+uniform bool imageNearest;
 
 uniform mat4 ProjectionMatrix;
 uniform mat4 ViewMatrixInverse;
@@ -35,13 +36,10 @@ void main()
        vec4 diffuse_color;
 
 #ifdef V3D_SHADING_TEXTURE_COLOR
-       diffuse_color = texture(image, uv_interp);
+       diffuse_color = workbench_sample_texture(image, uv_interp, imageSrgb, imageNearest);
        if (diffuse_color.a < ImageTransparencyCutoff) {
                discard;
        }
-       if (imageSrgb) {
-               diffuse_color = srgb_to_linearrgb(diffuse_color);
-       }
 #else
        diffuse_color = vec4(materialDiffuseColor, 1.0);
 #endif /* V3D_SHADING_TEXTURE_COLOR */
index 2ce816c..db51d3d 100644 (file)
@@ -7,6 +7,7 @@ uniform float materialRoughness;
 uniform sampler2D image;
 uniform float ImageTransparencyCutoff = 0.1;
 uniform bool imageSrgb;
+uniform bool imageNearest;
 
 #ifdef NORMAL_VIEWPORT_PASS_ENABLED
 in vec3 normal_viewport;
@@ -37,13 +38,10 @@ void main()
        vec4 color;
 
 #  ifdef V3D_SHADING_TEXTURE_COLOR
-       color = texture(image, uv_interp);
+       color = workbench_sample_texture(image, uv_interp, imageSrgb, imageNearest);
        if (color.a < ImageTransparencyCutoff) {
                discard;
        }
-       if (imageSrgb) {
-               color = srgb_to_linearrgb(color);
-       }
 #  else
        color.rgb = materialDiffuseColor;
 #  endif
index 2aa5888..cc8254d 100644 (file)
@@ -709,7 +709,7 @@ void workbench_deferred_cache_init(WORKBENCH_Data *vedata)
 }
 
 static WORKBENCH_MaterialData *get_or_create_material_data(
-        WORKBENCH_Data *vedata, Object *ob, Material *mat, Image *ima, int color_type)
+        WORKBENCH_Data *vedata, Object *ob, Material *mat, Image *ima, int color_type, int interp)
 {
        WORKBENCH_StorageList *stl = vedata->stl;
        WORKBENCH_PassList *psl = vedata->psl;
@@ -725,6 +725,7 @@ static WORKBENCH_MaterialData *get_or_create_material_data(
        material_template.object_id = OBJECT_ID_PASS_ENABLED(wpd) ? engine_object_data->object_id : 1;
        material_template.color_type = color_type;
        material_template.ima = ima;
+       material_template.interp = interp;
        uint hash = workbench_material_get_hash(&material_template, is_ghost);
 
        material = BLI_ghash_lookup(wpd->material_hash, POINTER_FROM_UINT(hash));
@@ -736,7 +737,7 @@ static WORKBENCH_MaterialData *get_or_create_material_data(
                workbench_material_copy(material, &material_template);
                DRW_shgroup_stencil_mask(material->shgrp, (ob->dtx & OB_DRAWXRAY) ? 0x00 : 0xFF);
                DRW_shgroup_uniform_int(material->shgrp, "object_id", &material->object_id, 1);
-               workbench_material_shgroup_uniform(wpd, material->shgrp, material, ob, true, true);
+               workbench_material_shgroup_uniform(wpd, material->shgrp, material, ob, true, true, interp);
 
                BLI_ghash_insert(wpd->material_hash, POINTER_FROM_UINT(hash), material);
        }
@@ -764,11 +765,12 @@ static void workbench_cache_populate_particles(WORKBENCH_Data *vedata, Object *o
                const int draw_as = (part->draw_as == PART_DRAW_REND) ? part->ren_as : part->draw_as;
 
                if (draw_as == PART_DRAW_PATH) {
-                       Image *image = NULL;
-                       Material *mat = give_current_material(ob, part->omat);
-                       ED_object_get_active_image(ob, part->omat, &image, NULL, NULL, NULL);
+                       Material *mat;
+                       Image *image;
+                       int interp;
+                       workbench_material_get_image_and_mat(ob, part->omat, &image, &interp, &mat);
                        int color_type = workbench_material_determine_color_type(wpd, image, ob);
-                       WORKBENCH_MaterialData *material = get_or_create_material_data(vedata, ob, mat, image, color_type);
+                       WORKBENCH_MaterialData *material = get_or_create_material_data(vedata, ob, mat, image, color_type, interp);
 
                        struct GPUShader *shader = (color_type != V3D_SHADING_TEXTURE_COLOR) ?
                                wpd->prepass_solid_hair_sh :
@@ -779,7 +781,7 @@ static void workbench_cache_populate_particles(WORKBENCH_Data *vedata, Object *o
                                shader);
                        DRW_shgroup_stencil_mask(shgrp, (ob->dtx & OB_DRAWXRAY) ? 0x00 : 0xFF);
                        DRW_shgroup_uniform_int(shgrp, "object_id", &material->object_id, 1);
-                       workbench_material_shgroup_uniform(wpd, shgrp, material, ob, true, true);
+                       workbench_material_shgroup_uniform(wpd, shgrp, material, ob, true, true, interp);
                }
        }
 }
@@ -829,11 +831,12 @@ void workbench_deferred_solid_cache_populate(WORKBENCH_Data *vedata, Object *ob)
                        struct GPUBatch **geom_array = DRW_cache_mesh_surface_texpaint_get(ob);
                        for (int i = 0; i < materials_len; i++) {
                                if (geom_array != NULL && geom_array[i] != NULL) {
-                                       Material *mat = give_current_material(ob, i + 1);
+                                       Material *mat;
                                        Image *image;
-                                       ED_object_get_active_image(ob, i + 1, &image, NULL, NULL, NULL);
+                                       int interp;
+                                       workbench_material_get_image_and_mat(ob, i + 1, &image, &interp, &mat);
                                        int color_type = workbench_material_determine_color_type(wpd, image, ob);
-                                       material = get_or_create_material_data(vedata, ob, mat, image, color_type);
+                                       material = get_or_create_material_data(vedata, ob, mat, image, color_type, interp);
                                        DRW_shgroup_call_object_add(material->shgrp, geom_array[i], ob);
                                }
                        }
@@ -842,7 +845,7 @@ void workbench_deferred_solid_cache_populate(WORKBENCH_Data *vedata, Object *ob)
                              V3D_SHADING_SINGLE_COLOR, V3D_SHADING_OBJECT_COLOR, V3D_SHADING_RANDOM_COLOR))
                {
                        /* Draw solid color */
-                       material = get_or_create_material_data(vedata, ob, NULL, NULL, wpd->shading.color_type);
+                       material = get_or_create_material_data(vedata, ob, NULL, NULL, wpd->shading.color_type, 0);
                        if (is_sculpt_mode) {
                                DRW_shgroup_call_sculpt_add(material->shgrp, ob, ob->obmat);
                        }
@@ -858,7 +861,7 @@ void workbench_deferred_solid_cache_populate(WORKBENCH_Data *vedata, Object *ob)
                        if (is_sculpt_mode) {
                                /* Multiple materials are not supported in sculpt mode yet. */
                                Material *mat = give_current_material(ob, 1);
-                               material = get_or_create_material_data(vedata, ob, mat, NULL, V3D_SHADING_MATERIAL_COLOR);
+                               material = get_or_create_material_data(vedata, ob, mat, NULL, V3D_SHADING_MATERIAL_COLOR, 0);
                                DRW_shgroup_call_sculpt_add(material->shgrp, ob, ob->obmat);
                        }
                        else {
@@ -870,7 +873,7 @@ void workbench_deferred_solid_cache_populate(WORKBENCH_Data *vedata, Object *ob)
                                for (int i = 0; i < materials_len; ++i) {
                                        if (geoms != NULL && geoms[i] != NULL) {
                                                Material *mat = give_current_material(ob, i + 1);
-                                               material = get_or_create_material_data(vedata, ob, mat, NULL, V3D_SHADING_MATERIAL_COLOR);
+                                               material = get_or_create_material_data(vedata, ob, mat, NULL, V3D_SHADING_MATERIAL_COLOR, 0);
                                                DRW_shgroup_call_object_add(material->shgrp, geoms[i], ob);
                                        }
                                }
index 6c955ac..d303f2e 100644 (file)
@@ -139,7 +139,7 @@ static void workbench_init_object_data(DrawData *dd)
 }
 
 static WORKBENCH_MaterialData *get_or_create_material_data(
-        WORKBENCH_Data *vedata, Object *ob, Material *mat, Image *ima, int color_type)
+        WORKBENCH_Data *vedata, Object *ob, Material *mat, Image *ima, int color_type, int interp)
 {
        WORKBENCH_StorageList *stl = vedata->stl;
        WORKBENCH_PassList *psl = vedata->psl;
@@ -155,6 +155,7 @@ static WORKBENCH_MaterialData *get_or_create_material_data(
        material_template.object_id = OBJECT_ID_PASS_ENABLED(wpd) ? engine_object_data->object_id : 1;
        material_template.color_type = color_type;
        material_template.ima = ima;
+       material_template.interp = interp;
        uint hash = workbench_material_get_hash(&material_template, false);
 
        material = BLI_ghash_lookup(wpd->material_hash, POINTER_FROM_UINT(hash));
@@ -177,7 +178,7 @@ static WORKBENCH_MaterialData *get_or_create_material_data(
                        DRW_shgroup_uniform_vec2(grp, "invertedViewportSize", DRW_viewport_invert_size_get(), 1);
                }
 
-               workbench_material_shgroup_uniform(wpd, grp, material, ob, false, false);
+               workbench_material_shgroup_uniform(wpd, grp, material, ob, false, false, interp);
                material->shgrp = grp;
 
                /* Depth */
@@ -430,11 +431,12 @@ static void workbench_forward_cache_populate_particles(WORKBENCH_Data *vedata, O
                const int draw_as = (part->draw_as == PART_DRAW_REND) ? part->ren_as : part->draw_as;
 
                if (draw_as == PART_DRAW_PATH) {
-                       Image *image = NULL;
-                       Material *mat = give_current_material(ob, part->omat);
-                       ED_object_get_active_image(ob, part->omat, &image, NULL, NULL, NULL);
+                       Material *mat;
+                       Image *image;
+                       int interp;
+                       workbench_material_get_image_and_mat(ob, part->omat, &image, &interp, &mat);
                        int color_type = workbench_material_determine_color_type(wpd, image, ob);
-                       WORKBENCH_MaterialData *material = get_or_create_material_data(vedata, ob, mat, image, color_type);
+                       WORKBENCH_MaterialData *material = get_or_create_material_data(vedata, ob, mat, image, color_type, interp);
 
                        struct GPUShader *shader = (color_type != V3D_SHADING_TEXTURE_COLOR)
                                                   ? wpd->transparent_accum_hair_sh
@@ -444,7 +446,7 @@ static void workbench_forward_cache_populate_particles(WORKBENCH_Data *vedata, O
                                                        psl->transparent_accum_pass,
                                                        shader);
                        DRW_shgroup_uniform_block(shgrp, "world_block", wpd->world_ubo);
-                       workbench_material_shgroup_uniform(wpd, shgrp, material, ob, false, false);
+                       workbench_material_shgroup_uniform(wpd, shgrp, material, ob, false, false, interp);
                        DRW_shgroup_uniform_vec4(shgrp, "viewvecs[0]", (float *)wpd->viewvecs, 3);
                        /* Hairs have lots of layer and can rapidly become the most prominent surface.
                         * So lower their alpha artificially. */
@@ -509,20 +511,12 @@ void workbench_forward_cache_populate(WORKBENCH_Data *vedata, Object *ob)
                                const int materials_len = MAX2(1, (is_sculpt_mode ? 1 : ob->totcol));
                                struct GPUBatch **geom_array = DRW_cache_mesh_surface_texpaint_get(ob);
                                for (int i = 0; i < materials_len; i++) {
-                                       Material *mat = give_current_material(ob, i + 1);
+                                       Material *mat;
                                        Image *image;
-                                       ED_object_get_active_image(ob, i + 1, &image, NULL, NULL, NULL);
-                                       /* use OB_SOLID when no texture could be determined */
-
-                                       int color_type = wpd->shading.color_type;
-                                       if (color_type == V3D_SHADING_TEXTURE_COLOR) {
-                                               /* use OB_SOLID when no texture could be determined */
-                                               if (image == NULL) {
-                                                       color_type = V3D_SHADING_MATERIAL_COLOR;
-                                               }
-                                       }
-
-                                       material = get_or_create_material_data(vedata, ob, mat, image, color_type);
+                                       int interp;
+                                       workbench_material_get_image_and_mat(ob, i + 1, &image, &interp, &mat);
+                                       int color_type = workbench_material_determine_color_type(wpd, image, ob);
+                                       material = get_or_create_material_data(vedata, ob, mat, image, color_type, interp);
                                        DRW_shgroup_call_object_add(material->shgrp_object_outline, geom_array[i], ob);
                                        DRW_shgroup_call_object_add(material->shgrp, geom_array[i], ob);
                                }
@@ -538,7 +532,7 @@ void workbench_forward_cache_populate(WORKBENCH_Data *vedata, Object *ob)
                                /* No material split needed */
                                struct GPUBatch *geom = DRW_cache_object_surface_get(ob);
                                if (geom) {
-                                       material = get_or_create_material_data(vedata, ob, NULL, NULL, wpd->shading.color_type);
+                                       material = get_or_create_material_data(vedata, ob, NULL, NULL, wpd->shading.color_type, 0);
                                        if (is_sculpt_mode) {
                                                DRW_shgroup_call_sculpt_add(material->shgrp_object_outline, ob, ob->obmat);
                                                if (!is_wire) {
@@ -569,7 +563,7 @@ void workbench_forward_cache_populate(WORKBENCH_Data *vedata, Object *ob)
                                                }
 
                                                Material *mat = give_current_material(ob, i + 1);
-                                               material = get_or_create_material_data(vedata, ob, mat, NULL, V3D_SHADING_MATERIAL_COLOR);
+                                               material = get_or_create_material_data(vedata, ob, mat, NULL, V3D_SHADING_MATERIAL_COLOR, 0);
                                                if (is_sculpt_mode) {
                                                        DRW_shgroup_call_sculpt_add(material->shgrp_object_outline, ob, ob->obmat);
                                                        if (!is_wire) {
index faca4b4..679f116 100644 (file)
@@ -5,10 +5,15 @@
 #include "BIF_gl.h"
 
 #include "BKE_image.h"
+#include "BKE_node.h"
 
 #include "BLI_dynstr.h"
 #include "BLI_hash.h"
 
+#include "DNA_node_types.h"
+
+#include "ED_uvedit.h"
+
 #define HSV_SATURATION 0.5
 #define HSV_VALUE 0.8
 
@@ -186,9 +191,24 @@ int workbench_material_determine_color_type(WORKBENCH_PrivateData *wpd, Image *i
        return color_type;
 }
 
+void workbench_material_get_image_and_mat(Object *ob, int mat_nr, Image **r_image, int *r_interp, Material **r_mat)
+{
+       bNode *node;
+       *r_mat = give_current_material(ob, mat_nr);
+       ED_object_get_active_image(ob, mat_nr, r_image, NULL, &node, NULL);
+       if (node) {
+               BLI_assert(node->type == SH_NODE_TEX_IMAGE);
+               NodeTexImage *storage = node->storage;
+               *r_interp = storage->interpolation;
+       }
+       else {
+               *r_interp = 0;
+       }
+}
+
 void workbench_material_shgroup_uniform(
         WORKBENCH_PrivateData *wpd, DRWShadingGroup *grp, WORKBENCH_MaterialData *material, Object *ob,
-        const bool use_metallic, const bool deferred)
+        const bool use_metallic, const bool deferred, const int interp)
 {
        if (deferred && !MATDATA_PASS_ENABLED(wpd)) {
                return;
@@ -201,6 +221,7 @@ void workbench_material_shgroup_uniform(
                GPUTexture *tex = GPU_texture_from_blender(material->ima, NULL, GL_TEXTURE_2D, false, 0.0f);
                DRW_shgroup_uniform_texture(grp, "image", tex);
                DRW_shgroup_uniform_bool_copy(grp, "imageSrgb", do_color_correction);
+               DRW_shgroup_uniform_bool_copy(grp, "imageNearest", (interp == SHD_INTERP_CLOSEST));
        }
        else {
                DRW_shgroup_uniform_vec3(grp, "materialDiffuseColor", (use_metallic) ? material->base_color : material->diffuse_color, 1);
index 832f755..e7712ff 100644 (file)
@@ -234,6 +234,7 @@ typedef struct WORKBENCH_MaterialData {
        float roughness;
        int object_id;
        int color_type;
+       int interp;
        Image *ima;
 
        /* Linked shgroup for drawing */
@@ -295,6 +296,7 @@ int workbench_taa_calculate_num_iterations(WORKBENCH_Data *vedata);
 
 /* workbench_materials.c */
 int workbench_material_determine_color_type(WORKBENCH_PrivateData *wpd, Image *ima, Object *ob);
+void workbench_material_get_image_and_mat(Object *ob, int mat_nr, Image **r_image, int *r_interp, Material **r_mat);
 char *workbench_material_build_defines(WORKBENCH_PrivateData *wpd, bool use_textures, bool is_hair);
 void workbench_material_update_data(WORKBENCH_PrivateData *wpd, Object *ob, Material *mat, WORKBENCH_MaterialData *data);
 uint workbench_material_get_hash(WORKBENCH_MaterialData *material_template, bool is_ghost);
@@ -303,7 +305,7 @@ int workbench_material_get_prepass_shader_index(WORKBENCH_PrivateData *wpd, bool
 int workbench_material_get_accum_shader_index(WORKBENCH_PrivateData *wpd, bool use_textures, bool is_hair);
 void workbench_material_shgroup_uniform(
         WORKBENCH_PrivateData *wpd, DRWShadingGroup *grp, WORKBENCH_MaterialData *material, Object *ob,
-        const bool use_metallic, const bool deferred);
+        const bool use_metallic, const bool deferred, const int interp);
 void workbench_material_copy(WORKBENCH_MaterialData *dest_material, const WORKBENCH_MaterialData *source_material);
 
 /* workbench_studiolight.c */