1226 lines
50 KiB
C
1226 lines
50 KiB
C
|
|
#include "../global.h"
|
|
|
|
static string_array_t *render_path_paint_additional(char *nor, char *pack) {
|
|
static string_array_t ar = {0};
|
|
ar.length = 0;
|
|
string_array_push(&ar, nor);
|
|
string_array_push(&ar, pack);
|
|
string_array_push(&ar, "texpaint_blend0");
|
|
return &ar;
|
|
}
|
|
|
|
static bool render_path_paint_uses_gbuffer0() {
|
|
if (g_context->xray || g_config->brush_angle_reject || context_is_decal()) {
|
|
return true;
|
|
}
|
|
if (g_context->brush_mask_image != NULL && (g_context->tool == TOOL_TYPE_BRUSH || g_context->tool == TOOL_TYPE_ERASER)) {
|
|
return true;
|
|
}
|
|
if (g_context->tool == TOOL_TYPE_FILL && g_context->fill_type == FILL_TYPE_ANGLE) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool render_path_paint_dilated = true;
|
|
mesh_object_t *render_path_paint_painto = NULL;
|
|
mesh_object_t *render_path_paint_planeo = NULL;
|
|
u8_array_t *render_path_paint_visibles = NULL;
|
|
bool render_path_paint_merged_object_visible = false;
|
|
f32 render_path_paint_saved_fov = 0.0;
|
|
render_target_t *_render_path_paint_texpaint;
|
|
render_target_t *_render_path_paint_texpaint_nor;
|
|
render_target_t *_render_path_paint_texpaint_pack;
|
|
render_target_t *_render_path_paint_texpaint_undo;
|
|
render_target_t *_render_path_paint_texpaint_nor_undo;
|
|
render_target_t *_render_path_paint_texpaint_pack_undo;
|
|
bake_type_t _render_path_paint_bake_type;
|
|
|
|
void render_path_paint_init() {
|
|
|
|
{
|
|
render_target_t *t = render_target_create();
|
|
t->name = "texpaint_blend0";
|
|
t->width = config_get_texture_res_x();
|
|
t->height = config_get_texture_res_y();
|
|
t->format = "R8";
|
|
render_path_create_render_target(t);
|
|
}
|
|
{
|
|
render_target_t *t = render_target_create();
|
|
t->name = "texpaint_blend1";
|
|
t->width = config_get_texture_res_x();
|
|
t->height = config_get_texture_res_y();
|
|
t->format = "R8";
|
|
render_path_create_render_target(t);
|
|
}
|
|
{
|
|
render_target_t *t = render_target_create();
|
|
t->name = "texpaint_colorid";
|
|
t->width = 1;
|
|
t->height = 1;
|
|
t->format = "RGBA32";
|
|
render_path_create_render_target(t);
|
|
}
|
|
{
|
|
render_target_t *t = render_target_create();
|
|
t->name = "texpaint_picker";
|
|
t->width = 1;
|
|
t->height = 1;
|
|
t->format = "RGBA32";
|
|
render_path_create_render_target(t);
|
|
}
|
|
{
|
|
render_target_t *t = render_target_create();
|
|
t->name = "texpaint_nor_picker";
|
|
t->width = 1;
|
|
t->height = 1;
|
|
t->format = "RGBA32";
|
|
render_path_create_render_target(t);
|
|
}
|
|
{
|
|
render_target_t *t = render_target_create();
|
|
t->name = "texpaint_pack_picker";
|
|
t->width = 1;
|
|
t->height = 1;
|
|
t->format = "RGBA32";
|
|
render_path_create_render_target(t);
|
|
}
|
|
{
|
|
render_target_t *t = render_target_create();
|
|
t->name = "texpaint_uv_picker";
|
|
t->width = 1;
|
|
t->height = 1;
|
|
t->format = "RGBA32";
|
|
render_path_create_render_target(t);
|
|
}
|
|
{
|
|
render_target_t *t = render_target_create();
|
|
t->name = "texpaint_posnortex_picker0";
|
|
t->width = 1;
|
|
t->height = 1;
|
|
t->format = "RGBA128";
|
|
render_path_create_render_target(t);
|
|
}
|
|
{
|
|
render_target_t *t = render_target_create();
|
|
t->name = "texpaint_posnortex_picker1";
|
|
t->width = 1;
|
|
t->height = 1;
|
|
t->format = "RGBA128";
|
|
render_path_create_render_target(t);
|
|
}
|
|
|
|
render_path_load_shader("Scene/copy_mrt3_pass/copy_mrt3_pass");
|
|
render_path_load_shader("Scene/copy_mrt3_pass/copy_mrt3RGBA64_pass");
|
|
render_path_load_shader("Scene/copy_mrt3_pass/copy_mrt3RGBA128_pass");
|
|
render_path_load_shader("Scene/dilate_pass/dilate_pass");
|
|
render_path_load_shader("Scene/dilate_pass/dilateRGBA64_pass");
|
|
render_path_load_shader("Scene/dilate_pass/dilateRGBA128_pass");
|
|
}
|
|
|
|
void render_path_paint_draw_fullscreen_triangle(char *ctx) {
|
|
// Vertices are mangled in vertex shader to form a fullscreen triangle,
|
|
// so plane transform does not matter
|
|
mesh_object_t *plane = scene_get_child(".Plane")->ext;
|
|
bool _visible = plane->base->visible;
|
|
plane->base->visible = true;
|
|
mesh_object_render(plane, ctx, _render_path_bind_params);
|
|
plane->base->visible = _visible;
|
|
render_path_end();
|
|
}
|
|
|
|
void render_path_paint_commands_particle(i32 tid, char *texpaint, bool is_mask) {
|
|
// Hide bullets to avoid self-painting
|
|
for (i32 _pi = 0; _pi < 32; ++_pi) {
|
|
if (g_context->particles[_pi].bullet != NULL) {
|
|
g_context->particles[_pi].bullet->visible = false;
|
|
}
|
|
}
|
|
// Paint pass per particle
|
|
for (i32 _pi = 0; _pi < 32; ++_pi) {
|
|
if (g_context->particles[_pi].timer == NULL || g_context->particles[_pi].hit_x == 0) {
|
|
continue;
|
|
}
|
|
g_context->particle_index = _pi;
|
|
g_context->particle_hit_x = g_context->particles[_pi].hit_x;
|
|
g_context->particle_hit_y = g_context->particles[_pi].hit_y;
|
|
g_context->particle_hit_z = g_context->particles[_pi].hit_z;
|
|
g_context->last_particle_hit_x = g_context->particles[_pi].hit_last_x;
|
|
g_context->last_particle_hit_y = g_context->particles[_pi].hit_last_y;
|
|
g_context->last_particle_hit_z = g_context->particles[_pi].hit_last_z;
|
|
if (is_mask) {
|
|
tid = g_context->layer->parent->id;
|
|
if (slot_layer_is_group(g_context->layer->parent)) {
|
|
for (i32 i = 0; i < slot_layer_get_children(g_context->layer->parent)->length; ++i) {
|
|
slot_layer_t *c = slot_layer_get_children(g_context->layer->parent)->buffer[i];
|
|
tid = c->id;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
render_path_set_target(texpaint, render_path_paint_additional(string_tmp("texpaint_nor%d", tid), string_tmp("texpaint_pack%d", tid)), NULL,
|
|
GPU_CLEAR_NONE, 0, 0.0);
|
|
render_path_bind_target("main", "gbufferD");
|
|
if (render_path_paint_uses_gbuffer0()) {
|
|
render_path_bind_target("gbuffer0", "gbuffer0");
|
|
}
|
|
render_path_bind_target("texpaint_blend1", "paintmask");
|
|
if (g_context->colorid_picked) {
|
|
render_path_bind_target("texpaint_colorid", "texpaint_colorid");
|
|
}
|
|
render_path_draw_meshes("paint");
|
|
}
|
|
for (i32 _pi = 0; _pi < 32; ++_pi) {
|
|
if (g_context->particles[_pi].bullet != NULL) {
|
|
g_context->particles[_pi].bullet->visible = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void render_path_paint_ref_target(char *name, char *format) {
|
|
render_target_t *rt = any_map_get(render_path_render_targets, name);
|
|
if (rt != NULL && (rt->width != config_get_texture_res_x() || rt->height != config_get_texture_res_y() || !string_equals(rt->format, format))) {
|
|
gpu_delete_texture(rt->_image);
|
|
map_delete(render_path_render_targets, name);
|
|
rt = NULL;
|
|
}
|
|
if (rt == NULL) {
|
|
render_target_t *t = render_target_create();
|
|
t->name = name;
|
|
t->width = config_get_texture_res_x();
|
|
t->height = config_get_texture_res_y();
|
|
t->format = format;
|
|
render_path_create_render_target(t);
|
|
}
|
|
}
|
|
|
|
static void render_path_paint_ref_snapshot(i32 tid, bool is_mask) {
|
|
if (is_mask) {
|
|
render_path_paint_ref_target("texpaint_ref", "RGBA32");
|
|
render_path_set_target("texpaint_ref", NULL, NULL, GPU_CLEAR_NONE, 0, 0.0);
|
|
render_path_bind_target(string_tmp("texpaint%d", tid), "tex");
|
|
render_path_draw_shader("Scene/copy_pass/copy_pass");
|
|
return;
|
|
}
|
|
char *format = base_bits_handle->i == TEXTURE_BITS_BITS8 ? "RGBA32" : base_bits_handle->i == TEXTURE_BITS_BITS16 ? "RGBA64" : "RGBA128";
|
|
render_path_paint_ref_target("texpaint_ref", format);
|
|
render_path_paint_ref_target("texpaint_nor_ref", format);
|
|
render_path_paint_ref_target("texpaint_pack_ref", format);
|
|
string_array_t *additional = any_array_create_from_raw_tmp(
|
|
(void *[]){
|
|
"texpaint_nor_ref",
|
|
"texpaint_pack_ref",
|
|
},
|
|
2);
|
|
render_path_set_target("texpaint_ref", additional, NULL, GPU_CLEAR_NONE, 0, 0.0);
|
|
render_path_bind_target(string_tmp("texpaint%d", tid), "tex0");
|
|
render_path_bind_target(string_tmp("texpaint_nor%d", tid), "tex1");
|
|
render_path_bind_target(string_tmp("texpaint_pack%d", tid), "tex2");
|
|
char *pipe = string_equals(format, "RGBA32") ? "copy_mrt3_pass" : string_equals(format, "RGBA64") ? "copy_mrt3RGBA64_pass" : "copy_mrt3RGBA128_pass";
|
|
render_path_draw_shader(string_tmp("Scene/copy_mrt3_pass/%s", pipe));
|
|
}
|
|
|
|
void render_path_paint_commands_paint(bool dilation) {
|
|
i32 tid = g_context->layer->id;
|
|
|
|
bool picking_tool = g_context->tool == TOOL_TYPE_COLORID || g_context->tool == TOOL_TYPE_PICKER || g_context->tool == TOOL_TYPE_MATERIAL ||
|
|
g_context->tool == TOOL_TYPE_CURSOR;
|
|
if (g_context->layer->texpaint_sculpt != NULL && !picking_tool) {
|
|
render_path_sculpt_commands();
|
|
return;
|
|
}
|
|
|
|
if (g_context->pdirty <= 0) {
|
|
return;
|
|
}
|
|
|
|
if (g_context->tool == TOOL_TYPE_COLORID) {
|
|
render_path_set_target("texpaint_colorid", NULL, NULL, GPU_CLEAR_COLOR, 0xff000000, 0.0);
|
|
render_path_bind_target("gbuffer2", "gbuffer2");
|
|
render_path_paint_draw_fullscreen_triangle("paint");
|
|
ui_header_handle->redraws = 2;
|
|
}
|
|
else if (g_context->tool == TOOL_TYPE_PICKER || g_context->tool == TOOL_TYPE_MATERIAL) {
|
|
if (g_context->pick_pos_nor_tex) {
|
|
if (g_context->paint2d) {
|
|
string_array_t *additional = any_array_create_from_raw(
|
|
(void *[]){
|
|
"gbuffer1",
|
|
"gbuffer2",
|
|
},
|
|
2);
|
|
render_path_set_target("gbuffer0", additional, "main", GPU_CLEAR_NONE, 0, 0.0);
|
|
render_path_draw_meshes("mesh");
|
|
}
|
|
string_array_t *additional = any_array_create_from_raw(
|
|
(void *[]){
|
|
"texpaint_posnortex_picker1",
|
|
},
|
|
1);
|
|
render_path_set_target("texpaint_posnortex_picker0", additional, NULL, GPU_CLEAR_NONE, 0, 0.0);
|
|
render_path_bind_target("gbuffer2", "gbuffer2");
|
|
render_path_bind_target("main", "gbufferD");
|
|
render_path_draw_meshes("paint");
|
|
render_target_t *picker0 = any_map_get(render_path_render_targets, "texpaint_posnortex_picker0");
|
|
render_target_t *picker1 = any_map_get(render_path_render_targets, "texpaint_posnortex_picker1");
|
|
gpu_texture_t *texpaint_posnortex_picker0 = picker0->_image;
|
|
gpu_texture_t *texpaint_posnortex_picker1 = picker1->_image;
|
|
buffer_t *a = gpu_get_texture_pixels(texpaint_posnortex_picker0);
|
|
buffer_t *b = gpu_get_texture_pixels(texpaint_posnortex_picker1);
|
|
g_context->posx_picked = buffer_get_f32(a, 0);
|
|
g_context->posy_picked = buffer_get_f32(a, 4);
|
|
g_context->posz_picked = buffer_get_f32(a, 8);
|
|
g_context->uvx_picked = buffer_get_f32(a, 12);
|
|
g_context->norx_picked = buffer_get_f32(b, 0);
|
|
g_context->nory_picked = buffer_get_f32(b, 4);
|
|
g_context->norz_picked = buffer_get_f32(b, 8);
|
|
g_context->uvy_picked = buffer_get_f32(b, 12);
|
|
}
|
|
else {
|
|
tid = g_context->layer->id;
|
|
bool is_mask = slot_layer_is_mask(g_context->layer);
|
|
if (is_mask) {
|
|
string_array_t *additional = any_array_create_from_raw(
|
|
(void *[]){
|
|
"texpaint_uv_picker",
|
|
},
|
|
1);
|
|
render_path_set_target("texpaint_picker", additional, NULL, GPU_CLEAR_NONE, 0, 0.0);
|
|
render_path_bind_target("gbuffer2", "gbuffer2");
|
|
render_path_bind_target(string_tmp("texpaint%d", tid), "texpaint");
|
|
render_path_paint_draw_fullscreen_triangle("paint");
|
|
}
|
|
else {
|
|
string_array_t *additional = any_array_create_from_raw(
|
|
(void *[]){
|
|
"texpaint_nor_picker",
|
|
"texpaint_pack_picker",
|
|
"texpaint_uv_picker",
|
|
},
|
|
3);
|
|
render_path_set_target("texpaint_picker", additional, NULL, GPU_CLEAR_NONE, 0, 0.0);
|
|
render_path_bind_target("gbuffer2", "gbuffer2");
|
|
bool use_live_layer = g_context->tool == TOOL_TYPE_MATERIAL;
|
|
if (use_live_layer) {
|
|
render_path_paint_use_live_layer(true);
|
|
}
|
|
render_path_bind_target(string_tmp("texpaint%d", tid), "texpaint");
|
|
render_path_bind_target(string_tmp("texpaint_nor%d", tid), "texpaint_nor");
|
|
render_path_bind_target(string_tmp("texpaint_pack%d", tid), "texpaint_pack");
|
|
render_path_paint_draw_fullscreen_triangle("paint");
|
|
if (use_live_layer) {
|
|
render_path_paint_use_live_layer(false);
|
|
}
|
|
}
|
|
ui_header_handle->redraws = 2;
|
|
ui_base_hwnds->buffer[2]->redraws = 2;
|
|
|
|
render_target_t *texpaint_picker = any_map_get(render_path_render_targets, "texpaint_picker");
|
|
render_target_t *texpaint_uv_picker = any_map_get(render_path_render_targets, "texpaint_uv_picker");
|
|
buffer_t *a = gpu_get_texture_pixels(texpaint_picker->_image);
|
|
buffer_t *d = gpu_get_texture_pixels(texpaint_uv_picker->_image);
|
|
|
|
if (g_context->color_picker_callback != NULL) {
|
|
g_context->color_picker_callback(g_context->picked_color);
|
|
}
|
|
|
|
// Picked surface values
|
|
#ifdef IRON_BGRA
|
|
i32 i0 = 2;
|
|
i32 i1 = 1;
|
|
i32 i2 = 0;
|
|
#else
|
|
i32 i0 = 0;
|
|
i32 i1 = 1;
|
|
i32 i2 = 2;
|
|
#endif
|
|
i32 i3 = 3;
|
|
g_context->picked_color->base = color_set_rb(g_context->picked_color->base, buffer_get_u8(a, i0));
|
|
g_context->picked_color->base = color_set_gb(g_context->picked_color->base, buffer_get_u8(a, i1));
|
|
g_context->picked_color->base = color_set_bb(g_context->picked_color->base, buffer_get_u8(a, i2));
|
|
g_context->picked_color->opacity = buffer_get_u8(a, i3) / 255.0;
|
|
g_context->uvx_picked = buffer_get_u8(d, i0) / 255.0;
|
|
g_context->uvy_picked = buffer_get_u8(d, i1) / 255.0;
|
|
|
|
if (!is_mask) {
|
|
render_target_t *texpaint_nor_picker = any_map_get(render_path_render_targets, "texpaint_nor_picker");
|
|
render_target_t *texpaint_pack_picker = any_map_get(render_path_render_targets, "texpaint_pack_picker");
|
|
buffer_t *b = gpu_get_texture_pixels(texpaint_nor_picker->_image);
|
|
buffer_t *c = gpu_get_texture_pixels(texpaint_pack_picker->_image);
|
|
g_context->picked_color->normal = color_set_rb(g_context->picked_color->normal, buffer_get_u8(b, i0));
|
|
g_context->picked_color->normal = color_set_gb(g_context->picked_color->normal, buffer_get_u8(b, i1));
|
|
g_context->picked_color->normal = color_set_bb(g_context->picked_color->normal, buffer_get_u8(b, i2));
|
|
g_context->picked_color->occlusion = buffer_get_u8(c, i0) / 255.0;
|
|
g_context->picked_color->roughness = buffer_get_u8(c, i1) / 255.0;
|
|
g_context->picked_color->metallic = buffer_get_u8(c, i2) / 255.0;
|
|
g_context->picked_color->height = buffer_get_u8(c, i3) / 255.0;
|
|
|
|
// Pick material
|
|
if (g_context->color_picker_callback == NULL) {
|
|
// matid % 3 == 0 - normal, 1 - emission, 2 - subsurface
|
|
i32 id = buffer_get_u8(b, 3);
|
|
i32 matid = math_floor((id - (id % 3)) / 3.0);
|
|
for (i32 i = 0; i < g_project->_->materials->length; ++i) {
|
|
slot_material_t *m = g_project->_->materials->buffer[i];
|
|
if (m->id == matid) {
|
|
if (g_context->picker_select_material) {
|
|
context_set_material(m);
|
|
}
|
|
g_context->materialid_picked = matid;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update fill layers which are using materials containing a PICKER node
|
|
slot_material_t *_material = g_context->material;
|
|
for (i32 i = 0; i < g_project->_->materials->length; ++i) {
|
|
slot_material_t *m = g_project->_->materials->buffer[i];
|
|
bool has_picker = false;
|
|
for (i32 j = 0; j < m->canvas->nodes->length; ++j) {
|
|
ui_node_t *node = m->canvas->nodes->buffer[j];
|
|
if (string_equals(node->type, "PICKER")) {
|
|
has_picker = true;
|
|
break;
|
|
}
|
|
}
|
|
if (has_picker) {
|
|
g_context->material = m;
|
|
layers_update_fill_layers();
|
|
util_render_make_material_preview();
|
|
ui_base_hwnds->buffer[TAB_AREA_SIDEBAR1]->redraws = 2;
|
|
}
|
|
}
|
|
g_context->material = _material;
|
|
}
|
|
}
|
|
else if (g_context->tool == TOOL_TYPE_CURSOR) {
|
|
if (g_context->pick_object_id) {
|
|
viewport_mode_t _viewport_mode = g_context->viewport_mode;
|
|
g_context->viewport_mode = VIEWPORT_MODE_OBJECT_ID;
|
|
make_material_parse_mesh_material();
|
|
|
|
// Objects with assigned material do not write object id
|
|
shader_data_t *base_material = g_project->_->materials->buffer[0]->data;
|
|
any_array_t *_object_materials = any_array_create(g_project->_->paint_objects->length);
|
|
for (i32 i = 0; i < g_project->_->paint_objects->length; ++i) {
|
|
mesh_object_t *o = g_project->_->paint_objects->buffer[i];
|
|
_object_materials->buffer[i] = o->material;
|
|
o->material = base_material;
|
|
}
|
|
|
|
render_path_base_draw_gbuffer();
|
|
|
|
for (i32 i = 0; i < g_project->_->paint_objects->length; ++i) {
|
|
g_project->_->paint_objects->buffer[i]->material = _object_materials->buffer[i];
|
|
}
|
|
|
|
g_context->viewport_mode = _viewport_mode;
|
|
make_material_parse_mesh_material();
|
|
|
|
g_context->pdirty = 1;
|
|
make_material_parse_paint_material(false);
|
|
render_path_set_target("texpaint_picker", NULL, NULL, GPU_CLEAR_NONE, 0, 0.0);
|
|
render_path_bind_target("gbuffer1", "gbuffer1");
|
|
render_path_bind_target("gbuffer2", "gbuffer2");
|
|
render_path_paint_draw_fullscreen_triangle("paint");
|
|
|
|
render_target_t *rt = any_map_get(render_path_render_targets, "texpaint_picker");
|
|
buffer_t *pixel = gpu_get_texture_pixels(rt->_image);
|
|
if (buffer_get_u8(pixel, 3) > 0) {
|
|
#ifdef IRON_BGRA
|
|
i32 r_byte = buffer_get_u8(pixel, 2);
|
|
#else
|
|
i32 r_byte = buffer_get_u8(pixel, 0);
|
|
#endif
|
|
i32 index = r_byte - 1;
|
|
if (index >= 0 && index < g_project->_->paint_objects->length) {
|
|
g_context->paint_object = g_project->_->paint_objects->buffer[index];
|
|
ui_header_handle->redraws = 2;
|
|
|
|
// g_context->layer->object_mask = index + 1;
|
|
// context_set_layer(g_context->layer);
|
|
// make_material_parse_mesh_material();
|
|
// layers_set_object_mask();
|
|
|
|
base_redraw_status();
|
|
}
|
|
}
|
|
|
|
g_context->pick_object_id = false;
|
|
make_material_parse_paint_material(false);
|
|
render_path_base_draw_gbuffer();
|
|
}
|
|
}
|
|
else {
|
|
char *texpaint = string_tmp("texpaint%d", tid);
|
|
if (g_context->tool == TOOL_TYPE_BAKE && g_context->brush_time == sys_delta()) {
|
|
// Clear to black on bake start
|
|
render_path_set_target(texpaint, NULL, NULL, GPU_CLEAR_COLOR, 0xff000000, 0.0);
|
|
}
|
|
|
|
render_path_set_target("texpaint_blend1", NULL, NULL, GPU_CLEAR_NONE, 0, 0.0);
|
|
render_path_bind_target("texpaint_blend0", "tex");
|
|
render_path_draw_shader("Scene/copy_pass/copyR8_pass");
|
|
bool is_mask = slot_layer_is_mask(g_context->layer);
|
|
|
|
// Read texcoords from gbuffer
|
|
bool read_tc = (g_context->tool == TOOL_TYPE_FILL && g_context->fill_type == FILL_TYPE_FACE) || g_context->tool == TOOL_TYPE_CLONE ||
|
|
g_context->tool == TOOL_TYPE_BLUR;
|
|
|
|
if (g_context->tool == TOOL_TYPE_BLUR) {
|
|
render_path_paint_ref_snapshot(tid, is_mask);
|
|
}
|
|
|
|
if (g_context->tool == TOOL_TYPE_PARTICLE) {
|
|
render_path_paint_commands_particle(tid, texpaint, is_mask);
|
|
}
|
|
else {
|
|
if (is_mask) {
|
|
i32 ptid = g_context->layer->parent->id;
|
|
if (slot_layer_is_group(g_context->layer->parent)) { // Group mask
|
|
for (i32 i = 0; i < slot_layer_get_children(g_context->layer->parent)->length; ++i) {
|
|
slot_layer_t *c = slot_layer_get_children(g_context->layer->parent)->buffer[i];
|
|
ptid = c->id;
|
|
break;
|
|
}
|
|
}
|
|
render_path_set_target(texpaint, render_path_paint_additional(string_tmp("texpaint_nor%d", ptid), string_tmp("texpaint_pack%d", ptid)), NULL,
|
|
GPU_CLEAR_NONE, 0, 0.0);
|
|
}
|
|
else {
|
|
render_path_set_target(texpaint, render_path_paint_additional(string_tmp("texpaint_nor%d", tid), string_tmp("texpaint_pack%d", tid)), NULL,
|
|
GPU_CLEAR_NONE, 0, 0.0);
|
|
}
|
|
render_path_bind_target("main", "gbufferD");
|
|
if (render_path_paint_uses_gbuffer0()) {
|
|
render_path_bind_target("gbuffer0", "gbuffer0");
|
|
}
|
|
render_path_bind_target("texpaint_blend1", "paintmask");
|
|
if (g_context->colorid_picked) {
|
|
render_path_bind_target("texpaint_colorid", "texpaint_colorid");
|
|
}
|
|
if (read_tc) {
|
|
render_path_bind_target("gbuffer2", "gbuffer2");
|
|
}
|
|
i32 sculpt_layer_count = 0;
|
|
for (i32 i = 0; i < g_project->_->layers->length; ++i) {
|
|
slot_layer_t *sl = g_project->_->layers->buffer[i];
|
|
if (sl->texpaint_sculpt != NULL && slot_layer_is_visible(sl)) {
|
|
render_path_bind_target(string_tmp("texpaint_sculpt%d", sl->id), string_tmp("texpaint_sculpt%d", sl->id));
|
|
sculpt_layer_count++;
|
|
}
|
|
}
|
|
if (sculpt_layer_count > 1) {
|
|
render_path_bind_target("texpaint_sculpt_base", "texpaint_sculpt_base");
|
|
}
|
|
object_t *bullet = scene_get_child(".Bullet");
|
|
bool bullet_vis = bullet != NULL && bullet->visible;
|
|
if (bullet != NULL) {
|
|
bullet->visible = false;
|
|
}
|
|
render_path_draw_meshes("paint");
|
|
if (bullet != NULL) {
|
|
bullet->visible = bullet_vis;
|
|
}
|
|
}
|
|
|
|
if (g_context->tool == TOOL_TYPE_BAKE && g_context->bake_type == BAKE_TYPE_CURVATURE && g_context->bake_curv_smooth > 0) {
|
|
if (any_map_get(render_path_render_targets, "texpaint_blur") == NULL) {
|
|
render_target_t *t = render_target_create();
|
|
t->name = "texpaint_blur";
|
|
t->width = math_floor(config_get_texture_res_x() * 0.95);
|
|
t->height = math_floor(config_get_texture_res_y() * 0.95);
|
|
t->format = "RGBA32";
|
|
render_path_create_render_target(t);
|
|
}
|
|
i32 blurs = math_round(g_context->bake_curv_smooth);
|
|
for (i32 i = 0; i < blurs; ++i) {
|
|
render_path_set_target("texpaint_blur", NULL, NULL, GPU_CLEAR_NONE, 0, 0.0);
|
|
render_path_bind_target(texpaint, "tex");
|
|
render_path_draw_shader("Scene/copy_pass/copy_pass");
|
|
render_path_set_target(texpaint, NULL, NULL, GPU_CLEAR_NONE, 0, 0.0);
|
|
render_path_bind_target("texpaint_blur", "tex");
|
|
render_path_draw_shader("Scene/copy_pass/copy_pass");
|
|
}
|
|
}
|
|
|
|
if (dilation) {
|
|
render_path_paint_dilate(true, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void render_path_paint_use_live_layer(bool use) {
|
|
i32 tid = g_context->layer->id;
|
|
i32 hid = history_undo_i - 1 < 0 ? g_config->undo_steps - 1 : history_undo_i - 1;
|
|
if (use) {
|
|
_render_path_paint_texpaint = any_map_get(render_path_render_targets, string_tmp("texpaint%d", tid));
|
|
_render_path_paint_texpaint_undo = any_map_get(render_path_render_targets, string_tmp("texpaint_undo%d", hid));
|
|
_render_path_paint_texpaint_nor_undo = any_map_get(render_path_render_targets, string_tmp("texpaint_nor_undo%d", hid));
|
|
_render_path_paint_texpaint_pack_undo = any_map_get(render_path_render_targets, string_tmp("texpaint_pack_undo%d", hid));
|
|
_render_path_paint_texpaint_nor = any_map_get(render_path_render_targets, string_tmp("texpaint_nor%d", tid));
|
|
_render_path_paint_texpaint_pack = any_map_get(render_path_render_targets, string_tmp("texpaint_pack%d", tid));
|
|
any_map_set(render_path_render_targets, string("texpaint_undo%d", hid), any_map_get(render_path_render_targets, string_tmp("texpaint%d", tid)));
|
|
any_map_set(render_path_render_targets, string("texpaint%d", tid), any_map_get(render_path_render_targets, "texpaint_live"));
|
|
if (slot_layer_is_layer(g_context->layer)) {
|
|
any_map_set(render_path_render_targets, string("texpaint_nor_undo%d", hid),
|
|
any_map_get(render_path_render_targets, string_tmp("texpaint_nor%d", tid)));
|
|
any_map_set(render_path_render_targets, string("texpaint_pack_undo%d", hid),
|
|
any_map_get(render_path_render_targets, string_tmp("texpaint_pack%d", tid)));
|
|
any_map_set(render_path_render_targets, string("texpaint_nor%d", tid), any_map_get(render_path_render_targets, "texpaint_nor_live"));
|
|
any_map_set(render_path_render_targets, string("texpaint_pack%d", tid), any_map_get(render_path_render_targets, "texpaint_pack_live"));
|
|
}
|
|
}
|
|
else {
|
|
any_map_set(render_path_render_targets, string("texpaint%d", tid), _render_path_paint_texpaint);
|
|
any_map_set(render_path_render_targets, string("texpaint_undo%d", hid), _render_path_paint_texpaint_undo);
|
|
if (slot_layer_is_layer(g_context->layer)) {
|
|
any_map_set(render_path_render_targets, string("texpaint_nor_undo%d", hid), _render_path_paint_texpaint_nor_undo);
|
|
any_map_set(render_path_render_targets, string("texpaint_pack_undo%d", hid), _render_path_paint_texpaint_pack_undo);
|
|
any_map_set(render_path_render_targets, string("texpaint_nor%d", tid), _render_path_paint_texpaint_nor);
|
|
any_map_set(render_path_render_targets, string("texpaint_pack%d", tid), _render_path_paint_texpaint_pack);
|
|
}
|
|
}
|
|
render_path_paint_live_layer_locked = use;
|
|
}
|
|
|
|
void render_path_paint_commands_symmetry() {
|
|
if (g_context->sym_x || g_context->sym_y || g_context->sym_z) {
|
|
g_context->ddirty = 2;
|
|
transform_t *t = g_context->paint_object->base->transform;
|
|
f32 sx = t->scale.x;
|
|
f32 sy = t->scale.y;
|
|
f32 sz = t->scale.z;
|
|
if (g_context->sym_x) {
|
|
t->scale = (vec4_t){-sx, sy, sz, 1.0};
|
|
transform_build_matrix(t);
|
|
render_path_paint_commands_paint(false);
|
|
}
|
|
if (g_context->sym_y) {
|
|
t->scale = (vec4_t){sx, -sy, sz, 1.0};
|
|
transform_build_matrix(t);
|
|
render_path_paint_commands_paint(false);
|
|
}
|
|
if (g_context->sym_z) {
|
|
t->scale = (vec4_t){sx, sy, -sz, 1.0};
|
|
transform_build_matrix(t);
|
|
render_path_paint_commands_paint(false);
|
|
}
|
|
if (g_context->sym_x && g_context->sym_y) {
|
|
t->scale = (vec4_t){-sx, -sy, sz, 1.0};
|
|
transform_build_matrix(t);
|
|
render_path_paint_commands_paint(false);
|
|
}
|
|
if (g_context->sym_x && g_context->sym_z) {
|
|
t->scale = (vec4_t){-sx, sy, -sz, 1.0};
|
|
transform_build_matrix(t);
|
|
render_path_paint_commands_paint(false);
|
|
}
|
|
if (g_context->sym_y && g_context->sym_z) {
|
|
t->scale = (vec4_t){sx, -sy, -sz, 1.0};
|
|
transform_build_matrix(t);
|
|
render_path_paint_commands_paint(false);
|
|
}
|
|
if (g_context->sym_x && g_context->sym_y && g_context->sym_z) {
|
|
t->scale = (vec4_t){-sx, -sy, -sz, 1.0};
|
|
transform_build_matrix(t);
|
|
render_path_paint_commands_paint(false);
|
|
}
|
|
t->scale = (vec4_t){sx, sy, sz, 1.0};
|
|
transform_build_matrix(t);
|
|
}
|
|
}
|
|
|
|
void render_path_paint_commands_live_brush() {
|
|
tool_type_t tool = g_context->tool;
|
|
if (tool != TOOL_TYPE_BRUSH && tool != TOOL_TYPE_ERASER && tool != TOOL_TYPE_CLONE && tool != TOOL_TYPE_DECAL && tool != TOOL_TYPE_TEXT &&
|
|
tool != TOOL_TYPE_BLUR) {
|
|
return;
|
|
}
|
|
|
|
if (render_path_paint_live_layer_locked) {
|
|
return;
|
|
}
|
|
|
|
if (render_path_paint_live_layer == NULL) {
|
|
render_path_paint_live_layer = slot_layer_create("_live", LAYER_SLOT_TYPE_LAYER, NULL);
|
|
}
|
|
|
|
i32 tid = g_context->layer->id;
|
|
if (slot_layer_is_mask(g_context->layer)) {
|
|
render_path_set_target("texpaint_live", NULL, NULL, GPU_CLEAR_NONE, 0, 0.0);
|
|
render_path_bind_target(string_tmp("texpaint%d", tid), "tex");
|
|
render_path_draw_shader("Scene/copy_pass/copy_pass");
|
|
}
|
|
else {
|
|
string_array_t *additional = any_array_create_from_raw(
|
|
(void *[]){
|
|
"texpaint_nor_live",
|
|
"texpaint_pack_live",
|
|
},
|
|
2);
|
|
render_path_set_target("texpaint_live", additional, NULL, GPU_CLEAR_NONE, 0, 0.0);
|
|
render_path_bind_target(string_tmp("texpaint%d", tid), "tex0");
|
|
render_path_bind_target(string_tmp("texpaint_nor%d", tid), "tex1");
|
|
render_path_bind_target(string_tmp("texpaint_pack%d", tid), "tex2");
|
|
render_path_draw_shader("Scene/copy_mrt3_pass/copy_mrt3_pass");
|
|
}
|
|
|
|
render_path_paint_use_live_layer(true);
|
|
|
|
render_path_paint_live_layer_drawn = 2;
|
|
|
|
ui_view2d_hwnd->redraws = 2;
|
|
f32 _x = g_context->paint_vec.x;
|
|
f32 _y = g_context->paint_vec.y;
|
|
f32 _last_x = g_context->last_paint_vec_x;
|
|
f32 _last_y = g_context->last_paint_vec_y;
|
|
i32 _pdirty = g_context->pdirty;
|
|
g_context->last_paint_vec_x = g_context->paint_vec.x;
|
|
g_context->last_paint_vec_y = g_context->paint_vec.y;
|
|
if (keymap_shortcut(any_map_get(g_keymap, "brush_ruler"), SHORTCUT_TYPE_STARTED)) {
|
|
g_context->last_paint_vec_x = g_context->last_paint_x;
|
|
g_context->last_paint_vec_y = g_context->last_paint_y;
|
|
}
|
|
g_context->pdirty = 2;
|
|
|
|
render_path_paint_commands_symmetry();
|
|
render_path_paint_commands_paint(true);
|
|
|
|
render_path_paint_use_live_layer(false);
|
|
|
|
g_context->paint_vec.x = _x;
|
|
g_context->paint_vec.y = _y;
|
|
g_context->last_paint_vec_x = _last_x;
|
|
g_context->last_paint_vec_y = _last_y;
|
|
g_context->pdirty = _pdirty;
|
|
g_context->brush_blend_dirty = true;
|
|
}
|
|
|
|
void render_path_paint_draw_cursor(f32 mx, f32 my, f32 radius, f32 tint_r, f32 tint_g, f32 tint_b) {
|
|
mesh_object_t *plane = scene_get_child(".Plane")->ext;
|
|
mesh_data_t *geom = plane->data;
|
|
|
|
render_path_set_target("", NULL, NULL, GPU_CLEAR_NONE, 0, 0.0);
|
|
gpu_set_pipeline(pipes_cursor);
|
|
render_target_t *rt = any_map_get(render_path_render_targets, "main");
|
|
gpu_texture_t *main = rt->_image;
|
|
gpu_set_texture(pipes_cursor_gbufferd, main);
|
|
gpu_set_float2(pipes_cursor_mouse, mx, my);
|
|
gpu_set_float2(pipes_cursor_tex_step, 1 / (float)main->width, 1 / (float)main->height);
|
|
gpu_set_float(pipes_cursor_radius, radius);
|
|
vec4_t right = vec4_norm(camera_object_right_world(scene_camera));
|
|
gpu_set_float3(pipes_cursor_camera_right, right.x, right.y, right.z);
|
|
gpu_set_float3(pipes_cursor_tint, tint_r, tint_g, tint_b);
|
|
vec4_t up = vec4_norm(camera_object_up_world(scene_camera));
|
|
gpu_set_float3(pipes_cursor_camera_up, up.x, up.y, up.z);
|
|
gpu_set_float(pipes_cursor_camera_align, context_is_brush_camera_align() ? 1.0f : 0.0f);
|
|
gpu_set_mat4(pipes_cursor_vp, scene_camera->vp);
|
|
mat4_t inv_vp = mat4_inv(scene_camera->vp);
|
|
gpu_set_mat4(pipes_cursor_inv_vp, inv_vp);
|
|
gpu_set_vertex_buffer(geom->_->vertex_buffer);
|
|
gpu_set_index_buffer(geom->_->index_buffer);
|
|
gpu_draw();
|
|
render_path_end();
|
|
}
|
|
|
|
void render_path_paint_draw_cursor_decal(f32 mx, f32 my, f32 radius, f32 opacity_scale, gpu_texture_t *image) {
|
|
if (image == NULL) {
|
|
return;
|
|
}
|
|
|
|
mesh_object_t *plane = scene_get_child(".Plane")->ext;
|
|
mesh_data_t *geom = plane->data;
|
|
|
|
// Prevent cursor jumping constantly if randomness is used in brush nodes
|
|
f32 brush_nodes_opacity = g_context->brush_nodes_opacity;
|
|
f32 brush_nodes_angle = g_context->brush_nodes_angle;
|
|
if (g_context->brush_nodes_uses_random) {
|
|
brush_nodes_opacity = 1.0;
|
|
brush_nodes_angle = 0.0;
|
|
}
|
|
|
|
render_path_set_target("", NULL, NULL, GPU_CLEAR_NONE, 0, 0.0);
|
|
gpu_set_pipeline(pipes_cursor_decal);
|
|
render_target_t *rt = any_map_get(render_path_render_targets, "main");
|
|
gpu_texture_t *main = rt->_image;
|
|
gpu_set_texture(pipes_cursor_decal_gbufferd, main);
|
|
gpu_set_texture(pipes_cursor_decal_texdecal, image);
|
|
render_target_t *gbuffer0_rt = any_map_get(render_path_render_targets, "gbuffer0");
|
|
gpu_set_texture(pipes_cursor_decal_gbuffer0, gbuffer0_rt->_image);
|
|
gpu_set_float2(pipes_cursor_decal_mouse, mx, my);
|
|
gpu_set_float2(pipes_cursor_decal_tex_step, 1 / (float)main->width, 1 / (float)main->height);
|
|
gpu_set_float(pipes_cursor_decal_radius, radius);
|
|
vec4_t right = vec4_norm(camera_object_right_world(scene_camera));
|
|
gpu_set_float3(pipes_cursor_decal_camera_right, right.x, right.y, right.z);
|
|
vec4_t up = vec4_norm(camera_object_up_world(scene_camera));
|
|
gpu_set_float3(pipes_cursor_decal_camera_up, up.x, up.y, up.z);
|
|
gpu_set_float(pipes_cursor_decal_camera_align, context_is_brush_camera_align() ? 1.0f : 0.0f);
|
|
f32 opacity = g_context->brush_opacity * brush_nodes_opacity * opacity_scale;
|
|
gpu_set_float(pipes_cursor_decal_opacity, opacity);
|
|
f32 angle = (g_context->brush_angle + brush_nodes_angle) * (math_pi() / 180.0);
|
|
gpu_set_float2(pipes_cursor_decal_angle, math_cos(angle), math_sin(angle));
|
|
gpu_set_float(pipes_cursor_decal_scale_x, g_context->brush_scale_x);
|
|
gpu_set_mat4(pipes_cursor_decal_vp, scene_camera->vp);
|
|
mat4_t inv_vp = mat4_inv(scene_camera->vp);
|
|
gpu_set_mat4(pipes_cursor_decal_inv_vp, inv_vp);
|
|
gpu_set_vertex_buffer(geom->_->vertex_buffer);
|
|
gpu_set_index_buffer(geom->_->index_buffer);
|
|
gpu_draw();
|
|
render_path_end();
|
|
}
|
|
|
|
void render_path_paint_commands_cursor() {
|
|
bool decal_mask = context_is_decal_mask();
|
|
tool_type_t tool = g_context->tool;
|
|
if (tool != TOOL_TYPE_BRUSH && tool != TOOL_TYPE_ERASER && tool != TOOL_TYPE_CLONE && tool != TOOL_TYPE_BLUR && tool != TOOL_TYPE_PARTICLE &&
|
|
!context_is_decal() && !decal_mask) {
|
|
return;
|
|
}
|
|
|
|
bool fill_layer = g_context->layer->fill_material != NULL;
|
|
bool group_layer = slot_layer_is_group(g_context->layer);
|
|
if (!base_ui_enabled || base_is_dragging || fill_layer || group_layer) {
|
|
return;
|
|
}
|
|
|
|
f32 mx = g_context->paint_vec.x;
|
|
f32 my = 1.0 - g_context->paint_vec.y;
|
|
|
|
// Prevent cursor jumping constantly if randomness is used in brush nodes
|
|
f32 brush_nodes_radius = g_context->brush_nodes_radius;
|
|
if (g_context->brush_nodes_uses_random) {
|
|
brush_nodes_radius = 1.0;
|
|
}
|
|
|
|
if (context_is_decal() && !g_context->paint2d) {
|
|
f32 scale2d = (900 / (f32)base_h()) * g_config->window_scale;
|
|
f32 radius = brush_nodes_radius * g_context->brush_radius / 15.0 * scale2d * 2.0;
|
|
f32 cmx = decal_mask ? g_context->decal_x : mx;
|
|
f32 cmy = decal_mask ? 1.0f - g_context->decal_y : my;
|
|
gpu_texture_t *image = g_context->decal_image;
|
|
render_path_paint_draw_cursor_decal(cmx, cmy, radius, decal_mask ? 0.3f : 0.9f, image);
|
|
if (decal_mask) {
|
|
render_path_paint_draw_cursor(mx, my, brush_nodes_radius * g_context->brush_decal_mask_radius / 3.4, 1.0, 1.0, 1.0);
|
|
}
|
|
return;
|
|
}
|
|
|
|
f32 radius = decal_mask ? g_context->brush_decal_mask_radius : g_context->brush_radius;
|
|
render_path_paint_draw_cursor(mx, my, brush_nodes_radius * radius / 3.4, 1.0, 1.0, 1.0);
|
|
}
|
|
|
|
bool render_path_paint_paint_enabled() {
|
|
if (g_context->tool == TOOL_TYPE_BAKE) {
|
|
return true;
|
|
}
|
|
|
|
if (g_context->tool == TOOL_TYPE_CURSOR) {
|
|
return true;
|
|
}
|
|
|
|
bool fill_layer = g_context->layer->fill_material != NULL && g_context->tool != TOOL_TYPE_PICKER && g_context->tool != TOOL_TYPE_MATERIAL &&
|
|
g_context->tool != TOOL_TYPE_COLORID;
|
|
bool group_layer = slot_layer_is_group(g_context->layer);
|
|
|
|
// Skip paint if material references this layer and wants to paint into it at the same time
|
|
bool self_in_material = false;
|
|
i32 layer_i = array_index_of(g_project->_->layers, g_context->layer);
|
|
ui_node_t_array_t *nodes = g_context->material->canvas->nodes;
|
|
for (i32 i = 0; i < nodes->length; ++i) {
|
|
ui_node_t *n = nodes->buffer[i];
|
|
if (string_equals(n->type, "LAYER") && (i32)n->buttons->buffer[0]->default_value->buffer[0] == layer_i) {
|
|
self_in_material = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return !fill_layer && !group_layer && !g_context->foreground_event && !self_in_material;
|
|
}
|
|
|
|
void render_path_paint_begin() {
|
|
if (g_context->layer->texpaint_sculpt != NULL) {
|
|
render_path_sculpt_begin();
|
|
return;
|
|
}
|
|
|
|
if (!render_path_paint_dilated) {
|
|
render_path_paint_dilate(false, true);
|
|
render_path_paint_dilated = true;
|
|
}
|
|
|
|
if (!render_path_paint_paint_enabled()) {
|
|
return;
|
|
}
|
|
|
|
render_path_paint_push_undo_last = history_push_undo;
|
|
|
|
if (history_push_undo && history_undo_layers != NULL) {
|
|
history_paint();
|
|
}
|
|
|
|
if (g_context->paint2d) {
|
|
render_path_paint_set_plane_mesh();
|
|
}
|
|
|
|
if (render_path_paint_live_layer_drawn > 0) {
|
|
render_path_paint_live_layer_drawn--;
|
|
}
|
|
|
|
if (g_config->brush_live && g_context->pdirty <= 0 && g_context->ddirty <= 0 && g_context->brush_time == 0) {
|
|
// Depth is unchanged, draw before gbuffer gets updated
|
|
render_path_paint_commands_live_brush();
|
|
}
|
|
}
|
|
|
|
void render_path_paint_end() {
|
|
g_context->ddirty--;
|
|
|
|
g_context->last_paint_vec_x = g_context->paint_vec.x;
|
|
g_context->last_paint_vec_y = g_context->paint_vec.y;
|
|
|
|
if (!render_path_paint_paint_enabled()) {
|
|
return;
|
|
}
|
|
|
|
if (g_context->pdirty > 0) {
|
|
layers_update_linked_layers();
|
|
}
|
|
|
|
g_context->pdirty--;
|
|
}
|
|
|
|
void _render_path_paint_final() {
|
|
g_context->bake_type = _render_path_paint_bake_type;
|
|
make_material_parse_paint_material(false);
|
|
g_context->pdirty = 1;
|
|
render_path_paint_commands_paint(true);
|
|
g_context->pdirty = 0;
|
|
render_path_paint_baking = false;
|
|
}
|
|
|
|
void _render_path_paint_deriv_on_next_frame(void *_) {
|
|
_render_path_paint_final();
|
|
}
|
|
|
|
void _render_path_paint_deriv() {
|
|
g_context->bake_type = BAKE_TYPE_HEIGHT;
|
|
make_material_parse_paint_material(false);
|
|
g_context->pdirty = 1;
|
|
render_path_paint_commands_paint(true);
|
|
g_context->pdirty = 0;
|
|
if (render_path_paint_push_undo_last) {
|
|
history_paint();
|
|
}
|
|
sys_notify_on_next_frame(&_render_path_paint_deriv_on_next_frame, NULL);
|
|
}
|
|
|
|
bool render_path_paint_is_rt_bake() {
|
|
return (g_context->bake_type == BAKE_TYPE_OCCLUSION || g_context->bake_type == BAKE_TYPE_LIGHTMAP || g_context->bake_type == BAKE_TYPE_BENT_NORMAL ||
|
|
g_context->bake_type == BAKE_TYPE_THICKNESS);
|
|
}
|
|
|
|
void render_path_paint_draw_bake(void *_) {
|
|
_render_path_paint_final();
|
|
}
|
|
|
|
void render_path_paint_draw_bake_derivative(void *_) {
|
|
_render_path_paint_deriv();
|
|
}
|
|
|
|
void render_path_paint_draw() {
|
|
if (!render_path_paint_paint_enabled()) {
|
|
return;
|
|
}
|
|
|
|
if (g_config->brush_live && g_context->pdirty <= 0 && g_context->ddirty > 0 && g_context->brush_time == 0) {
|
|
// gbuffer has been updated now but brush will lag 1 frame
|
|
render_path_paint_commands_live_brush();
|
|
}
|
|
|
|
if (history_undo_layers != NULL) {
|
|
render_path_paint_commands_symmetry();
|
|
|
|
if (g_context->pdirty > 0) {
|
|
render_path_paint_dilated = false;
|
|
}
|
|
|
|
if (g_context->tool == TOOL_TYPE_BAKE) {
|
|
if (g_context->bake_type == BAKE_TYPE_NORMAL || g_context->bake_type == BAKE_TYPE_HEIGHT || g_context->bake_type == BAKE_TYPE_DERIVATIVE) {
|
|
if (!render_path_paint_baking && g_context->pdirty > 0) {
|
|
|
|
render_path_paint_baking = true;
|
|
_render_path_paint_bake_type = g_context->bake_type;
|
|
g_context->bake_type = g_context->bake_type == BAKE_TYPE_NORMAL ? BAKE_TYPE_NORMAL_OBJECT : BAKE_TYPE_POSITION; // Bake high poly data
|
|
make_material_parse_paint_material(false);
|
|
mesh_object_t *_paint_object = g_context->paint_object;
|
|
mesh_object_t *high_poly = g_project->_->paint_objects->buffer[g_context->bake_high_poly];
|
|
bool _visible = high_poly->base->visible;
|
|
high_poly->base->visible = true;
|
|
context_select_paint_object(high_poly);
|
|
render_path_paint_commands_paint(true);
|
|
high_poly->base->visible = _visible;
|
|
if (render_path_paint_push_undo_last) {
|
|
history_paint();
|
|
}
|
|
context_select_paint_object(_paint_object);
|
|
|
|
if (g_context->bake_type == BAKE_TYPE_DERIVATIVE) {
|
|
sys_notify_on_next_frame(&render_path_paint_draw_bake_derivative, NULL);
|
|
}
|
|
else {
|
|
sys_notify_on_next_frame(&render_path_paint_draw_bake, NULL);
|
|
}
|
|
}
|
|
}
|
|
else if (g_context->bake_type == BAKE_TYPE_OBJECTID) {
|
|
i32 _layer_filter = g_context->layer_filter;
|
|
mesh_object_t *_paint_object = g_context->paint_object;
|
|
bool is_merged = g_context->merged_object != NULL;
|
|
bool _visible = is_merged && g_context->merged_object->base->visible;
|
|
g_context->layer_filter = 1;
|
|
if (is_merged) {
|
|
g_context->merged_object->base->visible = false;
|
|
}
|
|
|
|
for (i32 i = 0; i < g_project->_->paint_objects->length; ++i) {
|
|
mesh_object_t *p = g_project->_->paint_objects->buffer[i];
|
|
context_select_paint_object(p);
|
|
render_path_paint_commands_paint(true);
|
|
}
|
|
|
|
g_context->layer_filter = _layer_filter;
|
|
context_select_paint_object(_paint_object);
|
|
if (is_merged)
|
|
g_context->merged_object->base->visible = _visible;
|
|
}
|
|
else if (render_path_paint_is_rt_bake()) {
|
|
render_path_raytrace_bake_commands(make_material_parse_paint_material);
|
|
render_path_paint_dilate(true, false);
|
|
}
|
|
else {
|
|
render_path_paint_commands_paint(true);
|
|
}
|
|
}
|
|
else { // Paint
|
|
render_path_paint_commands_paint(true);
|
|
}
|
|
}
|
|
|
|
if (g_context->brush_blend_dirty) {
|
|
g_context->brush_blend_dirty = false;
|
|
render_path_set_target("texpaint_blend0", NULL, NULL, GPU_CLEAR_COLOR, 0x00000000, 0.0);
|
|
render_path_set_target("texpaint_blend1", NULL, NULL, GPU_CLEAR_COLOR, 0x00000000, 0.0);
|
|
render_path_end();
|
|
}
|
|
|
|
if (g_context->paint2d) {
|
|
render_path_paint_restore_plane_mesh();
|
|
}
|
|
}
|
|
|
|
void render_path_paint_set_plane_mesh() {
|
|
g_context->paint2d_view = true;
|
|
render_path_paint_painto = g_context->paint_object;
|
|
render_path_paint_visibles = u8_array_create_from_raw((u8[]){}, 0);
|
|
for (i32 i = 0; i < g_project->_->paint_objects->length; ++i) {
|
|
mesh_object_t *p = g_project->_->paint_objects->buffer[i];
|
|
u8_array_push(render_path_paint_visibles, p->base->visible);
|
|
p->base->visible = false;
|
|
}
|
|
if (g_context->merged_object != NULL) {
|
|
render_path_paint_merged_object_visible = g_context->merged_object->base->visible;
|
|
g_context->merged_object->base->visible = false;
|
|
}
|
|
|
|
camera_object_t *cam = scene_camera;
|
|
g_context->saved_camera = cam->base->transform->local;
|
|
render_path_paint_saved_fov = cam->data->fov;
|
|
viewport_update_camera_type(CAMERA_TYPE_PERSPECTIVE);
|
|
mat4_t m = mat4_identity();
|
|
m = mat4_translate(m, 0, 0, 0.5);
|
|
transform_set_matrix(cam->base->transform, m);
|
|
cam->data->fov = 0.69;
|
|
camera_object_build_proj(cam, -1.0);
|
|
camera_object_build_mat(cam);
|
|
f32 tw = 0.9 * ui_view2d_pan_scale * (fmin(ui_view2d_ww, ui_view2d_wh) / ui_view2d_ww);
|
|
f32 tx = ui_view2d_pan_x / (float)ui_view2d_ww;
|
|
f32 ty = ui_view2d_pan_y / (float)sys_h();
|
|
m = mat4_identity();
|
|
m = mat4_scale(m, (vec4_t){tw, tw, 1, 1.0});
|
|
m = mat4_set_loc(m, (vec4_t){tx, ty, 0, 1.0});
|
|
mat4_t m2 = mat4_identity();
|
|
m2 = mat4_inv(scene_camera->vp);
|
|
m = mat4_mult_mat(m, m2);
|
|
|
|
bool tiled = ui_view2d_tiled_show;
|
|
if (tiled && scene_get_child(".PlaneTiled") == NULL) {
|
|
// 3x3 planes
|
|
i16_array_t *posa = i16_array_create_from_raw(
|
|
(i16[]){
|
|
32767, 0, -32767, 0, 10922, 0, -10922, 0, 10922, 0, -32767, 0, 10922, 0, -10922, 0, -10922, 0, 10922, 0, -10922, 0, -10922, 0,
|
|
-10922, 0, 10922, 0, -32767, 0, 32767, 0, -32767, 0, 10922, 0, 10922, 0, 10922, 0, -10922, 0, 32767, 0, -10922, 0, 10922, 0,
|
|
32767, 0, 10922, 0, 10922, 0, 32767, 0, 10922, 0, 10922, 0, -10922, 0, -10922, 0, -32767, 0, 10922, 0, -32767, 0, -10922, 0,
|
|
32767, 0, -10922, 0, 10922, 0, 10922, 0, 10922, 0, -10922, 0, -10922, 0, -32767, 0, -32767, 0, -10922, 0, -32767, 0, -32767, 0,
|
|
10922, 0, -32767, 0, -10922, 0, -10922, 0, -10922, 0, -32767, 0, 32767, 0, -32767, 0, 32767, 0, -10922, 0, 10922, 0, -10922, 0,
|
|
10922, 0, -10922, 0, 10922, 0, 10922, 0, -10922, 0, 10922, 0, -10922, 0, 10922, 0, -10922, 0, 32767, 0, -32767, 0, 32767, 0,
|
|
10922, 0, 10922, 0, 10922, 0, 32767, 0, -10922, 0, 32767, 0, 32767, 0, 10922, 0, 32767, 0, 32767, 0, 10922, 0, 32767, 0,
|
|
-10922, 0, -10922, 0, -10922, 0, 10922, 0, -32767, 0, 10922, 0, 32767, 0, -10922, 0, 32767, 0, 10922, 0, 10922, 0, 10922, 0,
|
|
-10922, 0, -32767, 0, -10922, 0, -10922, 0, -32767, 0, -10922, 0, 10922, 0, -32767, 0, 10922, 0, -10922, 0, -10922, 0, -10922, 0,
|
|
},
|
|
216);
|
|
i16_array_t *nora = i16_array_create_from_raw(
|
|
(i16[]){
|
|
0, -32767, 0, -32767, 0, -32767, 0, -32767, 0, -32767, 0, -32767, 0, -32767, 0, -32767, 0, -32767, 0, -32767, 0, -32767,
|
|
0, -32767, 0, -32767, 0, -32767, 0, -32767, 0, -32767, 0, -32767, 0, -32767, 0, -32767, 0, -32767, 0, -32767, 0, -32767,
|
|
0, -32767, 0, -32767, 0, -32767, 0, -32767, 0, -32767, 0, -32767, 0, -32767, 0, -32767, 0, -32767, 0, -32767, 0, -32767,
|
|
0, -32767, 0, -32767, 0, -32767, 0, -32767, 0, -32767, 0, -32767, 0, -32767, 0, -32767, 0, -32767, 0, -32767, 0, -32767,
|
|
0, -32767, 0, -32767, 0, -32767, 0, -32767, 0, -32767, 0, -32767, 0, -32767, 0, -32767, 0, -32767, 0, -32767,
|
|
},
|
|
108);
|
|
i16_array_t *texa = i16_array_create_from_raw(
|
|
(i16[]){
|
|
32767, 32767, 0, 0, 0, 32767, 32767, 32767, 0, 0, 0, 32767, 32767, 32767, 0, 0, 0, 32767, 32767, 32767, 0, 0, 0, 32767,
|
|
32767, 32767, 0, 0, 0, 32767, 32767, 32767, 0, 0, 0, 32767, 32767, 32767, 0, 0, 0, 32767, 32767, 32767, 0, 0, 0, 32767,
|
|
32767, 32767, 0, 0, 0, 32767, 32767, 32767, 32767, 0, 0, 0, 32767, 32767, 32767, 0, 0, 0, 32767, 32767, 32767, 0, 0, 0,
|
|
32767, 32767, 32767, 0, 0, 0, 32767, 32767, 32767, 0, 0, 0, 32767, 32767, 32767, 0, 0, 0, 32767, 32767, 32767, 0, 0, 0,
|
|
32767, 32767, 32767, 0, 0, 0, 32767, 32767, 32767, 0, 0, 0,
|
|
},
|
|
108);
|
|
u32_array_t *inda = u32_array_create_from_raw(
|
|
(u32[]){
|
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
|
|
27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
|
|
},
|
|
54);
|
|
mesh_data_t *raw =
|
|
ALLOC_INIT(mesh_data_t, {.name = ".PlaneTiled",
|
|
.vertex_arrays = any_array_create_from_raw(
|
|
(void *[]){
|
|
ALLOC_INIT(vertex_array_t, {.attrib = "pos", .values = i16_array_create_from_array(posa), .data = "short4norm"}),
|
|
ALLOC_INIT(vertex_array_t, {.attrib = "nor", .values = i16_array_create_from_array(nora), .data = "short2norm"}),
|
|
ALLOC_INIT(vertex_array_t, {.attrib = "tex", .values = i16_array_create_from_array(texa), .data = "short2norm"}),
|
|
},
|
|
3),
|
|
.index_array = u32_array_create_from_array(inda),
|
|
.scale_pos = 1.5,
|
|
.scale_tex = 1.0});
|
|
mesh_data_t *md = mesh_data_create(raw);
|
|
mesh_object_t *mo = scene_get_child(".Plane")->ext;
|
|
shader_data_t *material = mo->material;
|
|
mesh_object_t *o = scene_add_mesh_object(md, material, NULL);
|
|
o->base->name = ".PlaneTiled";
|
|
}
|
|
|
|
render_path_paint_planeo = scene_get_child(tiled ? ".PlaneTiled" : ".Plane")->ext;
|
|
|
|
render_path_paint_planeo->base->visible = true;
|
|
g_context->paint_object = render_path_paint_planeo;
|
|
|
|
vec4_t v = (vec4_t){m.m00, m.m01, m.m02, 1.0};
|
|
f32 sx = vec4_len(v);
|
|
render_path_paint_planeo->base->transform->rot = quat_from_euler(-math_pi() / 2.0, 0, 0);
|
|
render_path_paint_planeo->base->transform->scale = (vec4_t){sx, 1.0, sx, 1.0};
|
|
render_path_paint_planeo->base->transform->scale.z *= config_get_texture_res_y() / (float)config_get_texture_res_x();
|
|
render_path_paint_planeo->base->transform->loc = (vec4_t){m.m30, -m.m31, 0.0, 1.0};
|
|
transform_build_matrix(render_path_paint_planeo->base->transform);
|
|
}
|
|
|
|
void render_path_paint_restore_plane_mesh() {
|
|
g_context->paint2d_view = false;
|
|
render_path_paint_planeo->base->visible = false;
|
|
render_path_paint_planeo->base->transform->loc = (vec4_t){0.0, 0.0, 0.0, 1.0};
|
|
for (i32 i = 0; i < g_project->_->paint_objects->length; ++i) {
|
|
g_project->_->paint_objects->buffer[i]->base->visible = render_path_paint_visibles->buffer[i];
|
|
}
|
|
if (g_context->merged_object != NULL) {
|
|
g_context->merged_object->base->visible = render_path_paint_merged_object_visible;
|
|
}
|
|
g_context->paint_object = render_path_paint_painto;
|
|
transform_set_matrix(scene_camera->base->transform, g_context->saved_camera);
|
|
scene_camera->data->fov = render_path_paint_saved_fov;
|
|
viewport_update_camera_type(g_context->camera_type);
|
|
camera_object_build_proj(scene_camera, -1.0);
|
|
camera_object_build_mat(scene_camera);
|
|
|
|
render_path_base_draw_gbuffer();
|
|
}
|
|
|
|
void render_path_paint_bind_layers() {
|
|
bool is_live = g_config->brush_live && render_path_paint_live_layer_drawn > 0;
|
|
bool is_material_tool = g_context->tool == TOOL_TYPE_MATERIAL;
|
|
if (is_live || is_material_tool) {
|
|
render_path_paint_use_live_layer(true);
|
|
}
|
|
|
|
for (i32 i = 0; i < g_project->_->layers->length; ++i) {
|
|
slot_layer_t *l = g_project->_->layers->buffer[i];
|
|
char *n = string_tmp("texpaint%d", l->id);
|
|
render_path_bind_target(n, n);
|
|
|
|
if (slot_layer_is_layer(l)) {
|
|
n = string_tmp("texpaint_nor%d", l->id);
|
|
render_path_bind_target(n, n);
|
|
n = string_tmp("texpaint_pack%d", l->id);
|
|
render_path_bind_target(n, n);
|
|
}
|
|
|
|
if (l->texpaint_sculpt != NULL) {
|
|
n = string_tmp("texpaint_sculpt%d", l->id);
|
|
render_path_bind_target(n, n);
|
|
}
|
|
}
|
|
|
|
if (g_context->colorid_viewport_mask) {
|
|
render_path_bind_target("texpaint_colorid", "texpaint_colorid");
|
|
}
|
|
}
|
|
|
|
void render_path_paint_unbind_layers() {
|
|
bool is_live = g_config->brush_live && render_path_paint_live_layer_drawn > 0;
|
|
bool is_material_tool = g_context->tool == TOOL_TYPE_MATERIAL;
|
|
if (is_live || is_material_tool) {
|
|
render_path_paint_use_live_layer(false);
|
|
}
|
|
}
|
|
|
|
void render_path_paint_dilate(bool base, bool nor_pack) {
|
|
if (g_config->dilate_radius > 0 && !g_context->paint2d) {
|
|
util_uv_cache_dilate_map();
|
|
layers_make_temp_img();
|
|
i32 tid = g_context->layer->id;
|
|
|
|
char *format = base_bits_handle->i == TEXTURE_BITS_BITS8 ? "RGBA32" : base_bits_handle->i == TEXTURE_BITS_BITS16 ? "RGBA64" : "RGBA128";
|
|
char *copy_pass = string_equals(format, "RGBA64") ? "copyRGBA64_pass" : string_equals(format, "RGBA128") ? "copyRGBA128_pass" : "copy_pass";
|
|
char *dilate_pass = string_equals(format, "RGBA64") ? "dilateRGBA64_pass" : string_equals(format, "RGBA128") ? "dilateRGBA128_pass" : "dilate_pass";
|
|
|
|
if (base) {
|
|
render_path_set_target("temptex0", NULL, NULL, GPU_CLEAR_NONE, 0, 0.0);
|
|
render_path_bind_target(string_tmp("texpaint%d", tid), "tex");
|
|
render_path_draw_shader(string_tmp("Scene/copy_pass/%s", copy_pass));
|
|
render_path_set_target(string_tmp("texpaint%d", tid), NULL, NULL, GPU_CLEAR_NONE, 0, 0.0);
|
|
render_path_bind_target("temptex0", "tex");
|
|
render_path_draw_shader(string_tmp("Scene/dilate_pass/%s", dilate_pass));
|
|
}
|
|
if (nor_pack && !slot_layer_is_mask(g_context->layer)) {
|
|
render_path_set_target("temptex0", NULL, NULL, GPU_CLEAR_NONE, 0, 0.0);
|
|
render_path_bind_target(string_tmp("texpaint_nor%d", tid), "tex");
|
|
render_path_draw_shader(string_tmp("Scene/copy_pass/%s", copy_pass));
|
|
render_path_set_target(string_tmp("texpaint_nor%d", tid), NULL, NULL, GPU_CLEAR_NONE, 0, 0.0);
|
|
render_path_bind_target("temptex0", "tex");
|
|
render_path_draw_shader(string_tmp("Scene/dilate_pass/%s", dilate_pass));
|
|
render_path_set_target("temptex0", NULL, NULL, GPU_CLEAR_NONE, 0, 0.0);
|
|
render_path_bind_target(string_tmp("texpaint_pack%d", tid), "tex");
|
|
render_path_draw_shader(string_tmp("Scene/copy_pass/%s", copy_pass));
|
|
render_path_set_target(string_tmp("texpaint_pack%d", tid), NULL, NULL, GPU_CLEAR_NONE, 0, 0.0);
|
|
render_path_bind_target("temptex0", "tex");
|
|
render_path_draw_shader(string_tmp("Scene/dilate_pass/%s", dilate_pass));
|
|
}
|
|
}
|
|
}
|