diff options
| author | KonAki <konaki@anokonaki.org> | 2026-10-10 18:37:22 +0800 |
|---|---|---|
| committer | KonAki <konaki@anokonaki.org> | 2026-10-10 18:37:22 +0800 |
| commit | 5f60191923b4e4e93dacda497c7e24a79742fb8e (patch) | |
| tree | 4f30ca8317b986dc1006bae0bb45ff8ba30b91a4 /kon | |
| parent | 0bfbeec1abdf937e3658fe07f75de22fdb2e7bea (diff) | |
add billboard sprites
Diffstat (limited to 'kon')
| -rw-r--r-- | kon/konsofren.h | 80 |
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 |
