Added Billboard Node to Visual Shaders

This commit is contained in:
Yuri Roubinsky 2021-05-27 22:17:58 +03:00
parent 364ea7f280
commit f06db8b778
7 changed files with 242 additions and 1 deletions

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualShaderNodeBillboard" inherits="VisualShaderNode" version="4.0">
<brief_description>
A node that controls how the object faces the camera to be used within the visual shader graph.
</brief_description>
<description>
The output port of this node needs to be connected to [code]Model View Matrix[/code] port of [VisualShaderNodeOutput].
</description>
<tutorials>
</tutorials>
<methods>
</methods>
<members>
<member name="billboard_type" type="int" setter="set_billboard_type" getter="get_billboard_type" enum="VisualShaderNodeBillboard.BillboardType" default="1">
Controls how the object faces the camera. See [enum BillboardType].
</member>
<member name="keep_scale" type="bool" setter="set_keep_scale_enabled" getter="is_keep_scale_enabled" default="false">
If [code]true[/code], the shader will keep the scale set for the mesh. Otherwise, the scale is lost when billboarding.
</member>
</members>
<constants>
<constant name="BILLBOARD_TYPE_DISABLED" value="0" enum="BillboardType">
Billboarding is disabled and the node does nothing.
</constant>
<constant name="BILLBOARD_TYPE_ENABLED" value="1" enum="BillboardType">
A standard billboarding algorithm is enabled.
</constant>
<constant name="BILLBOARD_TYPE_FIXED_Y" value="2" enum="BillboardType">
A billboarding algorithm to rotate around Y-axis is enabled.
</constant>
<constant name="BILLBOARD_TYPE_PARTICLES" value="3" enum="BillboardType">
A billboarding algorithm designed to use on particles is enabled.
</constant>
<constant name="BILLBOARD_TYPE_MAX" value="4" enum="BillboardType">
Represents the size of the [enum BillboardType] enum.
</constant>
</constants>
</class>

View File

@ -4260,6 +4260,7 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("TransformDecompose", "Transform", "Composition", "VisualShaderNodeTransformDecompose", TTR("Decomposes transform to four vectors.")));
add_options.push_back(AddOption("Determinant", "Transform", "Functions", "VisualShaderNodeDeterminant", TTR("Calculates the determinant of a transform."), -1, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("GetBillboardMatrix", "Transform", "Functions", "VisualShaderNodeBillboard", TTR("Calculates how the object should face the camera to be applied on Model View Matrix output port for 3D objects."), -1, VisualShaderNode::PORT_TYPE_TRANSFORM, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("Inverse", "Transform", "Functions", "VisualShaderNodeTransformFunc", TTR("Calculates the inverse of a transform."), VisualShaderNodeTransformFunc::FUNC_INVERSE, VisualShaderNode::PORT_TYPE_TRANSFORM));
add_options.push_back(AddOption("Transpose", "Transform", "Functions", "VisualShaderNodeTransformFunc", TTR("Calculates the transpose of a transform."), VisualShaderNodeTransformFunc::FUNC_TRANSPOSE, VisualShaderNode::PORT_TYPE_TRANSFORM));

View File

@ -597,6 +597,7 @@ void register_scene_types() {
ClassDB::register_class<VisualShaderNodeIs>();
ClassDB::register_class<VisualShaderNodeCompare>();
ClassDB::register_class<VisualShaderNodeMultiplyAdd>();
ClassDB::register_class<VisualShaderNodeBillboard>();
ClassDB::register_class<VisualShaderNodeSDFToScreenUV>();
ClassDB::register_class<VisualShaderNodeScreenUVToSDF>();

View File

@ -152,6 +152,14 @@ bool VisualShaderNode::is_use_prop_slots() const {
return false;
}
bool VisualShaderNode::is_disabled() const {
return disabled;
}
void VisualShaderNode::set_disabled(bool p_disabled) {
disabled = p_disabled;
}
Vector<VisualShader::DefaultTextureParam> VisualShaderNode::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const {
return Vector<VisualShader::DefaultTextureParam>();
}
@ -1260,6 +1268,12 @@ void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const {
Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBuilder &global_code_per_node, Map<Type, StringBuilder> &global_code_per_func, StringBuilder &code, Vector<VisualShader::DefaultTextureParam> &def_tex_params, const VMap<ConnectionKey, const List<Connection>::Element *> &input_connections, const VMap<ConnectionKey, const List<Connection>::Element *> &output_connections, int node, Set<int> &processed, bool for_preview, Set<StringName> &r_classes) const {
const Ref<VisualShaderNode> vsnode = graph[type].nodes[node].node;
if (vsnode->is_disabled()) {
code += "// " + vsnode->get_caption() + ":" + itos(node) + "\n";
code += "\t// Node is disabled and code is not generated.\n";
return OK;
}
//check inputs recursively first
int input_count = vsnode->get_input_port_count();
for (int i = 0; i < input_count; i++) {
@ -1328,6 +1342,11 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
if (input_connections.has(ck)) {
//connected to something, use that output
int from_node = input_connections[ck]->get().from_node;
if (graph[type].nodes[from_node].node->is_disabled()) {
continue;
}
int from_port = input_connections[ck]->get().from_port;
VisualShaderNode::PortType in_type = vsnode->get_input_port_type(i);
@ -2531,6 +2550,8 @@ const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = {
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "model_view_matrix", "MODELVIEW_MATRIX" },
// Spatial, Fragment
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "albedo", "ALBEDO" },
@ -2670,9 +2691,13 @@ String VisualShaderNodeOutput::get_output_port_name(int p_port) const {
}
bool VisualShaderNodeOutput::is_port_separator(int p_index) const {
if (shader_mode == Shader::MODE_SPATIAL && shader_type == VisualShader::TYPE_VERTEX) {
String name = get_input_port_name(p_index);
return bool(name == "Model View Matrix");
}
if (shader_mode == Shader::MODE_SPATIAL && shader_type == VisualShader::TYPE_FRAGMENT) {
String name = get_input_port_name(p_index);
return (name == "Normal" || name == "Rim" || name == "Alpha Scissor Threshold");
return bool(name == "Normal" || name == "Rim" || name == "Alpha Scissor Threshold");
}
return false;
}

View File

@ -203,6 +203,8 @@ class VisualShaderNode : public Resource {
protected:
bool simple_decl = true;
bool disabled = false;
static void _bind_methods();
public:
@ -257,6 +259,9 @@ public:
virtual bool is_show_prop_names() const;
virtual bool is_use_prop_slots() const;
bool is_disabled() const;
void set_disabled(bool p_disabled = true);
virtual Vector<StringName> get_editable_properties() const;
virtual Vector<VisualShader::DefaultTextureParam> get_default_texture_parameters(VisualShader::Type p_type, int p_id) const;

View File

@ -5579,3 +5579,127 @@ VisualShaderNodeMultiplyAdd::VisualShaderNodeMultiplyAdd() {
set_input_port_default_value(1, 0.0);
set_input_port_default_value(2, 0.0);
}
////////////// Billboard
String VisualShaderNodeBillboard::get_caption() const {
return "GetBillboardMatrix";
}
int VisualShaderNodeBillboard::get_input_port_count() const {
return 0;
}
VisualShaderNodeBillboard::PortType VisualShaderNodeBillboard::get_input_port_type(int p_port) const {
return PORT_TYPE_SCALAR;
}
String VisualShaderNodeBillboard::get_input_port_name(int p_port) const {
return "";
}
int VisualShaderNodeBillboard::get_output_port_count() const {
return 1;
}
VisualShaderNodeBillboard::PortType VisualShaderNodeBillboard::get_output_port_type(int p_port) const {
return PORT_TYPE_TRANSFORM;
}
String VisualShaderNodeBillboard::get_output_port_name(int p_port) const {
return "model_view_matrix";
}
String VisualShaderNodeBillboard::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
String code;
switch (billboard_type) {
case BILLBOARD_TYPE_ENABLED:
code += "\t{\n";
code += "\t\tmat4 __mvm = INV_CAMERA_MATRIX * mat4(CAMERA_MATRIX[0], CAMERA_MATRIX[1], CAMERA_MATRIX[2], WORLD_MATRIX[3]);\n";
if (keep_scale) {
code += "\t\t__mvm = __mvm * mat4(vec4(length(WORLD_MATRIX[0].xyz), 0.0, 0.0, 0.0), vec4(0.0, length(WORLD_MATRIX[1].xyz), 0.0, 0.0), vec4(0.0, 0.0, length(WORLD_MATRIX[2].xyz), 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n";
}
code += "\t\t" + p_output_vars[0] + " = __mvm;\n";
code += "\t}\n";
break;
case BILLBOARD_TYPE_FIXED_Y:
code += "\t{\n";
code += "\t\tmat4 __mvm = INV_CAMERA_MATRIX * mat4(CAMERA_MATRIX[0], WORLD_MATRIX[1], vec4(normalize(cross(CAMERA_MATRIX[0].xyz, WORLD_MATRIX[1].xyz)), 0.0), WORLD_MATRIX[3]);\n";
if (keep_scale) {
code += "\t\t__mvm = __mvm * mat4(vec4(length(WORLD_MATRIX[0].xyz), 0.0, 0.0, 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(0.0, 0.0, length(WORLD_MATRIX[2].xyz), 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n";
} else {
code += "\t\t__mvm = __mvm * mat4(vec4(1.0, 0.0, 0.0, 0.0), vec4(0.0, 1.0 / length(WORLD_MATRIX[1].xyz), 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n";
}
code += "\t\t" + p_output_vars[0] + " = __mvm;\n";
code += "\t}\n";
break;
case BILLBOARD_TYPE_PARTICLES:
code += "\t{\n";
code += "\t\tmat4 __wm = mat4(normalize(CAMERA_MATRIX[0]) * length(WORLD_MATRIX[0]), normalize(CAMERA_MATRIX[1]) * length(WORLD_MATRIX[0]), normalize(CAMERA_MATRIX[2]) * length(WORLD_MATRIX[2]), WORLD_MATRIX[3]);\n";
code += "\t\t__wm = __wm * mat4(vec4(cos(INSTANCE_CUSTOM.x), -sin(INSTANCE_CUSTOM.x), 0.0, 0.0), vec4(sin(INSTANCE_CUSTOM.x), cos(INSTANCE_CUSTOM.x), 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n";
code += "\t\t" + p_output_vars[0] + " = INV_CAMERA_MATRIX * __wm;\n";
code += "\t}\n";
break;
default:
code += "\t" + p_output_vars[0] + " = mat4(1.0);\n";
break;
}
return code;
}
bool VisualShaderNodeBillboard::is_show_prop_names() const {
return true;
}
void VisualShaderNodeBillboard::set_billboard_type(BillboardType p_billboard_type) {
ERR_FAIL_INDEX((int)p_billboard_type, BILLBOARD_TYPE_MAX);
billboard_type = p_billboard_type;
simple_decl = bool(billboard_type == BILLBOARD_TYPE_DISABLED);
set_disabled(simple_decl);
emit_changed();
}
VisualShaderNodeBillboard::BillboardType VisualShaderNodeBillboard::get_billboard_type() const {
return billboard_type;
}
void VisualShaderNodeBillboard::set_keep_scale_enabled(bool p_enabled) {
keep_scale = p_enabled;
emit_changed();
}
bool VisualShaderNodeBillboard::is_keep_scale_enabled() const {
return keep_scale;
}
Vector<StringName> VisualShaderNodeBillboard::get_editable_properties() const {
Vector<StringName> props;
props.push_back("billboard_type");
if (billboard_type == BILLBOARD_TYPE_ENABLED || billboard_type == BILLBOARD_TYPE_FIXED_Y) {
props.push_back("keep_scale");
}
return props;
}
void VisualShaderNodeBillboard::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_billboard_type", "billboard_type"), &VisualShaderNodeBillboard::set_billboard_type);
ClassDB::bind_method(D_METHOD("get_billboard_type"), &VisualShaderNodeBillboard::get_billboard_type);
ClassDB::bind_method(D_METHOD("set_keep_scale_enabled", "enabled"), &VisualShaderNodeBillboard::set_keep_scale_enabled);
ClassDB::bind_method(D_METHOD("is_keep_scale_enabled"), &VisualShaderNodeBillboard::is_keep_scale_enabled);
ADD_PROPERTY(PropertyInfo(Variant::INT, "billboard_type", PROPERTY_HINT_ENUM, "Disabled,Enabled,Y-Billboard,Particles"), "set_billboard_type", "get_billboard_type");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keep_scale"), "set_keep_scale_enabled", "is_keep_scale_enabled");
BIND_ENUM_CONSTANT(BILLBOARD_TYPE_DISABLED);
BIND_ENUM_CONSTANT(BILLBOARD_TYPE_ENABLED);
BIND_ENUM_CONSTANT(BILLBOARD_TYPE_FIXED_Y);
BIND_ENUM_CONSTANT(BILLBOARD_TYPE_PARTICLES);
BIND_ENUM_CONSTANT(BILLBOARD_TYPE_MAX);
}
VisualShaderNodeBillboard::VisualShaderNodeBillboard() {
simple_decl = false;
}

View File

@ -2217,4 +2217,51 @@ public:
VARIANT_ENUM_CAST(VisualShaderNodeMultiplyAdd::OpType)
class VisualShaderNodeBillboard : public VisualShaderNode {
GDCLASS(VisualShaderNodeBillboard, VisualShaderNode);
public:
enum BillboardType {
BILLBOARD_TYPE_DISABLED,
BILLBOARD_TYPE_ENABLED,
BILLBOARD_TYPE_FIXED_Y,
BILLBOARD_TYPE_PARTICLES,
BILLBOARD_TYPE_MAX,
};
protected:
BillboardType billboard_type = BILLBOARD_TYPE_ENABLED;
bool keep_scale = false;
protected:
static void _bind_methods();
public:
virtual String get_caption() const override;
virtual int get_input_port_count() const override;
virtual PortType get_input_port_type(int p_port) const override;
virtual String get_input_port_name(int p_port) const override;
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
virtual bool is_show_prop_names() const override;
void set_billboard_type(BillboardType p_billboard_type);
BillboardType get_billboard_type() const;
void set_keep_scale_enabled(bool p_enabled);
bool is_keep_scale_enabled() const;
virtual Vector<StringName> get_editable_properties() const override;
VisualShaderNodeBillboard();
};
VARIANT_ENUM_CAST(VisualShaderNodeBillboard::BillboardType)
#endif // VISUAL_SHADER_NODES_H