Compare commits

...

18 Commits

Author SHA1 Message Date
bjorn b934fac1df Pass:skybox; 2022-07-04 00:18:38 -07:00
bjorn 8aa14ba42b Shader:hasAttribute; 2022-07-03 23:04:56 -07:00
bjorn 2c339dd944 Shader:hasStage; 2022-07-03 22:59:49 -07:00
bjorn b2b6b14e3e Fix Pass:mesh error handling; 2022-07-03 22:40:19 -07:00
bjorn 917b97ca2d Adjust; 2022-07-03 21:48:43 -07:00
mcc c34ee01c1b Fix typo in msvc warnings patch 2022-07-03 21:34:16 -07:00
mcc 9a7eedbf4d Remove 'check on egress' behaviors where an error for a sound being too large is emitted only when a getter is called (left harder-to-remove checks that occur when a sound is loaded) 2022-07-03 21:34:16 -07:00
mcc 15aa08bbe7 Adjustments to MSVC warnings PR based on github comments 2022-07-03 21:34:16 -07:00
mcc 28d64b6ced Fix various compiler warnings in MSVC (non graphics edition)
- Put in casts/checks in audio code when assigning size_t to 32 bit
- () is different from (void)
- Turned off warnings for anonymous unions and negating unsigned integers which were technically accurate but unhelpful (and interfered with bit conversion and a weird bit math construct in audio.c) (CMakeLists only)
2022-07-03 21:34:16 -07:00
bjorn cdf6b2017e Fix segfault on shader compilation failure; 2022-07-03 20:19:17 -07:00
bjorn 7ce32e38b1 nogame screen; 2022-07-03 20:07:05 -07:00
bjorn 4f15e7e34a Fix plane winding; 2022-07-03 20:07:00 -07:00
bjorn 1a1026bc0f Fix font alpha; 2022-07-03 20:06:55 -07:00
bjorn de090971f7 rm erroneous union; 2022-07-03 19:59:51 -07:00
bjorn 0d23d10e43 Animate normals; Use material color; 2022-07-03 19:20:30 -07:00
bjorn d088c5471d Model;
Does not include some of the fancier accessors yet.
2022-07-03 17:26:31 -07:00
bjorn e8e9e7fd57 Fix leaks; 2022-07-03 12:59:51 -07:00
bjorn 45f74bad3d Fix Image UB; 2022-07-03 12:59:17 -07:00
29 changed files with 1477 additions and 160 deletions

View File

@ -600,6 +600,10 @@ if(WIN32)
set_target_properties(lovr PROPERTIES COMPILE_FLAGS "/wd4244 /MP")
else()
set_target_properties(lovr PROPERTIES COMPILE_FLAGS "-MP")
# Excuse anonymous union for type punning
set_source_files_properties(src/util.c PROPERTIES COMPILE_FLAGS /wd4146)
# Excuse unsigned negation for flag-magic bit math
set_source_files_properties(src/modules/audio/audio.c PROPERTIES COMPILE_FLAGS /wd4116)
endif()
if(NOT LOVR_BUILD_SHARED)
set_target_properties(lovr PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:console /ENTRY:WinMainCRTStartup")

View File

@ -11,12 +11,58 @@ local function nogame()
local models = {}
function lovr.load()
print(string.format('LÖVR %d.%d.%d\nNo game', lovr.getVersion()))
lovr.event.quit()
if not lovr.graphics then
print(string.format('LÖVR %d.%d.%d\nNo game', lovr.getVersion()))
lovr.event.quit()
return
end
lovr.graphics.setBackground(0x20232c)
logo = lovr.graphics.newShader([[
vec4 lovrmain() {
return DefaultPosition;
}
]], [[
vec4 lovrmain() {
float y = FragUV.y;
vec2 uv = vec2(FragUV.x, 1. - FragUV.y);
uv = uv * 4. - 2.;
const float k = sqrt(3.);
uv.x = abs(uv.x) - 1.;
uv.y = uv.y + 1. / k + .25;
if (uv.x + k * uv.y > 0.) {
uv = vec2(uv.x - k * uv.y, -k * uv.x - uv.y) / 2.;
}
uv.x -= clamp(uv.x, -2., 0.);
float sdf = -length(uv) * sign(uv.y) - .5;
float w = fwidth(sdf) * .5;
float alpha = smoothstep(.22 + w, .22 - w, sdf);
vec3 color = mix(vec3(.094, .662, .890), vec3(.913, .275, .6), clamp(y * 1.5 - .25, 0., 1.));
color = mix(color, vec3(.2, .2, .24), smoothstep(-.12 + w, -.12 - w, sdf));
return vec4(pow(color, vec3(2.2)), alpha);
}
]])
end
function lovr.draw()
--
function lovr.draw(pass)
local padding = .1
local font = lovr.graphics.getDefaultFont()
local fade = .315 + .685 * math.abs(math.sin(lovr.headset.getTime() * 2))
local titlePosition = 1.5 - padding
local subtitlePosition = titlePosition - font:getHeight() * .25 - padding
pass:setCullMode('back')
pass:setBlendMode('alpha')
pass:setShader(logo)
pass:plane(0, 2, -3)
pass:setShader()
pass:setColor(1, 1, 1)
pass:text('LÖVR', -.012, titlePosition, -3, .25, quat(0, 0, 1, 0), nil, 'center', 'top')
pass:setColor(.9, .9, .9, fade)
pass:text('No game :(', -.005, subtitlePosition, -3, .15, 0, 0, 1, 0, nil, 'center', 'top')
end
end

View File

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

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

@ -0,0 +1,56 @@
#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;
vec3 normal = vec3(vertexIn[vertexIndex].nx, vertexIn[vertexIndex].ny, vertexIn[vertexIndex].nz);
vec3 skinnedNormal = mat3(matrix) * normal;
vertexOut[vertexIndex].nx = skinnedNormal.x;
vertexOut[vertexIndex].ny = skinnedNormal.y;
vertexOut[vertexIndex].nz = skinnedNormal.z;
}

13
etc/shaders/cubemap.frag Normal file
View File

@ -0,0 +1,13 @@
#version 460
#extension GL_EXT_multiview : require
#extension GL_GOOGLE_include_directive : require
#include "lovr.glsl"
layout(set = 1, binding = 1) uniform textureCube SkyboxTexture;
layout(location = 2) in vec3 FragDirection;
void main() {
PixelColors[0] = FragColor * texture(samplerCube(SkyboxTexture, Sampler), FragDirection);
}

26
etc/shaders/cubemap.vert Normal file
View File

@ -0,0 +1,26 @@
#version 460
#extension GL_EXT_multiview : require
#extension GL_GOOGLE_include_directive : require
#include "lovr.glsl"
layout(location = 2) out vec3 FragDirection;
void main() {
FragColor = VertexColor * Color;
const vec2 uvs[6] = vec2[6](
vec2(-1, -1),
vec2(-1, +1),
vec2(+1, -1),
vec2(+1, -1),
vec2(-1, +1),
vec2(+1, +1)
);
vec2 uv = uvs[VertexIndex % 6];
vec3 ray = vec3(uv, -1.);
mat3 inverseViewOrientation = transpose(mat3(View));
FragDirection = normalize(inverseViewOrientation * (InverseProjection * vec4(ray, 1.)).xyz);
Position = vec4(uv, 1, 1);
}

17
etc/shaders/equirect.frag Normal file
View File

@ -0,0 +1,17 @@
#version 460
#extension GL_EXT_multiview : require
#extension GL_GOOGLE_include_directive : require
#define PI 3.141592653589793238462643383
#include "lovr.glsl"
layout(location = 2) in vec3 FragDirection;
void main() {
vec3 dir = normalize(FragDirection);
float phi = acos(dir.y);
float theta = atan(dir.x, -dir.z);
vec2 uv = vec2(.5 + theta / (2 * PI), phi / PI);
PixelColors[0] = FragColor * texture(sampler2D(Texture, Sampler), uv);
}

View File

@ -20,5 +20,5 @@ void main() {
float screenPxDistance = screenPxRange() * (sdf - .5);
float alpha = clamp(screenPxDistance + .5, 0., 1.);
if (alpha <= 0.) discard;
PixelColors[0] = vec4(FragColor.rgb, alpha);
PixelColors[0] = vec4(FragColor.rgb, FragColor.a * alpha);
}

View File

@ -60,11 +60,13 @@ layout(location = 0) out vec4 PixelColors[1];
// Varyings
#ifdef GL_VERTEX_SHADER
layout(location = 10) out vec4 FragColor;
layout(location = 11) out vec2 FragUV;
layout(location = 10) out vec3 FragNormal;
layout(location = 11) out vec4 FragColor;
layout(location = 12) out vec2 FragUV;
#else
layout(location = 10) in vec4 FragColor;
layout(location = 11) in vec2 FragUV;
layout(location = 10) in vec3 FragNormal;
layout(location = 11) in vec4 FragColor;
layout(location = 12) in vec2 FragUV;
#endif
// Macros
@ -97,7 +99,7 @@ layout(location = 11) in vec2 FragUV;
#define ViewProjection cameras[ViewIndex].viewProjection
#define InverseProjection cameras[ViewIndex].inverseProjection
#define Transform draws[DrawId].transform
#define NormalMatrix draws[DrawId].normalMatrix
#define NormalMatrix mat3(draws[DrawId].normalMatrix)
#define Color draws[DrawId].color
#define ClipFromLocal (ViewProjection * Transform)
@ -108,5 +110,5 @@ layout(location = 11) in vec2 FragUV;
#define WorldFromLocal (Transform)
#define DefaultPosition (ClipFromLocal * VertexPosition)
#define DefaultColor (FragColor)
#define DefaultColor (FragColor * texture(sampler2D(Texture, Sampler), FragUV) * Material.color)
#endif

View File

@ -5,5 +5,5 @@
#include "lovr.glsl"
void main() {
PixelColors[0] = FragColor * texture(sampler2D(Texture, Sampler), FragUV);
PixelColors[0] = FragColor * texture(sampler2D(Texture, Sampler), FragUV) * Material.color;
}

View File

@ -6,6 +6,7 @@
void main() {
FragColor = VertexColor * Color;
FragNormal = normalize(NormalMatrix * VertexNormal);
FragUV = VertexUV;
Position = DefaultPosition;
PointSize = 1.f;

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

@ -51,7 +51,7 @@ static int l_lovrSoundGetSampleRate(lua_State* L) {
static int l_lovrSoundGetByteStride(lua_State* L) {
Sound* sound = luax_checktype(L, 1, Sound);
uint32_t stride = lovrSoundGetStride(sound);
size_t stride = lovrSoundGetStride(sound);
lua_pushinteger(L, stride);
return 1;
}
@ -125,7 +125,7 @@ static int l_lovrSoundGetFrames(lua_State* L) {
uint32_t frames = 0;
while (frames < count) {
char buffer[4096];
uint32_t chunk = MIN(sizeof(buffer) / stride, count - frames);
uint32_t chunk = MIN((uint32_t) (sizeof(buffer) / stride), count - frames);
uint32_t read = lovrSoundRead(sound, srcOffset + frames, chunk, buffer);
uint32_t samples = read * channels;
if (read == 0) break;
@ -189,7 +189,8 @@ static int l_lovrSoundSetFrames(lua_State* L) {
if (blob) {
uint32_t srcOffset = luax_optu32(L, 5, 0);
uint32_t dstOffset = luax_optu32(L, 4, 0);
uint32_t count = luax_optu32(L, 3, (blob->size - srcOffset) / stride);
uint32_t defaultCount = (uint32_t) MIN((blob->size - srcOffset) / stride, UINT32_MAX);
uint32_t count = luax_optu32(L, 3, defaultCount);
uint32_t frames = lovrSoundWrite(sound, dstOffset, count, (char*) blob->data + srcOffset);
lua_pushinteger(L, frames);
return 1;
@ -221,7 +222,7 @@ static int l_lovrSoundSetFrames(lua_State* L) {
uint32_t frames = 0;
while (frames < count) {
char buffer[4096];
uint32_t chunk = MIN(sizeof(buffer) / stride, count - frames);
uint32_t chunk = MIN((uint32_t) (sizeof(buffer) / stride), count - frames);
uint32_t samples = chunk * channels;
if (format == SAMPLE_I16) {

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"),
@ -53,6 +60,8 @@ StringEntry lovrCullMode[] = {
StringEntry lovrDefaultShader[] = {
[SHADER_UNLIT] = ENTRY("unlit"),
[SHADER_CUBE] = ENTRY("cube"),
[SHADER_FILL] = ENTRY("fill"),
[SHADER_FONT] = ENTRY("font"),
{ 0 }
};
@ -904,7 +913,7 @@ static int l_lovrGraphicsNewTexture(lua_State* L) {
lua_pop(L, 1);
}
if (info.imageCount == 0 && info.depth == 0) {
if (info.depth == 0) {
info.depth = info.type == TEXTURE_CUBE ? 6 : 1;
}
@ -1005,6 +1014,7 @@ static Blob* luax_checkshadercode(lua_State* L, int index, ShaderStage stage) {
}
Blob* code = lovrGraphicsCompileShader(stage, source);
lovrAssert(code, "Could not compile shader");
lovrRelease(source, lovrBlobDestroy);
return code;
}
@ -1269,6 +1279,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 +1336,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 +1347,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 +1359,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

@ -569,6 +569,13 @@ static int l_lovrPassText(lua_State* L) {
return 0;
}
static int l_lovrPassSkybox(lua_State* L) {
Pass* pass = luax_checktype(L, 1, Pass);
Texture* texture = luax_totype(L, 2, Texture);
lovrPassSkybox(pass, texture);
return 0;
}
static int l_lovrPassFill(lua_State* L) {
Pass* pass = luax_checktype(L, 1, Pass);
Texture* texture = luax_totype(L, 2, Texture);
@ -584,9 +591,27 @@ 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);
Buffer* vertices = !lua_toboolean(L, 2) ? NULL : luax_checktype(L, 2, Buffer);
Buffer* indices = luax_totype(L, 3, Buffer);
float transform[16];
int index = luax_readmat4(L, indices ? 4 : 3, transform, 1);
@ -846,8 +871,10 @@ const luaL_Reg lovrPass[] = {
{ "torus", l_lovrPassTorus },
{ "cylinder", l_lovrPassCylinder },
{ "text", l_lovrPassText },
{ "skybox", l_lovrPassSkybox },
{ "fill", l_lovrPassFill },
{ "monkey", l_lovrPassMonkey },
{ "draw", l_lovrPassDraw },
{ "mesh", l_lovrPassMesh },
{ "multimesh", l_lovrPassMultimesh },

View File

@ -5,13 +5,6 @@
#include <lauxlib.h>
#include <stdlib.h>
static int l_lovrShaderGetType(lua_State* L) {
Shader* shader = luax_checktype(L, 1, Shader);
const ShaderInfo* info = lovrShaderGetInfo(shader);
luax_pushenum(L, ShaderType, info->type);
return 1;
}
static int l_lovrShaderClone(lua_State* L) {
Shader* shader = luax_checktype(L, 1, Shader);
luaL_checktype(L, 2, LUA_TTABLE);
@ -40,8 +33,41 @@ static int l_lovrShaderClone(lua_State* L) {
return 1;
}
static int l_lovrShaderGetType(lua_State* L) {
Shader* shader = luax_checktype(L, 1, Shader);
const ShaderInfo* info = lovrShaderGetInfo(shader);
luax_pushenum(L, ShaderType, info->type);
return 1;
}
static int l_lovrShaderHasStage(lua_State* L) {
Shader* shader = luax_checktype(L, 1, Shader);
ShaderStage stage = luax_checkenum(L, 2, ShaderStage, NULL);
bool present = lovrShaderHasStage(shader, stage);
lua_pushboolean(L, present);
return 1;
}
static int l_lovrShaderHasAttribute(lua_State* L) {
Shader* shader = luax_checktype(L, 1, Shader);
const char* name;
uint32_t location;
if (lua_type(L, 2) == LUA_TNUMBER) {
location = luax_checku32(L, 2);
name = NULL;
} else {
name = lua_tostring(L, 2);
location = 0;
}
bool present = lovrShaderHasAttribute(shader, name, location);
lua_pushboolean(L, present);
return 1;
}
const luaL_Reg lovrShader[] = {
{ "getType", l_lovrShaderGetType },
{ "clone", l_lovrShaderClone },
{ "getType", l_lovrShaderGetType },
{ "hasStage", l_lovrShaderHasStage },
{ "hasAttribute", l_lovrShaderHasAttribute },
{ NULL, NULL }
};

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

@ -584,8 +584,9 @@ static Image* loadDDS(Blob* blob) {
// Magic
char* data = blob->data;
size_t length = blob->size;
uint32_t* magic = (uint32_t*) data;
if (*magic != 0x20534444) return false;
uint32_t magic;
memcpy(&magic, data, 4);
if (magic != 0x20534444) return false;
length -= 4;
data += 4;
@ -817,7 +818,7 @@ static Image* loadDDS(Blob* blob) {
}
static Image* loadASTC(Blob* blob) {
typedef struct {
struct {
uint32_t magic;
uint8_t blockX;
uint8_t blockY;
@ -825,23 +826,21 @@ static Image* loadASTC(Blob* blob) {
uint8_t width[3];
uint8_t height[3];
uint8_t depth[3];
} ASTCHeader;
} header;
union {
uint8_t* u8;
uint32_t* u32;
ASTCHeader* astc;
} data = { .u8 = blob->data };
if (blob->size <= sizeof(header)) {
return NULL;
}
uint32_t magic = 0x5ca1ab13;
memcpy(&header, blob->data, sizeof(header));
if (blob->size <= sizeof(*data.astc) || data.astc->magic != magic) {
if (header.magic != 0x5ca1ab13) {
return NULL;
}
TextureFormat format;
uint32_t bx = data.astc->blockX, by = data.astc->blockY, bz = data.astc->blockZ;
uint32_t bx = header.blockX, by = header.blockY, bz = header.blockZ;
if (bx == 4 && by == 4 && bz == 1) { format = FORMAT_ASTC_4x4; }
else if (bx == 5 && by == 4 && bz == 1) { format = FORMAT_ASTC_5x4; }
else if (bx == 5 && by == 5 && bz == 1) { format = FORMAT_ASTC_5x5; }
@ -858,12 +857,12 @@ static Image* loadASTC(Blob* blob) {
else if (bx == 12 && by == 12 && bz == 1) { format = FORMAT_ASTC_12x12; }
else { lovrThrow("Unsupported ASTC format %dx%dx%d", bx, by, bz); }
uint32_t width = data.astc->width[0] + (data.astc->width[1] << 8) + (data.astc->width[2] << 16);
uint32_t height = data.astc->height[0] + (data.astc->height[1] << 8) + (data.astc->height[2] << 16);
uint32_t width = header.width[0] + (header.width[1] << 8) + (header.width[2] << 16);
uint32_t height = header.height[0] + (header.height[1] << 8) + (header.height[2] << 16);
size_t imageSize = ((width + bx - 1) / bx) * ((height + by - 1) / by) * (128 / 8);
if (imageSize > blob->size - sizeof(ASTCHeader)) {
if (imageSize > blob->size - sizeof(header)) {
return NULL;
}
@ -877,12 +876,12 @@ static Image* loadASTC(Blob* blob) {
image->levels = 1;
image->blob = blob;
lovrRetain(blob);
image->mipmaps[0] = (Mipmap) { data.u8 + sizeof(ASTCHeader), imageSize, 0 };
image->mipmaps[0] = (Mipmap) { (char*) blob->data + sizeof(header), imageSize, 0 };
return image;
}
static Image* loadKTX1(Blob* blob) {
typedef struct {
struct {
uint8_t magic[12];
uint32_t endianness;
uint32_t glType;
@ -897,37 +896,49 @@ static Image* loadKTX1(Blob* blob) {
uint32_t numberOfFaces;
uint32_t numberOfMipmapLevels;
uint32_t bytesOfKeyValueData;
} KTX1Header;
} header;
char* data = blob->data;
size_t length = blob->size;
KTX1Header* header = (KTX1Header*) data;
data += sizeof(KTX1Header) + header->bytesOfKeyValueData;
length -= sizeof(KTX1Header) + header->bytesOfKeyValueData;
uint8_t magic[] = { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A };
if (length < sizeof(KTX1Header) || memcmp(header->magic, magic, sizeof(magic)) || header->endianness != 0x04030201) {
if (blob->size <= sizeof(header)) {
return NULL;
}
lovrAssert(header->pixelWidth > 0, "KTX image dimensions must be positive");
lovrAssert(header->pixelHeight > 0, "Unable to load 1D KTX images");
lovrAssert(header->pixelDepth == 0, "Unable to load 3D KTX images");
memcpy(&header, blob->data, sizeof(header));
uint8_t magic[] = { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A };
if (memcmp(header.magic, magic, sizeof(magic)) || header.endianness != 0x04030201) {
return NULL;
}
char* data = blob->data;
size_t length = blob->size;
data += sizeof(header);
length -= sizeof(header);
if (length < header.bytesOfKeyValueData) {
return NULL;
}
data += header.bytesOfKeyValueData;
length -= header.bytesOfKeyValueData;
lovrAssert(header.pixelWidth > 0, "KTX image dimensions must be positive");
lovrAssert(header.pixelHeight > 0, "Unable to load 1D KTX images");
lovrAssert(header.pixelDepth == 0, "Unable to load 3D KTX images");
Image* image = calloc(1, sizeof(Image));
lovrAssert(image, "Out of memory");
image->ref = 1;
image->width = header->pixelWidth;
image->height = header->pixelHeight;
image->layers = MAX(header->numberOfArrayElements, 1);
image->levels = MAX(header->numberOfMipmapLevels, 1);
image->width = header.pixelWidth;
image->height = header.pixelHeight;
image->layers = MAX(header.numberOfArrayElements, 1);
image->levels = MAX(header.numberOfMipmapLevels, 1);
image->blob = blob;
lovrRetain(blob);
if (header->numberOfFaces > 1) {
lovrAssert(header->numberOfFaces == 6, "KTX files must have 1 or 6 faces");
lovrAssert(header->numberOfArrayElements == 0, "KTX files with cubemap arrays are not supported");
if (header.numberOfFaces > 1) {
lovrAssert(header.numberOfFaces == 6, "KTX files must have 1 or 6 faces");
lovrAssert(header.numberOfArrayElements == 0, "KTX files with cubemap arrays are not supported");
image->flags |= IMAGE_CUBEMAP;
image->layers = 6;
}
@ -982,11 +993,11 @@ static Image* loadKTX1(Blob* blob) {
image->format = ~0u;
for (uint32_t i = 0; i < COUNTOF(lookup); i++) {
if (header->glType == lookup[i].type && header->glFormat == lookup[i].format) {
if (header->glInternalFormat == lookup[i].internalFormat) {
if (header.glType == lookup[i].type && header.glFormat == lookup[i].format) {
if (header.glInternalFormat == lookup[i].internalFormat) {
image->format = i;
break;
} else if (lookup[i].srgbInternalFormat && header->glInternalFormat == lookup[i].srgbInternalFormat) {
} else if (lookup[i].srgbInternalFormat && header.glInternalFormat == lookup[i].srgbInternalFormat) {
image->format = i;
image->flags |= IMAGE_SRGB;
break;
@ -1000,7 +1011,8 @@ static Image* loadKTX1(Blob* blob) {
uint32_t height = image->height;
size_t divisor = (image->flags & IMAGE_CUBEMAP) ? 1 : image->layers;
for (uint32_t i = 0; i < image->levels; i++) {
size_t levelSize = *(uint32_t*) data;
uint32_t levelSize;
memcpy(&levelSize, data, 4);
size_t size = measure(width, height, image->format);
lovrAssert(levelSize / divisor == size, "KTX size mismatch");
length -= 4;
@ -1123,8 +1135,8 @@ static Image* loadKTX2(Blob* blob) {
}
// Mipmaps
uint32_t width = width;
uint32_t height = height;
uint32_t width = image->width;
uint32_t height = image->height;
for (uint32_t i = 0; i < image->levels; i++) {
uint64_t offset = header->levels[i].byteOffset;
uint64_t size = header->levels[i].byteLength;

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

@ -7,6 +7,7 @@
#define MINIMP3_NO_STDIO
#include "lib/minimp3/minimp3_ex.h"
#include <stdlib.h>
#include <limits.h>
#include <string.h>
static const ma_format miniaudioFormats[] = {
@ -69,7 +70,7 @@ static uint32_t lovrSoundReadMp3(Sound* sound, uint32_t offset, uint32_t count,
uint32_t channels = lovrSoundGetChannelCount(sound);
size_t samples = mp3dec_ex_read(sound->decoder, data, count * channels);
uint32_t frames = samples / channels;
uint32_t frames = (uint32_t) (samples / channels);
sound->cursor += frames;
return frames;
}
@ -131,11 +132,13 @@ static bool loadOgg(Sound* sound, Blob* blob, bool decode) {
if (decode) {
sound->read = lovrSoundReadRaw;
uint32_t channels = lovrSoundGetChannelCount(sound);
lovrAssert(sound->frames * channels <= INT_MAX, "Decoded OGG file has too many samples");
size_t size = sound->frames * lovrSoundGetStride(sound);
void* data = calloc(1, size);
lovrAssert(data, "Out of memory");
sound->blob = lovrBlobCreate(data, size, "Sound");
if (stb_vorbis_get_samples_float_interleaved(sound->decoder, lovrSoundGetChannelCount(sound), data, size / sizeof(float)) < (int) sound->frames) {
if (stb_vorbis_get_samples_float_interleaved(sound->decoder, channels, data, size / sizeof(float)) < (int) sound->frames) {
lovrThrow("Could not decode vorbis from '%s'", blob->name);
}
stb_vorbis_close(sound->decoder);
@ -281,11 +284,12 @@ static bool loadMP3(Sound* sound, Blob* blob, bool decode) {
mp3dec_file_info_t info;
int status = mp3dec_load_buf(&decoder, blob->data, blob->size, &info, NULL, NULL);
lovrAssert(!status, "Could not decode mp3 from '%s'", blob->name);
lovrAssert(info.samples / info.channels <= UINT32_MAX, "MP3 is too long");
sound->blob = lovrBlobCreate(info.buffer, info.samples * sizeof(float), blob->name);
sound->format = SAMPLE_F32;
sound->sampleRate = info.hz;
sound->layout = info.channels == 2 ? CHANNEL_STEREO : CHANNEL_MONO;
sound->frames = info.samples / info.channels;
sound->frames = (uint32_t) (info.samples / info.channels);
sound->read = lovrSoundReadRaw;
return true;
} else {

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,66 @@ 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;
const void* data;
void** pointer;
} vertex;
struct {
Buffer* buffer;
uint32_t count;
uint32_t stride;
const void* data;
void** 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 +249,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 +330,7 @@ static struct {
bool hasTextureUpload;
bool hasMaterialUpload;
bool hasGlyphUpload;
bool hasReskin;
gpu_device_info device;
gpu_features features;
gpu_limits limits;
@ -309,6 +340,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 +363,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 +375,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 +530,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,
@ -520,6 +565,7 @@ bool lovrGraphicsInit(bool debug, bool vsync) {
void lovrGraphicsDestroy() {
if (!state.initialized) return;
cleanupPasses();
arr_free(&state.passes);
lovrRelease(state.window, lovrTextureDestroy);
for (uint32_t i = 0; i < state.attachments.length; i++) {
gpu_texture_destroy(state.attachments.data[i].texture);
@ -531,6 +577,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);
@ -707,6 +754,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;
@ -840,6 +895,7 @@ void lovrGraphicsSubmit(Pass** passes, uint32_t count) {
gpu_submit(streams, total);
cleanupPasses();
arr_clear(&state.passes);
if (state.window) {
state.window->gpu = NULL;
@ -1040,7 +1096,7 @@ Texture* lovrTextureCreate(TextureInfo* info) {
Image* image = info->imageCount == 1 ? info->images[0] : info->images[layer];
uint32_t slice = info->imageCount == 1 ? layer : 0;
uint32_t size = lovrImageGetLayerSize(image, level);
lovrCheck(size == levelSizes[level], "Texture/Image size mismatch!");
lovrCheck(size == levelSizes[level] / info->depth, "Texture/Image size mismatch!");
void* pixels = lovrImageGetLayerData(image, level, slice);
memcpy(data, pixels, size);
data += size;
@ -1370,16 +1426,26 @@ Shader* lovrGraphicsGetDefaultShader(DefaultShader type) {
info.stages[1] = lovrBlobCreate((void*) lovr_shader_unlit_frag, sizeof(lovr_shader_unlit_frag), "Unlit Fragment Shader");
info.label = "unlit";
break;
case SHADER_FONT:
info.stages[0] = lovrBlobCreate((void*) lovr_shader_unlit_vert, sizeof(lovr_shader_unlit_vert), "Unlit Vertex Shader");
info.stages[1] = lovrBlobCreate((void*) lovr_shader_font_frag, sizeof(lovr_shader_font_frag), "Font Fragment Shader");
info.label = "font";
case SHADER_CUBE:
info.stages[0] = lovrBlobCreate((void*) lovr_shader_cubemap_vert, sizeof(lovr_shader_cubemap_vert), "Cubemap Vertex Shader");
info.stages[1] = lovrBlobCreate((void*) lovr_shader_cubemap_frag, sizeof(lovr_shader_cubemap_frag), "Cubemap Fragment Shader");
info.label = "cubemap";
break;
case SHADER_PANO:
info.stages[0] = lovrBlobCreate((void*) lovr_shader_cubemap_vert, sizeof(lovr_shader_cubemap_vert), "Cubemap Vertex Shader");
info.stages[1] = lovrBlobCreate((void*) lovr_shader_equirect_frag, sizeof(lovr_shader_equirect_frag), "Equirect Fragment Shader");
info.label = "equirect";
break;
case SHADER_FILL:
info.stages[0] = lovrBlobCreate((void*) lovr_shader_fill_vert, sizeof(lovr_shader_fill_vert), "Fill Vertex Shader");
info.stages[1] = lovrBlobCreate((void*) lovr_shader_unlit_frag, sizeof(lovr_shader_unlit_frag), "Unlit Fragment Shader");
info.label = "fill";
break;
case SHADER_FONT:
info.stages[0] = lovrBlobCreate((void*) lovr_shader_unlit_vert, sizeof(lovr_shader_unlit_vert), "Unlit Vertex Shader");
info.stages[1] = lovrBlobCreate((void*) lovr_shader_font_frag, sizeof(lovr_shader_font_frag), "Font Fragment Shader");
info.label = "font";
break;
default: lovrUnreachable();
}
@ -1570,7 +1636,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]
@ -1585,11 +1657,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;
@ -1648,6 +1723,26 @@ const ShaderInfo* lovrShaderGetInfo(Shader* shader) {
return &shader->info;
}
bool lovrShaderHasStage(Shader* shader, ShaderStage stage) {
switch (stage) {
case STAGE_VERTEX: return shader->info.type == SHADER_GRAPHICS;
case STAGE_FRAGMENT: return shader->info.type == SHADER_GRAPHICS;
case STAGE_COMPUTE: return shader->info.type == SHADER_COMPUTE;
default: return false;
}
}
bool lovrShaderHasAttribute(Shader* shader, const char* name, uint32_t location) {
uint32_t hash = name ? (uint32_t) hash64(name, strlen(name)) : 0;
for (uint32_t i = 0; i < shader->attributeCount; i++) {
ShaderAttribute* attribute = &shader->attributes[i];
if (name ? (attribute->hash == hash) : (attribute->location == location)) {
return true;
}
}
return false;
}
// Material
Material* lovrMaterialCreate(MaterialInfo* info) {
@ -1747,7 +1842,7 @@ Material* lovrMaterialCreate(MaterialInfo* info) {
for (uint32_t i = 0; i < COUNTOF(textures); i++) {
lovrRetain(textures[i]);
Texture* texture = textures[i] ? textures[i] : state.defaultTexture;
lovrCheck(texture->info.type == TEXTURE_2D, "Material textures must be 2D");
lovrCheck(i == 0 || texture->info.type == TEXTURE_2D, "Material textures must be 2D");
bindings[i + 1] = (gpu_binding) { i + 1, GPU_SLOT_SAMPLED_TEXTURE, .texture = texture->gpu };
}
@ -2121,6 +2216,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) {
@ -2616,7 +3135,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++;
@ -2729,12 +3248,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;
@ -2753,7 +3272,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;
@ -2762,10 +3281,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);
@ -2777,16 +3296,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;
@ -2795,10 +3314,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);
@ -2810,10 +3329,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;
}
@ -3193,7 +3712,7 @@ void lovrPassPlane(Pass* pass, float* transform, DrawStyle style, uint32_t cols,
uint16_t b = a + 1;
uint16_t c = a + cols + 1;
uint16_t d = a + cols + 2;
uint16_t cell[] = { a, b, c, c, b, d };
uint16_t cell[] = { a, c, b, b, c, d };
memcpy(indices, cell, sizeof(cell));
indices += COUNTOF(cell);
}
@ -3680,6 +4199,26 @@ void lovrPassText(Pass* pass, Font* font, ColoredString* strings, uint32_t count
tempPop(stack);
}
void lovrPassSkybox(Pass* pass, Texture* texture) {
if (texture->info.type == TEXTURE_2D) {
lovrPassDraw(pass, &(Draw) {
.mode = VERTEX_TRIANGLES,
.shader = SHADER_PANO,
.material = texture ? lovrTextureGetMaterial(texture) : NULL,
.vertex.format = VERTEX_EMPTY,
.count = 6
});
} else {
lovrPassDraw(pass, &(Draw) {
.mode = VERTEX_TRIANGLES,
.shader = SHADER_CUBE,
.material = texture ? lovrTextureGetMaterial(texture) : NULL,
.vertex.format = VERTEX_EMPTY,
.count = 6
});
}
}
void lovrPassFill(Pass* pass, Texture* texture) {
lovrPassDraw(pass, &(Draw) {
.mode = VERTEX_TRIANGLES,
@ -3716,6 +4255,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];
if (node->skin == ~0u) 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;
@ -3873,6 +4447,7 @@ void lovrPassCopyTallyToBuffer(Pass* pass, Tally* tally, Buffer* buffer, uint32_
.stages[0] = source,
.label = "Chronophage"
});
source->data = NULL;
lovrRelease(source, lovrBlobDestroy);
}
@ -3881,8 +4456,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);
@ -3900,7 +4475,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);
@ -4053,6 +4628,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;
@ -4062,7 +4642,6 @@ static void beginFrame(void) {
state.tick = gpu_begin();
state.stream = gpu_stream_begin("Internal uploads");
state.allocator.cursor = 0;
arr_clear(&state.passes);
}
// Clean up ALL passes created during the frame, even unsubmitted ones
@ -4359,6 +4938,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;
@ -251,8 +253,10 @@ const SamplerInfo* lovrSamplerGetInfo(Sampler* sampler);
typedef enum {
SHADER_UNLIT,
SHADER_FONT,
SHADER_CUBE,
SHADER_PANO,
SHADER_FILL,
SHADER_FONT,
DEFAULT_SHADER_COUNT
} DefaultShader;
@ -287,6 +291,8 @@ Shader* lovrShaderCreate(ShaderInfo* info);
Shader* lovrShaderClone(Shader* parent, ShaderFlag* flags, uint32_t count);
void lovrShaderDestroy(void* ref);
const ShaderInfo* lovrShaderGetInfo(Shader* shader);
bool lovrShaderHasStage(Shader* shader, ShaderStage stage);
bool lovrShaderHasAttribute(Shader* shader, const char* name, uint32_t location);
// Material
@ -358,6 +364,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 {
@ -512,8 +542,10 @@ void lovrPassSphere(Pass* pass, float* transform, uint32_t segmentsH, uint32_t s
void lovrPassCylinder(Pass* pass, float* transform, bool capped, float angle1, float angle2, uint32_t segments);
void lovrPassTorus(Pass* pass, float* transform, uint32_t segmentsT, uint32_t segmentsP);
void lovrPassText(Pass* pass, Font* font, ColoredString* strings, uint32_t count, float* transform, float wrap, HorizontalAlign halign, VerticalAlign valign);
void lovrPassSkybox(Pass* pass, Texture* texture);
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],

View File

@ -36,7 +36,7 @@ static void onPermission(os_permission permission, bool granted) {
});
}
static void onQuit() {
static void onQuit(void) {
lovrEventPush((Event) {
.type = EVENT_QUIT,
.data.quit.exitCode = 0