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:
parent
61a173e635
commit
8502521934
5 changed files with 57 additions and 431 deletions
|
@ -2,9 +2,8 @@ set(SRC
|
|||
include/asset_types.hpp
|
||||
include/asset.hpp
|
||||
include/assetptr.hpp
|
||||
include/material_nodes.hpp
|
||||
|
||||
src/asset.cpp)
|
||||
src/asset.cpp)
|
||||
|
||||
add_library(Asset STATIC ${SRC})
|
||||
target_include_directories(Asset PUBLIC include)
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
#include "assetptr.hpp"
|
||||
#include "math.hpp"
|
||||
#include "material_nodes.hpp"
|
||||
#include "aabb.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
|
@ -19,27 +18,30 @@ public:
|
|||
|
||||
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 {
|
||||
public:
|
||||
std::vector<std::unique_ptr<MaterialNode>> nodes;
|
||||
|
||||
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();
|
||||
});
|
||||
}
|
||||
MaterialProperty colorProperty;
|
||||
|
||||
GFXPipeline* static_pipeline = nullptr;
|
||||
GFXPipeline* skinned_pipeline = nullptr;
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
};
|
|
@ -257,75 +257,17 @@ std::unique_ptr<Material> load_material(const prism::path path) {
|
|||
auto mat = std::make_unique<Material>();
|
||||
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());
|
||||
return mat;
|
||||
}
|
||||
|
||||
for(auto node : j["nodes"]) {
|
||||
std::unique_ptr<MaterialNode> n;
|
||||
auto property = j["color"];
|
||||
mat->colorProperty.value = property["value"];
|
||||
mat->colorProperty.float_value = property["float_value"];
|
||||
|
||||
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"]) {
|
||||
MaterialNode* n = nullptr;
|
||||
for(auto& nn : mat->nodes) {
|
||||
if(nn->id == node["id"])
|
||||
n = nn.get();
|
||||
}
|
||||
|
||||
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"];
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!property["asset_value"].get<std::string>().empty()) {
|
||||
mat->colorProperty.value_tex = assetm->get<Texture>(prism::app_domain / property["asset_value"].get<std::string>());
|
||||
}
|
||||
|
||||
return mat;
|
||||
|
@ -336,40 +278,15 @@ void save_material(Material* material, const prism::path path) {
|
|||
Expects(!path.empty());
|
||||
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
j["color"] = p;
|
||||
|
||||
std::ofstream out(path);
|
||||
out << j;
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#include "engine.hpp"
|
||||
#include "string_utils.hpp"
|
||||
#include "shadercompiler.hpp"
|
||||
#include "material_nodes.hpp"
|
||||
#include "renderer.hpp"
|
||||
|
||||
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};
|
||||
}
|
||||
|
||||
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 =
|
||||
"layout (constant_id = 0) const int max_materials = 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";
|
||||
|
||||
ShaderSource MaterialCompiler::compile_material_fragment(Material& material, bool use_ibl) {
|
||||
walked_nodes.clear();
|
||||
|
||||
if(!render_options.enable_ibl)
|
||||
use_ibl = false;
|
||||
|
||||
|
@ -241,14 +209,10 @@ ShaderSource MaterialCompiler::compile_material_fragment(Material& material, boo
|
|||
|
||||
// insert samplers as needed
|
||||
int sampler_index = 10;
|
||||
for(auto& node : material.nodes) {
|
||||
for(auto& property : node->properties) {
|
||||
if(property.type == DataType::AssetTexture) {
|
||||
material.bound_textures[sampler_index] = property.value_tex;
|
||||
if(material.colorProperty.type == DataType::AssetTexture) {
|
||||
material.bound_textures[sampler_index] = material.colorProperty.value_tex;
|
||||
|
||||
src += "layout(binding = " + std::to_string(sampler_index++) + ") uniform sampler2D " + node->get_property_variable_name(property) + ";\n";
|
||||
}
|
||||
}
|
||||
src += "layout(binding = " + std::to_string(sampler_index++) + ") uniform sampler2D colorTexture;\n";
|
||||
}
|
||||
|
||||
if(use_ibl) {
|
||||
|
@ -314,29 +278,20 @@ ShaderSource MaterialCompiler::compile_material_fragment(Material& material, boo
|
|||
|
||||
src += "void main() {\n";
|
||||
|
||||
bool has_output = false;
|
||||
bool has_normal_mapping = false;
|
||||
std::string normal_map_property_name;
|
||||
for(auto& node : material.nodes) {
|
||||
if(!strcmp(node->get_name(), "Material Output")) {
|
||||
for(auto& input : node->inputs) {
|
||||
if(input.is_normal_map && input.connected_node != nullptr) {
|
||||
has_normal_mapping = true;
|
||||
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(material.colorProperty.type == DataType::Vector3) {
|
||||
src += "vec3 Color = vec3(" + std::to_string(material.colorProperty.value.x) + ","
|
||||
+ std::to_string(material.colorProperty.value.y) + ","
|
||||
+ std::to_string(material.colorProperty.value.z) + ");\n";
|
||||
} else if(material.colorProperty.type == DataType::AssetTexture) {
|
||||
src += "vec3 Color = texture(colorTexture, in_uv).rgb;\n";
|
||||
} else if(material.colorProperty.type == DataType::Float) {
|
||||
src += "vec3 Color = vec3(" + std::to_string(material.colorProperty.float_value) + ");\n";
|
||||
}
|
||||
|
||||
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 +=
|
||||
"ComputedSurfaceInfo surface_info = compute_surface(final_diffuse_color.rgb, final_normal, final_metallic, final_roughness);\n \
|
||||
|
@ -357,10 +312,6 @@ ShaderSource MaterialCompiler::compile_material_fragment(Material& material, boo
|
|||
}\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 \
|
||||
}\n";
|
||||
|
||||
|
|
Reference in a new issue