From ce7c4c4cc6f077ec0afd1f1e649a062fa7978d15 Mon Sep 17 00:00:00 2001 From: Zack Buhman Date: Thu, 19 Mar 2026 20:09:06 -0500 Subject: [PATCH] draw flames on torches --- Makefile | 1 + include/flame.h | 7 ++ include/view.h | 2 + minecraft/flame.data | Bin 0 -> 5120 bytes minecraft/gen/mc.py | 6 +- minecraft/grandlecturn/global.lights.vtx | Bin 2080 -> 2080 bytes minecraft/grandlecturn/region.-1.0.lights.vtx | Bin 528 -> 528 bytes minecraft/grandlecturn/region.0.-1.lights.vtx | Bin 32 -> 32 bytes minecraft/grandlecturn/region.0.0.lights.vtx | Bin 1520 -> 1520 bytes shader/flame.frag | 28 +++++ shader/flame.vert | 42 +++++++ shader/lighting.frag | 3 + shader/minecraft.vert | 2 +- src/flame.cpp | 106 ++++++++++++++++++ src/hud.cpp | 6 +- src/test.cpp | 14 ++- src/view.cpp | 6 +- 17 files changed, 211 insertions(+), 12 deletions(-) create mode 100644 include/flame.h create mode 100644 minecraft/flame.data create mode 100644 shader/flame.frag create mode 100644 shader/flame.vert create mode 100644 src/flame.cpp diff --git a/Makefile b/Makefile index 0aafa8a..bdb08ab 100644 --- a/Makefile +++ b/Makefile @@ -47,6 +47,7 @@ OBJS = \ src/collada/animate.o \ src/lua_api.o \ src/pixel_line_art.o \ + src/flame.o \ data/scenes/ship20/ship20.o \ data/scenes/noodle/noodle.o \ data/scenes/shadow_test/shadow_test.o \ diff --git a/include/flame.h b/include/flame.h new file mode 100644 index 0000000..b4b9a70 --- /dev/null +++ b/include/flame.h @@ -0,0 +1,7 @@ +#pragma once + +namespace flame { + void load_program(); + void load_texture(); + void draw(unsigned int light_uniform_buffer, int light_count); +} diff --git a/include/view.h b/include/view.h index 47c04ed..320737c 100644 --- a/include/view.h +++ b/include/view.h @@ -1,5 +1,7 @@ #pragma once +#include "directxmath/directxmath.h" + namespace view { struct view_state { // positions diff --git a/minecraft/flame.data b/minecraft/flame.data new file mode 100644 index 0000000000000000000000000000000000000000..11beae73e1bade35cddb780f7165a7e6a66dadb0 GIT binary patch literal 5120 zcmeHKzb^zq6yE=V;y9Pfp>g~N3eD+IXhfqxgkBUntw!_|Iy$XHBB3EVR3r)&5}j5) z?|ZxNtj*lr>}(>rxJxE8^X7f;*Y14xHj4ho@4gtwSpJ2yOtyVB7 zd)n>&{;mQ=cnOYjs9^kvQ}o%3U`5d2K0Pc!>3&4f27n4Pq28 zTwwl#@Z#U_KECOL&%Vi@f+x;$Fk7yfdaQX9PaY%>%m0|lgj-#>v`n8lxl=I8{|TOU zyyE}704^KxF#h8{^&HdwTlKdt%M^?|Z$4-RX z{-pkCyvSYpS()-4`Hw^DN8Zv|cr~l*+N&mfb)PkGzEfBEg3`F`fP`DrzG3;%rsAYGB# literal 0 HcmV?d00001 diff --git a/minecraft/gen/mc.py b/minecraft/gen/mc.py index b4bf43e..f22459c 100644 --- a/minecraft/gen/mc.py +++ b/minecraft/gen/mc.py @@ -113,8 +113,8 @@ def pack_instance_data(position, block_id, block_data, texture_id): block_id, block_data, texture_id, special) return packed -def pack_light_data(position, block_id): - packed = struct.pack("7*N6h)tk^BMgBe}JW|(;qMhG{^@S7#0Lr0g*veYy~YU3W$P&l9m=NvI
    $@}69>ybbCcf}9m zf3z;I#_!^X@yFTp{ER>PPt0S^?Q_8RKU(in)0mq_`(ga&MTgPX);s526+euBXFc+# zIbVt&#(!&FUX8EC597bHp7BTjL-E7-pR9Mz|55xf{=qu=hvJ9vzgX{_|62TSX7c`; zXSwfv&iU}k`WN=<|F|J0C`;zgMmYM!zcaVRZVJ`7k>DiVmaG zpXe|;{fG{uA4+{NI{gQ;e`~)l`=|GgX8%5$`@E<1yUwxAYtFYDGv^n2;>h~`e4pxz>`T@E zXy*I9N0&YQd-IsreU7DBn#TBT%0q`Yi^;#W9{H1Bb!dI?!}zIh$oZ=8r=O?qs;!sv zg8KDc^_jo1A00*~f0i}0`6p)bI4@wWYnzWwK05j8zcyce($>Fr-rn_InpwY9VRY6D S9Y()*9{sd5(@*Tjr2hj2)`$WC literal 2080 zcmZ9Mtxf|$6h8Y5RuFW9szOyXRaN|0)T#!#K@b!{ zG)TgH+3zINyQVokcfY+mb9Yi&)hUh7QmXUsMgFZuJ$Hd;`G5H<`;;$n6Y62=clNdO zzCt}reae~YN0;&k=B_?w>QC)!`(dbuslN;!#!o}uO{j;dZ|v#!KGeh1KZN|pP!ChT z4*Aj_n{u9{v+gnhx$Xv`!!d2-?g77=iXoQr@iJx9@bolxgQVX z|9pQF^4pN7IVtls7iB)4d_4J@L)FuNW!8IXUMGR)X7yEi7_avzJ&b4m$%pa7{G9TB z7|(o9+z*Urp7AiA`NhL{<`oa)nNK{7XCCn|ejnZs#xs8~`#1IbvVVH-a{sQ)t9))~ z{cg>iZ+B+SFFfZHp7RGk%=alz>!jzg-mv;ApVyel<9r}*9X#JRz73x56MqPv{ua;2 z{lP!4eSay>rTX8Q`F=O}eBjA1&7IHRvAH&PFMS>!o{jqZ7xpP%%2OX&AL?Q1>96(u z?|z=XtFm9t3;Nf0B|mdL9>$Yj!x(%X~cfc=DM)@|jQaG$+biy5G5( Y^_v97vtD=@zi}S(Ha0Ua>__ka0WrgC9{>OV diff --git a/minecraft/grandlecturn/region.-1.0.lights.vtx b/minecraft/grandlecturn/region.-1.0.lights.vtx index 336460c3800001d317fc972fa883de2a47974e60..e69e3b2f2310b6d5a3658cf3f5adbd214b86cdb6 100644 GIT binary patch literal 528 zcmZ9Ju?>Py6h-dAM;I5Q}zv&`JP@&4-Yr)C40q{=hJgN4>Qkx zpLjp-H~PH0nSBTM;jg*zJRZi=FRFEaW2Uza?qXkfXQqdT@$@h5jo#;IrgwI)wZEGA aey2ai^S$sezIX4D&x`$Q^UcivSo{y>jdGa) literal 528 zcmZ9Jp%KDB5CE?L%AkN0fCwn$3nV2VA`uZ$41xkuMr0h&0TLOBqy#>bUBSm>cJ_XA z^NILABhoG+wh{aM9^nce^m%0VJv`M5C%A^E$I{cw4ZRK@`I&p&{eNC|-u+&jfAhU7 z?7m~cAGl#&FYE1Bd6#(~`zzS~y5J3LPcQ53pP9FRD0(NjXWtg~{*DFfy)V72AL!jU LpWqAZ|5*GFxISs> diff --git a/minecraft/grandlecturn/region.0.-1.lights.vtx b/minecraft/grandlecturn/region.0.-1.lights.vtx index 44b0370f7dca200e06a0a8deef0125277edd14aa..e639965f6cf2ebf6c578d13408481921c0f306bb 100644 GIT binary patch literal 32 bcmZQzuyJBw2ms=S{XlHbzz~AOXK(-jS#1Tj literal 32 ZcmZQzuyJBw2ms=S{R|8qKsp444**s|1c diff --git a/minecraft/grandlecturn/region.0.0.lights.vtx b/minecraft/grandlecturn/region.0.0.lights.vtx index aef2a0fc34cc4b7184caccc45f7c8c8c8883a80e..bb5c98ebae2c09ebe3a55c8a75a052d48c4e8e46 100644 GIT binary patch literal 1520 zcmZ9MElvYb6h?2*EQ1Af0Zkan1`x;!7EpmiAess?g0KM8C{z^+#R5=O)sqT>LN!Rj z{o3y&kK5#>bLM>S&d=-Ystgp-|ais|0sEw{KlU8+meUL@9aC*|1SBxnR-9wNv`{lb51V3 zzR8jNX|Fz!ht(Hi=Hp@fujlu!NBvEyuRdw(t1raV!^3#$tM4N7)SsC3T>5_IIfnG` zy;=9wdKjDDdr+J-rK8&Zo=e{2pzbf-#JpIdj7*D_AVLbhbhw=0y9>#CW z{lR$p4`%%qeqPp3&mGPB-I<3xr}y`eW0}Y7Z;xj7FMQ;A%oyYTi<2S|cO8xym{|BS|548XQ literal 1520 zcmZ9MAx;B97=-zjBPOSwENrMB;j_C1{b^Dg+N{VsoPU)o1JMII*qW?$X+6?vF^ zX`Nb+KI%U<@9H6wKe4a&W8`7-SK(p&GWzZ#50jtlS?^QiVe-$>{}Oqa{5twyBM*~* zkN!>MVe)hIZzB(r|A_wY$nT@?$2_!s*M1(IdwuUSaTufdOVE(_4QNq&(Wtj z>H9SoeLtRlJpGzOt<8 diff --git a/shader/flame.frag b/shader/flame.frag new file mode 100644 index 0000000..dfced8f --- /dev/null +++ b/shader/flame.frag @@ -0,0 +1,28 @@ +#version 430 core + +in vec4 PixelTexture; + +layout (location = 0, binding = 0) uniform sampler2D FlameSampler; +layout (location = 1) uniform int Frame; + +layout (location = 0) out vec3 Position; +layout (location = 1) out vec4 Normal; +layout (location = 2) out vec3 Color; +layout (location = 3) out vec4 Block; + +const float frames = 1.0 / 4.0; + +void main() +{ + vec2 coord = vec2(PixelTexture.x, 1.0 - PixelTexture.y) * vec2(1, frames) + vec2(0, frames * Frame); + vec4 texture_color = texture(FlameSampler, coord); + if (texture_color.w == 0) { + discard; + return; + } + + Position = vec3(0, 0, 0); + Normal = vec4(0, 0, 0, 0); + Color = vec3(texture_color.xyz); + Block = vec4(-1, -1, -1, -1); +} diff --git a/shader/flame.vert b/shader/flame.vert new file mode 100644 index 0000000..9aafc7f --- /dev/null +++ b/shader/flame.vert @@ -0,0 +1,42 @@ +#version 430 core + +layout (location = 2) uniform mat4 Transform; +layout (location = 3) uniform vec3 Eye; + +const vec2 vtx[4] = vec2[](vec2(-1.0, 1.0), // tl + vec2( 1.0, 1.0), // tr + vec2( 1.0, -1.0), // br + vec2(-1.0, -1.0)); // bl + +out vec4 PixelTexture; + +const vec3 up = vec3(0, 0, 1); + +layout (std140, binding = 0) uniform Lights +{ + vec4 light[256]; +}; + +void main() +{ + vec2 vertex = vtx[gl_VertexID]; + + PixelTexture = vec4(vertex * 0.5 + 0.5, 0, 0); + + vec4 light_instance = light[gl_InstanceID]; + bool is_candle = light_instance.w == 5; + float z_offset = is_candle ? 0.17 : 0.5; + float y_offset = float(light_instance.w == 4) - float(light_instance.w == 3); + float x_offset = float(light_instance.w == 2) - float(light_instance.w == 1); + + float size = is_candle ? 0.1 : 0.25; + + vec3 global_position = light_instance.xzy + vec3(x_offset * 0.2, y_offset * 0.2, z_offset); + vec3 direction = global_position - Eye; + vec3 normal = normalize(vec3(direction.x, direction.y, 0)); + vec3 right = normalize(cross(normal, up)); + + vec3 position = global_position + (vertex.x * right + vertex.y * up) * size; + + gl_Position = Transform * vec4(position, 1); +} diff --git a/shader/lighting.frag b/shader/lighting.frag index 71e1f57..a724e82 100644 --- a/shader/lighting.frag +++ b/shader/lighting.frag @@ -41,5 +41,8 @@ void main() //vec3 light_direction = normalize(Eye.xyz - position.xyz); //float diffuse = max(dot(normal.xyz, light_direction), 0.0); + if (normal == vec4(0, 0, 0, 0)) + out_color = color.xyz; + Color = vec4(out_color, 1.0); } diff --git a/shader/minecraft.vert b/shader/minecraft.vert index abdb1d8..a56e9d6 100644 --- a/shader/minecraft.vert +++ b/shader/minecraft.vert @@ -46,7 +46,7 @@ void main() vs_out.Position = position; vs_out.BlockPosition = BlockPosition; - vs_out.Normal = Normal; + vs_out.Normal = orientation(Normal); vs_out.Texture = Texture; vs_out.BlockID = BlockID; vs_out.Data = Data; diff --git a/src/flame.cpp b/src/flame.cpp new file mode 100644 index 0000000..bf439f3 --- /dev/null +++ b/src/flame.cpp @@ -0,0 +1,106 @@ +#include +#include + +#include "glad/gl.h" + +#include "file.h" +#include "opengl.h" + +#include "flame.h" +#include "view.h" + +extern unsigned int quad_index_buffer; +extern unsigned int empty_vertex_array_object; + +namespace flame { + static unsigned int program; + static unsigned int flame_texture; + static unsigned int vertex_array_object; + + const int per_instance_size = 4 * (sizeof (float)); + + struct layout { + struct { + unsigned int flame_sampler; + unsigned int frame; + unsigned int transform; + unsigned int eye; + } uniform; + struct { + unsigned int lights; + } binding; + }; + + layout layout = { + .uniform = { + .flame_sampler = 0, + .frame = 1, + .transform = 2, + .eye = 3, + }, + .binding = { + .lights = 0, + }, + }; + + void load_program() + { + program = compile_from_files("shader/flame.vert", + nullptr, + "shader/flame.frag"); + } + + void load_texture() + { + unsigned int texture; + glGenTextures(1, &texture); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + int texture_data_size; + void * texture_data = read_file("minecraft/flame.data", &texture_data_size); + assert(texture_data != nullptr); + + int width = 16; + int height = 80; + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, texture_data); + + free(texture_data); + + glBindTexture(GL_TEXTURE_2D, 0); + + flame_texture = texture; + } + + static int frame = 0; + + void draw(unsigned int light_uniform_buffer, int light_count) + { + glUseProgram(program); + + glBlendFunc(GL_ONE, GL_ZERO); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_GREATER); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, flame_texture); + glUniform1i(layout.uniform.frame, (frame / 20) % 4); + frame++; + + glUniformMatrix4fv(layout.uniform.transform, 1, false, (float *)&view::state.float_transform); + XMFLOAT3 eye; + XMStoreFloat3(&eye, view::state.eye); + glUniform3fv(layout.uniform.eye, 1, (float *)&eye); + + glBindBufferBase(GL_UNIFORM_BUFFER, layout.binding.lights, light_uniform_buffer); + + glBindVertexArray(empty_vertex_array_object); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quad_index_buffer); + + glDrawElementsInstanced(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, (void *)0, light_count); + } +} diff --git a/src/hud.cpp b/src/hud.cpp index c6f3eb5..8db6edc 100644 --- a/src/hud.cpp +++ b/src/hud.cpp @@ -101,9 +101,9 @@ namespace hud { y += ter_best.desc->glyph_height; */ - y = draw_vector(ter_best, buf, y, "eye", view::state.eye); - y = draw_vector(ter_best, buf, y, "at", view::state.at); - y = draw_vector(ter_best, buf, y, "forward", view::state.forward); + y = draw_vector(ter_best, buf, y, "eye", XMVectorSetW(view::state.eye, 0)); + y = draw_vector(ter_best, buf, y, "at", XMVectorSetW(view::state.at, 0)); + y = draw_vector(ter_best, buf, y, "forward", XMVectorSetW(view::state.forward, 0)); labeled_value(buf, "pitch: ", "%.4f", view::state.pitch); font::draw_string(ter_best, buf, 10, y); diff --git a/src/test.cpp b/src/test.cpp index 37df43d..68f08b4 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -25,6 +25,7 @@ #include "collada/types.h" #include "collada/instance_types.h" #include "pixel_line_art.h" +#include "flame.h" #include "world/entry_table.h" #include "world/world.h" @@ -195,6 +196,13 @@ void load(const char * source_path) //node_at = scene_state.find_node_by_name("Camera001.Target"); //assert(node_at != nullptr); + + ////////////////////////////////////////////////////////////////////// + // flame + ////////////////////////////////////////////////////////////////////// + + flame::load_program(); + flame::load_texture(); } void update_keyboard(int up, int down, int left, int right, @@ -418,13 +426,15 @@ void draw() glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); minecraft::draw(); - //draw_line(); + non_block::draw(); + flame::draw(minecraft::current_world->light_uniform_buffer, + minecraft::current_world->light_count); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - lighting::draw(minecraft::current_world->light_uniform_buffer, minecraft::current_world->light_count); //draw_quad(); diff --git a/src/view.cpp b/src/view.cpp index 8e539bf..84ea569 100644 --- a/src/view.cpp +++ b/src/view.cpp @@ -97,14 +97,14 @@ namespace view { state.up = XMVectorSet(0.0f, 0.0f, 1.0f, 0.0f); state.fov = 1.5; - state.pitch = -0.7; + state.pitch = -0.5520; - state.forward = XMVector3Normalize(XMVectorSet(-0.64, 0.77, 0, 0)); + state.forward = XMVector3Normalize(XMVectorSet(0.66, -0.75, 0, 0)); state.normal = get_normal(); // on forward change state.direction = get_direction(); // on forward/normal/pitch change // position - state.eye = XMVectorSet(-45.5f, 43.25f, 63.0f, 1); + state.eye = XMVectorSet(4.71f, 65.30, 57.92, 1); state.at = state.eye + state.direction * at_distance; //state.at = XMVectorSet(0, 0, 0, 1); //state.eye = XMVectorSet(0, -100, 0, 1);