mirror of https://github.com/bjornbytes/lovr.git
Model;
Does not include some of the fancier accessors yet.
This commit is contained in:
parent
e8e9e7fd57
commit
d088c5471d
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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 }
|
||||
};
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 }
|
||||
};
|
|
@ -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 },
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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],
|
||||
|
|
Loading…
Reference in New Issue