Archived
1
Fork 0
This repository has been archived on 2025-04-12. You can view files and clone it, but cannot push or open issues or pull requests.
prism/engine/asset/src/asset.cpp

388 lines
12 KiB
C++
Raw Normal View History

2020-08-11 12:07:21 -04:00
#include "asset.hpp"
#include <map>
#include <array>
#include <stb_image.h>
#include "log.hpp"
#include "engine.hpp"
#include "gfx.hpp"
#include "json_conversions.hpp"
#include "gfx_commandbuffer.hpp"
#include "assertions.hpp"
std::unique_ptr<Mesh> load_mesh(const file::Path path) {
Expects(!path.empty());
auto file = file::open(path);
if(!file.has_value()) {
console::error(System::Renderer, "Failed to load mesh from {}!", path);
return nullptr;
}
int version = 0;
file->read(&version);
if(version == 5 || version == 6) {
} else {
console::error(System::Renderer, "{} failed the mesh version check! reported version = {}", path, std::to_string(version));
return nullptr;
}
auto mesh = std::make_unique<Mesh>();
mesh->path = path;
enum MeshType : int {
Static,
Skinned
} mesh_type;
file->read(&mesh_type);
// TODO: use unsigned int here
int numVertices = 0;
file->read(&numVertices);
Expects(numVertices > 0);
const auto read_buffer = [&f = file.value(), numVertices](unsigned int size) -> GFXBuffer* {
auto buffer = engine->get_gfx()->create_buffer(nullptr, size * static_cast<unsigned int>(numVertices), false, GFXBufferUsage::Vertex);
auto buffer_ptr = reinterpret_cast<unsigned char*>(engine->get_gfx()->get_buffer_contents(buffer));
f.read(buffer_ptr, size * static_cast<unsigned int>(numVertices));
engine->get_gfx()->release_buffer_contents(buffer, buffer_ptr);
return buffer;
};
// read positions
mesh->position_buffer = read_buffer(sizeof(Vector3));
mesh->normal_buffer = read_buffer(sizeof(Vector3));
mesh->texture_coord_buffer = read_buffer(sizeof(Vector2));
mesh->tangent_buffer = read_buffer(sizeof(Vector3));
mesh->bitangent_buffer = read_buffer(sizeof(Vector3));
if(mesh_type == MeshType::Skinned)
mesh->bone_buffer = read_buffer(sizeof(BoneVertexData));
int numIndices = 0;
file->read(&numIndices);
Expects(numIndices > 0);
mesh->index_buffer = engine->get_gfx()->create_buffer(nullptr, sizeof(uint32_t) * numIndices, false, GFXBufferUsage::Index);
auto index_ptr = reinterpret_cast<uint32_t*>(engine->get_gfx()->get_buffer_contents(mesh->index_buffer));
file->read(index_ptr, sizeof(uint32_t) * numIndices);
engine->get_gfx()->release_buffer_contents(mesh->index_buffer, index_ptr);
int bone_len = 0;
file->read(&bone_len);
mesh->bones.reserve(bone_len);
if(bone_len > 0) {
file->read(&mesh->global_inverse_transformation);
std::map<std::string, uint32_t> boneMapping;
std::map<int, std::string> parentQueue;
for (int v = 0; v < bone_len; v++) {
std::string bone, parent;
file->read_string(bone);
file->read_string(parent);
Vector3 pos;
file->read(&pos);
Quaternion rot;
file->read(&rot);
Vector3 scl;
file->read(&scl);
int finalBoneIndex = 0;
if(boneMapping.count(bone)) {
finalBoneIndex = boneMapping[bone];
} else {
Bone b;
b.index = mesh->bones.size();
b.name = bone;
b.name = bone;
b.position = pos;
b.rotation = rot;
b.scale = scl;
if(parent != "none" && !parent.empty())
parentQueue[b.index] = parent;
mesh->bones.push_back(b);
boneMapping[bone] = b.index;
finalBoneIndex = b.index;
}
}
for(auto& [index, parentName] : parentQueue) {
for(auto& bone : mesh->bones) {
if(bone.name == parentName)
mesh->bones[index].parent = &bone;
}
}
for(auto& bone : mesh->bones) {
if(bone.parent == nullptr)
mesh->root_bone = &bone;
}
}
int numMeshes = 0;
file->read(&numMeshes);
Expects(numMeshes > 0);
mesh->parts.resize(numMeshes);
uint32_t vertexOffset = 0, indexOffset = 0;
for(int i = 0; i < numMeshes; i++) {
auto& p = mesh->parts[i];
p.vertex_offset = vertexOffset;
p.index_offset = indexOffset;
file->read_string(p.name);
if(version == 6) {
file->read(&p.aabb);
}
int numVerts = 0;
file->read(&numVerts);
file->read(&p.index_count);
int numBones = 0;
file->read(&numBones);
p.bone_batrix_buffer = engine->get_gfx()->create_buffer(nullptr, sizeof(Matrix4x4) * 128, true, GFXBufferUsage::Storage);
if(numBones > 0) {
p.offset_matrices.resize(numBones);
file->read(p.offset_matrices.data(), sizeof(Matrix4x4) * numBones);
}
file->read(&p.material_override);
vertexOffset += numVerts;
indexOffset += p.index_count;
}
mesh->num_indices = static_cast<uint32_t>(numIndices);
return mesh;
}
std::unique_ptr<Texture> load_texture(const file::Path path) {
Expects(!path.empty());
auto file = file::open(path);
if(!file.has_value()) {
console::error(System::Renderer, "Failed to load texture from {}!", path);
return nullptr;
}
bool should_generate_mipmaps = true;
auto texture_info_path = path;
texture_info_path.replace_extension(".json");
auto json_file = file::open(texture_info_path);
if(json_file != std::nullopt) {
nlohmann::json j;
json_file->read_as_stream() >> j;
should_generate_mipmaps = j["generate_mipmaps"];
}
file->read_all();
int width, height, channels;
unsigned char* data = stbi_load_from_memory(file->cast_data<unsigned char>(), file->size(), &width, &height, &channels, 4);
if(!data) {
console::error(System::Renderer, "Failed to load texture from {}!", path);
return nullptr;
}
Expects(width > 0);
Expects(height > 0);
auto texture = std::make_unique<Texture>();
texture->path = path;
texture->width = width;
texture->height = height;
GFXTextureCreateInfo createInfo = {};
createInfo.width = width;
createInfo.height = height;
createInfo.format = GFXPixelFormat::R8G8B8A8_UNORM;
createInfo.usage = GFXTextureUsage::Sampled;
if(should_generate_mipmaps)
createInfo.mip_count = std::floor(std::log2(std::max(width, height))) + 1;
texture->handle = engine->get_gfx()->create_texture(createInfo);
engine->get_gfx()->copy_texture(texture->handle, data, width * height * 4);
if(createInfo.mip_count > 1) {
GFXCommandBuffer* cmd_buf = engine->get_gfx()->acquire_command_buffer();
cmd_buf->generate_mipmaps(texture->handle, createInfo.mip_count);
engine->get_gfx()->submit(cmd_buf);
}
stbi_image_free(data);
return texture;
}
std::unique_ptr<Material> load_material(const file::Path path) {
Expects(!path.empty());
auto file = file::open(path);
if(!file.has_value()) {
console::error(System::Core, "Failed to load material from {}!", path);
return {};
}
nlohmann::json j;
file->read_as_stream() >> j;
auto mat = std::make_unique<Material>();
mat->path = path;
if(!j.count("version") || j["version"] != 2) {
console::error(System::Core, "Material {} failed the version check!", path);
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>();
}
n->id = node["id"];
n->x = node["x"];
n->y = node["y"];
n->width = node["width"];
n->height = node["height"];
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>(file::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"];
}
}
}
}
return mat;
}
void save_material(Material* material, const std::string_view path) {
Expects(material != nullptr);
Expects(!path.empty());
nlohmann::json j;
j["version"] = 2;
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;
n["width"] = node->width;
n["height"] = node->height;
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(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.data());
out << j;
}