/* * Copyright 2016, Blender Foundation. * * 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. * * Contributor(s): Blender Institute * */ /** \file blender/draw/draw_manager.c * \ingroup draw */ #include #include "BLI_listbase.h" #include "BLI_rect.h" #include "BLI_string.h" #include "BIF_glutil.h" #include "BKE_global.h" #include "BLT_translation.h" #include "DRW_engine.h" #include "DRW_render.h" #include "DNA_view3d_types.h" #include "GPU_basic_shader.h" #include "GPU_batch.h" #include "GPU_draw.h" #include "GPU_extensions.h" #include "GPU_framebuffer.h" #include "GPU_immediate.h" #include "GPU_matrix.h" #include "GPU_shader.h" #include "GPU_texture.h" #include "GPU_uniformbuffer.h" #include "GPU_viewport.h" #include "RE_engine.h" #include "UI_resources.h" #include "clay.h" extern char datatoc_gpu_shader_2D_vert_glsl[]; extern char datatoc_gpu_shader_3D_vert_glsl[]; extern char datatoc_gpu_shader_basic_vert_glsl[]; /* Structures */ typedef enum { DRW_UNIFORM_BOOL, DRW_UNIFORM_INT, DRW_UNIFORM_FLOAT, DRW_UNIFORM_TEXTURE, DRW_UNIFORM_BUFFER, DRW_UNIFORM_MAT3, DRW_UNIFORM_MAT4, DRW_UNIFORM_BLOCK } DRWUniformType; struct DRWUniform { struct DRWUniform *next, *prev; DRWUniformType type; int location; int length; int arraysize; int bindloc; const void *value; }; struct DRWInterface { ListBase uniforms; /* matrices locations */ int modelview; int projection; int modelviewprojection; int viewprojection; int normal; int eye; }; struct DRWPass { ListBase shgroups; DRWState state; float state_param; /* Line / Point width */ }; typedef struct DRWCall { struct DRWCall *next, *prev; Batch *geometry; float(*obmat)[4]; } DRWCall; struct DRWShadingGroup { struct DRWShadingGroup *next, *prev; struct GPUShader *shader; /* Shader to bind */ struct DRWInterface *interface; /* Uniforms pointers */ ListBase calls; /* List with all geometry and transforms */ int state; /* State changes for this batch only */ short dyntype; /* Dynamic Batch type, 0 is normal */ Batch *dyngeom; /* Dynamic batch */ GLuint instance_vbo; /* Dynamic batch VBO storing Model Matrices */ int instance_count; /* Dynamic batch Number of instance to render */ }; /* Render State */ static struct DRWGlobalState{ GPUShader *shader; struct GPUFrameBuffer *default_framebuffer; FramebufferList *current_fbl; TextureList *current_txl; PassList *current_psl; ListBase bound_texs; int tex_bind_id; float size[2]; float screenvecs[2][3]; float pixsize; /* Current rendering context set by DRW_viewport_init */ const struct bContext *context; } DST = {NULL}; /* ***************************************** TEXTURES ******************************************/ static void drw_texture_get_format(DRWTextureFormat format, GPUFormat *data_type, int *channels) { switch (format) { case DRW_TEX_RGBA_8: *data_type = GPU_RGBA8; break; case DRW_TEX_RGBA_16: *data_type = GPU_RGBA16F; break; case DRW_TEX_RGBA_32: *data_type = GPU_RGBA32F; break; case DRW_TEX_RGB_8: *data_type = GPU_RGB8; break; case DRW_TEX_RGB_16: *data_type = GPU_RGB16F; break; case DRW_TEX_RGB_32: *data_type = GPU_RGB32F; break; case DRW_TEX_RG_8: *data_type = GPU_RG8; break; case DRW_TEX_RG_16: *data_type = GPU_RG16F; break; case DRW_TEX_RG_32: *data_type = GPU_RG32F; break; case DRW_TEX_R_8: *data_type = GPU_R8; break; case DRW_TEX_R_16: *data_type = GPU_R16F; break; case DRW_TEX_R_32: *data_type = GPU_R32F; break; case DRW_TEX_DEPTH_16: *data_type = GPU_DEPTH_COMPONENT16; break; case DRW_TEX_DEPTH_24: *data_type = GPU_DEPTH_COMPONENT24; break; case DRW_TEX_DEPTH_32: *data_type = GPU_DEPTH_COMPONENT32F; break; } switch (format) { case DRW_TEX_RGBA_8: case DRW_TEX_RGBA_16: case DRW_TEX_RGBA_32: *channels = 4; break; case DRW_TEX_RGB_8: case DRW_TEX_RGB_16: case DRW_TEX_RGB_32: *channels = 3; break; case DRW_TEX_RG_8: case DRW_TEX_RG_16: case DRW_TEX_RG_32: *channels = 2; break; default: *channels = 1; break; } } static void drw_texture_set_parameters(GPUTexture *tex, DRWTextureFlag flags) { GPU_texture_bind(tex, 0); GPU_texture_filter_mode(tex, flags & DRW_TEX_FILTER); GPU_texture_wrap_mode(tex, flags & DRW_TEX_WRAP); GPU_texture_compare_mode(tex, flags & DRW_TEX_COMPARE); GPU_texture_unbind(tex); } GPUTexture *DRW_texture_create_1D(int w, DRWTextureFormat format, DRWTextureFlag flags, const float *fpixels) { GPUTexture *tex; GPUFormat data_type; int channels; drw_texture_get_format(format, &data_type, &channels); tex = GPU_texture_create_1D_custom(w, channels, data_type, fpixels, NULL); drw_texture_set_parameters(tex, flags); return tex; } GPUTexture *DRW_texture_create_2D(int w, int h, DRWTextureFormat format, DRWTextureFlag flags, const float *fpixels) { GPUTexture *tex; GPUFormat data_type; int channels; drw_texture_get_format(format, &data_type, &channels); tex = GPU_texture_create_2D_custom(w, h, channels, data_type, fpixels, NULL); drw_texture_set_parameters(tex, flags); return tex; } /* TODO make use of format */ GPUTexture *DRW_texture_create_2D_array(int w, int h, int d, DRWTextureFormat UNUSED(format), DRWTextureFlag flags, const float *fpixels) { GPUTexture *tex; tex = GPU_texture_create_2D_array(w, h, d, fpixels, NULL); drw_texture_set_parameters(tex, flags); return tex; } void DRW_texture_free(GPUTexture *tex) { GPU_texture_free(tex); } /* ************************************ UNIFORM BUFFER OBJECT **********************************/ GPUUniformBuffer *DRW_uniformbuffer_create(int size, const void *data) { return GPU_uniformbuffer_create(size, data, NULL); } void DRW_uniformbuffer_update(GPUUniformBuffer *ubo, const void *data) { GPU_uniformbuffer_update(ubo, data); } void DRW_uniformbuffer_free(GPUUniformBuffer *ubo) { GPU_uniformbuffer_free(ubo); } /* ****************************************** SHADERS ******************************************/ GPUShader *DRW_shader_create(const char *vert, const char *geom, const char *frag, const char *defines) { return GPU_shader_create(vert, frag, geom, NULL, defines, 0, 0, 0); } GPUShader *DRW_shader_create_2D(const char *frag, const char *defines) { return GPU_shader_create(datatoc_gpu_shader_2D_vert_glsl, frag, NULL, NULL, defines, 0, 0, 0); } GPUShader *DRW_shader_create_3D(const char *frag, const char *defines) { return GPU_shader_create(datatoc_gpu_shader_3D_vert_glsl, frag, NULL, NULL, defines, 0, 0, 0); } GPUShader *DRW_shader_create_3D_depth_only(void) { return GPU_shader_get_builtin_shader(GPU_SHADER_3D_DEPTH_ONLY); } void DRW_shader_free(GPUShader *shader) { GPU_shader_free(shader); } /* ***************************************** INTERFACE ******************************************/ static DRWInterface *DRW_interface_create(GPUShader *shader) { DRWInterface *interface = MEM_mallocN(sizeof(DRWInterface), "DRWInterface"); interface->modelview = GPU_shader_get_uniform(shader, "ModelViewMatrix"); interface->projection = GPU_shader_get_uniform(shader, "ProjectionMatrix"); interface->viewprojection = GPU_shader_get_uniform(shader, "ViewProjectionMatrix"); interface->modelviewprojection = GPU_shader_get_uniform(shader, "ModelViewProjectionMatrix"); interface->normal = GPU_shader_get_uniform(shader, "NormalMatrix"); interface->eye = GPU_shader_get_uniform(shader, "eye"); BLI_listbase_clear(&interface->uniforms); return interface; } static void DRW_interface_uniform(DRWShadingGroup *shgroup, const char *name, DRWUniformType type, const void *value, int length, int arraysize, int bindloc) { DRWUniform *uni = MEM_mallocN(sizeof(DRWUniform), "DRWUniform"); if (type == DRW_UNIFORM_BLOCK) { uni->location = GPU_shader_get_uniform_block(shgroup->shader, name); } else { uni->location = GPU_shader_get_uniform(shgroup->shader, name); } uni->type = type; uni->value = value; uni->length = length; uni->arraysize = arraysize; uni->bindloc = bindloc; /* for textures */ if (uni->location == -1) { if (G.debug & G_DEBUG) fprintf(stderr, "Uniform '%s' not found!\n", name); MEM_freeN(uni); return; } BLI_addtail(&shgroup->interface->uniforms, uni); } void DRW_get_dfdy_factors(float dfdyfac[2]) { GPU_get_dfdy_factors(dfdyfac); } /* ***************************************** SHADING GROUP ******************************************/ DRWShadingGroup *DRW_shgroup_create(struct GPUShader *shader, DRWPass *pass) { DRWShadingGroup *shgroup = MEM_callocN(sizeof(DRWShadingGroup), "DRWShadingGroup"); shgroup->shader = shader; shgroup->interface = DRW_interface_create(shader); shgroup->state = 0; shgroup->dyntype = 0; shgroup->dyngeom = NULL; BLI_addtail(&pass->shgroups, shgroup); return shgroup; } void DRW_shgroup_free(struct DRWShadingGroup *shgroup) { BLI_freelistN(&shgroup->calls); BLI_freelistN(&shgroup->interface->uniforms); MEM_freeN(shgroup->interface); if (shgroup->dyngeom) Batch_discard_all(shgroup->dyngeom); } /* Later use VBO */ void DRW_shgroup_call_add(DRWShadingGroup *shgroup, Batch *geom, float (*obmat)[4]) { if (geom) { DRWCall *call = MEM_callocN(sizeof(DRWCall), "DRWCall"); call->obmat = obmat; call->geometry = geom; BLI_addtail(&shgroup->calls, call); } } /* Make sure you know what you do when using this, * State is not revert back at the end of the shgroup */ void DRW_shgroup_state_set(DRWShadingGroup *shgroup, DRWState state) { shgroup->state = state; } void DRW_shgroup_dyntype_set(DRWShadingGroup *shgroup, int type) { shgroup->dyntype = type; } void DRW_shgroup_uniform_texture(DRWShadingGroup *shgroup, const char *name, const GPUTexture *tex, int loc) { DRW_interface_uniform(shgroup, name, DRW_UNIFORM_TEXTURE, tex, 0, 0, loc); } void DRW_shgroup_uniform_block(DRWShadingGroup *shgroup, const char *name, const GPUUniformBuffer *ubo, int loc) { DRW_interface_uniform(shgroup, name, DRW_UNIFORM_BLOCK, ubo, 0, 0, loc); } void DRW_shgroup_uniform_buffer(DRWShadingGroup *shgroup, const char *name, const int value, int loc) { /* we abuse the lenght attrib to store the buffer index */ DRW_interface_uniform(shgroup, name, DRW_UNIFORM_BUFFER, NULL, value, 0, loc); } void DRW_shgroup_uniform_bool(DRWShadingGroup *shgroup, const char *name, const bool *value, int arraysize) { DRW_interface_uniform(shgroup, name, DRW_UNIFORM_BOOL, value, 1, arraysize, 0); } void DRW_shgroup_uniform_float(DRWShadingGroup *shgroup, const char *name, const float *value, int arraysize) { DRW_interface_uniform(shgroup, name, DRW_UNIFORM_FLOAT, value, 1, arraysize, 0); } void DRW_shgroup_uniform_vec2(DRWShadingGroup *shgroup, const char *name, const float *value, int arraysize) { DRW_interface_uniform(shgroup, name, DRW_UNIFORM_FLOAT, value, 2, arraysize, 0); } void DRW_shgroup_uniform_vec3(DRWShadingGroup *shgroup, const char *name, const float *value, int arraysize) { DRW_interface_uniform(shgroup, name, DRW_UNIFORM_FLOAT, value, 3, arraysize, 0); } void DRW_shgroup_uniform_vec4(DRWShadingGroup *shgroup, const char *name, const float *value, int arraysize) { DRW_interface_uniform(shgroup, name, DRW_UNIFORM_FLOAT, value, 4, arraysize, 0); } void DRW_shgroup_uniform_int(DRWShadingGroup *shgroup, const char *name, const int *value, int arraysize) { DRW_interface_uniform(shgroup, name, DRW_UNIFORM_INT, value, 1, arraysize, 0); } void DRW_shgroup_uniform_ivec2(DRWShadingGroup *shgroup, const char *name, const int *value, int arraysize) { DRW_interface_uniform(shgroup, name, DRW_UNIFORM_INT, value, 2, arraysize, 0); } void DRW_shgroup_uniform_ivec3(DRWShadingGroup *shgroup, const char *name, const int *value, int arraysize) { DRW_interface_uniform(shgroup, name, DRW_UNIFORM_INT, value, 3, arraysize, 0); } void DRW_shgroup_uniform_mat3(DRWShadingGroup *shgroup, const char *name, const float *value) { DRW_interface_uniform(shgroup, name, DRW_UNIFORM_MAT3, value, 9, 1, 0); } void DRW_shgroup_uniform_mat4(DRWShadingGroup *shgroup, const char *name, const float *value) { DRW_interface_uniform(shgroup, name, DRW_UNIFORM_MAT4, value, 16, 1, 0); } /* Creates OGL primitives based on DRWCall.obmat position list */ static void shgroup_dynamic_batch_primitives(DRWShadingGroup *shgroup) { int i = 0; int nbr = BLI_listbase_count(&shgroup->calls); GLenum type; if (nbr == 0) { if (shgroup->dyngeom) { Batch_discard(shgroup->dyngeom); shgroup->dyngeom = NULL; } return; } /* Gather Data */ float *data = MEM_mallocN(sizeof(float) * 3 * nbr , "Object Center Batch data"); for (DRWCall *call = shgroup->calls.first; call; call = call->next, i++) { copy_v3_v3(&data[i*3], call->obmat[3]); } /* Upload Data */ static VertexFormat format = { 0 }; static unsigned pos_id; if (format.attrib_ct == 0) { pos_id = add_attrib(&format, "pos", GL_FLOAT, 3, KEEP_FLOAT); } VertexBuffer *vbo = VertexBuffer_create_with_format(&format); VertexBuffer_allocate_data(vbo, nbr); fillAttrib(vbo, pos_id, data); if (shgroup->dyntype == DRW_DYN_POINTS) type = GL_POINTS; else type = GL_LINES; /* TODO make the batch dynamic instead of freeing it every times */ if (shgroup->dyngeom) Batch_discard_all(shgroup->dyngeom); shgroup->dyngeom = Batch_create(type, vbo, NULL); MEM_freeN(data); } static void shgroup_dynamic_batch_instance(DRWShadingGroup *shgroup) { int i = 0; int nbr = BLI_listbase_count(&shgroup->calls); shgroup->instance_count = nbr; if (nbr == 0) { if (shgroup->instance_vbo) { glDeleteBuffers(1, &shgroup->instance_vbo); shgroup->instance_vbo = 0; } return; } /* Gather Data */ float *data = MEM_mallocN(sizeof(float) * 4 * 4 * nbr , "Instance Model Matrix"); for (DRWCall *call = shgroup->calls.first; call; call = call->next, i++) { copy_m4_m4((float (*)[4])&data[i*16], call->obmat); } /* TODO poke mike to add this to gawain */ if (shgroup->instance_vbo) { glDeleteBuffers(1, &shgroup->instance_vbo); shgroup->instance_vbo = 0; } glGenBuffers(1, &shgroup->instance_vbo); glBindBuffer(GL_ARRAY_BUFFER, shgroup->instance_vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 4 * 4 * nbr, data, GL_STATIC_DRAW); MEM_freeN(data); } static void shgroup_dynamic_batch_from_calls(DRWShadingGroup *shgroup) { #ifdef WITH_VIEWPORT_CACHE_TEST if (shgroup->dyngeom) return; #endif if (shgroup->dyntype == DRW_DYN_INSTANCE) { shgroup_dynamic_batch_instance(shgroup); } else { shgroup_dynamic_batch_primitives(shgroup); } } /* ***************************************** PASSES ******************************************/ DRWPass *DRW_pass_create(const char *name, DRWState state) { DRWPass *pass = MEM_callocN(sizeof(DRWPass), name); pass->state = state; BLI_listbase_clear(&pass->shgroups); return pass; } void DRW_pass_free(DRWPass *pass) { for (DRWShadingGroup *shgroup = pass->shgroups.first; shgroup; shgroup = shgroup->next) { DRW_shgroup_free(shgroup); } BLI_freelistN(&pass->shgroups); } /* TODO this is slow we should not have to use this (better store shgroup pointer somewhere) */ DRWShadingGroup *DRW_pass_nth_shgroup_get(DRWPass *pass, int n) { int i = 0; for (DRWShadingGroup *shgroup = pass->shgroups.first; shgroup; shgroup = shgroup->next) { if (i == n) return shgroup; i++; } return NULL; } /* ****************************************** DRAW ******************************************/ void DRW_draw_background(void) { /* Just to make sure */ glDepthMask(GL_TRUE); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); if (UI_GetThemeValue(TH_SHOW_BACK_GRAD)) { /* Gradient background Color */ gpuMatrixBegin3D(); /* TODO: finish 2D API */ glClear(GL_DEPTH_BUFFER_BIT); VertexFormat *format = immVertexFormat(); unsigned pos = add_attrib(format, "pos", COMP_F32, 2, KEEP_FLOAT); unsigned color = add_attrib(format, "color", COMP_U8, 3, NORMALIZE_INT_TO_FLOAT); unsigned char col_hi[3], col_lo[3]; immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR); UI_GetThemeColor3ubv(TH_LOW_GRAD, col_lo); UI_GetThemeColor3ubv(TH_HIGH_GRAD, col_hi); immBegin(GL_QUADS, 4); immAttrib3ubv(color, col_lo); immVertex2f(pos, -1.0f, -1.0f); immVertex2f(pos, 1.0f, -1.0f); immAttrib3ubv(color, col_hi); immVertex2f(pos, 1.0f, 1.0f); immVertex2f(pos, -1.0f, 1.0f); immEnd(); immUnbindProgram(); gpuMatrixEnd(); } else { /* Solid background Color */ UI_ThemeClearColorAlpha(TH_HIGH_GRAD, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } } #ifdef WITH_CLAY_ENGINE /* Only alter the state (does not reset it like set_state() ) */ static void shgroup_set_state(DRWShadingGroup *shgroup) { if (shgroup->state) { /* Blend */ if (shgroup->state & DRW_STATE_BLEND) { glEnable(GL_BLEND); } /* Wire width */ if (shgroup->state & DRW_STATE_WIRE) { glLineWidth(1.0f); } else if (shgroup->state & DRW_STATE_WIRE_LARGE) { glLineWidth(UI_GetThemeValuef(TH_OUTLINE_WIDTH) * 2.0f); } /* Line Stipple */ if (shgroup->state & DRW_STATE_STIPPLE_2) { setlinestyle(2); } else if (shgroup->state & DRW_STATE_STIPPLE_3) { setlinestyle(3); } else if (shgroup->state & DRW_STATE_STIPPLE_4) { setlinestyle(4); } if (shgroup->state & DRW_STATE_POINT) { GPU_enable_program_point_size(); glPointSize(5.0f); } } } typedef struct DRWBoundTexture { struct DRWBoundTexture *next, *prev; GPUTexture *tex; } DRWBoundTexture; static void draw_geometry(DRWShadingGroup *shgroup, DRWInterface *interface, Batch *geom, unsigned int instance_vbo, int instance_count, const float (*obmat)[4]) { RegionView3D *rv3d = CTX_wm_region_view3d(DST.context); float mvp[4][4], mv[4][4], n[3][3]; float eye[3] = { 0.0f, 0.0f, 1.0f }; /* looking into the screen */ bool do_mvp = (interface->modelviewprojection != -1); bool do_mv = (interface->modelview != -1); bool do_n = (interface->normal != -1); bool do_eye = (interface->eye != -1); if (do_mvp) { mul_m4_m4m4(mvp, rv3d->persmat, obmat); } if (do_mv || do_n || do_eye) { mul_m4_m4m4(mv, rv3d->viewmat, obmat); } if (do_n || do_eye) { copy_m3_m4(n, mv); invert_m3(n); transpose_m3(n); } if (do_eye) { /* Used by orthographic wires */ float tmp[3][3]; invert_m3_m3(tmp, n); /* set eye vector, transformed to object coords */ mul_m3_v3(tmp, eye); } /* Should be really simple */ /* step 1 : bind object dependent matrices */ if (interface->modelviewprojection != -1) { GPU_shader_uniform_vector(shgroup->shader, interface->modelviewprojection, 16, 1, (float *)mvp); } if (interface->viewprojection != -1) { GPU_shader_uniform_vector(shgroup->shader, interface->viewprojection, 16, 1, (float *)rv3d->persmat); } if (interface->projection != -1) { GPU_shader_uniform_vector(shgroup->shader, interface->projection, 16, 1, (float *)rv3d->winmat); } if (interface->modelview != -1) { GPU_shader_uniform_vector(shgroup->shader, interface->modelview, 16, 1, (float *)mv); } if (interface->normal != -1) { GPU_shader_uniform_vector(shgroup->shader, interface->normal, 9, 1, (float *)n); } if (interface->eye != -1) { GPU_shader_uniform_vector(shgroup->shader, interface->eye, 3, 1, (float *)eye); } /* step 2 : bind vertex array & draw */ Batch_set_program(geom, GPU_shader_get_program(shgroup->shader)); if (instance_vbo) { Batch_draw_stupid_instanced(geom, instance_vbo, instance_count); } else { Batch_draw_stupid(geom); } } static void draw_shgroup(DRWShadingGroup *shgroup) { BLI_assert(shgroup->shader); BLI_assert(shgroup->interface); DRWInterface *interface = shgroup->interface; if (DST.shader != shgroup->shader) { if (DST.shader) GPU_shader_unbind(); GPU_shader_bind(shgroup->shader); DST.shader = shgroup->shader; } if (shgroup->dyntype != 0) { shgroup_dynamic_batch_from_calls(shgroup); } shgroup_set_state(shgroup); /* Binding Uniform */ /* Don't check anything, Interface should already contain the least uniform as possible */ for (DRWUniform *uni = interface->uniforms.first; uni; uni = uni->next) { DRWBoundTexture *bound_tex; switch (uni->type) { case DRW_UNIFORM_BOOL: case DRW_UNIFORM_INT: GPU_shader_uniform_vector_int(shgroup->shader, uni->location, uni->length, uni->arraysize, (int *)uni->value); break; case DRW_UNIFORM_FLOAT: case DRW_UNIFORM_MAT3: case DRW_UNIFORM_MAT4: GPU_shader_uniform_vector(shgroup->shader, uni->location, uni->length, uni->arraysize, (float *)uni->value); break; case DRW_UNIFORM_TEXTURE: GPU_texture_bind((GPUTexture *)uni->value, uni->bindloc); bound_tex = MEM_callocN(sizeof(DRWBoundTexture), "DRWBoundTexture"); bound_tex->tex = (GPUTexture *)uni->value; BLI_addtail(&DST.bound_texs, bound_tex); GPU_shader_uniform_texture(shgroup->shader, uni->location, (GPUTexture *)uni->value); break; case DRW_UNIFORM_BUFFER: /* restore index from lenght we abused */ GPU_texture_bind(DST.current_txl->textures[uni->length], uni->bindloc); GPU_texture_compare_mode(DST.current_txl->textures[uni->length], false); GPU_texture_filter_mode(DST.current_txl->textures[uni->length], false); bound_tex = MEM_callocN(sizeof(DRWBoundTexture), "DRWBoundTexture"); bound_tex->tex = DST.current_txl->textures[uni->length]; BLI_addtail(&DST.bound_texs, bound_tex); GPU_shader_uniform_texture(shgroup->shader, uni->location, DST.current_txl->textures[uni->length]); break; case DRW_UNIFORM_BLOCK: GPU_uniformbuffer_bind((GPUUniformBuffer *)uni->value, uni->bindloc); GPU_shader_uniform_buffer(shgroup->shader, uni->location, (GPUUniformBuffer *)uni->value); break; } } /* Rendering Calls */ if (shgroup->dyntype != 0) { /* Replacing multiple calls with only one */ float obmat[4][4]; unit_m4(obmat); if (shgroup->dyntype == DRW_DYN_INSTANCE && shgroup->instance_count > 0) { DRWCall *call = shgroup->calls.first; draw_geometry(shgroup, interface, call->geometry, shgroup->instance_vbo, shgroup->instance_count, obmat); } else { /* Some dynamic batch can have no geom (no call to aggregate) */ if (shgroup->dyngeom) { draw_geometry(shgroup, interface, shgroup->dyngeom, 0, 1, obmat); } } } else { for (DRWCall *call = shgroup->calls.first; call; call = call->next) { draw_geometry(shgroup, interface, call->geometry, 0, 1, call->obmat); } } } static void set_state(short flag) { /* TODO Keep track of the state and only revert what is needed */ /* Depth Write */ if (flag & DRW_STATE_WRITE_DEPTH) glDepthMask(GL_TRUE); else glDepthMask(GL_FALSE); /* Color Write */ if (flag & DRW_STATE_WRITE_COLOR) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); else glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); /* Backface Culling */ if (flag & DRW_STATE_CULL_BACK || flag & DRW_STATE_CULL_FRONT) { glEnable(GL_CULL_FACE); if (flag & DRW_STATE_CULL_BACK) glCullFace(GL_BACK); else if (flag & DRW_STATE_CULL_FRONT) glCullFace(GL_FRONT); } else { glDisable(GL_CULL_FACE); } /* Depht Test */ if (flag & DRW_STATE_DEPTH_LESS || flag & DRW_STATE_DEPTH_EQUAL) { glEnable(GL_DEPTH_TEST); if (flag & DRW_STATE_DEPTH_LESS) glDepthFunc(GL_LEQUAL); else if (flag & DRW_STATE_DEPTH_EQUAL) glDepthFunc(GL_EQUAL); } else { glDisable(GL_DEPTH_TEST); } /* Wire Width */ if (flag & DRW_STATE_WIRE) { glLineWidth(1.0f); } else if (flag & DRW_STATE_WIRE_LARGE) { glLineWidth(UI_GetThemeValuef(TH_OUTLINE_WIDTH) * 2.0f); } /* Points Size */ if (flag & DRW_STATE_POINT) { GPU_enable_program_point_size(); glPointSize(5.0f); } else { GPU_disable_program_point_size(); } /* Blending (all buffer) */ if (flag & DRW_STATE_BLEND) { glEnable(GL_BLEND); } else { glDisable(GL_BLEND); } /* Line Stipple */ if (flag & DRW_STATE_STIPPLE_2) { setlinestyle(2); } else if (flag & DRW_STATE_STIPPLE_3) { setlinestyle(3); } else if (flag & DRW_STATE_STIPPLE_4) { setlinestyle(4); } else { setlinestyle(0); } } void DRW_draw_pass(DRWPass *pass) { /* Start fresh */ DST.shader = NULL; DST.tex_bind_id = 0; set_state(pass->state); BLI_listbase_clear(&DST.bound_texs); for (DRWShadingGroup *shgroup = pass->shgroups.first; shgroup; shgroup = shgroup->next) { draw_shgroup(shgroup); } /* Clear Bound textures */ for (DRWBoundTexture *bound_tex = DST.bound_texs.first; bound_tex; bound_tex = bound_tex->next) { GPU_texture_unbind(bound_tex->tex); } DST.tex_bind_id = 0; BLI_freelistN(&DST.bound_texs); if (DST.shader) { GPU_shader_unbind(); DST.shader = NULL; } } /* Reset state to not interfer with other UI drawcall */ void DRW_state_reset(void) { DRWState state = 0; state |= DRW_STATE_WRITE_DEPTH; state |= DRW_STATE_WRITE_COLOR; state |= DRW_STATE_DEPTH_LESS; set_state(state); } #endif /* ****************************************** Settings ******************************************/ void *DRW_material_settings_get(Material *ma, const char *engine_name) { MaterialEngineSettings *ms = NULL; ms = BLI_findstring(&ma->engines_settings, engine_name, offsetof(MaterialEngineSettings, name)); #ifdef WITH_CLAY_ENGINE /* If the settings does not exists yet, create it */ if (ms == NULL) { ms = MEM_callocN(sizeof(RenderEngineSettings), "RenderEngineSettings"); BLI_strncpy(ms->name, engine_name, 32); /* TODO make render_settings_create a polymorphic function */ if (STREQ(engine_name, RE_engine_id_BLENDER_CLAY)) { ms->data = CLAY_material_settings_create(); } else { /* No engine matched */ BLI_assert(false); } BLI_addtail(&ma->engines_settings, ms); } #else return NULL; #endif return ms->data; } /* If scene is NULL, use context scene */ void *DRW_render_settings_get(Scene *scene, const char *engine_name) { RenderEngineSettings *rs = NULL; if (scene == NULL) scene = CTX_data_scene(DST.context); rs = BLI_findstring(&scene->engines_settings, engine_name, offsetof(RenderEngineSettings, name)); #ifdef WITH_CLAY_ENGINE /* If the settings does not exists yet, create it */ if (rs == NULL) { rs = MEM_callocN(sizeof(RenderEngineSettings), "RenderEngineSettings"); BLI_strncpy(rs->name, engine_name, 32); /* TODO make render_settings_create a polymorphic function */ if (STREQ(engine_name, RE_engine_id_BLENDER_CLAY)) { rs->data = CLAY_render_settings_create(); } else { /* No engine matched */ BLI_assert(false); } BLI_addtail(&scene->engines_settings, rs); } #else return NULL; #endif return rs->data; } /* ****************************************** Framebuffers ******************************************/ void DRW_framebuffer_init(struct GPUFrameBuffer **fb, int width, int height, DRWFboTexture textures[MAX_FBO_TEX], int texnbr) { if (!*fb) { int color_attachment = -1; *fb = GPU_framebuffer_create(); for (int i = 0; i < texnbr; ++i) { DRWFboTexture fbotex = textures[i]; if (!*fbotex.tex) { /* TODO refine to opengl formats */ if (fbotex.format == DRW_BUF_DEPTH_16 || fbotex.format == DRW_BUF_DEPTH_24) { *fbotex.tex = GPU_texture_create_depth(width, height, NULL); GPU_texture_compare_mode(*fbotex.tex, false); GPU_texture_filter_mode(*fbotex.tex, false); } else { *fbotex.tex = GPU_texture_create_2D(width, height, NULL, NULL); ++color_attachment; } } GPU_framebuffer_texture_attach(*fb, *fbotex.tex, color_attachment); } if (!GPU_framebuffer_check_valid(*fb, NULL)) { printf("Error invalid framebuffer\n"); } GPU_framebuffer_bind(DST.default_framebuffer); } } void DRW_framebuffer_bind(struct GPUFrameBuffer *fb) { GPU_framebuffer_bind(fb); } void DRW_framebuffer_texture_attach(struct GPUFrameBuffer *fb, GPUTexture *tex, int slot) { GPU_framebuffer_texture_attach(fb, tex, slot); } void DRW_framebuffer_texture_detach(GPUTexture *tex) { GPU_framebuffer_texture_detach(tex); } /* ****************************************** Viewport ******************************************/ float *DRW_viewport_size_get(void) { return &DST.size[0]; } float *DRW_viewport_screenvecs_get(void) { return &DST.screenvecs[0][0]; } float *DRW_viewport_pixelsize_get(void) { return &DST.pixsize; } void DRW_viewport_init(const bContext *C, void **buffers, void **textures, void **passes) { RegionView3D *rv3d = CTX_wm_region_view3d(C); GPUViewport *viewport = rv3d->viewport; GPU_viewport_get_engine_data(viewport, buffers, textures, passes); /* Refresh DST.size */ DefaultTextureList *txl = (DefaultTextureList *)*textures; DST.size[0] = (float)GPU_texture_width(txl->color); DST.size[1] = (float)GPU_texture_height(txl->color); DefaultFramebufferList *fbl = (DefaultFramebufferList *)*buffers; DST.default_framebuffer = fbl->default_fb; DST.current_txl = (TextureList *)*textures; DST.current_fbl = (FramebufferList *)*buffers; DST.current_psl = (PassList *)*passes; /* Refresh DST.screenvecs */ copy_v3_v3(DST.screenvecs[0], rv3d->viewinv[0]); copy_v3_v3(DST.screenvecs[1], rv3d->viewinv[1]); normalize_v3(DST.screenvecs[0]); normalize_v3(DST.screenvecs[1]); /* Refresh DST.pixelsize */ DST.pixsize = rv3d->pixsize; /* Save context for all later needs */ DST.context = C; } void DRW_viewport_matrix_get(float mat[4][4], DRWViewportMatrixType type) { RegionView3D *rv3d = CTX_wm_region_view3d(DST.context); if (type == DRW_MAT_PERS) copy_m4_m4(mat, rv3d->persmat); else if (type == DRW_MAT_WIEW) copy_m4_m4(mat, rv3d->viewmat); else if (type == DRW_MAT_WIN) copy_m4_m4(mat, rv3d->winmat); } bool DRW_viewport_is_persp_get(void) { RegionView3D *rv3d = CTX_wm_region_view3d(DST.context); return rv3d->is_persp; } bool DRW_viewport_cache_is_dirty(void) { /* TODO Use a dirty flag */ return (DST.current_psl->passes[0] == NULL); } /* ****************************************** INIT ******************************************/ void DRW_engines_init(void) { #ifdef WITH_CLAY_ENGINE RE_engines_register(NULL, &viewport_clay_type); #endif } void DRW_engines_free(void) { #ifdef WITH_CLAY_ENGINE clay_engine_free(); DRW_shape_cache_free(); BLI_remlink(&R_engines, &viewport_clay_type); #endif }