summaryrefslogtreecommitdiff
path: root/kon
diff options
context:
space:
mode:
authorKonAki <konaki@anokonaki.org>2026-10-10 18:37:22 +0800
committerKonAki <konaki@anokonaki.org>2026-10-10 18:37:22 +0800
commit5f60191923b4e4e93dacda497c7e24a79742fb8e (patch)
tree4f30ca8317b986dc1006bae0bb45ff8ba30b91a4 /kon
parent0bfbeec1abdf937e3658fe07f75de22fdb2e7bea (diff)
add billboard sprites
Diffstat (limited to 'kon')
-rw-r--r--kon/konsofren.h80
1 files changed, 79 insertions, 1 deletions
diff --git a/kon/konsofren.h b/kon/konsofren.h
index e89915a..2c396b1 100644
--- a/kon/konsofren.h
+++ b/kon/konsofren.h
@@ -213,6 +213,14 @@ kon_light_t kon_lightDefault(void);
void kon_drawTriangle3D(kon_framebuffer_t *fb, kon_mat4_t viewProjection, kon_vertex_t a, kon_vertex_t b, kon_vertex_t c, const kon_material_t *material, const kon_light_t *light, kon_renderFlags_t flags);
void kon_drawMesh(kon_framebuffer_t *fb, kon_mat4_t viewProjection, kon_mat4_t model, const kon_mesh_t *mesh, const kon_material_t *material, const kon_light_t *light, kon_renderFlags_t flags);
+/* a flat sprite that always faces the camera, for things you can't model. position is the middle of its feet in the world,
+ height and width are in world units, src is the part of image to use (the whole image if it is 0 wide).
+ brightness scales the colors, 1 is as drawn, so you can darken it with distance yourself.
+ pixels with alpha 128 or more are solid, hide behind nearer things with KON_RENDER_DEPTH and write depth, the rest blend on top.
+ returns 0 if nothing was drawn because it is behind the camera */
+int kon_drawBillboard(kon_framebuffer_t *fb, kon_mat4_t viewProjection, kon_vec3_t position, float width, float height,
+ const kon_image *image, kon_rect_t src, float brightness, kon_renderFlags_t flags);
+
/* cube from -1 to 1 with every face mapped to the whole texture */
extern const kon_mesh_t kon_cubeMesh;
@@ -1417,6 +1425,76 @@ static const uint16_t kon_cubeIndices_[36] = {
const kon_mesh_t kon_cubeMesh = { kon_cubeVertices_, kon_cubeIndices_, 12 };
-#endif /* end of KONSOFREN_IMPLEMENTATION */
+int kon_drawBillboard(kon_framebuffer_t *fb, kon_mat4_t viewProjection, kon_vec3_t position, float width, float height,
+ const kon_image *image, kon_rect_t src, float brightness, kon_renderFlags_t flags) {
+ if (!fb || !image || width <= 0.0f || height <= 0.0f) return 0;
+ if (src.w <= 0 || src.h <= 0) src = KON_RECT(0, 0, image->width, image->height);
+ if (src.x < 0 || src.y < 0 || src.x + src.w > image->width || src.y + src.h > image->height) return 0;
+
+ kon_vec4_t foot = kon_mat4MulVec4(viewProjection, KON_VEC4(position.x, position.y, position.z, 1));
+ kon_vec4_t head = kon_mat4MulVec4(viewProjection, KON_VEC4(position.x, position.y + height, position.z, 1));
+ kon_vec4_t mid = kon_mat4MulVec4(viewProjection, KON_VEC4(position.x, position.y + height * 0.5f, position.z, 1));
+
+ if (foot.w <= 0.0f || head.w <= 0.0f || mid.w <= 0.0f || mid.z < -mid.w) return 0;
+
+ int useDepth = (flags & KON_RENDER_DEPTH) != 0;
+ if (useDepth && !fb->depth) {
+ fb->depth = malloc((size_t)fb->width * (size_t)fb->height * sizeof(float));
+ if (!fb->depth) return 0;
+ for (int i = 0; i < fb->width * fb->height; i++) fb->depth[i] = 1.0f;
+ }
+
+ kon_vec2_t bottom = kon_clipToScreen_(fb, foot);
+ kon_vec2_t top = kon_clipToScreen_(fb, head);
+ float heightPx = bottom.y - top.y;
+ float widthPx = heightPx * width / height;
+ if (heightPx < 1.0f || widthPx < 1.0f) return 0;
+
+ float left = bottom.x - widthPx * 0.5f;
+ int x0 = (int)ceilf(left - 0.5f), x1 = (int)floorf(left + widthPx - 0.5f);
+ int y0 = (int)ceilf(top.y - 0.5f), y1 = (int)floorf(bottom.y - 0.5f);
+ float z = mid.z / mid.w;
+ if (brightness < 0.0f) brightness = 0.0f;
+ int scale = (int)(brightness * 256.0f);
+
+ if (x0 < 0) x0 = 0;
+ if (y0 < 0) y0 = 0;
+ if (x1 > fb->width - 1) x1 = fb->width - 1;
+ if (y1 > fb->height - 1) y1 = fb->height - 1;
+
+ for (int y = y0; y <= y1; y++) {
+ int sy = src.y + (int)(((float)y + 0.5f - top.y) / heightPx * (float)src.h);
+ if (sy < src.y) sy = src.y;
+ if (sy > src.y + src.h - 1) sy = src.y + src.h - 1;
+ for (int x = x0; x <= x1; x++) {
+ size_t index = (size_t)y * (size_t)fb->width + (size_t)x;
+ if (useDepth && z >= fb->depth[index]) continue;
+
+ int sx = src.x + (int)(((float)x + 0.5f - left) / widthPx * (float)src.w);
+ if (sx < src.x) sx = src.x;
+ if (sx > src.x + src.w - 1) sx = src.x + src.w - 1;
+
+ uint32_t texel = image->data[(size_t)sy * (size_t)image->width + (size_t)sx];
+ uint32_t alpha = texel >> 24;
+ if (alpha == 0) continue;
+
+ if (scale != 256) {
+ uint32_t r = ((texel >> 16) & 0xFF) * (uint32_t)scale >> 8, g = ((texel >> 8) & 0xFF) * (uint32_t)scale >> 8, b = (texel & 0xFF) * (uint32_t)scale >> 8;
+ texel = (alpha << 24) | ((r > 255 ? 255 : r) << 16) | ((g > 255 ? 255 : g) << 8) | (b > 255 ? 255 : b);
+ }
+
+ if (alpha >= 128) {
+ fb->data[index] = texel | 0xFF000000u;
+ if (useDepth) fb->depth[index] = z;
+ } else {
+ fb->data[index] = kon_blendColor(fb->data[index], texel);
+ }
+ }
+ }
+ return 1;
+}
+
+
+#endif /* end of KONSOFREN_IMPLEMENTATION */
#endif