507 lines
17 KiB
TypeScript
507 lines
17 KiB
TypeScript
|
|
let make_material_default_scon: shader_context_t = null;
|
|
let make_material_default_mcon: material_context_t = null;
|
|
|
|
let make_material_height_used: bool = false;
|
|
let make_material_emis_used: bool = false;
|
|
let make_material_subs_used: bool = false;
|
|
|
|
function make_material_get_mout(): bool {
|
|
for (let i: i32 = 0; i < ui_nodes_get_canvas_material().nodes.length; ++i) {
|
|
let n: ui_node_t = ui_nodes_get_canvas_material().nodes[i];
|
|
if (n.type == "OUTPUT_MATERIAL_PBR") {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function make_material_parse_mesh_material() {
|
|
let m: material_data_t = project_materials[0].data;
|
|
|
|
for (let i: i32 = 0; i < m._.shader.contexts.length; ++i) {
|
|
let c: shader_context_t = m._.shader.contexts[i];
|
|
if (c.name == "mesh") {
|
|
array_remove(m._.shader.contexts, c);
|
|
make_material_delete_context(c);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (make_mesh_layer_pass_count > 1) {
|
|
let i: i32 = 0;
|
|
while (i < m._.shader.contexts.length) {
|
|
let c: shader_context_t = m._.shader.contexts[i];
|
|
for (let j: i32 = 1; j < make_mesh_layer_pass_count; ++j) {
|
|
let name: string = "mesh" + j;
|
|
if (c.name == name) {
|
|
array_remove(m._.shader.contexts, c);
|
|
make_material_delete_context(c);
|
|
i--;
|
|
break;
|
|
}
|
|
}
|
|
i++;
|
|
}
|
|
|
|
i = 0;
|
|
while (i < m.contexts.length) {
|
|
let c: material_context_t = m.contexts[i];
|
|
for (let j: i32 = 1; j < make_mesh_layer_pass_count; ++j) {
|
|
let name: string = "mesh" + j;
|
|
if (c.name == name) {
|
|
array_remove(m.contexts, c);
|
|
i--;
|
|
break;
|
|
}
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
|
|
let mm: material_t = {
|
|
name: "Material",
|
|
canvas: null
|
|
};
|
|
|
|
let con: node_shader_context_t = make_mesh_run(mm);
|
|
shader_context_load(con.data);
|
|
array_push(m._.shader.contexts, con.data);
|
|
|
|
for (let i: i32 = 1; i < make_mesh_layer_pass_count; ++i) {
|
|
let mm: material_t = {
|
|
name: "Material",
|
|
canvas: null
|
|
};
|
|
let con: node_shader_context_t = make_mesh_run(mm, i);
|
|
shader_context_load(con.data);
|
|
|
|
array_push(m._.shader.contexts, con.data);
|
|
|
|
let mcon: material_context_t = {
|
|
name: "mesh" + i,
|
|
bind_textures: []
|
|
};
|
|
material_context_load(mcon);
|
|
array_push(m.contexts, mcon);
|
|
}
|
|
|
|
context_raw.ddirty = 2;
|
|
|
|
render_path_raytrace_dirty = 1;
|
|
}
|
|
|
|
function make_material_parse_mesh_preview_material(md: material_data_t = null) {
|
|
if (!make_material_get_mout()) {
|
|
return;
|
|
}
|
|
|
|
let m: material_data_t = md == null ? project_materials[0].data : md;
|
|
let scon: shader_context_t = null;
|
|
for (let i: i32 = 0; i < m._.shader.contexts.length; ++i) {
|
|
let c: shader_context_t = m._.shader.contexts[i];
|
|
if (c.name == "mesh") {
|
|
scon = c;
|
|
break;
|
|
}
|
|
}
|
|
|
|
array_remove(m._.shader.contexts, scon);
|
|
|
|
let mcon: material_context_t = {
|
|
name: "mesh",
|
|
bind_textures: []
|
|
};
|
|
|
|
let sd: material_t = {
|
|
name: "Material",
|
|
canvas: null
|
|
};
|
|
let con: node_shader_context_t = make_mesh_preview_run(sd, mcon);
|
|
|
|
for (let i: i32 = 0; i < m.contexts.length; ++i) {
|
|
if (m.contexts[i].name == "mesh") {
|
|
material_context_load(mcon);
|
|
m.contexts[i] = mcon;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (scon != null) {
|
|
make_material_delete_context(scon);
|
|
}
|
|
|
|
let compile_error: bool = false;
|
|
shader_context_load(con.data);
|
|
if (con.data == null) {
|
|
compile_error = true;
|
|
}
|
|
scon = con.data;
|
|
if (compile_error) {
|
|
return;
|
|
}
|
|
|
|
array_push(m._.shader.contexts, scon);
|
|
}
|
|
|
|
function make_material_parse_paint_material(bake_previews: bool = true) {
|
|
if (!make_material_get_mout()) {
|
|
return;
|
|
}
|
|
|
|
if (bake_previews) {
|
|
let current: gpu_texture_t = _draw_current;
|
|
let in_use: bool = gpu_in_use;
|
|
if (in_use) draw_end();
|
|
make_material_bake_node_previews();
|
|
if (in_use) draw_begin(current);
|
|
}
|
|
|
|
let m: material_data_t = project_materials[0].data;
|
|
for (let i: i32 = 0; i < m._.shader.contexts.length; ++i) {
|
|
let c: shader_context_t = m._.shader.contexts[i];
|
|
if (c.name == "paint") {
|
|
array_remove(m._.shader.contexts, c);
|
|
if (c != make_material_default_scon) {
|
|
make_material_delete_context(c);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
for (let i: i32 = 0; i < m.contexts.length; ++i) {
|
|
let c: material_context_t = m.contexts[i];
|
|
if (c.name == "paint") {
|
|
array_remove(m.contexts, c);
|
|
break;
|
|
}
|
|
}
|
|
|
|
let sdata: material_t = {
|
|
name: "Material",
|
|
canvas: ui_nodes_get_canvas_material()
|
|
};
|
|
let tmcon: material_context_t = {
|
|
name: "paint",
|
|
bind_textures: []
|
|
};
|
|
let con: node_shader_context_t = make_paint_run(sdata, tmcon);
|
|
|
|
let compile_error: bool = false;
|
|
let scon: shader_context_t;
|
|
shader_context_load(con.data);
|
|
if (con.data == null) {
|
|
compile_error = true;
|
|
}
|
|
scon = con.data;
|
|
if (compile_error) {
|
|
return;
|
|
}
|
|
material_context_load(tmcon);
|
|
let mcon: material_context_t = tmcon;
|
|
|
|
array_push(m._.shader.contexts, scon);
|
|
array_push(m.contexts, mcon);
|
|
|
|
if (make_material_default_scon == null) {
|
|
make_material_default_scon = scon;
|
|
}
|
|
if (make_material_default_mcon == null) {
|
|
make_material_default_mcon = mcon;
|
|
}
|
|
}
|
|
|
|
function make_material_bake_node_previews() {
|
|
context_raw.node_previews_used = [];
|
|
if (context_raw.node_previews == null) {
|
|
context_raw.node_previews = map_create();
|
|
}
|
|
let empty: ui_node_t[] = [];
|
|
make_material_traverse_nodes(ui_nodes_get_canvas_material().nodes, null, empty);
|
|
|
|
let keys: string[] = map_keys(context_raw.node_previews);
|
|
for (let i: i32 = 0; i < keys.length; ++i) {
|
|
let key: string = keys[i];
|
|
if (array_index_of(context_raw.node_previews_used, key) == -1) {
|
|
let image: gpu_texture_t = map_get(context_raw.node_previews, key);
|
|
iron_delete_texture(image);
|
|
map_delete(context_raw.node_previews, key);
|
|
}
|
|
}
|
|
}
|
|
|
|
function make_material_traverse_nodes(nodes: ui_node_t[], group: ui_node_canvas_t, parents: ui_node_t[]) {
|
|
for (let i: i32 = 0; i < nodes.length; ++i) {
|
|
let node: ui_node_t = nodes[i];
|
|
make_material_bake_node_preview(node, group, parents);
|
|
if (node.type == "GROUP") {
|
|
for (let j: i32 = 0; j < project_material_groups.length; ++j) {
|
|
let g: node_group_t = project_material_groups[j];
|
|
let cname: string = g.canvas.name;
|
|
if (cname == node.name) {
|
|
array_push(parents, node);
|
|
make_material_traverse_nodes(g.canvas.nodes, g.canvas, parents);
|
|
array_pop(parents);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function make_material_bake_node_preview(node: ui_node_t, group: ui_node_canvas_t, parents: ui_node_t[]) {
|
|
if (node.type == "BLUR") {
|
|
let id: string = parser_material_node_name(node, parents);
|
|
let image: gpu_texture_t = map_get(context_raw.node_previews, id);
|
|
array_push(context_raw.node_previews_used, id);
|
|
let res_x: i32 = math_floor(config_get_texture_res_x() / 4);
|
|
let res_y: i32 = math_floor(config_get_texture_res_y() / 4);
|
|
if (image == null || image.width != res_x || image.height != res_y) {
|
|
if (image != null) {
|
|
iron_delete_texture(image);
|
|
}
|
|
image = gpu_create_render_target(res_x, res_y);
|
|
map_set(context_raw.node_previews, id, image);
|
|
}
|
|
|
|
parser_material_blur_passthrough = true;
|
|
util_render_make_node_preview(ui_nodes_get_canvas_material(), node, image, group, parents);
|
|
parser_material_blur_passthrough = false;
|
|
}
|
|
else if (node.type == "DIRECT_WARP") {
|
|
let id: string = parser_material_node_name(node, parents);
|
|
let image: gpu_texture_t = map_get(context_raw.node_previews, id);
|
|
array_push(context_raw.node_previews_used, id);
|
|
let res_x: i32 = math_floor(config_get_texture_res_x());
|
|
let res_y: i32 = math_floor(config_get_texture_res_y());
|
|
if (image == null || image.width != res_x || image.height != res_y) {
|
|
if (image != null) {
|
|
iron_delete_texture(image);
|
|
}
|
|
image = gpu_create_render_target(res_x, res_y);
|
|
map_set(context_raw.node_previews, id, image);
|
|
}
|
|
|
|
parser_material_warp_passthrough = true;
|
|
util_render_make_node_preview(ui_nodes_get_canvas_material(), node, image, group, parents);
|
|
parser_material_warp_passthrough = false;
|
|
}
|
|
else if (node.type == "BAKE_CURVATURE") {
|
|
let id: string = parser_material_node_name(node, parents);
|
|
let image: gpu_texture_t = map_get(context_raw.node_previews, id);
|
|
array_push(context_raw.node_previews_used, id);
|
|
let res_x: i32 = math_floor(config_get_texture_res_x());
|
|
let res_y: i32 = math_floor(config_get_texture_res_y());
|
|
if (image == null || image.width != res_x || image.height != res_y) {
|
|
if (image != null) {
|
|
iron_delete_texture(image);
|
|
}
|
|
image = gpu_create_render_target(res_x, res_y, tex_format_t.R8);
|
|
map_set(context_raw.node_previews, id, image);
|
|
}
|
|
|
|
if (render_path_paint_live_layer == null) {
|
|
render_path_paint_live_layer = slot_layer_create("_live");
|
|
}
|
|
|
|
let _space: i32 = ui_header_worktab.position;
|
|
let _tool: tool_type_t = context_raw.tool;
|
|
let _bake_type: bake_type_t = context_raw.bake_type;
|
|
ui_header_worktab.position = space_type_t.SPACE3D;
|
|
context_raw.tool = tool_type_t.BAKE;
|
|
context_raw.bake_type = bake_type_t.CURVATURE;
|
|
|
|
parser_material_bake_passthrough = true;
|
|
parser_material_start_node = node;
|
|
parser_material_start_group = group;
|
|
parser_material_start_parents = parents;
|
|
make_material_parse_paint_material(false);
|
|
parser_material_bake_passthrough = false;
|
|
parser_material_start_node = null;
|
|
parser_material_start_group = null;
|
|
parser_material_start_parents = null;
|
|
context_raw.pdirty = 1;
|
|
render_path_paint_use_live_layer(true);
|
|
render_path_paint_commands_paint(false);
|
|
render_path_paint_dilate(true, false);
|
|
render_path_paint_use_live_layer(false);
|
|
context_raw.pdirty = 0;
|
|
|
|
ui_header_worktab.position = _space;
|
|
context_raw.tool = _tool;
|
|
context_raw.bake_type = _bake_type;
|
|
make_material_parse_paint_material(false);
|
|
|
|
let rts: map_t<string, render_target_t> = render_path_render_targets;
|
|
let texpaint_live: render_target_t = map_get(rts, "texpaint_live");
|
|
|
|
draw_begin(image);
|
|
draw_image(texpaint_live._image, 0, 0);
|
|
draw_end();
|
|
}
|
|
}
|
|
|
|
type parse_node_preview_result_t = {
|
|
scon: shader_context_t;
|
|
mcon: material_context_t;
|
|
};
|
|
|
|
function make_material_parse_node_preview_material(node: ui_node_t, group: ui_node_canvas_t = null, parents: ui_node_t[] = null): parse_node_preview_result_t {
|
|
if (node.outputs.length == 0) {
|
|
return null;
|
|
}
|
|
let sdata: material_t = {
|
|
name: "Material",
|
|
canvas: ui_nodes_get_canvas_material()
|
|
};
|
|
let mcon_raw: material_context_t = {
|
|
name: "mesh",
|
|
bind_textures: []
|
|
};
|
|
let con: node_shader_context_t = make_node_preview_run(sdata, mcon_raw, node, group, parents);
|
|
let compile_error: bool = false;
|
|
let scon: shader_context_t;
|
|
shader_context_load(con.data);
|
|
if (con.data == null) {
|
|
compile_error = true;
|
|
}
|
|
scon = con.data;
|
|
if (compile_error) {
|
|
return null;
|
|
}
|
|
material_context_load(mcon_raw);
|
|
let mcon: material_context_t = mcon_raw;
|
|
let result: parse_node_preview_result_t = {
|
|
scon: scon,
|
|
mcon: mcon
|
|
};
|
|
return result;
|
|
}
|
|
|
|
function make_material_parse_brush() {
|
|
parser_logic_parse(context_raw.brush.canvas);
|
|
}
|
|
|
|
function make_material_blend_mode(kong: node_shader_t, blending: i32, cola: string, colb: string, opac: string): string {
|
|
if (blending == blend_type_t.MIX) {
|
|
return "lerp3(" + cola + ", " + colb + ", " + opac + ")";
|
|
}
|
|
else if (blending == blend_type_t.DARKEN) {
|
|
return "lerp3(" + cola + ", min3(" + cola + ", " + colb + "), " + opac + ")";
|
|
}
|
|
else if (blending == blend_type_t.MULTIPLY) {
|
|
return "lerp3(" + cola + ", " + cola + " * " + colb + ", " + opac + ")";
|
|
}
|
|
else if (blending == blend_type_t.BURN) {
|
|
return "lerp3(" + cola + ", float3(1.0, 1.0, 1.0) - (float3(1.0, 1.0, 1.0) - " + cola + ") / " + colb + ", " + opac + ")";
|
|
}
|
|
else if (blending == blend_type_t.LIGHTEN) {
|
|
return "max3(" + cola + ", " + colb + " * " + opac + ")";
|
|
}
|
|
else if (blending == blend_type_t.SCREEN) {
|
|
return "(float3(1.0, 1.0, 1.0) - (float3(1.0 - " + opac + ", 1.0 - " + opac + ", 1.0 - " + opac + ") + " + opac + " * (float3(1.0, 1.0, 1.0) - " + colb + ")) * (float3(1.0, 1.0, 1.0) - " + cola + "))";
|
|
}
|
|
else if (blending == blend_type_t.DODGE) {
|
|
return "lerp3(" + cola + ", " + cola + " / (float3(1.0, 1.0, 1.0) - " + colb + "), " + opac + ")";
|
|
}
|
|
else if (blending == blend_type_t.ADD) {
|
|
return "lerp3(" + cola + ", " + cola + " + " + colb + ", " + opac + ")";
|
|
}
|
|
else if (blending == blend_type_t.OVERLAY) {
|
|
return "lerp3(" + cola + ", float3( \
|
|
" + cola + ".r < 0.5 ? 2.0 * " + cola + ".r * " + colb + ".r : 1.0 - 2.0 * (1.0 - " + cola + ".r) * (1.0 - " + colb + ".r), \
|
|
" + cola + ".g < 0.5 ? 2.0 * " + cola + ".g * " + colb + ".g : 1.0 - 2.0 * (1.0 - " + cola + ".g) * (1.0 - " + colb + ".g), \
|
|
" + cola + ".b < 0.5 ? 2.0 * " + cola + ".b * " + colb + ".b : 1.0 - 2.0 * (1.0 - " + cola + ".b) * (1.0 - " + colb + ".b) \
|
|
), " + opac + ")";
|
|
}
|
|
else if (blending == blend_type_t.SOFT_LIGHT) {
|
|
return "((1.0 - " + opac + ") * " + cola + " + " + opac + " * ((float3(1.0, 1.0, 1.0) - " + cola + ") * " + colb + " * " + cola + " + " + cola + " * (float3(1.0, 1.0, 1.0) - (float3(1.0, 1.0, 1.0) - " + colb + ") * (float3(1.0, 1.0, 1.0) - " + cola + "))))";
|
|
}
|
|
else if (blending == blend_type_t.LINEAR_LIGHT) {
|
|
return "(" + cola + " + " + opac + " * (float3(2.0, 2.0, 2.0) * (" + colb + " - float3(0.5, 0.5, 0.5))))";
|
|
}
|
|
else if (blending == blend_type_t.DIFFERENCE) {
|
|
return "lerp3(" + cola + ", abs3(" + cola + " - " + colb + "), " + opac + ")";
|
|
}
|
|
else if (blending == blend_type_t.SUBTRACT) {
|
|
return "lerp3(" + cola + ", " + cola + " - " + colb + ", " + opac + ")";
|
|
}
|
|
else if (blending == blend_type_t.DIVIDE) {
|
|
return "float3(1.0 - " + opac + ", 1.0 - " + opac + ", 1.0 - " + opac + ") * " + cola + " + float3(" + opac + ", " + opac + ", " + opac + ") * " + cola + " / " + colb + "";
|
|
}
|
|
else if (blending == blend_type_t.HUE) {
|
|
node_shader_add_function(kong, str_hue_sat);
|
|
return "lerp3(" + cola + ", hsv_to_rgb(float3(rgb_to_hsv(" + colb + ").r, rgb_to_hsv(" + cola + ").g, rgb_to_hsv(" + cola + ").b)), " + opac + ")";
|
|
}
|
|
else if (blending == blend_type_t.SATURATION) {
|
|
node_shader_add_function(kong, str_hue_sat);
|
|
return "lerp3(" + cola + ", hsv_to_rgb(float3(rgb_to_hsv(" + cola + ").r, rgb_to_hsv(" + colb + ").g, rgb_to_hsv(" + cola + ").b)), " + opac + ")";
|
|
}
|
|
else if (blending == blend_type_t.COLOR) {
|
|
node_shader_add_function(kong, str_hue_sat);
|
|
return "lerp3(" + cola + ", hsv_to_rgb(float3(rgb_to_hsv(" + colb + ").r, rgb_to_hsv(" + colb + ").g, rgb_to_hsv(" + cola + ").b)), " + opac + ")";
|
|
}
|
|
else { // BlendValue
|
|
node_shader_add_function(kong, str_hue_sat);
|
|
return "lerp3(" + cola + ", hsv_to_rgb(float3(rgb_to_hsv(" + cola + ").r, rgb_to_hsv(" + cola + ").g, rgb_to_hsv(" + colb + ").b)), " + opac + ")";
|
|
}
|
|
}
|
|
|
|
function make_material_blend_mode_mask(blending: i32, cola: string, colb: string, opac: string): string {
|
|
if (blending == blend_type_t.MIX) {
|
|
return "lerp(" + cola + ", " + colb + ", " + opac + ")";
|
|
}
|
|
else if (blending == blend_type_t.DARKEN) {
|
|
return "lerp(" + cola + ", min(" + cola + ", " + colb + "), " + opac + ")";
|
|
}
|
|
else if (blending == blend_type_t.MULTIPLY) {
|
|
return "lerp(" + cola + ", " + cola + " * " + colb + ", " + opac + ")";
|
|
}
|
|
else if (blending == blend_type_t.BURN) {
|
|
return "lerp(" + cola + ", 1.0 - (1.0 - " + cola + ") / " + colb + ", " + opac + ")";
|
|
}
|
|
else if (blending == blend_type_t.LIGHTEN) {
|
|
return "max(" + cola + ", " + colb + " * " + opac + ")";
|
|
}
|
|
else if (blending == blend_type_t.SCREEN) {
|
|
return "(1.0 - ((1.0 - " + opac + ") + " + opac + " * (1.0 - " + colb + ")) * (1.0 - " + cola + "))";
|
|
}
|
|
else if (blending == blend_type_t.DODGE) {
|
|
return "lerp(" + cola + ", " + cola + " / (1.0 - " + colb + "), " + opac + ")";
|
|
}
|
|
else if (blending == blend_type_t.ADD) {
|
|
return "lerp(" + cola + ", " + cola + " + " + colb + ", " + opac + ")";
|
|
}
|
|
else if (blending == blend_type_t.OVERLAY) {
|
|
return "lerp(" + cola + ", " + cola + " < 0.5 ? 2.0 * " + cola + " * " + colb + " : 1.0 - 2.0 * (1.0 - " + cola + ") * (1.0 - " + colb + "), " + opac + ")";
|
|
}
|
|
else if (blending == blend_type_t.SOFT_LIGHT) {
|
|
return "((1.0 - " + opac + ") * " + cola + " + " + opac + " * ((1.0 - " + cola + ") * " + colb + " * " + cola + " + " + cola + " * (1.0 - (1.0 - " + colb + ") * (1.0 - " + cola + "))))";
|
|
}
|
|
else if (blending == blend_type_t.LINEAR_LIGHT) {
|
|
return "(" + cola + " + " + opac + " * (2.0 * (" + colb + " - 0.5)))";
|
|
}
|
|
else if (blending == blend_type_t.DIFFERENCE) {
|
|
return "lerp(" + cola + ", abs(" + cola + " - " + colb + "), " + opac + ")";
|
|
}
|
|
else if (blending == blend_type_t.SUBTRACT) {
|
|
return "lerp(" + cola + ", " + cola + " - " + colb + ", " + opac + ")";
|
|
}
|
|
else if (blending == blend_type_t.DIVIDE) {
|
|
return "(1.0 - " + opac + ") * " + cola + " + " + opac + " * " + cola + " / " + colb + "";
|
|
}
|
|
else { // BlendHue, BlendSaturation, BlendColor, BlendValue
|
|
return "lerp(" + cola + ", " + colb + ", " + opac + ")";
|
|
}
|
|
}
|
|
|
|
function make_material_get_displace_strength(): f32 {
|
|
let sc: vec4_t = context_main_object().base.transform.scale;
|
|
return config_raw.displace_strength * 0.02 * sc.x;
|
|
}
|
|
|
|
function make_material_delete_context(c: shader_context_t) {
|
|
sys_notify_on_next_frame(function (c: shader_context_t) { // Ensure pipeline is no longer in use
|
|
shader_context_delete(c);
|
|
}, c);
|
|
}
|