mirror of https://github.com/bjornbytes/lovr.git
Add core/map hash table; rm lib/map;
This commit is contained in:
parent
15e9fd49e7
commit
d21911d010
|
@ -320,13 +320,13 @@ set(LOVR_SRC
|
|||
src/main.c
|
||||
src/core/arr.c
|
||||
src/core/maf.c
|
||||
src/core/map.c
|
||||
src/core/platform.c
|
||||
src/core/ref.c
|
||||
src/core/utf.c
|
||||
src/core/util.c
|
||||
src/api/api.c
|
||||
src/api/l_lovr.c
|
||||
src/lib/map/map.c
|
||||
)
|
||||
|
||||
if (LOVR_BUILD_SHARED)
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
#include <lauxlib.h>
|
||||
#include <lualib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "core/hash.h"
|
||||
#include "util.h"
|
||||
|
||||
#pragma once
|
||||
|
@ -97,27 +99,19 @@ extern const char* WrapModes[];
|
|||
struct Color;
|
||||
|
||||
typedef struct {
|
||||
uint32_t hash;
|
||||
uint64_t hash;
|
||||
void* object;
|
||||
} Proxy;
|
||||
|
||||
static inline uint32_t hash(const char* str) {
|
||||
uint32_t x = 0;
|
||||
while (*str) {
|
||||
x = (x * 65599) + *str++;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
#ifndef LUA_RIDX_MAINTHERAD
|
||||
#define LUA_RIDX_MAINTHREAD 1
|
||||
#endif
|
||||
|
||||
#define luax_len(L, i) (int) lua_objlen(L, i)
|
||||
#define luax_registertype(L, T) _luax_registertype(L, #T, lovr ## T, lovr ## T ## Destroy)
|
||||
#define luax_totype(L, i, T) (T*) _luax_totype(L, i, hash(#T))
|
||||
#define luax_checktype(L, i, T) (T*) _luax_checktype(L, i, hash(#T), #T)
|
||||
#define luax_pushtype(L, T, o) _luax_pushtype(L, #T, hash(#T), o)
|
||||
#define luax_totype(L, i, T) (T*) _luax_totype(L, i, hash64(#T, strlen(#T)))
|
||||
#define luax_checktype(L, i, T) (T*) _luax_checktype(L, i, hash64(#T, strlen(#T)), #T)
|
||||
#define luax_pushtype(L, T, o) _luax_pushtype(L, #T, hash64(#T, strlen(#T)), o)
|
||||
#define luax_checkfloat(L, i) (float) luaL_checknumber(L, i)
|
||||
#define luax_optfloat(L, i, x) (float) luaL_optnumber(L, i, x)
|
||||
#define luax_geterror(L) lua_getfield(L, LUA_REGISTRYINDEX, "_lovrerror")
|
||||
|
|
|
@ -495,7 +495,7 @@ static int l_lovrColliderSetTag(lua_State* L) {
|
|||
}
|
||||
|
||||
const char* tag = luaL_checkstring(L, 2);
|
||||
if (lovrColliderSetTag(collider, tag)) {
|
||||
if (!lovrColliderSetTag(collider, tag)) {
|
||||
return luaL_error(L, "Invalid tag %s", tag);
|
||||
}
|
||||
|
||||
|
|
|
@ -76,7 +76,7 @@ int luax_pushvariant(lua_State* L, Variant* variant) {
|
|||
case TYPE_BOOLEAN: lua_pushboolean(L, variant->value.boolean); return 1;
|
||||
case TYPE_NUMBER: lua_pushnumber(L, variant->value.number); return 1;
|
||||
case TYPE_STRING: lua_pushstring(L, variant->value.string); return 1;
|
||||
case TYPE_OBJECT: _luax_pushtype(L, variant->value.object.type, hash(variant->value.object.type), variant->value.object.pointer); return 1;
|
||||
case TYPE_OBJECT: _luax_pushtype(L, variant->value.object.type, hash64(variant->value.object.type, strlen(variant->value.object.type)), variant->value.object.pointer); return 1;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "api.h"
|
||||
#include "graphics/font.h"
|
||||
#include "data/rasterizer.h"
|
||||
|
||||
static int l_lovrFontGetWidth(lua_State* L) {
|
||||
Font* font = luax_checktype(L, 1, Font);
|
||||
|
@ -7,9 +8,10 @@ static int l_lovrFontGetWidth(lua_State* L) {
|
|||
const char* string = luaL_checklstring(L, 2, &length);
|
||||
float wrap = luax_optfloat(L, 3, 0.f);
|
||||
float width;
|
||||
float height;
|
||||
uint32_t lineCount;
|
||||
uint32_t glyphCount;
|
||||
lovrFontMeasure(font, string, length, wrap, &width, &lineCount, &glyphCount);
|
||||
lovrFontMeasure(font, string, length, wrap, &width, &height, &lineCount, &glyphCount);
|
||||
lua_pushnumber(L, width);
|
||||
lua_pushnumber(L, lineCount + 1);
|
||||
return 2;
|
||||
|
|
|
@ -12,11 +12,24 @@ void luax_pushjoint(lua_State* L, Joint* joint) {
|
|||
|
||||
Joint* luax_checkjoint(lua_State* L, int index) {
|
||||
Proxy* p = lua_touserdata(L, index);
|
||||
if (!p || (p->hash != hash("BallJoint") && p->hash != hash("DistanceJoint") && p->hash != hash("HingeJoint") && p->hash != hash("SliderJoint"))) {
|
||||
luaL_typerror(L, index, "Joint");
|
||||
return NULL;
|
||||
|
||||
if (p) {
|
||||
const uint64_t hashes[] = {
|
||||
hash64("BallJoint", strlen("BallJoint")),
|
||||
hash64("DistanceJoint", strlen("DistanceJoint")),
|
||||
hash64("HingeJoint", strlen("HingeJoint")),
|
||||
hash64("SliderJoint", strlen("SliderJoint"))
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < sizeof(hashes) / sizeof(hashes[0]); i++) {
|
||||
if (p->hash == hashes[i]) {
|
||||
return p->object;
|
||||
}
|
||||
}
|
||||
}
|
||||
return p->object;
|
||||
|
||||
luaL_typerror(L, index, "Joint");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int l_lovrJointDestroy(lua_State* L) {
|
||||
|
|
|
@ -4,29 +4,15 @@
|
|||
#include "data/modelData.h"
|
||||
#include "core/maf.h"
|
||||
|
||||
static int luax_reverseModelDataNameMap(lua_State *L, ModelData *modelData, int idx, map_u32_t *t, int count, const char *noun) {
|
||||
lovrAssert(idx > 0 && idx <= count, "Model has no %s at index %d", idx, noun);
|
||||
map_iter_t iter = map_iter(t);
|
||||
const char* key;
|
||||
while ((key = map_next(t, &iter)) != NULL) {
|
||||
uint32_t iterIdx = *map_get(t, key);
|
||||
if (iterIdx == (uint32_t) idx - 1) { // Map is 0 indexed
|
||||
lua_pushstring(L, key);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static uint32_t luax_checkanimation(lua_State* L, int index, Model* model) {
|
||||
switch (lua_type(L, index)) {
|
||||
case LUA_TSTRING: {
|
||||
const char* name = lua_tostring(L, index);
|
||||
size_t length;
|
||||
const char* name = lua_tolstring(L, index, &length);
|
||||
ModelData* modelData = lovrModelGetModelData(model);
|
||||
uint32_t* index = map_get(&modelData->animationMap, name);
|
||||
lovrAssert(index, "Model has no animation named '%s'", name);
|
||||
return *index;
|
||||
uint64_t index = map_get(&modelData->animationMap, hash64(name, length));
|
||||
lovrAssert(index != MAP_NIL, "Model has no animation named '%s'", name);
|
||||
return (uint32_t) index;
|
||||
}
|
||||
case LUA_TNUMBER: return lua_tointeger(L, index) - 1;
|
||||
default: return luaL_typerror(L, index, "number or string");
|
||||
|
@ -64,11 +50,12 @@ static int l_lovrModelPose(lua_State* L) {
|
|||
node = lua_tointeger(L, 2) - 1;
|
||||
break;
|
||||
case LUA_TSTRING: {
|
||||
const char* name = lua_tostring(L, 2);
|
||||
size_t length;
|
||||
const char* name = lua_tolstring(L, 2, &length);
|
||||
ModelData* modelData = lovrModelGetModelData(model);
|
||||
uint32_t* index = map_get(&modelData->nodeMap, name);
|
||||
lovrAssert(index, "Model has no node named '%s'", name);
|
||||
node = *index;
|
||||
uint64_t index = map_get(&modelData->nodeMap, hash64(name, length));
|
||||
lovrAssert(index != MAP_NIL, "Model has no node named '%s'", name);
|
||||
node = (uint32_t) index;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -93,11 +80,12 @@ static int l_lovrModelGetMaterial(lua_State* L) {
|
|||
material = lua_tointeger(L, 2) - 1;
|
||||
break;
|
||||
case LUA_TSTRING: {
|
||||
const char* name = lua_tostring(L, 2);
|
||||
size_t length;
|
||||
const char* name = lua_tolstring(L, 2, &length);
|
||||
ModelData* modelData = lovrModelGetModelData(model);
|
||||
uint32_t* index = map_get(&modelData->materialMap, name);
|
||||
lovrAssert(index, "Model has no material named '%s'", name);
|
||||
material = *index;
|
||||
uint64_t index = map_get(&modelData->materialMap, hash64(name, length));
|
||||
lovrAssert(index != MAP_NIL, "Model has no material named '%s'", name);
|
||||
material = (uint32_t) index;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -126,11 +114,12 @@ static int l_lovrModelGetNodePose(lua_State* L) {
|
|||
node = lua_tointeger(L, 2) - 1;
|
||||
break;
|
||||
case LUA_TSTRING: {
|
||||
const char* name = lua_tostring(L, 2);
|
||||
size_t length;
|
||||
const char* name = lua_tolstring(L, 2, &length);
|
||||
ModelData* modelData = lovrModelGetModelData(model);
|
||||
uint32_t* index = map_get(&modelData->nodeMap, name);
|
||||
lovrAssert(index, "Model has no node named '%s'", name);
|
||||
node = *index;
|
||||
uint64_t index = map_get(&modelData->nodeMap, hash64(name, length));
|
||||
lovrAssert(index != MAP_NIL, "Model has no node named '%s'", name);
|
||||
node = (uint32_t) index;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -153,23 +142,29 @@ static int l_lovrModelGetNodePose(lua_State* L) {
|
|||
|
||||
static int l_lovrModelGetAnimationName(lua_State* L) {
|
||||
Model* model = luax_checktype(L, 1, Model);
|
||||
int idx = luaL_checknumber(L, 2);
|
||||
uint32_t index = luaL_checkinteger(L, 2);
|
||||
ModelData* modelData = lovrModelGetModelData(model);
|
||||
return luax_reverseModelDataNameMap(L, modelData, idx, &modelData->animationMap, modelData->animationCount, "animation");
|
||||
lovrAssert(index > 0 && index <= modelData->animationCount, "Model has no animation at index %d", index);
|
||||
lua_pushstring(L, modelData->animations[index - 1].name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_lovrModelGetMaterialName(lua_State* L) {
|
||||
Model* model = luax_checktype(L, 1, Model);
|
||||
int idx = luaL_checknumber(L, 2);
|
||||
uint32_t index = luaL_checkinteger(L, 2);
|
||||
ModelData* modelData = lovrModelGetModelData(model);
|
||||
return luax_reverseModelDataNameMap(L, modelData, idx, &modelData->materialMap, modelData->materialCount, "material");
|
||||
lovrAssert(index > 0 && index <= modelData->materialCount, "Model has no material at index %d", index);
|
||||
lua_pushstring(L, modelData->materials[index - 1].name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_lovrModelGetNodeName(lua_State* L) {
|
||||
Model* model = luax_checktype(L, 1, Model);
|
||||
int idx = luaL_checknumber(L, 2);
|
||||
uint32_t index = luaL_checkinteger(L, 2);
|
||||
ModelData* modelData = lovrModelGetModelData(model);
|
||||
return luax_reverseModelDataNameMap(L, modelData, idx, &modelData->nodeMap, modelData->nodeCount, "node");
|
||||
lovrAssert(index > 0 && index <= modelData->nodeCount, "Model has no node at index %d", index);
|
||||
lua_pushstring(L, modelData->nodes[index - 1].name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_lovrModelGetAnimationCount(lua_State* L) {
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "graphics/buffer.h"
|
||||
#include "graphics/shader.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static int l_lovrShaderBlockGetType(lua_State* L) {
|
||||
ShaderBlock* block = luax_checktype(L, 1, ShaderBlock);
|
||||
|
|
|
@ -12,11 +12,24 @@ void luax_pushshape(lua_State* L, Shape* shape) {
|
|||
|
||||
Shape* luax_checkshape(lua_State* L, int index) {
|
||||
Proxy* p = lua_touserdata(L, index);
|
||||
if (!p || (p->hash != hash("SphereShape") && p->hash != hash("BoxShape") && p->hash != hash("CapsuleShape") && p->hash != hash("CylinderShape"))) {
|
||||
luaL_typerror(L, index, "Shape");
|
||||
return NULL;
|
||||
|
||||
if (p) {
|
||||
const uint64_t hashes[] = {
|
||||
hash64("SphereShape", strlen("SphereShape")),
|
||||
hash64("BoxShape", strlen("BoxShape")),
|
||||
hash64("CapsuleShape", strlen("CapsuleShape")),
|
||||
hash64("CylinderShape", strlen("CylinderShape"))
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < sizeof(hashes) / sizeof(hashes[0]); i++) {
|
||||
if (p->hash == hashes[i]) {
|
||||
return p->object;
|
||||
}
|
||||
}
|
||||
}
|
||||
return p->object;
|
||||
|
||||
luaL_typerror(L, index, "Shape");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int l_lovrShapeDestroy(lua_State* L) {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#pragma once
|
||||
|
||||
#define arr_t(T)\
|
||||
struct { T *data; size_t length, capacity; }
|
||||
struct { T* data; size_t length, capacity; }
|
||||
|
||||
#define arr_init(a)\
|
||||
(a)->data = NULL,\
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
#include "util.h"
|
||||
#include <stdint.h>
|
||||
|
||||
// FNV1a
|
||||
static LOVR_INLINE uint64_t hash64(const void* data, size_t length) {
|
||||
const uint8_t* bytes = data;
|
||||
uint64_t hash = 0xcbf29ce484222325;
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
hash = (hash ^ bytes[i]) * 0x100000001b3;
|
||||
}
|
||||
return hash;
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
#include "map.h"
|
||||
#include "util.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static uint32_t prevpo2(uint32_t x) {
|
||||
x |= x >> 1;
|
||||
x |= x >> 2;
|
||||
x |= x >> 4;
|
||||
x |= x >> 8;
|
||||
x |= x >> 16;
|
||||
return x - (x >> 1);
|
||||
}
|
||||
|
||||
static void map_rehash(map_t* map) {
|
||||
map_t old = *map;
|
||||
map->size <<= 1;
|
||||
map->hashes = malloc(2 * map->size * sizeof(uint64_t));
|
||||
map->values = map->hashes + map->size;
|
||||
lovrAssert(map->size && map->hashes, "Out of memory");
|
||||
memset(map->hashes, 0xff, 2 * map->size * sizeof(uint64_t));
|
||||
|
||||
if (old.hashes) {
|
||||
uint64_t mask = map->size - 1;
|
||||
for (uint32_t i = 0; i < old.size; i++) {
|
||||
if (old.hashes[i] != MAP_NIL) {
|
||||
uint64_t index = old.hashes[i] & mask;
|
||||
while (map->hashes[index] != MAP_NIL) {
|
||||
index = (index + 1) & mask;
|
||||
}
|
||||
map->hashes[index] = old.hashes[i];
|
||||
map->values[index] = old.values[i];
|
||||
}
|
||||
}
|
||||
free(old.hashes);
|
||||
}
|
||||
}
|
||||
|
||||
static LOVR_INLINE uint64_t map_find(map_t* map, uint64_t hash) {
|
||||
uint64_t mask = map->size - 1;
|
||||
uint64_t h = hash & mask;
|
||||
|
||||
while (map->hashes[h] != hash && map->hashes[h] != MAP_NIL) {
|
||||
h = (h + 1) & mask;
|
||||
}
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
void map_init(map_t* map, uint32_t n) {
|
||||
map->size = prevpo2(n) + !n;
|
||||
map->used = 0;
|
||||
map->hashes = NULL;
|
||||
map_rehash(map);
|
||||
}
|
||||
|
||||
void map_free(map_t* map) {
|
||||
free(map->hashes);
|
||||
}
|
||||
|
||||
uint64_t map_get(map_t* map, uint64_t hash) {
|
||||
return map->values[map_find(map, hash)];
|
||||
}
|
||||
|
||||
void map_set(map_t* map, uint64_t hash, uint64_t value) {
|
||||
if (map->used >= (map->size >> 1) + (map->size >> 2)) {
|
||||
map_rehash(map);
|
||||
}
|
||||
|
||||
uint64_t h = map_find(map, hash);
|
||||
map->used += map->hashes[h] == MAP_NIL;
|
||||
map->hashes[h] = hash;
|
||||
map->values[h] = value;
|
||||
}
|
||||
|
||||
void map_remove(map_t* map, uint64_t hash) {
|
||||
uint64_t h = map_find(map, hash);
|
||||
|
||||
if (map->hashes[h] == MAP_NIL) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t mask = map->size - 1;
|
||||
uint64_t i = h;
|
||||
|
||||
do {
|
||||
i = (i + 1) & mask;
|
||||
uint64_t x = map->hashes[i] & mask;
|
||||
// Removing a key from an open-addressed hash table is complicated
|
||||
if ((i > h && (x <= h || x > i)) || (i < h && (x <= h && x > i))) {
|
||||
map->hashes[h] = map->hashes[i];
|
||||
map->values[h] = map->values[i];
|
||||
h = i;
|
||||
}
|
||||
} while (map->hashes[i] != MAP_NIL);
|
||||
|
||||
map->hashes[i] = MAP_NIL;
|
||||
map->values[i] = MAP_NIL;
|
||||
map->used--;
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
#include <stdint.h>
|
||||
|
||||
#pragma once
|
||||
|
||||
#define MAP_NIL UINT64_MAX
|
||||
|
||||
typedef struct {
|
||||
uint32_t size;
|
||||
uint32_t used;
|
||||
uint64_t* hashes;
|
||||
uint64_t* values;
|
||||
} map_t;
|
||||
|
||||
void map_init(map_t* map, uint32_t n);
|
||||
void map_free(map_t* map);
|
||||
uint64_t map_get(map_t* map, uint64_t hash);
|
||||
void map_set(map_t* map, uint64_t hash, uint64_t value);
|
||||
void map_remove(map_t* map, uint64_t hash);
|
|
@ -1,20 +0,0 @@
|
|||
Copyright (c) 2014 rxi
|
||||
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -1,193 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) 2014 rxi
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "map.h"
|
||||
|
||||
struct map_node_t {
|
||||
unsigned hash;
|
||||
void *value;
|
||||
map_node_t *next;
|
||||
/* char key[]; */
|
||||
/* char value[]; */
|
||||
};
|
||||
|
||||
|
||||
static unsigned map_hash(const char *str) {
|
||||
unsigned hash = 5381;
|
||||
while (*str) {
|
||||
hash = ((hash << 5) + hash) ^ *str++;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
|
||||
static map_node_t *map_newnode(const char *key, void *value, int vsize) {
|
||||
map_node_t *node;
|
||||
int ksize = (int) strlen(key) + 1;
|
||||
int voffset = ksize + ((sizeof(void*) - ksize) % sizeof(void*));
|
||||
node = malloc(sizeof(*node) + voffset + vsize);
|
||||
if (!node) return NULL;
|
||||
memcpy(node + 1, key, ksize);
|
||||
node->hash = map_hash(key);
|
||||
node->value = ((char*) (node + 1)) + voffset;
|
||||
memcpy(node->value, value, vsize);
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
static int map_bucketidx(map_base_t *m, unsigned hash) {
|
||||
/* If the implementation is changed to allow a non-power-of-2 bucket count,
|
||||
* the line below should be changed to use mod instead of AND */
|
||||
return hash & (m->nbuckets - 1);
|
||||
}
|
||||
|
||||
|
||||
static void map_addnode(map_base_t *m, map_node_t *node) {
|
||||
int n = map_bucketidx(m, node->hash);
|
||||
node->next = m->buckets[n];
|
||||
m->buckets[n] = node;
|
||||
}
|
||||
|
||||
|
||||
static int map_resize(map_base_t *m, int nbuckets) {
|
||||
map_node_t *nodes, *node, *next;
|
||||
map_node_t **buckets;
|
||||
int i;
|
||||
/* Chain all nodes together */
|
||||
nodes = NULL;
|
||||
i = m->nbuckets;
|
||||
while (i--) {
|
||||
node = (m->buckets)[i];
|
||||
while (node) {
|
||||
next = node->next;
|
||||
node->next = nodes;
|
||||
nodes = node;
|
||||
node = next;
|
||||
}
|
||||
}
|
||||
/* Reset buckets */
|
||||
buckets = realloc(m->buckets, sizeof(*m->buckets) * nbuckets);
|
||||
if (buckets != NULL) {
|
||||
m->buckets = buckets;
|
||||
m->nbuckets = nbuckets;
|
||||
}
|
||||
if (m->buckets) {
|
||||
memset(m->buckets, 0, sizeof(*m->buckets) * m->nbuckets);
|
||||
/* Re-add nodes to buckets */
|
||||
node = nodes;
|
||||
while (node) {
|
||||
next = node->next;
|
||||
map_addnode(m, node);
|
||||
node = next;
|
||||
}
|
||||
}
|
||||
/* Return error code if realloc() failed */
|
||||
return (buckets == NULL) ? -1 : 0;
|
||||
}
|
||||
|
||||
|
||||
static map_node_t **map_getref(map_base_t *m, const char *key) {
|
||||
unsigned hash = map_hash(key);
|
||||
map_node_t **next;
|
||||
if (m->nbuckets > 0) {
|
||||
next = &m->buckets[map_bucketidx(m, hash)];
|
||||
while (*next) {
|
||||
if ((*next)->hash == hash && !strcmp((char*) (*next + 1), key)) {
|
||||
return next;
|
||||
}
|
||||
next = &(*next)->next;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void map_deinit_(map_base_t *m) {
|
||||
map_node_t *next, *node;
|
||||
int i;
|
||||
i = m->nbuckets;
|
||||
while (i--) {
|
||||
node = m->buckets[i];
|
||||
while (node) {
|
||||
next = node->next;
|
||||
free(node);
|
||||
node = next;
|
||||
}
|
||||
}
|
||||
free(m->buckets);
|
||||
}
|
||||
|
||||
|
||||
void *map_get_(map_base_t *m, const char *key) {
|
||||
map_node_t **next = map_getref(m, key);
|
||||
return next ? (*next)->value : NULL;
|
||||
}
|
||||
|
||||
|
||||
int map_set_(map_base_t *m, const char *key, void *value, int vsize) {
|
||||
int n, err;
|
||||
map_node_t **next, *node;
|
||||
/* Find & replace existing node */
|
||||
next = map_getref(m, key);
|
||||
if (next) {
|
||||
memcpy((*next)->value, value, vsize);
|
||||
return 0;
|
||||
}
|
||||
/* Add new node */
|
||||
node = map_newnode(key, value, vsize);
|
||||
if (node == NULL) goto fail;
|
||||
if (m->nnodes >= m->nbuckets) {
|
||||
n = (m->nbuckets > 0) ? (m->nbuckets << 1) : 1;
|
||||
err = map_resize(m, n);
|
||||
if (err) goto fail;
|
||||
}
|
||||
map_addnode(m, node);
|
||||
m->nnodes++;
|
||||
return 0;
|
||||
fail:
|
||||
if (node) free(node);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
void map_remove_(map_base_t *m, const char *key) {
|
||||
map_node_t *node;
|
||||
map_node_t **next = map_getref(m, key);
|
||||
if (next) {
|
||||
node = *next;
|
||||
*next = (*next)->next;
|
||||
free(node);
|
||||
m->nnodes--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
map_iter_t map_iter_(void) {
|
||||
map_iter_t iter;
|
||||
iter.bucketidx = -1;
|
||||
iter.node = NULL;
|
||||
return iter;
|
||||
}
|
||||
|
||||
|
||||
const char *map_next_(map_base_t *m, map_iter_t *iter) {
|
||||
if (iter->node) {
|
||||
iter->node = iter->node->next;
|
||||
if (iter->node == NULL) goto nextBucket;
|
||||
} else {
|
||||
nextBucket:
|
||||
do {
|
||||
if (++iter->bucketidx >= m->nbuckets) {
|
||||
return NULL;
|
||||
}
|
||||
iter->node = m->buckets[iter->bucketidx];
|
||||
} while (iter->node == NULL);
|
||||
}
|
||||
return (char*) (iter->node + 1);
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) 2014 rxi
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef MAP_H
|
||||
#define MAP_H
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define MAP_VERSION "0.1.0"
|
||||
|
||||
struct map_node_t;
|
||||
typedef struct map_node_t map_node_t;
|
||||
|
||||
typedef struct {
|
||||
map_node_t **buckets;
|
||||
unsigned nbuckets, nnodes;
|
||||
} map_base_t;
|
||||
|
||||
typedef struct {
|
||||
unsigned bucketidx;
|
||||
map_node_t *node;
|
||||
} map_iter_t;
|
||||
|
||||
|
||||
#define map_t(T)\
|
||||
struct { map_base_t base; T *ref; T tmp; }
|
||||
|
||||
|
||||
#define map_init(m)\
|
||||
memset(m, 0, sizeof(*(m)))
|
||||
|
||||
|
||||
#define map_deinit(m)\
|
||||
map_deinit_(&(m)->base)
|
||||
|
||||
|
||||
#define map_get(m, key)\
|
||||
( (m)->ref = map_get_(&(m)->base, key) )
|
||||
|
||||
|
||||
#define map_set(m, key, value)\
|
||||
( (m)->tmp = (value),\
|
||||
map_set_(&(m)->base, key, &(m)->tmp, sizeof((m)->tmp)) )
|
||||
|
||||
|
||||
#define map_remove(m, key)\
|
||||
map_remove_(&(m)->base, key)
|
||||
|
||||
|
||||
#define map_iter(m)\
|
||||
map_iter_()
|
||||
|
||||
|
||||
#define map_next(m, iter)\
|
||||
map_next_(&(m)->base, iter)
|
||||
|
||||
|
||||
void map_deinit_(map_base_t *m);
|
||||
void *map_get_(map_base_t *m, const char *key);
|
||||
int map_set_(map_base_t *m, const char *key, void *value, int vsize);
|
||||
void map_remove_(map_base_t *m, const char *key);
|
||||
map_iter_t map_iter_(void);
|
||||
const char *map_next_(map_base_t *m, map_iter_t *iter);
|
||||
|
||||
|
||||
typedef map_t(void*) map_void_t;
|
||||
typedef map_t(char*) map_str_t;
|
||||
typedef map_t(int) map_int_t;
|
||||
typedef map_t(char) map_char_t;
|
||||
typedef map_t(float) map_float_t;
|
||||
typedef map_t(double) map_double_t;
|
||||
|
||||
#endif
|
|
@ -23,9 +23,9 @@ void lovrModelDataDestroy(void* ref) {
|
|||
for (uint32_t i = 0; i < model->textureCount; i++) {
|
||||
lovrRelease(TextureData, model->textures[i]);
|
||||
}
|
||||
map_deinit(&model->animationMap);
|
||||
map_deinit(&model->materialMap);
|
||||
map_deinit(&model->nodeMap);
|
||||
map_free(&model->animationMap);
|
||||
map_free(&model->materialMap);
|
||||
map_free(&model->nodeMap);
|
||||
free(model->data);
|
||||
}
|
||||
|
||||
|
@ -45,6 +45,7 @@ void lovrModelDataAllocate(ModelData* model) {
|
|||
totalSize += sizes[9] = model->channelCount * sizeof(ModelAnimationChannel);
|
||||
totalSize += sizes[10] = model->childCount * sizeof(uint32_t);
|
||||
totalSize += sizes[11] = model->jointCount * sizeof(uint32_t);
|
||||
totalSize += sizes[12] = model->charCount * sizeof(char);
|
||||
|
||||
size_t offset = 0;
|
||||
char* p = model->data = calloc(1, totalSize);
|
||||
|
@ -61,8 +62,9 @@ void lovrModelDataAllocate(ModelData* model) {
|
|||
model->channels = (ModelAnimationChannel*) (p + offset), offset += sizes[9];
|
||||
model->children = (uint32_t*) (p + offset), offset += sizes[10];
|
||||
model->joints = (uint32_t*) (p + offset), offset += sizes[11];
|
||||
model->chars = (char*) (p + offset), offset += sizes[12];
|
||||
|
||||
map_init(&model->animationMap);
|
||||
map_init(&model->materialMap);
|
||||
map_init(&model->nodeMap);
|
||||
map_init(&model->animationMap, model->animationCount);
|
||||
map_init(&model->materialMap, model->materialCount);
|
||||
map_init(&model->nodeMap, model->nodeCount);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#include "util.h"
|
||||
#include "lib/map/map.h"
|
||||
#include "core/map.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
@ -133,12 +133,14 @@ typedef struct {
|
|||
} ModelAnimationChannel;
|
||||
|
||||
typedef struct {
|
||||
const char* name;
|
||||
ModelAnimationChannel* channels;
|
||||
uint32_t channelCount;
|
||||
float duration;
|
||||
} ModelAnimation;
|
||||
|
||||
typedef struct {
|
||||
const char* name;
|
||||
float scalars[MAX_MATERIAL_SCALARS];
|
||||
Color colors[MAX_MATERIAL_COLORS];
|
||||
uint32_t textures[MAX_MATERIAL_TEXTURES];
|
||||
|
@ -154,6 +156,7 @@ typedef struct {
|
|||
} ModelPrimitive;
|
||||
|
||||
typedef struct {
|
||||
const char* name;
|
||||
float transform[16];
|
||||
float translation[4];
|
||||
float rotation[4];
|
||||
|
@ -172,8 +175,6 @@ typedef struct {
|
|||
float* inverseBindMatrices;
|
||||
} ModelSkin;
|
||||
|
||||
typedef map_t(uint32_t) map_u32_t;
|
||||
|
||||
typedef struct ModelData {
|
||||
void* data;
|
||||
struct Blob** blobs;
|
||||
|
@ -200,13 +201,15 @@ typedef struct ModelData {
|
|||
ModelAnimationChannel* channels;
|
||||
uint32_t* children;
|
||||
uint32_t* joints;
|
||||
char* chars;
|
||||
uint32_t channelCount;
|
||||
uint32_t childCount;
|
||||
uint32_t jointCount;
|
||||
uint32_t charCount;
|
||||
|
||||
map_u32_t animationMap;
|
||||
map_u32_t materialMap;
|
||||
map_u32_t nodeMap;
|
||||
map_t animationMap;
|
||||
map_t materialMap;
|
||||
map_t nodeMap;
|
||||
} ModelData;
|
||||
|
||||
ModelData* lovrModelDataInit(ModelData* model, struct Blob* blob);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "data/blob.h"
|
||||
#include "data/textureData.h"
|
||||
#include "filesystem/filesystem.h"
|
||||
#include "core/hash.h"
|
||||
#include "core/maf.h"
|
||||
#include "core/ref.h"
|
||||
#include "lib/jsmn/jsmn.h"
|
||||
|
@ -128,22 +129,6 @@ static void* decodeBase64(char* str, size_t length, size_t decodedSize) {
|
|||
return data;
|
||||
}
|
||||
|
||||
// Kinda like count += sum(map(arr, obj => #obj[key]))
|
||||
static jsmntok_t* aggregate(const char* json, jsmntok_t* token, const char* target, uint32_t* count) {
|
||||
for (int i = (token++)->size; i > 0; i--) {
|
||||
if (token->size > 0) {
|
||||
for (int k = (token++)->size; k > 0; k--) {
|
||||
gltfString key = NOM_STR(json, token);
|
||||
if (STR_EQ(key, target)) {
|
||||
*count += token->size;
|
||||
}
|
||||
token += NOM_VALUE(json, token);
|
||||
}
|
||||
}
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
static jsmntok_t* resolveTexture(const char* json, jsmntok_t* token, ModelMaterial* material, MaterialTexture textureType, gltfTexture* textures, gltfSampler* samplers) {
|
||||
for (int k = (token++)->size; k > 0; k--) {
|
||||
gltfString key = NOM_STR(json, token);
|
||||
|
@ -271,6 +256,7 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source) {
|
|||
gltfString key = NOM_STR(json, t);
|
||||
if (STR_EQ(key, "channels")) { model->channelCount += t->size; }
|
||||
else if (STR_EQ(key, "samplers")) { samplerCount += t->size; }
|
||||
else if (STR_EQ(key, "name")) { model->charCount += t->end - t->start + 1; }
|
||||
t += NOM_VALUE(json, t);
|
||||
}
|
||||
}
|
||||
|
@ -390,7 +376,13 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source) {
|
|||
} else if (STR_EQ(key, "materials")) {
|
||||
info.materials = token;
|
||||
model->materialCount = token->size;
|
||||
token += NOM_VALUE(json, token);
|
||||
for (int i = (token++)->size; i > 0; i--) {
|
||||
for (int k = (token++)->size; k > 0; k--) {
|
||||
gltfString key = NOM_STR(json, token);
|
||||
if (STR_EQ(key, "name")) { model->charCount += token->end - token->start + 1; }
|
||||
token += NOM_VALUE(json, token);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (STR_EQ(key, "meshes")) {
|
||||
info.meshes = token;
|
||||
|
@ -414,7 +406,16 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source) {
|
|||
} else if (STR_EQ(key, "nodes")) {
|
||||
info.nodes = token;
|
||||
model->nodeCount = token->size;
|
||||
token = aggregate(json, token, "children", &model->childCount);
|
||||
for (int i = (token++)->size; i > 0; i--) {
|
||||
if (token->size > 0) {
|
||||
for (int k = (token++)->size; k > 0; k--) {
|
||||
gltfString key = NOM_STR(json, token);
|
||||
if (STR_EQ(key, "children")) { model->childCount += token->size; }
|
||||
else if (STR_EQ(key, "name")) { model->charCount += token->end - token->start + 1; }
|
||||
token += NOM_VALUE(json, token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else if (STR_EQ(key, "scene")) {
|
||||
rootScene = NOM_INT(json, token);
|
||||
|
@ -440,7 +441,13 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source) {
|
|||
} else if (STR_EQ(key, "skins")) {
|
||||
info.skins = token;
|
||||
model->skinCount = token->size;
|
||||
token = aggregate(json, token, "joints", &model->jointCount);
|
||||
for (int i = (token++)->size; i > 0; i--) {
|
||||
for (int k = (token++)->size; k > 0; k--) {
|
||||
gltfString key = NOM_STR(json, token);
|
||||
if (STR_EQ(key, "joints")) { model->jointCount += token->size; }
|
||||
token += NOM_VALUE(json, token);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
token += NOM_VALUE(json, token);
|
||||
|
@ -633,9 +640,10 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source) {
|
|||
token += NOM_VALUE(json, token);
|
||||
} else if (STR_EQ(key, "name")) {
|
||||
gltfString name = NOM_STR(json, token);
|
||||
name.data[name.length] = '\0';
|
||||
map_set(&model->animationMap, name.data, model->animationCount - i);
|
||||
name.data[name.length] = '"';
|
||||
map_set(&model->animationMap, hash64(name.data, name.length), model->animationCount - i);
|
||||
memcpy(model->chars, name.data, name.length);
|
||||
animation->name = model->chars;
|
||||
model->chars += name.length + 1;
|
||||
} else {
|
||||
token += NOM_VALUE(json, token);
|
||||
}
|
||||
|
@ -726,9 +734,10 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source) {
|
|||
material->colors[COLOR_EMISSIVE].b = NOM_FLOAT(json, token);
|
||||
} else if (STR_EQ(key, "name")) {
|
||||
gltfString name = NOM_STR(json, token);
|
||||
name.data[name.length] = '\0';
|
||||
map_set(&model->materialMap, name.data, model->materialCount - i);
|
||||
name.data[name.length] = '"';
|
||||
map_set(&model->materialMap, hash64(name.data, name.length), model->materialCount - i);
|
||||
memcpy(model->chars, name.data, name.length);
|
||||
material->name = model->chars;
|
||||
model->chars += name.length + 1;
|
||||
} else {
|
||||
token += NOM_VALUE(json, token);
|
||||
}
|
||||
|
@ -846,9 +855,10 @@ ModelData* lovrModelDataInitGltf(ModelData* model, Blob* source) {
|
|||
scale[2] = NOM_FLOAT(json, token);
|
||||
} else if (STR_EQ(key, "name")) {
|
||||
gltfString name = NOM_STR(json, token);
|
||||
name.data[name.length] = '\0';
|
||||
map_set(&model->nodeMap, name.data, model->nodeCount - i);
|
||||
name.data[name.length] = '"';
|
||||
map_set(&model->nodeMap, hash64(name.data, name.length), model->nodeCount - i);
|
||||
memcpy(model->chars, name.data, name.length);
|
||||
node->name = model->chars;
|
||||
model->chars += name.length + 1;
|
||||
} else {
|
||||
token += NOM_VALUE(json, token);
|
||||
}
|
||||
|
|
|
@ -3,10 +3,11 @@
|
|||
#include "data/textureData.h"
|
||||
#include "filesystem/filesystem.h"
|
||||
#include "core/arr.h"
|
||||
#include "core/hash.h"
|
||||
#include "core/maf.h"
|
||||
#include "core/map.h"
|
||||
#include "core/ref.h"
|
||||
#include "core/util.h"
|
||||
#include "lib/map/map.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <float.h>
|
||||
|
@ -24,7 +25,7 @@ typedef arr_t(objGroup) arr_group_t;
|
|||
|
||||
#define STARTS_WITH(a, b) !strncmp(a, b, strlen(b))
|
||||
|
||||
static void parseMtl(char* path, arr_texturedata_t* textures, arr_material_t* materials, map_u32_t* names, char* base) {
|
||||
static void parseMtl(char* path, arr_texturedata_t* textures, arr_material_t* materials, map_t* names, char* base) {
|
||||
size_t length = 0;
|
||||
char* data = lovrFilesystemRead(path, -1, &length);
|
||||
lovrAssert(data && length > 0, "Unable to read mtl from '%s'", path);
|
||||
|
@ -35,9 +36,9 @@ static void parseMtl(char* path, arr_texturedata_t* textures, arr_material_t* ma
|
|||
|
||||
if (STARTS_WITH(s, "newmtl ")) {
|
||||
char name[128];
|
||||
bool hasName = sscanf(s + 7, "%s\n%n", name, &lineLength);
|
||||
lovrAssert(hasName, "Bad OBJ: Expected a material name");
|
||||
map_set(names, name, (uint32_t) materials->length);
|
||||
size_t length = sscanf(s + 7, "%s\n%n", name, &lineLength);
|
||||
lovrAssert(length > 0, "Bad OBJ: Expected a material name");
|
||||
map_set(names, hash64(name, length), materials->length);
|
||||
arr_push(materials, ((ModelMaterial) {
|
||||
.scalars[SCALAR_METALNESS] = 1.f,
|
||||
.scalars[SCALAR_ROUGHNESS] = 1.f,
|
||||
|
@ -102,8 +103,8 @@ ModelData* lovrModelDataInitObj(ModelData* model, Blob* source) {
|
|||
arr_material_t materials;
|
||||
arr_t(float) vertexBlob;
|
||||
arr_t(int) indexBlob;
|
||||
map_u32_t materialMap;
|
||||
map_int_t vertexMap;
|
||||
map_t materialMap;
|
||||
map_t vertexMap;
|
||||
arr_t(float) positions;
|
||||
arr_t(float) normals;
|
||||
arr_t(float) uvs;
|
||||
|
@ -111,10 +112,10 @@ ModelData* lovrModelDataInitObj(ModelData* model, Blob* source) {
|
|||
arr_init(&groups);
|
||||
arr_init(&textures);
|
||||
arr_init(&materials);
|
||||
map_init(&materialMap);
|
||||
map_init(&materialMap, 0);
|
||||
arr_init(&vertexBlob);
|
||||
arr_init(&indexBlob);
|
||||
map_init(&vertexMap);
|
||||
map_init(&vertexMap, 0);
|
||||
arr_init(&positions);
|
||||
arr_init(&normals);
|
||||
arr_init(&uvs);
|
||||
|
@ -158,15 +159,15 @@ ModelData* lovrModelDataInitObj(ModelData* model, Blob* source) {
|
|||
char terminator = i == 2 ? '\n' : ' ';
|
||||
char* space = strchr(s, terminator);
|
||||
if (space) {
|
||||
*space = '\0'; // I'll be back
|
||||
int* index = map_get(&vertexMap, s);
|
||||
if (index) {
|
||||
arr_push(&indexBlob, *index);
|
||||
uint64_t hash = hash64(s, space - s);
|
||||
uint64_t index = map_get(&vertexMap, hash);
|
||||
if (index != MAP_NIL) {
|
||||
arr_push(&indexBlob, index);
|
||||
} else {
|
||||
int v, vt, vn;
|
||||
int newIndex = (int) vertexBlob.length / 8;
|
||||
uint64_t newIndex = vertexBlob.length / 8;
|
||||
arr_push(&indexBlob, newIndex);
|
||||
map_set(&vertexMap, s, newIndex);
|
||||
map_set(&vertexMap, hash, newIndex);
|
||||
|
||||
// Can be improved
|
||||
if (sscanf(s, "%d/%d/%d", &v, &vt, &vn) == 3) {
|
||||
|
@ -188,7 +189,6 @@ ModelData* lovrModelDataInitObj(ModelData* model, Blob* source) {
|
|||
lovrThrow("Bad OBJ: Unknown face format");
|
||||
}
|
||||
}
|
||||
*space = terminator;
|
||||
s = space + 1;
|
||||
}
|
||||
}
|
||||
|
@ -203,21 +203,21 @@ ModelData* lovrModelDataInitObj(ModelData* model, Blob* source) {
|
|||
parseMtl(path, &textures, &materials, &materialMap, base);
|
||||
} else if (STARTS_WITH(data, "usemtl ")) {
|
||||
char name[128];
|
||||
bool hasName = sscanf(data + 7, "%s\n%n", name, &lineLength);
|
||||
uint32_t* material = map_get(&materialMap, name);
|
||||
lovrAssert(hasName, "Bad OBJ: Expected a valid material name");
|
||||
uint64_t length = sscanf(data + 7, "%s\n%n", name, &lineLength);
|
||||
uint64_t material = map_get(&materialMap, hash64(name, length));
|
||||
lovrAssert(length > 0, "Bad OBJ: Expected a valid material name");
|
||||
|
||||
// If the last group didn't have any faces, just reuse it, otherwise make a new group
|
||||
objGroup* group = &groups.data[groups.length - 1];
|
||||
if (group->count > 0) {
|
||||
int start = group->start + group->count; // Don't put this in the compound literal (realloc)
|
||||
arr_push(&groups, ((objGroup) {
|
||||
.material = material ? *material : -1,
|
||||
.material = material == MAP_NIL ? -1 : material,
|
||||
.start = start,
|
||||
.count = 0
|
||||
}));
|
||||
} else {
|
||||
group->material = material ? *material : -1;
|
||||
group->material = material == MAP_NIL ? -1 : material;
|
||||
}
|
||||
} else {
|
||||
char* newline = memchr(data, '\n', length);
|
||||
|
@ -332,7 +332,7 @@ ModelData* lovrModelDataInitObj(ModelData* model, Blob* source) {
|
|||
arr_free(&groups);
|
||||
arr_free(&textures);
|
||||
arr_free(&materials);
|
||||
map_deinit(&vertexMap);
|
||||
map_free(&vertexMap);
|
||||
arr_free(&positions);
|
||||
arr_free(&normals);
|
||||
arr_free(&uvs);
|
||||
|
|
|
@ -1,12 +1,37 @@
|
|||
#include "graphics/font.h"
|
||||
#include "graphics/texture.h"
|
||||
#include "data/rasterizer.h"
|
||||
#include "data/textureData.h"
|
||||
#include "core/arr.h"
|
||||
#include "core/hash.h"
|
||||
#include "core/map.h"
|
||||
#include "core/ref.h"
|
||||
#include "core/utf.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
typedef struct {
|
||||
uint32_t x;
|
||||
uint32_t y;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t rowHeight;
|
||||
uint32_t padding;
|
||||
arr_t(Glyph) glyphs;
|
||||
map_t glyphMap;
|
||||
} FontAtlas;
|
||||
|
||||
struct Font {
|
||||
Rasterizer* rasterizer;
|
||||
Texture* texture;
|
||||
FontAtlas atlas;
|
||||
map_t kerning;
|
||||
float lineHeight;
|
||||
float pixelDensity;
|
||||
bool flip;
|
||||
};
|
||||
|
||||
static float* lovrFontAlignLine(float* x, float* lineEnd, float width, HorizontalAlign halign) {
|
||||
while (x < lineEnd) {
|
||||
if (halign == ALIGN_CENTER) {
|
||||
|
@ -21,12 +46,18 @@ static float* lovrFontAlignLine(float* x, float* lineEnd, float width, Horizonta
|
|||
return x;
|
||||
}
|
||||
|
||||
Font* lovrFontInit(Font* font, Rasterizer* rasterizer) {
|
||||
static Glyph* lovrFontGetGlyph(Font* font, uint32_t codepoint);
|
||||
static void lovrFontAddGlyph(Font* font, Glyph* glyph);
|
||||
static void lovrFontExpandTexture(Font* font);
|
||||
static void lovrFontCreateTexture(Font* font);
|
||||
|
||||
Font* lovrFontCreate(Rasterizer* rasterizer) {
|
||||
Font* font = lovrAlloc(Font);
|
||||
lovrRetain(rasterizer);
|
||||
font->rasterizer = rasterizer;
|
||||
font->lineHeight = 1.f;
|
||||
font->pixelDensity = (float) font->rasterizer->height;
|
||||
map_init(&font->kerning);
|
||||
map_init(&font->kerning, 0);
|
||||
|
||||
// Atlas
|
||||
uint32_t padding = 1;
|
||||
|
@ -35,7 +66,8 @@ Font* lovrFontInit(Font* font, Rasterizer* rasterizer) {
|
|||
font->atlas.width = 128;
|
||||
font->atlas.height = 128;
|
||||
font->atlas.padding = padding;
|
||||
map_init(&font->atlas.glyphs);
|
||||
arr_init(&font->atlas.glyphs);
|
||||
map_init(&font->atlas.glyphMap, 0);
|
||||
|
||||
// Set initial atlas size
|
||||
while (font->atlas.height < 4 * rasterizer->size) {
|
||||
|
@ -52,20 +84,21 @@ void lovrFontDestroy(void* ref) {
|
|||
Font* font = ref;
|
||||
lovrRelease(Rasterizer, font->rasterizer);
|
||||
lovrRelease(Texture, font->texture);
|
||||
const char* key;
|
||||
map_iter_t iter = map_iter(&font->atlas.glyphs);
|
||||
while ((key = map_next(&font->atlas.glyphs, &iter)) != NULL) {
|
||||
Glyph* glyph = map_get(&font->atlas.glyphs, key);
|
||||
lovrRelease(TextureData, glyph->data);
|
||||
for (size_t i = 0; i < font->atlas.glyphs.length; i++) {
|
||||
lovrRelease(TextureData, font->atlas.glyphs.data[i].data);
|
||||
}
|
||||
map_deinit(&font->atlas.glyphs);
|
||||
map_deinit(&font->kerning);
|
||||
map_free(&font->atlas.glyphMap);
|
||||
map_free(&font->kerning);
|
||||
}
|
||||
|
||||
Rasterizer* lovrFontGetRasterizer(Font* font) {
|
||||
return font->rasterizer;
|
||||
}
|
||||
|
||||
Texture* lovrFontGetTexture(Font* font) {
|
||||
return font->texture;
|
||||
}
|
||||
|
||||
void lovrFontRender(Font* font, const char* str, size_t length, float wrap, HorizontalAlign halign, float* vertices, uint16_t* indices, uint16_t baseVertex) {
|
||||
FontAtlas* atlas = &font->atlas;
|
||||
bool flip = font->flip;
|
||||
|
@ -154,7 +187,7 @@ void lovrFontRender(Font* font, const char* str, size_t length, float wrap, Hori
|
|||
lovrFontAlignLine(lineStart, vertexCursor, cx, halign);
|
||||
}
|
||||
|
||||
void lovrFontMeasure(Font* font, const char* str, size_t length, float wrap, float* width, uint32_t* lineCount, uint32_t* glyphCount) {
|
||||
void lovrFontMeasure(Font* font, const char* str, size_t length, float wrap, float* width, float* height, uint32_t* lineCount, uint32_t* glyphCount) {
|
||||
float x = 0.f;
|
||||
const char* end = str + length;
|
||||
size_t bytes;
|
||||
|
@ -195,6 +228,7 @@ void lovrFontMeasure(Font* font, const char* str, size_t length, float wrap, flo
|
|||
}
|
||||
|
||||
*width = MAX(*width, x * scale);
|
||||
*height = ((*lineCount + 1) * font->rasterizer->height * font->lineHeight) * (font->flip ? -1 : 1);
|
||||
}
|
||||
|
||||
float lovrFontGetHeight(Font* font) {
|
||||
|
@ -229,17 +263,16 @@ void lovrFontSetFlipEnabled(Font* font, bool flip) {
|
|||
font->flip = flip;
|
||||
}
|
||||
|
||||
int32_t lovrFontGetKerning(Font* font, unsigned int left, unsigned int right) {
|
||||
char key[12];
|
||||
snprintf(key, 12, "%d,%d", left, right);
|
||||
int32_t lovrFontGetKerning(Font* font, uint32_t left, uint32_t right) {
|
||||
uint64_t key = ((uint64_t) left << 32) + right;
|
||||
uint64_t hash = hash64(&key, sizeof(key)); // TODO improve number hashing
|
||||
uint64_t kerning = map_get(&font->kerning, hash);
|
||||
|
||||
int* entry = map_get(&font->kerning, key);
|
||||
if (entry) {
|
||||
return *entry;
|
||||
if (kerning == MAP_NIL) {
|
||||
kerning = lovrRasterizerGetKerning(font->rasterizer, left, right);
|
||||
map_set(&font->kerning, hash, kerning);
|
||||
}
|
||||
|
||||
int32_t kerning = lovrRasterizerGetKerning(font->rasterizer, left, right);
|
||||
map_set(&font->kerning, key, kerning);
|
||||
return kerning;
|
||||
}
|
||||
|
||||
|
@ -255,27 +288,24 @@ void lovrFontSetPixelDensity(Font* font, float pixelDensity) {
|
|||
font->pixelDensity = pixelDensity;
|
||||
}
|
||||
|
||||
Glyph* lovrFontGetGlyph(Font* font, uint32_t codepoint) {
|
||||
char key[6];
|
||||
snprintf(key, 6, "%d", codepoint);
|
||||
|
||||
static Glyph* lovrFontGetGlyph(Font* font, uint32_t codepoint) {
|
||||
FontAtlas* atlas = &font->atlas;
|
||||
map_glyph_t* glyphs = &atlas->glyphs;
|
||||
Glyph* glyph = map_get(glyphs, key);
|
||||
uint64_t hash = hash64(&codepoint, sizeof(codepoint));
|
||||
uint64_t index = map_get(&atlas->glyphMap, hash);
|
||||
|
||||
// Add the glyph to the atlas if it isn't there
|
||||
if (!glyph) {
|
||||
Glyph g;
|
||||
lovrRasterizerLoadGlyph(font->rasterizer, codepoint, &g);
|
||||
map_set(glyphs, key, g);
|
||||
glyph = map_get(glyphs, key);
|
||||
lovrFontAddGlyph(font, glyph);
|
||||
if (index == MAP_NIL) {
|
||||
index = atlas->glyphs.length;
|
||||
arr_reserve(&atlas->glyphs, atlas->glyphs.length + 1);
|
||||
lovrRasterizerLoadGlyph(font->rasterizer, codepoint, &atlas->glyphs.data[atlas->glyphs.length++]);
|
||||
map_set(&atlas->glyphMap, hash, index);
|
||||
lovrFontAddGlyph(font, &atlas->glyphs.data[index]);
|
||||
}
|
||||
|
||||
return glyph;
|
||||
return &atlas->glyphs.data[index];
|
||||
}
|
||||
|
||||
void lovrFontAddGlyph(Font* font, Glyph* glyph) {
|
||||
static void lovrFontAddGlyph(Font* font, Glyph* glyph) {
|
||||
FontAtlas* atlas = &font->atlas;
|
||||
|
||||
// Don't waste space on empty glyphs
|
||||
|
@ -308,7 +338,7 @@ void lovrFontAddGlyph(Font* font, Glyph* glyph) {
|
|||
atlas->rowHeight = MAX(atlas->rowHeight, glyph->th);
|
||||
}
|
||||
|
||||
void lovrFontExpandTexture(Font* font) {
|
||||
static void lovrFontExpandTexture(Font* font) {
|
||||
FontAtlas* atlas = &font->atlas;
|
||||
|
||||
if (atlas->width == atlas->height) {
|
||||
|
@ -330,17 +360,14 @@ void lovrFontExpandTexture(Font* font) {
|
|||
atlas->rowHeight = 0;
|
||||
|
||||
// Re-pack all the glyphs
|
||||
const char* key;
|
||||
map_iter_t iter = map_iter(&atlas->glyphs);
|
||||
while ((key = map_next(&atlas->glyphs, &iter)) != NULL) {
|
||||
Glyph* glyph = map_get(&atlas->glyphs, key);
|
||||
lovrFontAddGlyph(font, glyph);
|
||||
for (size_t i = 0; i < atlas->glyphs.length; i++) {
|
||||
lovrFontAddGlyph(font, &atlas->glyphs.data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO we only need the TextureData here to clear the texture, but it's a big waste of memory.
|
||||
// Could look into using glClearTexImage when supported to make this more efficient.
|
||||
void lovrFontCreateTexture(Font* font) {
|
||||
static void lovrFontCreateTexture(Font* font) {
|
||||
lovrRelease(Texture, font->texture);
|
||||
TextureData* textureData = lovrTextureDataCreate(font->atlas.width, font->atlas.height, 0x0, FORMAT_RGB);
|
||||
font->texture = lovrTextureCreate(TEXTURE_2D, &textureData, 1, false, false, 0);
|
||||
|
|
|
@ -1,15 +1,12 @@
|
|||
#include "data/rasterizer.h"
|
||||
#include "lib/map/map.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#pragma once
|
||||
|
||||
struct Rasterizer;
|
||||
struct Texture;
|
||||
|
||||
typedef map_t(Glyph) map_glyph_t;
|
||||
|
||||
typedef enum {
|
||||
ALIGN_LEFT,
|
||||
ALIGN_CENTER,
|
||||
|
@ -22,32 +19,13 @@ typedef enum {
|
|||
ALIGN_BOTTOM
|
||||
} VerticalAlign;
|
||||
|
||||
typedef struct {
|
||||
uint32_t x;
|
||||
uint32_t y;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t rowHeight;
|
||||
uint32_t padding;
|
||||
map_glyph_t glyphs;
|
||||
} FontAtlas;
|
||||
|
||||
typedef struct Font {
|
||||
struct Rasterizer* rasterizer;
|
||||
struct Texture* texture;
|
||||
FontAtlas atlas;
|
||||
map_int_t kerning;
|
||||
float lineHeight;
|
||||
float pixelDensity;
|
||||
bool flip;
|
||||
} Font;
|
||||
|
||||
Font* lovrFontInit(Font* font, struct Rasterizer* rasterizer);
|
||||
#define lovrFontCreate(...) lovrFontInit(lovrAlloc(Font), __VA_ARGS__)
|
||||
typedef struct Font Font;
|
||||
Font* lovrFontCreate(struct Rasterizer* rasterizer);
|
||||
void lovrFontDestroy(void* ref);
|
||||
struct Rasterizer* lovrFontGetRasterizer(Font* font);
|
||||
struct Texture* lovrFontGetTexture(Font* font);
|
||||
void lovrFontRender(Font* font, const char* str, size_t length, float wrap, HorizontalAlign halign, float* vertices, uint16_t* indices, uint16_t baseVertex);
|
||||
void lovrFontMeasure(Font* font, const char* string, size_t length, float wrap, float* width, uint32_t* lineCount, uint32_t* glyphCount);
|
||||
void lovrFontMeasure(Font* font, const char* string, size_t length, float wrap, float* width, float* height, uint32_t* lineCount, uint32_t* glyphCount);
|
||||
float lovrFontGetHeight(Font* font);
|
||||
float lovrFontGetAscent(Font* font);
|
||||
float lovrFontGetDescent(Font* font);
|
||||
|
@ -59,7 +37,3 @@ void lovrFontSetFlipEnabled(Font* font, bool flip);
|
|||
int32_t lovrFontGetKerning(Font* font, unsigned int a, unsigned int b);
|
||||
float lovrFontGetPixelDensity(Font* font);
|
||||
void lovrFontSetPixelDensity(Font* font, float pixelDensity);
|
||||
Glyph* lovrFontGetGlyph(Font* font, uint32_t codepoint);
|
||||
void lovrFontAddGlyph(Font* font, Glyph* glyph);
|
||||
void lovrFontExpandTexture(Font* font);
|
||||
void lovrFontCreateTexture(Font* font);
|
||||
|
|
|
@ -1201,15 +1201,15 @@ void lovrGraphicsSkybox(Texture* texture) {
|
|||
|
||||
void lovrGraphicsPrint(const char* str, size_t length, mat4 transform, float wrap, HorizontalAlign halign, VerticalAlign valign) {
|
||||
float width;
|
||||
float height;
|
||||
uint32_t lineCount;
|
||||
uint32_t glyphCount;
|
||||
Font* font = lovrGraphicsGetFont();
|
||||
lovrFontMeasure(font, str, length, wrap, &width, &lineCount, &glyphCount);
|
||||
lovrFontMeasure(font, str, length, wrap, &width, &height, &lineCount, &glyphCount);
|
||||
|
||||
float scale = 1.f / font->pixelDensity;
|
||||
float offsetY = ((lineCount + 1) * font->rasterizer->height * font->lineHeight) * (valign / 2.f) * (font->flip ? -1 : 1);
|
||||
float scale = 1.f / lovrFontGetPixelDensity(font);
|
||||
mat4_scale(transform, scale, scale, scale);
|
||||
mat4_translate(transform, 0.f, offsetY, 0.f);
|
||||
mat4_translate(transform, 0.f, height * (valign / 2.f), 0.f);
|
||||
|
||||
Pipeline pipeline = state.pipeline;
|
||||
pipeline.blendMode = pipeline.blendMode == BLEND_NONE ? BLEND_ALPHA : pipeline.blendMode;
|
||||
|
@ -1223,7 +1223,7 @@ void lovrGraphicsPrint(const char* str, size_t length, mat4 transform, float wra
|
|||
.shader = SHADER_FONT,
|
||||
.pipeline = &pipeline,
|
||||
.transform = transform,
|
||||
.texture = font->texture,
|
||||
.texture = lovrFontGetTexture(font),
|
||||
.vertexCount = glyphCount * 4,
|
||||
.indexCount = glyphCount * 6,
|
||||
.vertices = &vertices,
|
||||
|
|
|
@ -196,8 +196,8 @@ typedef struct {
|
|||
} GpuLimits;
|
||||
|
||||
typedef struct {
|
||||
int shaderSwitches;
|
||||
int drawCalls;
|
||||
uint32_t shaderSwitches;
|
||||
uint32_t drawCalls;
|
||||
} GpuStats;
|
||||
|
||||
typedef struct {
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "graphics/buffer.h"
|
||||
#include "graphics/graphics.h"
|
||||
#include "graphics/material.h"
|
||||
#include "core/hash.h"
|
||||
#include "core/ref.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
|
@ -26,55 +27,60 @@ size_t lovrMeshGetIndexSize(Mesh* mesh) {
|
|||
}
|
||||
|
||||
void lovrMeshAttachAttribute(Mesh* mesh, const char* name, MeshAttribute* attribute) {
|
||||
lovrAssert(!map_get(&mesh->attributeMap, name), "Mesh already has an attribute named '%s'", name);
|
||||
uint64_t hash = hash64(name, strlen(name));
|
||||
lovrAssert(map_get(&mesh->attributeMap, hash) == MAP_NIL, "Mesh already has an attribute named '%s'", name);
|
||||
lovrAssert(mesh->attributeCount < MAX_ATTRIBUTES, "Mesh already has the max number of attributes (%d)", MAX_ATTRIBUTES);
|
||||
lovrAssert(strlen(name) < MAX_ATTRIBUTE_NAME_LENGTH, "Mesh attribute name '%s' is too long (max is %d)", name, MAX_ATTRIBUTE_NAME_LENGTH);
|
||||
lovrGraphicsFlushMesh(mesh);
|
||||
int index = mesh->attributeCount++;
|
||||
uint64_t index = mesh->attributeCount++;
|
||||
mesh->attributes[index] = *attribute;
|
||||
strcpy(mesh->attributeNames[index], name);
|
||||
map_set(&mesh->attributeMap, name, index);
|
||||
map_set(&mesh->attributeMap, hash, index);
|
||||
lovrRetain(attribute->buffer);
|
||||
}
|
||||
|
||||
void lovrMeshDetachAttribute(Mesh* mesh, const char* name) {
|
||||
int* index = map_get(&mesh->attributeMap, name);
|
||||
lovrAssert(index, "No attached attribute named '%s' was found", name);
|
||||
MeshAttribute* attribute = &mesh->attributes[*index];
|
||||
uint64_t hash = hash64(name, strlen(name));
|
||||
uint64_t index = map_get(&mesh->attributeMap, hash);
|
||||
lovrAssert(index != MAP_NIL, "No attached attribute named '%s' was found", name);
|
||||
MeshAttribute* attribute = &mesh->attributes[index];
|
||||
lovrGraphicsFlushMesh(mesh);
|
||||
lovrRelease(Buffer, attribute->buffer);
|
||||
map_remove(&mesh->attributeMap, name);
|
||||
mesh->attributeNames[*index][0] = '\0';
|
||||
memmove(mesh->attributeNames + *index, mesh->attributeNames + *index + 1, (mesh->attributeCount - *index - 1) * MAX_ATTRIBUTE_NAME_LENGTH * sizeof(char));
|
||||
memmove(mesh->attributes + *index, mesh->attributes + *index + 1, (mesh->attributeCount - *index - 1) * sizeof(MeshAttribute));
|
||||
map_remove(&mesh->attributeMap, hash);
|
||||
mesh->attributeNames[index][0] = '\0';
|
||||
memmove(mesh->attributeNames + index, mesh->attributeNames + index + 1, (mesh->attributeCount - index - 1) * MAX_ATTRIBUTE_NAME_LENGTH * sizeof(char));
|
||||
memmove(mesh->attributes + index, mesh->attributes + index + 1, (mesh->attributeCount - index - 1) * sizeof(MeshAttribute));
|
||||
mesh->attributeCount--;
|
||||
for (uint32_t i = 0; i < MAX_ATTRIBUTES; i++) {
|
||||
if (mesh->locations[i] > *index) {
|
||||
if (mesh->locations[i] > index) {
|
||||
mesh->locations[i]--;
|
||||
} else if (mesh->locations[i] == *index) {
|
||||
} else if (mesh->locations[i] == index) {
|
||||
mesh->locations[i] = 0xff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const MeshAttribute* lovrMeshGetAttribute(Mesh* mesh, const char* name) {
|
||||
int* index = map_get(&mesh->attributeMap, name);
|
||||
return index ? &mesh->attributes[*index] : NULL;
|
||||
uint64_t hash = hash64(name, strlen(name));
|
||||
uint64_t index = map_get(&mesh->attributeMap, hash);
|
||||
return index == MAP_NIL ? NULL : &mesh->attributes[index];
|
||||
}
|
||||
|
||||
bool lovrMeshIsAttributeEnabled(Mesh* mesh, const char* name) {
|
||||
int* index = map_get(&mesh->attributeMap, name);
|
||||
lovrAssert(index, "Mesh does not have an attribute named '%s'", name);
|
||||
return !mesh->attributes[*index].disabled;
|
||||
uint64_t hash = hash64(name, strlen(name));
|
||||
uint64_t index = map_get(&mesh->attributeMap, hash);
|
||||
lovrAssert(index != MAP_NIL, "Mesh does not have an attribute named '%s'", name);
|
||||
return !mesh->attributes[index].disabled;
|
||||
}
|
||||
|
||||
void lovrMeshSetAttributeEnabled(Mesh* mesh, const char* name, bool enable) {
|
||||
bool disable = !enable;
|
||||
int* index = map_get(&mesh->attributeMap, name);
|
||||
lovrAssert(index, "Mesh does not have an attribute named '%s'", name);
|
||||
if (mesh->attributes[*index].disabled != disable) {
|
||||
uint64_t hash = hash64(name, strlen(name));
|
||||
uint64_t index = map_get(&mesh->attributeMap, hash);
|
||||
lovrAssert(index != MAP_NIL, "Mesh does not have an attribute named '%s'", name);
|
||||
if (mesh->attributes[index].disabled != disable) {
|
||||
lovrGraphicsFlushMesh(mesh);
|
||||
mesh->attributes[*index].disabled = disable;
|
||||
mesh->attributes[index].disabled = disable;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "data/modelData.h"
|
||||
#include "graphics/opengl.h"
|
||||
#include "lib/map/map.h"
|
||||
#include "core/map.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
#pragma once
|
||||
|
@ -30,7 +30,7 @@ typedef struct Mesh {
|
|||
uint8_t locations[MAX_ATTRIBUTES];
|
||||
uint16_t enabledLocations;
|
||||
uint16_t divisors[MAX_ATTRIBUTES];
|
||||
map_int_t attributeMap;
|
||||
map_t attributeMap;
|
||||
uint32_t attributeCount;
|
||||
struct Buffer* vertexBuffer;
|
||||
struct Buffer* indexBuffer;
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
#include "graphics/texture.h"
|
||||
#include "resources/shaders.h"
|
||||
#include "data/modelData.h"
|
||||
#include "core/hash.h"
|
||||
#include "core/ref.h"
|
||||
#include "lib/map/map.h"
|
||||
#include <math.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
|
@ -47,11 +47,17 @@ typedef struct {
|
|||
} BlockBuffer;
|
||||
|
||||
typedef struct {
|
||||
size_t next;
|
||||
size_t oldest;
|
||||
GLuint timers[4];
|
||||
uint64_t ns;
|
||||
} TimerList;
|
||||
GLuint* queries;
|
||||
uint32_t* chain;
|
||||
uint32_t next;
|
||||
uint32_t count;
|
||||
} QueryPool;
|
||||
|
||||
typedef struct {
|
||||
uint32_t head;
|
||||
uint32_t tail;
|
||||
uint64_t nanoseconds;
|
||||
} Timer;
|
||||
|
||||
static struct {
|
||||
Texture* defaultTexture;
|
||||
|
@ -83,7 +89,10 @@ static struct {
|
|||
float viewports[2][4];
|
||||
uint32_t viewportCount;
|
||||
arr_t(void*) incoherents[MAX_BARRIERS];
|
||||
map_t(TimerList) timers;
|
||||
QueryPool queryPool;
|
||||
arr_t(Timer) timers;
|
||||
uint32_t activeTimer;
|
||||
map_t timerMap;
|
||||
GpuFeatures features;
|
||||
GpuLimits limits;
|
||||
GpuStats stats;
|
||||
|
@ -1099,6 +1108,10 @@ void lovrGpuInit(getProcAddressProc getProcAddress) {
|
|||
lovrTextureSetFilter(state.defaultTexture, (TextureFilter) { .mode = FILTER_NEAREST });
|
||||
lovrTextureSetWrap(state.defaultTexture, (TextureWrap) { WRAP_CLAMP, WRAP_CLAMP, WRAP_CLAMP });
|
||||
lovrRelease(TextureData, textureData);
|
||||
|
||||
map_init(&state.timerMap, 4);
|
||||
state.queryPool.next = ~0u;
|
||||
state.activeTimer = ~0u;
|
||||
}
|
||||
|
||||
void lovrGpuDestroy() {
|
||||
|
@ -1112,6 +1125,10 @@ void lovrGpuDestroy() {
|
|||
for (int i = 0; i < MAX_BARRIERS; i++) {
|
||||
arr_free(&state.incoherents[i]);
|
||||
}
|
||||
glDeleteQueries(state.queryPool.count, state.queryPool.queries);
|
||||
free(state.queryPool.queries);
|
||||
arr_free(&state.timers);
|
||||
map_free(&state.timerMap);
|
||||
memset(&state, 0, sizeof(state));
|
||||
}
|
||||
|
||||
|
@ -1264,42 +1281,102 @@ void lovrGpuDirtyTexture() {
|
|||
|
||||
void lovrGpuTick(const char* label) {
|
||||
#ifndef LOVR_WEBGL
|
||||
TimerList* timer = map_get(&state.timers, label);
|
||||
lovrAssert(state.activeTimer == ~0u, "Attempt to start a new GPU timer while one is already active!");
|
||||
QueryPool* pool = &state.queryPool;
|
||||
uint64_t hash = hash64(label, strlen(label));
|
||||
uint64_t index = map_get(&state.timerMap, hash);
|
||||
|
||||
if (!timer) {
|
||||
map_set(&state.timers, label, ((TimerList) { .oldest = 0 }));
|
||||
timer = map_get(&state.timers, label);
|
||||
glGenQueries(sizeof(timer->timers) / sizeof(timer->timers[0]), timer->timers);
|
||||
// Create new timer
|
||||
if (index == MAP_NIL) {
|
||||
index = state.timers.length++;
|
||||
map_set(&state.timerMap, hash, index);
|
||||
arr_reserve(&state.timers, state.timers.length);
|
||||
state.timers.data[index].head = ~0u;
|
||||
state.timers.data[index].tail = ~0u;
|
||||
}
|
||||
|
||||
glBeginQuery(GL_TIME_ELAPSED, timer->timers[timer->next]);
|
||||
Timer* timer = &state.timers.data[index];
|
||||
state.activeTimer = index;
|
||||
|
||||
size_t next = (timer->next + 1) % 4;
|
||||
if (next != timer->oldest) {
|
||||
timer->next = next;
|
||||
// Expand pool if no unused timers are available.
|
||||
// The pool manages one memory allocation split into two chunks.
|
||||
// - The first chunk contains OpenGL query objects (GLuint).
|
||||
// - The second chunk is a linked list of query indices (uint32_t), used for two purposes:
|
||||
// - For inactive queries, pool->chain[query] points to the next inactive query (freelist).
|
||||
// - For active queries, pool->chain[query] points to the next active query for that timer.
|
||||
// When resizing the query pool allocation, the second half of the old allocation needs to be
|
||||
// copied to the second half of the new allocation.
|
||||
if (pool->next == ~0u) {
|
||||
uint32_t n = pool->count;
|
||||
pool->count = n == 0 ? 4 : (n << 1);
|
||||
pool->queries = realloc(pool->queries, pool->count * (sizeof(GLuint) + sizeof(uint32_t)));
|
||||
lovrAssert(pool->queries, "Out of memory");
|
||||
pool->chain = pool->queries + pool->count;
|
||||
memcpy(pool->chain, pool->queries + n, n * sizeof(uint32_t));
|
||||
glGenQueries(n ? n : pool->count, pool->queries + n);
|
||||
for (uint32_t i = n; i < pool->count - 1; i++) {
|
||||
pool->chain[i] = i + 1;
|
||||
}
|
||||
pool->chain[pool->count - 1] = ~0u;
|
||||
pool->next = n;
|
||||
}
|
||||
|
||||
// Start query, update linked list pointers
|
||||
uint32_t query = pool->next;
|
||||
glBeginQuery(GL_TIME_ELAPSED, pool->queries[query]);
|
||||
if (timer->tail != ~0u) { pool->chain[timer->tail] = query; }
|
||||
if (timer->head == ~0u) { timer->head = query; }
|
||||
pool->next = pool->chain[query];
|
||||
pool->chain[query] = ~0u;
|
||||
timer->tail = query;
|
||||
#endif
|
||||
}
|
||||
|
||||
double lovrGpuTock(const char* label) {
|
||||
#ifndef LOVR_WEBGL
|
||||
TimerList* timer = map_get(&state.timers, label);
|
||||
if (!timer) return 0.;
|
||||
QueryPool* pool = &state.queryPool;
|
||||
uint64_t hash = hash64(label, strlen(label));
|
||||
uint64_t index = map_get(&state.timerMap, hash);
|
||||
|
||||
if (index == MAP_NIL) {
|
||||
return 0.;
|
||||
}
|
||||
|
||||
Timer* timer = &state.timers.data[index];
|
||||
|
||||
if (state.activeTimer != index) {
|
||||
return timer->nanoseconds / 1e9;
|
||||
}
|
||||
|
||||
glEndQuery(GL_TIME_ELAPSED);
|
||||
state.activeTimer = ~0u;
|
||||
|
||||
// Repeatedly check timer's oldest pending query for completion
|
||||
for (;;) {
|
||||
int query = timer->head;
|
||||
|
||||
while (timer->oldest != timer->next) {
|
||||
GLuint available;
|
||||
glGetQueryObjectuiv(timer->timers[timer->oldest], GL_QUERY_RESULT_AVAILABLE, &available);
|
||||
glGetQueryObjectuiv(pool->queries[query], GL_QUERY_RESULT_AVAILABLE, &available);
|
||||
|
||||
if (!available) {
|
||||
break;
|
||||
}
|
||||
|
||||
glGetQueryObjectui64v(timer->timers[timer->oldest], GL_QUERY_RESULT, &timer->ns);
|
||||
timer->oldest = (timer->oldest + 1) % 4;
|
||||
// Update timer result
|
||||
glGetQueryObjectui64v(pool->queries[query], GL_QUERY_RESULT, &timer->nanoseconds);
|
||||
|
||||
// Update timer's head pointer and return the completed query back to the pool
|
||||
timer->head = pool->chain[query];
|
||||
pool->chain[query] = pool->next;
|
||||
pool->next = query;
|
||||
|
||||
if (timer->head == ~0u) {
|
||||
timer->tail = ~0u;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return timer->ns / 1e9;
|
||||
return timer->nanoseconds / 1e9;
|
||||
#endif
|
||||
return 0.;
|
||||
}
|
||||
|
@ -1848,7 +1925,7 @@ static void lovrShaderSetupUniforms(Shader* shader) {
|
|||
int32_t blockCount;
|
||||
glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCKS, &blockCount);
|
||||
lovrAssert((size_t) blockCount <= MAX_BLOCK_BUFFERS, "Shader has too many uniform blocks (%d) the max is %d", blockCount, MAX_BLOCK_BUFFERS);
|
||||
map_init(&shader->blockMap);
|
||||
map_init(&shader->blockMap, blockCount);
|
||||
arr_block_t* uniformBlocks = &shader->blocks[BLOCK_UNIFORM];
|
||||
arr_init(uniformBlocks);
|
||||
arr_reserve(uniformBlocks, (size_t) blockCount);
|
||||
|
@ -1856,10 +1933,11 @@ static void lovrShaderSetupUniforms(Shader* shader) {
|
|||
UniformBlock block = { .slot = i, .source = NULL };
|
||||
glUniformBlockBinding(program, i, block.slot);
|
||||
|
||||
GLsizei length;
|
||||
char name[LOVR_MAX_UNIFORM_LENGTH];
|
||||
glGetActiveUniformBlockName(program, i, LOVR_MAX_UNIFORM_LENGTH, NULL, name);
|
||||
glGetActiveUniformBlockName(program, i, LOVR_MAX_UNIFORM_LENGTH, &length, name);
|
||||
int blockId = (i << 1) + BLOCK_UNIFORM;
|
||||
map_set(&shader->blockMap, name, blockId);
|
||||
map_set(&shader->blockMap, hash64(name, length), blockId);
|
||||
arr_push(uniformBlocks, block);
|
||||
arr_init(&uniformBlocks->data[uniformBlocks->length - 1].uniforms);
|
||||
}
|
||||
|
@ -1880,10 +1958,11 @@ static void lovrShaderSetupUniforms(Shader* shader) {
|
|||
glShaderStorageBlockBinding(program, i, block.slot);
|
||||
arr_init(&block.uniforms);
|
||||
|
||||
GLsizei length;
|
||||
char name[LOVR_MAX_UNIFORM_LENGTH];
|
||||
glGetProgramResourceName(program, GL_SHADER_STORAGE_BLOCK, i, LOVR_MAX_UNIFORM_LENGTH, NULL, name);
|
||||
glGetProgramResourceName(program, GL_SHADER_STORAGE_BLOCK, i, LOVR_MAX_UNIFORM_LENGTH, &length, name);
|
||||
int blockId = (i << 1) + BLOCK_COMPUTE;
|
||||
map_set(&shader->blockMap, name, blockId);
|
||||
map_set(&shader->blockMap, hash64(name, length), blockId);
|
||||
arr_push(computeBlocks, block);
|
||||
}
|
||||
|
||||
|
@ -1917,14 +1996,14 @@ static void lovrShaderSetupUniforms(Shader* shader) {
|
|||
int32_t uniformCount;
|
||||
int textureSlot = 0;
|
||||
int imageSlot = 0;
|
||||
map_init(&shader->uniformMap);
|
||||
arr_init(&shader->uniforms);
|
||||
arr_reserve(&shader->uniforms, 4);
|
||||
glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &uniformCount);
|
||||
map_init(&shader->uniformMap, 0);
|
||||
arr_init(&shader->uniforms);
|
||||
for (uint32_t i = 0; i < (uint32_t) uniformCount; i++) {
|
||||
Uniform uniform;
|
||||
GLenum glType;
|
||||
glGetActiveUniform(program, i, LOVR_MAX_UNIFORM_LENGTH, NULL, &uniform.count, &glType, uniform.name);
|
||||
GLsizei length;
|
||||
glGetActiveUniform(program, i, LOVR_MAX_UNIFORM_LENGTH, &length, &uniform.count, &glType, uniform.name);
|
||||
|
||||
char* subscript = strchr(uniform.name, '[');
|
||||
if (subscript) {
|
||||
|
@ -2025,7 +2104,7 @@ static void lovrShaderSetupUniforms(Shader* shader) {
|
|||
offset += uniform.components * (uniform.type == UNIFORM_MATRIX ? uniform.components : 1);
|
||||
}
|
||||
|
||||
map_set(&shader->uniformMap, uniform.name, (int) shader->uniforms.length);
|
||||
map_set(&shader->uniformMap, hash64(uniform.name, length), shader->uniforms.length);
|
||||
arr_push(&shader->uniforms, uniform);
|
||||
textureSlot += uniform.type == UNIFORM_SAMPLER ? uniform.count : 0;
|
||||
imageSlot += uniform.type == UNIFORM_IMAGE ? uniform.count : 0;
|
||||
|
@ -2130,13 +2209,14 @@ Shader* lovrShaderInitGraphics(Shader* shader, const char* vertexSource, const c
|
|||
// Attribute cache
|
||||
int32_t attributeCount;
|
||||
glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &attributeCount);
|
||||
map_init(&shader->attributes);
|
||||
map_init(&shader->attributes, 0);
|
||||
for (int i = 0; i < attributeCount; i++) {
|
||||
char name[LOVR_MAX_ATTRIBUTE_LENGTH];
|
||||
GLint size;
|
||||
GLenum type;
|
||||
glGetActiveAttrib(program, i, LOVR_MAX_ATTRIBUTE_LENGTH, NULL, &size, &type, name);
|
||||
map_set(&shader->attributes, name, glGetAttribLocation(program, name));
|
||||
GLsizei length;
|
||||
glGetActiveAttrib(program, i, LOVR_MAX_ATTRIBUTE_LENGTH, &length, &size, &type, name);
|
||||
map_set(&shader->attributes, hash64(name, length), glGetAttribLocation(program, name));
|
||||
}
|
||||
|
||||
shader->multiview = multiview;
|
||||
|
@ -2180,9 +2260,9 @@ void lovrShaderDestroy(void* ref) {
|
|||
arr_free(&shader->uniforms);
|
||||
arr_free(&shader->blocks[BLOCK_UNIFORM]);
|
||||
arr_free(&shader->blocks[BLOCK_COMPUTE]);
|
||||
map_deinit(&shader->attributes);
|
||||
map_deinit(&shader->uniformMap);
|
||||
map_deinit(&shader->blockMap);
|
||||
map_free(&shader->attributes);
|
||||
map_free(&shader->uniformMap);
|
||||
map_free(&shader->blockMap);
|
||||
}
|
||||
|
||||
// Mesh
|
||||
|
@ -2193,7 +2273,7 @@ Mesh* lovrMeshInit(Mesh* mesh, DrawMode mode, Buffer* vertexBuffer, uint32_t ver
|
|||
mesh->vertexCount = vertexCount;
|
||||
lovrRetain(mesh->vertexBuffer);
|
||||
glGenVertexArrays(1, &mesh->vao);
|
||||
map_init(&mesh->attributeMap);
|
||||
map_init(&mesh->attributeMap, MAX_ATTRIBUTES);
|
||||
memset(mesh->locations, 0xff, MAX_ATTRIBUTES * sizeof(uint8_t));
|
||||
return mesh;
|
||||
}
|
||||
|
@ -2205,7 +2285,7 @@ void lovrMeshDestroy(void* ref) {
|
|||
for (uint32_t i = 0; i < mesh->attributeCount; i++) {
|
||||
lovrRelease(Buffer, mesh->attributes[i].buffer);
|
||||
}
|
||||
map_deinit(&mesh->attributeMap);
|
||||
map_free(&mesh->attributeMap);
|
||||
lovrRelease(Buffer, mesh->vertexBuffer);
|
||||
lovrRelease(Buffer, mesh->indexBuffer);
|
||||
lovrRelease(Material, mesh->material);
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "graphics/buffer.h"
|
||||
#include "math/math.h"
|
||||
#include "resources/shaders.h"
|
||||
#include "core/hash.h"
|
||||
#include "core/ref.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
@ -77,30 +78,26 @@ ShaderType lovrShaderGetType(Shader* shader) {
|
|||
}
|
||||
|
||||
int lovrShaderGetAttributeLocation(Shader* shader, const char* name) {
|
||||
int* location = map_get(&shader->attributes, name);
|
||||
return location ? *location : -1;
|
||||
uint64_t location = map_get(&shader->attributes, hash64(name, strlen(name)));
|
||||
return location == MAP_NIL ? -1 : (int) location;
|
||||
}
|
||||
|
||||
bool lovrShaderHasUniform(Shader* shader, const char* name) {
|
||||
return map_get(&shader->uniformMap, name) != NULL;
|
||||
return map_get(&shader->uniformMap, hash64(name, strlen(name))) != MAP_NIL;
|
||||
}
|
||||
|
||||
const Uniform* lovrShaderGetUniform(Shader* shader, const char* name) {
|
||||
size_t* index = map_get(&shader->uniformMap, name);
|
||||
if (!index) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return &shader->uniforms.data[*index];
|
||||
uint64_t index = map_get(&shader->uniformMap, hash64(name, strlen(name)));
|
||||
return index == MAP_NIL ? NULL : &shader->uniforms.data[index];
|
||||
}
|
||||
|
||||
static void lovrShaderSetUniform(Shader* shader, const char* name, UniformType type, void* data, int start, int count, int size, const char* debug) {
|
||||
size_t* index = map_get(&shader->uniformMap, name);
|
||||
if (!index) {
|
||||
uint64_t index = map_get(&shader->uniformMap, hash64(name, strlen(name)));
|
||||
if (index == MAP_NIL) {
|
||||
return;
|
||||
}
|
||||
|
||||
Uniform* uniform = &shader->uniforms.data[*index];
|
||||
Uniform* uniform = &shader->uniforms.data[index];
|
||||
lovrAssert(uniform->type == type, "Unable to send %ss to uniform %s", debug, name);
|
||||
lovrAssert((start + count) * size <= uniform->size, "Too many %ss for uniform %s, maximum is %d", debug, name, uniform->size / size);
|
||||
|
||||
|
@ -140,11 +137,11 @@ void lovrShaderSetColor(Shader* shader, const char* name, Color color) {
|
|||
}
|
||||
|
||||
void lovrShaderSetBlock(Shader* shader, const char* name, Buffer* buffer, size_t offset, size_t size, UniformAccess access) {
|
||||
int* id = map_get(&shader->blockMap, name);
|
||||
if (!id) return;
|
||||
uint64_t id = map_get(&shader->blockMap, hash64(name, strlen(name)));
|
||||
if (id == MAP_NIL) return;
|
||||
|
||||
int type = *id & 1;
|
||||
int index = *id >> 1;
|
||||
int type = id & 1;
|
||||
int index = id >> 1;
|
||||
UniformBlock* block = &shader->blocks[type].data[index];
|
||||
|
||||
if (block->source != buffer || block->offset != offset || block->size != size) {
|
||||
|
@ -181,12 +178,13 @@ size_t lovrShaderComputeUniformLayout(arr_uniform_t* uniforms) {
|
|||
|
||||
ShaderBlock* lovrShaderBlockInit(ShaderBlock* block, BlockType type, Buffer* buffer, arr_uniform_t* uniforms) {
|
||||
arr_init(&block->uniforms);
|
||||
map_init(&block->uniformMap);
|
||||
map_init(&block->uniformMap, uniforms->length);
|
||||
|
||||
arr_append(&block->uniforms, uniforms->data, uniforms->length);
|
||||
|
||||
for (size_t i = 0; i < block->uniforms.length; i++) {
|
||||
map_set(&block->uniformMap, block->uniforms.data[i].name, i);
|
||||
Uniform* uniform = &block->uniforms.data[i];
|
||||
map_set(&block->uniformMap, hash64(uniform->name, strlen(uniform->name)), i);
|
||||
}
|
||||
|
||||
block->type = type;
|
||||
|
@ -199,7 +197,7 @@ void lovrShaderBlockDestroy(void* ref) {
|
|||
ShaderBlock* block = ref;
|
||||
lovrRelease(Buffer, block->buffer);
|
||||
arr_free(&block->uniforms);
|
||||
map_deinit(&block->uniformMap);
|
||||
map_free(&block->uniformMap);
|
||||
}
|
||||
|
||||
BlockType lovrShaderBlockGetType(ShaderBlock* block) {
|
||||
|
@ -248,10 +246,8 @@ char* lovrShaderBlockGetShaderCode(ShaderBlock* block, const char* blockName, si
|
|||
}
|
||||
|
||||
const Uniform* lovrShaderBlockGetUniform(ShaderBlock* block, const char* name) {
|
||||
size_t* index = map_get(&block->uniformMap, name);
|
||||
if (!index) return NULL;
|
||||
|
||||
return &block->uniforms.data[*index];
|
||||
uint64_t index = map_get(&block->uniformMap, hash64(name, strlen(name)));
|
||||
return index == MAP_NIL ? NULL : &block->uniforms.data[index];
|
||||
}
|
||||
|
||||
Buffer* lovrShaderBlockGetBuffer(ShaderBlock* block) {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#include "graphics/texture.h"
|
||||
#include "graphics/opengl.h"
|
||||
#include "core/arr.h"
|
||||
#include "lib/map/map.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
#pragma once
|
||||
|
@ -95,7 +94,7 @@ typedef arr_t(Uniform) arr_uniform_t;
|
|||
typedef struct {
|
||||
BlockType type;
|
||||
arr_uniform_t uniforms;
|
||||
map_t(size_t) uniformMap;
|
||||
map_t uniformMap;
|
||||
struct Buffer* buffer;
|
||||
} ShaderBlock;
|
||||
|
||||
|
@ -114,9 +113,9 @@ typedef struct Shader {
|
|||
ShaderType type;
|
||||
arr_uniform_t uniforms;
|
||||
arr_block_t blocks[2];
|
||||
map_int_t attributes;
|
||||
map_t(size_t) uniformMap;
|
||||
map_int_t blockMap;
|
||||
map_t attributes;
|
||||
map_t uniformMap;
|
||||
map_t blockMap;
|
||||
bool multiview;
|
||||
GPU_SHADER_FIELDS
|
||||
} Shader;
|
||||
|
|
|
@ -3,12 +3,15 @@
|
|||
#include "graphics/graphics.h"
|
||||
#include "graphics/canvas.h"
|
||||
#include "graphics/texture.h"
|
||||
#include "core/arr.h"
|
||||
#include "core/hash.h"
|
||||
#include "core/maf.h"
|
||||
#include "core/map.h"
|
||||
#include "core/ref.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <OVR_CAPI.h>
|
||||
#include <OVR_CAPI_GL.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
static struct {
|
||||
bool needRefreshTracking;
|
||||
|
@ -21,19 +24,21 @@ static struct {
|
|||
Canvas* canvas;
|
||||
ovrTextureSwapChain chain;
|
||||
ovrMirrorTexture mirror;
|
||||
map_t(Texture*) textureLookup;
|
||||
arr_t(Texture*) textures;
|
||||
map_t textureLookup;
|
||||
} state;
|
||||
|
||||
static Texture* lookupTexture(uint32_t handle) {
|
||||
char key[4 + 1] = { 0 };
|
||||
lovrAssert(handle < 9999, "Texture handle overflow");
|
||||
sprintf(key, "%d", handle);
|
||||
Texture** texture = map_get(&state.textureLookup, key);
|
||||
if (!texture) {
|
||||
map_set(&state.textureLookup, key, lovrTextureCreateFromHandle(handle, TEXTURE_2D, 1));
|
||||
texture = map_get(&state.textureLookup, key);
|
||||
uint64_t hash = hash64(&handle, sizeof(handle));
|
||||
uint64_t index = map_get(&state.textureLookup, hash);
|
||||
|
||||
if (index == MAP_NIL) {
|
||||
index = state.textures.length;
|
||||
map_set(&state.textureLookup, hash, index);
|
||||
arr_push(&state.textures, lovrTextureCreateFromHandle(handle, TEXTURE_2D, 1));
|
||||
}
|
||||
return *texture;
|
||||
|
||||
return state.textures.data[index];
|
||||
}
|
||||
|
||||
static ovrTrackingState *refreshTracking(void) {
|
||||
|
@ -85,20 +90,17 @@ static bool oculus_init(float offset, uint32_t msaa) {
|
|||
state.clipNear = 0.1f;
|
||||
state.clipFar = 30.f;
|
||||
|
||||
map_init(&state.textureLookup);
|
||||
map_init(&state.textureLookup, 4);
|
||||
|
||||
ovr_SetTrackingOriginType(state.session, ovrTrackingOrigin_FloorLevel);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void oculus_destroy(void) {
|
||||
const char* key;
|
||||
map_iter_t iter = map_iter(&state.textureLookup);
|
||||
while ((key = map_next(&state.textureLookup, &iter)) != NULL) {
|
||||
Texture* texture = *map_get(&state.textureLookup, key);
|
||||
lovrRelease(Texture, texture);
|
||||
for (size_t i = 0; i < state.textures.length; i++) {
|
||||
lovrRelease(Texture, state.textures.data[i]);
|
||||
}
|
||||
map_deinit(&state.textureLookup);
|
||||
map_free(&state.textureLookup);
|
||||
|
||||
if (state.mirror) {
|
||||
ovr_DestroyMirrorTexture(state.session, state.mirror);
|
||||
|
|
|
@ -31,6 +31,16 @@ static void raycastCallback(void* data, dGeomID a, dGeomID b) {
|
|||
}
|
||||
}
|
||||
|
||||
// XXX slow, but probably fine (tag names are not on any critical path), could switch to hashing if needed
|
||||
static uint32_t findTag(World* world, const char* name) {
|
||||
for (uint32_t i = 0; i < MAX_TAGS && world->tags[i]; i++) {
|
||||
if (!strcmp(world->tags[i], name)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return NO_TAG;
|
||||
}
|
||||
|
||||
static bool initialized = false;
|
||||
|
||||
bool lovrPhysicsInit() {
|
||||
|
@ -45,7 +55,7 @@ void lovrPhysicsDestroy() {
|
|||
initialized = false;
|
||||
}
|
||||
|
||||
World* lovrWorldInit(World* world, float xg, float yg, float zg, bool allowSleep, const char** tags, int tagCount) {
|
||||
World* lovrWorldInit(World* world, float xg, float yg, float zg, bool allowSleep, const char** tags, uint32_t tagCount) {
|
||||
world->id = dWorldCreate();
|
||||
world->space = dHashSpaceCreate(0);
|
||||
dHashSpaceSetLevels(world->space, -4, 8);
|
||||
|
@ -53,15 +63,12 @@ World* lovrWorldInit(World* world, float xg, float yg, float zg, bool allowSleep
|
|||
arr_init(&world->overlaps);
|
||||
lovrWorldSetGravity(world, xg, yg, zg);
|
||||
lovrWorldSetSleepingAllowed(world, allowSleep);
|
||||
map_init(&world->tags);
|
||||
for (int i = 0; i < tagCount; i++) {
|
||||
map_set(&world->tags, tags[i], i);
|
||||
for (uint32_t i = 0; i < tagCount; i++) {
|
||||
size_t size = strlen(tags[i]) + 1;
|
||||
world->tags[i] = malloc(size);
|
||||
memcpy(world->tags[i], tags[i], size);
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAX_TAGS; i++) {
|
||||
world->masks[i] = ~0;
|
||||
}
|
||||
|
||||
memset(world->masks, 0xff, sizeof(world->masks));
|
||||
return world;
|
||||
}
|
||||
|
||||
|
@ -69,7 +76,9 @@ void lovrWorldDestroy(void* ref) {
|
|||
World* world = ref;
|
||||
lovrWorldDestroyData(world);
|
||||
arr_free(&world->overlaps);
|
||||
map_deinit(&world->tags);
|
||||
for (uint32_t i = 0; i < MAX_TAGS && world->tags[i]; i++) {
|
||||
free(world->tags[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void lovrWorldDestroyData(World* world) {
|
||||
|
@ -132,10 +141,10 @@ int lovrWorldCollide(World* world, Shape* a, Shape* b, float friction, float res
|
|||
|
||||
Collider* colliderA = a->collider;
|
||||
Collider* colliderB = b->collider;
|
||||
int tag1 = colliderA->tag;
|
||||
int tag2 = colliderB->tag;
|
||||
uint32_t i = colliderA->tag;
|
||||
uint32_t j = colliderB->tag;
|
||||
|
||||
if (tag1 != NO_TAG && tag2 != NO_TAG && !((world->masks[tag1] & (1 << tag2)) && (world->masks[tag2] & (1 << tag1)))) {
|
||||
if (i != NO_TAG && j != NO_TAG && !((world->masks[i] & (1 << j)) && (world->masks[j] & (1 << i)))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -221,54 +230,42 @@ void lovrWorldRaycast(World* world, float x1, float y1, float z1, float x2, floa
|
|||
dGeomDestroy(ray);
|
||||
}
|
||||
|
||||
const char* lovrWorldGetTagName(World* world, int tag) {
|
||||
if (tag == NO_TAG) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* key;
|
||||
map_iter_t iter = map_iter(&world->tags);
|
||||
while ((key = map_next(&world->tags, &iter))) {
|
||||
if (*map_get(&world->tags, key) == tag) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
const char* lovrWorldGetTagName(World* world, uint32_t tag) {
|
||||
return (tag == NO_TAG) ? NULL : world->tags[tag];
|
||||
}
|
||||
|
||||
int lovrWorldDisableCollisionBetween(World* world, const char* tag1, const char* tag2) {
|
||||
int* index1 = map_get(&world->tags, tag1);
|
||||
int* index2 = map_get(&world->tags, tag2);
|
||||
if (!index1 || !index2) {
|
||||
uint32_t i = findTag(world, tag1);
|
||||
uint32_t j = findTag(world, tag2);
|
||||
if (i == NO_TAG || j == NO_TAG) {
|
||||
return NO_TAG;
|
||||
}
|
||||
|
||||
world->masks[*index1] &= ~(1 << *index2);
|
||||
world->masks[*index2] &= ~(1 << *index1);
|
||||
world->masks[i] &= ~(1 << j);
|
||||
world->masks[j] &= ~(1 << i);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lovrWorldEnableCollisionBetween(World* world, const char* tag1, const char* tag2) {
|
||||
int* index1 = map_get(&world->tags, tag1);
|
||||
int* index2 = map_get(&world->tags, tag2);
|
||||
if (!index1 || !index2) {
|
||||
uint32_t i = findTag(world, tag1);
|
||||
uint32_t j = findTag(world, tag2);
|
||||
if (i == NO_TAG || j == NO_TAG) {
|
||||
return NO_TAG;
|
||||
}
|
||||
|
||||
world->masks[*index1] |= (1 << *index2);
|
||||
world->masks[*index2] |= (1 << *index1);
|
||||
world->masks[i] |= (1 << j);
|
||||
world->masks[j] |= (1 << i);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lovrWorldIsCollisionEnabledBetween(World* world, const char* tag1, const char* tag2) {
|
||||
int* index1 = map_get(&world->tags, tag1);
|
||||
int* index2 = map_get(&world->tags, tag2);
|
||||
if (!index1 || !index2) {
|
||||
uint32_t i = findTag(world, tag1);
|
||||
uint32_t j = findTag(world, tag2);
|
||||
if (i == NO_TAG || j == NO_TAG) {
|
||||
return NO_TAG;
|
||||
}
|
||||
|
||||
return (world->masks[*index1] & (1 << *index2)) && (world->masks[*index2] & (1 << *index1));
|
||||
return (world->masks[i] & (1 << j)) && (world->masks[j] & (1 << i));
|
||||
}
|
||||
|
||||
Collider* lovrColliderInit(Collider* collider, World* world, float x, float y, float z) {
|
||||
|
@ -396,20 +393,14 @@ const char* lovrColliderGetTag(Collider* collider) {
|
|||
return lovrWorldGetTagName(collider->world, collider->tag);
|
||||
}
|
||||
|
||||
int lovrColliderSetTag(Collider* collider, const char* tag) {
|
||||
if (tag == NULL) {
|
||||
bool lovrColliderSetTag(Collider* collider, const char* tag) {
|
||||
if (!tag) {
|
||||
collider->tag = NO_TAG;
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
int* index = map_get(&collider->world->tags, tag);
|
||||
|
||||
if (!index) {
|
||||
return NO_TAG;
|
||||
}
|
||||
|
||||
collider->tag = *index;
|
||||
return 0;
|
||||
collider->tag = findTag(collider->world, tag);
|
||||
return collider->tag != NO_TAG;
|
||||
}
|
||||
|
||||
float lovrColliderGetFriction(Collider* collider) {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#include "core/arr.h"
|
||||
#include "lib/map/map.h"
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <ode/ode.h>
|
||||
|
@ -8,7 +7,7 @@
|
|||
|
||||
#define MAX_CONTACTS 4
|
||||
#define MAX_TAGS 16
|
||||
#define NO_TAG ~0
|
||||
#define NO_TAG ~0u
|
||||
|
||||
typedef enum {
|
||||
SHAPE_SPHERE,
|
||||
|
@ -33,7 +32,7 @@ typedef struct {
|
|||
dSpaceID space;
|
||||
dJointGroupID contactGroup;
|
||||
arr_t(Shape*) overlaps;
|
||||
map_int_t tags;
|
||||
char* tags[MAX_TAGS];
|
||||
uint16_t masks[MAX_TAGS];
|
||||
Collider* head;
|
||||
} World;
|
||||
|
@ -44,7 +43,7 @@ struct Collider {
|
|||
Collider* prev;
|
||||
Collider* next;
|
||||
void* userdata;
|
||||
int tag;
|
||||
uint32_t tag;
|
||||
arr_t(Shape*) shapes;
|
||||
arr_t(Joint*) joints;
|
||||
float friction;
|
||||
|
@ -85,7 +84,7 @@ typedef struct {
|
|||
bool lovrPhysicsInit(void);
|
||||
void lovrPhysicsDestroy(void);
|
||||
|
||||
World* lovrWorldInit(World* world, float xg, float yg, float zg, bool allowSleep, const char** tags, int tagCount);
|
||||
World* lovrWorldInit(World* world, float xg, float yg, float zg, bool allowSleep, const char** tags, uint32_t tagCount);
|
||||
#define lovrWorldCreate(...) lovrWorldInit(lovrAlloc(World), __VA_ARGS__)
|
||||
void lovrWorldDestroy(void* ref);
|
||||
void lovrWorldDestroyData(World* world);
|
||||
|
@ -102,7 +101,7 @@ void lovrWorldSetAngularDamping(World* world, float damping, float threshold);
|
|||
bool lovrWorldIsSleepingAllowed(World* world);
|
||||
void lovrWorldSetSleepingAllowed(World* world, bool allowed);
|
||||
void lovrWorldRaycast(World* world, float x1, float y1, float z1, float x2, float y2, float z2, RaycastCallback callback, void* userdata);
|
||||
const char* lovrWorldGetTagName(World* world, int tag);
|
||||
const char* lovrWorldGetTagName(World* world, uint32_t tag);
|
||||
int lovrWorldDisableCollisionBetween(World* world, const char* tag1, const char* tag2);
|
||||
int lovrWorldEnableCollisionBetween(World* world, const char* tag1, const char* tag2);
|
||||
int lovrWorldIsCollisionEnabledBetween(World* world, const char* tag1, const char* tag);
|
||||
|
@ -119,7 +118,7 @@ Joint** lovrColliderGetJoints(Collider* collider, size_t* count);
|
|||
void* lovrColliderGetUserData(Collider* collider);
|
||||
void lovrColliderSetUserData(Collider* collider, void* data);
|
||||
const char* lovrColliderGetTag(Collider* collider);
|
||||
int lovrColliderSetTag(Collider* collider, const char* tag);
|
||||
bool lovrColliderSetTag(Collider* collider, const char* tag);
|
||||
float lovrColliderGetFriction(Collider* collider);
|
||||
void lovrColliderSetFriction(Collider* collider, float friction);
|
||||
float lovrColliderGetRestitution(Collider* collider);
|
||||
|
|
|
@ -1,43 +1,47 @@
|
|||
#include "thread/thread.h"
|
||||
#include "thread/channel.h"
|
||||
#include "core/arr.h"
|
||||
#include "core/hash.h"
|
||||
#include "core/map.h"
|
||||
#include "core/ref.h"
|
||||
#include "util.h"
|
||||
#include "lib/map/map.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static struct {
|
||||
bool initialized;
|
||||
map_void_t channels;
|
||||
arr_t(Channel*) channels;
|
||||
map_t channelMap;
|
||||
} state;
|
||||
|
||||
bool lovrThreadModuleInit() {
|
||||
if (state.initialized) return false;
|
||||
map_init(&state.channels);
|
||||
arr_init(&state.channels);
|
||||
map_init(&state.channelMap, 0);
|
||||
return state.initialized = true;
|
||||
}
|
||||
|
||||
void lovrThreadModuleDestroy() {
|
||||
if (!state.initialized) return;
|
||||
const char* key;
|
||||
map_iter_t iter = map_iter(&state.channels);
|
||||
while ((key = map_next(&state.channels, &iter)) != NULL) {
|
||||
Channel* channel = *(Channel**) map_get(&state.channels, key);
|
||||
lovrRelease(Channel, channel);
|
||||
for (size_t i = 0; i < state.channels.length; i++) {
|
||||
lovrRelease(Channel, state.channels.data[i]);
|
||||
}
|
||||
map_deinit(&state.channels);
|
||||
arr_free(&state.channels);
|
||||
map_free(&state.channelMap);
|
||||
state.initialized = false;
|
||||
}
|
||||
|
||||
Channel* lovrThreadGetChannel(const char* name) {
|
||||
Channel** channel = (Channel**) map_get(&state.channels, name);
|
||||
uint64_t hash = hash64(name, strlen(name));
|
||||
uint64_t index = map_get(&state.channelMap, hash);
|
||||
|
||||
if (channel) {
|
||||
return *channel;
|
||||
} else {
|
||||
Channel* channel = lovrChannelCreate();
|
||||
map_set(&state.channels, name, channel);
|
||||
return channel;
|
||||
if (index == MAP_NIL) {
|
||||
index = state.channels.length;
|
||||
map_set(&state.channelMap, hash, index);
|
||||
arr_push(&state.channels, lovrChannelCreate());
|
||||
}
|
||||
|
||||
return state.channels.data[index];
|
||||
}
|
||||
|
||||
Thread* lovrThreadInit(Thread* thread, int (*runner)(void*), Blob* body) {
|
||||
|
|
Loading…
Reference in New Issue