313 lines
9 KiB
C++
313 lines
9 KiB
C++
#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"
|
|
#include "renderer.hpp"
|
|
#include "input.hpp"
|
|
#include "physics.hpp"
|
|
#include "imgui_backend.hpp"
|
|
|
|
std::unique_ptr<Mesh> load_mesh(const prism::path path) {
|
|
Expects(!path.empty());
|
|
|
|
auto file = prism::open_file(path, true);
|
|
if(!file.has_value()) {
|
|
prism::log("Failed to load mesh from {}!", path.string());
|
|
return nullptr;
|
|
}
|
|
|
|
int version = 0;
|
|
file->read(&version);
|
|
|
|
if(version == 5 || version == 6 || version == 7) {
|
|
} else {
|
|
prism::log("{} failed the mesh version check! reported version = {}", path.string(), version);
|
|
return nullptr;
|
|
}
|
|
|
|
auto mesh = std::make_unique<Mesh>();
|
|
mesh->path = path.string();
|
|
|
|
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(prism::float3));
|
|
mesh->normal_buffer = read_buffer(sizeof(prism::float3));
|
|
mesh->texture_coord_buffer = read_buffer(sizeof(prism::float2));
|
|
mesh->tangent_buffer = read_buffer(sizeof(prism::float3));
|
|
mesh->bitangent_buffer = read_buffer(sizeof(prism::float3));
|
|
|
|
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);
|
|
|
|
prism::float3 pos;
|
|
file->read(&pos);
|
|
|
|
Quaternion rot;
|
|
file->read(&rot);
|
|
|
|
prism::float3 scl;
|
|
file->read(&scl);
|
|
|
|
if(!boneMapping.count(bone)) {
|
|
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;
|
|
}
|
|
}
|
|
|
|
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.bounding_box);
|
|
}
|
|
|
|
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);
|
|
|
|
if(version == 7) {
|
|
file->read_string(p.material_hint);
|
|
}
|
|
|
|
vertexOffset += numVerts;
|
|
indexOffset += p.index_count;
|
|
}
|
|
|
|
mesh->num_indices = static_cast<uint32_t>(numIndices);
|
|
|
|
return mesh;
|
|
}
|
|
|
|
std::unique_ptr<Texture> load_texture(const prism::path path) {
|
|
Expects(!path.empty());
|
|
|
|
auto file = prism::open_file(path, true);
|
|
if(!file.has_value()) {
|
|
prism::log("Failed to load texture from {}!", path.string());
|
|
return nullptr;
|
|
}
|
|
|
|
// TODO: expose somehow??
|
|
const bool should_generate_mipmaps = true;
|
|
|
|
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) {
|
|
prism::log("Failed to load texture from {}!", path.string());
|
|
return nullptr;
|
|
}
|
|
|
|
Expects(width > 0);
|
|
Expects(height > 0);
|
|
|
|
auto texture = std::make_unique<Texture>();
|
|
texture->path = path.string();
|
|
texture->width = width;
|
|
texture->height = height;
|
|
|
|
GFXTextureCreateInfo createInfo = {};
|
|
createInfo.label = path.string();
|
|
createInfo.width = width;
|
|
createInfo.height = height;
|
|
createInfo.format = GFXPixelFormat::R8G8B8A8_UNORM;
|
|
createInfo.usage = GFXTextureUsage::Sampled | GFXTextureUsage::TransferDst | GFXTextureUsage::TransferSrc; // src and dst are needed for copy tex data -> image and mipmap gen (from image data) respectively
|
|
|
|
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 prism::path path) {
|
|
Expects(!path.empty());
|
|
|
|
auto file = prism::open_file(path);
|
|
if(!file.has_value()) {
|
|
prism::log("Failed to load material from {}!", path.string());
|
|
return {};
|
|
}
|
|
|
|
nlohmann::json j;
|
|
file->read_as_stream() >> j;
|
|
|
|
auto mat = std::make_unique<Material>();
|
|
mat->path = path.string();
|
|
|
|
if(!j.count("version") || j["version"] != 3) {
|
|
prism::log("Material {} failed the version check!", path.string());
|
|
return mat;
|
|
}
|
|
|
|
const auto loadProperty = [](nlohmann::json property) -> MaterialProperty {
|
|
MaterialProperty p;
|
|
p.value = property["value"];
|
|
p.float_value = property["float_value"];
|
|
p.type = property["type"];
|
|
|
|
if(!property["asset_value"].get<std::string>().empty()) {
|
|
p.value_tex = assetm->get<Texture>(prism::app_domain / property["asset_value"].get<std::string>());
|
|
}
|
|
|
|
return p;
|
|
};
|
|
|
|
mat->colorProperty = loadProperty(j["color"]);
|
|
|
|
if(j.contains("normal"))
|
|
mat->normalProperty = loadProperty(j["normal"]);
|
|
|
|
return mat;
|
|
}
|
|
|
|
void save_material(Material* material, const prism::path path) {
|
|
Expects(material != nullptr);
|
|
Expects(!path.empty());
|
|
|
|
nlohmann::json j;
|
|
j["version"] = 3;
|
|
|
|
const auto save_property = [](MaterialProperty& property) -> nlohmann::json {
|
|
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;
|
|
p["type"] = property.type;
|
|
|
|
return p;
|
|
};
|
|
|
|
j["color"] = save_property(material->colorProperty);
|
|
j["normal"] = save_property(material->normalProperty);
|
|
|
|
std::ofstream out(path);
|
|
out << j;
|
|
}
|