Skip to content
Snippets Groups Projects
Commit cd353119 authored by Tobias Frisch's avatar Tobias Frisch
Browse files

Merge branch '63-laden-mehrer-meshes-mit-materials-und-textures' into 'develop'

Resolve "Laden mehrer Meshes mit Materials und Textures"

Closes #63

See merge request !51
parents 3a5e99cd 70fa675d
No related branches found
No related tags found
1 merge request!51Resolve "Laden mehrer Meshes mit Materials und Textures"
Pipeline #25766 passed
Showing
with 496 additions and 197 deletions
...@@ -63,4 +63,4 @@ namespace vkcv{ ...@@ -63,4 +63,4 @@ namespace vkcv{
std::vector<VertexBinding> vertexBindings; std::vector<VertexBinding> vertexBindings;
}; };
} }
\ No newline at end of file
#pragma once #pragma once
/** /**
* @authors Trevor Hollmann * @authors Trevor Hollmann, Mara Vogt, Susanne Dötsch
* @file include/vkcv/asset/asset_loader.h * @file include/vkcv/asset/asset_loader.h
* @brief Interface of the asset loader module for the vkcv framework. * @brief Interface of the asset loader module for the vkcv framework.
*/ */
#include <string> #include <string>
#include <vector> #include <vector>
#include <array>
#include <cstdint> #include <cstdint>
/* These macros define limits of the following structs. Implementations can /** These macros define limits of the following structs. Implementations can
* test against these limits when performing sanity checks. The main constraint * test against these limits when performing sanity checks. The main constraint
* expressed is that of the data type: Material indices are identified by a * expressed is that of the data type: Material indices are identified by a
* uint8_t in the VertexGroup struct, so there can't be more than UINT8_MAX * uint8_t in the VertexGroup struct, so there can't be more than UINT8_MAX
...@@ -19,17 +20,18 @@ ...@@ -19,17 +20,18 @@
#define MAX_MATERIALS_PER_MESH UINT8_MAX #define MAX_MATERIALS_PER_MESH UINT8_MAX
#define MAX_VERTICES_PER_VERTEX_GROUP UINT32_MAX #define MAX_VERTICES_PER_VERTEX_GROUP UINT32_MAX
/* LOADING MESHES /** LOADING MESHES
* The description of meshes is a hierarchy of structures with the Mesh at the * The description of meshes is a hierarchy of structures with the Mesh at the
* top. * top.
* *
* Each Mesh has an array of one or more vertex groups (called "primitives" in * Each Mesh has an array of one or more vertex groups (called "primitives" in
* glTF parlance) and an array of zero or more Materials. * glTF parlance). Specifically, it has an array of indices into an array of
* vertex groups defined by the Scene struct.
* *
* Each vertex group describes a part of the meshes vertices by defining how * Each vertex group describes a part of the meshes vertices by defining how
* they should be rendered (as points, lines, triangles), how many indices and * they should be rendered (as points, lines, triangles), how many indices and
* vertices there are, how the content of the vertex buffer is to be * vertices there are, how the content of the vertex buffer is to be
* interpreted and which material from the Meshes materials array should be * interpreted and which material from the Scenes materials array should be
* used for the surface of the vertices. * used for the surface of the vertices.
* As a bonus there is also the axis aligned bounding box of the vertices. * As a bonus there is also the axis aligned bounding box of the vertices.
* *
...@@ -43,37 +45,99 @@ ...@@ -43,37 +45,99 @@
namespace vkcv::asset { namespace vkcv::asset {
/* This enum matches modes in fx-gltf, the library returns a standard mode /** This enum matches modes in fx-gltf, the library returns a standard mode
* (TRIANGLES) if no mode is given in the file. */ * (TRIANGLES) if no mode is given in the file. */
enum PrimitiveMode { enum class PrimitiveMode : uint8_t {
POINTS=0, LINES, LINELOOP, LINESTRIP, TRIANGLES, TRIANGLESTRIP, POINTS=0, LINES, LINELOOP, LINESTRIP, TRIANGLES, TRIANGLESTRIP,
TRIANGLEFAN TRIANGLEFAN
}; };
/* The indices in the index buffer can be of different bit width. */
enum IndexType { UINT32=0, UINT16=1, UINT8=2 };
/* With these enums, 0 is reserved to signal uninitialized or invalid data. */ /** The indices in the index buffer can be of different bit width. */
enum class IndexType : uint8_t { UNDEFINED=0, UINT8=1, UINT16=2, UINT32=3 };
typedef struct {
// TODO define struct for samplers (low priority)
// NOTE: glTF defines samplers based on OpenGL, which can not be
// directly translated to Vulkan. Specifically, OpenGL (and glTF)
// define a different set of Min/Mag-filters than Vulkan.
} Sampler;
/** struct for defining the loaded texture */
typedef struct {
int sampler; // index into the sampler array of the Scene
uint8_t channels; // number of channels
uint16_t w, h; // width and height of the texture
std::vector<uint8_t> data; // binary data of the decoded texture
} Texture;
/** The asset loader module only supports the PBR-MetallicRoughness model for
* materials.*/
typedef struct {
uint16_t textureMask; // bit mask with active texture targets
// Indices into the Array.textures array
int baseColor, metalRough, normal, occlusion, emissive;
// Scaling factors for each texture target
struct { float r, g, b, a; } baseColorFactor;
float metallicFactor, roughnessFactor;
float normalScale;
float occlusionStrength;
struct { float r, g, b; } emissiveFactor;
} Material;
/** Flags for the bit-mask in the Material struct. To check if a material has a
* certain texture target, you can use the hasTexture() function below, passing
* the material struct and the enum. */
enum class PBRTextureTarget {
baseColor=1, metalRough=2, normal=4, occlusion=8, emissive=16
};
/** This macro translates the index of an enum in the defined order to an
* integer with a single bit set in the corresponding place. It is used for
* working with the bitmask of texture targets ("textureMask") in the Material
* struct:
* Material mat = ...;
* if (mat.textureMask & bitflag(PBRTextureTarget::baseColor)) {...}
* However, this logic is also encapsulated in the convenience-function
* materialHasTexture() so users of the asset loader module can avoid direct
* contact with bit-level operations. */
#define bitflag(ENUM) (0x1u << ((unsigned)(ENUM)))
/** To signal that a certain texture target is active in a Material struct, its
* bit is set in the textureMask. You can use this function to check that:
* Material mat = ...;
* if (materialHasTexture(&mat, baseColor)) {...} */
bool materialHasTexture(const Material *const m, const PBRTextureTarget t);
/** With these enums, 0 is reserved to signal uninitialized or invalid data. */
enum class PrimitiveType : uint32_t { enum class PrimitiveType : uint32_t {
UNDEFINED = 0, UNDEFINED = 0,
POSITION = 1, POSITION = 1,
NORMAL = 2, NORMAL = 2,
TEXCOORD_0 = 3 TEXCOORD_0 = 3,
TEXCOORD_1 = 4
}; };
/* This struct describes one vertex attribute of a vertex buffer. */
/** These integer values are used the same way in OpenGL, Vulkan and glTF. This
* enum is not needed for translation, it's only for the programmers
* convenience (easier to read in if/switch statements etc). While this enum
* exists in (almost) the same definition in the fx-gltf library, we want to
* avoid exposing that dependency, thus it is re-defined here. */
enum class ComponentType : uint16_t {
NONE = 0, INT8 = 5120, UINT8 = 5121, INT16 = 5122, UINT16 = 5123,
UINT32 = 5125, FLOAT32 = 5126
};
/** This struct describes one vertex attribute of a vertex buffer. */
typedef struct { typedef struct {
PrimitiveType type; // POSITION, NORMAL, ... PrimitiveType type; // POSITION, NORMAL, ...
uint32_t offset; // offset in bytes uint32_t offset; // offset in bytes
uint32_t length; // length of ... in bytes uint32_t length; // length of ... in bytes
uint32_t stride; // stride in bytes uint32_t stride; // stride in bytes
uint16_t componentType; // eg. 5126 for float ComponentType componentType; // eg. 5126 for float
uint8_t componentCount; // eg. 3 for vec3 uint8_t componentCount; // eg. 3 for vec3
} VertexAttribute; } VertexAttribute;
typedef struct { /** This struct represents one (possibly the only) part of a mesh. There is
// TODO not yet needed for the first (unlit) triangle
} Material;
/* This struct represents one (possibly the only) part of a mesh. There is
* always one vertexBuffer and zero or one indexBuffer (indexed rendering is * always one vertexBuffer and zero or one indexBuffer (indexed rendering is
* common but not always used). If there is no index buffer, this is indicated * common but not always used). If there is no index buffer, this is indicated
* by indexBuffer.data being empty. Each vertex buffer can have one or more * by indexBuffer.data being empty. Each vertex buffer can have one or more
...@@ -91,34 +155,39 @@ typedef struct { ...@@ -91,34 +155,39 @@ typedef struct {
} vertexBuffer; } vertexBuffer;
struct { float x, y, z; } min; // bounding box lower left struct { float x, y, z; } min; // bounding box lower left
struct { float x, y, z; } max; // bounding box upper right struct { float x, y, z; } max; // bounding box upper right
uint8_t materialIndex; // index to one of the meshes materials int materialIndex; // index to one of the materials
} VertexGroup; } VertexGroup;
/* This struct represents a single mesh loaded from a glTF file. It consists of /** This struct represents a single mesh as it was loaded from a glTF file. It
* at least one VertexVroup and any number of Materials. */ * consists of at least one VertexGroup, which then references other resources
* such as Materials. */
typedef struct { typedef struct {
std::string name; std::string name;
std::vector<VertexGroup> vertexGroups; std::array<float, 16> modelMatrix;
std::vector<Material> materials; std::vector<int> vertexGroups;
// FIXME Dirty hack to get one(!) texture for our cube demo
// hardcoded to always have RGBA channel layout
struct {
int w, h, ch; // width, height and channels of image
uint8_t *img; // raw bytes, free after use (deal with it)
} texture_hack;
} Mesh; } Mesh;
/** The scene struct is simply a collection of objects in the scene as well as
* the resources used by those objects.
* For now the only type of object are the meshes and they are represented in a
* flat array.
* Note that parent-child relations are not yet possible. */
typedef struct {
std::vector<Mesh> meshes;
std::vector<VertexGroup> vertexGroups;
std::vector<Material> materials;
std::vector<Texture> textures;
std::vector<Sampler> samplers;
} Scene;
/** /**
* In its first iteration the asset loader module will only allow loading * Load every mesh from the glTF file, as well as materials and textures.
* single meshes, one per glTF file.
* It will later be extended to allow loading entire scenes from glTF files.
* *
* @param path must be the path to a glTF file containing a single mesh. * @param path must be the path to a glTF or glb file.
* @param mesh is a reference to a Mesh struct that will be filled with the * @param scene is a reference to a Scene struct that will be filled with the
* content of the glTF file being loaded. * content of the glTF file being loaded.
* */ * */
int loadMesh(const std::string &path, Mesh &mesh); int loadScene(const std::string &path, Scene &scene);
} }
...@@ -5,9 +5,10 @@ ...@@ -5,9 +5,10 @@
#include <fx/gltf.h> #include <fx/gltf.h>
#define STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_IMPLEMENTATION
#define STBI_ONLY_JPEG #define STBI_ONLY_JPEG
#define STBI_ONLY_PNG
#include <stb_image.h> #include <stb_image.h>
#include <vkcv/Logger.hpp> #include <vkcv/Logger.hpp>
#include <algorithm>
namespace vkcv::asset { namespace vkcv::asset {
...@@ -51,160 +52,318 @@ void print_what (const std::exception& e, const std::string &path) { ...@@ -51,160 +52,318 @@ void print_what (const std::exception& e, const std::string &path) {
} }
} }
int loadMesh(const std::string &path, Mesh &mesh) { /** Translate the component type used in the index accessor of fx-gltf to our
fx::gltf::Document object; * enum for index type. The reason we have defined an incompatible enum that
* needs translation is that only a subset of component types is valid for
try { * indices and we want to catch these incompatibilities here. */
if (path.rfind(".glb", (path.length()-4)) != std::string::npos) { enum IndexType getIndexType(const enum fx::gltf::Accessor::ComponentType &t)
object = fx::gltf::LoadFromBinary(path); {
} else { switch (t) {
object = fx::gltf::LoadFromText(path);
}
} catch (const std::system_error &err) {
print_what(err, path);
return 0;
} catch (const std::exception &e) {
print_what(e, path);
return 0;
}
// TODO Temporary restriction: Only one mesh per glTF file allowed
// currently. Later, we want to support whole scenes with more than
// just meshes.
if (object.meshes.size() != 1) return 0;
fx::gltf::Mesh const &objectMesh = object.meshes[0];
// TODO We want to support more than one vertex group per mesh
// eventually... right now this is hard-coded to use only the first one
// because we only care about the example triangle and cube
fx::gltf::Primitive const &objectPrimitive = objectMesh.primitives[0];
fx::gltf::Accessor posAccessor;
std::vector<VertexAttribute> vertexAttributes;
vertexAttributes.reserve(objectPrimitive.attributes.size());
for (auto const & attrib : objectPrimitive.attributes) {
fx::gltf::Accessor accessor = object.accessors[attrib.second];
VertexAttribute attribute;
if (attrib.first == "POSITION") {
attribute.type = PrimitiveType::POSITION;
posAccessor = accessor;
} else if (attrib.first == "NORMAL") {
attribute.type = PrimitiveType::NORMAL;
} else if (attrib.first == "TEXCOORD_0") {
attribute.type = PrimitiveType::TEXCOORD_0;
} else {
return 0;
}
attribute.offset = object.bufferViews[accessor.bufferView].byteOffset;
attribute.length = object.bufferViews[accessor.bufferView].byteLength;
attribute.stride = object.bufferViews[accessor.bufferView].byteStride;
attribute.componentType = static_cast<uint16_t>(accessor.componentType);
if (convertTypeToInt(accessor.type) != 10) {
attribute.componentCount = convertTypeToInt(accessor.type);
} else {
return 0;
}
vertexAttributes.push_back(attribute);
}
// TODO consider the case where there is no index buffer (not all
// meshes have to use indexed rendering)
const fx::gltf::Accessor &indexAccessor = object.accessors[objectPrimitive.indices];
const fx::gltf::BufferView &indexBufferView = object.bufferViews[indexAccessor.bufferView];
const fx::gltf::Buffer &indexBuffer = object.buffers[indexBufferView.buffer];
std::vector<uint8_t> indexBufferData;
indexBufferData.resize(indexBufferView.byteLength);
{
const size_t off = indexBufferView.byteOffset;
const void *const ptr = ((char*)indexBuffer.data.data()) + off;
if (!memcpy(indexBufferData.data(), ptr, indexBufferView.byteLength)) {
vkcv_log(LogLevel::ERROR, "Copying index buffer data");
return 0;
}
}
const fx::gltf::BufferView& vertexBufferView = object.bufferViews[posAccessor.bufferView];
const fx::gltf::Buffer& vertexBuffer = object.buffers[vertexBufferView.buffer];
// FIXME: This only works when all vertex attributes are in one buffer
std::vector<uint8_t> vertexBufferData;
vertexBufferData.resize(vertexBuffer.byteLength);
{
const size_t off = 0;
const void *const ptr = ((char*)vertexBuffer.data.data()) + off;
if (!memcpy(vertexBufferData.data(), ptr, vertexBuffer.byteLength)) {
vkcv_log(LogLevel::ERROR, "Copying vertex buffer data");
return 0;
}
}
IndexType indexType;
switch(indexAccessor.componentType) {
case fx::gltf::Accessor::ComponentType::UnsignedByte: case fx::gltf::Accessor::ComponentType::UnsignedByte:
indexType = UINT8; break; return IndexType::UINT8;
case fx::gltf::Accessor::ComponentType::UnsignedShort: case fx::gltf::Accessor::ComponentType::UnsignedShort:
indexType = UINT16; break; return IndexType::UINT16;
case fx::gltf::Accessor::ComponentType::UnsignedInt: case fx::gltf::Accessor::ComponentType::UnsignedInt:
indexType = UINT32; break; return IndexType::UINT32;
default: default:
vkcv_log(LogLevel::ERROR, "Index type (%u) not supported", std::cerr << "ERROR: Index type not supported: " <<
static_cast<uint16_t>(indexAccessor.componentType)); static_cast<uint16_t>(t) << std::endl;
return 0; return IndexType::UNDEFINED;
} }
}
const size_t numVertexGroups = objectMesh.primitives.size(); /**
* This function computes the modelMatrix out of the data given in the gltf file. It also checks, whether a modelMatrix was given.
std::vector<VertexGroup> vertexGroups; * @param translation possible translation vector (default 0,0,0)
vertexGroups.reserve(numVertexGroups); * @param scale possible scale vector (default 1,1,1)
* @param rotation possible rotation, given in quaternion (default 0,0,0,1)
vertexGroups.push_back({ * @param matrix possible modelmatrix (default identity)
static_cast<PrimitiveMode>(objectPrimitive.mode), * @return model Matrix as an array of floats
object.accessors[objectPrimitive.indices].count, */
posAccessor.count, std::array<float, 16> computeModelMatrix(std::array<float, 3> translation, std::array<float, 3> scale, std::array<float, 4> rotation, std::array<float, 16> matrix){
{indexType, indexBufferData}, std::array<float, 16> modelMatrix = {1,0,0,0,
{vertexBufferData, vertexAttributes}, 0,1,0,0,
{posAccessor.min[0], posAccessor.min[1], posAccessor.min[2]}, 0,0,1,0,
{posAccessor.max[0], posAccessor.max[1], posAccessor.max[2]}, 0,0,0,1};
static_cast<uint8_t>(objectPrimitive.material) if (matrix != modelMatrix){
}); return matrix;
} else {
std::vector<Material> materials; // translation
modelMatrix[3] = translation[0];
mesh = { modelMatrix[7] = translation[1];
object.meshes[0].name, modelMatrix[11] = translation[2];
vertexGroups, // rotation and scale
materials, auto a = rotation[0];
0, 0, 0, NULL auto q1 = rotation[1];
}; auto q2 = rotation[2];
auto q3 = rotation[3];
// FIXME HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK
// fail quietly if there is no texture modelMatrix[0] = (2 * (a * a + q1 * q1) - 1) * scale[0];
if (object.textures.size()) { modelMatrix[1] = (2 * (q1 * q2 - a * q3)) * scale[1];
const std::string mime_type("image/jpeg"); modelMatrix[2] = (2 * (q1 * q3 + a * q2)) * scale[2];
const fx::gltf::Texture &tex = object.textures[0];
const fx::gltf::Image &img = object.images[tex.source]; modelMatrix[4] = (2 * (q1 * q2 + a * q3)) * scale[0];
#ifndef NDEBUG modelMatrix[5] = (2 * (a * a + q2 * q2) - 1) * scale[1];
printf("texture name=%s sampler=%u source=%u\n", modelMatrix[6] = (2 * (q2 * q3 - a * q1)) * scale[2];
tex.name.c_str(), tex.sampler, tex.source);
printf("image name=%s uri=%s mime=%s\n", img.name.c_str(), modelMatrix[8] = (2 * (q1 * q3 - a * q2)) * scale[0];
img.uri.c_str(), img.mimeType.c_str()); modelMatrix[9] = (2 * (q2 * q3 + a * q1)) * scale[1];
#endif modelMatrix[10] = (2 * (a * a + q3 * q3) - 1) * scale[2];
size_t pos = path.find_last_of("/"); // flip y, because GLTF uses y up, but vulkan -y up
auto dir = path.substr(0, pos); modelMatrix[5] *= -1;
mesh.texture_hack.img = stbi_load((dir + "/" + img.uri).c_str(), return modelMatrix;
&mesh.texture_hack.w, &mesh.texture_hack.h, }
&mesh.texture_hack.ch, 4);
} }
// FIXME HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK
return 1; bool materialHasTexture(const Material *const m, const PBRTextureTarget t)
{
return m->textureMask & bitflag(t);
}
int loadScene(const std::string &path, Scene &scene){
fx::gltf::Document sceneObjects;
try {
if (path.rfind(".glb", (path.length()-4)) != std::string::npos) {
sceneObjects = fx::gltf::LoadFromBinary(path);
} else {
sceneObjects = fx::gltf::LoadFromText(path);
}
} catch (const std::system_error &err) {
print_what(err, path);
return 0;
} catch (const std::exception &e) {
print_what(e, path);
return 0;
}
size_t pos = path.find_last_of("/");
auto dir = path.substr(0, pos);
// file has to contain at least one mesh
if (sceneObjects.meshes.size() == 0) return 0;
fx::gltf::Accessor posAccessor;
std::vector<VertexAttribute> vertexAttributes;
std::vector<Material> materials;
std::vector<Texture> textures;
std::vector<Sampler> samplers;
std::vector<Mesh> meshes;
std::vector<VertexGroup> vertexGroups;
int groupCount = 0;
Mesh mesh = {};
for(int i = 0; i < sceneObjects.meshes.size(); i++){
std::vector<int> vertexGroupsIndices;
fx::gltf::Mesh const &objectMesh = sceneObjects.meshes[i];
for(int j = 0; j < objectMesh.primitives.size(); j++){
fx::gltf::Primitive const &objectPrimitive = objectMesh.primitives[j];
vertexAttributes.clear();
vertexAttributes.reserve(objectPrimitive.attributes.size());
for (auto const & attrib : objectPrimitive.attributes) {
fx::gltf::Accessor accessor = sceneObjects.accessors[attrib.second];
VertexAttribute attribute;
if (attrib.first == "POSITION") {
attribute.type = PrimitiveType::POSITION;
posAccessor = accessor;
} else if (attrib.first == "NORMAL") {
attribute.type = PrimitiveType::NORMAL;
} else if (attrib.first == "TEXCOORD_0") {
attribute.type = PrimitiveType::TEXCOORD_0;
} else if (attrib.first == "TEXCOORD_1") {
attribute.type = PrimitiveType::TEXCOORD_1;
} else {
return 0;
}
attribute.offset = sceneObjects.bufferViews[accessor.bufferView].byteOffset;
attribute.length = sceneObjects.bufferViews[accessor.bufferView].byteLength;
attribute.stride = sceneObjects.bufferViews[accessor.bufferView].byteStride;
attribute.componentType = static_cast<ComponentType>(accessor.componentType);
if (convertTypeToInt(accessor.type) != 10) {
attribute.componentCount = convertTypeToInt(accessor.type);
} else {
return 0;
}
vertexAttributes.push_back(attribute);
}
IndexType indexType;
std::vector<uint8_t> indexBufferData = {};
if (objectPrimitive.indices >= 0){ // if there is no index buffer, -1 is returned from fx-gltf
const fx::gltf::Accessor &indexAccessor = sceneObjects.accessors[objectPrimitive.indices];
const fx::gltf::BufferView &indexBufferView = sceneObjects.bufferViews[indexAccessor.bufferView];
const fx::gltf::Buffer &indexBuffer = sceneObjects.buffers[indexBufferView.buffer];
indexBufferData.resize(indexBufferView.byteLength);
{
const size_t off = indexBufferView.byteOffset;
const void *const ptr = ((char*)indexBuffer.data.data()) + off;
if (!memcpy(indexBufferData.data(), ptr, indexBufferView.byteLength)) {
vkcv_log(LogLevel::ERROR, "Copying index buffer data");
return 0;
}
}
indexType = getIndexType(indexAccessor.componentType);
if (indexType == IndexType::UNDEFINED){
vkcv_log(LogLevel::ERROR, "Index Type undefined.");
return 0;
}
}
const fx::gltf::BufferView& vertexBufferView = sceneObjects.bufferViews[posAccessor.bufferView];
const fx::gltf::Buffer& vertexBuffer = sceneObjects.buffers[vertexBufferView.buffer];
// only copy relevant part of vertex data
uint32_t relevantBufferOffset = std::numeric_limits<uint32_t>::max();
uint32_t relevantBufferEnd = 0;
for (const auto &attribute : vertexAttributes) {
relevantBufferOffset = std::min(attribute.offset, relevantBufferOffset);
const uint32_t attributeEnd = attribute.offset + attribute.length;
relevantBufferEnd = std::max(relevantBufferEnd, attributeEnd); // TODO: need to incorporate stride?
}
const uint32_t relevantBufferSize = relevantBufferEnd - relevantBufferOffset;
// FIXME: This only works when all vertex attributes are in one buffer
std::vector<uint8_t> vertexBufferData;
vertexBufferData.resize(relevantBufferSize);
{
const void *const ptr = ((char*)vertexBuffer.data.data()) + relevantBufferOffset;
if (!memcpy(vertexBufferData.data(), ptr, relevantBufferSize)) {
vkcv_log(LogLevel::ERROR, "Copying vertex buffer data");
return 0;
}
}
// make vertex attributes relative to copied section
for (auto &attribute : vertexAttributes) {
attribute.offset -= relevantBufferOffset;
}
const size_t numVertexGroups = objectMesh.primitives.size();
vertexGroups.reserve(numVertexGroups);
vertexGroups.push_back({
static_cast<PrimitiveMode>(objectPrimitive.mode),
sceneObjects.accessors[objectPrimitive.indices].count,
posAccessor.count,
{indexType, indexBufferData},
{vertexBufferData, vertexAttributes},
{posAccessor.min[0], posAccessor.min[1], posAccessor.min[2]},
{posAccessor.max[0], posAccessor.max[1], posAccessor.max[2]},
static_cast<uint8_t>(objectPrimitive.material)
});
vertexGroupsIndices.push_back(groupCount);
groupCount++;
}
mesh.name = sceneObjects.meshes[i].name;
mesh.vertexGroups = vertexGroupsIndices;
meshes.push_back(mesh);
}
for(int m = 0; m < sceneObjects.nodes.size(); m++) {
meshes[sceneObjects.nodes[m].mesh].modelMatrix = computeModelMatrix(sceneObjects.nodes[m].translation,
sceneObjects.nodes[m].scale,
sceneObjects.nodes[m].rotation,
sceneObjects.nodes[m].matrix);
}
if (sceneObjects.textures.size() > 0){
textures.reserve(sceneObjects.textures.size());
for(int k = 0; k < sceneObjects.textures.size(); k++){
const fx::gltf::Texture &tex = sceneObjects.textures[k];
const fx::gltf::Image &img = sceneObjects.images[tex.source];
std::string img_uri = dir + "/" + img.uri;
int w, h, c;
uint8_t *data = stbi_load(img_uri.c_str(), &w, &h, &c, 4);
c = 4; // FIXME hardcoded to always have RGBA channel layout
if (!data) {
vkcv_log(LogLevel::ERROR, "Loading texture image data.")
return 0;
}
const size_t byteLen = w * h * c;
std::vector<uint8_t> imgdata;
imgdata.resize(byteLen);
if (!memcpy(imgdata.data(), data, byteLen)) {
vkcv_log(LogLevel::ERROR, "Copying texture image data")
free(data);
return 0;
}
free(data);
textures.push_back({
0,
static_cast<uint8_t>(c),
static_cast<uint16_t>(w),
static_cast<uint16_t>(h),
imgdata
});
}
}
if (sceneObjects.materials.size() > 0){
materials.reserve(sceneObjects.materials.size());
for (int l = 0; l < sceneObjects.materials.size(); l++){
fx::gltf::Material material = sceneObjects.materials[l];
// TODO I think we shouldn't set the index for a texture target if
// it isn't defined. So we need to test first if there is a normal
// texture before assigning material.normalTexture.index.
// About the bitmask: If a normal texture is there, modify the
// materials textureMask like this:
// mat.textureMask |= bitflag(asset::normal);
materials.push_back({
0,
material.pbrMetallicRoughness.baseColorTexture.index,
material.pbrMetallicRoughness.metallicRoughnessTexture.index,
material.normalTexture.index,
material.occlusionTexture.index,
material.emissiveTexture.index,
{
material.pbrMetallicRoughness.baseColorFactor[0],
material.pbrMetallicRoughness.baseColorFactor[1],
material.pbrMetallicRoughness.baseColorFactor[2],
material.pbrMetallicRoughness.baseColorFactor[3]
},
material.pbrMetallicRoughness.metallicFactor,
material.pbrMetallicRoughness.roughnessFactor,
material.normalTexture.scale,
material.occlusionTexture.strength,
{
material.emissiveFactor[0],
material.emissiveFactor[1],
material.emissiveFactor[2]
}
});
}
}
scene = {
meshes,
vertexGroups,
materials,
textures,
samplers
};
return 1;
} }
} }
...@@ -2,4 +2,5 @@ ...@@ -2,4 +2,5 @@
# Add new projects/examples here: # Add new projects/examples here:
add_subdirectory(first_triangle) add_subdirectory(first_triangle)
add_subdirectory(first_mesh) add_subdirectory(first_mesh)
add_subdirectory(first_scene)
add_subdirectory(cmd_sync_test) add_subdirectory(cmd_sync_test)
\ No newline at end of file
...@@ -39,10 +39,10 @@ int main(int argc, const char** argv) { ...@@ -39,10 +39,10 @@ int main(int argc, const char** argv) {
{ "VK_KHR_swapchain" } { "VK_KHR_swapchain" }
); );
vkcv::asset::Mesh mesh; vkcv::asset::Scene mesh;
const char* path = argc > 1 ? argv[1] : "resources/cube/cube.gltf"; const char* path = argc > 1 ? argv[1] : "resources/cube/cube.gltf";
int result = vkcv::asset::loadMesh(path, mesh); int result = vkcv::asset::loadScene(path, mesh);
if (result == 1) { if (result == 1) {
std::cout << "Mesh loading successful!" << std::endl; std::cout << "Mesh loading successful!" << std::endl;
...@@ -136,8 +136,11 @@ int main(int argc, const char** argv) { ...@@ -136,8 +136,11 @@ int main(int argc, const char** argv) {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
vkcv::Image texture = core.createImage(vk::Format::eR8G8B8A8Srgb, mesh.texture_hack.w, mesh.texture_hack.h); //vkcv::Image texture = core.createImage(vk::Format::eR8G8B8A8Srgb, mesh.texture_hack.w, mesh.texture_hack.h);
texture.fill(mesh.texture_hack.img); //texture.fill(mesh.texture_hack.img);
vkcv::asset::Texture &tex = mesh.textures[0];
vkcv::Image texture = core.createImage(vk::Format::eR8G8B8A8Srgb, tex.w, tex.h);
texture.fill(tex.data.data());
vkcv::SamplerHandle sampler = core.createSampler( vkcv::SamplerHandle sampler = core.createSampler(
vkcv::SamplerFilterType::LINEAR, vkcv::SamplerFilterType::LINEAR,
......
File added
Source diff could not be displayed: it is stored in LFS. Options to address this: view the blob.
projects/first_mesh/resources/Szene/boards2_vcyc.jpg

132 B

projects/first_mesh/resources/Szene/boards2_vcyc_jpg.jpg

132 B

...@@ -29,10 +29,10 @@ int main(int argc, const char** argv) { ...@@ -29,10 +29,10 @@ int main(int argc, const char** argv) {
{ "VK_KHR_swapchain" } { "VK_KHR_swapchain" }
); );
vkcv::asset::Mesh mesh; vkcv::asset::Scene mesh;
const char* path = argc > 1 ? argv[1] : "resources/cube/cube.gltf"; const char* path = argc > 1 ? argv[1] : "resources/cube/cube.gltf";
int result = vkcv::asset::loadMesh(path, mesh); int result = vkcv::asset::loadScene(path, mesh);
if (result == 1) { if (result == 1) {
std::cout << "Mesh loading successful!" << std::endl; std::cout << "Mesh loading successful!" << std::endl;
...@@ -118,8 +118,11 @@ int main(int argc, const char** argv) { ...@@ -118,8 +118,11 @@ int main(int argc, const char** argv) {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
vkcv::Image texture = core.createImage(vk::Format::eR8G8B8A8Srgb, mesh.texture_hack.w, mesh.texture_hack.h); // FIXME There should be a test here to make sure there is at least 1
texture.fill(mesh.texture_hack.img); // texture in the mesh.
vkcv::asset::Texture &tex = mesh.textures[0];
vkcv::Image texture = core.createImage(vk::Format::eR8G8B8A8Srgb, tex.w, tex.h);
texture.fill(tex.data.data());
vkcv::SamplerHandle sampler = core.createSampler( vkcv::SamplerHandle sampler = core.createSampler(
vkcv::SamplerFilterType::LINEAR, vkcv::SamplerFilterType::LINEAR,
...@@ -129,9 +132,9 @@ int main(int argc, const char** argv) { ...@@ -129,9 +132,9 @@ int main(int argc, const char** argv) {
); );
const std::vector<vkcv::VertexBufferBinding> vertexBufferBindings = { const std::vector<vkcv::VertexBufferBinding> vertexBufferBindings = {
vkcv::VertexBufferBinding(static_cast<vk::DeviceSize>(attributes[0].offset), vertexBuffer.getVulkanHandle()), vkcv::VertexBufferBinding(static_cast<vk::DeviceSize>(attributes[0].offset), vertexBuffer.getVulkanHandle()),
vkcv::VertexBufferBinding(static_cast<vk::DeviceSize>(attributes[1].offset), vertexBuffer.getVulkanHandle()), vkcv::VertexBufferBinding(static_cast<vk::DeviceSize>(attributes[1].offset), vertexBuffer.getVulkanHandle()),
vkcv::VertexBufferBinding(static_cast<vk::DeviceSize>(attributes[2].offset), vertexBuffer.getVulkanHandle()) }; vkcv::VertexBufferBinding(static_cast<vk::DeviceSize>(attributes[2].offset), vertexBuffer.getVulkanHandle()) };
vkcv::DescriptorWrites setWrites; vkcv::DescriptorWrites setWrites;
setWrites.sampledImageWrites = { vkcv::SampledImageDescriptorWrite(0, texture.getHandle()) }; setWrites.sampledImageWrites = { vkcv::SampledImageDescriptorWrite(0, texture.getHandle()) };
...@@ -194,7 +197,6 @@ int main(int argc, const char** argv) { ...@@ -194,7 +197,6 @@ int main(int argc, const char** argv) {
renderTargets); renderTargets);
core.prepareSwapchainImageForPresent(cmdStream); core.prepareSwapchainImageForPresent(cmdStream);
core.submitCommandStream(cmdStream); core.submitCommandStream(cmdStream);
core.endFrame(); core.endFrame();
} }
......
first_scene
cmake_minimum_required(VERSION 3.16)
project(first_scene)
# setting c++ standard for the project
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# this should fix the execution path to load local files from the project
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
# adding source files to the project
add_executable(first_scene src/main.cpp)
# this should fix the execution path to load local files from the project (for MSVC)
if(MSVC)
set_target_properties(first_scene PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
set_target_properties(first_scene PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
# in addition to setting the output directory, the working directory has to be set
# by default visual studio sets the working directory to the build directory, when using the debugger
set_target_properties(first_scene PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
endif()
# including headers of dependencies and the VkCV framework
target_include_directories(first_scene SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_asset_loader_include} ${vkcv_camera_include})
# linking with libraries from all dependencies and the VkCV framework
target_link_libraries(first_scene vkcv ${vkcv_libraries} vkcv_asset_loader ${vkcv_asset_loader_libraries} vkcv_camera)
projects/first_scene/resources/Cutlery/Cutlery_chrome_BaseColor.png

128 B

projects/first_scene/resources/Cutlery/Cutlery_chrome_Normal.png

132 B

projects/first_scene/resources/Cutlery/Cutlery_details_BaseColor.png

128 B

projects/first_scene/resources/Cutlery/Cutlery_details_Normal.png

131 B

projects/first_scene/resources/Cutlery/Paris_LiquorBottle_01_Caps_BaseColor.png

131 B

projects/first_scene/resources/Cutlery/Paris_LiquorBottle_01_Caps_Normal.png

132 B

projects/first_scene/resources/Cutlery/Paris_LiquorBottle_01_Glass_Wine_BaseColor.png

128 B

projects/first_scene/resources/Cutlery/Paris_LiquorBottle_01_Glass_Wine_Normal.png

127 B

0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment