Compare commits

...

10 Commits

Author SHA1 Message Date
mid
4d74b5e3e9 Particle bugfixes and openmp-ization 2025-05-10 18:22:13 +03:00
mid
ab903ed4c0 k3Menu CSS-style properties 2025-05-10 18:21:51 +03:00
mid
71d75f2aa7 Constant changes 2025-05-10 18:21:10 +03:00
mid
0b0496d766 k3Batch border radius 2025-05-10 18:20:53 +03:00
mid
26e44eb4bc Animation looping 2025-05-10 18:20:00 +03:00
mid
f84505f36b Add multisampled FBO support 2025-05-10 18:19:38 +03:00
mid
f20d32eb3a Cascaded shadow mapping support 2025-05-10 18:17:33 +03:00
mid
68264fcf87 Add bc7enc source 2025-05-10 18:16:07 +03:00
mid
43fcf81381 Fix mipmapping and add texture compression 2025-05-10 18:15:39 +03:00
mid
deb6387908 Bug fixes and split to k3_internal.h 2025-03-23 15:09:26 +02:00
13 changed files with 3222 additions and 389 deletions

2215
src/compr/bc7enc.c Normal file

File diff suppressed because it is too large Load Diff

82
src/compr/bc7enc.h Normal file
View File

@ -0,0 +1,82 @@
// File: bc7enc.h - Richard Geldreich, Jr. - MIT license or public domain (see end of bc7enc.c)
#include <stdlib.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#define BC7ENC_BLOCK_SIZE (16)
#define BC7ENC_MAX_PARTITIONS1 (64)
#define BC7ENC_MAX_UBER_LEVEL (4)
typedef uint8_t bc7enc_bool;
#define BC7ENC_TRUE (1)
#define BC7ENC_FALSE (0)
typedef struct
{
// m_max_partitions_mode may range from 0 (disables mode 1) to BC7ENC_MAX_PARTITIONS1. The higher this value, the slower the compressor, but the higher the quality.
uint32_t m_max_partitions_mode;
// Relative RGBA or YCbCrA weights.
uint32_t m_weights[4];
// m_uber_level may range from 0 to BC7ENC_MAX_UBER_LEVEL. The higher this value, the slower the compressor, but the higher the quality.
uint32_t m_uber_level;
// If m_perceptual is true, colorspace error is computed in YCbCr space, otherwise RGB.
bc7enc_bool m_perceptual;
// Set m_try_least_squares to false for slightly faster/lower quality compression.
bc7enc_bool m_try_least_squares;
// When m_mode_partition_estimation_filterbank, the mode1 partition estimator skips lesser used partition patterns unless they are strongly predicted to be potentially useful.
// There's a slight loss in quality with this enabled (around .08 dB RGB PSNR or .05 dB Y PSNR), but up to a 11% gain in speed depending on the other settings.
bc7enc_bool m_mode_partition_estimation_filterbank;
bc7enc_bool m_use_mode5_for_alpha;
bc7enc_bool m_use_mode7_for_alpha;
} bc7enc_compress_block_params;
inline static void bc7enc_compress_block_params_init_linear_weights(bc7enc_compress_block_params *p)
{
p->m_perceptual = BC7ENC_FALSE;
p->m_weights[0] = 1;
p->m_weights[1] = 1;
p->m_weights[2] = 1;
p->m_weights[3] = 1;
}
inline static void bc7enc_compress_block_params_init_perceptual_weights(bc7enc_compress_block_params *p)
{
p->m_perceptual = BC7ENC_TRUE;
p->m_weights[0] = 128;
p->m_weights[1] = 64;
p->m_weights[2] = 16;
p->m_weights[3] = 32;
}
inline static void bc7enc_compress_block_params_init(bc7enc_compress_block_params *p)
{
p->m_max_partitions_mode = BC7ENC_MAX_PARTITIONS1;
p->m_try_least_squares = BC7ENC_TRUE;
p->m_mode_partition_estimation_filterbank = BC7ENC_TRUE;
p->m_uber_level = 0;
p->m_use_mode5_for_alpha = BC7ENC_TRUE;
p->m_use_mode7_for_alpha = BC7ENC_TRUE;
bc7enc_compress_block_params_init_perceptual_weights(p);
}
// bc7enc_compress_block_init() MUST be called before calling bc7enc_compress_block() (or you'll get artifacts).
void bc7enc_compress_block_init();
// Packs a single block of 16x16 RGBA pixels (R first in memory) to 128-bit BC7 block pBlock, using either mode 1 and/or 6.
// Alpha blocks will always use mode 6, and by default opaque blocks will use either modes 1 or 6.
// Returns BC7ENC_TRUE if the block had any pixels with alpha < 255, otherwise it return BC7ENC_FALSE. (This is not an error code - a block is always encoded.)
bc7enc_bool bc7enc_compress_block(void *pBlock, const void *pPixelsRGBA, const bc7enc_compress_block_params *pComp_params);
#ifdef __cplusplus
}
#endif

510
src/k3.c
View File

@ -1,4 +1,4 @@
#include"k3.h" #include"k3_internal.h"
#include"gl.h" #include"gl.h"
@ -15,41 +15,23 @@
#include"ssort.h" #include"ssort.h"
#define GL_FROM_K3TEX(k3t) ((k3t) ? (k3t)->tex : 0)
#define GL_FROM_K3MARCHER(k3m) ((GLuint) (uintptr_t) (k3m))
#define GL_FROM_K3ARBVP(k3m) ((GLuint) (uintptr_t) (k3m))
#define GL_FROM_K3ARBFP(k3m) ((GLuint) (uintptr_t) (k3m))
#define GL_FROM_K3GLSL(k3m) ((GLuint) (uintptr_t) (k3m))
#define RAST_DIFF 0 #define RAST_DIFF 0
#define RAST_NORM 1 #define RAST_NORM 1
#define RAST_DISP 2 #define RAST_DISP 2
#define RAST_LEVELS 3 #define RAST_LEVELS 3
static int k3CPUSkinning = 0; bool k3IsSoftSkinning = 0;
bool k3IsCore = 0;
int k3IsCore = 0;
static struct k3GLSLP *basicBlitProgram; static struct k3GLSLP *basicBlitProgram;
static struct k3GLSLP *basicCubemapProgram; static struct k3GLSLP *basicCubemapProgram;
struct k3Tex { typedef void*(*TextureOfflineCompressorFunc)(const void *pixels, uint16_t width, uint16_t height, GLenum externalFmt, GLenum intype, size_t *compressedSize);
GLuint tex;
int cubemap;
uint32_t szX;
uint32_t szY;
uint32_t szZ;
};
struct k3Storage { static bool TextureCompressionEnabled;
int16_t ref; static GLenum TextureCompressionRGBA;
void(*free)(struct k3Storage*); static GLenum TextureCompressionSRGBA;
}; static TextureOfflineCompressorFunc TextureOfflineCompressor;
struct k3StorageBasic {
struct k3Storage;
GLuint gl;
};
void k3StorageRef(struct k3Storage *s) { void k3StorageRef(struct k3Storage *s) {
s->ref++; s->ref++;
@ -73,40 +55,6 @@ struct k3StorageBasic *k3StorageBasic() {
return ret; return ret;
} }
struct k3Offscreen {
GLuint fbo;
struct k3Tex *diffuse;
struct k3Tex *depth;
};
struct k3Mdl {
struct {
vec3 *pos;
uint8_t *boneids;
uint16_t *boneweights;
} cpuSkinning;
size_t verts;
struct k3StorageBasic *vstore;
struct k3StorageBasic *estore;
uint16_t meshCount;
struct k3Mesh *meshes;
size_t boneCount;
mat4 *invBind;
uint8_t *boneParents;
uint16_t animCount;
struct k3Animation **anims;
vec3 aabb[2];
int offV, offN, offC, offU, offB, offT;
const char *debugname;
};
static uint16_t MainWidth, MainHeight; static uint16_t MainWidth, MainHeight;
void k3Resize(uint16_t width, uint16_t height) { void k3Resize(uint16_t width, uint16_t height) {
MainWidth = width; MainWidth = width;
@ -119,6 +67,12 @@ void k3SetTime(float t) {
} }
static void update_aabb(struct k3Mdl *mdl, vec3 *pos) { static void update_aabb(struct k3Mdl *mdl, vec3 *pos) {
if(pos == k3_ATTRIB_NONE || pos == k3_ATTRIB_EMPTY || mdl->verts == 0) {
glm_vec3_zero(mdl->aabb[0]);
glm_vec3_zero(mdl->aabb[1]);
return;
}
glm_vec3_copy(pos[0], mdl->aabb[0]); glm_vec3_copy(pos[0], mdl->aabb[0]);
glm_vec3_copy(pos[0], mdl->aabb[1]); glm_vec3_copy(pos[0], mdl->aabb[1]);
for(size_t i = 0; i < mdl->verts; i++) { for(size_t i = 0; i < mdl->verts; i++) {
@ -212,7 +166,7 @@ struct k3Mdl *k3MdlCreate(size_t verts, size_t indices, size_t boneCount, vec3 *
ret->animCount = 0; ret->animCount = 0;
ret->anims = NULL; ret->anims = NULL;
if(k3CPUSkinning && pos && boneids && boneweights) { if(k3IsSoftSkinning && pos && boneids && boneweights) {
ret->cpuSkinning.pos = malloc(sizeof(*ret->cpuSkinning.pos) * verts); ret->cpuSkinning.pos = malloc(sizeof(*ret->cpuSkinning.pos) * verts);
memcpy(ret->cpuSkinning.pos, pos, sizeof(*ret->cpuSkinning.pos) * verts); memcpy(ret->cpuSkinning.pos, pos, sizeof(*ret->cpuSkinning.pos) * verts);
@ -241,35 +195,44 @@ struct k3Mdl *k3MdlCreate(size_t verts, size_t indices, size_t boneCount, vec3 *
int o = 0; int o = 0;
ret->offV = o; ret->offV = o;
if(pos != k3_ATTRIB_EMPTY) {
glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, o, 12 * verts, pos); glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, o, 12 * verts, pos);
}
o += 12 * verts; o += 12 * verts;
if(nrm) { if(nrm) {
ret->offN = o; ret->offN = o;
if(nrm != k3_ATTRIB_EMPTY) {
glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, o, 3 * verts, nrm); glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, o, 3 * verts, nrm);
}
o += 3 * verts; o += 3 * verts;
} else ret->offN = -1; } else ret->offN = -1;
if(uvs) { if(uvs) {
ret->offU = o; ret->offU = o;
if(uvs != k3_ATTRIB_EMPTY) {
glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, o, 8 * verts, uvs); glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, o, 8 * verts, uvs);
}
o += 8 * verts; o += 8 * verts;
} else ret->offU = -1; } else ret->offU = -1;
if(nrm && uvs) { if(nrm && uvs) {
ret->offT = o; ret->offT = o;
if(nrm != k3_ATTRIB_EMPTY && uvs != k3_ATTRIB_EMPTY) {
int8_t *tans = generate_tangents(verts, indices, inds, pos, nrm, uvs); int8_t *tans = generate_tangents(verts, indices, inds, pos, nrm, uvs);
glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, o, 3 * verts, tans); glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, o, 3 * verts, tans);
free(tans); free(tans);
}
o += 3 * verts; o += 3 * verts;
} else ret->offT = -1; } else ret->offT = -1;
if(cols) { if(cols) {
ret->offC = o; ret->offC = o;
if(cols != k3_ATTRIB_EMPTY) {
glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, o, 4 * verts, cols); glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, o, 4 * verts, cols);
}
o += 4 * verts; o += 4 * verts;
} else ret->offC = -1; } else ret->offC = -1;
@ -284,7 +247,9 @@ struct k3Mdl *k3MdlCreate(size_t verts, size_t indices, size_t boneCount, vec3 *
} else ret->offB = -1; } else ret->offB = -1;
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, ret->estore->gl); glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, ret->estore->gl);
if(inds && inds != k3_ATTRIB_EMPTY) {
glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, indices * sizeof(uint16_t), inds, GL_STATIC_DRAW_ARB); glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, indices * sizeof(uint16_t), inds, GL_STATIC_DRAW_ARB);
}
update_aabb(ret, pos); update_aabb(ret, pos);
@ -439,6 +404,11 @@ static void anim_update(struct k3Animator *anim) {
void k3AnimatorSet(struct k3Animator *anim, float time) { void k3AnimatorSet(struct k3Animator *anim, float time) {
if(!anim->inter) anim->inter = _mm_malloc(sizeof(mat4) * anim->base->boneCount * anim->base->frameCount, 16); if(!anim->inter) anim->inter = _mm_malloc(sizeof(mat4) * anim->base->boneCount * anim->base->frameCount, 16);
if(!anim->loop) {
time = fmaxf(time, 0);
time = fminf(time, (float) (anim->base->frameCount - 1) / anim->base->fps);
}
anim->time = time; anim->time = time;
anim_update(anim); anim_update(anim);
@ -480,6 +450,7 @@ void k3TexUpdate(struct k3Tex *tex, enum k3TexType type, int index, uint16_t wid
GLint internalFmt, externalFmt, intype; GLint internalFmt, externalFmt, intype;
int linearFilter, mipmap; int linearFilter, mipmap;
int compressed = 0;
switch(type) { switch(type) {
case k3_RAWCOLOR: case k3_RAWCOLOR:
@ -490,7 +461,8 @@ void k3TexUpdate(struct k3Tex *tex, enum k3TexType type, int index, uint16_t wid
mipmap = 1; mipmap = 1;
break; break;
case k3_NORMAL: case k3_NORMAL:
internalFmt = GL_RGBA; compressed = TextureCompressionEnabled;
internalFmt = compressed ? TextureCompressionRGBA : GL_RGBA;
externalFmt = GL_RGBA; externalFmt = GL_RGBA;
intype = GL_UNSIGNED_BYTE; intype = GL_UNSIGNED_BYTE;
linearFilter = 1; linearFilter = 1;
@ -498,8 +470,9 @@ void k3TexUpdate(struct k3Tex *tex, enum k3TexType type, int index, uint16_t wid
break; break;
case k3_DIFFUSE: case k3_DIFFUSE:
case k3_EMISSION: case k3_EMISSION:
internalFmt = GLAD_GL_EXT_texture_compression_s3tc compressed = TextureCompressionEnabled;
? (GLAD_GL_EXT_texture_sRGB ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT : GL_COMPRESSED_RGBA_S3TC_DXT5_EXT) internalFmt = compressed
? (GLAD_GL_EXT_texture_sRGB ? TextureCompressionSRGBA : TextureCompressionRGBA)
: (GLAD_GL_EXT_texture_sRGB ? GL_SRGB8_ALPHA8_EXT : GL_RGBA8); : (GLAD_GL_EXT_texture_sRGB ? GL_SRGB8_ALPHA8_EXT : GL_RGBA8);
externalFmt = GL_RGBA; externalFmt = GL_RGBA;
intype = GL_UNSIGNED_BYTE; intype = GL_UNSIGNED_BYTE;
@ -522,7 +495,7 @@ void k3TexUpdate(struct k3Tex *tex, enum k3TexType type, int index, uint16_t wid
mipmap = 1; mipmap = 1;
break; break;
case k3_DEPTH: case k3_DEPTH:
internalFmt = GL_DEPTH_COMPONENT24; internalFmt = GL_DEPTH_COMPONENT32F;
externalFmt = GL_DEPTH_COMPONENT; externalFmt = GL_DEPTH_COMPONENT;
intype = GL_UNSIGNED_INT; intype = GL_UNSIGNED_INT;
if(!k3IsCore) glTexParameteri(target, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE); if(!k3IsCore) glTexParameteri(target, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE);
@ -544,12 +517,9 @@ void k3TexUpdate(struct k3Tex *tex, enum k3TexType type, int index, uint16_t wid
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
} else { } else {
if(!k3IsCore && !mipmap) { if(!k3IsCore && mipmap) {
glTexParameteri(target, GL_GENERATE_MIPMAP, GL_FALSE); glTexParameteri(target, GL_GENERATE_MIPMAP, GL_TRUE);
} else {
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
} }
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
} }
if(type == k3_DEPTH) { if(type == k3_DEPTH) {
@ -557,13 +527,18 @@ void k3TexUpdate(struct k3Tex *tex, enum k3TexType type, int index, uint16_t wid
glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
} }
if(!tex->cubemap && mipmap) {
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, linearFilter ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_LINEAR);
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, linearFilter ? GL_LINEAR : GL_NEAREST);
} else {
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, linearFilter ? GL_LINEAR : GL_NEAREST);
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, linearFilter ? GL_LINEAR : GL_NEAREST);
}
} else { } else {
glBindTexture(target, tex->tex); glBindTexture(target, tex->tex);
} }
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, linearFilter ? GL_LINEAR : GL_NEAREST);
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, linearFilter ? GL_LINEAR : GL_NEAREST);
if(k3IsCore && type == k3_ALPHA) { if(k3IsCore && type == k3_ALPHA) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_ONE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_ONE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_ONE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_ONE);
@ -577,7 +552,16 @@ void k3TexUpdate(struct k3Tex *tex, enum k3TexType type, int index, uint16_t wid
k3Log(k3_WARN, "Non-zero index passed for non-cubemap texture"); k3Log(k3_WARN, "Non-zero index passed for non-cubemap texture");
} }
if(compressed && TextureOfflineCompressor) {
size_t len;
data = TextureOfflineCompressor(data, width, height, externalFmt, intype, &len);
glCompressedTexImage2D(target, 0, internalFmt, width, height, 0, len, data);
free(data);
} else {
glTexImage2D(target, 0, internalFmt, width, height, 0, externalFmt, intype, data); glTexImage2D(target, 0, internalFmt, width, height, 0, externalFmt, intype, data);
}
if(!tex->cubemap && k3IsCore && mipmap) { if(!tex->cubemap && k3IsCore && mipmap) {
glGenerateMipmap(target); glGenerateMipmap(target);
@ -586,6 +570,8 @@ void k3TexUpdate(struct k3Tex *tex, enum k3TexType type, int index, uint16_t wid
tex->szX = width; tex->szX = width;
tex->szY = height; tex->szY = height;
tex->szZ = 0; tex->szZ = 0;
tex->glInternalFormat = internalFmt;
} }
uint32_t k3TexSzX(struct k3Tex *this) { uint32_t k3TexSzX(struct k3Tex *this) {
return this->szX; return this->szX;
@ -603,6 +589,16 @@ static struct k3Light *Lights;
void k3SetLights(size_t count, struct k3Light *lightsNew) { void k3SetLights(size_t count, struct k3Light *lightsNew) {
LightCount = count; LightCount = count;
Lights = lightsNew; Lights = lightsNew;
for(int i = 0; i < count; i++) {
if(Lights[i].type == k3_DIRECTIONAL) {
if(Lights[i].dir.cascadeCount == 0) {
Lights[i].dir.cascadeCount = 1;
} else if(Lights[i].dir.cascadeCount > 3) {
Lights[i].dir.cascadeCount = 3;
}
}
}
} }
struct k3Light *k3GetLights(size_t *a) { struct k3Light *k3GetLights(size_t *a) {
@ -636,15 +632,18 @@ static mat4 CamMat;
static mat4 ProjMat; static mat4 ProjMat;
struct LightShadow { struct LightShadow {
mat4 vp; uint8_t vpCount;
mat4 vp[6];
vec4 atlasSegment; vec4 atlasSegment;
#ifdef k3_IRREGULAR_SHADOWS #ifdef k3_IRREGULAR_SHADOWS
GLuint multimapEls; // buffer GLuint multimapEls; // buffer
GLuint multimapHeads; // image GLuint multimapHeads; // image
#endif #endif
} __attribute__((aligned(16))); } __attribute__((aligned(16)));
static size_t LightShadowsCount, LightShadowsCapacity;
static struct LightShadow *LightShadows; static struct LightShadow *LightShadows;
static size_t LightShadowsCount = 0, LightShadowsCapacity = 0;
static struct k3Offscreen *ShadowAtlas; static struct k3Offscreen *ShadowAtlas;
static bool LightShadowIrregularMode = false; static bool LightShadowIrregularMode = false;
@ -837,17 +836,22 @@ static void setup_glsl_lighting_uniforms(GLuint bound, int lightsStart, int ligh
lightsCount = LightCount - lightsStart; lightsCount = LightCount - lightsStart;
} }
vec4 settings1[4]; if(lightsCount) {
vec4 settings2[4]; // raise(SIGINT);
}
vec4 colors[4]; vec4 settings1[4] = {};
memset(colors, 0, sizeof(colors)); vec4 settings2[4] = {};
vec4 colors[4] = {};
for(int i = 0; i < lightsCount; i++) { for(int i = 0; i < lightsCount; i++) {
struct k3Light *l = &Lights[i + lightsStart]; struct k3Light *l = &Lights[i + lightsStart];
if(l->type == k3_DIRECTIONAL) { if(l->type == k3_DIRECTIONAL) {
glm_vec4_copy(l->dir.direction, settings1[i]); glm_vec4_copy(l->dir.direction, settings1[i]);
settings2[i][0] = l->dir.cascadeCount;
} else if(l->type == k3_SPOT) { } else if(l->type == k3_SPOT) {
glm_vec4_copy(l->spot.position, settings1[i]); glm_vec4_copy(l->spot.position, settings1[i]);
@ -898,20 +902,26 @@ static void setup_glsl_shadow_uniforms(GLuint bound, int atlasUnit, int lightsSt
k3Log(k3_ERR, "Max 4 lights per pass"); k3Log(k3_ERR, "Max 4 lights per pass");
} }
if(!LightShadows) return; if(!ShadowAtlas) return;
if(LightShadowIrregularMode) { if(LightShadowIrregularMode) {
assert(k3IsCore); assert(k3IsCore);
glUniform1i(glGetUniformLocation(bound, "u_pixelsinshadow"), 0); glUniform1i(glGetUniformLocation(bound, "u_pixelsinshadow"), 0);
} else { } else {
mat4 m[4]; size_t vpi = 0;
mat4 m[6];
memset(m, 0, sizeof(m)); memset(m, 0, sizeof(m));
vec4 seg[4]; vec4 seg[4];
for(int i = 0; i < lightsCount; i++) { for(int i = 0; i < lightsCount; i++) {
glm_mat4_copy(LightShadows[i + lightsStart].vp, m[i]); for(int z = 0; z < LightShadows[i + lightsStart].vpCount; z++) {
glm_mat4_copy(LightShadows[i + lightsStart].vp[z], m[vpi]);
vpi++;
}
glm_vec4_copy(LightShadows[i + lightsStart].atlasSegment, seg[i]); glm_vec4_copy(LightShadows[i + lightsStart].atlasSegment, seg[i]);
} }
@ -924,12 +934,12 @@ static void setup_glsl_shadow_uniforms(GLuint bound, int atlasUnit, int lightsSt
} }
if(!k3IsCore) { if(!k3IsCore) {
glUniformMatrix4fvARB(glGetUniformLocationARB(bound, "u_shadows0vp"), 4, GL_FALSE, (float*) m); glUniformMatrix4fvARB(glGetUniformLocationARB(bound, "u_shadows0vp"), vpi, GL_FALSE, (float*) m);
glUniform4fvARB(glGetUniformLocationARB(bound, "u_shadows0seg"), 4, (float*) seg); glUniform4fvARB(glGetUniformLocationARB(bound, "u_shadows0seg"), 4, (float*) seg);
glUniform1iARB(glGetUniformLocationARB(bound, "u_shadows0atlas"), atlasUnit); glUniform1iARB(glGetUniformLocationARB(bound, "u_shadows0atlas"), atlasUnit);
} else { } else {
glUniformMatrix4fv(glGetUniformLocationARB(bound, "u_shadows0vp"), 4, GL_FALSE, (float*) m); glUniformMatrix4fv(glGetUniformLocationARB(bound, "u_shadows0vp"), vpi, GL_FALSE, (float*) m);
glUniform4fv(glGetUniformLocationARB(bound, "u_shadows0seg"), 4, (float*) seg); glUniform4fv(glGetUniformLocationARB(bound, "u_shadows0seg"), 4, (float*) seg);
glUniform1i(glGetUniformLocationARB(bound, "u_shadows0atlas"), atlasUnit); glUniform1i(glGetUniformLocationARB(bound, "u_shadows0atlas"), atlasUnit);
@ -972,7 +982,6 @@ static int bind_shadow_texture(int textureUnit) {
} }
static void enable_glsl_bones(GLuint bound, struct k3Mdl *mdl, struct k3AnimationBone *bones) { static void enable_glsl_bones(GLuint bound, struct k3Mdl *mdl, struct k3AnimationBone *bones) {
if(mdl->boneCount) {
GLint a0; GLint a0;
GLint a1; GLint a1;
@ -989,6 +998,7 @@ static void enable_glsl_bones(GLuint bound, struct k3Mdl *mdl, struct k3Animatio
} }
if(a0 != -1) { if(a0 != -1) {
if(bones) {
if(!k3IsCore) { if(!k3IsCore) {
glEnableVertexAttribArrayARB(a0); glEnableVertexAttribArrayARB(a0);
glEnableVertexAttribArrayARB(a1); glEnableVertexAttribArrayARB(a1);
@ -1000,6 +1010,15 @@ static void enable_glsl_bones(GLuint bound, struct k3Mdl *mdl, struct k3Animatio
glVertexAttribPointer(a0, 4, GL_UNSIGNED_BYTE, GL_FALSE, 0, (void*) (mdl->offB + 0 * mdl->verts)); glVertexAttribPointer(a0, 4, GL_UNSIGNED_BYTE, GL_FALSE, 0, (void*) (mdl->offB + 0 * mdl->verts));
glVertexAttribPointer(a1, 4, GL_UNSIGNED_SHORT, GL_TRUE, 0, (void*) (mdl->offB + 4 * mdl->verts)); glVertexAttribPointer(a1, 4, GL_UNSIGNED_SHORT, GL_TRUE, 0, (void*) (mdl->offB + 4 * mdl->verts));
} }
} else {
if(!k3IsCore) {
glVertexAttrib4fARB(a0, 0, 0, 0, 0);
glVertexAttrib4fARB(a1, 1, 0, 0, 0);
} else {
glVertexAttrib4f(a0, 0, 0, 0, 0);
glVertexAttrib4f(a1, 1, 0, 0, 0);
}
}
} }
if(bones) { if(bones) {
@ -1020,11 +1039,9 @@ static void enable_glsl_bones(GLuint bound, struct k3Mdl *mdl, struct k3Animatio
glUniform4fv(glGetUniformLocationARB(bound, "u_bonedata"), 48, (float*) data); glUniform4fv(glGetUniformLocationARB(bound, "u_bonedata"), 48, (float*) data);
} }
} }
}
} }
static void disable_glsl_bones(struct k3Mdl *mdl, GLuint bound) { static void disable_glsl_bones(struct k3Mdl *mdl, GLuint bound) {
if(mdl->boneCount) {
GLint a0; GLint a0;
GLint a1; GLint a1;
@ -1040,7 +1057,6 @@ static void disable_glsl_bones(struct k3Mdl *mdl, GLuint bound) {
glDisableVertexAttribArray(a1); glDisableVertexAttribArray(a1);
} }
} }
}
} }
static void enable_glsl_tangents(GLuint bound, struct k3Mdl *mdl) { static void enable_glsl_tangents(GLuint bound, struct k3Mdl *mdl) {
@ -1300,6 +1316,7 @@ static void forward_subpass(mat4 view, int transparent, int lightsStart, int lig
GLuint lastVP = 0, lastFP = 0; GLuint lastVP = 0, lastFP = 0;
struct k3Mat *lastMaterial = NULL; struct k3Mat *lastMaterial = NULL;
int lastAdditive = -1; int lastAdditive = -1;
int lastDepthwrite = -1;
for(size_t rble = rbleStart; rble < rbleEnd; rble++) { for(size_t rble = rbleStart; rble < rbleEnd; rble++) {
struct k3Mdl *mdl = renderQueue[rble].mdl; struct k3Mdl *mdl = renderQueue[rble].mdl;
@ -1346,6 +1363,15 @@ static void forward_subpass(mat4 view, int transparent, int lightsStart, int lig
lastAdditive = mat->passes[0].additive; lastAdditive = mat->passes[0].additive;
} }
if(lastDepthwrite != mat->passes[0].depthwrite) {
lastDepthwrite = mat->passes[0].depthwrite;
if(lastDepthwrite) {
glDepthMask(GL_TRUE);
} else {
glDepthMask(GL_FALSE);
}
}
if(glsl) { if(glsl) {
if(lastGLSL != glsl) { if(lastGLSL != glsl) {
if(k3IsCore) if(k3IsCore)
@ -1455,7 +1481,7 @@ static void forward_subpass(mat4 view, int transparent, int lightsStart, int lig
enable_glsl_tangents(glsl, mdl); enable_glsl_tangents(glsl, mdl);
} }
if(k3CPUSkinning) { if(k3IsSoftSkinning) {
apply_cpu_skinning(mdl, bones); apply_cpu_skinning(mdl, bones);
} }
@ -1490,7 +1516,6 @@ void k3PassForward(mat4 projection, mat4 cam) {
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDepthMask(GL_TRUE);
glDepthFunc(GL_LEQUAL); glDepthFunc(GL_LEQUAL);
if(GLAD_GL_EXT_framebuffer_sRGB) { if(GLAD_GL_EXT_framebuffer_sRGB) {
@ -1504,6 +1529,8 @@ void k3PassForward(mat4 projection, mat4 cam) {
if(!k3IsCore) { if(!k3IsCore) {
glEnable(GL_NORMALIZE); glEnable(GL_NORMALIZE);
glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
} }
int l = 0, k = LightCount; int l = 0, k = LightCount;
@ -1639,9 +1666,7 @@ void k3PassDepthOnly(mat4 projection, mat4 cam, int clear, int cull) {
enable_glsl_bones(glsl, mdl, bones); enable_glsl_bones(glsl, mdl, bones);
} }
enable_vertex_buffers(mdl, glsl); if(k3IsSoftSkinning) {
if(k3CPUSkinning) {
apply_cpu_skinning(mdl, bones); apply_cpu_skinning(mdl, bones);
} }
@ -1663,27 +1688,46 @@ void k3PassDepthOnly(mat4 projection, mat4 cam, int clear, int cull) {
glFrontFace(GL_CCW); glFrontFace(GL_CCW);
} }
static size_t compute_light_views(mat4 **_projs, mat4 **_cams, char **_isCube) { static void split_frustum(mat4 proj, int cascades, mat4 croppeds[]) {
mat4 *projs = _mm_malloc(sizeof(*projs) * LightCount * 6, 16); float fovy = glm_persp_fovy(proj);
mat4 *cams = _mm_malloc(sizeof(*cams) * LightCount * 6, 16); float aspect = glm_persp_aspect(proj);
char *isCube = _mm_malloc(sizeof(*isCube) * LightCount * 6, 1);
size_t s = 0; float near, far;
glm_persp_decomp_near(proj, &near);
glm_persp_decomp_far(proj, &far);
float depthChunk = (far - near) / cascades;
for(int c = 0; c < cascades; c++) {
glm_perspective(fovy, aspect, near + c * depthChunk, near + (c + 1) * depthChunk, croppeds[c]);
}
}
struct LightView {
uint8_t cams;
uint8_t projs;
mat4 c[6];
mat4 p[3];
};
static size_t compute_light_views(struct LightView lv[]) {
size_t totalTilesUsed = 0;
for(int i = 0; i < LightCount; i++) { for(int i = 0; i < LightCount; i++) {
isCube[s] = 0;
struct k3Light *l = &Lights[i]; struct k3Light *l = &Lights[i];
/*if(!l->castShadow) {
continue;
}*/
if(l->type == k3_DIRECTIONAL) { if(l->type == k3_DIRECTIONAL) {
glm_ortho(-60, 60, -60, 60, -60, 60, projs[s]); size_t CASCADE_COUNT = l->dir.cascadeCount;
lv[i].projs = CASCADE_COUNT;
lv[i].cams = CASCADE_COUNT;
mat4 croppeds[CASCADE_COUNT];
split_frustum(ProjMat, CASCADE_COUNT, croppeds);
for(int cascade = 0; cascade < CASCADE_COUNT; cascade++) {
mat4 invmainproj; mat4 invmainproj;
glm_mat4_inv_fast(ProjMat, invmainproj); glm_mat4_inv_fast(croppeds[cascade], invmainproj);
mat4 frustummat; mat4 frustummat;
glm_mat4_mul(CamMat, invmainproj, frustummat); glm_mat4_mul(CamMat, invmainproj, frustummat);
@ -1694,45 +1738,59 @@ static size_t compute_light_views(mat4 **_projs, mat4 **_cams, char **_isCube) {
vec4 viewcenter; vec4 viewcenter;
glm_frustum_center(corners, viewcenter); glm_frustum_center(corners, viewcenter);
mat4 view; mat4 lightview;
glm_look_anyup(viewcenter, l->dir.direction, view); glm_look_anyup(viewcenter, l->dir.direction, lightview);
glm_mat4_inv_fast(view, cams[s]); vec4 minaabb = {+HUGE_VALF, +HUGE_VALF, +HUGE_VALF};
vec4 maxaabb = {-HUGE_VALF, -HUGE_VALF, -HUGE_VALF};
s++; for(int c = 0; c < 8; c++) {
vec4 lightspaceCorner;
glm_mat4_mulv(lightview, corners[c], lightspaceCorner);
glm_vec4_minv(minaabb, lightspaceCorner, minaabb);
glm_vec4_maxv(maxaabb, lightspaceCorner, maxaabb);
}
glm_ortho(minaabb[0], maxaabb[0], minaabb[1], maxaabb[1], minaabb[2] - 50, maxaabb[2], lv[i].p[cascade]);
glm_mat4_inv_fast(lightview, lv[i].c[cascade]);
}
} else if(l->type == k3_SPOT || l->type == k3_HALF_OMNI) { } else if(l->type == k3_SPOT || l->type == k3_HALF_OMNI) {
glm_perspective(l->spot.angle, 1, 0.1, l->radius, projs[s]); lv[i].projs = 1;
lv[i].cams = 1;
glm_perspective(l->spot.angle, 1, 0.1, l->radius, lv[i].p[0]);
mat4 view; mat4 view;
glm_look_anyup(l->spot.position, l->spot.direction, view); glm_look_anyup(l->spot.position, l->spot.direction, view);
glm_mat4_inv_fast(view, cams[s]); glm_mat4_inv_fast(view, lv[i].c[0]);
s++;
} else if(l->type == k3_OMNI) { } else if(l->type == k3_OMNI) {
static const vec3 dirs[] = {{0, 0, -1}, {0, 0, 1}, {-1, 0, 0}, {1, 0, 0}, {0, -1, 0}, {0, 1, 0}}; static const vec3 dirs[] = {{0, 0, -1}, {0, 0, 1}, {-1, 0, 0}, {1, 0, 0}, {0, -1, 0}, {0, 1, 0}};
static const vec3 ups[] = {{0, 1, 0}, {0, 1, 0}, {0, 1, 0}, {0, 1, 0}, {0, 0, 1}, {0, 0, -1}}; static const vec3 ups[] = {{0, 1, 0}, {0, 1, 0}, {0, 1, 0}, {0, 1, 0}, {0, 0, 1}, {0, 0, -1}};
lv[i].projs = 1;
lv[i].cams = 6;
mat4 proj;
glm_perspective(glm_rad(120), 1, 0.1, l->radius, proj);
glm_mat4_copy(proj, lv[i].p[0]);
for(int d = 0; d < 6; d++) { for(int d = 0; d < 6; d++) {
glm_perspective(glm_rad(120), 1, 0.1, l->radius, projs[s]);
mat4 view; mat4 view;
glm_look(l->omni.position, dirs[d], ups[d], view); glm_look(l->omni.position, dirs[d], ups[d], view);
glm_mat4_inv_fast(view, cams[s]); glm_mat4_inv_fast(view, lv[i].c[d]);
isCube[s] = d != 0;
s++;
} }
} }
totalTilesUsed += lv[i].cams;
} }
*_projs = projs; return totalTilesUsed;
*_cams = cams;
*_isCube = isCube;
return s;
} }
#ifdef k3_IRREGULAR_SHADOWS #ifdef k3_IRREGULAR_SHADOWS
@ -1854,9 +1912,7 @@ static void pass_irregular(int passnum, mat4 mainproj, mat4 maincam, mat4 lightp
enable_glsl_bones(glsl, mdl, bones); enable_glsl_bones(glsl, mdl, bones);
} }
enable_vertex_buffers(mdl, glsl); if(k3IsSoftSkinning) {
if(k3CPUSkinning) {
apply_cpu_skinning(mdl, bones); apply_cpu_skinning(mdl, bones);
} }
@ -1898,7 +1954,7 @@ void k3PassIrregular(struct k3Offscreen *mainview, mat4 mainproj, mat4 maincam)
mat4 *cams; mat4 *cams;
char *isCube; char *isCube;
size_t count = compute_light_views(&projs, &cams, &isCube); count = compute_light_views(&projs, &cams, &isCube);
LightShadowsCount = count; LightShadowsCount = count;
if(LightShadowsCount > LightShadowsCapacity) { if(LightShadowsCount > LightShadowsCapacity) {
@ -2001,11 +2057,8 @@ void k3PassShadowmap(mat4 projection, mat4 cam, struct k3Offscreen *offscr) {
glm_mat4_copy(projection, ProjMat); glm_mat4_copy(projection, ProjMat);
glm_mat4_copy(cam, CamMat); glm_mat4_copy(cam, CamMat);
mat4 *projs; struct LightView *views = alloca(sizeof(*views) * LightCount);
mat4 *cams; size_t totalTilesUsed = compute_light_views(views);
char *isCube;
size_t count = compute_light_views(&projs, &cams, &isCube);
ShadowAtlas = offscr; ShadowAtlas = offscr;
@ -2020,7 +2073,7 @@ void k3PassShadowmap(mat4 projection, mat4 cam, struct k3Offscreen *offscr) {
LightShadowsCapacity = LightShadowsCount; LightShadowsCapacity = LightShadowsCount;
} }
if(count == 0) { if(totalTilesUsed == 0) {
return; return;
} }
@ -2031,45 +2084,56 @@ void k3PassShadowmap(mat4 projection, mat4 cam, struct k3Offscreen *offscr) {
int cellsPerDimension = 0; int cellsPerDimension = 0;
if(count == 1) { if(totalTilesUsed == 1) {
cellsPerDimension = 1; cellsPerDimension = 1;
} else { } else {
int cellsTotalLog = 1; int cellsTotalSqrt = 1;
while((1 << cellsTotalLog) < count) { while((cellsTotalSqrt * cellsTotalSqrt) < totalTilesUsed) {
cellsTotalLog++; cellsTotalSqrt++;
} }
cellsPerDimension = 1 << ((cellsTotalLog + 1) / 2); cellsPerDimension = cellsTotalSqrt;
} }
uint16_t sz = k3TexSzX(offscr->depth); uint16_t sz = k3TexSzX(offscr->depth);
uint16_t cellSz = sz / cellsPerDimension; float cellSz = (float) sz / cellsPerDimension;
size_t s = 0;
k3BeginOffscreen(offscr); k3BeginOffscreen(offscr);
for(size_t i = 0; i < count; i++) { size_t i = 0;
for(size_t li = 0; li < LightCount; li++) {
struct LightView *lv = &views[li];
for(int camIdx = 0; camIdx < lv->cams; camIdx++) {
int cellX = i % cellsPerDimension; int cellX = i % cellsPerDimension;
int cellY = i / cellsPerDimension; int cellY = i / cellsPerDimension;
int vp[] = {cellX * cellSz, cellY * cellSz, cellSz, cellSz}; int vp[] = {
(int) roundf(cellX * cellSz),
(int) roundf(cellY * cellSz),
(int) cellSz,
(int) cellSz
};
mat4 view = GLM_MAT4_IDENTITY_INIT; mat4 view = GLM_MAT4_IDENTITY_INIT;
glm_mat4_inv(cams[i], view); glm_mat4_inv(lv->c[camIdx], view);
if(!isCube[i]) { mat4 proj = GLM_MAT4_IDENTITY_INIT;
glm_mat4_mul(projs[i], view, LightShadows[s].vp); glm_mat4_copy(lv->projs == 1 ? lv->p[0] : lv->p[camIdx], proj);
glm_vec4_copy((vec4) {i, (float) cellSz / sz, 0, 0}, LightShadows[s].atlasSegment);
s++; LightShadows[li].vpCount = lv->projs;
if(lv->projs > 1 || camIdx == 0) {
glm_mat4_mul(proj, view, LightShadows[li].vp[camIdx]);
}
if(camIdx == 0) {
glm_vec4_copy((vec4) {i, (float) cellSz / sz, 0, 0}, LightShadows[li].atlasSegment);
} }
glViewport(vp[0], vp[1], vp[2], vp[3]); glViewport(vp[0], vp[1], vp[2], vp[3]);
k3PassDepthOnly(projs[i], cams[i], i == 0, false); k3PassDepthOnly(proj, lv->c[camIdx], i == 0, false);
i++;
}
} }
k3EndOffscreen(offscr); k3EndOffscreen(offscr);
_mm_free(projs);
_mm_free(cams);
_mm_free(isCube);
} }
void k3BatchClear() { void k3BatchClear() {
@ -2230,6 +2294,50 @@ void GlCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei
} }
} }
#include"compr/bc7enc.h"
static void *compress_rgba_bc7(const void *pixels, uint16_t width, uint16_t height, GLenum externalFmt, GLenum intype, size_t *compressedSize) {
static bool called = false;
if(!called) {
bc7enc_compress_block_init();
called = true;
}
assert(externalFmt == GL_RGBA);
assert(intype == GL_UNSIGNED_BYTE);
uint16_t blockWidth = (width + 3) / 4;
uint16_t blockHeight = (height + 3) / 4;
size_t blocks = blockWidth * blockHeight;
*compressedSize = blocks * 16;
uint8_t *output = calloc(blocks, 16);
#pragma omp parallel
{
bc7enc_compress_block_params p;
bc7enc_compress_block_params_init(&p);
#pragma omp for
for(size_t blk = 0; blk < blocks; blk++) {
uint8_t blockPixels[64];
uint16_t blkX = blk % blockWidth;
uint16_t blkY = blk / blockWidth;
memcpy(blockPixels + 0, pixels + 4 * (((blkY * 4) + 0) * width + (blkX * 4)), 16);
memcpy(blockPixels +16, pixels + 4 * (((blkY * 4) + 1) * width + (blkX * 4)), 16);
memcpy(blockPixels +32, pixels + 4 * (((blkY * 4) + 2) * width + (blkX * 4)), 16);
memcpy(blockPixels +48, pixels + 4 * (((blkY * 4) + 3) * width + (blkX * 4)), 16);
bc7enc_compress_block(output + blk * 16, blockPixels, &p);
}
}
return output;
}
void k3Init() { void k3Init() {
if(GLAD_GL_ARB_debug_output) { if(GLAD_GL_ARB_debug_output) {
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB); glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
@ -2288,6 +2396,23 @@ void k3Init() {
"}\n" "}\n"
, NULL), NULL); , NULL), NULL);
} }
/*if(GLAD_GL_KHR_texture_compression_astc_ldr) {
TextureCompressionEnabled = true;
} else */ if(GLAD_GL_ARB_texture_compression_bptc || GLAD_GL_VERSION_4_2) {
TextureCompressionEnabled = true;
TextureCompressionSRGBA = GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM;
TextureCompressionRGBA = GL_COMPRESSED_RGBA_BPTC_UNORM;
TextureOfflineCompressor = compress_rgba_bc7;
} else if(GL_EXT_texture_compression_s3tc) {
TextureCompressionEnabled = true;
TextureCompressionSRGBA = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;
TextureCompressionRGBA = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
}
} }
static size_t preprocess(char *src, const char*(*ldr)(const char *fn), const char ***strs, GLuint **sizes) { static size_t preprocess(char *src, const char*(*ldr)(const char *fn), const char ***strs, GLuint **sizes) {
@ -2624,14 +2749,29 @@ struct k3ARBFP *k3ProgramARBFP(const char *src) {
return (struct k3ARBFP*) (uintptr_t) p; return (struct k3ARBFP*) (uintptr_t) p;
} }
struct k3Offscreen *k3OffscreenCreate(struct k3Tex *diffuse, struct k3Tex *depth) { struct k3Offscreen *k3OffscreenCreateMultisampled(struct k3Tex *diffuse, struct k3Tex *depth, uint8_t samples) {
k3Log(k3_INFO, "Init %sFBO", !diffuse && depth ? "depth-only " : ""); k3Log(k3_INFO, "Init %sFBO", !diffuse && depth ? "depth-only " : "");
if(samples && (!GLAD_GL_EXT_framebuffer_multisample || !GLAD_GL_EXT_framebuffer_blit)) {
samples = 0;
k3Log(k3_WARN, "Multisampled offscreens not supported.");
}
if(!GLAD_GL_EXT_framebuffer_object) {
k3Log(k3_ERR, "Non-FBO offscreens not implemented");
return NULL;
}
GLuint fbo = 0; GLuint fbo = 0;
if(GLAD_GL_EXT_framebuffer_object) {
glGenFramebuffersEXT(1, &fbo); glGenFramebuffersEXT(1, &fbo);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
struct k3Offscreen *ret = calloc(1, sizeof(*ret));
ret->fbo = fbo;
ret->diffuse = diffuse;
ret->depth = depth;
ret->multisampling.samples = samples;
if(diffuse) { if(diffuse) {
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, GL_FROM_K3TEX(diffuse), 0); glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, GL_FROM_K3TEX(diffuse), 0);
} else { } else {
@ -2647,23 +2787,43 @@ struct k3Offscreen *k3OffscreenCreate(struct k3Tex *diffuse, struct k3Tex *depth
k3Log(k3_WARN, "Framebuffer incomplete"); k3Log(k3_WARN, "Framebuffer incomplete");
} }
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); if(samples) {
glDrawBuffer(GL_BACK); // XXX: this should not be necessary GLuint msfbo;
} else { glGenFramebuffersEXT(1, &msfbo);
k3Log(k3_ERR, "Non-FBO offscreens not implemented"); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, msfbo);
return NULL;
ret->multisampling.fbo = msfbo;
if(diffuse) {
glGenRenderbuffersEXT(1, &ret->multisampling.rboDiffuse);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, ret->multisampling.rboDiffuse);
glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples, diffuse->glInternalFormat, diffuse->szX, diffuse->szY);
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, ret->multisampling.rboDiffuse);
} }
struct k3Offscreen *ret = malloc(sizeof(*ret)); if(depth) {
ret->fbo = fbo; glGenRenderbuffersEXT(1, &ret->multisampling.rboDepth);
ret->diffuse = diffuse; glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, ret->multisampling.rboDepth);
ret->depth = depth; glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples, depth->glInternalFormat, depth->szX, depth->szY);
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, ret->multisampling.rboDepth);
}
}
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
glDrawBuffer(GL_BACK); // XXX: this should not be necessary
return ret; return ret;
} }
struct k3Offscreen *k3OffscreenCreate(struct k3Tex *diffuse, struct k3Tex *depth) {
return k3OffscreenCreateMultisampled(diffuse, depth, 0);
}
void k3BeginOffscreen(struct k3Offscreen *offscr) { void k3BeginOffscreen(struct k3Offscreen *offscr) {
if(GLAD_GL_EXT_framebuffer_object) { if(GLAD_GL_EXT_framebuffer_object) {
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, offscr->fbo); GLuint fbo = offscr->multisampling.samples > 0 ? offscr->multisampling.fbo : offscr->fbo;
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
struct k3Tex *t = offscr->diffuse ? offscr->diffuse : offscr->depth; struct k3Tex *t = offscr->diffuse ? offscr->diffuse : offscr->depth;
glViewport(0, 0, k3TexSzX(t), k3TexSzY(t)); glViewport(0, 0, k3TexSzX(t), k3TexSzY(t));
@ -2674,6 +2834,12 @@ void k3BeginOffscreen(struct k3Offscreen *offscr) {
void k3EndOffscreen(struct k3Offscreen *offscr) { void k3EndOffscreen(struct k3Offscreen *offscr) {
if(GLAD_GL_EXT_framebuffer_object) { if(GLAD_GL_EXT_framebuffer_object) {
if(offscr->multisampling.samples) {
glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, offscr->fbo);
glBlitFramebufferEXT(0, 0, offscr->diffuse->szX, offscr->diffuse->szY, 0, 0, offscr->diffuse->szX, offscr->diffuse->szY, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST);
}
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
glViewport(0, 0, MainWidth, MainHeight); glViewport(0, 0, MainWidth, MainHeight);
} else { } else {
@ -2689,7 +2855,9 @@ void k3BlitToScreenEffect(struct k3Offscreen *offscr, int additive, int effect,
if(GLAD_GL_EXT_framebuffer_object) { if(GLAD_GL_EXT_framebuffer_object) {
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, GL_FROM_K3TEX(offscr->diffuse)); struct k3Tex *tex = offscr->diffuse ? offscr->diffuse : offscr->depth;
glBindTexture(GL_TEXTURE_2D, GL_FROM_K3TEX(tex));
if(additive) { if(additive) {
glEnable(GL_BLEND); glEnable(GL_BLEND);
@ -2736,7 +2904,7 @@ void k3BlitToScreenEffect(struct k3Offscreen *offscr, int additive, int effect,
} }
GLint uSz = glGetUniformLocation(GL_FROM_K3GLSL(basicBlitProgram), "u_sz"); GLint uSz = glGetUniformLocation(GL_FROM_K3GLSL(basicBlitProgram), "u_sz");
glUniform2f(uSz, k3TexSzX(offscr->diffuse), k3TexSzY(offscr->diffuse)); glUniform2f(uSz, k3TexSzX(tex), k3TexSzY(tex));
glDrawArrays(GL_TRIANGLES, 0, 3); glDrawArrays(GL_TRIANGLES, 0, 3);
} else { } else {
@ -2744,8 +2912,8 @@ void k3BlitToScreenEffect(struct k3Offscreen *offscr, int additive, int effect,
glUseProgramObjectARB(effect == k3_GLSL ? GL_FROM_K3GLSL(program) : 0); glUseProgramObjectARB(effect == k3_GLSL ? GL_FROM_K3GLSL(program) : 0);
} }
float bleedW = 1.0f / k3TexSzX(offscr->diffuse); float bleedW = 1.0f / k3TexSzX(tex);
float bleedH = 1.0f / k3TexSzY(offscr->diffuse); float bleedH = 1.0f / k3TexSzY(tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

View File

@ -6,8 +6,13 @@
#include<cglm/mat4.h> #include<cglm/mat4.h>
#include<cglm/quat.h> #include<cglm/quat.h>
#ifdef k3_MULTITHREAD
#include<pthread.h>
#endif
void k3Init(); void k3Init();
void k3Resize(uint16_t width, uint16_t height); void k3Resize(uint16_t width, uint16_t height);
void k3Update();
enum k3TexType { enum k3TexType {
k3_DIFFUSE, k3_NORMAL, k3_DISPLACEMENT, k3_EMISSION, k3_ROUGHNESS, k3_ALPHA, k3_DIFFUSE, k3_NORMAL, k3_DISPLACEMENT, k3_EMISSION, k3_ROUGHNESS, k3_ALPHA,
@ -43,7 +48,8 @@ struct k3ARBFP;
struct k3ARBFP *k3ProgramARBFP(const char *src); struct k3ARBFP *k3ProgramARBFP(const char *src);
extern uint8_t k3GraphicalReduction; extern uint8_t k3GraphicalReduction;
extern int k3IsCore; extern bool k3IsCore;
extern bool k3IsSoftSkinning;
struct k3Mat { struct k3Mat {
struct { struct {
@ -91,6 +97,7 @@ struct k3Mat {
char transparent; char transparent;
char nocull; char nocull;
char alphatest; char alphatest;
char depthwrite;
} passes[1]; } passes[1];
}; };
@ -134,6 +141,7 @@ struct k3Light {
_Alignas(16) union { _Alignas(16) union {
struct { struct {
vec4 direction; vec4 direction;
uint8_t cascadeCount;
} dir; } dir;
struct { struct {
vec4 position; vec4 position;
@ -155,6 +163,8 @@ struct k3Storage;
void k3StorageRef(struct k3Storage*); void k3StorageRef(struct k3Storage*);
void k3StorageUnref(struct k3Storage*); void k3StorageUnref(struct k3Storage*);
#define k3_ATTRIB_NONE (NULL)
#define k3_ATTRIB_EMPTY ((void*) 1)
struct k3Mdl; struct k3Mdl;
struct k3Mdl *k3MdlCreate(size_t verts, size_t indices, size_t boneCount, vec3 *pos, uint8_t *nrm, float *uvs, uint8_t *cols, uint8_t *boneids, uint16_t *boneweights, uint16_t *inds, mat4 *invBind, uint8_t *boneParents); struct k3Mdl *k3MdlCreate(size_t verts, size_t indices, size_t boneCount, vec3 *pos, uint8_t *nrm, float *uvs, uint8_t *cols, uint8_t *boneids, uint16_t *boneweights, uint16_t *inds, mat4 *invBind, uint8_t *boneParents);
void k3MdlUpdatePos(struct k3Mdl *mdl, vec3 *pos); void k3MdlUpdatePos(struct k3Mdl *mdl, vec3 *pos);
@ -185,6 +195,7 @@ void k3PassDepthOnly(mat4 projection, mat4 cam, int clear, int cull);
void k3PassShadowmap(mat4 projection, mat4 cam, struct k3Offscreen *offscr); void k3PassShadowmap(mat4 projection, mat4 cam, struct k3Offscreen *offscr);
struct k3Offscreen; struct k3Offscreen;
struct k3Offscreen *k3OffscreenCreateMultisampled(struct k3Tex *diffuse, struct k3Tex *depth, uint8_t samples);
struct k3Offscreen *k3OffscreenCreate(struct k3Tex *diffuse, struct k3Tex *depth); struct k3Offscreen *k3OffscreenCreate(struct k3Tex *diffuse, struct k3Tex *depth);
void k3BeginOffscreen(struct k3Offscreen*); void k3BeginOffscreen(struct k3Offscreen*);
void k3EndOffscreen(struct k3Offscreen*); void k3EndOffscreen(struct k3Offscreen*);

83
src/k3_internal.h Normal file
View File

@ -0,0 +1,83 @@
#pragma once
#include"k3.h"
#include"gl.h"
#include<cglm/vec2.h>
#include<cglm/frustum.h>
#include<cglm/cam.h>
#define GL_FROM_K3TEX(k3t) ((k3t) ? (k3t)->tex : 0)
#define GL_FROM_K3MARCHER(k3m) ((GLuint) (uintptr_t) (k3m))
#define GL_FROM_K3ARBVP(k3m) ((GLuint) (uintptr_t) (k3m))
#define GL_FROM_K3ARBFP(k3m) ((GLuint) (uintptr_t) (k3m))
#define GL_FROM_K3GLSL(k3m) ((GLuint) (uintptr_t) (k3m))
struct k3Tex {
GLuint tex;
int cubemap;
uint32_t szX;
uint32_t szY;
uint32_t szZ;
GLuint glInternalFormat;
GLuint glExternalFormat;
GLuint glInType;
bool mipmap;
// Asynchronous decoding
uint8_t deferredRemaining;
uint8_t *deferredData[6];
struct k3Tex *deferredNext;
};
struct k3Storage {
int16_t ref;
void(*free)(struct k3Storage*);
};
struct k3StorageBasic {
struct k3Storage;
GLuint gl;
};
struct k3Offscreen {
GLuint fbo;
struct k3Tex *diffuse;
struct k3Tex *depth;
struct {
uint8_t samples;
GLuint fbo;
GLuint rboDiffuse;
GLuint rboDepth;
} multisampling;
};
struct k3Mdl {
struct {
vec3 *pos;
uint8_t *boneids;
uint16_t *boneweights;
} cpuSkinning;
size_t verts;
struct k3StorageBasic *vstore;
struct k3StorageBasic *estore;
uint16_t meshCount;
struct k3Mesh *meshes;
size_t boneCount;
mat4 *invBind;
uint8_t *boneParents;
uint16_t animCount;
struct k3Animation **anims;
vec3 aabb[2];
int offV, offN, offC, offU, offB, offT;
const char *debugname;
};

View File

@ -12,6 +12,8 @@ struct S {
}; };
static struct k3Tex *activeTex; static struct k3Tex *activeTex;
static float activeBorderRadius;
static size_t SCount, SCapacity; static size_t SCount, SCapacity;
static struct S *S; static struct S *S;
@ -25,21 +27,33 @@ void k3BatchInit() {
"in vec2 a_pos;\n" "in vec2 a_pos;\n"
"in vec2 a_uv;\n" "in vec2 a_uv;\n"
"in vec4 a_color;\n" "in vec4 a_color;\n"
"in vec2 a_size;\n"
"out vec2 v_uv;\n" "out vec2 v_uv;\n"
"out vec4 v_color;\n" "out vec4 v_color;\n"
"out vec2 v_size;\n"
"void main() {\n" "void main() {\n"
" v_uv = a_uv;\n" " v_uv = a_uv;\n"
" v_color = a_color;\n" " v_color = a_color;\n"
" v_size = a_size;\n"
" gl_Position = vec4(vec2(a_pos.x / 3600.0, a_pos.y / 2025.0) * 2.0 - 1.0, 0.0, 1.0);\n" " gl_Position = vec4(vec2(a_pos.x / 3600.0, a_pos.y / 2025.0) * 2.0 - 1.0, 0.0, 1.0);\n"
"}\n" "}\n"
, NULL), k3ShaderGLSLF( , NULL), k3ShaderGLSLF(
"#version 330\n" "#version 330\n"
"uniform sampler2D u_tex;\n" "uniform sampler2D u_tex;\n"
"uniform float u_texuse;\n" "uniform float u_texuse;\n"
"uniform float u_borderradius;\n"
"in vec2 v_uv;\n" "in vec2 v_uv;\n"
"in vec4 v_color;\n" "in vec4 v_color;\n"
"in vec2 v_size;\n"
"out vec4 fragcolor;\n" "out vec4 fragcolor;\n"
"void main() {\n" "void main() {\n"
" vec2 sz = v_size / 2.0;\n"
" vec2 c = abs(v_uv * 2.0 - 1.0);\n"
" c = c * (sz + u_borderradius);\n"
" c = max(c - sz, 0.0);\n"
" if(length(c) > u_borderradius) {\n"
" discard;\n"
" }\n"
" fragcolor = mix(vec4(1, 1, 1, 1), texture2D(u_tex, v_uv), u_texuse) * v_color;\n" " fragcolor = mix(vec4(1, 1, 1, 1), texture2D(u_tex, v_uv), u_texuse) * v_color;\n"
"}\n" "}\n"
, NULL), NULL); , NULL), NULL);
@ -48,10 +62,11 @@ void k3BatchInit() {
} }
} }
void k3BatchAdd(struct k3Tex *tex, struct k3RectF src, struct k3RectF dst, float rot, vec4 color) { void k3BatchAdd(struct k3Tex *tex, struct k3RectF src, struct k3RectF dst, float rot, vec4 color, float borderRadius) {
if(activeTex != tex) { if(activeTex != tex || borderRadius != activeBorderRadius) {
k3BatchFlush(); k3BatchFlush();
activeTex = tex; activeTex = tex;
activeBorderRadius = borderRadius;
} }
if(SCount == SCapacity) { if(SCount == SCapacity) {
@ -73,6 +88,8 @@ void k3BatchAdd(struct k3Tex *tex, struct k3RectF src, struct k3RectF dst, float
} }
void k3BatchFlush() { void k3BatchFlush() {
if(!S) return;
if(!k3IsCore) { if(!k3IsCore) {
glDisable(GL_NORMALIZE); glDisable(GL_NORMALIZE);
@ -108,89 +125,107 @@ void k3BatchFlush() {
if(k3IsCore) { if(k3IsCore) {
glUseProgram((GLuint) coreProg); glUseProgram((GLuint) coreProg);
float *farr = alloca(SCount * 48 * sizeof(*farr)); float *farr = alloca(SCount * 60 * sizeof(*farr));
struct S *s = S; struct S *s = S;
for(size_t i = 0; i < SCount; i++) { for(size_t i = 0; i < SCount; i++) {
farr[i * 48 + 0] = s->dst.x; farr[i * 60 + 0] = s->dst.x;
farr[i * 48 + 1] = s->dst.y; farr[i * 60 + 1] = s->dst.y;
farr[i * 48 + 2] = s->src.x; farr[i * 60 + 2] = s->src.x;
farr[i * 48 + 3] = s->src.y + s->src.h; farr[i * 60 + 3] = s->src.y + s->src.h;
farr[i * 48 + 4] = s->color[0]; farr[i * 60 + 4] = s->color[0];
farr[i * 48 + 5] = s->color[1]; farr[i * 60 + 5] = s->color[1];
farr[i * 48 + 6] = s->color[2]; farr[i * 60 + 6] = s->color[2];
farr[i * 48 + 7] = s->color[3]; farr[i * 60 + 7] = s->color[3];
farr[i * 60 + 8] = s->dst.w;
farr[i * 60 + 9] = s->dst.h;
farr[i * 48 + 8] = s->dst.x + s->dst.w; farr[i * 60 + 10] = s->dst.x + s->dst.w;
farr[i * 48 + 9] = s->dst.y; farr[i * 60 + 11] = s->dst.y;
farr[i * 48 + 10] = s->src.x + s->src.w; farr[i * 60 + 12] = s->src.x + s->src.w;
farr[i * 48 + 11] = s->src.y + s->src.h; farr[i * 60 + 13] = s->src.y + s->src.h;
farr[i * 48 + 12] = s->color[0]; farr[i * 60 + 14] = s->color[0];
farr[i * 48 + 13] = s->color[1]; farr[i * 60 + 15] = s->color[1];
farr[i * 48 + 14] = s->color[2]; farr[i * 60 + 16] = s->color[2];
farr[i * 48 + 15] = s->color[3]; farr[i * 60 + 17] = s->color[3];
farr[i * 60 + 18] = s->dst.w;
farr[i * 60 + 19] = s->dst.h;
farr[i * 48 + 16] = s->dst.x + s->dst.w; farr[i * 60 + 20] = s->dst.x + s->dst.w;
farr[i * 48 + 17] = s->dst.y + s->dst.h; farr[i * 60 + 21] = s->dst.y + s->dst.h;
farr[i * 48 + 18] = s->src.x + s->src.w; farr[i * 60 + 22] = s->src.x + s->src.w;
farr[i * 48 + 19] = s->src.y; farr[i * 60 + 23] = s->src.y;
farr[i * 48 + 20] = s->color[0]; farr[i * 60 + 24] = s->color[0];
farr[i * 48 + 21] = s->color[1]; farr[i * 60 + 25] = s->color[1];
farr[i * 48 + 22] = s->color[2]; farr[i * 60 + 26] = s->color[2];
farr[i * 48 + 23] = s->color[3]; farr[i * 60 + 27] = s->color[3];
farr[i * 60 + 28] = s->dst.w;
farr[i * 60 + 29] = s->dst.h;
farr[i * 48 + 24] = s->dst.x; farr[i * 60 + 30] = s->dst.x;
farr[i * 48 + 25] = s->dst.y; farr[i * 60 + 31] = s->dst.y;
farr[i * 48 + 26] = s->src.x; farr[i * 60 + 32] = s->src.x;
farr[i * 48 + 27] = s->src.y + s->src.h; farr[i * 60 + 33] = s->src.y + s->src.h;
farr[i * 48 + 28] = s->color[0]; farr[i * 60 + 34] = s->color[0];
farr[i * 48 + 29] = s->color[1]; farr[i * 60 + 35] = s->color[1];
farr[i * 48 + 30] = s->color[2]; farr[i * 60 + 36] = s->color[2];
farr[i * 48 + 31] = s->color[3]; farr[i * 60 + 37] = s->color[3];
farr[i * 60 + 38] = s->dst.w;
farr[i * 60 + 39] = s->dst.h;
farr[i * 48 + 32] = s->dst.x + s->dst.w; farr[i * 60 + 40] = s->dst.x + s->dst.w;
farr[i * 48 + 33] = s->dst.y + s->dst.h; farr[i * 60 + 41] = s->dst.y + s->dst.h;
farr[i * 48 + 34] = s->src.x + s->src.w; farr[i * 60 + 42] = s->src.x + s->src.w;
farr[i * 48 + 35] = s->src.y; farr[i * 60 + 43] = s->src.y;
farr[i * 48 + 36] = s->color[0]; farr[i * 60 + 44] = s->color[0];
farr[i * 48 + 37] = s->color[1]; farr[i * 60 + 45] = s->color[1];
farr[i * 48 + 38] = s->color[2]; farr[i * 60 + 46] = s->color[2];
farr[i * 48 + 39] = s->color[3]; farr[i * 60 + 47] = s->color[3];
farr[i * 60 + 48] = s->dst.w;
farr[i * 60 + 49] = s->dst.h;
farr[i * 48 + 40] = s->dst.x; farr[i * 60 + 50] = s->dst.x;
farr[i * 48 + 41] = s->dst.y + s->dst.h; farr[i * 60 + 51] = s->dst.y + s->dst.h;
farr[i * 48 + 42] = s->src.x; farr[i * 60 + 52] = s->src.x;
farr[i * 48 + 43] = s->src.y; farr[i * 60 + 53] = s->src.y;
farr[i * 48 + 44] = s->color[0]; farr[i * 60 + 54] = s->color[0];
farr[i * 48 + 45] = s->color[1]; farr[i * 60 + 55] = s->color[1];
farr[i * 48 + 46] = s->color[2]; farr[i * 60 + 56] = s->color[2];
farr[i * 48 + 47] = s->color[3]; farr[i * 60 + 57] = s->color[3];
farr[i * 60 + 58] = s->dst.w;
farr[i * 60 + 59] = s->dst.h;
s++; s++;
} }
glBindBufferARB(GL_ARRAY_BUFFER_ARB, coreVBO); glBindBufferARB(GL_ARRAY_BUFFER_ARB, coreVBO);
glBufferDataARB(GL_ARRAY_BUFFER_ARB, SCount * 48 * sizeof(*farr), farr, GL_DYNAMIC_DRAW); glBufferDataARB(GL_ARRAY_BUFFER_ARB, SCount * 60 * sizeof(*farr), farr, GL_DYNAMIC_DRAW);
glUniform1f(glGetUniformLocation((GLuint) coreProg, "u_texuse"), !!activeTex); glUniform1f(glGetUniformLocation((GLuint) coreProg, "u_texuse"), !!activeTex);
glUniform1f(glGetUniformLocation((GLuint) coreProg, "u_borderradius"), activeBorderRadius);
GLint aPos = glGetAttribLocation((GLuint) coreProg, "a_pos"); GLint aPos = glGetAttribLocation((GLuint) coreProg, "a_pos");
GLint aUv = glGetAttribLocation((GLuint) coreProg, "a_uv"); GLint aUv = glGetAttribLocation((GLuint) coreProg, "a_uv");
GLint aColor = glGetAttribLocation((GLuint) coreProg, "a_color"); GLint aColor = glGetAttribLocation((GLuint) coreProg, "a_color");
GLint aSize = glGetAttribLocation((GLuint) coreProg, "a_size");
glEnableVertexAttribArray(aPos); glEnableVertexAttribArray(aPos);
glEnableVertexAttribArray(aUv); glEnableVertexAttribArray(aUv);
glEnableVertexAttribArray(aColor); glEnableVertexAttribArray(aColor);
glEnableVertexAttribArray(aSize);
glVertexAttribPointer(aPos, 2, GL_FLOAT, GL_FALSE, 32, (void*) 0); glVertexAttribPointer(aPos, 2, GL_FLOAT, GL_FALSE, 40, (void*) 0);
glVertexAttribPointer(aUv, 2, GL_FLOAT, GL_FALSE, 32, (void*) 8); glVertexAttribPointer(aUv, 2, GL_FLOAT, GL_FALSE, 40, (void*) 8);
glVertexAttribPointer(aColor, 4, GL_FLOAT, GL_FALSE, 32, (void*) 16); glVertexAttribPointer(aColor, 4, GL_FLOAT, GL_FALSE, 40, (void*) 16);
glVertexAttribPointer(aSize, 2, GL_FLOAT, GL_FALSE, 40, (void*) 32);
glDrawArrays(GL_TRIANGLES, 0, SCount * 6); glDrawArrays(GL_TRIANGLES, 0, SCount * 6);
glDisableVertexAttribArray(aPos); glDisableVertexAttribArray(aPos);
glDisableVertexAttribArray(aUv); glDisableVertexAttribArray(aUv);
glDisableVertexAttribArray(aColor); glDisableVertexAttribArray(aColor);
glDisableVertexAttribArray(aSize);
} else { } else {
if(GLAD_GL_ARB_shading_language_100) { if(GLAD_GL_ARB_shading_language_100) {
glUseProgramObjectARB(0); glUseProgramObjectARB(0);

View File

@ -10,5 +10,5 @@ struct k3RectF {
float h; float h;
}; };
void k3BatchAdd(struct k3Tex *tex, struct k3RectF src, struct k3RectF dst, float rot, vec4 color); void k3BatchAdd(struct k3Tex *tex, struct k3RectF src, struct k3RectF dst, float rot, vec4 color, float borderRadius);
void k3BatchFlush(); void k3BatchFlush();

View File

@ -32,8 +32,8 @@ int make_mb1() {
if(!v) return 0; if(!v) return 0;
struct k3GLSLF *f = !k3IsCore ? k3ShaderGLSLF( struct k3GLSLF *f = !k3IsCore ? k3ShaderGLSLF(
"#define THRESHOLD 0.7" "\n" "#define THRESHOLD 0.6" "\n"
"#define FACTOR 0.08" "\n" "#define FACTOR 0.07" "\n"
"uniform sampler2D u_tex;" "\n" "uniform sampler2D u_tex;" "\n"
"varying vec2 v_uv;" "\n" "varying vec2 v_uv;" "\n"
"vec3 get_col(vec2 uv) {" "\n" "vec3 get_col(vec2 uv) {" "\n"
@ -57,8 +57,8 @@ int make_mb1() {
"}" "\n" "}" "\n"
, NULL) : k3ShaderGLSLF( , NULL) : k3ShaderGLSLF(
"#version 330\n" "#version 330\n"
"#define THRESHOLD 0.7" "\n" "#define THRESHOLD 0.6" "\n"
"#define FACTOR 0.08" "\n" "#define FACTOR 0.07" "\n"
"uniform sampler2D u_tex;" "\n" "uniform sampler2D u_tex;" "\n"
"in vec2 v_uv;" "\n" "in vec2 v_uv;" "\n"
"out vec4 fragcolor;" "\n" "out vec4 fragcolor;" "\n"
@ -122,15 +122,15 @@ int make_mb2() {
"}" "\n" "}" "\n"
"void main() {" "\n" "void main() {" "\n"
" vec3 c = vec3(0);" "\n" " vec3 c = vec3(0);" "\n"
" c += 0.0020 * get_col(v_uv + vec2(0, -4.0 / 512.0));" "\n" " c += 0.0020 * get_col(v_uv + vec2(0, -4.0 / 256.0));" "\n"
" c += 0.0060 * get_col(v_uv + vec2(0, -3.0 / 512.0));" "\n" " c += 0.0060 * get_col(v_uv + vec2(0, -3.0 / 256.0));" "\n"
" c += 0.0606 * get_col(v_uv + vec2(0, -2.0 / 512.0));" "\n" " c += 0.0606 * get_col(v_uv + vec2(0, -2.0 / 256.0));" "\n"
" c += 0.2417 * get_col(v_uv + vec2(0, -1.0 / 512.0));" "\n" " c += 0.2417 * get_col(v_uv + vec2(0, -1.0 / 256.0));" "\n"
" c += 0.3829 * get_col(v_uv + vec2(0, +0.0 / 512.0));" "\n" " c += 0.3829 * get_col(v_uv + vec2(0, +0.0 / 256.0));" "\n"
" c += 0.2417 * get_col(v_uv + vec2(0, +1.0 / 512.0));" "\n" " c += 0.2417 * get_col(v_uv + vec2(0, +1.0 / 256.0));" "\n"
" c += 0.0606 * get_col(v_uv + vec2(0, +2.0 / 512.0));" "\n" " c += 0.0606 * get_col(v_uv + vec2(0, +2.0 / 256.0));" "\n"
" c += 0.0060 * get_col(v_uv + vec2(0, +3.0 / 512.0));" "\n" " c += 0.0060 * get_col(v_uv + vec2(0, +3.0 / 256.0));" "\n"
" c += 0.0020 * get_col(v_uv + vec2(0, +4.0 / 512.0));" "\n" " c += 0.0020 * get_col(v_uv + vec2(0, +4.0 / 256.0));" "\n"
" gl_FragColor = vec4(c, 1);" "\n" " gl_FragColor = vec4(c, 1);" "\n"
"}" "\n" "}" "\n"
, NULL) : k3ShaderGLSLF( , NULL) : k3ShaderGLSLF(
@ -144,15 +144,15 @@ int make_mb2() {
"}" "\n" "}" "\n"
"void main() {" "\n" "void main() {" "\n"
" vec3 c = vec3(0);" "\n" " vec3 c = vec3(0);" "\n"
" c += 0.0020 * get_col(v_uv + vec2(0, -4.0 / 512.0));" "\n" " c += 0.0020 * get_col(v_uv + vec2(0, -4.0 / 256.0));" "\n"
" c += 0.0060 * get_col(v_uv + vec2(0, -3.0 / 512.0));" "\n" " c += 0.0060 * get_col(v_uv + vec2(0, -3.0 / 256.0));" "\n"
" c += 0.0606 * get_col(v_uv + vec2(0, -2.0 / 512.0));" "\n" " c += 0.0606 * get_col(v_uv + vec2(0, -2.0 / 256.0));" "\n"
" c += 0.2417 * get_col(v_uv + vec2(0, -1.0 / 512.0));" "\n" " c += 0.2417 * get_col(v_uv + vec2(0, -1.0 / 256.0));" "\n"
" c += 0.3829 * get_col(v_uv + vec2(0, +0.0 / 512.0));" "\n" " c += 0.3829 * get_col(v_uv + vec2(0, +0.0 / 256.0));" "\n"
" c += 0.2417 * get_col(v_uv + vec2(0, +1.0 / 512.0));" "\n" " c += 0.2417 * get_col(v_uv + vec2(0, +1.0 / 256.0));" "\n"
" c += 0.0606 * get_col(v_uv + vec2(0, +2.0 / 512.0));" "\n" " c += 0.0606 * get_col(v_uv + vec2(0, +2.0 / 256.0));" "\n"
" c += 0.0060 * get_col(v_uv + vec2(0, +3.0 / 512.0));" "\n" " c += 0.0060 * get_col(v_uv + vec2(0, +3.0 / 256.0));" "\n"
" c += 0.0020 * get_col(v_uv + vec2(0, +4.0 / 512.0));" "\n" " c += 0.0020 * get_col(v_uv + vec2(0, +4.0 / 256.0));" "\n"
" fragcolor = vec4(c, 1);" "\n" " fragcolor = vec4(c, 1);" "\n"
"}" "\n" "}" "\n"
, NULL); , NULL);
@ -237,7 +237,7 @@ int make_tonemapper() {
"}" "\n" "}" "\n"
"vec3 get_col(vec2 uv) {" "\n" "vec3 get_col(vec2 uv) {" "\n"
" vec3 col = texture2D(u_tex, uv).rgb;" "\n" " vec3 col = texture2D(u_tex, uv).rgb;" "\n"
" col = toner(col, 0.80, 1.5);" "\n" " col = toner(col, 0.80, 1.0);" "\n"
" return col;" "\n" " return col;" "\n"
"}" "\n" "}" "\n"
"void main() {" "\n" "void main() {" "\n"

View File

@ -131,6 +131,7 @@ void k3FontDraw(struct k3Font *this, float xStart, float yStart, float sz, const
struct k3Tex *tex = this->pages[g->page]; struct k3Tex *tex = this->pages[g->page];
size_t texW = this->texW; size_t texW = this->texW;
size_t texH = this->texH; size_t texH = this->texH;
k3BatchAdd(tex, k3BatchAdd(tex,
(struct k3RectF) {(float) g->x / texW, (float) g->y / texH, (float) g->width / texW, (float) g->height / texH}, (struct k3RectF) {(float) g->x / texW, (float) g->y / texH, (float) g->width / texW, (float) g->height / texH},
(struct k3RectF) { (struct k3RectF) {
@ -138,7 +139,7 @@ void k3FontDraw(struct k3Font *this, float xStart, float yStart, float sz, const
y + ((-g->height - g->yoffset) * this->lineScale + 1) * sz, y + ((-g->height - g->yoffset) * this->lineScale + 1) * sz,
g->width * this->lineScale * sz, g->width * this->lineScale * sz,
g->height * this->lineScale * sz g->height * this->lineScale * sz
}, 0, color); }, 0, color, 0);
x += g->xadvance * this->lineScale * sz; x += g->xadvance * this->lineScale * sz;
} }

View File

@ -70,9 +70,30 @@ bool k3MEventSend(struct k3MEvent *ev) {
return true; return true;
} }
static void obj_draw(struct k3MObj *this) {
struct k3MProperty *borderRadiusProp = k3MFindProperty(this, k3M_PROP_BORDER_RADIUS, false);
float borderRadius = borderRadiusProp ? borderRadiusProp->si[0] : 0;
struct k3MProperty *bgColorProp = k3MFindProperty(this, k3M_PROP_BG_COLOR, false);
vec4 bgColor = {0, 0, 0, 0};
if(bgColorProp) {
bgColor[0] = bgColorProp->si[0] / 255.0;
bgColor[1] = bgColorProp->si[1] / 255.0;
bgColor[2] = bgColorProp->si[2] / 255.0;
bgColor[3] = bgColorProp->si[3] / 255.0;
}
k3BatchAdd(NULL, (struct k3RectF) {0, 0, 1, 1}, (struct k3RectF) {
this->x, this->y,
this->w, this->h
}, 0, bgColor, borderRadius);
}
static bool label_draw(struct k3MEvent *ev, uint8_t *ud) { static bool label_draw(struct k3MEvent *ev, uint8_t *ud) {
struct k3MLabel *this = (void*) ev->target; struct k3MLabel *this = (void*) ev->target;
obj_draw((void*) this);
if(this->txt) { if(this->txt) {
k3FontDraw(this->font, this->x, this->y, this->sz, this->txt, (vec4) {1, 1, 1, 1}); k3FontDraw(this->font, this->x, this->y, this->sz, this->txt, (vec4) {1, 1, 1, 1});
} }
@ -118,51 +139,29 @@ void k3MAddChild(struct k3MObj *parent, struct k3MObj *child) {
child->parent = parent; child->parent = parent;
} }
/*static struct k3Menu *k3ScreenFuncMouse(struct k3Menu *_, int16_t x, int16_t y, int ev) { void k3MOverrideProperty(struct k3MObj *obj, struct k3MProperty n) {
struct k3Screen *this = (void*) _; struct k3MProperty *o = k3MFindProperty(obj, n.type, true);
if(ev == k3_MENU_MOUSE_MOVE) { if(!o) {
struct k3Menu *newHovered = k3ContainerFuncMouse(_, x, y, k3_MENU_MOUSE_TEST); obj->properties = realloc(obj->properties, ++obj->propertyCount * sizeof(*obj->properties));
if(this->hovered != newHovered) { o = &obj->properties[obj->propertyCount - 1];
if(this->hovered && this->hovered->funcmouse) {
this->hovered->funcmouse(this->hovered, x, y, k3_MENU_MOUSE_OUT);
} }
this->hovered = newHovered; memcpy(o, &n, sizeof(n));
}
if(this->hovered && this->hovered->funcmouse) { struct k3MProperty *k3MFindProperty(struct k3MObj *obj, enum k3MPropertyType t, bool direct) {
this->hovered->funcmouse(this->hovered, x, y, k3_MENU_MOUSE_IN); for(size_t i = 0; i < obj->propertyCount; i++) {
} if(obj->properties[i].type == t) {
} else { return &obj->properties[i];
if(this->hovered && this->hovered->funcmouse) {
return this->hovered->funcmouse(this->hovered, x, y, k3_MENU_MOUSE_MOVE);
} }
} }
} else if(this->hovered && this->hovered->funcmouse) { if(direct || !obj->parent) {
struct k3Menu *ret = this->hovered->funcmouse(this->hovered, x, y, ev);
this->keyboardFocus = ret;
return ret;
}
return NULL; return NULL;
}
static void k3ScreenFuncKey(struct k3Menu *_, int key, int action, int modifiers) {
struct k3Screen *this = (void*) _;
if(this->keyboardFocus && this->keyboardFocus->funckey) {
this->keyboardFocus->funckey(this->keyboardFocus, key, action, modifiers);
} }
return k3MFindProperty(obj->parent, t, direct);
} }
static void k3ScreenFuncChar(struct k3Menu *_, uint32_t codepoint) {
struct k3Screen *this = (void*) _;
if(this->keyboardFocus && this->keyboardFocus->funcchar) {
this->keyboardFocus->funcchar(this->keyboardFocus, codepoint);
}
}*/
static bool screen_ev(struct k3MEvent *ev, uint8_t *ud) { static bool screen_ev(struct k3MEvent *ev, uint8_t *ud) {
struct k3MScreen *this = (void*) ev->target; struct k3MScreen *this = (void*) ev->target;
@ -194,6 +193,12 @@ static bool screen_ev(struct k3MEvent *ev, uint8_t *ud) {
} }
if(innermost != this->lastHover) {
this->lastHover->hovered = false;
innermost->hovered = true;
this->lastHover = innermost;
}
if(innermost != (void*) this) { if(innermost != (void*) this) {
if(ev->code == k3M_EVENT_MOUSE_PRESS) { if(ev->code == k3M_EVENT_MOUSE_PRESS) {
@ -242,6 +247,8 @@ static bool screen_ev(struct k3MEvent *ev, uint8_t *ud) {
struct k3MScreen *k3MScreen() { struct k3MScreen *k3MScreen() {
struct k3MScreen *ret = calloc(1, sizeof(*ret)); struct k3MScreen *ret = calloc(1, sizeof(*ret));
ret->lastHover = (void*) ret;
k3MRegisterEventHandler(ret, k3M_EVENT_ALL, screen_ev, NULL, 0); k3MRegisterEventHandler(ret, k3M_EVENT_ALL, screen_ev, NULL, 0);
return ret; return ret;
@ -250,12 +257,9 @@ struct k3MScreen *k3MScreen() {
static bool textbutton_draw(struct k3MEvent *ev, uint8_t *ud) { static bool textbutton_draw(struct k3MEvent *ev, uint8_t *ud) {
struct k3MTextButton *this = (void*) ev->target; struct k3MTextButton *this = (void*) ev->target;
if(this->txt) { obj_draw((void*) this);
k3BatchAdd(NULL, (struct k3RectF) {}, (struct k3RectF) {
this->x, this->y,
this->w, this->h
}, 0, (vec4) {1, 1, 1, 0.2 + 0.1 * this->hovered});
if(this->txt) {
struct k3RectF txtsz; struct k3RectF txtsz;
k3FontSz(this->font, this->sz, this->txt, &txtsz); k3FontSz(this->font, this->sz, this->txt, &txtsz);
@ -280,12 +284,9 @@ struct k3MTextButton *k3MTextButton(struct k3Font *font, float sz, const char *t
static bool textinput_draw(struct k3MEvent *ev, uint8_t *ud) { static bool textinput_draw(struct k3MEvent *ev, uint8_t *ud) {
struct k3MTextInput *this = (void*) ev->target; struct k3MTextInput *this = (void*) ev->target;
if(this->txt) { obj_draw((void*) this);
k3BatchAdd(NULL, (struct k3RectF) {}, (struct k3RectF) {
this->x, this->y,
this->w, this->h
}, 0, (vec4) {1, 1, 1, 0.2});
if(this->txt) {
int isPlaceholder = strlen(this->txt) == 0; int isPlaceholder = strlen(this->txt) == 0;
const char *txt = isPlaceholder ? this->placeholder : this->txt; const char *txt = isPlaceholder ? this->placeholder : this->txt;

View File

@ -61,6 +61,19 @@ typedef struct k3MEventHandler {
uint16_t code; uint16_t code;
} k3MEventHandler; } k3MEventHandler;
enum k3MPropertyType {
k3M_PROP_BG_COLOR,
k3M_PROP_BORDER_RADIUS,
};
struct k3MProperty {
enum k3MPropertyType type;
union {
intmax_t si[4];
void *ptr;
uint8_t buf[32];
};
};
struct k3MObj { struct k3MObj {
struct k3MObj *parent; struct k3MObj *parent;
@ -76,6 +89,9 @@ struct k3MObj {
k3MEventHandler *handlers; k3MEventHandler *handlers;
size_t handlerCount; size_t handlerCount;
struct k3MProperty *properties;
size_t propertyCount;
}; };
#define k3MenuSetBounds(a, x, y, w, h) k3MenuSetBounds_((struct k3MObj*) (a), (x), (y), (w), (h)) #define k3MenuSetBounds(a, x, y, w, h) k3MenuSetBounds_((struct k3MObj*) (a), (x), (y), (w), (h))
@ -89,6 +105,9 @@ bool k3MEventSend(struct k3MEvent *ev);
int k3MRemoveChild(struct k3MObj *parent, struct k3MObj *child); int k3MRemoveChild(struct k3MObj *parent, struct k3MObj *child);
void k3MAddChild(struct k3MObj *parent, struct k3MObj *child); void k3MAddChild(struct k3MObj *parent, struct k3MObj *child);
void k3MOverrideProperty(struct k3MObj *obj, struct k3MProperty);
struct k3MProperty *k3MFindProperty(struct k3MObj *obj, enum k3MPropertyType, bool direct);
struct k3MLabel { struct k3MLabel {
struct k3MObj; struct k3MObj;
@ -102,6 +121,7 @@ struct k3MScreen {
struct k3MObj; struct k3MObj;
struct k3MObj *keyboardFocus; struct k3MObj *keyboardFocus;
struct k3MObj *lastHover;
}; };
struct k3MScreen *k3MScreen(); struct k3MScreen *k3MScreen();

185
src/k3particles.c Normal file
View File

@ -0,0 +1,185 @@
#include"k3particles.h"
#include"k3_internal.h"
struct xorshift128_state {
uint32_t x[4];
};
uint32_t xorshift128(struct xorshift128_state *state) {
uint32_t t = state->x[3];
uint32_t s = state->x[0];
state->x[3] = state->x[2];
state->x[2] = state->x[1];
state->x[1] = s;
t ^= t << 11;
t ^= t >> 8;
return state->x[0] = t ^ s ^ (s >> 19);
}
struct xorshift128_state xs = {{1, 2, 3, 4}};
static uint32_t randint(uint32_t min, uint32_t max) {
uint32_t x;
do {
x = xorshift128(&xs);
} while(0x100000000UL - 0x100000000UL % ((uint64_t) max + 1) <= x);
return x / (0x100000000UL / ((uint64_t) max + 1));
}
static float randfloat() {
union { uint32_t u32; float f; } u = { .u32 = xorshift128(&xs) >> 9 | 0x3f800000 };
return u.f - 1.0;
}
void k3CPUQuadParticlesInit(struct k3CPUQuadParticles *this, struct k3Mat *mat) {
uint16_t *inds = calloc(sizeof(*inds), this->capacity * 6);
for(size_t i = 0; i < this->capacity; i++) {
inds[i * 6 + 0] = i * 4 + 0;
inds[i * 6 + 1] = i * 4 + 1;
inds[i * 6 + 2] = i * 4 + 2;
inds[i * 6 + 3] = i * 4 + 0;
inds[i * 6 + 4] = i * 4 + 2;
inds[i * 6 + 5] = i * 4 + 3;
}
this->mdl = k3MdlCreate(this->capacity * 4, this->capacity * 6, 0, k3_ATTRIB_EMPTY, k3_ATTRIB_EMPTY, k3_ATTRIB_EMPTY, k3_ATTRIB_EMPTY, NULL, NULL, inds, NULL, NULL);
k3MdlAddMesh(this->mdl, mat, 0, this->capacity * 6);
k3MdlSetDebugName(this->mdl, "k3CPUQuadParticles");
free(inds);
this->positions = calloc(sizeof(*this->positions), this->capacity);
this->velocities = calloc(sizeof(*this->velocities), this->capacity);
this->sizes = calloc(sizeof(*this->sizes), this->capacity);
this->lifetimes = calloc(sizeof(*this->lifetimes), this->capacity);
}
static void random_cone_vector(float coneAngle, vec3 output) {
float minZ = cosf(coneAngle);
float z = randfloat() * (1 - minZ) + minZ;
float phi = randfloat() * 6.2831853;
output[0] = sqrtf(1 - z * z) * cosf(phi);
output[1] = z;
output[2] = sqrtf(1 - z * z) * sinf(phi);
}
static void regenerate_model(struct k3CPUQuadParticles *this, vec3 cameraRight, vec3 cameraUp, vec3 cameraFront) {
vec3 *vpos = calloc(sizeof(*vpos), 4 * this->capacity);
uint8_t *vcols = calloc(4, 4 * this->capacity);
vec2 *vuvs = calloc(sizeof(*vuvs), 4 * this->capacity);
uint8_t *vnrms = calloc(3, 4 * this->capacity);
vec3 halfRight, halfUp;
glm_vec3_scale(cameraRight, 0.5, halfRight);
glm_vec3_scale(cameraUp, 0.5, halfUp);
#pragma omp parallel for
for(size_t i = 0; i < this->count; i++) {
glm_vec3_copy(this->positions[i], vpos[i * 4 + 0]);
glm_vec3_copy(this->positions[i], vpos[i * 4 + 1]);
glm_vec3_copy(this->positions[i], vpos[i * 4 + 2]);
glm_vec3_copy(this->positions[i], vpos[i * 4 + 3]);
glm_vec3_muladds(halfRight, -this->sizes[i], vpos[i * 4 + 0]);
glm_vec3_muladds(halfUp, -this->sizes[i], vpos[i * 4 + 0]);
glm_vec3_muladds(halfRight, this->sizes[i], vpos[i * 4 + 1]);
glm_vec3_muladds(halfUp, -this->sizes[i], vpos[i * 4 + 1]);
glm_vec3_muladds(halfRight, this->sizes[i], vpos[i * 4 + 2]);
glm_vec3_muladds(halfUp, this->sizes[i], vpos[i * 4 + 2]);
glm_vec3_muladds(halfRight, -this->sizes[i], vpos[i * 4 + 3]);
glm_vec3_muladds(halfUp, this->sizes[i], vpos[i * 4 + 3]);
vec4 color;
glm_vec4_lerp(this->colorEnd, this->colorStart, this->lifetimes[i] / this->particleLifetime, color);
for(size_t c = 0; c < 16; c += 4) {
vcols[i * 16 + c + 0] = color[0] * 255;
vcols[i * 16 + c + 1] = color[1] * 255;
vcols[i * 16 + c + 2] = color[2] * 255;
vcols[i * 16 + c + 3] = color[3] * 255;
}
glm_vec2_copy((vec2) {0, 0}, vuvs[i * 4 + 0]);
glm_vec2_copy((vec2) {1, 0}, vuvs[i * 4 + 1]);
glm_vec2_copy((vec2) {1, 1}, vuvs[i * 4 + 2]);
glm_vec2_copy((vec2) {0, 1}, vuvs[i * 4 + 3]);
for(size_t c = 0; c < 12; c += 3) {
vnrms[i * 12 + c + 0] = (uint8_t) (cameraFront[0] * 127);
vnrms[i * 12 + c + 1] = (uint8_t) (cameraFront[1] * 127);
vnrms[i * 12 + c + 2] = (uint8_t) (cameraFront[2] * 127);
}
}
// This update the AABB
k3MdlUpdatePos(this->mdl, vpos);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, this->mdl->vstore->gl);
//glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, this->mdl->offV, sizeof(*vpos) * this->capacity * 4, vpos);
glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, this->mdl->offC, sizeof(*vcols) * this->capacity * 4 * 4, vcols);
glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, this->mdl->offU, sizeof(*vuvs) * this->capacity * 4, vuvs);
glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, this->mdl->offN, sizeof(*vnrms) * this->capacity * 4 * 3, vnrms);
this->mdl->meshes[0].idxNumber = this->count * 6;
free(vpos);
free(vcols);
free(vuvs);
free(vnrms);
}
static void copy_particle(struct k3CPUQuadParticles *this, size_t from, size_t to) {
glm_vec3_copy(this->positions[from], this->positions[to]);
glm_vec3_copy(this->velocities[from], this->velocities[to]);
this->sizes[to] = this->sizes[from];
this->lifetimes[to] = this->lifetimes[from];
}
void k3CPUQuadParticlesUpdate(struct k3CPUQuadParticles *this, float dt, vec3 cameraRight, vec3 cameraUp, vec3 cameraFront) {
size_t numGenerated = dt * this->emissionRate;
if((this->emissionLifetime -= dt) <= 0) {
this->emissionEnabled = false;
}
if(!this->emissionEnabled) numGenerated = 0;
if(this->count + numGenerated > this->capacity) {
numGenerated = this->capacity - this->count;
}
for(size_t i = this->count, j = 0; j < numGenerated; i++, j++, this->count++) {
glm_vec3_copy(this->origin, this->positions[i]);
random_cone_vector(this->emissionConeAngle, this->velocities[i]);
glm_vec3_scale(this->velocities[i], 2, this->velocities[i]);
this->sizes[i] = 1;
this->lifetimes[i] = this->particleLifetime;
}
vec3 accdt;
glm_vec3_scale(this->gravity, dt, accdt);
for(size_t i = 0; i < this->count;) {
glm_vec3_add(this->velocities[i], accdt, this->velocities[i]);
vec3 veldt;
glm_vec3_scale(this->velocities[i], dt, veldt);
glm_vec3_add(this->positions[i], veldt, this->positions[i]);
this->lifetimes[i] -= dt;
if(this->lifetimes[i] <= 0) {
copy_particle(this, --this->count, i);
} else {
i++;
}
}
regenerate_model(this, cameraRight, cameraUp, cameraFront);
}

32
src/k3particles.h Normal file
View File

@ -0,0 +1,32 @@
#pragma once
#include<cglm/vec3.h>
#include<stdbool.h>
#include"k3.h"
struct k3CPUQuadParticles {
size_t capacity;
size_t count;
vec3 *positions;
vec3 *velocities;
float *sizes;
float *lifetimes;
struct k3Mdl *mdl;
vec4 colorStart;
vec4 colorEnd;
vec3 origin;
vec3 gravity;
float emissionRate;
vec3 emissionConeDirection;
float emissionConeAngle;
float particleLifetime;
bool emissionEnabled;
float emissionLifetime;
};
void k3CPUQuadParticlesInit(struct k3CPUQuadParticles*, struct k3Mat*);
void k3CPUQuadParticlesUpdate(struct k3CPUQuadParticles*, float dt, vec3 cameraRight, vec3 cameraUp, vec3 cameraFront);