Changeset View
Changeset View
Standalone View
Standalone View
source/blender/draw/editors/image_uv_editor.c
- This file was added.
| /* | |||||
| * This program is free software; you can redistribute it and/or | |||||
| * modify it under the terms of the GNU General Public License | |||||
| * as published by the Free Software Foundation; either version 2 | |||||
| * of the License, or (at your option) any later version. | |||||
| * | |||||
| * This program is distributed in the hope that it will be useful, | |||||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
| * GNU General Public License for more details. | |||||
| * | |||||
| * You should have received a copy of the GNU General Public License | |||||
| * along with this program; if not, write to the Free Software Foundation, | |||||
| * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||||
| * | |||||
| * Copyright 2016, Blender Foundation. | |||||
| */ | |||||
| /** \file | |||||
| * \ingroup draw_editors | |||||
| * | |||||
| * Draw engine to draw the Image/UV editor | |||||
| */ | |||||
| #include "DRW_render.h" | |||||
| #include "draw_cache_impl.h" | |||||
| #include "GPU_batch.h" | |||||
| #include "GPU_shader.h" | |||||
| #include "BKE_object.h" | |||||
| #include "BKE_image.h" | |||||
| #include "BLI_dynstr.h" | |||||
| #include "ED_image.h" | |||||
| #include "IMB_imbuf.h" | |||||
| #include "IMB_imbuf_types.h" | |||||
| #include "IMB_colormanagement.h" | |||||
| #include "DNA_space_types.h" | |||||
| #include "DNA_screen_types.h" | |||||
| #include "DNA_camera_types.h" | |||||
| #include "UI_resources.h" | |||||
| #include "UI_interface.h" | |||||
| #include "image_uv_editor.h" | |||||
| /* Shaders */ | |||||
| extern char datatoc_gpu_shader_2D_image_vert_glsl[]; | |||||
| extern char datatoc_common_view_lib_glsl[]; | |||||
| extern char datatoc_image_uv_editor_region_vert_glsl[]; | |||||
| extern char datatoc_image_uv_editor_region_frag_glsl[]; | |||||
| typedef enum eImageShaderFlags { | |||||
| IMAGE_SHADER_FLAG_VALID_IMAGE = (1 << 0), | |||||
| IMAGE_SHADER_FLAG_REPEAT_IMAGE = (1 << 1), | |||||
| IMAGE_SHADER_FLAG_SHUFFLE_CHANNELS = (1 << 2), | |||||
| IMAGE_SHADER_FLAG_USE_ALPHA = (1 << 3), | |||||
| IMAGE_SHADER_FLAG_NORMALIZE_DEPTH = (1 << 4), | |||||
| } eImageShaderFlags; | |||||
| #define MAX_IMAGE_SHADERS (1 << 5) | |||||
| /* *********** LISTS *********** */ | |||||
| /* GPUViewport.storage | |||||
| * Is freed everytime the viewport engine changes */ | |||||
| typedef struct IMAGE_UV_EDITOR_PrivateData { | |||||
| DRWShadingGroup *uv_overlay_faces_shgrp; | |||||
| DRWShadingGroup *uv_overlay_face_dots_shgrp; | |||||
| DRWShadingGroup *uv_overlay_verts_shgrp[2]; | |||||
| DRWShadingGroup *uv_overlay_edges_shgrp; | |||||
| DRWShadingGroup *uv_stretching_overlay_shgrp; | |||||
| DRWShadingGroup *uv_shadow_overlay_shgrp; | |||||
| float uv_stretching_total_area; | |||||
| float uv_stretching_total_area_uv; | |||||
| float uv_stretching_total_area_ratio; | |||||
| float uv_stretching_total_area_ratio_inv; | |||||
| } IMAGE_UV_EDITOR_PrivateData; | |||||
| typedef struct IMAGE_UV_EDITOR_StorageList { | |||||
| struct IMAGE_UV_EDITOR_PrivateData *g_data; | |||||
| } IMAGE_UV_EDITOR_StorageList; | |||||
| typedef struct IMAGE_UV_EDITOR_PassList { | |||||
| DRWPass *image_pass; | |||||
| DRWPass *uv_overlay_verts_pass; | |||||
| DRWPass *uv_overlay_edges_pass; | |||||
| DRWPass *uv_overlay_faces_pass; | |||||
| DRWPass *uv_stretching_overlay_pass; | |||||
| DRWPass *uv_shadow_overlay_pass; | |||||
| } IMAGE_UV_EDITOR_PassList; | |||||
| typedef struct IMAGE_UV_EDITOR_Data { | |||||
| void *engine_type; | |||||
| DRWViewportEmptyList *fbl; | |||||
| DRWViewportEmptyList *txl; | |||||
| IMAGE_UV_EDITOR_PassList *psl; | |||||
| IMAGE_UV_EDITOR_StorageList *stl; | |||||
| } IMAGE_UV_EDITOR_Data; | |||||
| typedef struct IMAGE_UV_EDITOR_Shaders { | |||||
| GPUShader *image_shaders[MAX_IMAGE_SHADERS]; | |||||
| } IMAGE_UV_EDITOR_Shaders; | |||||
| /* Default image width and height when image is not available */ | |||||
| #define DEFAULT_IMAGE_WIDTH_PX 1024 | |||||
| #define DEFAULT_IMAGE_HEIGHT_PX 1024 | |||||
| /* *********** STATIC *********** */ | |||||
| static struct { | |||||
| int flag; | |||||
| IMAGE_UV_EDITOR_Shaders shaders; | |||||
| void *lock; | |||||
| ImBuf *ibuf; | |||||
| Image *image; | |||||
| GPUTexture *texture; | |||||
| bool do_color_management; | |||||
| bool do_channel_shuffling; | |||||
| bool do_uv_overlay; | |||||
| bool do_uv_overlay_show_faces; | |||||
| bool do_uv_overlay_show_faces_dots; | |||||
| bool do_uv_stretching_overlay; | |||||
| bool do_shadow_overlay; | |||||
| void *cache_handle; | |||||
| GPUBatch *gpu_batch_image; | |||||
| /* For now stores a static unit matrix. */ | |||||
| float image_mat[4][4]; | |||||
| } e_data = {0}; /* Engine data */ | |||||
| /* -------------------------------------------------------------------- */ | |||||
| /** \name Common Util Functions | |||||
| * \{ */ | |||||
| static bool image_uv_editor_is_render_result(void) | |||||
| { | |||||
| return (e_data.image && e_data.image->type == IMA_TYPE_R_RESULT); | |||||
| } | |||||
| static bool image_uv_editor_is_viewer_output(void) | |||||
| { | |||||
| return (e_data.image && e_data.image->source == IMA_SRC_VIEWER); | |||||
| } | |||||
| static bool image_uv_editor_is_image(void) | |||||
| { | |||||
| return !(image_uv_editor_is_render_result() && image_uv_editor_is_viewer_output()); | |||||
| } | |||||
| /** \} */ | |||||
| /* -------------------------------------------------------------------- */ | |||||
| /** \name Image Shaders | |||||
| * \{ */ | |||||
| static eImageShaderFlags image_uv_editor_shader_index_get(SpaceImage *sima) | |||||
| { | |||||
| eImageShaderFlags flags = 0; | |||||
| SET_FLAG_FROM_TEST(flags, e_data.texture, IMAGE_SHADER_FLAG_VALID_IMAGE); | |||||
| SET_FLAG_FROM_TEST(flags, sima->flag & SI_DRAW_TILE, IMAGE_SHADER_FLAG_REPEAT_IMAGE); | |||||
| SET_FLAG_FROM_TEST(flags, sima->flag & SI_USE_ALPHA, IMAGE_SHADER_FLAG_USE_ALPHA); | |||||
| SET_FLAG_FROM_TEST(flags, sima->flag & SI_SHOW_ZBUF, IMAGE_SHADER_FLAG_NORMALIZE_DEPTH); | |||||
| SET_FLAG_FROM_TEST(flags, | |||||
| e_data.do_channel_shuffling | ((sima->flag & SI_SHOW_ZBUF) != 0), | |||||
| IMAGE_SHADER_FLAG_SHUFFLE_CHANNELS); | |||||
| return flags; | |||||
| } | |||||
| static char *image_uv_editor_build_defines(eImageShaderFlags shader_flags) | |||||
| { | |||||
| DynStr *ds = BLI_dynstr_new(); | |||||
| if (shader_flags & IMAGE_SHADER_FLAG_VALID_IMAGE) { | |||||
| BLI_dynstr_append(ds, "#define IMAGE_SHADER_FLAG_VALID_IMAGE\n"); | |||||
| } | |||||
| if (shader_flags & IMAGE_SHADER_FLAG_REPEAT_IMAGE) { | |||||
| BLI_dynstr_append(ds, "#define IMAGE_SHADER_FLAG_REPEAT_IMAGE\n"); | |||||
| } | |||||
| if (shader_flags & IMAGE_SHADER_FLAG_USE_ALPHA) { | |||||
| BLI_dynstr_append(ds, "#define IMAGE_SHADER_FLAG_USE_ALPHA\n"); | |||||
| } | |||||
| if (shader_flags & IMAGE_SHADER_FLAG_SHUFFLE_CHANNELS) { | |||||
| BLI_dynstr_append(ds, "#define IMAGE_SHADER_FLAG_SHUFFLE_CHANNELS\n"); | |||||
| } | |||||
| if (shader_flags & IMAGE_SHADER_FLAG_NORMALIZE_DEPTH) { | |||||
| BLI_dynstr_append(ds, "#define IMAGE_SHADER_FLAG_NORMALIZE_DEPTH\n"); | |||||
| } | |||||
| char *str = BLI_dynstr_get_cstring(ds); | |||||
| BLI_dynstr_free(ds); | |||||
| return str; | |||||
| } | |||||
| static GPUShader *image_uv_editor_shader_get(eImageShaderFlags shader_flags) | |||||
| { | |||||
| GPUShader *result = e_data.shaders.image_shaders[shader_flags]; | |||||
| if (!result) { | |||||
| char *defines = image_uv_editor_build_defines(shader_flags); | |||||
| result = GPU_shader_create_from_arrays({ | |||||
| .vert = (const char *[]){datatoc_common_view_lib_glsl, | |||||
| datatoc_image_uv_editor_region_vert_glsl, | |||||
| NULL}, | |||||
| .frag = (const char *[]){datatoc_image_uv_editor_region_frag_glsl, NULL}, | |||||
| .defs = (const char *[]){defines, NULL}, | |||||
| }); | |||||
| MEM_freeN(defines); | |||||
| e_data.shaders.image_shaders[shader_flags] = result; | |||||
| } | |||||
| return result; | |||||
| } | |||||
| static DRWShadingGroup *image_uv_editor_shgroup_create(Scene *scene, | |||||
| SpaceImage *sima, | |||||
| DRWPass *pass) | |||||
| { | |||||
| eImageShaderFlags shader_flags = image_uv_editor_shader_index_get(sima); | |||||
| GPUShader *shader = image_uv_editor_shader_get(shader_flags); | |||||
| DRWShadingGroup *shgrp = DRW_shgroup_create(shader, pass); | |||||
| if ((shader_flags & IMAGE_SHADER_FLAG_VALID_IMAGE) != 0) { | |||||
| DRW_shgroup_uniform_texture(shgrp, "image", e_data.texture); | |||||
| } | |||||
| if ((shader_flags & IMAGE_SHADER_FLAG_REPEAT_IMAGE) != 0) { | |||||
| } | |||||
| else { | |||||
| /* TODO: background color */ | |||||
| static float color[3]; | |||||
| UI_GetThemeColor3fv(TH_BACK, color); | |||||
| DRW_shgroup_uniform_vec3_copy(shgrp, "uiColorBackground", color); | |||||
| } | |||||
| if ((shader_flags & IMAGE_SHADER_FLAG_SHUFFLE_CHANNELS) != 0) { | |||||
| float shuffle[4] = {0.0f, 0.0f, 0.0f, 0.0f}; | |||||
| if (sima->flag & (SI_SHOW_R | SI_SHOW_ZBUF)) { | |||||
| shuffle[0] = 1.0f; | |||||
| } | |||||
| else if (sima->flag & SI_SHOW_G) { | |||||
| shuffle[1] = 1.0f; | |||||
| } | |||||
| else if (sima->flag & SI_SHOW_B) { | |||||
| shuffle[2] = 1.0f; | |||||
| } | |||||
| else if (sima->flag & SI_SHOW_ALPHA) { | |||||
| shuffle[3] = 1.0f; | |||||
| } | |||||
| DRW_shgroup_uniform_vec4_copy(shgrp, "shuffleChannels", shuffle); | |||||
| } | |||||
| if ((shader_flags & IMAGE_SHADER_FLAG_NORMALIZE_DEPTH) != 0) { | |||||
| if (scene && scene->camera && scene->camera->type == OB_CAMERA) { | |||||
| Camera *camera = (Camera *)scene->camera->data; | |||||
| DRW_shgroup_uniform_float_copy(shgrp, "cameraClipStart", camera->clip_start); | |||||
| DRW_shgroup_uniform_float_copy(shgrp, "cameraClipEnd", camera->clip_end); | |||||
| DRW_shgroup_uniform_float_copy( | |||||
| shgrp, "cameraClipScale", 1.0f / (camera->clip_end - camera->clip_start)); | |||||
| } | |||||
| } | |||||
| return shgrp; | |||||
| } | |||||
| /** \} */ | |||||
| /* -------------------------------------------------------------------- */ | |||||
| /** \name Image Pass | |||||
| * \{ */ | |||||
| static void image_uv_editor_add_image(IMAGE_UV_EDITOR_PassList *psl, | |||||
| Image *ima, | |||||
| ImageUser *iuser, | |||||
| ImBuf *ibuf) | |||||
| { | |||||
| const DRWContextState *draw_ctx = DRW_context_state_get(); | |||||
| Scene *scene = draw_ctx->scene; | |||||
| SpaceImage *sima = (SpaceImage *)draw_ctx->space_data; | |||||
| // TODO: color management needs to be applied when doing channel shuffling. | |||||
| // So question remains: is the current display textire already converted to the display space? | |||||
| e_data.do_color_management = e_data.do_channel_shuffling = sima->flag & | |||||
| (SI_SHOW_R | SI_SHOW_G | SI_SHOW_B | | |||||
| SI_SHOW_ALPHA); | |||||
| e_data.do_color_management = true; | |||||
| unsigned char *display_buffer; | |||||
| ColorManagedViewSettings *view_settings; | |||||
| ColorManagedDisplaySettings *display_settings; | |||||
| if (ima && ibuf) { | |||||
| if (sima->flag & SI_SHOW_ZBUF && (ibuf->zbuf || ibuf->zbuf_float || (ibuf->channels == 1))) { | |||||
| if (ibuf->zbuf) { | |||||
| // TODO: zbuf integer based | |||||
| // sima_draw_zbuf_pixels(x, y, ibuf->x, ibuf->y, ibuf->zbuf, zoomx, zoomy); | |||||
| } | |||||
| else if (ibuf->zbuf_float) { | |||||
| e_data.texture = GPU_texture_create_2d(ibuf->x, ibuf->y, GPU_R16F, ibuf->zbuf_float, NULL); | |||||
| } | |||||
| else if (ibuf->rect_float && ibuf->channels == 1) { | |||||
| e_data.texture = GPU_texture_create_2d(ibuf->x, ibuf->y, GPU_R16F, ibuf->rect_float, NULL); | |||||
| } | |||||
| } | |||||
| else { | |||||
| if (e_data.do_color_management) { | |||||
| IMB_colormanagement_display_settings_from_ctx( | |||||
| draw_ctx->evil_C, &view_settings, &display_settings); | |||||
| display_buffer = IMB_display_buffer_acquire( | |||||
| ibuf, view_settings, display_settings, &e_data.cache_handle); | |||||
| if (display_buffer) { | |||||
| e_data.texture = GPU_texture_create_nD(ibuf->x, | |||||
| ibuf->y, | |||||
| 0, | |||||
| 2, | |||||
| display_buffer, | |||||
| GPU_RGBA8, | |||||
| GPU_DATA_UNSIGNED_BYTE, | |||||
| 0, | |||||
| false, | |||||
| NULL); | |||||
| } | |||||
| } | |||||
| else { | |||||
| e_data.texture = GPU_texture_from_blender(ima, iuser, GL_TEXTURE_2D); | |||||
| } | |||||
| } | |||||
| } | |||||
| if (e_data.texture) { | |||||
| GPU_texture_bind(e_data.texture, 0); | |||||
| GPU_texture_wrap_mode(e_data.texture, (sima->flag & SI_DRAW_TILE) != 0); | |||||
| #if 0 | |||||
| if ((sima->flag & SI_SHOW_ZBUF) == 0) { | |||||
| GPU_texture_mipmap_mode(e_data.texture, true, false); | |||||
| GPU_texture_generate_mipmap(e_data.texture); | |||||
| } | |||||
| else { | |||||
| GPU_texture_filters(e_data.texture, GPU_NEAREST, GPU_NEAREST); | |||||
| } | |||||
| #else | |||||
| GPU_texture_filters(e_data.texture, GPU_NEAREST, GPU_NEAREST); | |||||
| #endif | |||||
| GPU_texture_unbind(e_data.texture); | |||||
| } | |||||
| DRWShadingGroup *shgrp = image_uv_editor_shgroup_create(scene, sima, psl->image_pass); | |||||
| DRW_shgroup_call_obmat(shgrp, e_data.gpu_batch_image, e_data.image_mat); | |||||
| } | |||||
| /* \} */ | |||||
| /* -------------------------------------------------------------------- */ | |||||
| /** \name UV Overlay Pass | |||||
| * \{ */ | |||||
| static bool image_uv_editor_uv_overlay_enabled(void) | |||||
| { | |||||
| const DRWContextState *draw_ctx = DRW_context_state_get(); | |||||
| SpaceImage *sima = (SpaceImage *)draw_ctx->space_data; | |||||
| if (!image_uv_editor_is_image()) { | |||||
| return false; | |||||
| } | |||||
| return (sima->mode == SI_MODE_UV) && (draw_ctx->object_edit != NULL); | |||||
| } | |||||
| static bool image_uv_editor_draw_face_dots(const ToolSettings *ts) | |||||
| { | |||||
| if (!image_uv_editor_uv_overlay_enabled()) { | |||||
| return false; | |||||
| } | |||||
| /* checks if we are selecting only faces */ | |||||
| if (ts->uv_flag & UV_SYNC_SELECTION) { | |||||
| return (ts->selectmode & SCE_SELECT_FACE) != 0; | |||||
| } | |||||
| else { | |||||
| return (ts->uv_selectmode == UV_SELECT_FACE); | |||||
| } | |||||
| } | |||||
| static bool image_uv_editor_edges_interpolate(const ToolSettings *ts) | |||||
| { | |||||
| bool interpolate_edges; | |||||
| if (ts->uv_flag & UV_SYNC_SELECTION) { | |||||
| interpolate_edges = (ts->selectmode & SCE_SELECT_VERTEX) != 0; | |||||
| } | |||||
| else { | |||||
| interpolate_edges = (ts->uv_selectmode == UV_SELECT_VERTEX); | |||||
| } | |||||
| return interpolate_edges; | |||||
| } | |||||
| static bool image_uv_editor_edges_smooth(SpaceImage *sima) | |||||
| { | |||||
| return (sima->flag & SI_SMOOTH_UV) != 0; | |||||
| } | |||||
| static DRWShadingGroup *image_uv_editor_face_shgroup_create(IMAGE_UV_EDITOR_PassList *psl) | |||||
| { | |||||
| GPUShader *sh = GPU_shader_get_builtin_shader(GPU_SHADER_2D_UV_FACES); | |||||
| DRWShadingGroup *shgrp = DRW_shgroup_create(sh, psl->uv_overlay_faces_pass); | |||||
| float face_color[4]; | |||||
| float face_select_color[4]; | |||||
| float face_active_color[4]; | |||||
| UI_GetThemeColor4fv(TH_FACE, face_color); | |||||
| UI_GetThemeColor4fv(TH_FACE_SELECT, face_select_color); | |||||
| UI_GetThemeColor4fv(TH_EDITMESH_ACTIVE, face_active_color); | |||||
| DRW_shgroup_uniform_vec4_copy(shgrp, "faceColor", face_color); | |||||
| DRW_shgroup_uniform_vec4_copy(shgrp, "selectColor", face_select_color); | |||||
| DRW_shgroup_uniform_vec4_copy(shgrp, "activeColor", face_active_color); | |||||
| return shgrp; | |||||
| } | |||||
| static DRWShadingGroup *image_uv_editor_edge_shgroup_create(IMAGE_UV_EDITOR_PassList *psl) | |||||
| { | |||||
| const DRWContextState *draw_ctx = DRW_context_state_get(); | |||||
| SpaceImage *sima = (SpaceImage *)draw_ctx->space_data; | |||||
| const bool interpolate_edges = image_uv_editor_edges_interpolate(draw_ctx->scene->toolsettings); | |||||
| DRWShadingGroup *shgrp; | |||||
| const float black_color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; | |||||
| const float white_color[4] = {1.0f, 1.0f, 1.0f, 1.0f}; | |||||
| float edge_select_color[4]; | |||||
| UI_GetThemeColor4fv(TH_EDGE_SELECT, edge_select_color); | |||||
| switch (sima->dt_uv) { | |||||
| case SI_UVDT_DASH: { | |||||
| static float dash_colors[8] = {0.56f, 0.56f, 0.56f, 1.0f, 0.07f, 0.07f, 0.07f, 1.0f}; | |||||
| const float *viewport_size = DRW_viewport_size_get(); | |||||
| float viewport_size_processed[2] = {viewport_size[0] / UI_DPI_FAC, | |||||
| viewport_size[1] / UI_DPI_FAC}; | |||||
| GPUShader *sh = GPU_shader_get_builtin_shader(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR); | |||||
| shgrp = DRW_shgroup_create(sh, psl->uv_overlay_edges_pass); | |||||
| DRW_shgroup_uniform_vec4(shgrp, "colors", dash_colors, 2); | |||||
| DRW_shgroup_uniform_vec2_copy(shgrp, "viewport_size", viewport_size_processed); | |||||
| DRW_shgroup_uniform_int_copy(shgrp, "colors_len", 2); | |||||
| DRW_shgroup_uniform_float_copy(shgrp, "dash_width", 4.0f); | |||||
| DRW_shgroup_uniform_float_copy(shgrp, "dash_factor", 0.5f); | |||||
| break; | |||||
| } | |||||
| case SI_UVDT_BLACK: { | |||||
| GPUShader *sh = GPU_shader_get_builtin_shader( | |||||
| (interpolate_edges) ? GPU_SHADER_2D_UV_EDGES_SMOOTH : GPU_SHADER_2D_UV_EDGES); | |||||
| shgrp = DRW_shgroup_create(sh, psl->uv_overlay_edges_pass); | |||||
| DRW_shgroup_uniform_vec4_copy(shgrp, "edgeColor", black_color); | |||||
| DRW_shgroup_uniform_vec4_copy(shgrp, "selectColor", edge_select_color); | |||||
| break; | |||||
| } | |||||
| case SI_UVDT_WHITE: { | |||||
| GPUShader *sh = GPU_shader_get_builtin_shader( | |||||
| (interpolate_edges) ? GPU_SHADER_2D_UV_EDGES_SMOOTH : GPU_SHADER_2D_UV_EDGES); | |||||
| shgrp = DRW_shgroup_create(sh, psl->uv_overlay_edges_pass); | |||||
| DRW_shgroup_uniform_vec4_copy(shgrp, "edgeColor", white_color); | |||||
| DRW_shgroup_uniform_vec4_copy(shgrp, "selectColor", edge_select_color); | |||||
| break; | |||||
| } | |||||
| case SI_UVDT_OUTLINE: { | |||||
| /* TODO: this is a dummy implementation so this choice does not crash */ | |||||
| GPUShader *sh = GPU_shader_get_builtin_shader(GPU_SHADER_2D_UV_EDGES); | |||||
| shgrp = DRW_shgroup_create(sh, psl->uv_overlay_edges_pass); | |||||
| DRW_shgroup_uniform_vec4_copy(shgrp, "edgeColor", white_color); | |||||
| DRW_shgroup_uniform_vec4_copy(shgrp, "selectColor", edge_select_color); | |||||
| break; | |||||
| } | |||||
| } | |||||
| return shgrp; | |||||
| } | |||||
| /* \} */ | |||||
| /* -------------------------------------------------------------------- */ | |||||
| /** \name UV Stretching Overlay Pass | |||||
| * \{ */ | |||||
| static bool image_uv_editor_stretching_overlay_enabled(void) | |||||
| { | |||||
| const DRWContextState *draw_ctx = DRW_context_state_get(); | |||||
| SpaceImage *sima = (SpaceImage *)draw_ctx->space_data; | |||||
| if (!image_uv_editor_is_image()) { | |||||
| return false; | |||||
| } | |||||
| return (sima->mode == SI_MODE_UV) && ((sima->flag & SI_DRAW_STRETCH) != 0); | |||||
| } | |||||
| static DRWShadingGroup *image_uv_editor_stretching_shgroup_create(IMAGE_UV_EDITOR_PassList *psl) | |||||
| { | |||||
| const DRWContextState *draw_ctx = DRW_context_state_get(); | |||||
| SpaceImage *sima = (SpaceImage *)draw_ctx->space_data; | |||||
| DRWShadingGroup *shgrp = NULL; | |||||
| if (sima->dt_uvstretch == SI_UVDT_STRETCH_ANGLE) { | |||||
| GPUShader *sh = GPU_shader_get_builtin_shader(GPU_SHADER_2D_UV_FACES_STRETCH_ANGLE); | |||||
| shgrp = DRW_shgroup_create(sh, psl->uv_stretching_overlay_pass); | |||||
| float asp[2]; | |||||
| ED_space_image_get_uv_aspect(sima, &asp[0], &asp[1]); | |||||
| DRW_shgroup_uniform_vec2_copy(shgrp, "aspect", asp); | |||||
| } | |||||
| else if (sima->dt_uvstretch == SI_UVDT_STRETCH_AREA) { | |||||
| GPUShader *sh = GPU_shader_get_builtin_shader(GPU_SHADER_2D_UV_FACES_STRETCH_AREA); | |||||
| shgrp = DRW_shgroup_create(sh, psl->uv_stretching_overlay_pass); | |||||
| // Uniforms for the stretchign ratio are set in image_uv_editor_stretching_ratio_update | |||||
| } | |||||
| return shgrp; | |||||
| } | |||||
| static GPUBatch *image_uv_editor_stretching_batch_get(IMAGE_UV_EDITOR_PrivateData *pd, | |||||
| struct Mesh *mesh) | |||||
| { | |||||
| const DRWContextState *draw_ctx = DRW_context_state_get(); | |||||
| SpaceImage *sima = (SpaceImage *)draw_ctx->space_data; | |||||
| GPUBatch *geom = NULL; | |||||
| if (sima->dt_uvstretch == SI_UVDT_STRETCH_ANGLE) { | |||||
| geom = DRW_mesh_batch_cache_get_edituv_faces_stretch_angle(mesh); | |||||
| } | |||||
| else if (sima->dt_uvstretch == SI_UVDT_STRETCH_AREA) { | |||||
| float *tmp_tot_area, *tmp_tot_area_uv; | |||||
| geom = DRW_mesh_batch_cache_get_edituv_faces_stretch_area( | |||||
| mesh, &tmp_tot_area, &tmp_tot_area_uv); | |||||
| pd->uv_stretching_total_area += *tmp_tot_area; | |||||
| pd->uv_stretching_total_area_uv += *tmp_tot_area_uv; | |||||
| } | |||||
| return geom; | |||||
| } | |||||
| static void image_uv_editor_stretching_ratio_init(IMAGE_UV_EDITOR_PrivateData *pd) | |||||
| { | |||||
| pd->uv_stretching_total_area = 0.0f; | |||||
| pd->uv_stretching_total_area_uv = 0.0f; | |||||
| pd->uv_stretching_total_area_ratio = 0.0f; | |||||
| pd->uv_stretching_total_area_ratio_inv = 0.0f; | |||||
| } | |||||
| static void image_uv_editor_stretching_ratio_update(IMAGE_UV_EDITOR_PrivateData *pd) | |||||
| { | |||||
| if (pd->uv_stretching_total_area > FLT_EPSILON && | |||||
| pd->uv_stretching_total_area_uv > FLT_EPSILON) { | |||||
| pd->uv_stretching_total_area_ratio = pd->uv_stretching_total_area / | |||||
| pd->uv_stretching_total_area_uv; | |||||
| pd->uv_stretching_total_area_ratio_inv = pd->uv_stretching_total_area_uv / | |||||
| pd->uv_stretching_total_area; | |||||
| DRW_shgroup_uniform_float_copy( | |||||
| pd->uv_stretching_overlay_shgrp, "totalAreaRatio", pd->uv_stretching_total_area_ratio); | |||||
| DRW_shgroup_uniform_float_copy(pd->uv_stretching_overlay_shgrp, | |||||
| "totalAreaRatioInv", | |||||
| pd->uv_stretching_total_area_ratio_inv); | |||||
| } | |||||
| } | |||||
| /* \} */ | |||||
| /* -------------------------------------------------------------------- */ | |||||
| /** \name UV Shadow Overlay Pass | |||||
| * \{ */ | |||||
| static DRWShadingGroup *image_uv_editor_shadow_shgroup_create(IMAGE_UV_EDITOR_PassList *psl) | |||||
| { | |||||
| float shadow_color[4]; | |||||
| UI_GetThemeColor4fv(TH_UV_SHADOW, shadow_color); | |||||
| GPUShader *sh = GPU_shader_get_builtin_shader(GPU_SHADER_2D_UV_EDGES_SMOOTH); | |||||
| DRWShadingGroup *shgrp = DRW_shgroup_create(sh, psl->uv_shadow_overlay_pass); | |||||
| DRW_shgroup_uniform_vec4_copy(shgrp, "edgeColor", shadow_color); | |||||
| return shgrp; | |||||
| } | |||||
| static bool image_uv_editor_shadow_overlay_enabled(void) | |||||
| { | |||||
| const DRWContextState *draw_ctx = DRW_context_state_get(); | |||||
| SpaceImage *sima = (SpaceImage *)draw_ctx->space_data; | |||||
| if (!image_uv_editor_is_image()) { | |||||
| return false; | |||||
| } | |||||
| return ((sima->mode == SI_MODE_PAINT) && | |||||
| ((draw_ctx->object_mode & (OB_MODE_TEXTURE_PAINT | OB_MODE_EDIT)) != 0)) || | |||||
| ((sima->mode == SI_MODE_VIEW) && | |||||
| ((draw_ctx->object_mode & (OB_MODE_TEXTURE_PAINT)) != 0)); | |||||
| } | |||||
| /* \} */ | |||||
| /* -------------------------------------------------------------------- */ | |||||
| /** \name DrawEngine Interface | |||||
| * \{ */ | |||||
| static void image_uv_editor_init(void *vedata) | |||||
| { | |||||
| IMAGE_UV_EDITOR_Data *ied = (IMAGE_UV_EDITOR_Data *)vedata; | |||||
| IMAGE_UV_EDITOR_StorageList *stl = ied->stl; | |||||
| e_data.image = NULL; | |||||
| e_data.ibuf = NULL; | |||||
| e_data.lock = NULL; | |||||
| e_data.texture = NULL; | |||||
| e_data.do_color_management = false; | |||||
| e_data.do_channel_shuffling = false; | |||||
| e_data.do_uv_overlay = false; | |||||
| e_data.do_uv_stretching_overlay = false; | |||||
| e_data.do_shadow_overlay = false; | |||||
| e_data.cache_handle = NULL; | |||||
| if (!stl->g_data) { | |||||
| /* Alloc transient pointers */ | |||||
| stl->g_data = MEM_callocN(sizeof(*stl->g_data), __func__); | |||||
| } | |||||
| IMAGE_UV_EDITOR_PrivateData *pd = stl->g_data; | |||||
| pd->uv_stretching_overlay_shgrp = NULL; | |||||
| pd->uv_overlay_faces_shgrp = NULL; | |||||
| pd->uv_overlay_edges_shgrp = NULL; | |||||
| pd->uv_overlay_face_dots_shgrp = NULL; | |||||
| pd->uv_overlay_verts_shgrp[0] = NULL; | |||||
| pd->uv_overlay_verts_shgrp[1] = NULL; | |||||
| image_uv_editor_stretching_ratio_init(pd); | |||||
| /* Create Shaders */ | |||||
| { | |||||
| } | |||||
| /* Create batch and unit matrix */ | |||||
| if (!e_data.gpu_batch_image) { | |||||
| e_data.gpu_batch_image = DRW_cache_image_plane_get(); | |||||
| unit_m4(e_data.image_mat); | |||||
| } | |||||
| } | |||||
| static void image_uv_editor_cache_init(void *vedata) | |||||
| { | |||||
| IMAGE_UV_EDITOR_Data *ied = (IMAGE_UV_EDITOR_Data *)vedata; | |||||
| IMAGE_UV_EDITOR_PassList *psl = ied->psl; | |||||
| IMAGE_UV_EDITOR_StorageList *stl = ied->stl; | |||||
| IMAGE_UV_EDITOR_PrivateData *pd = stl->g_data; | |||||
| const DRWContextState *draw_ctx = DRW_context_state_get(); | |||||
| SpaceImage *sima = (SpaceImage *)draw_ctx->space_data; | |||||
| /* e_data.image needs to be set as first other calls may access it to determine | |||||
| * if we are looking at a texture, viewer or render result */ | |||||
| e_data.image = ED_space_image(sima); | |||||
| e_data.do_uv_overlay = image_uv_editor_uv_overlay_enabled(); | |||||
| e_data.do_uv_overlay_show_faces = ((sima->flag & SI_NO_DRAWFACES) == 0); | |||||
| e_data.do_uv_overlay_show_faces_dots = image_uv_editor_draw_face_dots( | |||||
| draw_ctx->scene->toolsettings); | |||||
| e_data.do_uv_stretching_overlay = image_uv_editor_stretching_overlay_enabled(); | |||||
| e_data.do_shadow_overlay = image_uv_editor_shadow_overlay_enabled(); | |||||
| { | |||||
| psl->image_pass = DRW_pass_create( | |||||
| "Image", DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_ALWAYS | DRW_STATE_BLEND_ALPHA); | |||||
| } | |||||
| if (e_data.do_uv_overlay) { | |||||
| psl->uv_overlay_faces_pass = DRW_pass_create( | |||||
| "UV Overlay face", DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_ALWAYS | DRW_STATE_BLEND_ALPHA); | |||||
| pd->uv_overlay_faces_shgrp = image_uv_editor_face_shgroup_create(psl); | |||||
| psl->uv_overlay_verts_pass = DRW_pass_create("UV Overlay verts", | |||||
| DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_ALWAYS | | |||||
| DRW_STATE_PROGRAM_POINT_SIZE | | |||||
| DRW_STATE_BLEND_ALPHA); | |||||
| float vert_color[4]; | |||||
| const float pinned_color[4] = {1.0f, 0.0f, 0.0f, 1.0f}; | |||||
| const float transparent_color[4] = {0.0f, 0.0f, 0.0f, 0.0f}; | |||||
| float select_color[4]; | |||||
| UI_GetThemeColor4fv(TH_VERTEX, vert_color); | |||||
| vert_color[3] = 1.0f; | |||||
| UI_GetThemeColor4fv(TH_VERTEX_SELECT, select_color); | |||||
| const float point_size = UI_GetThemeValuef(TH_VERTEX_SIZE); | |||||
| DRWShadingGroup *shgrp; | |||||
| shgrp = DRW_shgroup_create(GPU_shader_get_builtin_shader(GPU_SHADER_2D_UV_VERTS), | |||||
| psl->uv_overlay_verts_pass); | |||||
| DRW_shgroup_uniform_vec4_copy(shgrp, "vertColor", vert_color); | |||||
| DRW_shgroup_uniform_vec4_copy(shgrp, "selectColor", transparent_color); | |||||
| DRW_shgroup_uniform_vec4_copy(shgrp, "pinnedColor", pinned_color); | |||||
| DRW_shgroup_uniform_float_copy(shgrp, "pointSize", (point_size + 1.5f) * M_SQRT2); | |||||
| DRW_shgroup_uniform_float_copy(shgrp, "outlineWidth", 0.75f); | |||||
| pd->uv_overlay_verts_shgrp[0] = shgrp; | |||||
| /* We have problem in this mode when face order make some verts | |||||
| * appear unselected because an adjacent face is not selected and | |||||
| * render after the selected face. | |||||
| * So, to avoid sorting verts by state we just render selected verts | |||||
| * on top. A bit overkill but it's simple. */ | |||||
| shgrp = DRW_shgroup_create(GPU_shader_get_builtin_shader(GPU_SHADER_2D_UV_VERTS), | |||||
| psl->uv_overlay_verts_pass); | |||||
| DRW_shgroup_uniform_vec4_copy(shgrp, "vertColor", transparent_color); | |||||
| DRW_shgroup_uniform_vec4_copy(shgrp, "selectColor", select_color); | |||||
| DRW_shgroup_uniform_vec4_copy(shgrp, "pinnedColor", pinned_color); | |||||
| DRW_shgroup_uniform_float_copy(shgrp, "pointSize", (point_size + 1.5f) * M_SQRT2); | |||||
| DRW_shgroup_uniform_float_copy(shgrp, "outlineWidth", 0.75f); | |||||
| pd->uv_overlay_verts_shgrp[1] = shgrp; | |||||
| const bool smooth_edges = image_uv_editor_edges_smooth(sima); | |||||
| psl->uv_overlay_edges_pass = DRW_pass_create( | |||||
| "UV Overlay edge", | |||||
| DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_ALWAYS | DRW_STATE_FIRST_VERTEX_CONVENTION | | |||||
| (smooth_edges ? (DRW_STATE_WIRE_SMOOTH | DRW_STATE_BLEND_ALPHA) : 0)); | |||||
| if (smooth_edges) { | |||||
| DRW_pass_line_width_set(psl->uv_overlay_edges_pass, 1.0f); | |||||
| } | |||||
| pd->uv_overlay_edges_shgrp = image_uv_editor_edge_shgroup_create(psl); | |||||
| } | |||||
| if (e_data.do_uv_overlay_show_faces_dots) { | |||||
| DRWShadingGroup *shgrp = DRW_shgroup_create( | |||||
| GPU_shader_get_builtin_shader(GPU_SHADER_2D_UV_FACEDOTS), psl->uv_overlay_verts_pass); | |||||
| float vert_color[4]; | |||||
| float select_color[4]; | |||||
| UI_GetThemeColor4fv(TH_WIRE, vert_color); | |||||
| vert_color[3] = 1.0f; | |||||
| UI_GetThemeColor4fv(TH_VERTEX_SELECT, select_color); | |||||
| const float point_size = UI_GetThemeValuef(TH_FACEDOT_SIZE); | |||||
| DRW_shgroup_uniform_vec4_copy(shgrp, "vertColor", vert_color); | |||||
| DRW_shgroup_uniform_vec4_copy(shgrp, "selectColor", select_color); | |||||
| DRW_shgroup_uniform_float_copy(shgrp, "pointSize", point_size); | |||||
| pd->uv_overlay_face_dots_shgrp = shgrp; | |||||
| } | |||||
| if (e_data.do_uv_stretching_overlay) { | |||||
| psl->uv_stretching_overlay_pass = DRW_pass_create( | |||||
| "UV Stretching Overlay", DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_ALWAYS); | |||||
| pd->uv_stretching_overlay_shgrp = image_uv_editor_stretching_shgroup_create(psl); | |||||
| } | |||||
| if (e_data.do_shadow_overlay) { | |||||
| psl->uv_shadow_overlay_pass = DRW_pass_create("UV Shadow Overlay", | |||||
| DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_ALWAYS | | |||||
| DRW_STATE_WIRE_SMOOTH | | |||||
| DRW_STATE_BLEND_ALPHA); | |||||
| DRW_pass_line_width_set(psl->uv_shadow_overlay_pass, 1.0f); | |||||
| pd->uv_shadow_overlay_shgrp = image_uv_editor_shadow_shgroup_create(psl); | |||||
| } | |||||
| bool show_stereo3d = (e_data.image && BKE_image_is_stereo(e_data.image) && | |||||
| (sima->iuser.flag & IMA_SHOW_STEREO)); | |||||
| bool show_multilayer = e_data.image && BKE_image_is_multilayer(e_data.image); | |||||
| if (show_stereo3d) { | |||||
| if (show_multilayer) { | |||||
| /* update multiindex and pass for the current eye */ | |||||
| BKE_image_multilayer_index(e_data.image->rr, &sima->iuser); | |||||
| } | |||||
| else { | |||||
| BKE_image_multiview_index(e_data.image, &sima->iuser); | |||||
| } | |||||
| } | |||||
| { | |||||
| ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &e_data.lock); | |||||
| image_uv_editor_add_image(psl, e_data.image, &sima->iuser, ibuf); | |||||
| e_data.ibuf = ibuf; | |||||
| } | |||||
| } | |||||
| static void image_uv_editor_cache_populate(void *vedata, Object *ob) | |||||
| { | |||||
| const DRWContextState *draw_ctx = DRW_context_state_get(); | |||||
| IMAGE_UV_EDITOR_Data *ied = (IMAGE_UV_EDITOR_Data *)vedata; | |||||
| IMAGE_UV_EDITOR_StorageList *stl = ied->stl; | |||||
| IMAGE_UV_EDITOR_PrivateData *pd = stl->g_data; | |||||
| if (e_data.do_uv_overlay) { | |||||
| if (ob->type == OB_MESH) { | |||||
| if ((ob == draw_ctx->object_edit) || BKE_object_is_in_editmode(ob)) { | |||||
| if (e_data.do_uv_overlay_show_faces) { | |||||
| struct GPUBatch *geom = DRW_mesh_batch_cache_get_edituv_faces(ob->data); | |||||
| if (geom) { | |||||
| DRW_shgroup_call_obmat(pd->uv_overlay_faces_shgrp, geom, e_data.image_mat); | |||||
| } | |||||
| } | |||||
| if (e_data.do_uv_overlay_show_faces_dots) { | |||||
| struct GPUBatch *geom = DRW_mesh_batch_cache_get_edituv_facedots(ob->data); | |||||
| if (geom) { | |||||
| DRW_shgroup_call_obmat(pd->uv_overlay_face_dots_shgrp, geom, e_data.image_mat); | |||||
| } | |||||
| } | |||||
| { | |||||
| struct GPUBatch *geom = DRW_mesh_batch_cache_get_edituv_edges(ob->data); | |||||
| if (geom) { | |||||
| DRW_shgroup_call_obmat(pd->uv_overlay_edges_shgrp, geom, e_data.image_mat); | |||||
| } | |||||
| } | |||||
| { | |||||
| struct GPUBatch *geom = DRW_mesh_batch_cache_get_edituv_verts(ob->data); | |||||
| if (geom) { | |||||
| DRW_shgroup_call_obmat(pd->uv_overlay_verts_shgrp[0], geom, e_data.image_mat); | |||||
| DRW_shgroup_call_obmat(pd->uv_overlay_verts_shgrp[1], geom, e_data.image_mat); | |||||
| } | |||||
| } | |||||
| if (e_data.do_uv_stretching_overlay) { | |||||
| struct GPUBatch *geom = image_uv_editor_stretching_batch_get(pd, ob->data); | |||||
| if (geom) { | |||||
| DRW_shgroup_call_obmat(pd->uv_stretching_overlay_shgrp, geom, e_data.image_mat); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| if (e_data.do_shadow_overlay) { | |||||
| if (ob->type == OB_MESH) { | |||||
| struct GPUBatch *geom = DRW_mesh_batch_cache_get_uv_edges(ob->data); | |||||
| if (geom) { | |||||
| DRW_shgroup_call_obmat(pd->uv_shadow_overlay_shgrp, geom, e_data.image_mat); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| static void image_uv_editor_cache_finish(void *vedata) | |||||
| { | |||||
| IMAGE_UV_EDITOR_Data *ied = (IMAGE_UV_EDITOR_Data *)vedata; | |||||
| IMAGE_UV_EDITOR_StorageList *stl = ied->stl; | |||||
| IMAGE_UV_EDITOR_PrivateData *pd = stl->g_data; | |||||
| image_uv_editor_stretching_ratio_update(pd); | |||||
| } | |||||
| static void image_uv_editor_draw_finish(void *UNUSED(vedata)) | |||||
| { | |||||
| const DRWContextState *draw_ctx = DRW_context_state_get(); | |||||
| SpaceImage *sima = (SpaceImage *)draw_ctx->space_data; | |||||
| /* free temp memory */ | |||||
| if (e_data.do_color_management && e_data.texture) { | |||||
| GPU_texture_free(e_data.texture); | |||||
| IMB_display_buffer_release(e_data.cache_handle); | |||||
| } | |||||
| ED_space_image_release_buffer(sima, e_data.ibuf, e_data.lock); | |||||
| e_data.image = NULL; | |||||
| } | |||||
| static void image_uv_editor_draw_scene(void *vedata) | |||||
| { | |||||
| IMAGE_UV_EDITOR_Data *ied = (IMAGE_UV_EDITOR_Data *)vedata; | |||||
| IMAGE_UV_EDITOR_PassList *psl = ied->psl; | |||||
| DRW_draw_pass(psl->image_pass); | |||||
| if (e_data.do_uv_stretching_overlay) { | |||||
| DRW_draw_pass(psl->uv_stretching_overlay_pass); | |||||
| } | |||||
| if (e_data.do_uv_overlay) { | |||||
| DRW_stats_group_start("UV Overlay"); | |||||
| DRW_draw_pass(psl->uv_overlay_faces_pass); | |||||
| DRW_draw_pass(psl->uv_overlay_edges_pass); | |||||
| DRW_draw_pass(psl->uv_overlay_verts_pass); | |||||
| DRW_stats_group_end(); | |||||
| } | |||||
| if (e_data.do_shadow_overlay) { | |||||
| DRW_draw_pass(psl->uv_shadow_overlay_pass); | |||||
| } | |||||
| image_uv_editor_draw_finish(vedata); | |||||
| } | |||||
| static void image_uv_editor_free(void) | |||||
| { | |||||
| GPUShader **sh_data_as_array = (GPUShader **)&e_data.shaders; | |||||
| for (int i = 0; i < (sizeof(IMAGE_UV_EDITOR_Shaders) / sizeof(GPUShader *)); i++) { | |||||
| DRW_SHADER_FREE_SAFE(sh_data_as_array[i]); | |||||
| } | |||||
| } | |||||
| /* \} */ | |||||
| static const DrawEngineDataSize image_uv_editor_data_size = DRW_VIEWPORT_DATA_SIZE( | |||||
| IMAGE_UV_EDITOR_Data); | |||||
| DrawEngineType draw_engine_image_uv_editor_type = { | |||||
| NULL, | |||||
| NULL, | |||||
| N_("Image/UV Editor"), | |||||
| &image_uv_editor_data_size, | |||||
| &image_uv_editor_init, | |||||
| &image_uv_editor_free, | |||||
| &image_uv_editor_cache_init, | |||||
| &image_uv_editor_cache_populate, | |||||
| &image_uv_editor_cache_finish, | |||||
| NULL, | |||||
| &image_uv_editor_draw_scene, | |||||
| NULL, | |||||
| NULL, | |||||
| NULL, | |||||
| }; | |||||