From f5e6f995d8abc254ddd9ddee310686881e55803f Mon Sep 17 00:00:00 2001 From: welisson Date: Thu, 26 Mar 2026 01:40:10 -0300 Subject: [PATCH 1/3] feat(core): implement Fortress config, OIT transparency, and robust nil-safety Enhance the rendering engine with robust nil-safety checks across the core pipeline, complete OIT transparency support for UI properties, and establish a dedicated configuration profile. - Config & Identity: - config.json: Established the default `FortressVision` profile, introducing parameters for custom networking (dfhack_port: 5000), threaded generation (mesher_threads: 4), and extended camera/FOV controls. - Shaders & UI Transparency: - ui_nine.frag: Refactored background layout to use `mix(fragBGColor, texColor * fragColor, texColor.a)` for true alpha blending. - inc_fragment_oit_block.inl: Removed aggressive `discard` logic to prevent premature clipping of non-OIT surfaces. - panel.go & css_background_color.go: Decoupled BgColor from FgColor using native `SetBGColor()` and dynamically assigned `TextureSquare`. - css_left.go: Upgraded size handling with `helpers.NumFromLength` for accurate unit (`px`) parsing. - System-Wide Nil-Safety (Crash Prevention): - font.go: Fixed `.SelectRoot()` validation and implemented strict pointer checks for textures during `initFont`, `createLetterMesh`, and `RenderMeshes` to prevent nil-dereferences. - gpu_device_drawing_vulkan.go: Hardened execution queues by returning safely from `blitTargetsImpl` and `combineTargets` if render images or pass groups are empty. - entity.go & transform.go: Protected initializations by checking allocation states prior to executing matrix setups. - material.go: Fortified `CreateInstance` caching strategy to gracefully bypass missing textures without halting the engine. --- src/config.json | 23 ++++++++++++++++++ .../renderer/spv/ui_nine.frag.spv | Bin 8552 -> 8608 bytes .../spv/ui_transparent_ui_nine.frag.spv | Bin 9420 -> 9596 bytes .../renderer/src/inc_fragment_oit_block.inl | 3 --- .../editor_content/renderer/src/ui_nine.frag | 5 +++- src/engine/entity.go | 4 ++- .../css/properties/css_background_color.go | 4 +++ .../ui/markup/css/properties/css_left.go | 3 ++- src/engine/ui/panel.go | 11 ++++++--- src/matrix/transform.go | 3 +++ src/rendering/font.go | 21 +++++++++++++--- src/rendering/gpu_device_drawing_vulkan.go | 16 ++++++++++++ src/rendering/material.go | 4 +++ 13 files changed, 84 insertions(+), 13 deletions(-) create mode 100644 src/config.json diff --git a/src/config.json b/src/config.json new file mode 100644 index 000000000..04d5e1dd8 --- /dev/null +++ b/src/config.json @@ -0,0 +1,23 @@ +{ + "window_width": 1280, + "window_height": 720, + "window_title": "FortressVision", + "fullscreen": false, + "target_fps": 60, + "dfhack_host": "localhost", + "dfhack_port": 5000, + "server_url": "ws://127.0.0.1:8080/ws", + "draw_distance": 10, + "view_levels": 5, + "mesher_threads": 4, + "fov": 60, + "draw_range_down": 5, + "draw_range_up": 1, + "draw_range_side": 4, + "camera_speed": 10, + "camera_sensitivity": 0.3, + "zoom_speed": 5, + "show_debug_info": true, + "show_grid": false, + "wireframe_mode": false +} \ No newline at end of file diff --git a/src/editor/editor_embedded_content/editor_content/renderer/spv/ui_nine.frag.spv b/src/editor/editor_embedded_content/editor_content/renderer/spv/ui_nine.frag.spv index b47162aeaca4fdaceee37c506184de63c20f6124..15ee140a44b5a03ab9f45b5ffe480a9f5ed171aa 100644 GIT binary patch literal 8608 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 literal 8552 zcmZ9Q2Y_5v6^387rIG?k2qh$;h87@%8X%n|up}Xy5JFovJCkH&cW2qzgar!*fnWoB zM}vx86f2^jK~WSH6%|2I>;-$HNb&pLyaPA$$l?CyKjoft?|t`8(%f{w$SfO?jm>t; zHjc>hXHwP#lfkv+V|zzu$DG~8p$Y)sbP(bc|kWqZf6uAYH`uG-KQz2&anN`JYl zR2eMy3{|$4^&6G=Rr-5+x0F(cRy@+WTJayp90P;Zo^q|WXt+|Fz44g&^*`|Xk7n$t zRtHOkK8A4vYI(43AB#TNJyfaY_R-l?_3D=XQn|FaI^4PR^x0cC&YfSEdGn2F%l5#h zr`$hO9$ZzeRfuJkoNF?Ct#T!u)b%NhgWaXdmYRBVHVwVE+@JgIEAH&+?#0{K{n0l( zuQz808(XgBaTV}oh<;Zy_E!h{)}E{0mha8R!R}3K%EL>FHgiAsOMdNEbF^hQpx2zP zw4#VMepJ4v`mJCuI(sWU6&?!oT9+@~em(V*Yl zpx@e{-`1diszLuOdVl$HTg+>}lW|M`x^iXH=Am+FNwv2+nAf%weyBX$+1)qLTi?&t z{A^lp-K*Ziz4(xt34^)QP(E6MYpci{wu=U;_%HvAq<)z`g zHs1s1Tb!RMSE)W{3+KLm&Z6E?`TO#MZ_d{`)~sGk3Uak%V~HtMYD0B>Wd3fw;J>;! zNp;`d@J;t%LW4eO7riZ;j9%-jR);p1ON)93Hh1T9wq=Xq>vb+==I;KU;yfLz`;B2$voo=-o9p{ zPhiw0uYDq;IjmdzBu4G>=G~o9yK##4!|{`wj%3-(7lb||*RzF_seUea3VvrW&aCr! zoci)aH%~{RYhRCyK&bI}=^|y$!nGhw#^ae}nG#A?)t0--Xc4@BJUTqj!7g z=JWneb?@)cjraZz-O>9y)xEbvH{X&5eOaQL&-*(39lftp-Md=-RQAhv^jgICQ{6Lg zJ+d8HiI?_Q7M#2f;k@Imx$RY8RTlhm#MVcN9M9{O^R5 zkKFG98z*m^Yx{1*UedVt6uf=<4ycQFFj}AZW=8X!L>B9QFXHcB{dXYRqqg^fT^sM- z===R(`SAGw*dA{|!smlv`KbRxVB_Q??!(~5xR1cehtEd~pLG6@!O2JNkAsbqH_o;B z1mfDHai1)B*TDB(U3?p(^@)AY%@=F)X|QXfzkb?RF^6aNGl*-akFhrtoNM42{~WR% zi8KEBf|K|0jO%Nz+Y#d<_6y)N_6~G8r3vwTUt84iMX>SuY%91gft^pAwRvC3ncr{D z-N?>DcmA&;`m7_~+P{YABOf(=9efYsy!w0t(NEqvyaVOJ|C?afIAb^Z=`Mj`IGKHo+3(`JtQ3NCVd5B(Y>^zVbs8Ef_fus-s}-_Ix){yzkp zGxQ&U^@%&_$6$TrjemeqPW%w#!$_RPpMs5Dj+n2F@n?v9ob#W9A3;)|U!cq1PS0~0 zA4QClf1G)K#pv@CIUi;GCGu;;Z>2b`yfDKw_y3G?RQ{nOMQNi zE+4i10c@PSwf%|F$J(_05&1LX{NkwXF|f7iuT7t*?JwZQb^9xveE9qg?7pV!_IGso zsQ({e>re0Qf1=B~pYF%M5aZnATO2+4+t8lE=9uBOBJ)|D@{2t(`#F@`H?fUsUOPe`7 zCsV+32Ug)&);2gv5(Wh?qhm>_kxp;@7vyBIdezc zKIq1!XL?^a`G}himNPEu*$>@(+RQZ)+YGSlnbt59PJUbQe#`>P86R=`gCow~)b)!R z4**+ZdX5i-laCq?0?Vbn9gJ>VdbSUNlaILBV7aunL($ErEowXrY>jCRhr`K7Z%2UT zjE}e@!4YR~>iR`*bHI)Jb`+d^_#9pMqoP5kZ7i^q-^mZ&*KGwuNnuqSWPWR|I zIQiJy?*@SUv89Gr(~tw1d^- zPFN21PB5Qw+Rd?;QCr*zXMyG7PUryhXO}xcpXl#w@O13@neR+SxyZK?EEoQ(!1~MU zzk*RNdN>DsC-%@+qqh`x@2F029>d%2-MR){E){Fnwicd0*&M{Y=F;DJokM%nc`kS| z5__}`tWSDQ&V!TpcbB!S2OB3Jap!|0&Uw`J3;P9$J?6d;PCkBrTnv`G2=Tji38RnS zHEmspoZmI^({w48i*+l3t-~66!0ON7cOv4yNm&0llk$v0PQcH1b992oBzP>?Id)_8 z-zoH+hB$`4Z-c&ngMM(KN8fv)J4fhygPkYVcOS4m^7i3;a^b%pST6i$g5}bCbQbz_ zWF}$`Ym|!|2Y{_1avTVjOLH8AZVqkcurImDaR}IT3w<`&y5h_q3f4zHd=3MfC-lR? z))x285nz4fqqZZ#=F%2%o^9*jR_y6fVEyE+)isw3|D(Zat$ypRHO}6#V1490d&e>Q zc=oi-Lry|G3*yLoJUGpJ0=jz{{hSEaM?UJ3^LLeVn9sbC?__YAZ$5gurl+90C-U)o z@KmsI+N^y6qmQ*~I}MR@E^*AY5bRvBUyH!zUWkOxVsP5e5_J9KBW@|!IBn6RoIS?8 zj%m;K9&;=MyI;@ZFHYZ8?{1&n5#QAbjC&+_a)Bo?PC@K(PsaE+#TDT6UF}MAb5#mG z?x{1;&A%Lp`pyE&$9kU)mh-ow>%EH6$Mx2>66r*&MQk4L<#P~gjK52-E;xCgX2kWg zW^JyQxy@s~$g>8V=2?p_r;I?FkjSGg&0{|EjAuL-ajxin9oW3~eopaTod?#>$DI1e zMa=nNW7Z=Pa{<^GYcWP2xrn(C>>3*rYkUz{KHkTR!E&+2-C&>mTfle;QbJrCapc|r zjvgX+51hQUxc};|i8+loPsEqO(NDy0gp-eW^)%i*;kya!9FcD`IO;K8T_643gBKyL z#W=o9>X#y}TMMEs{CdHjb>BJt`Vf8PW3GO1%oX)i;pF4rVFti*%C;icWnlAZvqs+u zxyU;RmP_A>8oGS=3>7}`nm7X`!Lw~qlYUB zyFSLd9#{<3Od_y)eo72)ZXa4*-EwkG?`uf_c1DAG{ z+qx<}<+f6#zuYlUSy9$+MB-QJ>F63Pr4EgFr1dr8-^?6+{k6>jmEPRGRknkA?_f`{>Dx2*x0fn|RrO)nPUv0bp4@l5xV59b z3vXlhL|^)bJ}leU*m5G#y>_txp3t<%4N-cw#_i+K%SWgM`-u3mPn#%-!`t?k`?UA4Vz$~MCd z_AD+}mMtGBmul|z;JLMjnRDLcJjZg8RKLgIN|o+%Pn9jr_pBj%94_6{C(!#UtJrw! zXv&^MFU~^2*4F5mJg!upTUwR-J%^sxke@wQv(`s5??>%CMr|YVck4~xoUgW=xo{>a z$kmXIBc@cT4%GDF`Fr@L|H9(L)qJ-3$s(3`UH=+*At-ht)i(u}UYL zP1&jNwK``pb9+xmUVlS&+7|H*+39uq{4MmxY$1B3r>ES1dVBTaJooVIEV!6+X1+eP zneEm4rN6wQ+}>5|ryG4`zQgKG`Q5XutL@}`D{HSgd}Dqe^$rfi_GWX)wTAh2N4yi% zeMi*21Ju3e)V(9rJ^Sk3W9pk2CnA*ahVFaoquq0+f7m@^>OR^%1M1#k>fWL1jfL)g zs_w0<-QLwBpZBb_Zd=&BbG7@7qe|0*{}Bb23)zu* zw<&n@O|Z_wF0XxbVYd(MV+y;xdB+xZ;}q=&;^(}(Az60yiJ|Y2>)G_}sD27~d;Cse zoKWNQIQ6-SZl0Dz*S-W9f>_VB1-9O4h3?*m{rUo1uXc6Mg7)xVSLo(jUts4Af6s>g z_Tl_@A+A^G8^P}5&YZRf8I9FHeL~+A&xbJR=X_r)oaCHOeN(~3{Ewg;AG&8v-SvBl zaYCV+?`g36D{p;{&muk%@7Xjy?9bQfucJqQ#*f0;{&phY)?n8%&cin7=8`wRx@TQ3 z=JTwpTVDsjT&|IJb^~f(r?;U&Xv7z^&6FnpJD*DJL_Bt-~!Q4K5dgv?BCp_?A zOU&=Pq)XVZKwJyH3BSPWSto#`}E@-TD2#hVHxR_chfwC%XQR)#;Dd z>3$OVB(2H=p0ARQDSd zy7~M@h3@D#D%JfyrMllB_2b!3@0jZl?-zB?$c@Mi$b7uC-&An&K7?~eHRiUP!N%(o z@wb4}_*>EC6wj7t>c%4eHn2JLIRT4jOKt}C^^CV8>k#ix^LtM3K;*;ctzhpn@38QB z8(7|XjCng^oP5N+16&{XPB{7Sc~{|+&i`&W`N;hquyOLnxwh{^>?MtRf5F?Q?})m1 z1Eckc?`AaLF=Vms4bOQQHT>u8rT0==(!p`SAHL*d8|`;qwu&eANF@uyOJc z_c3sN+{fYM!{-x)Pdfi6;p8Ltr@+R^8|T`58gXsXxX%>4Yv6sLF20x1`o!J?=8LuY z9N4wdUq9^&n8P#sdBnBT$Jn(6=Nfp%zlhv`#2Nom!O8o0#`QJVeTeZ9`(NMM!^oyWcmA&<`Ya~i+P{J5BX7L-pIp@U zP4FX${w=URVgEMxQN$X}_Z>tZ`PifHf{jVf&G*pd7qE-QeIJoarI}x4+J1nzhLaKN zaSr{hcQd2*sON`Z&syxokHB(@k9mxHj8U6$uFH=Rx!CicfcGfG^QX^G5&g88luHCJcpz{zc2Xv@R^5OFW*nLgc?L~C?sQ)Ff_1}d=k1vDe-A{Y|A7Y&R{l$8}0(L%a z_VgN~k3DI7RfR)r-Qwu!b+A3@uT7t*cL=zCy@$fdhtDvuJ*Dg204EoHv0!=U_jj4PHsAdnz`k$V{7t4Fww=Jfx7z%DrXD$W0sH%2 zo4?i6bryalO2Y%Al_5@#2)Mc-VIJa^ZA=l zF7|N(*nLdT??gEH_}1hkH`=Xmqo4JN#+Yjt|rZr50 zlV4rDANzyljE}ejz!7I}>iR{E2ZF6JJ;w*Z$w!R`gXPlRCZij-5s7s_1WrEU4h74l zy&Z;bK5bFs6tFd>H5?8nAH7Wl%NZYWM}Q;F-qiJr-i`!YW4dNX!O4fu(S=WX?;Has zA9EiIHcmczn+BGTHF1xQL-$;#^BoT-AA5TOSkCz9<3w=8*@wD*QR8%Q{T|JLlMkPn zg-^O4C&9@_AG7pFSJ;pT#H_JuCrl#vb}P=*@-QJL+7pG4cEEJg{6U z@%!$4c>d(S@62m1{hil2v`3v6fX5@TM{Qtz(sObloV>rgtfd`noP5MB1xK9osOuN@ za$=9UJK^N@FEOfzZ5dcDew$Rla?26Fc^5PK_|4OH5hCX|Py7liAeM_7dcaX*%+m`e zAH7`ymg_^Bi@)dfgZY!~gSdzKtS;7K0Ia==g#QXKf3jT=?Sn|RNQ zbTevqt$fd}2fKdr`EFhZz5$U_d>r_t^u~JM2vNV@H^IrTrFV1OjF?A0_URUIecY{Z z@@Ew5ejC_WpWxOOoOE#O3eLLX-B}N3y#CtF9p~Y8uzg#fJ>LP=U*0uY!zlME-5!nj zZ*sm9&4|wkQQJEYAV_9YiN z_5-_ap-%!^SDg9%!TQLD&jDcbgzj0kw)ln}1lC7BYC9NgE^QGv8EpOWZXW{HPu^Nx zbGh(86r9%T??-EmJL_<;KJwmKM=<($XK9;?9D{fk#F6(%aGLihboVm)IU1~weAFf9 zZ*}J|pLrwSvEVe{H1u>$k3)A~<>T)d$AgX2X6+|3`dGWR6A(G)631NA!Oj)?H3Mw! z=}7p@1gHI+gsz`_#LWU5r!9Jvv&Wd%G40vjV~*Ki_vhr+qyV{oM=BgBW+-Ikvn}046^_>QmkM%wsEa%@pT<-;pKCZX6 z`A92bEn@R{Kb?VCWBmKf!h)0c8HTui)~wC-GPilm7kSPEr+F5k%PB*Up-AM>mgX^^ zdA4CZ3vsUKeKFX)_8xcg**H@^qQ zYY*E>u(o{={oMEHeHGaGqlZfiyFSLd9+x4JTYr6A((HgqmL`W W^=tSRIQh8mt^yk?@13RYo%MfJ4X_LV literal 9420 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 O)nH@gy|dK4v;GhB3zzBu diff --git a/src/editor/editor_embedded_content/editor_content/renderer/src/inc_fragment_oit_block.inl b/src/editor/editor_embedded_content/editor_content/renderer/src/inc_fragment_oit_block.inl index 69cca32bf..738321923 100644 --- a/src/editor/editor_embedded_content/editor_content/renderer/src/inc_fragment_oit_block.inl +++ b/src/editor/editor_embedded_content/editor_content/renderer/src/inc_fragment_oit_block.inl @@ -6,8 +6,5 @@ float weight = clamp(pow(min(1.0, unWeightedColor.a * 10.0) + 0.01, 3.0) * 1e8 * pow(1.0 - gl_FragCoord.z * 0.9, 3.0), 1e-2, 3e3); outColor = vec4(unWeightedColor.rgb * unWeightedColor.a, unWeightedColor.a) * weight; #else - if (unWeightedColor.a < (1.0 - 0.001)) { - discard; - } outColor = unWeightedColor; #endif \ No newline at end of file diff --git a/src/editor/editor_embedded_content/editor_content/renderer/src/ui_nine.frag b/src/editor/editor_embedded_content/editor_content/renderer/src/ui_nine.frag index eac98e7d1..ec3324d72 100644 --- a/src/editor/editor_embedded_content/editor_content/renderer/src/ui_nine.frag +++ b/src/editor/editor_embedded_content/editor_content/renderer/src/ui_nine.frag @@ -47,7 +47,10 @@ void main(void) { processAxis(normUV.y, fragNineSliceEdgeLen.y / fragSize2D.w, fragSize2D.w / fragSize2D.y) ); vec2 newUV = fragUvs.xy + scaledNormUV * fragUvs.zw; - vec4 unWeightedColor = texture(texSampler, newUV) * fragColor; + vec4 texColor = texture(texSampler, newUV); + // Lógica Premium de Cor de Fundo: + // Misturamos fragBGColor (fundo sólido) com (texColor * fragColor) baseado no alpha da textura. + vec4 unWeightedColor = mix(fragBGColor, texColor * fragColor, texColor.a); // Border { vec2 dimensions = fragSize2D.xy; diff --git a/src/engine/entity.go b/src/engine/entity.go index 9a971ed37..251f1d8eb 100644 --- a/src/engine/entity.go +++ b/src/engine/entity.go @@ -87,7 +87,9 @@ func NewEntity(workGroup *concurrent.WorkGroup) *Entity { func (e *Entity) Init(workGroup *concurrent.WorkGroup) { e.isActive = true e.Children = make([]*Entity, 0) - e.Transform.Initialize(workGroup) + if e != nil { + e.Transform.Initialize(workGroup) + } e.namedData = make(map[string][]interface{}) e.name = "Entity" } diff --git a/src/engine/ui/markup/css/properties/css_background_color.go b/src/engine/ui/markup/css/properties/css_background_color.go index 134874404..1644b871a 100644 --- a/src/engine/ui/markup/css/properties/css_background_color.go +++ b/src/engine/ui/markup/css/properties/css_background_color.go @@ -46,6 +46,7 @@ import ( "kaijuengine.com/engine/ui/markup/css/rules" "kaijuengine.com/engine/ui/markup/document" "kaijuengine.com/matrix" + "kaijuengine.com/rendering" ) func setChildTextBackgroundColor(elm *document.Element, color matrix.Color) { @@ -91,7 +92,10 @@ func (p BackgroundColor) Process(panel *ui.Panel, elm *document.Element, values } if color, err = matrix.ColorFromHexString(hex); err == nil { if applyPanelColor || panel.Base().Type() == ui.ElementTypeImage { + tex, _ := host.TextureCache().Texture(assets.TextureSquare, rendering.TextureFilterLinear) + panel.SetBackground(tex) panel.SetColor(color) + panel.SetBGColor(color) } if panel.Base().IsType(ui.ElementTypeInput) { panel.Base().ToInput().SetBGColor(color) diff --git a/src/engine/ui/markup/css/properties/css_left.go b/src/engine/ui/markup/css/properties/css_left.go index 761871a3b..0fb92927f 100644 --- a/src/engine/ui/markup/css/properties/css_left.go +++ b/src/engine/ui/markup/css/properties/css_left.go @@ -40,6 +40,7 @@ import ( "errors" "kaijuengine.com/engine" "kaijuengine.com/engine/ui" + "kaijuengine.com/engine/ui/markup/css/helpers" "kaijuengine.com/engine/ui/markup/css/rules" "kaijuengine.com/engine/ui/markup/document" "strings" @@ -63,7 +64,7 @@ func (p Left) Process(panel *ui.Panel, elm *document.Element, values []rules.Pro offsetX += elm.Parent.Value().UI.Layout().Offset().X() } default: - val := values[0].Num + val := helpers.NumFromLength(values[0].Str, host.Window) if strings.HasSuffix(values[0].Str, "%") { l := panel.Base().Layout() if l.Ui().Entity().IsRoot() { diff --git a/src/engine/ui/panel.go b/src/engine/ui/panel.go index 213fb6e4b..c2c8dcad9 100644 --- a/src/engine/ui/panel.go +++ b/src/engine/ui/panel.go @@ -600,12 +600,17 @@ func (p *Panel) UnEnforceColor() { func (p *Panel) Color() matrix.Color { return p.shaderData.FgColor } -func (p *Panel) SetColor(bgColor matrix.Color) { +func (p *Panel) SetColor(color matrix.Color) { if p.HasEnforcedColor() { - p.PanelData().enforcedColorStack[0] = bgColor + p.PanelData().enforcedColorStack[0] = color return } - p.setColorInternal(bgColor) + p.setColorInternal(color) +} + +func (p *Panel) SetBGColor(color matrix.Color) { + p.shaderData.BgColor = color + p.Base().SetDirty(DirtyTypeColorChange) } func (p *Panel) SetScrollX(value float32) { diff --git a/src/matrix/transform.go b/src/matrix/transform.go index 02f2a855f..035f9ae74 100644 --- a/src/matrix/transform.go +++ b/src/matrix/transform.go @@ -78,6 +78,9 @@ func (t *Transform) setup() { } func (t *Transform) Initialize(workGroup *concurrent.WorkGroup) { + if t == nil { + return + } defer tracing.NewRegion("matrix.Initialize").End() t.workGroup = workGroup t.setup() diff --git a/src/rendering/font.go b/src/rendering/font.go index 8c08d3fa1..2af1e8d2e 100644 --- a/src/rendering/font.go +++ b/src/rendering/font.go @@ -146,7 +146,7 @@ func (cache *FontCache) TransparentMaterial(target *Material) *Material { } else if target.SelectRoot() == cache.textOrthoMaterial.SelectRoot() { return cache.textOrthoMaterialTransparent } else if target.SelectRoot() == cache.textMaterialTransparent.SelectRoot() || - target == cache.textOrthoMaterialTransparent.SelectRoot() { + target.SelectRoot() == cache.textOrthoMaterialTransparent.SelectRoot() { return target } slog.Error("invalid material used for getting transparent text material", "material", target.Id) @@ -265,6 +265,9 @@ func (cache *FontCache) cachedMeshLetter(font fontBin, letter rune, isOrtho bool func (cache *FontCache) createLetterMesh(font fontBin, key rune, c fontBinChar, meshCache *MeshCache) { defer tracing.NewRegion("FontCache.createLetterMesh").End() + if font.cachedLetters == nil || font.cachedOrthoLetters == nil { + return + } mat := cache.textMaterial oMat := cache.textOrthoMaterial @@ -307,12 +310,15 @@ func (cache *FontCache) createLetterMesh(font fontBin, key rune, c fontBinChar, func (cache *FontCache) initFont(face FontFace, adb assets.Database) bool { defer tracing.NewRegion("FontCache.initFont").End() bin := fontBin{} - bin.texture, _ = cache.renderCaches.TextureCache().Texture(face.string()+".png", TextureFilterLinear) - bin.texture.MipLevels = 1 bin.cachedLetters = make(map[rune]*cachedLetterMesh) bin.cachedOrthoLetters = make(map[rune]*cachedLetterMesh) + bin.texture, _ = cache.renderCaches.TextureCache().Texture(face.string()+".png", TextureFilterLinear) + if bin.texture == nil { + return false + } + bin.texture.MipLevels = 1 out, _ := adb.Read(face.string() + ".bin") - if bin.texture == nil || out == nil || len(out) == 0 { + if out == nil || len(out) == 0 { return false } read := bytes.NewReader(out) @@ -421,6 +427,10 @@ func (cache *FontCache) RenderMeshes(caches RenderCaches, material = cache.textOrthoMaterial } } + if material == nil { + slog.Error("FontCache.RenderMeshes: material de texto nulo", "transparência", fgColor.A() < 1 || bgColor.A() < 1, "is3D", is3D) + return nil + } // Iterate through all characters runes := []rune(text) textLen := len(runes) @@ -548,6 +558,9 @@ func (cache *FontCache) RenderMeshes(caches RenderCaches, Scissor: matrix.Vec4{-matrix.FloatMax, -matrix.FloatMax, matrix.FloatMax, matrix.FloatMax}, } shaderData.SetModel(model) + if material == nil || fontFace.texture == nil { + continue + } drawing := Drawing{ Material: material.CreateInstance([]*Texture{fontFace.texture}), Mesh: m, diff --git a/src/rendering/gpu_device_drawing_vulkan.go b/src/rendering/gpu_device_drawing_vulkan.go index 8e47d0fd9..15ae53c45 100644 --- a/src/rendering/gpu_device_drawing_vulkan.go +++ b/src/rendering/gpu_device_drawing_vulkan.go @@ -145,9 +145,17 @@ func (g *GPUDevice) blitTargetsImpl(passes []*RenderPass) { cmd.Begin() defer cmd.End() g.Painter.forceQueueCommand(*cmd, false) + frame := g.Painter.currentFrame idxSF := g.Painter.imageIndex[frame] swapChain := g.LogicalDevice.SwapChain + + if img == nil { + g.TransitionImageLayout(&swapChain.Images[idxSF], GPUImageLayoutPresentSrc, + GPUImageAspectColorBit, GPUAccessTransferWriteBit, cmd) + return + } + g.TransitionImageLayout(&swapChain.Images[idxSF], GPUImageLayoutTransferDstOptimal, GPUImageAspectColorBit, GPUAccessTransferWriteBit, cmd) @@ -370,6 +378,9 @@ func (g *GPUDevice) combineTargets() *TextureId { cmd.Begin() defer cmd.End() g.Painter.forceQueueCommand(*cmd, false) + if len(g.Painter.combinedDrawings.renderPassGroups) == 0 { + return nil + } // There is only one render pass in combined, so we can just grab the first one draws := g.Painter.combinedDrawings.renderPassGroups[0].draws for i := range draws[0].instanceGroups { @@ -386,6 +397,11 @@ func (g *GPUDevice) combineTargets() *TextureId { func (g *GPUDevice) cleanupCombined(cmd *CommandRecorder) { defer tracing.NewRegion("Vulkan.cleanupCombined").End() + + if len(g.Painter.combinedDrawings.renderPassGroups) == 0 { + return + } + // There is only one render pass in combined, so we can just grab the first one groups := g.Painter.combinedDrawings.renderPassGroups[0].draws[0].instanceGroups for i := range groups { diff --git a/src/rendering/material.go b/src/rendering/material.go index 5ba537201..19ba698f3 100644 --- a/src/rendering/material.go +++ b/src/rendering/material.go @@ -97,6 +97,10 @@ func (m *Material) CreateInstance(textures []*Texture) *Material { defer tracing.NewRegion("Material.CreateInstance").End() instanceKey := strings.Builder{} for i := range textures { + if textures[i] == nil { + instanceKey.WriteString("nil;") + continue + } instanceKey.WriteString(textures[i].Key) instanceKey.WriteRune(';') } From 804c856faadb5c08cedb148625cffba5ade3fe99 Mon Sep 17 00:00:00 2001 From: welisson Date: Thu, 26 Mar 2026 17:58:48 -0300 Subject: [PATCH 2/3] docs(engine/ui): add Go interoperability guidelines and layout pitfalls - Expanded docs/engine/ui/writing.md to detail dynamic UI updates from backend Go code. - Covered UIPanel vs UILabel hierarchical dependency within html element tags. - Documented explicit SetDirty flag execution requirement within render loops. - Added major warning regarding the "Invisible Canvas Bug" (Pitfalls): defining how basic Screen Space metrics (width, position) must remain under DOM CSS sovereignty instead of being bypassed purely by Go's native coordinate matrices. - Added UI & Engine layout guardrails to AGENTS.md to protect element positioning from unauthorized structural purges during future AI interactions. --- AGENTS.md | 7 +++++++ docs/engine/ui/writing.md | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 4e0b4f57f..4dfba43d9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -78,6 +78,13 @@ kaiju/ └── AGENTS.md # This file ``` +## UI & Engine Interoperability Guidelines (CRITICAL) + +When creating or modifying User Interfaces (HUDs, Menus, Overlays) within Kaiju Engine, AI agents must strict adhere to the following axioms: +1. **Always preserve HTML/CSS layout sovereignty.** The document parser (`markup.DocumentFromHTMLAsset`) uses the bounding boundaries defined in the CSS (`position`, `top`, `width`, `height`) to anchor UI meshes correctly to the screen canvas viewport. DO NOT strip structural styling from HTML placeholders to reconstruct bounding boxes exclusively via Go transforms (`Transform.SetLocalPosition`). Removing these HTML anchors will cause render meshes to collapse to `(0,0)` or visually disappear. +2. **Mandatory Explicit Dirty Flags**: Kaiju UI rendering does not redraw structural changes automatically via setters in the loop. If you hook into an element via Go and modify its inner text (`label.SetText()`) or dynamic coloring, you MUST explicitly tell the GPU pipeline to redraw that interface by firing `element.UI.SetDirty(ui.DirtyTypeLayout)` before the frame ends. +3. **Hierarchical Panels vs Labels Nature**: A standard block like `
Text
` implicitly generates a `UIPanel` wrapper hosting a pure `UILabel` child. Background colors must be painted strictly on the outer Panel (`UIPanel.SetBGColor`), while internal Font styling applies exclusively to the `InnerLabel()`. Mixing these commands results in UI rendering glitches. + ## CRITICAL: Custom Math Library **DO NOT use external math libraries (e.g., gonum, mathgl).** The Kaiju Engine has a complete custom math library at `kaijuengine.com/matrix`. diff --git a/docs/engine/ui/writing.md b/docs/engine/ui/writing.md index 654ec3a57..248dbdea2 100644 --- a/docs/engine/ui/writing.md +++ b/docs/engine/ui/writing.md @@ -72,4 +72,36 @@ data := struct{ doc, err := markup.DocumentFromHTMLAsset(host, "ui/tests/binding.html", data, nil) ``` -This will load up the HTML document and any of the CSS it references and build out your UI. The returned `doc` will contain the document and all the elements/panels/labels. This UI is immediately loaded into the `host` so you don't need to worry about doing that yourself. *The last argument is a funcmap used for inline template functions* \ No newline at end of file +This will load up the HTML document and any of the CSS it references and build out your UI. The returned `doc` will contain the document and all the elements/panels/labels. This UI is immediately loaded into the `host` so you don't need to worry about doing that yourself. *The last argument is a funcmap used for inline template functions* +## Dynamic UI Updates (Go Interoperability) +While HTML/CSS provides the initial structural foundation and styling (the canvas initial geometric bounding box), you will commonly need to update the UI dynamically during runtime from your game's Go logic (e.g., updating a timer, health bar, or coordinate tracker). + +### 1. Element Hierarchy (Panel vs Label) +When declaring a simple markup with text in HTML: +`html +
FPS: --
+` +The parsing engine instantiates a ui.UIPanel base structural wrapper, and creates an attached hierarchical child ui.UILabel for the string text logic. +To retrieve and modify properties dynamically in the Go loop: +* **Backgrounds & Solid Borders**: Are applied to the parent pane (element.UIPanel.SetBGColor(...)). +* **Text Strings & Typography Colors**: Are applied to the inner textual label (element.InnerLabel().SetText(...) or element.InnerLabel().SetColor(...)). + +### 2. Dirty Flags (Reactivity is NOT Automatic) +Unlike standard web browsers, the Kaiju Engine does NOT automatically redraw the UI canvas frame when you modify an element's property via backend Go scripts. You **must explicitly** flag the element as "dirty" in your game or system's update loop to force the Vulkan render pipeline to redraw the updated geometry on the screen: +`go +if e, ok := doc.GetElementById("fps-val"); ok { + if lbl := e.InnerLabel(); lbl != nil { + lbl.SetText(fmt.Sprintf("FPS: %d", fps)) + } + // Critical: Signal the renderer that this specific element layout has mutated! + e.UI.SetDirty(ui.DirtyTypeLayout) +} +` + +### 3. Layout Control Pitfalls (The Invisible Canvas Bug) +**Never strip foundational HTML positioning anchors in an attempt to control the Screen Layout purely via Go Transform functions.** + +The document parser deeply relies on CSS properties (position: absolute, op, ottom, ight, width, height) to anchor the UI primitives to the Screen Viewport properly. + +If you declare empty/naked
tags in the HTML and attempt to explicitly align and mathematically scale them exclusively via Transform.SetLocalPosition(...) or manual offset matrices natively in Go, the engine's internal UI-Layout System will fight the DOM logic. This typically cascades in elements infinitely collapsing to coordinates (0,0), or losing their mesh dimension entirely, making your UI visually disappear from the screen logic. +* **Golden Rule**: HTML/CSS solely owns the global layout spatial anchors, responsivity bounds, and z-index ordering. Go owns dynamic text injection, loop data binding, and situational state-color updates. From e7f34ee78ada8ae52743442322e2d33e5a56de6e Mon Sep 17 00:00:00 2001 From: welisson Date: Thu, 26 Mar 2026 20:01:57 -0300 Subject: [PATCH 3/3] =?UTF-8?q?refactor(hud):=20migra=C3=A7=C3=A3o=20total?= =?UTF-8?q?=20para=20CSS=20e=20corre=C3=A7=C3=B5es=20de=20estabilidade=20n?= =?UTF-8?q?o=20motor=20Kaiju?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removidos overrides manuais de layout e estilo no código Go da HUD ( hud.go ) para permitir o controle total via CSS. Atualizado hud.html com um design "Dark Premium", incluindo painéis translúcidos, métricas coloridas e layout responsivo. Aplicadas correções críticas (nil-checks) no motor Kaiju em várias propriedades de fonte (font-weight, font-style, font-size, line-height, font-family) para evitar panics durante a aplicação de estilos. Corrigido um bug de tipagem em css_font_style.go que tentava aplicar peso de fonte em vez de estilo de fonte. Sincronizadas as IDs entre o HTML e o sistema de atualização de dados em tempo real no Go. --- .../ui/markup/css/properties/css_font_family.go | 7 ++++++- src/engine/ui/markup/css/properties/css_font_size.go | 10 ++++++---- .../ui/markup/css/properties/css_font_style.go | 9 +++++++-- .../ui/markup/css/properties/css_font_weight.go | 7 ++++++- .../ui/markup/css/properties/css_line_height.go | 12 +++++++----- src/engine/ui/markup/html.go | 1 + 6 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/engine/ui/markup/css/properties/css_font_family.go b/src/engine/ui/markup/css/properties/css_font_family.go index c5d9ba696..c36aca832 100644 --- a/src/engine/ui/markup/css/properties/css_font_family.go +++ b/src/engine/ui/markup/css/properties/css_font_family.go @@ -50,9 +50,14 @@ import ( func setChildrenFontFace(elm *document.Element, face rendering.FontFace) { defer tracing.NewRegion("properties.setChildrenFontFace").End() + if elm.UI == nil { + return + } if elm.IsText() { lbl := elm.UI.ToLabel() - lbl.SetFontFace(face) + if lbl != nil { + lbl.SetFontFace(face) + } } else if elm.UI.IsType(ui.ElementTypeInput) { elm.UI.ToInput().SetFontFace(face) } else { diff --git a/src/engine/ui/markup/css/properties/css_font_size.go b/src/engine/ui/markup/css/properties/css_font_size.go index 65b4a506d..c7f971309 100644 --- a/src/engine/ui/markup/css/properties/css_font_size.go +++ b/src/engine/ui/markup/css/properties/css_font_size.go @@ -46,14 +46,16 @@ import ( ) func setChildrenFontSize(elm *document.Element, size string, host *engine.Host) { - if elm.Stylizer.HasRule("font-size") { + if elm.Stylizer.HasRule("font-size") || elm.UI == nil { return } if elm.IsText() { lbl := elm.UI.ToLabel() - size := helpers.NumFromLengthWithFont(size, host.Window, - host.FontCache().EMSize(lbl.FontFace())) - lbl.SetFontSize(size) + if lbl != nil { + size := helpers.NumFromLengthWithFont(size, host.Window, + host.FontCache().EMSize(lbl.FontFace())) + lbl.SetFontSize(size) + } } else { for _, child := range elm.Children { setChildrenFontSize(child, size, host) diff --git a/src/engine/ui/markup/css/properties/css_font_style.go b/src/engine/ui/markup/css/properties/css_font_style.go index 42ec930cd..545c644e9 100644 --- a/src/engine/ui/markup/css/properties/css_font_style.go +++ b/src/engine/ui/markup/css/properties/css_font_style.go @@ -45,11 +45,16 @@ import ( ) func setChildrenFontStyle(elm *document.Element, style string) { + if elm.UI == nil { + return + } if elm.IsText() { lbl := elm.UI.ToLabel() - lbl.SetFontStyle(style) + if lbl != nil { + lbl.SetFontStyle(style) + } } else if elm.UI.IsType(ui.ElementTypeInput) { - elm.UI.ToInput().SetFontWeight(style) + elm.UI.ToInput().SetFontStyle(style) } else { for _, child := range elm.Children { setChildrenFontStyle(child, style) diff --git a/src/engine/ui/markup/css/properties/css_font_weight.go b/src/engine/ui/markup/css/properties/css_font_weight.go index ecaca13a5..32897a9f0 100644 --- a/src/engine/ui/markup/css/properties/css_font_weight.go +++ b/src/engine/ui/markup/css/properties/css_font_weight.go @@ -45,9 +45,14 @@ import ( ) func setChildrenFontWeight(elm *document.Element, weight string) { + if elm.UI == nil { + return + } if elm.IsText() { lbl := elm.UI.ToLabel() - lbl.SetFontWeight(weight) + if lbl != nil { + lbl.SetFontWeight(weight) + } } else if elm.UI.IsType(ui.ElementTypeInput) { elm.UI.ToInput().SetFontWeight(weight) } else { diff --git a/src/engine/ui/markup/css/properties/css_line_height.go b/src/engine/ui/markup/css/properties/css_line_height.go index 3d410a99a..a6a8597f7 100644 --- a/src/engine/ui/markup/css/properties/css_line_height.go +++ b/src/engine/ui/markup/css/properties/css_line_height.go @@ -46,15 +46,17 @@ import ( ) func setChildrenLineHeight(elm *document.Element, size string, host *engine.Host) { - if elm.Stylizer.HasRule("line-height") { + if elm.Stylizer.HasRule("line-height") || elm.UI == nil { return } if elm.IsText() { lbl := elm.UI.ToLabel() - height := helpers.NumFromLengthWithFont(size, host.Window, - host.FontCache().EMSize(lbl.FontFace())) - height = elm.Parent.Value().UI.Layout().PixelSize().Y() * height - lbl.SetLineHeight(height) + if lbl != nil { + size := helpers.NumFromLengthWithFont(size, host.Window, + host.FontCache().EMSize(lbl.FontFace())) + height := elm.Parent.Value().UI.Layout().PixelSize().Y() * size + lbl.SetLineHeight(height) + } } else { for _, child := range elm.Children { setChildrenLineHeight(child, size, host) diff --git a/src/engine/ui/markup/html.go b/src/engine/ui/markup/html.go index fd1545523..181c1b9b9 100644 --- a/src/engine/ui/markup/html.go +++ b/src/engine/ui/markup/html.go @@ -41,6 +41,7 @@ import ( "kaijuengine.com/engine" "kaijuengine.com/engine/ui" "kaijuengine.com/engine/ui/markup/css" + _ "kaijuengine.com/engine/ui/markup/css/properties" "kaijuengine.com/engine/ui/markup/css/rules" "kaijuengine.com/engine/ui/markup/document" "log/slog"