Does not include some of the fancier accessors yet.
This commit is contained in:
bjorn 2022-07-03 17:26:31 -07:00
parent e8e9e7fd57
commit d088c5471d
15 changed files with 1169 additions and 78 deletions

View File

@ -2,6 +2,7 @@
#include "shaders/unlit.frag.h"
#include "shaders/font.frag.h"
#include "shaders/fill.vert.h"
#include "shaders/animator.comp.h"
#include "shaders/timewizard.comp.h"
#include "shaders/lovr.glsl.h"

50
etc/shaders/animator.comp Normal file
View File

@ -0,0 +1,50 @@
#version 460
layout(local_size_x = 32, local_size_x_id = 0) in;
layout(push_constant) uniform Constants {
uint baseVertex;
uint vertexCount;
};
struct ModelVertex {
float x, y, z;
float nx, ny, nz;
float u, v;
uint color;
float tx, ty, tz;
};
struct SkinVertex {
uint indices;
uint weights;
};
layout(set = 0, binding = 0) buffer restrict readonly VertexIn { ModelVertex vertexIn[]; };
layout(set = 0, binding = 1) buffer restrict writeonly VertexOut { ModelVertex vertexOut[]; };
layout(set = 0, binding = 2) buffer restrict readonly VertexWeights { SkinVertex skin[]; };
layout(set = 0, binding = 3) uniform JointTransforms { mat4 joints[256]; };
void main() {
if (gl_GlobalInvocationID.x >= vertexCount) return;
uint vertexIndex = baseVertex + gl_GlobalInvocationID.x;
uint indices = skin[vertexIndex].indices;
uint i0 = (indices >> 0) & 0xff;
uint i1 = (indices >> 8) & 0xff;
uint i2 = (indices >> 16) & 0xff;
uint i3 = (indices >> 24) & 0xff;
vec4 weights = unpackUnorm4x8(skin[vertexIndex].weights);
mat4 matrix = mat4(0);
matrix += joints[i0] * weights[0];
matrix += joints[i1] * weights[1];
matrix += joints[i2] * weights[2];
matrix += joints[i3] * weights[3];
vec4 position = vec4(vertexIn[vertexIndex].x, vertexIn[vertexIndex].y, vertexIn[vertexIndex].z, 1.);
vec3 skinned = (matrix * position).xyz;
vertexOut[vertexIndex].x = skinned.x;
vertexOut[vertexIndex].y = skinned.y;
vertexOut[vertexIndex].z = skinned.z;
}

View File

@ -158,10 +158,12 @@ void* luax_readfile(const char* filename, size_t* bytesRead);
#ifndef LOVR_DISABLE_GRAPHICS
struct Buffer;
struct ColoredString;
struct Model;
void luax_readbufferfield(struct lua_State* L, int index, int type, void* data);
void luax_readbufferdata(struct lua_State* L, int index, struct Buffer* buffer, char* data);
uint32_t luax_checkcomparemode(struct lua_State* L, int index);
struct ColoredString* luax_checkcoloredstrings(struct lua_State* L, int index, uint32_t* count, struct ColoredString* stack);
uint32_t luax_checknodeindex(struct lua_State* L, int index, struct Model* model);
#endif
#ifndef LOVR_DISABLE_MATH

View File

@ -4,6 +4,7 @@
#include "data/rasterizer.h"
#include "data/sound.h"
#include "data/image.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
#include <stdlib.h>
@ -33,7 +34,7 @@ StringEntry lovrDefaultAttribute[] = {
[ATTR_TEXCOORD] = ENTRY("texcoord"),
[ATTR_COLOR] = ENTRY("color"),
[ATTR_TANGENT] = ENTRY("tangent"),
[ATTR_BONES] = ENTRY("bones"),
[ATTR_JOINTS] = ENTRY("joints"),
[ATTR_WEIGHTS] = ENTRY("weights"),
{ 0 }
};

View File

@ -1,6 +1,7 @@
#include "api.h"
#include "data/modelData.h"
#include "core/maf.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
@ -195,6 +196,17 @@ static int l_lovrModelDataGetNodeTransform(lua_State* L) {
return 10;
}
static int l_lovrModelDataGetNodeParent(lua_State* L) {
ModelData* model = luax_checktype(L, 1, ModelData);
ModelNode* node = luax_checknode(L, 2, model);
if (node->parent == ~0u) {
lua_pushnil(L);
} else {
lua_pushinteger(L, node->parent + 1);
}
return 1;
}
static int l_lovrModelDataGetNodeChildren(lua_State* L) {
ModelData* model = luax_checktype(L, 1, ModelData);
ModelNode* node = luax_checknode(L, 2, model);
@ -584,6 +596,7 @@ const luaL_Reg lovrModelData[] = {
{ "getNodeOrientation", l_lovrModelDataGetNodeOrientation },
{ "getNodeScale", l_lovrModelDataGetNodeScale },
{ "getNodeTransform", l_lovrModelDataGetNodeTransform },
{ "getNodeParent", l_lovrModelDataGetNodeParent },
{ "getNodeChildren", l_lovrModelDataGetNodeChildren },
{ "getNodeMeshes", l_lovrModelDataGetNodeMeshes },
{ "getNodeSkin", l_lovrModelDataGetNodeSkin },

View File

@ -2,6 +2,7 @@
#include "graphics/graphics.h"
#include "data/blob.h"
#include "data/image.h"
#include "data/modelData.h"
#include "data/rasterizer.h"
#include "util.h"
#include <lua.h>
@ -44,6 +45,12 @@ StringEntry lovrCompareMode[] = {
{ 0 }
};
StringEntry lovrCoordinateSpace[] = {
[SPACE_LOCAL] = ENTRY("local"),
[SPACE_GLOBAL] = ENTRY("global"),
{ 0 }
};
StringEntry lovrCullMode[] = {
[CULL_NONE] = ENTRY("none"),
[CULL_FRONT] = ENTRY("front"),
@ -1269,6 +1276,32 @@ static int l_lovrGraphicsNewFont(lua_State* L) {
return 1;
}
static int l_lovrGraphicsNewModel(lua_State* L) {
ModelInfo info = { 0 };
info.data = luax_totype(L, 1, ModelData);
info.mipmaps = true;
if (!info.data) {
Blob* blob = luax_readblob(L, 1, "Model");
info.data = lovrModelDataCreate(blob, luax_readfile);
lovrRelease(blob, lovrBlobDestroy);
} else {
lovrRetain(info.data);
}
if (lua_istable(L, 2)) {
lua_getfield(L, 2, "mipmaps");
info.mipmaps = lua_isnil(L, -1) || lua_toboolean(L, -1);
lua_pop(L, 1);
}
Model* model = lovrModelCreate(&info);
luax_pushtype(L, Model, model);
lovrRelease(info.data, lovrModelDataDestroy);
lovrRelease(model, lovrModelDestroy);
return 1;
}
static int l_lovrGraphicsGetPass(lua_State* L) {
PassInfo info;
info.type = luax_checkenum(L, 1, PassType, NULL);
@ -1300,6 +1333,7 @@ static const luaL_Reg lovrGraphics[] = {
{ "newShader", l_lovrGraphicsNewShader },
{ "newMaterial", l_lovrGraphicsNewMaterial },
{ "newFont", l_lovrGraphicsNewFont },
{ "newModel", l_lovrGraphicsNewModel },
{ "getPass", l_lovrGraphicsGetPass },
{ NULL, NULL }
};
@ -1310,6 +1344,7 @@ extern const luaL_Reg lovrSampler[];
extern const luaL_Reg lovrShader[];
extern const luaL_Reg lovrMaterial[];
extern const luaL_Reg lovrFont[];
extern const luaL_Reg lovrModel[];
extern const luaL_Reg lovrPass[];
int luaopen_lovr_graphics(lua_State* L) {
@ -1321,6 +1356,7 @@ int luaopen_lovr_graphics(lua_State* L) {
luax_registertype(L, Shader);
luax_registertype(L, Material);
luax_registertype(L, Font);
luax_registertype(L, Model);
luax_registertype(L, Pass);
return 1;
}

247
src/api/l_graphics_model.c Normal file
View File

@ -0,0 +1,247 @@
#include "api.h"
#include "graphics/graphics.h"
#include "data/modelData.h"
#include "core/maf.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
static uint32_t luax_checkanimation(lua_State* L, int index, Model* model) {
switch (lua_type(L, index)) {
case LUA_TSTRING: {
size_t length;
const char* name = lua_tolstring(L, index, &length);
ModelData* modelData = lovrModelGetModelData(model);
uint64_t animationIndex = map_get(&modelData->animationMap, hash64(name, length));
lovrCheck(animationIndex != MAP_NIL, "ModelData has no animation named '%s'", name);
return (uint32_t) animationIndex;
}
case LUA_TNUMBER: return lua_tointeger(L, index) - 1;
default: return luax_typeerror(L, index, "number or string"), ~0u;
}
}
uint32_t luax_checknodeindex(lua_State* L, int index, Model* model) {
switch (lua_type(L, index)) {
case LUA_TSTRING: {
size_t length;
const char* name = lua_tolstring(L, index, &length);
ModelData* modelData = lovrModelGetModelData(model);
uint64_t nodeIndex = map_get(&modelData->nodeMap, hash64(name, length));
lovrCheck(nodeIndex != MAP_NIL, "ModelData has no node named '%s'", name);
return (uint32_t) nodeIndex;
}
case LUA_TNUMBER: return lua_tointeger(L, index) - 1;
default: return luax_typeerror(L, index, "number or string"), ~0u;
}
}
static int l_lovrModelGetModelData(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
luax_pushtype(L, ModelData, lovrModelGetModelData(model));
return 1;
}
static int l_lovrModelAnimate(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
uint32_t animation = luax_checkanimation(L, 2, model);
float time = luax_checkfloat(L, 3);
float alpha = luax_optfloat(L, 4, 1.f);
lovrModelAnimate(model, animation, time, alpha);
return 0;
}
static int l_lovrModelResetPose(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
lovrModelResetPose(model);
return 0;
}
static int l_lovrModelGetNodePose(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
uint32_t node = luax_checknodeindex(L, 2, model);
float position[4], rotation[4], angle, ax, ay, az;
CoordinateSpace space = luax_checkenum(L, 3, CoordinateSpace, "global");
lovrModelGetNodePose(model, node, position, rotation, space);
lua_pushnumber(L, position[0]);
lua_pushnumber(L, position[1]);
lua_pushnumber(L, position[2]);
quat_getAngleAxis(rotation, &angle, &ax, &ay, &az);
lua_pushnumber(L, angle);
lua_pushnumber(L, ax);
lua_pushnumber(L, ay);
lua_pushnumber(L, az);
return 7;
}
static int l_lovrModelSetNodePose(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
uint32_t node = luax_checknodeindex(L, 2, model);
int index = 3;
float position[4], rotation[4];
index = luax_readvec3(L, index, position, NULL);
index = luax_readquat(L, index, rotation, NULL);
float alpha = luax_optfloat(L, index, 1.f);
lovrModelSetNodePose(model, node, position, rotation, alpha);
return 0;
}
static int l_lovrModelGetTexture(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
uint32_t index = luaL_checkinteger(L, 2);
Texture* texture = lovrModelGetTexture(model, index);
luax_pushtype(L, Texture, texture);
return 1;
}
static int l_lovrModelGetMaterial(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
uint32_t index = luaL_checkinteger(L, 2);
Material* material = lovrModelGetMaterial(model, index);
luax_pushtype(L, Material, material);
return 1;
}
static int l_lovrModelGetVertexBuffer(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
Buffer* buffer = lovrModelGetVertexBuffer(model);
luax_pushtype(L, Buffer, buffer);
return 1;
}
static int l_lovrModelGetIndexBuffer(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
Buffer* buffer = lovrModelGetIndexBuffer(model);
luax_pushtype(L, Buffer, buffer);
return 1;
}
/*
static int l_lovrModelGetTriangles(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
float* vertices = NULL;
uint32_t* indices = NULL;
uint32_t vertexCount;
uint32_t indexCount;
lovrModelGetTriangles(model, &vertices, &vertexCount, &indices, &indexCount);
lua_createtable(L, vertexCount * 3, 0);
for (uint32_t i = 0; i < vertexCount; i++) {
lua_pushnumber(L, vertices[i]);
lua_rawseti(L, -2, i + 1);
}
lua_createtable(L, indexCount, 0);
for (uint32_t i = 0; i < indexCount; i++) {
lua_pushinteger(L, indices[i] + 1);
lua_rawseti(L, -2, i + 1);
}
return 2;
}
static int l_lovrModelGetTriangleCount(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
uint32_t count = lovrModelGetTriangleCount(model);
lua_pushinteger(L, count);
return 1;
}
static int l_lovrModelGetVertexCount(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
uint32_t count = lovrModelGetVertexCount(model);
lua_pushinteger(L, count);
return 1;
}
static int l_lovrModelGetWidth(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
float bounds[6];
lovrModelGetBoundingBox(model, bounds);
lua_pushnumber(L, bounds[1] - bounds[0]);
return 1;
}
static int l_lovrModelGetHeight(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
float bounds[6];
lovrModelGetBoundingBox(model, bounds);
lua_pushnumber(L, bounds[3] - bounds[2]);
return 1;
}
static int l_lovrModelGetDepth(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
float bounds[6];
lovrModelGetBoundingBox(model, bounds);
lua_pushnumber(L, bounds[5] - bounds[4]);
return 1;
}
static int l_lovrModelGetDimensions(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
float bounds[6];
lovrModelGetBoundingBox(model, bounds);
lua_pushnumber(L, bounds[1] - bounds[0]);
lua_pushnumber(L, bounds[3] - bounds[2]);
lua_pushnumber(L, bounds[5] - bounds[4]);
return 3;
}
static int l_lovrModelGetCenter(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
float bounds[6];
lovrModelGetBoundingBox(model, bounds);
lua_pushnumber(L, (bounds[0] + bounds[1]) / 2.f);
lua_pushnumber(L, (bounds[2] + bounds[3]) / 2.f);
lua_pushnumber(L, (bounds[4] + bounds[5]) / 2.f);
return 1;
}
static int l_lovrModelGetBoundingBox(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
float bounds[6];
lovrModelGetBoundingBox(model, bounds);
lua_pushnumber(L, bounds[0]);
lua_pushnumber(L, bounds[1]);
lua_pushnumber(L, bounds[2]);
lua_pushnumber(L, bounds[3]);
lua_pushnumber(L, bounds[4]);
lua_pushnumber(L, bounds[5]);
return 6;
}
static int l_lovrModelGetBoundingSphere(lua_State* L) {
Model* model = luax_checktype(L, 1, Model);
float sphere[4];
lovrModelGetBoundingSphere(model, sphere);
lua_pushnumber(L, sphere[0]);
lua_pushnumber(L, sphere[1]);
lua_pushnumber(L, sphere[2]);
lua_pushnumber(L, sphere[3]);
return 4;
}
*/
const luaL_Reg lovrModel[] = {
{ "getModelData", l_lovrModelGetModelData },
{ "animate", l_lovrModelAnimate },
{ "resetPose", l_lovrModelResetPose },
{ "getNodePose", l_lovrModelGetNodePose },
{ "setNodePose", l_lovrModelSetNodePose },
{ "getTexture", l_lovrModelGetTexture },
{ "getMaterial", l_lovrModelGetMaterial },
{ "getVertexBuffer", l_lovrModelGetVertexBuffer },
{ "getIndexBuffer", l_lovrModelGetIndexBuffer },
/*{ "getTriangles", l_lovrModelGetTriangles },
{ "getTriangleCount", l_lovrModelGetTriangleCount },
{ "getVertexCount", l_lovrModelGetVertexCount },
{ "getWidth", l_lovrModelGetWidth },
{ "getHeight", l_lovrModelGetHeight },
{ "getDepth", l_lovrModelGetDepth },
{ "getDimensions", l_lovrModelGetDimensions },
{ "getCenter", l_lovrModelGetCenter },
{ "getBoundingBox", l_lovrModelGetBoundingBox },
{ "getBoundingSphere", l_lovrModelGetBoundingSphere },*/
{ NULL, NULL }
};

View File

@ -584,6 +584,24 @@ static int l_lovrPassMonkey(lua_State* L) {
return 0;
}
static int l_lovrPassDraw(lua_State* L) {
Pass* pass = luax_checktype(L, 1, Pass);
float transform[16];
Model* model = luax_totype(L, 2, Model);
if (model) {
int index = luax_readmat4(L, 3, transform, 1);
uint32_t node = lua_isnoneornil(L, index) ? ~0u : luax_checknodeindex(L, index, model);
bool recurse = lua_isnoneornil(L, index + 1) ? true : lua_toboolean(L, index + 1);
uint32_t instances = lua_isnoneornil(L, index + 2) ? 1 : luax_checku32(L, index + 2);
lovrPassDrawModel(pass, model, transform, node, recurse, instances);
return 0;
}
return luax_typeerror(L, 2, "Model");
}
static int l_lovrPassMesh(lua_State* L) {
Pass* pass = luax_checktype(L, 1, Pass);
Buffer* vertices = !lua_toboolean(L, 2) ? NULL : luax_totype(L, 2, Buffer);
@ -848,6 +866,7 @@ const luaL_Reg lovrPass[] = {
{ "text", l_lovrPassText },
{ "fill", l_lovrPassFill },
{ "monkey", l_lovrPassMonkey },
{ "draw", l_lovrPassDraw },
{ "mesh", l_lovrPassMesh },
{ "multimesh", l_lovrPassMultimesh },

View File

@ -237,6 +237,8 @@ static spv_result spv_parse_spec_constant(spv_context* spv, const uint32_t* op,
if (spv->cache[id].flag.name != 0xffff) {
constant->name = (char*) (spv->words + spv->cache[id].flag.name);
} else {
constant->name = NULL;
}
if (OP_CODE(op) == 50) { // OpSpecConstant

View File

@ -2,22 +2,93 @@
#include "data/blob.h"
#include "data/image.h"
#include <stdlib.h>
#include <string.h>
static size_t typeSizes[] = {
[I8] = 1,
[U8] = 1,
[I16] = 2,
[U16] = 2,
[I32] = 4,
[U32] = 4,
[F32] = 4
};
ModelData* lovrModelDataCreate(Blob* source, ModelDataIO* io) {
ModelData* model = calloc(1, sizeof(ModelData));
lovrAssert(model, "Out of memory");
model->ref = 1;
if (lovrModelDataInitGltf(model, source, io)) {
return model;
} else if (lovrModelDataInitObj(model, source, io)) {
return model;
} else if (lovrModelDataInitStl(model, source, io)) {
return model;
if (!lovrModelDataInitGltf(model, source, io)) {
if (!lovrModelDataInitObj(model, source, io)) {
if (!lovrModelDataInitStl(model, source, io)) {
lovrThrow("Unable to load model from '%s'", source->name);
return NULL;
}
}
}
lovrThrow("Unable to load model from '%s'", source->name);
return NULL;
// Precomputed properties and validation
for (uint32_t i = 0; i < model->primitiveCount; i++) {
model->primitives[i].skin = 0xaaaaaaaa;
}
for (uint32_t i = 0; i < model->nodeCount; i++) {
ModelNode* node = &model->nodes[i];
for (uint32_t j = 0, index = node->primitiveIndex; j < node->primitiveCount; j++, index++) {
if (model->primitives[index].skin != 0xaaaaaaaa) {
lovrCheck(model->primitives[index].skin == node->skin, "Model has a mesh used with multiple skins, which is not supported");
} else {
model->primitives[index].skin = node->skin;
}
}
}
model->indexType = U16;
for (uint32_t i = 0; i < model->primitiveCount; i++) {
ModelPrimitive* primitive = &model->primitives[i];
uint32_t vertexCount = primitive->attributes[ATTR_POSITION]->count;
if (primitive->skin != ~0u) {
model->skins[primitive->skin].vertexCount += vertexCount;
model->skinnedVertexCount += vertexCount;
}
model->vertexCount += vertexCount;
model->indexCount += primitive->indices ? primitive->indices->count : 0;
if (primitive->indices) {
if (primitive->indices->type == U32) {
primitive->indices->stride = 4;
model->indexType = U32;
} else {
primitive->indices->stride = 2;
}
}
for (uint32_t i = 0; i < MAX_DEFAULT_ATTRIBUTES; i++) {
ModelAttribute* attribute = primitive->attributes[i];
if (attribute) {
attribute->stride = model->buffers[attribute->buffer].stride;
if (attribute->stride == 0) {
attribute->stride = typeSizes[attribute->type] * attribute->components;
}
}
}
}
for (uint32_t i = 0; i < model->nodeCount; i++) {
model->nodes[i].parent = ~0u;
}
for (uint32_t i = 0; i < model->nodeCount; i++) {
ModelNode* node = &model->nodes[i];
for (uint32_t j = 0; j < node->childCount; j++) {
model->nodes[node->children[j]].parent = i;
}
}
return model;
}
void lovrModelDataDestroy(void* ref) {
@ -75,3 +146,64 @@ void lovrModelDataAllocate(ModelData* model) {
map_init(&model->materialMap, model->materialCount);
map_init(&model->nodeMap, model->nodeCount);
}
void lovrModelDataCopyAttribute(ModelData* data, ModelAttribute* attribute, char* dst, AttributeType type, uint32_t components, bool normalized, uint32_t count, size_t stride, uint8_t clear) {
char* src = attribute ? data->buffers[attribute->buffer].data + attribute->offset : NULL;
size_t size = components * typeSizes[type];
if (!attribute) {
for (uint32_t i = 0; i < count; i++, dst += stride) {
memset(dst, clear, size);
}
} else if (attribute->type == type && attribute->components >= components) {
for (uint32_t i = 0; i < count; i++, src += attribute->stride, dst += stride) {
memcpy(dst, src, size);
}
} else if (type == F32) {
if (attribute->type == U8 && attribute->normalized) {
for (uint32_t i = 0; i < count; i++, src += attribute->stride, dst += stride) {
for (uint32_t j = 0; j < components; j++) {
((float*) dst)[j] = ((uint8_t*) src)[j] / 255.f;
}
}
} else if (attribute->type == U16 && attribute->normalized) {
for (uint32_t i = 0; i < count; i++, src += attribute->stride, dst += stride) {
for (uint32_t j = 0; j < components; j++) {
((float*) dst)[j] = ((uint16_t*) src)[j] / 65535.f;
}
}
} else {
lovrUnreachable();
}
} else if (type == U8) {
if (attribute->type == U16 && attribute->normalized && normalized) {
for (uint32_t i = 0; i < count; i++, src += attribute->stride, dst += stride) {
for (uint32_t j = 0; j < components; j++) {
((uint8_t*) dst)[j] = ((uint16_t*) src)[j] >> 8;
}
if (components == 4 && attribute->components == 3) {
((float*) dst)[3] = 255;
}
}
} else if (attribute->type == U16 && !attribute->normalized && !normalized) {
for (uint32_t i = 0; i < count; i++, src += attribute->stride, dst += stride) {
for (uint32_t j = 0; j < components; j++) {
((uint8_t*) dst)[j] = (uint8_t) ((uint16_t*) src)[j];
}
}
} else if (attribute->type == F32 && normalized) {
for (uint32_t i = 0; i < count; i++, src += attribute->stride, dst += stride) {
for (uint32_t j = 0; j < components; j++) {
((uint8_t*) dst)[j] = ((float*) src)[j] * 255.f + .5f;
}
if (components == 4 && attribute->components == 3) {
((float*) dst)[3] = 255;
}
}
} else {
lovrUnreachable();
}
} else {
lovrUnreachable();
}
}

View File

@ -22,7 +22,7 @@ typedef enum {
ATTR_TEXCOORD,
ATTR_COLOR,
ATTR_TANGENT,
ATTR_BONES,
ATTR_JOINTS,
ATTR_WEIGHTS,
MAX_DEFAULT_ATTRIBUTES
} DefaultAttribute;
@ -43,6 +43,7 @@ typedef union {
typedef struct {
uint32_t offset;
uint32_t buffer;
size_t stride;
uint32_t count;
AttributeType type;
unsigned components : 3;
@ -69,6 +70,7 @@ typedef struct {
ModelAttribute* indices;
DrawMode mode;
uint32_t material;
uint32_t skin;
} ModelPrimitive;
typedef enum {
@ -150,6 +152,7 @@ typedef struct {
typedef struct {
uint32_t* joints;
uint32_t jointCount;
uint32_t vertexCount;
float* inverseBindMatrices;
} ModelSkin;
@ -163,6 +166,7 @@ typedef struct {
float scale[4];
} properties;
} transform;
uint32_t parent;
uint32_t* children;
uint32_t childCount;
uint32_t primitiveIndex;
@ -205,6 +209,11 @@ typedef struct ModelData {
uint32_t jointCount;
uint32_t charCount;
uint32_t vertexCount;
uint32_t skinnedVertexCount;
uint32_t indexCount;
AttributeType indexType;
map_t animationMap;
map_t materialMap;
map_t nodeMap;
@ -218,3 +227,4 @@ ModelData* lovrModelDataInitObj(ModelData* model, struct Blob* blob, ModelDataIO
ModelData* lovrModelDataInitStl(ModelData* model, struct Blob* blob, ModelDataIO* io);
void lovrModelDataDestroy(void* ref);
void lovrModelDataAllocate(ModelData* model);
void lovrModelDataCopyAttribute(ModelData* data, ModelAttribute* attribute, char* dst, AttributeType type, uint32_t components, bool normalized, uint32_t count, size_t stride, uint8_t clear);

View File

@ -1,12 +1,9 @@
#include "data/modelData.h"
#include "data/blob.h"
#include "data/image.h"
#include "core/maf.h"
#include "lib/jsmn/jsmn.h"
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAX_STACK_TOKENS 1024
@ -60,7 +57,7 @@ typedef struct {
static uint32_t nomInt(const char* s) {
uint32_t n = 0;
lovrAssert(*s != '-', "Expected a positive number");
while (isdigit(*s)) { n = 10 * n + (*s++ - '0'); }
while (*s >= '0' && *s <= '9') { n = 10 * n + (*s++ - '0'); }
return n;
}
@ -777,7 +774,7 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source, ModelDataIO* io
else if (STR_EQ(name, "TEXCOORD_0")) { attributeType = ATTR_TEXCOORD; }
else if (STR_EQ(name, "COLOR_0")) { attributeType = ATTR_COLOR; }
else if (STR_EQ(name, "TANGENT")) { attributeType = ATTR_TANGENT; }
else if (STR_EQ(name, "JOINTS_0")) { attributeType = ATTR_BONES; }
else if (STR_EQ(name, "JOINTS_0")) { attributeType = ATTR_JOINTS; }
else if (STR_EQ(name, "WEIGHTS_0")) { attributeType = ATTR_WEIGHTS; }
if (attributeType != (DefaultAttribute) ~0) {
primitive->attributes[attributeType] = &model->attributes[attributeIndex];
@ -801,9 +798,12 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source, ModelDataIO* io
jsmntok_t* token = info.nodes;
ModelNode* node = model->nodes;
for (int i = (token++)->size; i > 0; i--, node++) {
vec3 translation = vec3_set(node->transform.properties.translation, 0.f, 0.f, 0.f);
quat rotation = quat_set(node->transform.properties.rotation, 0.f, 0.f, 0.f, 1.f);
vec3 scale = vec3_set(node->transform.properties.scale, 1.f, 1.f, 1.f);
float* translation = node->transform.properties.translation;
float* rotation = node->transform.properties.rotation;
float* scale = node->transform.properties.scale;
memcpy(translation, (float[3]) { 0.f, 0.f, 0.f }, 3 * sizeof(float));
memcpy(rotation, (float[4]) { 0.f, 0.f, 0.f, 1.f }, 4 * sizeof(float));
memcpy(scale, (float[3]) { 1.f, 1.f, 1.f }, 3 * sizeof(float));
node->matrix = false;
node->primitiveCount = 0;
node->skin = ~0u;
@ -890,11 +890,14 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source, ModelDataIO* io
ModelNode* lastNode = &model->nodes[model->rootNode];
lastNode->childCount = scenes[rootScene].nodeCount;
lastNode->children = &model->children[childIndex];
mat4_identity(lastNode->transform.matrix);
lastNode->matrix = true;
lastNode->primitiveCount = 0;
lastNode->skin = ~0u;
float* matrix = lastNode->transform.matrix;
memset(matrix, 0, 16 * sizeof(float));
matrix[0] = matrix[5] = matrix[10] = matrix[15] = 1.f;
lastNode->matrix = true;
jsmntok_t* token = info.scenes;
int sceneCount = (token++)->size;
for (int i = 0; i < sceneCount; i++) {

View File

@ -1,6 +1,7 @@
#include "graphics/graphics.h"
#include "data/blob.h"
#include "data/image.h"
#include "data/modelData.h"
#include "data/rasterizer.h"
#include "headset/headset.h"
#include "math/math.h"
@ -38,6 +39,14 @@ typedef struct {
struct { uint8_t r, g, b, a; } color;
} GlyphVertex;
typedef struct {
struct { float x, y, z; } position;
struct { float x, y, z; } normal;
struct { float u, v; } uv;
struct { uint8_t r, g, b, a; } color;
struct { float x, y, z; } tangent;
} ModelVertex;
typedef struct {
gpu_phase readPhase;
gpu_phase writePhase;
@ -150,6 +159,68 @@ struct Font {
uint32_t atlasY;
};
typedef struct {
float transform[16];
float cofactor[16];
float color[4];
} DrawData;
typedef enum {
VERTEX_SHAPE,
VERTEX_POINT,
VERTEX_GLYPH,
VERTEX_MODEL,
VERTEX_EMPTY,
VERTEX_FORMAT_COUNT
} VertexFormat;
typedef struct {
VertexMode mode;
DefaultShader shader;
Material* material;
float* transform;
struct {
Buffer* buffer;
VertexFormat format;
uint32_t count;
union {
void *data, **pointer;
};
} vertex;
struct {
Buffer* buffer;
uint32_t count;
uint32_t stride;
union {
void *data, **pointer;
};
} index;
uint32_t start;
uint32_t count;
uint32_t instances;
uint32_t base;
} Draw;
typedef struct {
float properties[3][4];
} NodeTransform;
struct Model {
uint32_t ref;
ModelInfo info;
Draw* draws;
Buffer* rawVertexBuffer;
Buffer* vertexBuffer;
Buffer* indexBuffer;
Buffer* skinBuffer;
Texture** textures;
Material** materials;
NodeTransform* localTransforms;
float* globalTransforms;
bool transformsDirty;
uint32_t lastReskin;
};
struct Tally {
uint32_t ref;
uint32_t tick;
@ -180,45 +251,6 @@ typedef struct {
bool dirty;
} Pipeline;
typedef struct {
float transform[16];
float cofactor[16];
float color[4];
} DrawData;
typedef enum {
VERTEX_SHAPE,
VERTEX_POINT,
VERTEX_GLYPH,
VERTEX_EMPTY,
VERTEX_FORMAT_COUNT
} VertexFormat;
typedef struct {
VertexMode mode;
DefaultShader shader;
Material* material;
float* transform;
struct {
Buffer* buffer;
VertexFormat format;
const void* data;
void** pointer;
uint32_t count;
} vertex;
struct {
Buffer* buffer;
const void* data;
void** pointer;
uint32_t count;
uint32_t stride;
} index;
uint32_t start;
uint32_t count;
uint32_t instances;
uint32_t base;
} Draw;
typedef struct {
Sync* sync;
Buffer* buffer;
@ -300,6 +332,7 @@ static struct {
bool hasTextureUpload;
bool hasMaterialUpload;
bool hasGlyphUpload;
bool hasReskin;
gpu_device_info device;
gpu_features features;
gpu_limits limits;
@ -309,6 +342,7 @@ static struct {
Buffer* defaultBuffer;
Texture* defaultTexture;
Sampler* defaultSamplers[2];
Shader* animator;
Shader* timeWizard;
Shader* defaultShaders[DEFAULT_SHADER_COUNT];
gpu_vertex_format vertexFormats[VERTEX_FORMAT_COUNT];
@ -331,6 +365,7 @@ static void* tempAlloc(size_t size);
static void* tempGrow(void* p, size_t size);
static uint32_t tempPush(void);
static void tempPop(uint32_t stack);
static int u64cmp(const void* a, const void* b);
static void beginFrame(void);
static void cleanupPasses(void);
static uint32_t getLayout(gpu_slot* slots, uint32_t count);
@ -342,6 +377,7 @@ static void mipmapTexture(gpu_stream* stream, Texture* texture, uint32_t base, u
static ShaderResource* findShaderResource(Shader* shader, const char* name, size_t length, uint32_t slot);
static void trackBuffer(Pass* pass, Buffer* buffer, gpu_phase phase, gpu_cache cache);
static void trackTexture(Pass* pass, Texture* texture, gpu_phase phase, gpu_cache cache);
static void updateModelTransforms(Model* model, uint32_t nodeIndex, float* parent);
static void checkShaderFeatures(uint32_t* features, uint32_t count);
static void onMessage(void* context, const char* message, bool severe);
@ -496,6 +532,17 @@ bool lovrGraphicsInit(bool debug, bool vsync) {
.attributes[4] = { 1, 14, 0, GPU_TYPE_F32x4 }
};
state.vertexFormats[VERTEX_MODEL] = (gpu_vertex_format) {
.bufferCount = 2,
.attributeCount = 5,
.bufferStrides[0] = sizeof(ModelVertex),
.attributes[0] = { 0, 10, offsetof(ModelVertex, position), GPU_TYPE_F32x3 },
.attributes[1] = { 0, 11, offsetof(ModelVertex, normal), GPU_TYPE_F32x3 },
.attributes[2] = { 0, 12, offsetof(ModelVertex, uv), GPU_TYPE_F32x2 },
.attributes[3] = { 0, 13, offsetof(ModelVertex, color), GPU_TYPE_UN8x4 },
.attributes[4] = { 0, 14, offsetof(ModelVertex, tangent), GPU_TYPE_F32x3 }
};
state.vertexFormats[VERTEX_EMPTY] = (gpu_vertex_format) {
.bufferCount = 2,
.attributeCount = 5,
@ -532,6 +579,7 @@ void lovrGraphicsDestroy() {
lovrRelease(state.defaultTexture, lovrTextureDestroy);
lovrRelease(state.defaultSamplers[0], lovrSamplerDestroy);
lovrRelease(state.defaultSamplers[1], lovrSamplerDestroy);
lovrRelease(state.animator, lovrShaderDestroy);
lovrRelease(state.timeWizard, lovrShaderDestroy);
for (uint32_t i = 0; i < COUNTOF(state.defaultShaders); i++) {
lovrRelease(state.defaultShaders[i], lovrShaderDestroy);
@ -708,6 +756,14 @@ void lovrGraphicsSubmit(Pass** passes, uint32_t count) {
state.hasGlyphUpload = false;
}
if (state.hasReskin) {
barriers[0].prev |= GPU_PHASE_SHADER_COMPUTE;
barriers[0].next |= GPU_PHASE_INPUT_VERTEX;
barriers[0].flush |= GPU_CACHE_STORAGE_WRITE;
barriers[0].clear |= GPU_CACHE_VERTEX;
state.hasReskin = false;
}
// End passes
for (uint32_t i = 0; i < count; i++) {
streams[i + 1] = passes[i]->stream;
@ -1572,7 +1628,13 @@ Shader* lovrShaderCreate(ShaderInfo* info) {
};
uint32_t index = shader->flagCount++;
shader->flagLookup[index] = (uint32_t) hash64(constant->name, strlen(constant->name));
if (constant->name) {
shader->flagLookup[index] = (uint32_t) hash64(constant->name, strlen(constant->name));
} else {
shader->flagLookup[index] = 0;
}
shader->flags[index] = (gpu_shader_flag) {
.id = constant->id,
.type = flagTypes[constant->type]
@ -1587,11 +1649,14 @@ Shader* lovrShaderCreate(ShaderInfo* info) {
gpu_shader_info gpu = {
.stages[0] = { info->stages[0]->data, info->stages[0]->size },
.stages[1] = { info->stages[1]->data, info->stages[1]->size },
.pushConstantSize = shader->constantSize,
.label = info->label
};
if (info->stages[1]) {
gpu.stages[1] = (gpu_shader_stage) { info->stages[1]->data, info->stages[1]->size };
}
if (info->type == SHADER_GRAPHICS) {
gpu.layouts[0] = state.layouts.data[state.builtinLayout].gpu;
gpu.layouts[1] = state.layouts.data[state.materialLayout].gpu;
@ -2123,6 +2188,430 @@ void lovrFontGetLines(Font* font, ColoredString* strings, uint32_t count, float
tempPop(stack);
}
// Model
Model* lovrModelCreate(ModelInfo* info) {
ModelData* data = info->data;
Model* model = calloc(1, sizeof(Model));
lovrAssert(model, "Out of memory");
model->ref = 1;
model->info = *info;
lovrRetain(info->data);
// Textures
model->textures = malloc(data->imageCount * sizeof(Texture*));
lovrAssert(model->textures, "Out of memory");
for (uint32_t i = 0; i < data->imageCount; i++) {
model->textures[i] = lovrTextureCreate(&(TextureInfo) {
.type = TEXTURE_2D,
.usage = TEXTURE_SAMPLE,
.format = lovrImageGetFormat(data->images[i]),
.width = lovrImageGetWidth(data->images[i], 0),
.height = lovrImageGetHeight(data->images[i], 0),
.depth = 1,
.mipmaps = info->mipmaps || lovrImageGetLevelCount(data->images[i]) > 1 ? ~0u : 1,
.samples = 1,
.srgb = lovrImageIsSRGB(data->images[i]),
.images = &data->images[i],
.imageCount = 1
});
}
// Materials
model->materials = malloc(data->materialCount * sizeof(Material*));
lovrAssert(model->materials, "Out of memory");
for (uint32_t i = 0; i < data->materialCount; i++) {
MaterialInfo material;
ModelMaterial* properties = &data->materials[i];
memcpy(&material.data, properties, sizeof(MaterialData));
material.texture = properties->texture == ~0u ? NULL : model->textures[properties->texture];
material.glowTexture = properties->glowTexture == ~0u ? NULL : model->textures[properties->glowTexture];
material.occlusionTexture = properties->occlusionTexture == ~0u ? NULL : model->textures[properties->occlusionTexture];
material.metalnessTexture = properties->metalnessTexture == ~0u ? NULL : model->textures[properties->metalnessTexture];
material.roughnessTexture = properties->roughnessTexture == ~0u ? NULL : model->textures[properties->roughnessTexture];
material.clearcoatTexture = properties->clearcoatTexture == ~0u ? NULL : model->textures[properties->clearcoatTexture];
material.normalTexture = properties->normalTexture == ~0u ? NULL : model->textures[properties->normalTexture];
model->materials[i] = lovrMaterialCreate(&material);
}
// Buffers
char* vertices;
char* indices;
char* skinData;
BufferInfo vertexBufferInfo = {
.length = data->vertexCount,
.stride = sizeof(ModelVertex),
.fieldCount = 5,
.fields[0] = { 0, 10, FIELD_F32x3, offsetof(ModelVertex, position) },
.fields[1] = { 0, 11, FIELD_F32x3, offsetof(ModelVertex, normal) },
.fields[2] = { 0, 12, FIELD_F32x2, offsetof(ModelVertex, uv) },
.fields[3] = { 0, 13, FIELD_UN8x4, offsetof(ModelVertex, color) },
.fields[4] = { 0, 14, FIELD_F32x3, offsetof(ModelVertex, tangent) }
};
model->vertexBuffer = lovrBufferCreate(&vertexBufferInfo, (void**) &vertices);
if (data->skinnedVertexCount > 0) {
model->skinBuffer = lovrBufferCreate(&(BufferInfo) {
.length = data->skinnedVertexCount,
.stride = 8,
.fieldCount = 2,
.fields[0] = { 0, 0, FIELD_UN8x4, 0 },
.fields[1] = { 0, 0, FIELD_U8x4, 4 }
}, (void**) &skinData);
vertexBufferInfo.length = data->skinnedVertexCount;
model->rawVertexBuffer = lovrBufferCreate(&vertexBufferInfo, NULL);
beginFrame();
gpu_buffer* src = model->vertexBuffer->gpu;
gpu_buffer* dst = model->rawVertexBuffer->gpu;
gpu_copy_buffers(state.stream, src, dst, 0, 0, data->skinnedVertexCount * sizeof(ModelVertex));
gpu_barrier barrier;
barrier.prev = GPU_PHASE_TRANSFER;
barrier.next = GPU_PHASE_SHADER_COMPUTE;
barrier.flush = GPU_CACHE_TRANSFER_WRITE;
barrier.clear = GPU_CACHE_STORAGE_READ | GPU_CACHE_STORAGE_WRITE;
gpu_sync(state.stream, &barrier, 1);
}
size_t indexSize = data->indexType == U32 ? 4 : 2;
if (data->indexCount > 0) {
model->indexBuffer = lovrBufferCreate(&(BufferInfo) {
.length = data->indexCount,
.stride = indexSize,
.fieldCount = 1,
.fields[0] = { 0, 0, data->indexType == U32 ? FIELD_U32 : FIELD_I32, 0 }
}, (void**) &indices);
}
// Sort primitives by their skin, so there is a single contiguous region of skinned vertices
uint32_t stack = tempPush();
uint64_t* map = tempAlloc(data->primitiveCount * sizeof(uint64_t));
for (uint32_t i = 0; i < data->primitiveCount; i++) {
map[i] = ((uint64_t) data->primitives[i].skin << 32) | i;
}
qsort(map, data->primitiveCount, sizeof(uint64_t), u64cmp);
// Draws
model->draws = calloc(data->primitiveCount, sizeof(Draw));
lovrAssert(model->draws, "Out of memory");
for (uint32_t i = 0, vertexCursor = 0, indexCursor = 0; i < data->primitiveCount; i++) {
ModelPrimitive* primitive = &data->primitives[map[i] & ~0u];
Draw* draw = &model->draws[map[i] & ~0u];
switch (primitive->mode) {
case DRAW_POINTS: draw->mode = VERTEX_POINTS; break;
case DRAW_LINES: draw->mode = VERTEX_LINES; break;
case DRAW_TRIANGLES: draw->mode = VERTEX_TRIANGLES; break;
default: lovrThrow("Model uses an unsupported draw mode (lineloop, linestrip, strip, fan)");
}
draw->material = primitive->material == ~0u ? NULL: model->materials[primitive->material];
draw->vertex.buffer = model->vertexBuffer;
draw->index.stride = indexSize;
if (primitive->indices) {
draw->index.buffer = model->indexBuffer;
draw->start = indexCursor;
draw->count = primitive->indices->count;
draw->base = vertexCursor;
indexCursor += draw->count;
} else {
draw->start = vertexCursor;
draw->count = primitive->attributes[ATTR_POSITION]->count;
}
vertexCursor += primitive->attributes[ATTR_POSITION]->count;
}
// Vertices
for (uint32_t i = 0; i < data->primitiveCount; i++) {
ModelPrimitive* primitive = &data->primitives[map[i] & ~0u];
ModelAttribute** attributes = primitive->attributes;
uint32_t count = attributes[ATTR_POSITION]->count;
size_t stride = sizeof(ModelVertex);
lovrModelDataCopyAttribute(data, attributes[ATTR_POSITION], vertices + 0, F32, 3, false, count, stride, 0);
lovrModelDataCopyAttribute(data, attributes[ATTR_NORMAL], vertices + 12, F32, 3, false, count, stride, 0);
lovrModelDataCopyAttribute(data, attributes[ATTR_TEXCOORD], vertices + 24, F32, 2, false, count, stride, 0);
lovrModelDataCopyAttribute(data, attributes[ATTR_COLOR], vertices + 32, U8, 4, true, count, stride, 255);
lovrModelDataCopyAttribute(data, attributes[ATTR_TANGENT], vertices + 36, F32, 3, false, count, stride, 0);
vertices += count * stride;
if (data->skinnedVertexCount > 0 && primitive->skin != ~0u) {
lovrModelDataCopyAttribute(data, attributes[ATTR_JOINTS], skinData + 0, U8, 4, false, count, 8, 0);
lovrModelDataCopyAttribute(data, attributes[ATTR_WEIGHTS], skinData + 4, U8, 4, true, count, 8, 0);
skinData += count * 8;
}
if (primitive->indices) {
char* indexData = data->buffers[primitive->indices->buffer].data + primitive->indices->offset;
memcpy(indices, indexData, primitive->indices->count * indexSize);
indices += primitive->indices->count * indexSize;
}
}
for (uint32_t i = 0; i < data->skinCount; i++) {
lovrCheck(data->skins[i].jointCount <= 256, "Currently, the max number of joints per skin is 256");
}
model->localTransforms = malloc(sizeof(NodeTransform) * data->nodeCount);
model->globalTransforms = malloc(16 * sizeof(float) * data->nodeCount);
lovrAssert(model->localTransforms && model->globalTransforms, "Out of memory");
lovrModelResetPose(model);
tempPop(stack);
return model;
}
void lovrModelDestroy(void* ref) {
Model* model = ref;
ModelData* data = model->info.data;
for (uint32_t i = 0; i < data->materialCount; i++) {
lovrRelease(model->materials[i], lovrMaterialDestroy);
}
for (uint32_t i = 0; i < data->imageCount; i++) {
lovrRelease(model->textures[i], lovrTextureDestroy);
}
lovrRelease(model->rawVertexBuffer, lovrBufferDestroy);
lovrRelease(model->vertexBuffer, lovrBufferDestroy);
lovrRelease(model->indexBuffer, lovrBufferDestroy);
lovrRelease(model->skinBuffer, lovrBufferDestroy);
lovrRelease(model->info.data, lovrModelDataDestroy);
free(model->localTransforms);
free(model->globalTransforms);
free(model->draws);
free(model->materials);
free(model->textures);
free(model);
}
ModelData* lovrModelGetModelData(Model* model) {
return model->info.data;
}
void lovrModelResetPose(Model* model) {
ModelData* data = model->info.data;
for (uint32_t i = 0; i < data->nodeCount; i++) {
vec3 position = model->localTransforms[i].properties[PROP_TRANSLATION];
quat orientation = model->localTransforms[i].properties[PROP_ROTATION];
vec3 scale = model->localTransforms[i].properties[PROP_SCALE];
if (data->nodes[i].matrix) {
mat4_getPosition(data->nodes[i].transform.matrix, position);
mat4_getOrientation(data->nodes[i].transform.matrix, orientation);
mat4_getScale(data->nodes[i].transform.matrix, scale);
} else {
vec3_init(position, data->nodes[i].transform.properties.translation);
quat_init(orientation, data->nodes[i].transform.properties.rotation);
vec3_init(scale, data->nodes[i].transform.properties.scale);
}
}
model->transformsDirty = true;
}
void lovrModelAnimate(Model* model, uint32_t animationIndex, float time, float alpha) {
if (alpha <= 0.f) return;
ModelData* data = model->info.data;
lovrAssert(animationIndex < data->animationCount, "Invalid animation index '%d' (Model has %d animation%s)", animationIndex + 1, data->animationCount, data->animationCount == 1 ? "" : "s");
ModelAnimation* animation = &data->animations[animationIndex];
time = fmodf(time, animation->duration);
for (uint32_t i = 0; i < animation->channelCount; i++) {
ModelAnimationChannel* channel = &animation->channels[i];
uint32_t node = channel->nodeIndex;
NodeTransform* transform = &model->localTransforms[node];
uint32_t keyframe = 0;
while (keyframe < channel->keyframeCount && channel->times[keyframe] < time) {
keyframe++;
}
float property[4];
bool rotate = channel->property == PROP_ROTATION;
size_t n = 3 + rotate;
float* (*lerp)(float* a, float* b, float t) = rotate ? quat_slerp : vec3_lerp;
// Handle the first/last keyframe case (no interpolation)
if (keyframe == 0 || keyframe >= channel->keyframeCount) {
size_t index = MIN(keyframe, channel->keyframeCount - 1);
// For cubic interpolation, each keyframe has 3 parts, and the actual data is in the middle
if (channel->smoothing == SMOOTH_CUBIC) {
index = 3 * index + 1;
}
memcpy(property, channel->data + index * n, n * sizeof(float));
} else {
float t1 = channel->times[keyframe - 1];
float t2 = channel->times[keyframe];
float z = (time - t1) / (t2 - t1);
switch (channel->smoothing) {
case SMOOTH_STEP:
memcpy(property, channel->data + (z >= .5f ? keyframe : keyframe - 1) * n, n * sizeof(float));
break;
case SMOOTH_LINEAR:
memcpy(property, channel->data + (keyframe - 1) * n, n * sizeof(float));
lerp(property, channel->data + keyframe * n, z);
break;
case SMOOTH_CUBIC: {
size_t stride = 3 * n;
float* p0 = channel->data + (keyframe - 1) * stride + 1 * n;
float* m0 = channel->data + (keyframe - 1) * stride + 2 * n;
float* p1 = channel->data + (keyframe - 0) * stride + 1 * n;
float* m1 = channel->data + (keyframe - 0) * stride + 0 * n;
float dt = t2 - t1;
float z2 = z * z;
float z3 = z2 * z;
float a = 2.f * z3 - 3.f * z2 + 1.f;
float b = 2.f * z3 - 3.f * z2 + 1.f;
float c = -2.f * z3 + 3.f * z2;
float d = (z3 * -z2) * dt;
for (size_t j = 0; j < n; j++) {
property[j] = a * p0[j] + b * m0[j] + c * p1[j] + d * m1[j];
}
break;
}
default: break;
}
}
if (alpha >= 1.f) {
memcpy(transform->properties[channel->property], property, n * sizeof(float));
} else {
lerp(transform->properties[channel->property], property, alpha);
}
}
model->transformsDirty = true;
}
void lovrModelGetNodePose(Model* model, uint32_t node, float position[4], float rotation[4], CoordinateSpace space) {
ModelData* data = model->info.data;
lovrAssert(node < data->nodeCount, "Invalid node index '%d' (Model has %d node%s)", node, data->nodeCount, data->nodeCount == 1 ? "" : "s");
if (space == SPACE_LOCAL) {
vec3_init(position, model->localTransforms[node].properties[PROP_TRANSLATION]);
quat_init(rotation, model->localTransforms[node].properties[PROP_ROTATION]);
} else {
if (model->transformsDirty) {
updateModelTransforms(model, data->rootNode, (float[]) MAT4_IDENTITY);
model->transformsDirty = false;
}
mat4_getPosition(model->globalTransforms + 16 * node, position);
mat4_getOrientation(model->globalTransforms + 16 * node, rotation);
}
}
void lovrModelSetNodePose(Model* model, uint32_t node, float position[4], float rotation[4], float alpha) {
if (alpha <= 0.f) return;
ModelData* data = model->info.data;
lovrAssert(node < data->nodeCount, "Invalid node index '%d' (Model has %d node%s)", node, data->nodeCount, data->nodeCount == 1 ? "" : "s");
NodeTransform* transform = &model->localTransforms[node];
if (alpha >= 1.f) {
vec3_init(transform->properties[PROP_TRANSLATION], position);
quat_init(transform->properties[PROP_ROTATION], rotation);
} else {
vec3_lerp(transform->properties[PROP_TRANSLATION], position, alpha);
quat_slerp(transform->properties[PROP_ROTATION], rotation, alpha);
}
model->transformsDirty = true;
}
Texture* lovrModelGetTexture(Model* model, uint32_t index) {
ModelData* data = model->info.data;
lovrAssert(index < data->imageCount, "Invalid texture index '%d' (Model has %d texture%s)", index, data->imageCount, data->imageCount == 1 ? "" : "s");
return model->textures[index];
}
Material* lovrModelGetMaterial(Model* model, uint32_t index) {
ModelData* data = model->info.data;
lovrAssert(index < data->materialCount, "Invalid material index '%d' (Model has %d material%s)", index, data->materialCount, data->materialCount == 1 ? "" : "s");
return model->materials[index];
}
Buffer* lovrModelGetVertexBuffer(Model* model) {
return model->rawVertexBuffer;
}
Buffer* lovrModelGetIndexBuffer(Model* model) {
return model->indexBuffer;
}
static void lovrModelReskin(Model* model) {
ModelData* data = model->info.data;
if (data->skinCount == 0 || model->lastReskin == state.tick) {
return;
}
if (!state.animator) {
Blob* source = lovrBlobCreate((void*) lovr_shader_animator_comp, sizeof(lovr_shader_animator_comp), NULL);
state.animator = lovrShaderCreate(&(ShaderInfo) {
.type = SHADER_COMPUTE,
.stages[0] = source,
.flags = &(ShaderFlag) { "local_size_x_id", 0, state.device.subgroupSize },
.label = "Animator"
});
source->data = NULL;
lovrRelease(source, lovrBlobDestroy);
}
gpu_pipeline* pipeline = state.pipelines.data[state.animator->computePipeline];
gpu_layout* layout = state.layouts.data[state.animator->layout].gpu;
gpu_shader* shader = state.animator->gpu;
gpu_buffer* joints = tempAlloc(gpu_sizeof_buffer());
uint32_t count = data->skinnedVertexCount;
gpu_binding bindings[] = {
{ 0, GPU_SLOT_STORAGE_BUFFER, .buffer = { model->rawVertexBuffer->gpu, 0, count * sizeof(ModelVertex) } },
{ 1, GPU_SLOT_STORAGE_BUFFER, .buffer = { model->vertexBuffer->gpu, 0, count * sizeof(ModelVertex) } },
{ 2, GPU_SLOT_STORAGE_BUFFER, .buffer = { model->skinBuffer->gpu, 0, count * 8 } },
{ 3, GPU_SLOT_UNIFORM_BUFFER, .buffer = { joints, 0, 0 } } // Filled in for each skin
};
for (uint32_t i = 0, baseVertex = 0; i < data->skinCount; i++) {
ModelSkin* skin = &data->skins[i];
float transform[16];
uint32_t size = bindings[3].buffer.extent = skin->jointCount * 16 * sizeof(float);
float* joint = gpu_map(joints, size, state.limits.uniformBufferAlign, GPU_MAP_WRITE);
for (uint32_t j = 0; j < skin->jointCount; j++) {
mat4_init(transform, model->globalTransforms + 16 * skin->joints[j]);
mat4_mul(transform, skin->inverseBindMatrices + 16 * j);
memcpy(joint, transform, sizeof(transform));
joint += 16;
}
gpu_bundle* bundle = getBundle(state.animator->layout);
gpu_bundle_info bundleInfo = { layout, bindings, COUNTOF(bindings) };
gpu_bundle_write(&bundle, &bundleInfo, 1);
uint32_t constants[] = { baseVertex, skin->vertexCount };
uint32_t subgroupSize = state.device.subgroupSize;
gpu_compute_begin(state.stream);
gpu_bind_pipeline(state.stream, pipeline, true);
gpu_bind_bundle(state.stream, shader, 0, bundle, NULL, 0);
gpu_push_constants(state.stream, shader, constants, sizeof(constants));
gpu_compute(state.stream, (skin->vertexCount + subgroupSize - 1) / subgroupSize, 1, 1);
gpu_compute_end(state.stream);
baseVertex += skin->vertexCount;
}
state.hasReskin = true;
}
// Tally
Tally* lovrTallyCreate(TallyInfo* info) {
@ -2618,7 +3107,7 @@ void lovrPassSetShader(Pass* pass, Shader* shader) {
j++;
} else {
if (previous->resources[i].type != shader->resources[j].type) {
pass->bindingMask &= ~(1 << shader->resources[j].binding);
pass->bindingMask &= ~(1u << shader->resources[j].binding);
}
i++;
j++;
@ -2731,12 +3220,12 @@ void lovrPassSendBuffer(Pass* pass, const char* name, size_t length, uint32_t sl
ShaderResource* resource = findShaderResource(shader, name, length, slot);
slot = resource->binding;
lovrCheck(shader->bufferMask & (1 << slot), "Trying to send a Buffer to slot %d, but the active Shader doesn't have a Buffer in that slot");
lovrCheck(shader->bufferMask & (1u << slot), "Trying to send a Buffer to slot %d, but the active Shader doesn't have a Buffer in that slot");
lovrCheck(offset < buffer->size, "Buffer offset is past the end of the Buffer");
uint32_t limit;
if (shader->storageMask & (1 << slot)) {
if (shader->storageMask & (1u << slot)) {
lovrCheck(!lovrBufferIsTemporary(buffer), "Temporary buffers can not be sent to storage buffer variables", slot + 1);
lovrCheck((offset & (state.limits.storageBufferAlign - 1)) == 0, "Storage buffer offset (%d) is not aligned to storageBufferAlign limit (%d)", offset, state.limits.storageBufferAlign);
limit = state.limits.storageBufferRange;
@ -2755,7 +3244,7 @@ void lovrPassSendBuffer(Pass* pass, const char* name, size_t length, uint32_t sl
pass->bindings[slot].buffer.object = buffer->gpu;
pass->bindings[slot].buffer.offset = offset;
pass->bindings[slot].buffer.extent = extent;
pass->bindingMask |= (1 << slot);
pass->bindingMask |= (1u << slot);
pass->bindingsDirty = true;
gpu_phase phase = 0;
@ -2764,10 +3253,10 @@ void lovrPassSendBuffer(Pass* pass, const char* name, size_t length, uint32_t sl
if (pass->info.type == PASS_RENDER) {
if (resource->stageMask & GPU_STAGE_VERTEX) phase |= GPU_PHASE_SHADER_VERTEX;
if (resource->stageMask & GPU_STAGE_FRAGMENT) phase |= GPU_PHASE_SHADER_FRAGMENT;
cache = (shader->storageMask & (1 << slot)) ? GPU_CACHE_STORAGE_READ : GPU_CACHE_UNIFORM;
cache = (shader->storageMask & (1u << slot)) ? GPU_CACHE_STORAGE_READ : GPU_CACHE_UNIFORM;
} else {
phase = GPU_PHASE_SHADER_COMPUTE;
cache = (shader->storageMask & (1 << slot)) ? GPU_CACHE_STORAGE_WRITE : GPU_CACHE_UNIFORM; // TODO readonly
cache = (shader->storageMask & (1u << slot)) ? GPU_CACHE_STORAGE_WRITE : GPU_CACHE_UNIFORM; // TODO readonly
}
trackBuffer(pass, buffer, phase, cache);
@ -2779,16 +3268,16 @@ void lovrPassSendTexture(Pass* pass, const char* name, size_t length, uint32_t s
ShaderResource* resource = findShaderResource(shader, name, length, slot);
slot = resource->binding;
lovrCheck(shader->textureMask & (1 << slot), "Trying to send a Texture to slot %d, but the active Shader doesn't have a Texture in that slot");
lovrCheck(shader->textureMask & (1u << slot), "Trying to send a Texture to slot %d, but the active Shader doesn't have a Texture in that slot");
if (shader->storageMask & (1 << slot)) {
if (shader->storageMask & (1u << slot)) {
lovrCheck(texture->info.usage & TEXTURE_STORAGE, "Textures must be created with the 'storage' usage to send them to image variables in shaders");
} else {
lovrCheck(texture->info.usage & TEXTURE_SAMPLE, "Textures must be created with the 'sample' usage to send them to sampler variables in shaders");
}
pass->bindings[slot].texture = texture->gpu;
pass->bindingMask |= (1 << slot);
pass->bindingMask |= (1u << slot);
pass->bindingsDirty = true;
gpu_phase phase = 0;
@ -2797,10 +3286,10 @@ void lovrPassSendTexture(Pass* pass, const char* name, size_t length, uint32_t s
if (pass->info.type == PASS_RENDER) {
if (resource->stageMask & GPU_STAGE_VERTEX) phase |= GPU_PHASE_SHADER_VERTEX;
if (resource->stageMask & GPU_STAGE_FRAGMENT) phase |= GPU_PHASE_SHADER_FRAGMENT;
cache = (shader->storageMask & (1 << slot)) ? GPU_CACHE_STORAGE_READ : GPU_CACHE_TEXTURE;
cache = (shader->storageMask & (1u << slot)) ? GPU_CACHE_STORAGE_READ : GPU_CACHE_TEXTURE;
} else {
phase = GPU_PHASE_SHADER_COMPUTE;
cache = (shader->storageMask & (1 << slot)) ? GPU_CACHE_STORAGE_WRITE : GPU_CACHE_TEXTURE; // TODO readonly
cache = (shader->storageMask & (1u << slot)) ? GPU_CACHE_STORAGE_WRITE : GPU_CACHE_TEXTURE; // TODO readonly
}
trackTexture(pass, texture, phase, cache);
@ -2812,10 +3301,10 @@ void lovrPassSendSampler(Pass* pass, const char* name, size_t length, uint32_t s
ShaderResource* resource = findShaderResource(shader, name, length, slot);
slot = resource->binding;
lovrCheck(shader->samplerMask & (1 << slot), "Trying to send a Sampler to slot %d, but the active Shader doesn't have a Sampler in that slot");
lovrCheck(shader->samplerMask & (1u << slot), "Trying to send a Sampler to slot %d, but the active Shader doesn't have a Sampler in that slot");
pass->bindings[slot].sampler = sampler->gpu;
pass->bindingMask |= (1 << slot);
pass->bindingMask |= (1u << slot);
pass->bindingsDirty = true;
}
@ -3718,6 +4207,41 @@ void lovrPassMonkey(Pass* pass, float* transform) {
}
}
static void renderNode(Pass* pass, Model* model, uint32_t index, bool recurse, uint32_t instances) {
ModelNode* node = &model->info.data->nodes[index];
mat4 globalTransform = model->globalTransforms + 16 * index;
for (uint32_t i = 0; i < node->primitiveCount; i++) {
Draw draw = model->draws[node->primitiveIndex + i];
draw.transform = globalTransform;
draw.instances = instances;
lovrPassDraw(pass, &draw);
}
if (recurse) {
for (uint32_t i = 0; i < node->childCount; i++) {
renderNode(pass, model, node->children[i], true, instances);
}
}
}
void lovrPassDrawModel(Pass* pass, Model* model, float* transform, uint32_t node, bool recurse, uint32_t instances) {
if (model->transformsDirty) {
updateModelTransforms(model, model->info.data->rootNode, (float[]) MAT4_IDENTITY);
lovrModelReskin(model);
model->transformsDirty = false;
}
if (node == ~0u) {
node = model->info.data->rootNode;
}
lovrPassPush(pass, STACK_TRANSFORM);
lovrPassTransform(pass, transform);
renderNode(pass, model, node, recurse, instances);
lovrPassPop(pass, STACK_TRANSFORM);
}
void lovrPassMesh(Pass* pass, Buffer* vertices, Buffer* indices, float* transform, uint32_t start, uint32_t count, uint32_t instances) {
if (count == ~0u) {
count = (indices ? indices : vertices)->info.length - start;
@ -3875,6 +4399,7 @@ void lovrPassCopyTallyToBuffer(Pass* pass, Tally* tally, Buffer* buffer, uint32_
.stages[0] = source,
.label = "Chronophage"
});
source->data = NULL;
lovrRelease(source, lovrBlobDestroy);
}
@ -3883,8 +4408,8 @@ void lovrPassCopyTallyToBuffer(Pass* pass, Tally* tally, Buffer* buffer, uint32_
gpu_shader* shader = state.timeWizard->gpu;
gpu_binding bindings[] = {
[0].buffer = { tally->buffer, 0, ~0u },
[1].buffer = { buffer->gpu, dstOffset, count * sizeof(uint32_t) },
[0] = { 0, GPU_SLOT_STORAGE_BUFFER, .buffer = { tally->buffer, 0, ~0u } },
[1] = { 1, GPU_SLOT_STORAGE_BUFFER, .buffer = { buffer->gpu, dstOffset, count * sizeof(uint32_t) } }
};
gpu_bundle* bundle = getBundle(state.timeWizard->layout);
@ -3902,7 +4427,7 @@ void lovrPassCopyTallyToBuffer(Pass* pass, Tally* tally, Buffer* buffer, uint32_
gpu_bind_pipeline(pass->stream, pipeline, true);
gpu_bind_bundle(pass->stream, shader, 0, bundle, NULL, 0);
gpu_push_constants(pass->stream, shader, &constants, sizeof(constants));
gpu_compute(pass->stream, (count + 31) / 32, 0, 0);
gpu_compute(pass->stream, (count + 31) / 32, 1, 1);
gpu_compute_end(pass->stream);
trackBuffer(pass, buffer, GPU_PHASE_SHADER_COMPUTE, GPU_CACHE_STORAGE_WRITE);
@ -4055,6 +4580,11 @@ static void tempPop(uint32_t stack) {
state.allocator.cursor = stack;
}
static int u64cmp(const void* a, const void* b) {
uint64_t x = *(uint64_t*) a, y = *(uint64_t*) b;
return (x > y) - (x < y);
}
static void beginFrame(void) {
if (state.active) {
return;
@ -4360,6 +4890,24 @@ static void trackTexture(Pass* pass, Texture* texture, gpu_phase phase, gpu_cach
lovrRetain(texture);
}
static void updateModelTransforms(Model* model, uint32_t nodeIndex, float* parent) {
mat4 global = model->globalTransforms + 16 * nodeIndex;
NodeTransform* local = &model->localTransforms[nodeIndex];
vec3 T = local->properties[PROP_TRANSLATION];
quat R = local->properties[PROP_ROTATION];
vec3 S = local->properties[PROP_SCALE];
mat4_init(global, parent);
mat4_translate(global, T[0], T[1], T[2]);
mat4_rotateQuat(global, R);
mat4_scale(global, S[0], S[1], S[2]);
ModelNode* node = &model->info.data->nodes[nodeIndex];
for (uint32_t i = 0; i < node->childCount; i++) {
updateModelTransforms(model, node->children[i], global);
}
}
// Only an explicit set of SPIR-V capabilities are allowed
// Some capabilities require a GPU feature to be supported
// Some common unsupported capabilities are checked directly, to provide better error messages

View File

@ -7,6 +7,7 @@
struct Blob;
struct Image;
struct Rasterizer;
struct ModelData;
typedef struct Buffer Buffer;
typedef struct Texture Texture;
@ -14,6 +15,7 @@ typedef struct Sampler Sampler;
typedef struct Shader Shader;
typedef struct Material Material;
typedef struct Font Font;
typedef struct Model Model;
typedef struct Tally Tally;
typedef struct Pass Pass;
@ -358,6 +360,30 @@ float lovrFontGetKerning(Font* font, uint32_t left, uint32_t right);
float lovrFontGetWidth(Font* font, ColoredString* strings, uint32_t count);
void lovrFontGetLines(Font* font, ColoredString* strings, uint32_t count, float wrap, void (*callback)(void* context, const char* string, size_t length), void* context);
// Model
typedef struct {
struct ModelData* data;
bool mipmaps;
} ModelInfo;
typedef enum {
SPACE_LOCAL,
SPACE_GLOBAL
} CoordinateSpace;
Model* lovrModelCreate(ModelInfo* info);
void lovrModelDestroy(void* ref);
struct ModelData* lovrModelGetModelData(Model* model);
void lovrModelResetPose(Model* model);
void lovrModelAnimate(Model* model, uint32_t animationIndex, float time, float alpha);
void lovrModelGetNodePose(Model* model, uint32_t node, float position[4], float rotation[4], CoordinateSpace space);
void lovrModelSetNodePose(Model* model, uint32_t node, float position[4], float rotation[4], float alpha);
Texture* lovrModelGetTexture(Model* model, uint32_t index);
Material* lovrModelGetMaterial(Model* model, uint32_t index);
Buffer* lovrModelGetVertexBuffer(Model* model);
Buffer* lovrModelGetIndexBuffer(Model* model);
// Tally
typedef enum {
@ -514,6 +540,7 @@ void lovrPassTorus(Pass* pass, float* transform, uint32_t segmentsT, uint32_t se
void lovrPassText(Pass* pass, Font* font, ColoredString* strings, uint32_t count, float* transform, float wrap, HorizontalAlign halign, VerticalAlign valign);
void lovrPassFill(Pass* pass, Texture* texture);
void lovrPassMonkey(Pass* pass, float* transform);
void lovrPassDrawModel(Pass* pass, Model* model, float* transform, uint32_t node, bool recurse, uint32_t instances);
void lovrPassMesh(Pass* pass, Buffer* vertices, Buffer* indices, float* transform, uint32_t start, uint32_t count, uint32_t instances);
void lovrPassMultimesh(Pass* pass, Buffer* vertices, Buffer* indices, Buffer* indirect, uint32_t count, uint32_t offset, uint32_t stride);
void lovrPassCompute(Pass* pass, uint32_t x, uint32_t y, uint32_t z, Buffer* indirect, uint32_t offset);

View File

@ -1494,7 +1494,7 @@ static struct ModelData* openxr_newModelData(Device device, bool animated) {
[ATTR_POSITION] = &model->attributes[0],
[ATTR_NORMAL] = &model->attributes[1],
[ATTR_TEXCOORD] = &model->attributes[2],
[ATTR_BONES] = &model->attributes[3],
[ATTR_JOINTS] = &model->attributes[3],
[ATTR_WEIGHTS] = &model->attributes[4]
},
.indices = &model->attributes[5],