Archived
1
Fork 0

Rip out the material node system

This was over engineered, and it's blocking progress in the model compiler. Now it's
simpler, but not all features are implemented back.
This commit is contained in:
Joshua Goins 2022-02-03 10:00:59 -05:00
parent 61a173e635
commit 8502521934
5 changed files with 57 additions and 431 deletions

View file

@ -2,9 +2,8 @@ set(SRC
include/asset_types.hpp include/asset_types.hpp
include/asset.hpp include/asset.hpp
include/assetptr.hpp include/assetptr.hpp
include/material_nodes.hpp
src/asset.cpp)
src/asset.cpp)
add_library(Asset STATIC ${SRC}) add_library(Asset STATIC ${SRC})
target_include_directories(Asset PUBLIC include) target_include_directories(Asset PUBLIC include)

View file

@ -4,7 +4,6 @@
#include "assetptr.hpp" #include "assetptr.hpp"
#include "math.hpp" #include "math.hpp"
#include "material_nodes.hpp"
#include "aabb.hpp" #include "aabb.hpp"
#include "utility.hpp" #include "utility.hpp"
@ -19,28 +18,31 @@ public:
class GFXPipeline; class GFXPipeline;
enum class DataType {
Vector3,
Float,
AssetTexture
};
struct MaterialProperty {
std::string name;
DataType type;
MaterialProperty() {
name = "";
type = DataType::Vector3;
}
MaterialProperty(std::string n, DataType t) : name(n), type(t) {}
prism::float3 value;
AssetPtr<Texture> value_tex;
float float_value = 0.0f;
};
class Material : public Asset { class Material : public Asset {
public: public:
std::vector<std::unique_ptr<MaterialNode>> nodes; MaterialProperty colorProperty;
void delete_node(MaterialNode* node) {
// disconnect all inputs from their node outputs
for(auto& input : node->inputs) {
if(input.is_connected())
input.disconnect();
}
// disconnect all our outputs from other node's inputs
for(auto& output : node->outputs) {
if(output.is_connected())
output.connected_connector->disconnect();
}
utility::erase_if(nodes, [node](std::unique_ptr<MaterialNode>& other_node) {
return node == other_node.get();
});
}
GFXPipeline* static_pipeline = nullptr; GFXPipeline* static_pipeline = nullptr;
GFXPipeline* skinned_pipeline = nullptr; GFXPipeline* skinned_pipeline = nullptr;
GFXPipeline* capture_pipeline = nullptr; GFXPipeline* capture_pipeline = nullptr;

View file

@ -1,243 +0,0 @@
#pragma once
#include "assetptr.hpp"
#include "render_options.hpp"
class MaterialNode;
enum class DataType {
Vector3,
Float,
AssetTexture
};
struct MaterialConnector {
MaterialConnector(std::string n, DataType t) : name(n), type(t) {}
MaterialConnector(std::string n, DataType t, bool isn) : name(n), type(t), is_normal_map(isn) {}
bool is_connected() const {
return connected_connector != nullptr && connected_node != nullptr && connected_index != -1;
}
void disconnect() {
connected_connector = nullptr;
connected_node = nullptr;
connected_index = -1;
}
std::string name;
DataType type;
bool is_normal_map = false;
MaterialConnector* connected_connector = nullptr;
MaterialNode* connected_node = nullptr;
int connected_index = -1;
};
inline bool operator==(const MaterialConnector& a, const MaterialConnector& b) {
return a.name == b.name;
}
class Texture;
struct MaterialProperty {
std::string name;
DataType type;
MaterialProperty(std::string n, DataType t) : name(n), type(t) {}
prism::float3 value;
AssetPtr<Texture> value_tex;
float float_value = 0.0f;
};
static int last_id = 0;
class MaterialNode {
public:
virtual ~MaterialNode() {}
int id = last_id++;
std::vector<MaterialConnector> inputs, outputs;
std::vector<MaterialProperty> properties;
virtual const char* get_prefix() = 0;
virtual const char* get_name() = 0;
virtual std::string get_glsl() = 0;
std::string get_connector_variable_name(MaterialConnector& connector) {
return std::string(get_prefix()) + std::to_string(id) + "_" + connector.name;
}
std::string get_property_variable_name(MaterialProperty& property) {
return std::string(get_prefix()) + std::to_string(id) + "_" + property.name;
}
std::string get_connector_value(MaterialConnector& connector) {
if(connector.connected_node != nullptr) {
if(connector.type != connector.connected_connector->type) {
return connector.connected_node->get_connector_variable_name(*connector.connected_connector) + ".r";
} else {
if(connector.is_normal_map) {
if(render_options.enable_normal_mapping) {
return "in_tbn * (2.0 * " + connector.connected_node->get_connector_variable_name(*connector.connected_connector) + " - 1.0)";
} else {
return "in_normal";
}
} else {
return connector.connected_node->get_connector_variable_name(*connector.connected_connector);
}
}
} else {
switch(connector.type) {
case DataType::Float:
return "0.0";
default:
return "vec3(1, 1, 1)";
}
}
}
std::string get_property_value(MaterialProperty& property) {
switch(property.type) {
case DataType::Vector3:
return "vec3(" + std::to_string(property.value.x) + ", " + std::to_string(property.value.y) + ", " + std::to_string(property.value.z) + ")";
case DataType::Float:
return std::to_string(property.float_value);
case DataType::AssetTexture:
return "texture(" + get_property_variable_name(property) + ", in_uv).rgb";
}
}
MaterialConnector* find_connector(std::string name) {
for(auto& input : inputs) {
if(input.name == name)
return &input;
}
for(auto& output : outputs) {
if(output.name == name)
return &output;
}
return nullptr;
}
float x = 0.0f, y = 0.0f;
};
class MaterialOutput : public MaterialNode {
public:
MaterialOutput() {
inputs = {
MaterialConnector("Color", DataType::Vector3),
MaterialConnector("Roughness", DataType::Float),
MaterialConnector("Metallic", DataType::Float),
MaterialConnector("Normals", DataType::Vector3, true),
};
}
const char* get_prefix() override {
return "material_output_";
}
const char* get_name() override {
return "Material Output";
}
std::string get_glsl() override {
std::string glsl = "vec3 final_diffuse_color = from_srgb_to_linear(Color);\n \
float final_roughness = Roughness;\n \
float final_metallic = Metallic;\n";
if(find_connector("Normals")->connected_node != nullptr) {
glsl += "vec3 final_normal = Normals;\n";
} else {
glsl += "vec3 final_normal = in_normal;\n";
}
return glsl;
}
};
class Vector3Constant : public MaterialNode {
public:
Vector3Constant() {
outputs = {MaterialConnector("Value", DataType::Vector3)};
properties = {MaterialProperty("Color", DataType::Vector3)};
}
const char* get_prefix() override {
return "vec3const_";
}
const char* get_name() override {
return "Vector3 Constant";
}
std::string get_glsl() override {
return "vec3 Value = Color;\n";
}
};
class FloatConstant : public MaterialNode {
public:
FloatConstant() {
outputs = {MaterialConnector("Value", DataType::Float)};
properties = {MaterialProperty("Data", DataType::Float)};
}
const char* get_prefix() override {
return "floatconst_";
}
const char* get_name() override {
return "Float Constant";
}
std::string get_glsl() override {
return "float Value = Data;\n";
}
};
class TextureNode : public MaterialNode {
public:
TextureNode() {
outputs = {MaterialConnector("Value", DataType::Vector3)};
properties = {MaterialProperty("Texture", DataType::AssetTexture)};
}
const char* get_prefix() override {
return "texture_";
}
const char* get_name() override {
return "Texture";
}
std::string get_glsl() override {
return "vec3 Value = Texture;\n";
}
};
class Geometry : public MaterialNode {
public:
Geometry() {
outputs = {MaterialConnector("Normal", DataType::Vector3)};
}
const char* get_prefix() override {
return "geometry_";
}
const char* get_name() override {
return "Geometry";
}
std::string get_glsl() override {
return "vec3 Normal = normalize(in_normal);\n";
}
};

View file

@ -257,75 +257,17 @@ std::unique_ptr<Material> load_material(const prism::path path) {
auto mat = std::make_unique<Material>(); auto mat = std::make_unique<Material>();
mat->path = path.string(); mat->path = path.string();
if(!j.count("version") || j["version"] != 2) { if(!j.count("version") || j["version"] != 3) {
prism::log("Material {} failed the version check!", path.string()); prism::log("Material {} failed the version check!", path.string());
return mat; return mat;
} }
for(auto node : j["nodes"]) {
std::unique_ptr<MaterialNode> n;
auto name = node["name"];
if(name == "Material Output") {
n = std::make_unique<MaterialOutput>();
} else if(name == "Texture") {
n = std::make_unique<TextureNode>();
} else if(name == "Float Constant") {
n = std::make_unique<FloatConstant>();
} else if(name == "Vector3 Constant") {
n = std::make_unique<Vector3Constant>();
}
n->id = node["id"];
n->x = node["x"];
n->y = node["y"];
for(auto& property : node["properties"]) {
for(auto& p : n->properties) {
if(property["name"] == p.name) {
p.value = property["value"];
p.float_value = property["float_value"];
if(!property["asset_value"].get<std::string>().empty()) {
p.value_tex = assetm->get<Texture>(prism::app_domain / property["asset_value"].get<std::string>());
}
}
}
}
mat->nodes.emplace_back(std::move(n));
}
for(auto node : j["nodes"]) { auto property = j["color"];
MaterialNode* n = nullptr; mat->colorProperty.value = property["value"];
for(auto& nn : mat->nodes) { mat->colorProperty.float_value = property["float_value"];
if(nn->id == node["id"])
n = nn.get(); if(!property["asset_value"].get<std::string>().empty()) {
} mat->colorProperty.value_tex = assetm->get<Texture>(prism::app_domain / property["asset_value"].get<std::string>());
if(n == nullptr)
continue;
for(auto connection : node["connections"]) {
for(auto [i, output] : utility::enumerate(n->outputs)) {
if(connection["name"] == output.name) {
for(auto& nn : mat->nodes) {
if(nn->id == connection["connected_node"]) {
output.connected_node = nn.get();
auto connector = nn->find_connector(connection["connected_connector"]);
output.connected_connector = connector;
connector->connected_index = i;
connector->connected_node = n;
connector->connected_connector = &output;
}
}
output.connected_index = connection["connected_index"];
}
}
}
} }
return mat; return mat;
@ -336,40 +278,15 @@ void save_material(Material* material, const prism::path path) {
Expects(!path.empty()); Expects(!path.empty());
nlohmann::json j; nlohmann::json j;
j["version"] = 3;
j["version"] = 2; nlohmann::json p;
p["name"] = material->colorProperty.name;
p["value"] = material->colorProperty.value;
p["asset_value"] = material->colorProperty.value_tex ? material->colorProperty.value_tex->path : "";
p["float_value"] = material->colorProperty.float_value;
for(auto& node : material->nodes) { j["color"] = p;
nlohmann::json n;
n["name"] = node->get_name();
n["id"] = node->id;
n["x"] = node->x;
n["y"] = node->y;
for(auto property : node->properties) {
nlohmann::json p;
p["name"] = property.name;
p["value"] = property.value;
p["asset_value"] = property.value_tex ? property.value_tex->path : "";
p["float_value"] = property.float_value;
n["properties"].push_back(p);
}
for(const auto& output : node->outputs) {
if(output.connected_connector != nullptr) {
nlohmann::json c;
c["name"] = output.name;
c["connected_connector"] = output.connected_connector->name;
c["connected_node"] = output.connected_node->id;
c["connected_index"] = output.connected_index;
n["connections"].push_back(c);
}
}
j["nodes"].push_back(n);
}
std::ofstream out(path); std::ofstream out(path);
out << j; out << j;

View file

@ -7,7 +7,6 @@
#include "engine.hpp" #include "engine.hpp"
#include "string_utils.hpp" #include "string_utils.hpp"
#include "shadercompiler.hpp" #include "shadercompiler.hpp"
#include "material_nodes.hpp"
#include "renderer.hpp" #include "renderer.hpp"
ShaderSource get_shader(const std::string& filename, bool skinned, bool cubemap) { ShaderSource get_shader(const std::string& filename, bool skinned, bool cubemap) {
@ -129,35 +128,6 @@ std::tuple<GFXPipeline*, GFXPipeline*> MaterialCompiler::create_pipeline_permuta
return {st, ss}; return {st, ss};
} }
std::vector<MaterialNode*> walked_nodes;
void walk_node(std::string& src, MaterialNode* node) {
if(utility::contains(walked_nodes, node))
return;
walked_nodes.push_back(node);
for(auto& input : node->inputs) {
if(input.connected_node != nullptr)
walk_node(src, input.connected_node);
}
std::string intermediate = node->get_glsl();
for(auto& property : node->properties) {
intermediate = replace_substring(intermediate, property.name, node->get_property_value(property));
}
for(auto& input : node->inputs) {
intermediate = replace_substring(intermediate, input.name, node->get_connector_value(input));
}
for(auto& output : node->outputs) {
intermediate = replace_substring(intermediate, output.name, node->get_connector_variable_name(output));
}
src += intermediate;
}
constexpr std::string_view struct_info = constexpr std::string_view struct_info =
"layout (constant_id = 0) const int max_materials = 25;\n \ "layout (constant_id = 0) const int max_materials = 25;\n \
layout (constant_id = 1) const int max_lights = 25;\n \ layout (constant_id = 1) const int max_lights = 25;\n \
@ -192,8 +162,6 @@ layout(push_constant, binding = 0) uniform PushConstant {\n \
};\n"; };\n";
ShaderSource MaterialCompiler::compile_material_fragment(Material& material, bool use_ibl) { ShaderSource MaterialCompiler::compile_material_fragment(Material& material, bool use_ibl) {
walked_nodes.clear();
if(!render_options.enable_ibl) if(!render_options.enable_ibl)
use_ibl = false; use_ibl = false;
@ -241,14 +209,10 @@ ShaderSource MaterialCompiler::compile_material_fragment(Material& material, boo
// insert samplers as needed // insert samplers as needed
int sampler_index = 10; int sampler_index = 10;
for(auto& node : material.nodes) { if(material.colorProperty.type == DataType::AssetTexture) {
for(auto& property : node->properties) { material.bound_textures[sampler_index] = material.colorProperty.value_tex;
if(property.type == DataType::AssetTexture) {
material.bound_textures[sampler_index] = property.value_tex; src += "layout(binding = " + std::to_string(sampler_index++) + ") uniform sampler2D colorTexture;\n";
src += "layout(binding = " + std::to_string(sampler_index++) + ") uniform sampler2D " + node->get_property_variable_name(property) + ";\n";
}
}
} }
if(use_ibl) { if(use_ibl) {
@ -313,30 +277,21 @@ ShaderSource MaterialCompiler::compile_material_fragment(Material& material, boo
} }
src += "void main() {\n"; src += "void main() {\n";
bool has_output = false; if(material.colorProperty.type == DataType::Vector3) {
bool has_normal_mapping = false; src += "vec3 Color = vec3(" + std::to_string(material.colorProperty.value.x) + ","
std::string normal_map_property_name; + std::to_string(material.colorProperty.value.y) + ","
for(auto& node : material.nodes) { + std::to_string(material.colorProperty.value.z) + ");\n";
if(!strcmp(node->get_name(), "Material Output")) { } else if(material.colorProperty.type == DataType::AssetTexture) {
for(auto& input : node->inputs) { src += "vec3 Color = texture(colorTexture, in_uv).rgb;\n";
if(input.is_normal_map && input.connected_node != nullptr) { } else if(material.colorProperty.type == DataType::Float) {
has_normal_mapping = true; src += "vec3 Color = vec3(" + std::to_string(material.colorProperty.float_value) + ");\n";
normal_map_property_name = input.connected_node->get_property_variable_name(input.connected_node->properties[0]); // quick and dirty workaround to get the normal map texture name
}
}
walk_node(src, node.get());
has_output = true;
}
}
if(!has_output) {
src += "vec3 final_diffuse_color = vec3(1);\n";
src += "float final_roughness = 0.5;\n";
src += "float final_metallic = 0.0;\n";
src += "vec3 final_normal = in_normal;\n";
} }
src += "vec3 final_diffuse_color = from_srgb_to_linear(Color);\n";
src += "float final_roughness = 0.5;\n";
src += "float final_metallic = 0.0;\n";
src += "vec3 final_normal = in_normal;\n";
src += src +=
"ComputedSurfaceInfo surface_info = compute_surface(final_diffuse_color.rgb, final_normal, final_metallic, final_roughness);\n \ "ComputedSurfaceInfo surface_info = compute_surface(final_diffuse_color.rgb, final_normal, final_metallic, final_roughness);\n \
@ -356,10 +311,6 @@ ShaderSource MaterialCompiler::compile_material_fragment(Material& material, boo
break;\n \ break;\n \
}\n \ }\n \
SurfaceBRDF surface_brdf = brdf(light_info.direction, surface_info);\n"; SurfaceBRDF surface_brdf = brdf(light_info.direction, surface_info);\n";
if(render_options.enable_normal_mapping && has_normal_mapping && render_options.enable_normal_shadowing) {
src += std::string("light_info.radiance *= calculate_normal_lighting(") + normal_map_property_name + ", final_normal, light_info.direction);\n";
}
src += "Lo += ((surface_brdf.specular + surface_brdf.diffuse) * light_info.radiance * surface_brdf.NdotL) * scene.lights[i].colorSize.rgb;\n \ src += "Lo += ((surface_brdf.specular + surface_brdf.diffuse) * light_info.radiance * surface_brdf.NdotL) * scene.lights[i].colorSize.rgb;\n \
}\n"; }\n";