Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • 119-graphicspipeline-refactoring
  • 129-projekte-und-assets-auslagern
  • 132-denoising-module
  • 143-ar-vr-support-via-openxr
  • 43-multi-threading
  • 91-compute-first-network
  • 95-arm64-raspberry-pi-4-support
  • develop
  • master
  • optimizations
  • 0.1.0
  • 0.2.0
12 results

Target

Select target project
  • vulkan2021/vkcv-framework
1 result
Select Git revision
  • 119-graphicspipeline-refactoring
  • 129-projekte-und-assets-auslagern
  • 132-denoising-module
  • 143-ar-vr-support-via-openxr
  • 43-multi-threading
  • 91-compute-first-network
  • 95-arm64-raspberry-pi-4-support
  • develop
  • master
  • optimizations
  • 0.1.0
  • 0.2.0
12 results
Show changes
Commits on Source (43)
Showing
with 496 additions and 197 deletions
......@@ -63,4 +63,4 @@ namespace vkcv{
std::vector<VertexBinding> vertexBindings;
};
}
\ No newline at end of file
}
#pragma once
/**
* @authors Trevor Hollmann
* @authors Trevor Hollmann, Mara Vogt, Susanne Dötsch
* @file include/vkcv/asset/asset_loader.h
* @brief Interface of the asset loader module for the vkcv framework.
*/
#include <string>
#include <vector>
#include <array>
#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
* 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
......@@ -19,17 +20,18 @@
#define MAX_MATERIALS_PER_MESH UINT8_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
* top.
*
* 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
* 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
* 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.
* As a bonus there is also the axis aligned bounding box of the vertices.
*
......@@ -43,37 +45,99 @@
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. */
enum PrimitiveMode {
enum class PrimitiveMode : uint8_t {
POINTS=0, LINES, LINELOOP, LINESTRIP, TRIANGLES, TRIANGLESTRIP,
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 {
UNDEFINED = 0,
POSITION = 1,
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 {
PrimitiveType type; // POSITION, NORMAL, ...
uint32_t offset; // offset in bytes
uint32_t length; // length of ... 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
} VertexAttribute;
typedef struct {
// TODO not yet needed for the first (unlit) triangle
} Material;
/* This struct represents one (possibly the only) part of a mesh. There is
/** This struct represents one (possibly the only) part of a mesh. There 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
* by indexBuffer.data being empty. Each vertex buffer can have one or more
......@@ -91,34 +155,39 @@ typedef struct {
} vertexBuffer;
struct { float x, y, z; } min; // bounding box lower left
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;
/* This struct represents a single mesh loaded from a glTF file. It consists of
* at least one VertexVroup and any number of Materials. */
/** This struct represents a single mesh as it was loaded from a glTF file. It
* consists of at least one VertexGroup, which then references other resources
* such as Materials. */
typedef struct {
std::string name;
std::vector<VertexGroup> vertexGroups;
std::vector<Material> materials;
// 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;
std::array<float, 16> modelMatrix;
std::vector<int> vertexGroups;
} 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
* single meshes, one per glTF file.
* It will later be extended to allow loading entire scenes from glTF files.
* Load every mesh from the glTF file, as well as materials and textures.
*
* @param path must be the path to a glTF file containing a single mesh.
* @param mesh is a reference to a Mesh struct that will be filled with the
* @param path must be the path to a glTF or glb file.
* @param scene is a reference to a Scene struct that will be filled with the
* 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 @@
#include <fx/gltf.h>
#define STB_IMAGE_IMPLEMENTATION
#define STBI_ONLY_JPEG
#define STBI_ONLY_PNG
#include <stb_image.h>
#include <vkcv/Logger.hpp>
#include <algorithm>
namespace vkcv::asset {
......@@ -51,160 +52,318 @@ void print_what (const std::exception& e, const std::string &path) {
}
}
int loadMesh(const std::string &path, Mesh &mesh) {
fx::gltf::Document object;
try {
if (path.rfind(".glb", (path.length()-4)) != std::string::npos) {
object = fx::gltf::LoadFromBinary(path);
} else {
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) {
/** Translate the component type used in the index accessor of fx-gltf to our
* 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
* indices and we want to catch these incompatibilities here. */
enum IndexType getIndexType(const enum fx::gltf::Accessor::ComponentType &t)
{
switch (t) {
case fx::gltf::Accessor::ComponentType::UnsignedByte:
indexType = UINT8; break;
return IndexType::UINT8;
case fx::gltf::Accessor::ComponentType::UnsignedShort:
indexType = UINT16; break;
return IndexType::UINT16;
case fx::gltf::Accessor::ComponentType::UnsignedInt:
indexType = UINT32; break;
return IndexType::UINT32;
default:
vkcv_log(LogLevel::ERROR, "Index type (%u) not supported",
static_cast<uint16_t>(indexAccessor.componentType));
return 0;
std::cerr << "ERROR: Index type not supported: " <<
static_cast<uint16_t>(t) << std::endl;
return IndexType::UNDEFINED;
}
}
const size_t numVertexGroups = objectMesh.primitives.size();
std::vector<VertexGroup> vertexGroups;
vertexGroups.reserve(numVertexGroups);
vertexGroups.push_back({
static_cast<PrimitiveMode>(objectPrimitive.mode),
object.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)
});
std::vector<Material> materials;
mesh = {
object.meshes[0].name,
vertexGroups,
materials,
0, 0, 0, NULL
};
// FIXME HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK
// fail quietly if there is no texture
if (object.textures.size()) {
const std::string mime_type("image/jpeg");
const fx::gltf::Texture &tex = object.textures[0];
const fx::gltf::Image &img = object.images[tex.source];
#ifndef NDEBUG
printf("texture name=%s sampler=%u source=%u\n",
tex.name.c_str(), tex.sampler, tex.source);
printf("image name=%s uri=%s mime=%s\n", img.name.c_str(),
img.uri.c_str(), img.mimeType.c_str());
#endif
size_t pos = path.find_last_of("/");
auto dir = path.substr(0, pos);
mesh.texture_hack.img = stbi_load((dir + "/" + img.uri).c_str(),
&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;
/**
* This function computes the modelMatrix out of the data given in the gltf file. It also checks, whether a modelMatrix was given.
* @param translation possible translation vector (default 0,0,0)
* @param scale possible scale vector (default 1,1,1)
* @param rotation possible rotation, given in quaternion (default 0,0,0,1)
* @param matrix possible modelmatrix (default identity)
* @return model Matrix as an array of floats
*/
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){
std::array<float, 16> modelMatrix = {1,0,0,0,
0,1,0,0,
0,0,1,0,
0,0,0,1};
if (matrix != modelMatrix){
return matrix;
} else {
// translation
modelMatrix[3] = translation[0];
modelMatrix[7] = translation[1];
modelMatrix[11] = translation[2];
// rotation and scale
auto a = rotation[0];
auto q1 = rotation[1];
auto q2 = rotation[2];
auto q3 = rotation[3];
modelMatrix[0] = (2 * (a * a + q1 * q1) - 1) * scale[0];
modelMatrix[1] = (2 * (q1 * q2 - a * q3)) * scale[1];
modelMatrix[2] = (2 * (q1 * q3 + a * q2)) * scale[2];
modelMatrix[4] = (2 * (q1 * q2 + a * q3)) * scale[0];
modelMatrix[5] = (2 * (a * a + q2 * q2) - 1) * scale[1];
modelMatrix[6] = (2 * (q2 * q3 - a * q1)) * scale[2];
modelMatrix[8] = (2 * (q1 * q3 - a * q2)) * scale[0];
modelMatrix[9] = (2 * (q2 * q3 + a * q1)) * scale[1];
modelMatrix[10] = (2 * (a * a + q3 * q3) - 1) * scale[2];
// flip y, because GLTF uses y up, but vulkan -y up
modelMatrix[5] *= -1;
return modelMatrix;
}
}
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 @@
# Add new projects/examples here:
add_subdirectory(first_triangle)
add_subdirectory(first_mesh)
add_subdirectory(first_scene)
add_subdirectory(cmd_sync_test)
\ No newline at end of file
......@@ -39,10 +39,10 @@ int main(int argc, const char** argv) {
{ "VK_KHR_swapchain" }
);
vkcv::asset::Mesh mesh;
vkcv::asset::Scene mesh;
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) {
std::cout << "Mesh loading successful!" << std::endl;
......@@ -136,8 +136,11 @@ int main(int argc, const char** argv) {
return EXIT_FAILURE;
}
vkcv::Image texture = core.createImage(vk::Format::eR8G8B8A8Srgb, mesh.texture_hack.w, mesh.texture_hack.h);
texture.fill(mesh.texture_hack.img);
//vkcv::Image texture = core.createImage(vk::Format::eR8G8B8A8Srgb, mesh.texture_hack.w, mesh.texture_hack.h);
//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::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) {
{ "VK_KHR_swapchain" }
);
vkcv::asset::Mesh mesh;
vkcv::asset::Scene mesh;
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) {
std::cout << "Mesh loading successful!" << std::endl;
......@@ -118,8 +118,11 @@ int main(int argc, const char** argv) {
return EXIT_FAILURE;
}
vkcv::Image texture = core.createImage(vk::Format::eR8G8B8A8Srgb, mesh.texture_hack.w, mesh.texture_hack.h);
texture.fill(mesh.texture_hack.img);
// FIXME There should be a test here to make sure there is at least 1
// 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::SamplerFilterType::LINEAR,
......@@ -129,9 +132,9 @@ int main(int argc, const char** argv) {
);
const std::vector<vkcv::VertexBufferBinding> vertexBufferBindings = {
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[2].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[2].offset), vertexBuffer.getVulkanHandle()) };
vkcv::DescriptorWrites setWrites;
setWrites.sampledImageWrites = { vkcv::SampledImageDescriptorWrite(0, texture.getHandle()) };
......@@ -194,7 +197,6 @@ int main(int argc, const char** argv) {
renderTargets);
core.prepareSwapchainImageForPresent(cmdStream);
core.submitCommandStream(cmdStream);
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