Entity system, generate some trees around player, etc
This commit is contained in:
+4
-4
@@ -1,11 +1,11 @@
|
||||
SRC=src/main.c src/program.c src/resources.c src/camera.c src/chunk/chunk.c \
|
||||
src/chunk/mesh.c src/flags.c src/texture.c src/chunk/manager.c \
|
||||
src/event/manager.c src/event/handlers.c src/state.c src/graphics.c \
|
||||
src/entity/meshes.c src/entity/manager.c
|
||||
src/entity/meshes.c src/entity/manager.c src/entity/templates.c
|
||||
|
||||
OBJ=$(SRC:.c=.o)
|
||||
CFLAGS=-pg -Werror -Wextra -Wall `pkg-config --cflags sdl3 sdl3-image cglm gl glib-2.0`
|
||||
LDFLAGS=`pkg-config --libs sdl3 sdl3-image cglm gl glib-2.0` -lm
|
||||
CFLAGS=-g -pg -Werror -Wextra -Wall `pkg-config --cflags sdl3 sdl3-image cglm gl glib-2.0`
|
||||
LDFLAGS=-g -pg -lm `pkg-config --libs sdl3 sdl3-image cglm gl glib-2.0`
|
||||
TARGET=wschod
|
||||
CC=gcc
|
||||
LD=gcc
|
||||
@@ -21,4 +21,4 @@ $(TARGET): $(OBJ)
|
||||
$(CC) -c $^ -o $@ $(CFLAGS)
|
||||
|
||||
clean:
|
||||
$(RM) $(TARGET) $(OBJ)
|
||||
$(RM) gmon.out $(TARGET) $(OBJ)
|
||||
|
||||
@@ -4,8 +4,9 @@ attribute vec4 position;
|
||||
|
||||
uniform mat4 view;
|
||||
uniform mat4 projection;
|
||||
uniform mat4 model;
|
||||
|
||||
void main ()
|
||||
{
|
||||
gl_Position = projection * view * position;
|
||||
gl_Position = projection * view * model * position;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,5 @@ void main ()
|
||||
result = mix (result, texture2D (materials[3], f_uv), f_material[2]);
|
||||
result = mix (result, texture2D (materials[4], f_uv), f_material[3]);
|
||||
|
||||
result = result * (f_height / 5.0);
|
||||
gl_FragColor = result;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ varying vec2 f_uv;
|
||||
|
||||
void main ()
|
||||
{
|
||||
if(texture2D (texture, f_uv).a < 0.1)
|
||||
if(texture2D (texture, f_uv).a < 0.2)
|
||||
{
|
||||
discard;
|
||||
}
|
||||
|
||||
@@ -5,11 +5,12 @@ attribute vec2 uv;
|
||||
|
||||
uniform mat4 view;
|
||||
uniform mat4 projection;
|
||||
uniform mat4 model;
|
||||
|
||||
varying vec2 f_uv;
|
||||
|
||||
void main ()
|
||||
{
|
||||
f_uv = uv;
|
||||
gl_Position = projection * view * position;
|
||||
gl_Position = projection * view * model * position;
|
||||
}
|
||||
|
||||
+29
-1
@@ -2,6 +2,9 @@
|
||||
|
||||
#include <math.h>
|
||||
|
||||
extern GHashTable *entity_templates;
|
||||
extern GSequence *entity_manager;
|
||||
|
||||
Chunk *
|
||||
Chunk_new ()
|
||||
{
|
||||
@@ -11,6 +14,13 @@ Chunk_new ()
|
||||
return chunk;
|
||||
}
|
||||
|
||||
/* Example function for a tick for an entity. */
|
||||
void
|
||||
tree_grow (Entity *entity)
|
||||
{
|
||||
entity->scale[1] += 0.01;
|
||||
}
|
||||
|
||||
void
|
||||
Chunk_random (Chunk *chunk, int xoffset, int zoffset)
|
||||
{
|
||||
@@ -20,10 +30,28 @@ Chunk_random (Chunk *chunk, int xoffset, int zoffset)
|
||||
{
|
||||
chunk->heightmap[INDEX (x, z)] =
|
||||
(glm_perlin_vec2
|
||||
((vec2) { (x + xoffset + 0.5) / 10.0, (z + zoffset + 0.5) / 10.0 }) + 0.6) * 5;
|
||||
((vec2) { (x + xoffset + 0.5) / 10.0, (z + zoffset + 0.5) / 10.0 }) + 0.6) * 10;
|
||||
|
||||
chunk->materialmap[INDEX (x, z)] = (int)
|
||||
(glm_perlin_vec2 ((vec2) { (x + xoffset + 0.5) / 20.0, (z + zoffset + 0.5) / 20.0 }) + 0.6 * 5) % 5;
|
||||
|
||||
if (rand() % 100 < 2)
|
||||
{
|
||||
Entity *tree = malloc (sizeof (Entity));
|
||||
|
||||
*tree = (Entity) {
|
||||
.type = ENTITY_SOLID,
|
||||
.mesh_type = ENTITY_MESH_X,
|
||||
.origin = ENTITY_BOTTOM,
|
||||
.position = { (float) x + xoffset, chunk->heightmap[INDEX (x, z)], (float) z + zoffset },
|
||||
.scale = { 3.0, 10.0, 3.0 },
|
||||
.rotation = (rand () % 180) * (M_PI / 180.0),
|
||||
.texture = "tree0",
|
||||
.on_tick = tree_grow
|
||||
};
|
||||
|
||||
EM_ENTADD (tree);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,13 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <glib-2.0/glib.h>
|
||||
#include <cglm/cglm.h>
|
||||
#include <cglm/noise.h>
|
||||
|
||||
#include "../util.h"
|
||||
#include "../entity/templates.h"
|
||||
#include "../entity/manager.h"
|
||||
|
||||
#define CHUNK_DIM 32
|
||||
#define CHUNK_SIZE (CHUNK_DIM*CHUNK_DIM)
|
||||
|
||||
@@ -43,6 +43,9 @@ ChunkManager_init ()
|
||||
chunk_manager.chunk_meshes = g_hash_table_new_full (cm_ivec2_hash, cm_ivec2_equal, free, (GDestroyNotify) Chunk_free);
|
||||
}
|
||||
|
||||
/* TODO: Update the chunks only if the player has moved into another
|
||||
chunk, to save frame time. */
|
||||
|
||||
void
|
||||
ChunkManager_update (vec3 camera_position)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
#include "templates.h"
|
||||
|
||||
GHashTable *entity_templates;
|
||||
|
||||
void
|
||||
EntityTemplates_init ()
|
||||
{
|
||||
entity_templates = g_hash_table_new (g_str_hash, g_str_equal);
|
||||
|
||||
Entity *tree = malloc (sizeof (Entity));
|
||||
*tree = (Entity) {
|
||||
.type = ENTITY_SOLID,
|
||||
.mesh_type = ENTITY_MESH_X,
|
||||
.origin = ENTITY_BOTTOM,
|
||||
.position = { 0.0, 0.0, 0.0 },
|
||||
.scale = { 1.0, 3.0, 1.0 },
|
||||
.rotation = 0.0,
|
||||
.texture = "tree0"
|
||||
};
|
||||
|
||||
ET_ENTADD ("tree", tree);
|
||||
}
|
||||
|
||||
/* To avoid confusion with EM_ENTADD. */
|
||||
#undef ET_ENTADD
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
emssej@Komputer-Ze-Smieci.2233:1748755544
|
||||
@@ -1,18 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
typedef enum EntityMeshType
|
||||
{
|
||||
ENTITY_MESH_BILLBOARD,
|
||||
ENTITY_MESH_X
|
||||
} EntityMeshType;
|
||||
|
||||
typedef struct Entity
|
||||
{
|
||||
enum { ENTITY_DEFAULT, ENTITY_SOLID } type;
|
||||
enum { ENTITY_MESH_BILLBOARD, ENTITY_MESH_X } mesh_type;
|
||||
enum { ENTITY_CENTER, ENTITY_BOTTOM } origin;
|
||||
void *userdata;
|
||||
|
||||
void (*on_tick) (struct Entity *self);
|
||||
|
||||
vec3 position;
|
||||
vec3 rotation; /* Unused for now. */
|
||||
float rotation;
|
||||
vec3 scale; /* Unused for now. */
|
||||
int texture;
|
||||
EntityMeshType type;
|
||||
char *texture;
|
||||
} Entity;
|
||||
|
||||
Entity *Entity_new (vec3 position, int texture, EntityMeshType type);
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
#include "manager.h"
|
||||
|
||||
GSequence *entity_manager;
|
||||
/* TODO: Sort entities by mesh type AS WELL AS distance. Binding
|
||||
buffers is very performance-sensitive, thus to conserve speed one
|
||||
type should be rendered in one go, then the other type, etc. */
|
||||
|
||||
GSequence *entity_manager;
|
||||
EntityMesh mesh_billboard, mesh_x;
|
||||
|
||||
extern Resources resources;
|
||||
|
||||
void
|
||||
EntityManager_init ()
|
||||
{
|
||||
@@ -12,14 +17,65 @@ EntityManager_init ()
|
||||
mesh_x = EntityMesh_make_x ();
|
||||
}
|
||||
|
||||
void
|
||||
em_tick_internal (gpointer data, gpointer userdata)
|
||||
{
|
||||
UNUSED (userdata);
|
||||
Entity *entity = data;
|
||||
|
||||
if (entity->on_tick)
|
||||
{
|
||||
entity->on_tick (entity);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
EntityManager_tick ()
|
||||
{
|
||||
g_sequence_foreach (entity_manager, em_tick_internal, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
em_render_internal (gpointer data, gpointer userdata)
|
||||
{
|
||||
UNUSED (userdata);
|
||||
Entity *entity = data;
|
||||
EntityMesh to_render;
|
||||
unsigned int program;
|
||||
|
||||
switch (entity->mesh_type)
|
||||
{
|
||||
case ENTITY_MESH_X:
|
||||
program = R_PRGGET ("default");
|
||||
to_render = mesh_x;
|
||||
break;
|
||||
default:
|
||||
program = R_PRGGET ("billboard");
|
||||
to_render = mesh_billboard;
|
||||
break;
|
||||
}
|
||||
|
||||
mat4 model = GLM_MAT4_IDENTITY_INIT;
|
||||
glm_translate (model, entity->position);
|
||||
|
||||
if (entity->origin == ENTITY_BOTTOM)
|
||||
{
|
||||
glm_translate (model, (vec3) { 0.0, entity->scale[1], 0.0 });
|
||||
}
|
||||
|
||||
glm_scale (model, entity->scale);
|
||||
|
||||
program_use (program);
|
||||
program_set_mat4 (program, "model", model);
|
||||
|
||||
glActiveTexture (GL_TEXTURE0);
|
||||
glBindTexture (GL_TEXTURE_2D, R_TEXGET (entity->texture));
|
||||
|
||||
EntityMesh_render (&to_render);
|
||||
}
|
||||
|
||||
void
|
||||
EntityManager_render ()
|
||||
{
|
||||
|
||||
g_sequence_foreach (entity_manager, em_render_internal, NULL);
|
||||
}
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
|
||||
#include "entity.h"
|
||||
#include "meshes.h"
|
||||
#include "../util.h"
|
||||
#include "../resources.h"
|
||||
|
||||
/* TODO: Sort entities by mesh type AS WELL AS distance. Binding
|
||||
buffers is very performance-sensitive, thus to conserve speed one
|
||||
type should be rendered in one go, then the other type, etc. */
|
||||
#define EM_ENTADD(entity) (g_sequence_append (entity_manager, entity));
|
||||
|
||||
void EntityManager_init ();
|
||||
void EntityManager_tick ();
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#include "meshes.h"
|
||||
|
||||
/* TODO: add back faces. */
|
||||
/* TODO: Split binding/rendering */
|
||||
|
||||
extern GameFlags game_flags;
|
||||
|
||||
void
|
||||
EntityMesh_bind (EntityMesh *mesh)
|
||||
EntityMesh_render (EntityMesh *mesh)
|
||||
{
|
||||
glBindBuffer (GL_ARRAY_BUFFER, mesh->pbo);
|
||||
glEnableVertexAttribArray (0);
|
||||
@@ -16,11 +16,7 @@ EntityMesh_bind (EntityMesh *mesh)
|
||||
glVertexAttribPointer (1, 2, GL_FLOAT, GL_FALSE, sizeof (float) * 2, 0);
|
||||
|
||||
glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, mesh->ebo);
|
||||
}
|
||||
|
||||
void
|
||||
EntityMesh_render (EntityMesh *mesh)
|
||||
{
|
||||
if (game_flags.wireframe_enabled)
|
||||
{
|
||||
glDrawElements (GL_LINES, mesh->indices_len, GL_UNSIGNED_INT, 0);
|
||||
|
||||
@@ -13,7 +13,6 @@ typedef struct EntityMesh
|
||||
unsigned int ebo;
|
||||
} EntityMesh;
|
||||
|
||||
void EntityMesh_bind (EntityMesh *mesh);
|
||||
void EntityMesh_render (EntityMesh *mesh);
|
||||
EntityMesh EntityMesh_make_billboard ();
|
||||
EntityMesh EntityMesh_make_x ();
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
#include "templates.h"
|
||||
|
||||
GHashTable *entity_templates;
|
||||
|
||||
void
|
||||
EntityTemplates_init ()
|
||||
{
|
||||
entity_templates = g_hash_table_new (g_str_hash, g_str_equal);
|
||||
|
||||
Entity *tree = malloc (sizeof (Entity));
|
||||
*tree = (Entity) {
|
||||
.type = ENTITY_SOLID,
|
||||
.mesh_type = ENTITY_MESH_X,
|
||||
.origin = ENTITY_BOTTOM,
|
||||
.position = { 0.0, 0.0, 0.0 },
|
||||
.scale = { 1.0, 3.0, 1.0 },
|
||||
.rotation = 0.0,
|
||||
.texture = "tree0"
|
||||
};
|
||||
|
||||
ET_ENTADD ("tree", tree);
|
||||
}
|
||||
|
||||
/* Example function for a tick for an entity. */
|
||||
void
|
||||
example_entity_tick (Entity *entity)
|
||||
{
|
||||
entity->scale[1] += 0.0000001;
|
||||
}
|
||||
|
||||
/* To avoid confusion with EM_ENTADD. */
|
||||
#undef ET_ENTADD
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <glib-2.0/glib.h>
|
||||
#include <cglm/cglm.h>
|
||||
|
||||
#include "entity.h"
|
||||
|
||||
#define ET_ENTGET(key) ((Entity *) g_hash_table_lookup (entity_templates, key))
|
||||
#define ET_ENTADD(key, entity) (g_hash_table_insert (entity_templates, key, entity))
|
||||
|
||||
void EntityTemplates_init ();
|
||||
+20
-22
@@ -6,9 +6,6 @@ extern ChunkManager chunk_manager;
|
||||
extern GameState game_state;
|
||||
extern Resources resources;
|
||||
|
||||
extern EntityMesh mesh_billboard;
|
||||
extern EntityMesh mesh_x;
|
||||
|
||||
void
|
||||
GraphicsContext_init ()
|
||||
{
|
||||
@@ -60,32 +57,33 @@ GraphicsContext_render ()
|
||||
mat4 projection, view;
|
||||
Camera_get_projection (&game_state.camera, 640.0 / 480.0, projection);
|
||||
Camera_get_view (&game_state.camera, view);
|
||||
|
||||
program_use (_RGP ("chunk"));
|
||||
program_set_mat4 (_RGP ("chunk"), "projection", projection);
|
||||
program_set_mat4 (_RGP ("chunk"), "view", view);
|
||||
|
||||
/* Set views and projections in all programs. TODO: make this dynamic*/
|
||||
program_use (R_PRGGET ("billboard"));
|
||||
program_set_mat4 (R_PRGGET ("billboard"), "projection", projection);
|
||||
program_set_mat4 (R_PRGGET ("billboard"), "view", view);
|
||||
|
||||
program_use (R_PRGGET ("default"));
|
||||
program_set_mat4 (R_PRGGET ("default"), "projection", projection);
|
||||
program_set_mat4 (R_PRGGET ("default"), "view", view);
|
||||
|
||||
program_use (R_PRGGET ("chunk"));
|
||||
program_set_mat4 (R_PRGGET ("chunk"), "projection", projection);
|
||||
program_set_mat4 (R_PRGGET ("chunk"), "view", view);
|
||||
|
||||
glActiveTexture (GL_TEXTURE0);
|
||||
glBindTexture (GL_TEXTURE_2D, _RGT ("terrain0"));
|
||||
glBindTexture (GL_TEXTURE_2D, R_TEXGET ("terrain0"));
|
||||
glActiveTexture (GL_TEXTURE1);
|
||||
glBindTexture (GL_TEXTURE_2D, _RGT ("terrain1"));
|
||||
glBindTexture (GL_TEXTURE_2D, R_TEXGET ("terrain1"));
|
||||
glActiveTexture (GL_TEXTURE2);
|
||||
glBindTexture (GL_TEXTURE_2D, _RGT ("terrain2"));
|
||||
glBindTexture (GL_TEXTURE_2D, R_TEXGET ("terrain2"));
|
||||
glActiveTexture (GL_TEXTURE3);
|
||||
glBindTexture (GL_TEXTURE_2D, _RGT ("terrain3"));
|
||||
glBindTexture (GL_TEXTURE_2D, R_TEXGET ("terrain3"));
|
||||
glActiveTexture (GL_TEXTURE4);
|
||||
glBindTexture (GL_TEXTURE_2D, _RGT ("terrain4"));
|
||||
glBindTexture (GL_TEXTURE_2D, R_TEXGET ("terrain4"));
|
||||
|
||||
ChunkManager_render (game_state.camera.position, _RGP ("chunk"));
|
||||
|
||||
program_use (_RGP ("default"));
|
||||
program_set_mat4 (_RGP ("default"), "projection", projection);
|
||||
program_set_mat4 (_RGP ("default"), "view", view);
|
||||
glActiveTexture (GL_TEXTURE0);
|
||||
glBindTexture (GL_TEXTURE_2D, _RGT ("tree0"));
|
||||
|
||||
EntityMesh_bind (&mesh_x);
|
||||
EntityMesh_render (&mesh_x);
|
||||
ChunkManager_render (game_state.camera.position, R_PRGGET ("chunk"));
|
||||
EntityManager_render ();
|
||||
|
||||
SDL_GL_SwapWindow (gfx.window);
|
||||
}
|
||||
|
||||
+4
-1
@@ -6,6 +6,7 @@
|
||||
#include "event/manager.h"
|
||||
#include "event/handlers.h"
|
||||
#include "chunk/manager.h"
|
||||
#include "entity/templates.h"
|
||||
#include "state.h"
|
||||
#include "resources.h"
|
||||
#include "graphics.h"
|
||||
@@ -24,6 +25,7 @@ SDL_AppInit (void **appstate, int argc, char **argv)
|
||||
Resources_init ();
|
||||
ChunkManager_init ();
|
||||
EntityManager_init ();
|
||||
EntityTemplates_init ();
|
||||
GameState_init (SDL_GetTicks ());
|
||||
/* Set some basic game flags for testing. */
|
||||
game_flags.can_free_look = 1;
|
||||
@@ -51,8 +53,9 @@ SDL_AppIterate (void *appstate)
|
||||
{
|
||||
UNUSED (appstate);
|
||||
|
||||
ChunkManager_update (game_state.camera.position);
|
||||
GameState_tick (SDL_GetTicks ());
|
||||
EntityManager_tick ();
|
||||
ChunkManager_update (game_state.camera.position);
|
||||
GraphicsContext_render ();
|
||||
|
||||
return SDL_APP_CONTINUE;
|
||||
|
||||
+11
-11
@@ -8,20 +8,20 @@ Resources_init ()
|
||||
resources.programs = g_hash_table_new (g_str_hash, g_str_equal);
|
||||
resources.textures = g_hash_table_new (g_str_hash, g_str_equal);
|
||||
|
||||
_RAP ("default", _RLP ("res/shaders/default.vert", "res/shaders/default.frag"));
|
||||
_RAP ("billboard", _RLP ("res/shaders/billboard.vert", "res/shaders/billboard.frag"));
|
||||
_RAP ("chunk", _RLP ("res/shaders/chunk.vert", "res/shaders/chunk.frag"));
|
||||
R_PRGADD ("default", R_PRGLOAD ("res/shaders/default.vert", "res/shaders/default.frag"));
|
||||
R_PRGADD ("billboard", R_PRGLOAD ("res/shaders/billboard.vert", "res/shaders/billboard.frag"));
|
||||
R_PRGADD ("chunk", R_PRGLOAD ("res/shaders/chunk.vert", "res/shaders/chunk.frag"));
|
||||
|
||||
program_use (_RGP ("chunk"));
|
||||
program_set_int_array (_RGP ("chunk"), "materials", 5, (int[5]) { 0, 1, 2, 3, 4 });
|
||||
program_use (R_PRGGET ("chunk"));
|
||||
program_set_int_array (R_PRGGET ("chunk"), "materials", 5, (int[5]) { 0, 1, 2, 3, 4 });
|
||||
|
||||
_RAT ("terrain0", _RLT ("res/textures/dirt.png"));
|
||||
_RAT ("terrain1", _RLT ("res/textures/drygrass.png"));
|
||||
_RAT ("terrain2", _RLT ("res/textures/grass.png"));
|
||||
_RAT ("terrain3", _RLT ("res/textures/rock_cement.png"));
|
||||
_RAT ("terrain4", _RLT ("res/textures/asphalt_gravel.png"));
|
||||
R_TEXADD ("terrain0", R_TEXLOAD ("res/textures/dirt.png"));
|
||||
R_TEXADD ("terrain1", R_TEXLOAD ("res/textures/drygrass.png"));
|
||||
R_TEXADD ("terrain2", R_TEXLOAD ("res/textures/grass.png"));
|
||||
R_TEXADD ("terrain3", R_TEXLOAD ("res/textures/rock_cement.png"));
|
||||
R_TEXADD ("terrain4", R_TEXLOAD ("res/textures/asphalt_gravel.png"));
|
||||
|
||||
_RAT ("tree0", _RLT ("res/textures/tree.png"));
|
||||
R_TEXADD ("tree0", R_TEXLOAD ("res/textures/tree.png"));
|
||||
}
|
||||
|
||||
unsigned int
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
#include "program.h"
|
||||
#include "texture.h"
|
||||
|
||||
#define _RAP(key, program) (g_hash_table_insert (resources.programs, key, GINT_TO_POINTER (program)))
|
||||
#define _RAT(key, texture) (g_hash_table_insert (resources.textures, key, GINT_TO_POINTER (texture)))
|
||||
#define _RLP(v_path, f_path) (Resources_load_program (v_path, f_path))
|
||||
#define _RLT(path) (Resources_load_texture (path))
|
||||
#define _RGP(key) (GPOINTER_TO_INT (g_hash_table_lookup (resources.programs, key)))
|
||||
#define _RGT(key) (GPOINTER_TO_INT (g_hash_table_lookup (resources.textures, key)))
|
||||
#define R_PRGADD(key, program) (g_hash_table_insert (resources.programs, key, GINT_TO_POINTER (program)))
|
||||
#define R_TEXADD(key, texture) (g_hash_table_insert (resources.textures, key, GINT_TO_POINTER (texture)))
|
||||
#define R_PRGLOAD(v_path, f_path) (Resources_load_program (v_path, f_path))
|
||||
#define R_TEXLOAD(path) (Resources_load_texture (path))
|
||||
#define R_PRGGET(key) (GPOINTER_TO_INT (g_hash_table_lookup (resources.programs, key)))
|
||||
#define R_TEXGET(key) (GPOINTER_TO_INT (g_hash_table_lookup (resources.textures, key)))
|
||||
|
||||
typedef struct Resources {
|
||||
GHashTable *programs;
|
||||
|
||||
Reference in New Issue
Block a user