Spritebatch cleanup
This commit is contained in:
@@ -22,7 +22,7 @@
|
||||
#include "util/memory.h"
|
||||
#include "util/string.h"
|
||||
#include "asset/asset.h"
|
||||
#include "display/shader/shaderunlit.h"
|
||||
#include "display/shader/shaderlist.h"
|
||||
#include "time/time.h"
|
||||
|
||||
display_t DISPLAY = { 0 };
|
||||
@@ -53,31 +53,8 @@ errorret_t displayInit(void) {
|
||||
errorChain(screenInit());
|
||||
|
||||
// Setup initial shader with default values
|
||||
mat4 view, proj, model;
|
||||
glm_lookat(
|
||||
(vec3){ 0.0f, 0.0f, 1.0f },
|
||||
(vec3){ 0.0f, 0.0f, 0.0f },
|
||||
(vec3){ 0.0f, 1.0f, 0.0f },
|
||||
view
|
||||
);
|
||||
|
||||
glm_perspective(
|
||||
glm_rad(45.0f),
|
||||
SCREEN.aspect,
|
||||
0.1f,
|
||||
100.0f,
|
||||
proj
|
||||
);
|
||||
|
||||
glm_mat4_identity(model);
|
||||
|
||||
errorChain(shaderInit(&SHADER_UNLIT, &SHADER_UNLIT_DEFINITION));
|
||||
errorChain(shaderBind(&SHADER_UNLIT));
|
||||
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_PROJECTION, proj));
|
||||
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_VIEW, view));
|
||||
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_MODEL, model));
|
||||
errorChain(shaderSetTexture(&SHADER_UNLIT, SHADER_UNLIT_TEXTURE, NULL));
|
||||
errorChain(shaderSetColor(&SHADER_UNLIT, SHADER_UNLIT_COLOR, COLOR_WHITE));
|
||||
errorChain(shaderListInit());
|
||||
|
||||
errorOk();
|
||||
}
|
||||
@@ -119,7 +96,7 @@ errorret_t displaySetState(displaystate_t state) {
|
||||
}
|
||||
|
||||
errorret_t displayDispose(void) {
|
||||
errorChain(shaderDispose(&SHADER_UNLIT));
|
||||
errorChain(shaderListDispose());
|
||||
errorChain(spriteBatchDispose());
|
||||
screenDispose();
|
||||
errorChain(textDispose());
|
||||
|
||||
@@ -7,5 +7,6 @@
|
||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
shader.c
|
||||
shaderlist.c
|
||||
shaderunlit.c
|
||||
)
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "shader.h"
|
||||
#include "shadermaterial.h"
|
||||
#include "assert/assert.h"
|
||||
#include "log/log.h"
|
||||
|
||||
shader_t *bound = NULL;
|
||||
|
||||
@@ -57,6 +58,7 @@ errorret_t shaderSetColor(
|
||||
) {
|
||||
assertNotNull(shader, "Shader cannot be null");
|
||||
assertStrLenMin(name, 1, "Uniform name cannot be empty");
|
||||
assertTrue(bound == shader, "Shader must be bound.");
|
||||
errorChain(shaderSetColorPlatform(shader, name, color));
|
||||
errorOk();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "shaderlist.h"
|
||||
#include "display/screen/screen.h"
|
||||
#include "assert/assert.h"
|
||||
|
||||
shaderlistdef_t SHADER_LIST_DEFS[SHADER_LIST_SHADER_COUNT] = {
|
||||
[SHADER_LIST_SHADER_UNLIT] = {
|
||||
.shader = &SHADER_UNLIT,
|
||||
.definition = &SHADER_UNLIT_DEFINITION
|
||||
},
|
||||
};
|
||||
|
||||
errorret_t shaderListInit() {
|
||||
mat4 view, proj, model;
|
||||
glm_lookat(
|
||||
(vec3){ 0.0f, 0.0f, 1.0f },
|
||||
(vec3){ 0.0f, 0.0f, 0.0f },
|
||||
(vec3){ 0.0f, 1.0f, 0.0f },
|
||||
view
|
||||
);
|
||||
|
||||
glm_perspective(
|
||||
glm_rad(45.0f),
|
||||
SCREEN.aspect,
|
||||
0.1f,
|
||||
100.0f,
|
||||
proj
|
||||
);
|
||||
|
||||
glm_mat4_identity(model);
|
||||
|
||||
for(shaderlistshader_t i = 0; i < SHADER_LIST_SHADER_COUNT; i++) {
|
||||
if(i == SHADER_LIST_SHADER_NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
assertNotNull(
|
||||
SHADER_LIST_DEFS[i].shader, "Shader cannot be null"
|
||||
);
|
||||
assertNotNull(
|
||||
SHADER_LIST_DEFS[i].definition, "Shader definition cannot be null"
|
||||
);
|
||||
|
||||
errorChain(shaderInit(
|
||||
SHADER_LIST_DEFS[i].shader, SHADER_LIST_DEFS[i].definition
|
||||
));
|
||||
errorChain(shaderBind(SHADER_LIST_DEFS[i].shader));
|
||||
errorChain(shaderSetMatrix(
|
||||
SHADER_LIST_DEFS[i].shader, SHADER_UNLIT_PROJECTION, proj
|
||||
));
|
||||
errorChain(shaderSetMatrix(
|
||||
SHADER_LIST_DEFS[i].shader, SHADER_UNLIT_VIEW, view
|
||||
));
|
||||
errorChain(shaderSetMatrix(
|
||||
SHADER_LIST_DEFS[i].shader, SHADER_UNLIT_MODEL, model
|
||||
));
|
||||
errorChain(shaderSetTexture(
|
||||
SHADER_LIST_DEFS[i].shader, SHADER_UNLIT_TEXTURE, NULL
|
||||
));
|
||||
errorChain(shaderSetColor(
|
||||
SHADER_LIST_DEFS[i].shader, SHADER_UNLIT_COLOR, COLOR_WHITE
|
||||
));
|
||||
}
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t shaderListDispose(void) {
|
||||
for(shaderlistshader_t i = 0; i < SHADER_LIST_SHADER_COUNT; i++) {
|
||||
if(i == SHADER_LIST_SHADER_NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
assertNotNull(
|
||||
SHADER_LIST_DEFS[i].shader, "Shader cannot be null"
|
||||
);
|
||||
|
||||
errorChain(shaderDispose(SHADER_LIST_DEFS[i].shader));
|
||||
}
|
||||
errorOk();
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "display/shader/shader.h"
|
||||
#include "display/shader/shaderunlit.h"
|
||||
|
||||
typedef enum {
|
||||
SHADER_LIST_SHADER_NULL,
|
||||
|
||||
SHADER_LIST_SHADER_UNLIT,
|
||||
|
||||
SHADER_LIST_SHADER_COUNT
|
||||
} shaderlistshader_t;
|
||||
|
||||
typedef struct {
|
||||
shader_t *shader;
|
||||
shaderdefinition_t *definition;
|
||||
} shaderlistdef_t;
|
||||
|
||||
extern shaderlistdef_t SHADER_LIST_DEFS[SHADER_LIST_SHADER_COUNT];
|
||||
|
||||
/**
|
||||
* Initializes all default shaders and uploads the initial view, projection,
|
||||
* and model matrices to each.
|
||||
*
|
||||
* @return Error state.
|
||||
*/
|
||||
errorret_t shaderListInit();
|
||||
|
||||
/**
|
||||
* Disposes all default shaders.
|
||||
*
|
||||
* @return Error state.
|
||||
*/
|
||||
errorret_t shaderListDispose(void);
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "display/shader/shaderunlit.h"
|
||||
#include "display/shader/shaderlist.h"
|
||||
|
||||
typedef union shadermaterial_u {
|
||||
shaderunlitmaterial_t unlit;
|
||||
|
||||
@@ -9,14 +9,13 @@
|
||||
#include "assert/assert.h"
|
||||
#include "util/memory.h"
|
||||
#include "util/math.h"
|
||||
#include "display/shader/shaderunlit.h"
|
||||
#include "display/shader/shadermaterial.h"
|
||||
|
||||
meshvertex_t SPRITEBATCH_VERTICES[SPRITEBATCH_VERTEX_COUNT];
|
||||
spritebatch_t SPRITEBATCH;
|
||||
|
||||
errorret_t spriteBatchInit() {
|
||||
memoryZero(&SPRITEBATCH, sizeof(spritebatch_t));
|
||||
|
||||
errorChain(meshInit(
|
||||
&SPRITEBATCH.mesh,
|
||||
QUAD_PRIMITIVE_TYPE,
|
||||
@@ -28,137 +27,100 @@ errorret_t spriteBatchInit() {
|
||||
|
||||
errorret_t spriteBatchBuffer(
|
||||
const spritebatchsprite_t *sprites,
|
||||
const uint32_t count
|
||||
const uint32_t count,
|
||||
shader_t *shader,
|
||||
const shadermaterial_t material
|
||||
) {
|
||||
for(uint32_t i = 0; i < count; i++) {
|
||||
const spritebatchsprite_t *s = &sprites[i];
|
||||
assertNotNull(sprites, "Sprites cannot be null");
|
||||
assertTrue(count > 0, "Count must be greater than zero");
|
||||
assertNotNull(shader, "Shader cannot be null");
|
||||
|
||||
// if(s->texture != SPRITEBATCH.currentTexture) {
|
||||
// errorChain(spriteBatchFlush());
|
||||
// }
|
||||
// Did the shader or material data change?
|
||||
if(shader != SPRITEBATCH.shader) {
|
||||
errorChain(spriteBatchFlush());
|
||||
SPRITEBATCH.shader = shader;
|
||||
SPRITEBATCH.material = material;
|
||||
} else if(memoryCompare(
|
||||
&material, &SPRITEBATCH.material, sizeof(shadermaterial_t)
|
||||
) != 0) {
|
||||
// Did the material data change?
|
||||
errorChain(spriteBatchFlush());
|
||||
SPRITEBATCH.shader = shader;
|
||||
SPRITEBATCH.material = material;
|
||||
}
|
||||
|
||||
if(SPRITEBATCH.spriteCount >= SPRITEBATCH_SPRITES_MAX_PER_FLUSH) {
|
||||
errorChain(spriteBatchFlush());
|
||||
}
|
||||
// Buffer the vertices.
|
||||
for(uint32_t i = 0; i < count; i++ ){
|
||||
spritebatchsprite_t sprite = sprites[i];
|
||||
|
||||
meshvertex_t *v = &SPRITEBATCH_VERTICES[
|
||||
(SPRITEBATCH.spriteCount + (SPRITEBATCH.spriteFlush *
|
||||
SPRITEBATCH_SPRITES_MAX_PER_FLUSH)) * QUAD_VERTEX_COUNT
|
||||
];
|
||||
|
||||
// Buffer the quad
|
||||
v[0].pos[0] = sprite.min[0];
|
||||
v[0].pos[1] = sprite.min[1];
|
||||
v[0].pos[2] = sprite.min[2];
|
||||
|
||||
v[0].pos[0] = s->min[0]; v[0].pos[1] = s->min[1]; v[0].pos[2] = s->min[2];
|
||||
v[0].uv[0] = s->uvMin[0]; v[0].uv[1] = s->uvMin[1];
|
||||
v[0].uv[0] = sprite.uvMin[0];
|
||||
v[0].uv[1] = sprite.uvMin[1];
|
||||
|
||||
v[1].pos[0] = s->max[0]; v[1].pos[1] = s->min[1]; v[1].pos[2] = s->min[2];
|
||||
v[1].uv[0] = s->uvMax[0]; v[1].uv[1] = s->uvMin[1];
|
||||
|
||||
v[2].pos[0] = s->max[0]; v[2].pos[1] = s->max[1]; v[2].pos[2] = s->min[2];
|
||||
v[2].uv[0] = s->uvMax[0]; v[2].uv[1] = s->uvMax[1];
|
||||
v[1].pos[0] = sprite.max[0];
|
||||
v[1].pos[1] = sprite.min[1];
|
||||
v[1].pos[2] = sprite.min[2];
|
||||
|
||||
v[3].pos[0] = s->min[0]; v[3].pos[1] = s->min[1]; v[3].pos[2] = s->min[2];
|
||||
v[3].uv[0] = s->uvMin[0]; v[3].uv[1] = s->uvMin[1];
|
||||
v[1].uv[0] = sprite.uvMax[0];
|
||||
v[1].uv[1] = sprite.uvMin[1];
|
||||
|
||||
v[4].pos[0] = s->max[0]; v[4].pos[1] = s->max[1]; v[4].pos[2] = s->min[2];
|
||||
v[4].uv[0] = s->uvMax[0]; v[4].uv[1] = s->uvMax[1];
|
||||
|
||||
v[5].pos[0] = s->min[0]; v[5].pos[1] = s->max[1]; v[5].pos[2] = s->min[2];
|
||||
v[5].uv[0] = s->uvMin[0]; v[5].uv[1] = s->uvMax[1];
|
||||
v[2].pos[0] = sprite.max[0];
|
||||
v[2].pos[1] = sprite.max[1];
|
||||
v[2].pos[2] = sprite.max[2];
|
||||
|
||||
#if MESH_ENABLE_COLOR
|
||||
for(uint8_t vi = 0; vi < QUAD_VERTEX_COUNT; vi++) {
|
||||
v[vi].color = s->color;
|
||||
}
|
||||
#endif
|
||||
v[2].uv[0] = sprite.uvMax[0];
|
||||
v[2].uv[1] = sprite.uvMax[1];
|
||||
|
||||
|
||||
v[3].pos[0] = sprite.min[0];
|
||||
v[3].pos[1] = sprite.min[1];
|
||||
v[3].pos[2] = sprite.min[2];
|
||||
|
||||
v[3].uv[0] = sprite.uvMin[0];
|
||||
v[3].uv[1] = sprite.uvMin[1];
|
||||
|
||||
|
||||
v[4].pos[0] = sprite.max[0];
|
||||
v[4].pos[1] = sprite.max[1];
|
||||
v[4].pos[2] = sprite.max[2];
|
||||
|
||||
v[4].uv[0] = sprite.uvMax[0];
|
||||
v[4].uv[1] = sprite.uvMax[1];
|
||||
|
||||
|
||||
v[5].pos[0] = sprite.min[0];
|
||||
v[5].pos[1] = sprite.max[1];
|
||||
v[5].pos[2] = sprite.max[2];
|
||||
|
||||
v[5].uv[0] = sprite.uvMin[0];
|
||||
v[5].uv[1] = sprite.uvMax[1];
|
||||
|
||||
// Do we need to flush?
|
||||
SPRITEBATCH.spriteCount++;
|
||||
if(SPRITEBATCH.spriteCount >= SPRITEBATCH_SPRITES_MAX_PER_FLUSH) {
|
||||
errorChain(spriteBatchFlush());
|
||||
}
|
||||
}
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t spriteBatchBufferSprite(const spritebatchsprite_t *sprite) {
|
||||
return spriteBatchBuffer(sprite, 1);
|
||||
}
|
||||
|
||||
errorret_t spriteBatchPushv(
|
||||
const float_t *minX,
|
||||
const float_t *minY,
|
||||
const float_t *maxX,
|
||||
const float_t *maxY,
|
||||
const float_t *z,
|
||||
#if MESH_ENABLE_COLOR
|
||||
const color_t *color,
|
||||
#endif
|
||||
const float_t *u0,
|
||||
const float_t *v0,
|
||||
const float_t *u1,
|
||||
const float_t *v1,
|
||||
const size_t count
|
||||
) {
|
||||
for(size_t i = 0; i < count; i++) {
|
||||
spritebatchsprite_t sprite;
|
||||
sprite.min[0] = minX[i]; sprite.min[1] = minY[i]; sprite.min[2] = z[i];
|
||||
sprite.max[0] = maxX[i]; sprite.max[1] = maxY[i]; sprite.max[2] = z[i];
|
||||
sprite.uvMin[0] = u0[i]; sprite.uvMin[1] = v0[i];
|
||||
sprite.uvMax[0] = u1[i]; sprite.uvMax[1] = v1[i];
|
||||
sprite.texture = SPRITEBATCH.currentTexture;
|
||||
#if MESH_ENABLE_COLOR
|
||||
sprite.color = color[i];
|
||||
#endif
|
||||
errorChain(spriteBatchBuffer(&sprite, 1));
|
||||
}
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t spriteBatchPush(
|
||||
const float_t minX,
|
||||
const float_t minY,
|
||||
const float_t maxX,
|
||||
const float_t maxY,
|
||||
#if MESH_ENABLE_COLOR
|
||||
const color_t color,
|
||||
#endif
|
||||
const float_t u0,
|
||||
const float_t v0,
|
||||
const float_t u1,
|
||||
const float_t v1
|
||||
) {
|
||||
spritebatchsprite_t sprite;
|
||||
sprite.min[0] = minX; sprite.min[1] = minY; sprite.min[2] = 0;
|
||||
sprite.max[0] = maxX; sprite.max[1] = maxY; sprite.max[2] = 0;
|
||||
sprite.uvMin[0] = u0; sprite.uvMin[1] = v0;
|
||||
sprite.uvMax[0] = u1; sprite.uvMax[1] = v1;
|
||||
sprite.texture = SPRITEBATCH.currentTexture;
|
||||
#if MESH_ENABLE_COLOR
|
||||
sprite.color = color;
|
||||
#endif
|
||||
return spriteBatchBuffer(&sprite, 1);
|
||||
}
|
||||
|
||||
errorret_t spriteBatchPushZ(
|
||||
const vec3 min,
|
||||
const vec3 max,
|
||||
#if MESH_ENABLE_COLOR
|
||||
const color_t color,
|
||||
#endif
|
||||
const vec2 uvMin,
|
||||
const vec2 uvMax
|
||||
) {
|
||||
spritebatchsprite_t sprite;
|
||||
sprite.min[0] = min[0]; sprite.min[1] = min[1]; sprite.min[2] = min[2];
|
||||
sprite.max[0] = max[0]; sprite.max[1] = max[1]; sprite.max[2] = max[2];
|
||||
sprite.uvMin[0] = uvMin[0]; sprite.uvMin[1] = uvMin[1];
|
||||
sprite.uvMax[0] = uvMax[0]; sprite.uvMax[1] = uvMax[1];
|
||||
sprite.texture = SPRITEBATCH.currentTexture;
|
||||
#if MESH_ENABLE_COLOR
|
||||
sprite.color = color;
|
||||
#endif
|
||||
return spriteBatchBuffer(&sprite, 1);
|
||||
}
|
||||
|
||||
void spriteBatchClear() {
|
||||
SPRITEBATCH.spriteCount = 0;
|
||||
SPRITEBATCH.spriteFlush = 0;
|
||||
SPRITEBATCH.currentTexture = NULL;
|
||||
SPRITEBATCH.shader = NULL;
|
||||
memoryZero(&SPRITEBATCH.material, sizeof(shadermaterial_t));
|
||||
}
|
||||
|
||||
errorret_t spriteBatchFlush() {
|
||||
@@ -171,6 +133,9 @@ errorret_t spriteBatchFlush() {
|
||||
SPRITEBATCH.spriteFlush * SPRITEBATCH_SPRITES_MAX_PER_FLUSH *
|
||||
QUAD_VERTEX_COUNT
|
||||
);
|
||||
|
||||
errorChain(shaderBind(SPRITEBATCH.shader));
|
||||
errorChain(shaderSetMaterial(SPRITEBATCH.shader, &SPRITEBATCH.material));
|
||||
errorChain(meshFlush(&SPRITEBATCH.mesh, vertexOffset, vertexCount));
|
||||
errorChain(meshDraw(&SPRITEBATCH.mesh, vertexOffset, vertexCount));
|
||||
|
||||
@@ -186,4 +151,4 @@ errorret_t spriteBatchFlush() {
|
||||
errorret_t spriteBatchDispose() {
|
||||
errorChain(meshDispose(&SPRITEBATCH.mesh));
|
||||
errorOk();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#pragma once
|
||||
#include "display/mesh/quad.h"
|
||||
#include "display/texture/texture.h"
|
||||
#include "display/shader/shadermaterial.h"
|
||||
|
||||
#define SPRITEBATCH_SPRITES_MAX 512
|
||||
#define SPRITEBATCH_VERTEX_COUNT (SPRITEBATCH_SPRITES_MAX * QUAD_VERTEX_COUNT)
|
||||
@@ -21,146 +22,66 @@ typedef struct {
|
||||
vec3 max;
|
||||
vec2 uvMin;
|
||||
vec2 uvMax;
|
||||
texture_t *texture;
|
||||
#if MESH_ENABLE_COLOR
|
||||
color_t color;
|
||||
#endif
|
||||
} spritebatchsprite_t;
|
||||
|
||||
typedef struct {
|
||||
mesh_t mesh;
|
||||
int32_t spriteCount;
|
||||
int32_t spriteFlush;
|
||||
texture_t *currentTexture;
|
||||
|
||||
shader_t *shader;
|
||||
shadermaterial_t material;
|
||||
} spritebatch_t;
|
||||
|
||||
// Have to define these seperately because of alignment in certain platforms.
|
||||
// Have to define these separately because of alignment on certain platforms.
|
||||
extern meshvertex_t SPRITEBATCH_VERTICES[SPRITEBATCH_VERTEX_COUNT];
|
||||
extern spritebatch_t SPRITEBATCH;
|
||||
|
||||
/**
|
||||
* Initializes a sprite batch.
|
||||
* Initializes the global sprite batch and its internal mesh buffer.
|
||||
*
|
||||
* @return An error code indicating success or failure.
|
||||
* @return Error state.
|
||||
*/
|
||||
errorret_t spriteBatchInit();
|
||||
|
||||
/**
|
||||
* Buffers an array of sprites. Flushes automatically when the texture changes
|
||||
* or the per-flush limit is reached.
|
||||
* Lowest-level buffer function. Writes sprites into the internal vertex buffer.
|
||||
* Flushes automatically when the per-flush capacity is reached. Does not
|
||||
* modify material state — call spriteBatchSetState or use a high-level push
|
||||
* function before buffering.
|
||||
*
|
||||
* @param sprites Pointer to the sprite array.
|
||||
* @param count Number of sprites to buffer.
|
||||
* @return An error code indicating success or failure.
|
||||
* @param shader Shader to use when flushing.
|
||||
* @param material Material information passed to the shader when flushing.
|
||||
* @return Error state.
|
||||
*/
|
||||
errorret_t spriteBatchBuffer(
|
||||
const spritebatchsprite_t *sprites,
|
||||
const uint32_t count
|
||||
const uint32_t count,
|
||||
shader_t *shader,
|
||||
const shadermaterial_t material
|
||||
);
|
||||
|
||||
/**
|
||||
* Convenience wrapper — buffers a single sprite.
|
||||
*
|
||||
* @param sprite The sprite to buffer.
|
||||
* @return An error code indicating success or failure.
|
||||
*/
|
||||
errorret_t spriteBatchBufferSprite(const spritebatchsprite_t *sprite);
|
||||
|
||||
/**
|
||||
* Pushes multiple sprites to the batch using parallel arrays of values.
|
||||
*
|
||||
* @param minX Array of minimum x coordinates.
|
||||
* @param minY Array of minimum y coordinates.
|
||||
* @param maxX Array of maximum x coordinates.
|
||||
* @param maxY Array of maximum y coordinates.
|
||||
* @param z Array of z coordinates.
|
||||
* @param color Array of colors (if enabled).
|
||||
* @param u0 Array of u0 texture coordinates.
|
||||
* @param v0 Array of v0 texture coordinates.
|
||||
* @param u1 Array of u1 texture coordinates.
|
||||
* @param v1 Array of v1 texture coordinates.
|
||||
* @param count Number of sprites to push.
|
||||
* @return An error code indicating success or failure.
|
||||
*/
|
||||
errorret_t spriteBatchPushv(
|
||||
const float_t *minX,
|
||||
const float_t *minY,
|
||||
const float_t *maxX,
|
||||
const float_t *maxY,
|
||||
const float_t *z,
|
||||
#if MESH_ENABLE_COLOR
|
||||
const color_t *color,
|
||||
#endif
|
||||
const float_t *u0,
|
||||
const float_t *v0,
|
||||
const float_t *u1,
|
||||
const float_t *v1,
|
||||
const size_t count
|
||||
);
|
||||
|
||||
/**
|
||||
* Pushes a 2D sprite (z=0) to the batch.
|
||||
*
|
||||
* @param minX The minimum x coordinate of the sprite.
|
||||
* @param minY The minimum y coordinate of the sprite.
|
||||
* @param maxX The maximum x coordinate of the sprite.
|
||||
* @param maxY The maximum y coordinate of the sprite.
|
||||
* @param color The color to tint the sprite with.
|
||||
* @param u0 Texture u coordinate for the top-left corner.
|
||||
* @param v0 Texture v coordinate for the top-left corner.
|
||||
* @param u1 Texture u coordinate for the bottom-right corner.
|
||||
* @param v1 Texture v coordinate for the bottom-right corner.
|
||||
* @return An error code indicating success or failure.
|
||||
*/
|
||||
errorret_t spriteBatchPush(
|
||||
const float_t minX,
|
||||
const float_t minY,
|
||||
const float_t maxX,
|
||||
const float_t maxY,
|
||||
#if MESH_ENABLE_COLOR
|
||||
const color_t color,
|
||||
#endif
|
||||
const float_t u0,
|
||||
const float_t v0,
|
||||
const float_t u1,
|
||||
const float_t v1
|
||||
);
|
||||
|
||||
/**
|
||||
* Pushes a 3D sprite to the batch.
|
||||
*
|
||||
* @param min The minimum (x,y,z) coordinate of the sprite.
|
||||
* @param max The maximum (x,y,z) coordinate of the sprite.
|
||||
* @param color The color to tint the sprite with.
|
||||
* @param uvMin Texture coordinate for the top-left corner.
|
||||
* @param uvMax Texture coordinate for the bottom-right corner.
|
||||
* @return An error code indicating success or failure.
|
||||
*/
|
||||
errorret_t spriteBatchPushZ(
|
||||
const vec3 min,
|
||||
const vec3 max,
|
||||
#if MESH_ENABLE_COLOR
|
||||
const color_t color,
|
||||
#endif
|
||||
const vec2 uvMin,
|
||||
const vec2 uvMax
|
||||
);
|
||||
|
||||
/**
|
||||
* Clears the sprite batch. Calling flush after this renders nothing.
|
||||
* Resets sprite and flush counters and clears the current material state.
|
||||
* Calling spriteBatchFlush after this renders nothing.
|
||||
*/
|
||||
void spriteBatchClear();
|
||||
|
||||
/**
|
||||
* Flushes the sprite batch, rendering all queued sprites.
|
||||
* Uploads and draws all buffered sprites. If a material type has been set via
|
||||
* spriteBatchSetState or spriteBatchCheckState, the shader is bound and the
|
||||
* material is applied first. If matType is NULL the caller is responsible for
|
||||
* having the correct shader already bound. Does nothing if the buffer is empty.
|
||||
*
|
||||
* @return An error code indicating success or failure.
|
||||
* @return Error state.
|
||||
*/
|
||||
errorret_t spriteBatchFlush();
|
||||
|
||||
/**
|
||||
* Disposes of the sprite batch, freeing any allocated resources.
|
||||
* Disposes of the sprite batch and frees its internal mesh buffer.
|
||||
*
|
||||
* @return An error code indicating success or failure.
|
||||
* @return Error state.
|
||||
*/
|
||||
errorret_t spriteBatchDispose();
|
||||
|
||||
@@ -34,15 +34,12 @@ errorret_t textDispose(void) {
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t textDrawChar(
|
||||
const float_t x,
|
||||
const float_t y,
|
||||
const char_t c,
|
||||
#if MESH_ENABLE_COLOR
|
||||
const color_t color,
|
||||
#endif
|
||||
font_t *font
|
||||
spritebatchsprite_t textGetSprite(
|
||||
const vec2 pos, const char_t c, const font_t *font
|
||||
) {
|
||||
assertNotNull(font, "Font cannot be NULL");
|
||||
|
||||
// Change char from ASCII to a tile index.
|
||||
int32_t tileIndex = (int32_t)(c) - TEXT_CHAR_START;
|
||||
if(tileIndex < 0 || tileIndex >= font->tileset->tileCount) {
|
||||
tileIndex = ((int32_t)'@') - TEXT_CHAR_START;
|
||||
@@ -52,19 +49,22 @@ errorret_t textDrawChar(
|
||||
"Character is out of bounds for font tiles"
|
||||
);
|
||||
|
||||
// Create sprite.
|
||||
vec4 uv;
|
||||
tilesetTileGetUV(font->tileset, tileIndex, uv);
|
||||
|
||||
errorChain(spriteBatchPush(
|
||||
x, y,
|
||||
x + font->tileset->tileWidth,
|
||||
y + font->tileset->tileHeight,
|
||||
#if MESH_ENABLE_COLOR
|
||||
color,
|
||||
#endif
|
||||
uv[0], uv[1], uv[2], uv[3]
|
||||
));
|
||||
errorOk();
|
||||
spritebatchsprite_t sprite;
|
||||
sprite.min[0] = pos[0];
|
||||
sprite.min[1] = pos[1];
|
||||
sprite.min[2] = 0.0f;
|
||||
sprite.max[0] = pos[0] + font->tileset->tileWidth;
|
||||
sprite.max[1] = pos[1] + font->tileset->tileHeight;
|
||||
sprite.max[2] = 0.0f;
|
||||
sprite.uvMin[0] = uv[0];
|
||||
sprite.uvMin[1] = uv[1];
|
||||
sprite.uvMax[0] = uv[2];
|
||||
sprite.uvMax[1] = uv[3];
|
||||
return sprite;
|
||||
}
|
||||
|
||||
errorret_t textDraw(
|
||||
@@ -76,6 +76,14 @@ errorret_t textDraw(
|
||||
) {
|
||||
assertNotNull(text, "Text cannot be NULL");
|
||||
|
||||
spritebatchsprite_t sprite;
|
||||
shadermaterial_t material = {
|
||||
.unlit = {
|
||||
.color = color,
|
||||
.texture = font->texture
|
||||
}
|
||||
};
|
||||
|
||||
float_t posX = x;
|
||||
float_t posY = y;
|
||||
|
||||
@@ -100,13 +108,8 @@ errorret_t textDraw(
|
||||
continue;
|
||||
}
|
||||
|
||||
errorChain(textDrawChar(
|
||||
posX, posY, c,
|
||||
#if MESH_ENABLE_COLOR
|
||||
color,
|
||||
#endif
|
||||
font
|
||||
));
|
||||
sprite = textGetSprite((vec2){posX, posY}, c, font);
|
||||
errorChain(spriteBatchBuffer(&sprite, 1, &SHADER_UNLIT, material));
|
||||
posX += font->tileset->tileWidth;
|
||||
}
|
||||
errorOk();
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#pragma once
|
||||
#include "asset/asset.h"
|
||||
#include "display/text/font.h"
|
||||
#include "display/spritebatch/spritebatch.h"
|
||||
|
||||
#define TEXT_CHAR_START '!'
|
||||
|
||||
@@ -28,23 +29,17 @@ errorret_t textInit(void);
|
||||
errorret_t textDispose(void);
|
||||
|
||||
/**
|
||||
* Draws a single character at the specified position.
|
||||
* Builds a sprite for a single character at the given position.
|
||||
*
|
||||
* @param x The x-coordinate to draw the character at.
|
||||
* @param y The y-coordinate to draw the character at.
|
||||
* @param c The character to draw.
|
||||
* @param color The color to draw the character in.
|
||||
* @param font Font to use for rendering.
|
||||
* @return Either an error or success result.
|
||||
* @param pos The (x, y) position of the character in screen/world space.
|
||||
* @param c The character to build a sprite for.
|
||||
* @param font Font to use for tile lookup.
|
||||
* @return The populated sprite ready for spriteBatchBuffer.
|
||||
*/
|
||||
errorret_t textDrawChar(
|
||||
const float_t x,
|
||||
const float_t y,
|
||||
spritebatchsprite_t textGetSprite(
|
||||
const vec2 pos,
|
||||
const char_t c,
|
||||
#if MESH_ENABLE_COLOR
|
||||
const color_t color,
|
||||
#endif
|
||||
font_t *font
|
||||
const font_t *font
|
||||
);
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,22 +7,12 @@
|
||||
|
||||
#include "entityrenderable.h"
|
||||
#include "entity/entitymanager.h"
|
||||
#include "display/shader/shadermaterial.h"
|
||||
#include "display/shader/shaderunlit.h"
|
||||
#include "display/display.h"
|
||||
#include "display/mesh/cube.h"
|
||||
#include "display/spritebatch/spritebatch.h"
|
||||
|
||||
errorret_t entityRenderableDrawDefault(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
void *user
|
||||
) {
|
||||
(void)entityId;
|
||||
(void)componentId;
|
||||
(void)user;
|
||||
errorChain(shaderSetColor(&SHADER_UNLIT, SHADER_UNLIT_COLOR, COLOR_WHITE));
|
||||
errorChain(shaderSetTexture(&SHADER_UNLIT, SHADER_UNLIT_TEXTURE, NULL));
|
||||
return meshDraw(&CUBE_MESH_SIMPLE, 0, -1);
|
||||
}
|
||||
#include "util/memory.h"
|
||||
#include "assert/assert.h"
|
||||
|
||||
void entityRenderableInit(
|
||||
const entityid_t entityId,
|
||||
@@ -31,15 +21,44 @@ void entityRenderableInit(
|
||||
entityrenderable_t *r = componentGetData(
|
||||
entityId, componentId, COMPONENT_TYPE_RENDERABLE
|
||||
);
|
||||
r->draw = entityRenderableDrawDefault;
|
||||
r->drawUser = NULL;
|
||||
memoryZero(r, sizeof(entityrenderable_t));
|
||||
r->type = ENTITY_RENDERABLE_TYPE_SHADER_MATERIAL;
|
||||
r->data.material.shaderType = SHADER_LIST_SHADER_UNLIT;
|
||||
r->data.material.material.unlit.color = COLOR_WHITE;
|
||||
r->data.material.meshes[0] = &CUBE_MESH_SIMPLE;
|
||||
r->data.material.meshOffsets[0] = 0;
|
||||
r->data.material.meshCounts[0] = -1;
|
||||
r->data.material.meshCount = 1;
|
||||
r->data.material.state.flags = DISPLAY_STATE_FLAG_DEPTH_TEST;
|
||||
}
|
||||
|
||||
void entityRenderableDispose(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
void entityRenderableSetType(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
const entityrenderabletype_t type
|
||||
) {
|
||||
entityrenderable_t *r = componentGetData(
|
||||
entityId, componentId, COMPONENT_TYPE_RENDERABLE
|
||||
);
|
||||
r->type = type;
|
||||
}
|
||||
|
||||
void entityRenderableSetPriority(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
const int8_t priority
|
||||
) {
|
||||
entityrenderable_t *r = componentGetData(
|
||||
entityId, componentId, COMPONENT_TYPE_RENDERABLE
|
||||
);
|
||||
r->priority = priority;
|
||||
}
|
||||
|
||||
void entityRenderableSetDraw(
|
||||
@@ -52,11 +71,13 @@ void entityRenderableSetDraw(
|
||||
),
|
||||
void *user
|
||||
) {
|
||||
assertNotNull(draw, "Draw callback cannot be null");
|
||||
entityrenderable_t *r = componentGetData(
|
||||
entityId, componentId, COMPONENT_TYPE_RENDERABLE
|
||||
);
|
||||
r->draw = draw;
|
||||
r->drawUser = user;
|
||||
r->type = ENTITY_RENDERABLE_TYPE_CUSTOM;
|
||||
r->data.custom.draw = draw;
|
||||
r->data.custom.drawUser = user;
|
||||
}
|
||||
|
||||
errorret_t entityRenderableDraw(
|
||||
@@ -66,5 +87,42 @@ errorret_t entityRenderableDraw(
|
||||
entityrenderable_t *r = componentGetData(
|
||||
entityId, componentId, COMPONENT_TYPE_RENDERABLE
|
||||
);
|
||||
return r->draw(entityId, componentId, r->drawUser);
|
||||
|
||||
switch(r->type) {
|
||||
case ENTITY_RENDERABLE_TYPE_SPRITEBATCH: {
|
||||
const entityrenderablespritebatch_t *sb = &r->data.spritebatch;
|
||||
errorChain(displaySetState((displaystate_t){
|
||||
.flags = DISPLAY_STATE_FLAG_BLEND
|
||||
}));
|
||||
spriteBatchClear();
|
||||
shadermaterial_t mat;
|
||||
memoryZero(&mat, sizeof(shadermaterial_t));
|
||||
mat.unlit.texture = sb->texture;
|
||||
mat.unlit.color = COLOR_WHITE;
|
||||
errorChain(spriteBatchBuffer(
|
||||
sb->sprites, sb->spriteCount,
|
||||
SHADER_LIST_DEFS[SHADER_LIST_SHADER_UNLIT].shader, mat
|
||||
));
|
||||
return spriteBatchFlush();
|
||||
}
|
||||
|
||||
case ENTITY_RENDERABLE_TYPE_SHADER_MATERIAL: {
|
||||
const entityrenderablematerial_t *m = &r->data.material;
|
||||
errorChain(displaySetState(m->state));
|
||||
shader_t *shader = SHADER_LIST_DEFS[m->shaderType].shader;
|
||||
assertNotNull(shader, "Shader cannot be null for material type");
|
||||
errorChain(shaderBind(shader));
|
||||
errorChain(shaderSetMaterial(shader, &m->material));
|
||||
for(uint8_t i = 0; i < m->meshCount; i++) {
|
||||
errorChain(meshDraw(m->meshes[i], m->meshOffsets[i], m->meshCounts[i]));
|
||||
}
|
||||
errorOk();
|
||||
}
|
||||
|
||||
case ENTITY_RENDERABLE_TYPE_CUSTOM:
|
||||
return r->data.custom.draw(entityId, componentId, r->data.custom.drawUser);
|
||||
|
||||
default:
|
||||
assertUnreachable("Invalid renderable type");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,32 @@
|
||||
#include "display/mesh/mesh.h"
|
||||
#include "display/shader/shadermaterial.h"
|
||||
#include "display/spritebatch/spritebatch.h"
|
||||
#include "display/displaystate.h"
|
||||
|
||||
#define ENTITY_RENDERABLE_SPRITEBATCH_SPRITES_MAX 64
|
||||
#define ENTITY_RENDERABLE_MESHES_MAX 8
|
||||
|
||||
typedef enum {
|
||||
ENTITY_RENDERABLE_TYPE_CUSTOM = 0,
|
||||
ENTITY_RENDERABLE_TYPE_SPRITEBATCH,
|
||||
ENTITY_RENDERABLE_TYPE_SHADER_MATERIAL,
|
||||
} entityrenderabletype_t;
|
||||
|
||||
typedef struct {
|
||||
spritebatchsprite_t sprites[ENTITY_RENDERABLE_SPRITEBATCH_SPRITES_MAX];
|
||||
uint32_t spriteCount;
|
||||
texture_t *texture;
|
||||
} entityrenderablespritebatch_t;
|
||||
|
||||
typedef struct {
|
||||
mesh_t *meshes[ENTITY_RENDERABLE_MESHES_MAX];
|
||||
int32_t meshOffsets[ENTITY_RENDERABLE_MESHES_MAX];
|
||||
int32_t meshCounts[ENTITY_RENDERABLE_MESHES_MAX];
|
||||
uint8_t meshCount;
|
||||
shaderlistshader_t shaderType;
|
||||
shadermaterial_t material;
|
||||
displaystate_t state;
|
||||
} entityrenderablematerial_t;
|
||||
|
||||
typedef struct {
|
||||
errorret_t (*draw)(
|
||||
@@ -18,12 +44,30 @@ typedef struct {
|
||||
void *user
|
||||
);
|
||||
void *drawUser;
|
||||
} entityrenderablecustom_t;
|
||||
|
||||
typedef union entityrenderabledata_u {
|
||||
entityrenderablespritebatch_t spritebatch;
|
||||
entityrenderablematerial_t material;
|
||||
entityrenderablecustom_t custom;
|
||||
} entityrenderabledata_t;
|
||||
|
||||
typedef struct {
|
||||
entityrenderabletype_t type;
|
||||
entityrenderabledata_t data;
|
||||
|
||||
/**
|
||||
* Render priority. 0 = auto (derived from type/flags). Higher values render
|
||||
* later (on top of lower values). Range: [-128..127] with 0 is auto.
|
||||
*/
|
||||
int8_t priority;
|
||||
} entityrenderable_t;
|
||||
|
||||
/**
|
||||
* Initializes the entity renderable component. Defaults to
|
||||
* ENTITY_RENDERABLE_TYPE_MATERIAL, the unlit shader, white color, no mesh.
|
||||
*
|
||||
* ENTITY_RENDERABLE_TYPE_SHADER_MATERIAL with the unlit shader, a white cube,
|
||||
* and depth-test enabled.
|
||||
*
|
||||
* @param entityId The entity to initialize the component for.
|
||||
* @param componentId The renderable component of the entity.
|
||||
*/
|
||||
@@ -33,8 +77,8 @@ void entityRenderableInit(
|
||||
);
|
||||
|
||||
/**
|
||||
* Disposes the entity renderable component, freeing any callback user data.
|
||||
*
|
||||
* Disposes the entity renderable component.
|
||||
*
|
||||
* @param entityId The entity to dispose the component for.
|
||||
* @param componentId The renderable component of the entity.
|
||||
*/
|
||||
@@ -44,11 +88,40 @@ void entityRenderableDispose(
|
||||
);
|
||||
|
||||
/**
|
||||
* Sets the draw callback for the entity's renderable component.
|
||||
* Sets the rendering type for the renderable component. Resets type-specific
|
||||
* data to zero.
|
||||
*
|
||||
* @param entityId The entity to configure.
|
||||
* @param componentId The renderable component.
|
||||
* @param type The rendering type to use.
|
||||
*/
|
||||
void entityRenderableSetType(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
const entityrenderabletype_t type
|
||||
);
|
||||
|
||||
/**
|
||||
* Sets the render priority. 0 = auto (derived from type/flags). Higher values
|
||||
* render later (on top). Use non-zero to force ordering.
|
||||
*
|
||||
* @param entityId The entity to configure.
|
||||
* @param componentId The renderable component.
|
||||
* @param priority The priority value, or 0 for auto.
|
||||
*/
|
||||
void entityRenderableSetPriority(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
const int8_t priority
|
||||
);
|
||||
|
||||
/**
|
||||
* Sets the draw callback, switching the type to ENTITY_RENDERABLE_TYPE_CUSTOM.
|
||||
*
|
||||
* @param entityId The entity to configure.
|
||||
* @param componentId The renderable component of the entity.
|
||||
* @param draw The draw callback to assign.
|
||||
* @param user Userdata passed to the callback.
|
||||
*/
|
||||
void entityRenderableSetDraw(
|
||||
const entityid_t entityId,
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
scene.c
|
||||
scenerenderpipeline.c
|
||||
)
|
||||
|
||||
# Subdirectories
|
||||
|
||||
@@ -7,40 +7,21 @@
|
||||
|
||||
#include "overworldground.h"
|
||||
#include "entity/entitymanager.h"
|
||||
#include "entity/component/display/entityrenderable.h"
|
||||
#include "entity/component/physics/entityphysics.h"
|
||||
#include "display/mesh/plane.h"
|
||||
#include "display/shader/shaderunlit.h"
|
||||
|
||||
#define OVERWORLD_GROUND_SIZE 20.0f
|
||||
|
||||
static errorret_t overworldGroundDraw(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
void *user
|
||||
) {
|
||||
(void)entityId;
|
||||
(void)componentId;
|
||||
(void)user;
|
||||
errorChain(shaderSetColor(&SHADER_UNLIT, SHADER_UNLIT_COLOR, COLOR_MAGENTA));
|
||||
errorChain(shaderSetTexture(&SHADER_UNLIT, SHADER_UNLIT_TEXTURE, NULL));
|
||||
return meshDraw(&PLANE_MESH_SIMPLE, 0, -1);
|
||||
}
|
||||
|
||||
void overworldGroundAdd(overworldground_t *ground) {
|
||||
ground->entityId = entityManagerAdd();
|
||||
ground->posCompId = entityAddComponent(
|
||||
ground->entityId, COMPONENT_TYPE_POSITION
|
||||
);
|
||||
componentid_t renderComp = entityAddComponent(
|
||||
ground->entityId, COMPONENT_TYPE_RENDERABLE
|
||||
);
|
||||
(void)entityAddComponent(ground->entityId, COMPONENT_TYPE_RENDERABLE);
|
||||
|
||||
vec3 pos = { -OVERWORLD_GROUND_SIZE, 0.0f, -OVERWORLD_GROUND_SIZE };
|
||||
vec3 scale = { OVERWORLD_GROUND_SIZE * 2.0f, 1.0f, OVERWORLD_GROUND_SIZE * 2.0f };
|
||||
entityPositionSetLocalPosition(ground->entityId, ground->posCompId, pos);
|
||||
entityPositionSetLocalScale(ground->entityId, ground->posCompId, scale);
|
||||
entityRenderableSetDraw(ground->entityId, renderComp, overworldGroundDraw, NULL);
|
||||
|
||||
// Separate physics entity centered on the finite ground area.
|
||||
// The visual entity's position is its corner {-size, 0, -size}, not its
|
||||
|
||||
@@ -11,9 +11,15 @@
|
||||
#include "overworldnpc.h"
|
||||
#include "console/console.h"
|
||||
#include "entity/entitymanager.h"
|
||||
#include "entity/component/display/entityrenderable.h"
|
||||
#include "display/shader/shaderlist.h"
|
||||
#include "entity/component/overworld/entityinteractable.h"
|
||||
#include "entity/component/overworld/entityoverworldcamera.h"
|
||||
#include "display/mesh/cube.h"
|
||||
#include "display/mesh/plane.h"
|
||||
#include "display/displaystate.h"
|
||||
#include "scene/scene.h"
|
||||
#include "assert/assert.h"
|
||||
|
||||
#define OVERWORLD (SCENE.data.overworld)
|
||||
|
||||
@@ -26,14 +32,72 @@ static void overworldSceneNpcInteract(
|
||||
consolePrint("NPC interacted with!");
|
||||
}
|
||||
|
||||
void overworldSceneConfigureShaderMaterial(
|
||||
const entityid_t entityId,
|
||||
const color_t color,
|
||||
mesh_t *mesh
|
||||
) {
|
||||
assertNotNull(mesh, "Mesh cannot be null");
|
||||
componentid_t renderComp = entityGetComponent(
|
||||
entityId, COMPONENT_TYPE_RENDERABLE
|
||||
);
|
||||
entityRenderableSetType(entityId, renderComp, ENTITY_RENDERABLE_TYPE_SHADER_MATERIAL);
|
||||
entityrenderable_t *r = componentGetData(
|
||||
entityId, renderComp, COMPONENT_TYPE_RENDERABLE
|
||||
);
|
||||
r->data.material.shaderType = SHADER_LIST_SHADER_UNLIT;
|
||||
r->data.material.material.unlit.color = color;
|
||||
r->data.material.material.unlit.texture = NULL;
|
||||
r->data.material.state.flags = DISPLAY_STATE_FLAG_DEPTH_TEST;
|
||||
r->data.material.meshes[0] = mesh;
|
||||
r->data.material.meshOffsets[0] = 0;
|
||||
r->data.material.meshCounts[0] = -1;
|
||||
r->data.material.meshCount = 1;
|
||||
}
|
||||
|
||||
void overworldSceneConfigureSprite(
|
||||
const entityid_t entityId,
|
||||
const color_t color,
|
||||
texture_t *texture
|
||||
) {
|
||||
componentid_t renderComp = entityGetComponent(
|
||||
entityId, COMPONENT_TYPE_RENDERABLE
|
||||
);
|
||||
entityRenderableSetType(entityId, renderComp, ENTITY_RENDERABLE_TYPE_SPRITEBATCH);
|
||||
entityrenderable_t *r = componentGetData(
|
||||
entityId, renderComp, COMPONENT_TYPE_RENDERABLE
|
||||
);
|
||||
r->data.spritebatch.texture = texture;
|
||||
r->data.spritebatch.spriteCount = 1;
|
||||
|
||||
r->data.spritebatch.sprites[0].min[0] = -0.5f;
|
||||
r->data.spritebatch.sprites[0].min[1] = -0.0f;
|
||||
r->data.spritebatch.sprites[0].min[2] = -0.5f;
|
||||
|
||||
r->data.spritebatch.sprites[0].max[0] = 0.5f;
|
||||
r->data.spritebatch.sprites[0].max[1] = 0.0f;
|
||||
r->data.spritebatch.sprites[0].max[2] = 0.5f;
|
||||
|
||||
r->data.spritebatch.sprites[0].uvMin[0] = 0.0f;
|
||||
r->data.spritebatch.sprites[0].uvMin[1] = 0.0f;
|
||||
r->data.spritebatch.sprites[0].uvMax[0] = 1.0f;
|
||||
r->data.spritebatch.sprites[0].uvMax[1] = 1.0f;
|
||||
}
|
||||
|
||||
void overworldSceneInit(void) {
|
||||
consolePrint("Overworld scene initialized");
|
||||
|
||||
overworldGroundAdd(&OVERWORLD.ground);
|
||||
overworldSceneConfigureShaderMaterial(
|
||||
OVERWORLD.ground.entityId, COLOR_MAGENTA, &PLANE_MESH_SIMPLE
|
||||
);
|
||||
|
||||
overworldPlayerAdd(&OVERWORLD.player);
|
||||
overworldSceneConfigureSprite(OVERWORLD.player.entityId, COLOR_GREEN, NULL);
|
||||
|
||||
vec3 npcPos = { 3.0f, 0.5f, 3.0f };
|
||||
overworldNpcAdd(&OVERWORLD.npc, npcPos);
|
||||
overworldSceneConfigureSprite(OVERWORLD.npc.entityId, COLOR_BLUE, NULL);
|
||||
|
||||
OVERWORLD.cameraEntityId = entityManagerAdd();
|
||||
(void)entityAddComponent(OVERWORLD.cameraEntityId, COMPONENT_TYPE_POSITION);
|
||||
@@ -48,7 +112,7 @@ void overworldSceneInit(void) {
|
||||
entityoverworldcamera_t *camData = entityOverworldCameraGet(
|
||||
OVERWORLD.cameraEntityId, OVERWORLD.cameraOverworldCompId
|
||||
);
|
||||
glm_vec3_copy((vec3){ 0.5f, 0.5f, 0.5f }, camData->targetOffset);
|
||||
glm_vec3_zero(camData->targetOffset);
|
||||
glm_vec3_copy((vec3){ 0.0f, 0.0f, 5.0f }, camData->eyeOffset);
|
||||
camData->scale = 32.0f;
|
||||
entityInteractableSetCallback(
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
#pragma once
|
||||
#include "error/error.h"
|
||||
#include "entity/entitybase.h"
|
||||
#include "display/color.h"
|
||||
#include "display/mesh/mesh.h"
|
||||
#include "display/texture/texture.h"
|
||||
#include "scene/overworld/overworldplayer.h"
|
||||
#include "scene/overworld/overworldground.h"
|
||||
#include "scene/overworld/overworldnpc.h"
|
||||
@@ -20,6 +23,48 @@ typedef struct {
|
||||
overworldnpc_t npc;
|
||||
} overworldscene_t;
|
||||
|
||||
/**
|
||||
* Configures a SHADER_MATERIAL renderable on an entity with the unlit shader,
|
||||
* depth testing enabled, no blending, and a single mesh.
|
||||
*
|
||||
* @param entityId The entity to configure.
|
||||
* @param color Unlit diffuse color.
|
||||
* @param mesh Mesh to render.
|
||||
*/
|
||||
void overworldSceneConfigureShaderMaterial(
|
||||
const entityid_t entityId,
|
||||
const color_t color,
|
||||
mesh_t *mesh
|
||||
);
|
||||
|
||||
/**
|
||||
* Configures a SPRITEBATCH renderable on an entity as a single 1x1 billboard
|
||||
* sprite centered at the entity's origin.
|
||||
*
|
||||
* @param entityId The entity to configure.
|
||||
* @param color Per-vertex sprite tint color.
|
||||
* @param texture Texture to use, or NULL for untextured.
|
||||
*/
|
||||
void overworldSceneConfigureSprite(
|
||||
const entityid_t entityId,
|
||||
const color_t color,
|
||||
texture_t *texture
|
||||
);
|
||||
|
||||
/**
|
||||
* Initializes the overworld scene, spawning all entities and configuring
|
||||
* the camera, player, ground, and NPC.
|
||||
*/
|
||||
void overworldSceneInit(void);
|
||||
|
||||
/**
|
||||
* Updates the overworld scene each frame.
|
||||
*
|
||||
* @return Error state.
|
||||
*/
|
||||
errorret_t overworldSceneUpdate(void);
|
||||
|
||||
/**
|
||||
* Disposes the overworld scene, invalidating all entity and component IDs.
|
||||
*/
|
||||
void overworldSceneDispose(void);
|
||||
|
||||
+6
-57
@@ -10,13 +10,12 @@
|
||||
#include "time/time.h"
|
||||
#include "display/screen/screen.h"
|
||||
#include "entity/entitymanager.h"
|
||||
#include "entity/component/display/entityrenderable.h"
|
||||
#include "display/shader/shaderunlit.h"
|
||||
#include "display/spritebatch/spritebatch.h"
|
||||
#include "display/screen/screen.h"
|
||||
#include "display/display.h"
|
||||
#include "console/console.h"
|
||||
#include "util/string.h"
|
||||
#include "ui/ui.h"
|
||||
#include "scene/scenerenderpipeline.h"
|
||||
|
||||
scenefuncs_t SCENE_FUNCTIONS[SCENE_TYPE_COUNT] = {
|
||||
{ 0 },
|
||||
@@ -69,63 +68,13 @@ errorret_t sceneUpdate(void) {
|
||||
}
|
||||
|
||||
errorret_t sceneRender(void) {
|
||||
mat4 proj, view, model, ident;
|
||||
mat4 proj, view, ident;
|
||||
glm_mat4_identity(ident);
|
||||
|
||||
errorChain(shaderBind(&SHADER_UNLIT));
|
||||
|
||||
// Render scene.
|
||||
entityid_t camera = entityCameraGetCurrent();
|
||||
entityid_t entities[ENTITY_COUNT_MAX];
|
||||
componentid_t components[ENTITY_COUNT_MAX];
|
||||
entityid_t entCount = componentGetEntitiesWithComponent(
|
||||
COMPONENT_TYPE_RENDERABLE,
|
||||
entities, components
|
||||
);
|
||||
|
||||
// Anything to render?
|
||||
if(camera != ENTITY_ID_INVALID && entCount > 0) {
|
||||
componentid_t camPos = entityGetComponent(camera,COMPONENT_TYPE_POSITION);
|
||||
componentid_t camCam = entityGetComponent(camera,COMPONENT_TYPE_CAMERA);
|
||||
|
||||
// View
|
||||
if(camPos == COMPONENT_ID_INVALID) {
|
||||
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_VIEW, ident));
|
||||
} else {
|
||||
entityPositionGetTransform(camera, camPos, view);
|
||||
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_VIEW, view));
|
||||
}
|
||||
|
||||
// Projection
|
||||
assertTrue(
|
||||
camCam != COMPONENT_ID_INVALID,
|
||||
"Current camera does not have a camera component?"
|
||||
);
|
||||
entityCameraGetProjection(camera, camCam, proj);
|
||||
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_PROJECTION, proj));
|
||||
|
||||
// For each ent.
|
||||
for(entityid_t i = 0; i < entCount; i++) {
|
||||
entityid_t entityId = entities[i];
|
||||
componentid_t renderableComp = components[i];
|
||||
|
||||
// Has position?
|
||||
componentid_t posComp = entityGetComponent(
|
||||
entityId, COMPONENT_TYPE_POSITION
|
||||
);
|
||||
if(posComp == COMPONENT_ID_INVALID) {
|
||||
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_MODEL, ident));
|
||||
} else {
|
||||
entityPositionGetTransform(entityId, posComp, model);
|
||||
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_MODEL, model));
|
||||
}
|
||||
|
||||
// Render.
|
||||
errorChain(entityRenderableDraw(entityId, renderableComp));
|
||||
}
|
||||
}
|
||||
errorChain(sceneRenderPipeline(entityCameraGetCurrent()));
|
||||
|
||||
// UI Rendering
|
||||
errorChain(shaderBind(&SHADER_UNLIT));
|
||||
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_MODEL, ident));
|
||||
|
||||
glm_ortho(
|
||||
@@ -143,7 +92,7 @@ errorret_t sceneRender(void) {
|
||||
view
|
||||
);
|
||||
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_VIEW, view));
|
||||
|
||||
|
||||
errorChain(displaySetState((displaystate_t){
|
||||
.flags = DISPLAY_STATE_FLAG_BLEND
|
||||
}));
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "scenerenderpipeline.h"
|
||||
#include "entity/entitymanager.h"
|
||||
#include "entity/component/display/entityposition.h"
|
||||
#include "entity/component/display/entitycamera.h"
|
||||
#include "display/shader/shaderlist.h"
|
||||
#include "display/shader/shaderunlit.h"
|
||||
#include "display/displaystate.h"
|
||||
#include "assert/assert.h"
|
||||
|
||||
int8_t sceneRenderPipelineGetPriority(const entityrenderable_t *r) {
|
||||
assertNotNull(r, "Renderable cannot be null");
|
||||
if(r->priority != 0) return r->priority;
|
||||
switch(r->type) {
|
||||
case ENTITY_RENDERABLE_TYPE_SHADER_MATERIAL:
|
||||
return (r->data.material.state.flags & DISPLAY_STATE_FLAG_BLEND)
|
||||
? (int8_t)10 : (int8_t)-10;
|
||||
case ENTITY_RENDERABLE_TYPE_SPRITEBATCH:
|
||||
return (int8_t)100;
|
||||
case ENTITY_RENDERABLE_TYPE_CUSTOM:
|
||||
default:
|
||||
return (int8_t)0;
|
||||
}
|
||||
}
|
||||
|
||||
int_t sceneRenderPipelineCompare(const void *a, const void *b) {
|
||||
assertNotNull(a, "Entry a cannot be null");
|
||||
assertNotNull(b, "Entry b cannot be null");
|
||||
const scenerenderpipelineentry_t *ea = (const scenerenderpipelineentry_t *)a;
|
||||
const scenerenderpipelineentry_t *eb = (const scenerenderpipelineentry_t *)b;
|
||||
return (int_t)ea->effectivePriority - (int_t)eb->effectivePriority;
|
||||
}
|
||||
|
||||
shader_t *sceneRenderPipelineGetShader(const entityrenderable_t *r) {
|
||||
assertNotNull(r, "Renderable cannot be null");
|
||||
switch(r->type) {
|
||||
case ENTITY_RENDERABLE_TYPE_SPRITEBATCH:
|
||||
return SHADER_LIST_DEFS[SHADER_LIST_SHADER_UNLIT].shader;
|
||||
case ENTITY_RENDERABLE_TYPE_SHADER_MATERIAL:
|
||||
return SHADER_LIST_DEFS[r->data.material.shaderType].shader;
|
||||
case ENTITY_RENDERABLE_TYPE_CUSTOM:
|
||||
default:
|
||||
return SHADER_LIST_DEFS[SHADER_LIST_SHADER_UNLIT].shader;
|
||||
}
|
||||
}
|
||||
|
||||
errorret_t sceneRenderPipeline(const entityid_t cameraId) {
|
||||
mat4 proj, view, model, ident;
|
||||
glm_mat4_identity(ident);
|
||||
|
||||
entityid_t entities[ENTITY_COUNT_MAX];
|
||||
componentid_t components[ENTITY_COUNT_MAX];
|
||||
entityid_t entCount = componentGetEntitiesWithComponent(
|
||||
COMPONENT_TYPE_RENDERABLE, entities, components
|
||||
);
|
||||
|
||||
if(cameraId == ENTITY_ID_INVALID || entCount == 0) errorOk();
|
||||
|
||||
scenerenderpipelineentry_t pipeline[ENTITY_COUNT_MAX];
|
||||
for(entityid_t i = 0; i < entCount; i++) {
|
||||
entityrenderable_t *r = componentGetData(
|
||||
entities[i], components[i], COMPONENT_TYPE_RENDERABLE
|
||||
);
|
||||
pipeline[i].entityId = entities[i];
|
||||
pipeline[i].componentId = components[i];
|
||||
pipeline[i].effectivePriority = sceneRenderPipelineGetPriority(r);
|
||||
}
|
||||
sort(pipeline, (size_t)entCount, sizeof(scenerenderpipelineentry_t), sceneRenderPipelineCompare);
|
||||
|
||||
componentid_t camPos = entityGetComponent(cameraId, COMPONENT_TYPE_POSITION);
|
||||
componentid_t camCam = entityGetComponent(cameraId, COMPONENT_TYPE_CAMERA);
|
||||
assertTrue(
|
||||
camCam != COMPONENT_ID_INVALID,
|
||||
"Current camera does not have a camera component?"
|
||||
);
|
||||
|
||||
if(camPos == COMPONENT_ID_INVALID) {
|
||||
glm_mat4_copy(ident, view);
|
||||
} else {
|
||||
entityPositionGetTransform(cameraId, camPos, view);
|
||||
}
|
||||
entityCameraGetProjection(cameraId, camCam, proj);
|
||||
|
||||
for(shaderlistshader_t si = SHADER_LIST_SHADER_NULL + 1; si < SHADER_LIST_SHADER_COUNT; si++) {
|
||||
shader_t *s = SHADER_LIST_DEFS[si].shader;
|
||||
assertNotNull(s, "Shader in list cannot be null");
|
||||
errorChain(shaderBind(s));
|
||||
errorChain(shaderSetMatrix(s, SHADER_UNLIT_VIEW, view));
|
||||
errorChain(shaderSetMatrix(s, SHADER_UNLIT_PROJECTION, proj));
|
||||
}
|
||||
|
||||
for(entityid_t i = 0; i < entCount; i++) {
|
||||
entityid_t eid = pipeline[i].entityId;
|
||||
componentid_t cid = pipeline[i].componentId;
|
||||
|
||||
entityrenderable_t *r = componentGetData(eid, cid, COMPONENT_TYPE_RENDERABLE);
|
||||
shader_t *s = sceneRenderPipelineGetShader(r);
|
||||
|
||||
componentid_t posComp = entityGetComponent(eid, COMPONENT_TYPE_POSITION);
|
||||
if(posComp == COMPONENT_ID_INVALID) {
|
||||
errorChain(shaderBind(s));
|
||||
errorChain(shaderSetMatrix(s, SHADER_UNLIT_MODEL, ident));
|
||||
} else {
|
||||
entityPositionGetTransform(eid, posComp, model);
|
||||
errorChain(shaderBind(s));
|
||||
errorChain(shaderSetMatrix(s, SHADER_UNLIT_MODEL, model));
|
||||
}
|
||||
|
||||
errorChain(entityRenderableDraw(eid, cid));
|
||||
}
|
||||
|
||||
errorChain(displaySetState((displaystate_t){ .flags = 0 }));
|
||||
errorOk();
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "entity/entitybase.h"
|
||||
#include "entity/component/display/entityrenderable.h"
|
||||
#include "display/shader/shader.h"
|
||||
#include "util/sort.h"
|
||||
|
||||
typedef struct {
|
||||
entityid_t entityId;
|
||||
componentid_t componentId;
|
||||
int8_t effectivePriority;
|
||||
} scenerenderpipelineentry_t;
|
||||
|
||||
/**
|
||||
* Returns the effective render priority for a renderable. When the renderable's
|
||||
* explicit priority is non-zero that value is returned directly; otherwise an
|
||||
* automatic value is derived from the renderable type and display state flags.
|
||||
*
|
||||
* @param r The renderable component data.
|
||||
* @return Effective priority: lower renders first, higher renders last.
|
||||
*/
|
||||
int8_t sceneRenderPipelineGetPriority(const entityrenderable_t *r);
|
||||
|
||||
/**
|
||||
* sortcompare_t comparator for scenerenderpipelineentry_t. Compares by
|
||||
* effectivePriority ascending so lower-priority entries sort first.
|
||||
*
|
||||
* @param a Pointer to the first scenerenderpipelineentry_t.
|
||||
* @param b Pointer to the second scenerenderpipelineentry_t.
|
||||
* @return Negative, zero, or positive.
|
||||
*/
|
||||
int_t sceneRenderPipelineCompare(const void *a, const void *b);
|
||||
|
||||
/**
|
||||
* Returns the shader that will be used to render the given renderable.
|
||||
* SPRITEBATCH and CUSTOM default to SHADER_UNLIT; SHADER_MATERIAL uses the
|
||||
* shader indexed by the material's shaderType field in SHADER_LIST_DEFS.
|
||||
*
|
||||
* @param r The renderable component data.
|
||||
* @return Pointer to the shader, never NULL.
|
||||
*/
|
||||
shader_t *sceneRenderPipelineGetShader(const entityrenderable_t *r);
|
||||
|
||||
/**
|
||||
* Renders all entities with renderable components in priority order.
|
||||
* Lower effective priority renders first (behind); higher renders last (on top).
|
||||
* When priority is 0 the effective value is derived from the renderable's type
|
||||
* and flags; otherwise the explicit priority is used directly.
|
||||
*
|
||||
* @param cameraId The entity ID of the active camera, or ENTITY_ID_INVALID.
|
||||
* @return Error state.
|
||||
*/
|
||||
errorret_t sceneRenderPipeline(const entityid_t cameraId);
|
||||
+98
-69
@@ -26,97 +26,126 @@ errorret_t uiFrameDraw(
|
||||
assertNotNull(frame, "frame must not be NULL");
|
||||
assertNotNull(frame->texture, "frame texture must not be NULL");
|
||||
|
||||
errorChain(shaderSetTexture(
|
||||
&SHADER_UNLIT, SHADER_UNLIT_TEXTURE, frame->texture
|
||||
));
|
||||
|
||||
shadermaterial_t material = {
|
||||
.unlit = {
|
||||
.color = COLOR_WHITE,
|
||||
.texture = frame->texture
|
||||
}
|
||||
};
|
||||
spritebatchsprite_t sprites[9];
|
||||
float_t tileW = (float_t)frame->tileset.tileWidth;
|
||||
float_t tileH = (float_t)frame->tileset.tileHeight;
|
||||
|
||||
vec4 uv;
|
||||
|
||||
tilesetPositionGetUV(&frame->tileset, 0, 0, uv);
|
||||
errorChain(spriteBatchPush(
|
||||
x, y, x + tileW, y + tileH,
|
||||
#if MESH_ENABLE_COLOR
|
||||
COLOR_WHITE,
|
||||
#endif
|
||||
uv[0], uv[1], uv[2], uv[3]
|
||||
));
|
||||
sprites[0].min[0] = x;
|
||||
sprites[0].min[1] = y;
|
||||
sprites[0].min[2] = 0.0f;
|
||||
sprites[0].max[0] = x + tileW;
|
||||
sprites[0].max[1] = y + tileH;
|
||||
sprites[0].max[2] = 0.0f;
|
||||
sprites[0].uvMin[0] = uv[0];
|
||||
sprites[0].uvMin[1] = uv[1];
|
||||
sprites[0].uvMax[0] = uv[2];
|
||||
sprites[0].uvMax[1] = uv[3];
|
||||
|
||||
tilesetPositionGetUV(&frame->tileset, 1, 0, uv);
|
||||
errorChain(spriteBatchPush(
|
||||
x + tileW, y, x + width - tileW, y + tileH,
|
||||
#if MESH_ENABLE_COLOR
|
||||
COLOR_WHITE,
|
||||
#endif
|
||||
uv[0], uv[1], uv[2], uv[3]
|
||||
));
|
||||
sprites[1].min[0] = x + tileW;
|
||||
sprites[1].min[1] = y;
|
||||
sprites[1].min[2] = 0.0f;
|
||||
sprites[1].max[0] = x + width - tileW;
|
||||
sprites[1].max[1] = y + tileH;
|
||||
sprites[1].max[2] = 0.0f;
|
||||
sprites[1].uvMin[0] = uv[0];
|
||||
sprites[1].uvMin[1] = uv[1];
|
||||
sprites[1].uvMax[0] = uv[2];
|
||||
sprites[1].uvMax[1] = uv[3];
|
||||
|
||||
tilesetPositionGetUV(&frame->tileset, 2, 0, uv);
|
||||
errorChain(spriteBatchPush(
|
||||
x + width - tileW, y, x + width, y + tileH,
|
||||
#if MESH_ENABLE_COLOR
|
||||
COLOR_WHITE,
|
||||
#endif
|
||||
uv[0], uv[1], uv[2], uv[3]
|
||||
));
|
||||
sprites[2].min[0] = x + width - tileW;
|
||||
sprites[2].min[1] = y;
|
||||
sprites[2].min[2] = 0.0f;
|
||||
sprites[2].max[0] = x + width;
|
||||
sprites[2].max[1] = y + tileH;
|
||||
sprites[2].max[2] = 0.0f;
|
||||
sprites[2].uvMin[0] = uv[0];
|
||||
sprites[2].uvMin[1] = uv[1];
|
||||
sprites[2].uvMax[0] = uv[2];
|
||||
sprites[2].uvMax[1] = uv[3];
|
||||
|
||||
tilesetPositionGetUV(&frame->tileset, 0, 1, uv);
|
||||
errorChain(spriteBatchPush(
|
||||
x, y + tileH, x + tileW, y + height - tileH,
|
||||
#if MESH_ENABLE_COLOR
|
||||
COLOR_WHITE,
|
||||
#endif
|
||||
uv[0], uv[1], uv[2], uv[3]
|
||||
));
|
||||
sprites[3].min[0] = x;
|
||||
sprites[3].min[1] = y + tileH;
|
||||
sprites[3].min[2] = 0.0f;
|
||||
sprites[3].max[0] = x + tileW;
|
||||
sprites[3].max[1] = y + height - tileH;
|
||||
sprites[3].max[2] = 0.0f;
|
||||
sprites[3].uvMin[0] = uv[0];
|
||||
sprites[3].uvMin[1] = uv[1];
|
||||
sprites[3].uvMax[0] = uv[2];
|
||||
sprites[3].uvMax[1] = uv[3];
|
||||
|
||||
tilesetPositionGetUV(&frame->tileset, 1, 1, uv);
|
||||
errorChain(spriteBatchPush(
|
||||
x + tileW, y + tileH, x + width - tileW, y + height - tileH,
|
||||
#if MESH_ENABLE_COLOR
|
||||
COLOR_WHITE,
|
||||
#endif
|
||||
uv[0], uv[1], uv[2], uv[3]
|
||||
));
|
||||
sprites[4].min[0] = x + tileW;
|
||||
sprites[4].min[1] = y + tileH;
|
||||
sprites[4].min[2] = 0.0f;
|
||||
sprites[4].max[0] = x + width - tileW;
|
||||
sprites[4].max[1] = y + height - tileH;
|
||||
sprites[4].max[2] = 0.0f;
|
||||
sprites[4].uvMin[0] = uv[0];
|
||||
sprites[4].uvMin[1] = uv[1];
|
||||
sprites[4].uvMax[0] = uv[2];
|
||||
sprites[4].uvMax[1] = uv[3];
|
||||
|
||||
tilesetPositionGetUV(&frame->tileset, 2, 1, uv);
|
||||
errorChain(spriteBatchPush(
|
||||
x + width - tileW, y + tileH, x + width, y + height - tileH,
|
||||
#if MESH_ENABLE_COLOR
|
||||
COLOR_WHITE,
|
||||
#endif
|
||||
uv[0], uv[1], uv[2], uv[3]
|
||||
));
|
||||
sprites[5].min[0] = x + width - tileW;
|
||||
sprites[5].min[1] = y + tileH;
|
||||
sprites[5].min[2] = 0.0f;
|
||||
sprites[5].max[0] = x + width;
|
||||
sprites[5].max[1] = y + height - tileH;
|
||||
sprites[5].max[2] = 0.0f;
|
||||
sprites[5].uvMin[0] = uv[0];
|
||||
sprites[5].uvMin[1] = uv[1];
|
||||
sprites[5].uvMax[0] = uv[2];
|
||||
sprites[5].uvMax[1] = uv[3];
|
||||
|
||||
tilesetPositionGetUV(&frame->tileset, 0, 2, uv);
|
||||
errorChain(spriteBatchPush(
|
||||
x, y + height - tileH, x + tileW, y + height,
|
||||
#if MESH_ENABLE_COLOR
|
||||
COLOR_WHITE,
|
||||
#endif
|
||||
uv[0], uv[1], uv[2], uv[3]
|
||||
));
|
||||
sprites[6].min[0] = x;
|
||||
sprites[6].min[1] = y + height - tileH;
|
||||
sprites[6].min[2] = 0.0f;
|
||||
sprites[6].max[0] = x + tileW;
|
||||
sprites[6].max[1] = y + height;
|
||||
sprites[6].max[2] = 0.0f;
|
||||
sprites[6].uvMin[0] = uv[0];
|
||||
sprites[6].uvMin[1] = uv[1];
|
||||
sprites[6].uvMax[0] = uv[2];
|
||||
sprites[6].uvMax[1] = uv[3];
|
||||
|
||||
tilesetPositionGetUV(&frame->tileset, 1, 2, uv);
|
||||
errorChain(spriteBatchPush(
|
||||
x + tileW, y + height - tileH, x + width - tileW, y + height,
|
||||
#if MESH_ENABLE_COLOR
|
||||
COLOR_WHITE,
|
||||
#endif
|
||||
uv[0], uv[1], uv[2], uv[3]
|
||||
));
|
||||
sprites[7].min[0] = x + tileW;
|
||||
sprites[7].min[1] = y + height - tileH;
|
||||
sprites[7].min[2] = 0.0f;
|
||||
sprites[7].max[0] = x + width - tileW;
|
||||
sprites[7].max[1] = y + height;
|
||||
sprites[7].max[2] = 0.0f;
|
||||
sprites[7].uvMin[0] = uv[0];
|
||||
sprites[7].uvMin[1] = uv[1];
|
||||
sprites[7].uvMax[0] = uv[2];
|
||||
sprites[7].uvMax[1] = uv[3];
|
||||
|
||||
tilesetPositionGetUV(&frame->tileset, 2, 2, uv);
|
||||
errorChain(spriteBatchPush(
|
||||
x + width - tileW, y + height - tileH, x + width, y + height,
|
||||
#if MESH_ENABLE_COLOR
|
||||
COLOR_WHITE,
|
||||
#endif
|
||||
uv[0], uv[1], uv[2], uv[3]
|
||||
));
|
||||
sprites[8].min[0] = x + width - tileW;
|
||||
sprites[8].min[1] = y + height - tileH;
|
||||
sprites[8].min[2] = 0.0f;
|
||||
sprites[8].max[0] = x + width;
|
||||
sprites[8].max[1] = y + height;
|
||||
sprites[8].max[2] = 0.0f;
|
||||
sprites[8].uvMin[0] = uv[0];
|
||||
sprites[8].uvMin[1] = uv[1];
|
||||
sprites[8].uvMax[0] = uv[2];
|
||||
sprites[8].uvMax[1] = uv[3];
|
||||
|
||||
errorOk();
|
||||
return spriteBatchBuffer(sprites, 9, &SHADER_UNLIT, material);
|
||||
}
|
||||
|
||||
void uiFrameDispose(uiframe_t *frame) {
|
||||
|
||||
+8
-10
@@ -58,16 +58,14 @@ errorret_t uiFullboxDraw(uifullbox_t *fullbox) {
|
||||
errorChain(shaderSetColor(&SHADER_UNLIT, SHADER_UNLIT_COLOR, color));
|
||||
#endif
|
||||
|
||||
errorChain(spriteBatchPush(
|
||||
0.0f, 0.0f,
|
||||
(float_t)SCREEN.width, (float_t)SCREEN.height,
|
||||
#if MESH_ENABLE_COLOR
|
||||
color,
|
||||
#endif
|
||||
0.0f, 0.0f, 1.0f, 1.0f
|
||||
));
|
||||
errorChain(spriteBatchFlush());
|
||||
errorOk();
|
||||
spritebatchsprite_t sprite;
|
||||
shadermaterial_t material = {
|
||||
.unlit = {
|
||||
.color = color,
|
||||
.texture = &TEXTURE_WHITE
|
||||
}
|
||||
};
|
||||
return spriteBatchBuffer(&sprite, 1, &SHADER_UNLIT, material);
|
||||
}
|
||||
|
||||
void uiFullboxTransition(
|
||||
|
||||
+12
-18
@@ -12,6 +12,7 @@
|
||||
#include "event/event.h"
|
||||
#include "display/screen/screen.h"
|
||||
#include "display/texture/texture.h"
|
||||
#include "display/text/text.h"
|
||||
#include "display/spritebatch/spritebatch.h"
|
||||
#include "display/shader/shaderunlit.h"
|
||||
|
||||
@@ -216,15 +217,12 @@ errorret_t uiTextboxDraw(void) {
|
||||
));
|
||||
errorChain(spriteBatchFlush());
|
||||
|
||||
errorChain(shaderSetTexture(
|
||||
&SHADER_UNLIT, SHADER_UNLIT_TEXTURE, UI_TEXTBOX.font->texture
|
||||
));
|
||||
#if MESH_ENABLE_COLOR
|
||||
#else
|
||||
errorChain(shaderSetColor(
|
||||
&SHADER_UNLIT, SHADER_UNLIT_COLOR, UI_TEXTBOX.textColor
|
||||
));
|
||||
#endif
|
||||
shadermaterial_t textMaterial = {
|
||||
.unlit = {
|
||||
.color = UI_TEXTBOX.textColor,
|
||||
.texture = UI_TEXTBOX.font->texture
|
||||
}
|
||||
};
|
||||
|
||||
float_t frameTileW = (float_t)UI_TEXTBOX.frame.tileset.tileWidth;
|
||||
float_t frameTileH = (float_t)UI_TEXTBOX.frame.tileset.tileHeight;
|
||||
@@ -242,21 +240,17 @@ errorret_t uiTextboxDraw(void) {
|
||||
for(int32_t li = pageFirst; li < pageLast && charsLeft > 0; li++) {
|
||||
uitextboxline_t *line = &UI_TEXTBOX.lines[li];
|
||||
int32_t visible = line->count < charsLeft ? line->count : charsLeft;
|
||||
float_t lineY = contentY;
|
||||
lineY += (float_t)(li - pageFirst) * (fontH + UI_TEXTBOX_LINE_SPACING);
|
||||
float_t lineY = contentY + (float_t)(li - pageFirst) * (fontH + UI_TEXTBOX_LINE_SPACING);
|
||||
|
||||
for(int32_t ci = 0; ci < visible; ci++) {
|
||||
char_t c = UI_TEXTBOX.text[line->start + ci];
|
||||
if(c == ' ') continue;
|
||||
errorChain(textDrawChar(
|
||||
contentX + (float_t)ci * fontW,
|
||||
lineY,
|
||||
spritebatchsprite_t sprite = textGetSprite(
|
||||
(vec2){ contentX + (float_t)ci * fontW, lineY },
|
||||
c,
|
||||
#if MESH_ENABLE_COLOR
|
||||
UI_TEXTBOX.textColor,
|
||||
#endif
|
||||
UI_TEXTBOX.font
|
||||
));
|
||||
);
|
||||
errorChain(spriteBatchBuffer(&sprite, 1, &SHADER_UNLIT, textMaterial));
|
||||
}
|
||||
|
||||
charsLeft -= visible;
|
||||
|
||||
Reference in New Issue
Block a user