%8U z_guR>!d9L-d57;0NyD1dxe+FdtKio5Ru9KnHE%xsainI!`J<f6r4d%s8^7;ffya_Af5-7!KNfhJ(fV7?9MP?akY z_{0h`Vm)#)QyrFW`;~AXE3lOd{a7hJ6-;U;7k *;$Z47hPOko=(3}>a#oMuvcxx_^*5VU|sL>fs?GbdzzzV%tDy Wc zPI6%&i+7S+55Q&+$u48OOIRtSWlUO0E|FrzlBZm+lJ4Nbxz>K@X%@I>lPyHyD3dzN zCH=7_bSk%B$O<5P09hnt)|`KSSW@-58rKn_cyLX?i76720<+GF`8h{PPMURyrDK$} zGd7`UmS7hnX^*RB LQl}W1cBiBDNHMCYE!sBbg1Z6{jQa4mh%9 z8ebq*Ek#nqFg{^Ci+7Vt7D7h0#GLnc!O{HMQVr|6*e>d`1jiU*5{q}2TW Tn zWu@+NNfdT+YKuI<=+tJ;mc}Ew29Ba<$0!apE1O1P%W;Fu95*J(N^le-E`GuftiVGq zX@nam`5_=bB)Q-yA5kB+_Lw;?rowluKq{Bi$E|^SGmTRV&g_?alw^fj2fyIu6>#Pm zM?5`Qsi$0$iS4mG#*#uaXByQ#a6QT`%dxSNCwANzM}i9iXHD}!>&ja$sf1fsIAmhB z)FDdJ0-Pxu6!2sOxK7ZS=It=JF5rZmEl=EcXx=aahbZ9yR_Y@c7BXp7xzLBj<8ufr zs4ADN#~vMnTK3#n?t!CGOW1XnDC-)Yu)xiZniW)&OZI}NxyLH>j*^JH`1ydZl2vBT zG*{Qbwc+}vybSa O@0Iap$ta@q2JIHY?O|EVuI%(sq!FsDDW`Zk~x$ zhl|+)aMTAPD2b9>1=koH#%mkp?BgpCv;>FEgYJf~=xkeyJag1SL)jsMRE+%Md~w_c zX$wfPbqTCd*W}?JkeWu1oOTZPGtU_6P@#I9P03hjsBidX8|AnU97UcYBH>4CEv}By zvIK`1=O(yn4n6OiD{seoJv7(_9AljC;wt%bbtIi}`;CJJs{!k*4BP;4R%~h2C`m$1 zfgqOSkn7IJ!L=ohoJDam^;1n^=U=`_AT1NXHQ^f3Vp|K264;h4#q?hRM=8d2X6=C| zfJk!Bu@kOZLN03au$z3{qMU*s3vE$H6t@I7+KZwM>0-%h0h0xIX6g<&$8&2hvF9 zBZ+bN6<6dF%nR5xTO?XViLf^F^ouswKt#Z{OONvcasgX{fTE4XEf5190F^IMYKJAl z6{5Ix(DM^lK1azuf-Mq=*gL-}24XQlt`H?2PIKi;)D|@mBd&aolAU^HJyGJZX>)}r zxsCMwiWA_1s}KfCvm9yWqd=(;m9X8_P3|`3WC8(EGTY3||1*@VW5T(1t<3F+lAYFO z-jb3WdkW_+)-P*b! cs<8h?2Z7F({v-R6l{!ewG?I z6;+^${QxqkF!PC^RK7$hwFUyzJ{h3$&roaBOU1K17makTnLuDk$zTRRR0D_rEkK2+ z4Ui3x!@~j6j|8Y#Qd{t2IjM<~!xPQ?L>fPNGR-Xb93=;)oApGgqdb5*W&r7D15_+2 z$>#t>=K@rSQo90TPz?M9QSxjdKpiYL)1{zPEGg-i17vuGnSKLGg(%5an&~P~Dnv=X zniv!_HSibZl#~=&!oPnz!Tr8E^@u{0JgfoycRN84sI&qoD6NS>v7{t#15i5rcRN84 z{=1$0r?(UP@@-^wQE9ER8isoB1G|KUXwL0w{Y*A4q2a(Qk-c2DP3V2Guur=a8}e$V zRXKnBc+m7)53VZvINE&mFf8w-{C$|U)rTryFIAK+rtkeo|MyPsk?U$)^F?f3_-jS2 z^77hAw|>9U@@$(WUNiM6hbmsKJo5OXA-@bMHeUMvXsUZJPs6>skt;fNTWna7)hDf| zeUW?o>SI5?x!J+$5G?VA2k}-x#rGTac$JVSDtUdt*ye0N#qJBnX=< i*A6VbVU;oa?ecE#+ncsylf#3ty_cI7H~gi; zQ@gN)2Q|H)oU8oq*NdIwPHRTgITbreVR(8qHTJsFdc*EE3G45!KWpFj{N9L*84Hz? ztBylQjSep )^;DmnLPo!ex!?}fED8jgP2f7;iNtM$0^e73*- zq+~^hoXW9e*I=GI{=QE3#hZ6*O{ -Y)zlj zYulap!!i>5lM&e#mo$oQe?-4L+RD(k_=oLVw>*d%-QjFm?J++mVE_Kk8Ms-GwUED;g#&zw9#V{Bdv9uKl}mGA$$vtVz>Ol=F-8}nvux3HUKVb>sT7NIOY`_tBG4TC2dSNp$S+H75J z$FZx0MMcVcv(i1Aj=%Z!?H0XSyI-$$AR_wpw*B!1g;kGCcr#b@AkW9Me+viGW0q+> z53w*T-ngv)`8!pcc)o7)bG>Go=HW5@uSeYL;nr%<`Squ+W4+&3yM%_oLFn kV@XhjshWu-0Z=jesqy zQ=e8W8YuZX|En#R+cvkkJ1yQW$vNkZa}TfSdAV~IJ}z!Ps(IVrBi242 tELdFJ}Fsgw5Wr>0HXv-9SlQF}uw&zXMWw|C!dkPpA&e#m7_!*wIwd|J-) zEo{)`SOceBi%JJo@tCQ*|ArnI`JBkHbhznL*YB@xa62J$vlTt{{Vwrxe?@BSYP-p$ zKaP1|2rhnlxol;zQ1mi$RoQXRisRq;9r8E2yePR?@;u04K!uHG`_T7H-taI>!%vb= z4a@GZUr`zo(K_3=QSHDrmBzp5U9;}gtGPaV`?SCG;N0M}-=8ZRmDcrjiOXN&{wyTU z;d#4T{;rqpTD&}2#R<0X^~yAN^aX)0;vua&*j>JNr1Of6wLS6`yQZ!hZ}q{2-IWvv zc+Bp)$*W-CxaZTBIBY&WKCqr=?Af$Q&oh3kqX>RmW8JGcUH3j|G#`d|yCZm`pyHON z;zsopUp^4_RgJHjRS@m$G|tfYNt}Lkr;}UrQ(w1^niM@KU$UiE*^)Zle%%@pJy*CT z?^Rxn=(fAAT?<&hDWfFN!Z800hU|-#V+!8ZQ4KmXLNH@j;Jm}zen?-pcKDiyo1eRH zJ+sHF%)9E|yxRV2SV^5{jjFwIy0Jo^H@@+P_s?Qh7`&yu&NuD((!%g4vS=>iE TKGM;}{d%K^W& zn0u$Fw(E|~hp$SVX2A|0iI2BBI9zeo%F;G_e!OD%;3FEnWp3tDx7=}y<4?6KJS^PR zx3**RgQ|)n(wnK4&RDc##-U@^>TLa^;Op^w?>_oX5U{@PE1#XF-!4p3XXL#F6=&|4 zuw!_o-r9~X%a(SpQ8(oJn`+PBRbH?%BbqfIK4tHTDamp5U-Wh#G~s@=?Z@vW7-9}~ zf2v%5z3G`qzdJn+y=r&e!sA>^!*gf uJ$Jo991m^O)7c zMZ6{PN@o2b`Hy6C*X|8n6tmgwMd |f_*$;#4SL>>)DvM@Z>((u`hvz+~hHU6;d zQr9P|Jcl|}Thm7F@B4Pd_FgA9RC3+==2DT3_wT26CTa8H*Pc<}@;_S|ix^ z@Xn)0>R`Ha@;Nc?lcWfkzOYs&Q9 m+S z^7hsqmA%uWfpMeqYSt7*?+FvL#LY#ACbO{vA5OF|Jl@jbtG2Bsozs5HiccS}Qn$tS zPHa{S$EVXY{(ee7!;Z8SEsuP-8MmtRUf#*F1x?d~RN5;uL>tB|t-E`xDsIoX0j*^Z zEeubvG@QIO>7ISV71!2W=_lJXa`de^y^F?;>gHW(X_<8Fj=*}CyY)X1`cq~V^^3B@ z6BhNp@nC;Q*@o;TF?)i;ciGk)?tuTU7|KII-t$FlNY0398F%;2@<&qlnuD&4k9-kq zcVN}xv$cQE`98XbUq6ZWkap*vW~Roa8Qxa6N}Aj8QKQTyw|kuPIHs^mb=_71LwtxQ z@kT+#KTWP@6;|cuCF^ICa-O{$-D{B{b$9j0g91tp=q0Y(a~o`aY`@#V5WB4L
pFF(U4#6BdRiTN-}7p?+PT=1MEuCHf^Px8?q?HCOI975KT_ z{xBl&$szrjjn@lZXPqtjIc?x{k9#}4yRE*tZe-Gj$Nq-Yu{m>+M+eroFg(T5@Z6Q# zy0lU3dADwQXr<8+f7t%gbb8~MVbcPR4qP{@d)p@m?4>pl8(OMu&mMAI86R`8s8W~9 zN5<6YyK}5MaK^fqi~g{%JJr%|cG|-D2+b|SuKQB&M+-7TbYYoW=d{|`X?k(gXs@A{ zyMAbAb5IwfD!?pV^ONqOOs=SH=NNEnnGsW0ric=aL6M=Sm}A+j%SpA{T7V)^aC zacx?c-0Uu$fAwXXuiO-^UC(W=vgcZtUnVwOc uLMOjhP4u?d-Ni3wZ z%m4ni&h)|hE1}!NhYR zVALFM&lMMe%4KC^%ch7}Y z${+Wyz}{mz*%b)Z<+08wjS%YhA54XW^u?a;!gyfg3i0GGf&K+Th5pk-b?E0Z`esRm zz6O(w7Uy_?3VnkmnKeL`XPHT8#$ld zl0rv`MPF|ynJ9Vmz(61#=nb?5=!-=IAR34P8Ul?dNOD|60wF*>APAtO_y(XkppaAY zO#}J^y@2*WJD>?r6L1A+zA5P_$yNd@fMvj9fO04wm slYxK`0!c 9V;~p^0Q`aKfD~8`%mDfU0{{h(4dejB0A5asng>v#Q5}*KC6GLT6te+ZB0Qag zYl4x4D86%%WC3;*1T|0;mwH!dAn#~$DJ%m38o^+IQj6wb6p#j_0yMHrKm*YHr2|@E zI6!knp3|sl^g{rC6qL%Or{tr#r9`B}MAV|Vd^Um^4deoJO>`VE9^jLL=Bx`%0Lp+> ztW|md)1 ysh%8UtJQu$8f~WAdWm?`w;M)PUx18k> UnScF%V~Y2OT|G~glI5_VQ0U#5 zvnEV9jkVNt6y`IA>j&%*K8LWk8mSlmR(~(gJz|fl##JI=2<+0l<+FO3(g^+q?`onO z{klGWtRcH0oL%l90X%y7^0al!(1tIlWiY4N&PHZRz4(I%Hh2AwcC>fcMEX!}tY_J# zOsTLAJA%&!>{e#3a5qcVhT-+gueDNm_r12i#9Lw-Z$9(UNriJ+lrA@t|1Oz*clqpU z?_Y$8g#BQUO!BYucNvwst87!zI%uMx!7wBh*khfC7ythMH**&~wDuj)lxqrqk-N-K zFBJx|7<@*s?)oq<^SkNBA7a(}Mek4Np)NPASeb0OK3Qa2kvU{_5ME^QSz*EwHZ4nv z>s7d3#x7+^z4$l(Pxe}SqV3DYw?x8G6w+|)M$Bo5RQL@G91 zwn)gg vxPZQFaC7G>YVo(VN1N4iYZzl Ufpo7 zsf2!B|2{lrU$5EUh=dIx!M<(e;@CMGTt#q6@%#5Ja#~_75-K5agXFW*$BlD7YS01> zv48g9t5&XJk#IaDu8@?lv|*GAm=Mg Ihqa*$( z2!FZ(8rpH8;SYcC=NY&*p|l41V 4|Duk6aM%F8qvN( zBXZ`Rri~UZgasiE0s(&xg+E$>Hnboy)
6nKccKnpAMA z&zVCy2n2uK*Z4cd7(8^~&&VGDL7S+cP~1O*KOK?ffcRrI{NWL_G2iNz*WjMhjRx~M zk5imEZpOJYGw6sv83Iix!i^~Kr)>DMBwU*?iUNO5hd+%14UHAsF@HjbKZj!0V5r=g z9y~)B`J*czRs8WD{tyc^m puaRyax<~yLqV}w|I^y1 zZ$~;#%sUOf0_qP7iuwH)=b}4Tz+>AtZWczaYuk3mjwY+A7lB~I2@|z%m{q^AZ-}ps zbsZj7C0&yyqaJXGDl{=motjiHO`%Q?V#0YoMeBxF@pep9r`O}3%YtUjtD49&^a(R2{d)7eE!6y_40(@fg5@Uq z9F0m>FFiw>rbtx}Q^_<6 GufXETR;G5rj8>u_uhVeHui zzXV=I5i74Jiz@FfQIV)htw%>dWdpT}G!>aapVUk>M3pW}#ECu?&f?LDJYA|fU8R#L z(vxJ$jMS{Obe1>VoB9*u))Pi!tMlg?I`sP&nT*?TS@9g-hF|IpO~2?3n>jO}mieS0 z>hgC3#F=(e#1f}X6 =esnzJ|}IIof|-!7WQ}G$|Q6y(&qTtk%oa=>sz`$;u3^ zN|u(9l$EM74^AbcCC#s@RJBr-u2ZGx^%`AtJ*>xMwT@k#UGa;x;mMbbaXV5tWr|Ld zlua&ZRZ2Z~jXcn*bUL+pjnRhD{c{R#>!8MeYM|n5hm_k^Wh+%0J UjU qhSbku 0izAHUpI YCPAS5mvS1mYeb8nUizURz|>X@;B#ePv*EpfXVR zFql=)1pNT$KZ3;aGcH@+4mk$$fe5>wXTf73?;sB$(~1lSekoGZ;=t40n&tr?0nUoe zD5qQ*s4Ph@Ua2)eX1(e)tAj;V#U 9BA%fO-n}k zSurAR30+FEqu2{R2-=h2^#4JaO8-kMs#YSa^)u|@x%#U8b#;EobmNPQ=$#?9{>h=5 zh6psafwLdS?b9fyJ_o+w*L^W^PJ~{6zVzis63*SIWM8c*)ycZ_=%)IG^dTKAK98vu zN)YJ|>G7EE3s~@Z(%{KgH;He6spd-%X^Zp>F!jG;@lQqfZaJcR)oAfWo9eg(qfI>u zt9&L(Tvo}Q(ub0b;)^lW>k^DHbuU(Z0m?Nw;_ 5 z)>JjphxBLSi!)sx_md5AQ}r=e*mt05q%_5ORh{(3nfh_)CPJrVdW={97?N!kBzI1K z3?-jM=d-hcroIdEc*``lKPAC 98zyy;$m5a-I20r~0)@gvNEsWVej=>OX`u*^=n|VF@Oh`g{a=?v=hfr4PC@(B(qswrm@WRhMVgnC|iFg^;E} ziewn{$HHY1(v;}cUksPI=%?F_X`L(wJ^8wc+>9$dG(ln@`>_XgP<% 04usv1=99tTe&_2$qzXpkc>n{w!5bQiU5M@cr!jk2V zz6BD)V}(wAD1AdsJrk!8ZP6@S) HWFmlpmW60L}^!Wb*QR8ud(*1Zy1y)!IX9=UrV-DmZhjV(+I`dhx$K+1sR7KENV zXt=3IV*gKqE>aY{&x2$yfFrP?8q!>pxmaI+0n$83%IYf?n|C%O*c9tk3&c0lRGkt; z8ZSLa%cXCmsdwRM$VV-Y73p3`^h%fMNnUsIP);^yWvTStVCv69=F-8g8}8LVg2cN0 zF?L@66ek> 62}yQ)y*SJa{-uG4abFsX|u`@ z42!PE*$$3cM2_95jkKg|bM}9Orj4}*92Ks3HD`siu%28=dRDR&Wc4RPI2yS7ExHzG z>-2CrIb}?!MdUOWo5dn>mS+O^cjhEpat=6&97hV5hehBBIfx22G%0|{m0W K~v0x)x{sC06P6IqN+Lu)pP+7P1Cf0fDr_O0Uf$QC1AF1Kt%#{|$GeUOB*-ssKp; z=J1~#Rs!U!0amOANaX#1wE!&+0xYiwNDgP71`=Ot2Uzh(qpP u#Wc` zc8oL2x^&S?USZglm+oZ%qf-U2Eid5f0QSKv4R!c7($=RVp^^qriO(LZF*~je>lSe| zP+Em7lN;srP0 Eypar~bv5ysiA7CgMI?l^oz+B*N zU> 4C3@{cL2N(dSgW*pFhV!#!1TyrBUQ7d~12X`6#4vG= zc&A+i%m;1*asjX0@OWZq3Ua) N+2yHn_clX~k-)ZWl_T4cpC }-Rn(41OFcw2`7O}6|WlBf3 z>XMocL+zCxcNi(otLBk}{J$-~>+utoS(&J=9hO5KMvn7*xbXD(v<+1Q7o$AS?#p=} zojc)Zm+{+UgHXa5$6DDdF*^(uD;tq6km*R@kR>~e6z7HYeDMPddkYTFL diff --git a/generators/README.md.txt b/generators/README.md.txt new file mode 100644 index 0000000..64be2ee --- /dev/null +++ b/generators/README.md.txt @@ -0,0 +1,39 @@ +# noeidelon + +Foxes dream of electron beams. + +**noe** + **eidelon** _(n. a thing, an image, a reflection)_ + +https://art.mekanoe.com + +## Development + +`nix` to install bun... or do it globally. + +`bun run .` to generate HTMLs + +`bun serve` to serve them locally + +## Artworks + + + +## Infrastructure + +- The "generator" does + + - Generates a work list from .js files within `html` + - Creates this README.md file + - Creates the index.html file + - Creates individual HTML files for each work + +- The platform supplies + - Shader types + - Primitive objects + - A canvas to draw on + - WebGL hooks + - Helpers, etc + +## License + +Code and images are licensed under the AGPLv3 license. See [LICENSE](./LICENSE) for more information. diff --git a/generators/generate.js b/generators/generate.ts similarity index 76% rename from generators/generate.js rename to generators/generate.ts index 0ee938a..b5022ef 100644 --- a/generators/generate.js +++ b/generators/generate.ts @@ -1,5 +1,8 @@ -import { unlinkSync } from "fs"; +import { unlinkSync } from "node:fs"; import { globSync } from "glob"; +import indexTemplate from "./index.html.txt"; +import workTemplate from "./work.html.txt"; +import readmeTemplate from "./README.md.txt"; const allHtmls = globSync("html/*.html").filter( (file) => file !== "html/index.html" @@ -8,10 +11,6 @@ for (const htmlFile of allHtmls) { unlinkSync(htmlFile); } -const indexTemplate = await Bun.file("generators/index.html.template").text(); -const workTemplate = await Bun.file("generators/work.html.template").text(); -const readmeTemplate = await Bun.file("generators/README.md.template").text(); - const allWorks = globSync("html/*.js") .map((file) => file.replace("html/", "").replace(".js", "")) .filter((work) => work !== "platform"); @@ -31,7 +30,7 @@ const index = indexTemplate.replace( await Bun.write("html/index.html", index); const readme = readmeTemplate.replace( - //, + "", allWorks .map((work) => `- [./${work}](https://art.mekanoe.com/${work})`) .join("\n") diff --git a/generators/index.html.template b/generators/index.html.txt similarity index 100% rename from generators/index.html.template rename to generators/index.html.txt diff --git a/generators/work.html.template b/generators/work.html.txt similarity index 100% rename from generators/work.html.template rename to generators/work.html.txt diff --git a/html/001-platform-provenance.js b/html/001-platform-provenance.js index e4e6555..8b1623a 100644 --- a/html/001-platform-provenance.js +++ b/html/001-platform-provenance.js @@ -1,12 +1,14 @@ -import { main } from "./lib/platform.js"; import { Shader } from "./lib/shader.js"; import { BasicPlane } from "./lib/basic-plane.js"; +import { App } from "./lib/app.js"; -main({ fov: 20 }, (gl, core) => { - const shader = new Shader(gl, core) - .attach( - gl.VERTEX_SHADER, - ` +const app = new App({ fov: 20 }); +const gl = app.gl; + +const shader = new Shader(app) + .attach( + gl.VERTEX_SHADER, + ` attribute vec4 aVertexPosition; attribute vec2 aTextureCoord; @@ -20,10 +22,10 @@ main({ fov: 20 }, (gl, core) => { vTextureCoord = aTextureCoord; } ` - ) - .attach( - gl.FRAGMENT_SHADER, - ` + ) + .attach( + gl.FRAGMENT_SHADER, + ` uniform lowp float uTime; uniform lowp float uSinTime; uniform lowp float uCosTime; @@ -38,22 +40,10 @@ main({ fov: 20 }, (gl, core) => { gl_FragColor = clamp(gl_FragColor, 0.0, 1.0); } ` - ) - .link(); + ) + .link(); - const plane = new BasicPlane(gl, core); - plane.attachShader(shader); +const plane = new BasicPlane(app); +plane.attachShader(shader); - const render = () => { - core.clear(); - plane.draw2D(); - - if (gl.getError() !== gl.NO_ERROR) { - throw new Error("WebGL error"); - } - - requestAnimationFrame(render); - }; - - requestAnimationFrame(render); -}); +app.loop(); diff --git a/html/lib/app.js b/html/lib/app.js new file mode 100644 index 0000000..a4c8964 --- /dev/null +++ b/html/lib/app.js @@ -0,0 +1,92 @@ +export class App { + constructor( + config = { + fov: 45, + } + ) { + this.registry = { + onStart: [], + onUpdate: [], + onBeforeUpdate: [], + }; + + this.config = config; + this.canvas = document.querySelector("canvas"); + this.canvas.width = window.innerWidth; + this.canvas.height = window.innerHeight; + this.gl = this.canvas.getContext("webgl"); + + if (this.gl === null) { + document.querySelector( + "main" + ).innerHTML = ` your browser didn't let me set up webgl`; + throw new Error( + "Unable to initialize WebGL. Your browser or machine may not support it." + ); + } + + const gl = this.gl; + + const fieldOfView = (this.config.fov * Math.PI) / 180; // in radians + const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight; + const zNear = 0.1; + const zFar = 100.0; + const projectionMatrix = glMatrix.mat4.create(); + glMatrix.mat4.perspective( + projectionMatrix, + fieldOfView, + aspect, + zNear, + zFar + ); + + const modelViewMatrix = glMatrix.mat4.create(); + glMatrix.mat4.translate( + modelViewMatrix, + modelViewMatrix, + [-0.0, 0.0, -6.0] + ); + + this.projectionMatrix = projectionMatrix; + this.modelViewMatrix = modelViewMatrix; + + this.clear(); + this.onBeforeUpdate(() => this.clear()); + } + + clear() { + const gl = this.gl; + + gl.clearColor(0.0, 0.0, 0.0, 1.0); // Clear to black, fully opaque + gl.clearDepth(1.0); // Clear everything + gl.enable(gl.DEPTH_TEST); // Enable depth testing + gl.depthFunc(gl.LEQUAL); // Near things obscure far things + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + } + + onStart(fn) { + this.registry.onStart.push(fn); + } + + onUpdate(fn) { + this.registry.onUpdate.push(fn); + } + + onBeforeUpdate(fn) { + this.registry.onBeforeUpdate.push(fn); + } + + start() { + this.registry.onStart.forEach((fn) => fn(this.gl, this)); + } + + update() { + this.registry.onBeforeUpdate.forEach((fn) => fn(this.gl, this)); + this.registry.onUpdate.forEach((fn) => fn(this.gl, this)); + } + + loop() { + this.update(); + requestAnimationFrame(() => this.loop()); + } +} diff --git a/html/lib/basic-plane.js b/html/lib/basic-plane.js index 1555156..34e1a56 100644 --- a/html/lib/basic-plane.js +++ b/html/lib/basic-plane.js @@ -1,7 +1,8 @@ -export class BasicPlane { - constructor(gl, core) { - this.gl = gl; - this.core = core; +import { Object } from "./object.js"; + +export class BasicPlane extends Object { + constructor(app) { + super(app); this.vertexPositions = new Float32Array([ -1.0, -1.0, +1.0, -1.0, -1.0, +1.0, +1.0, +1.0, @@ -10,53 +11,7 @@ export class BasicPlane { this.textureBuffer = this.initBuffer( new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]) ); - } - initBuffer(data, draw = this.gl.STATIC_DRAW) { - const buffer = this.gl.createBuffer(); - this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer); - this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(data), draw); - - return buffer; - } - - attachShader(shader) { - this.shader = shader; - this.vertexPosition = shader.location("aVertexPosition"); - this.textureCoord = shader.location("aTextureCoord"); - - this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer); - this.gl.vertexAttribPointer( - this.vertexPosition, - 2, - this.gl.FLOAT, - false, - 0, - 0 - ); - this.gl.enableVertexAttribArray(this.vertexPosition); - - this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.textureBuffer); - this.gl.vertexAttribPointer( - this.textureCoord, - 2, - this.gl.FLOAT, - false, - 0, - 0 - ); - this.gl.enableVertexAttribArray(this.textureCoord); - - return this; - } - - draw2D() { - this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer); - this.shader.activate(this.core.projectionMatrix, this.core.modelViewMatrix); - this.gl.drawArrays( - this.gl.TRIANGLE_STRIP, - 0, - this.vertexPositions.length / 2 - ); + this.app.onUpdate(() => this.draw2D()); } } diff --git a/html/lib/object.js b/html/lib/object.js new file mode 100644 index 0000000..1bb25ce --- /dev/null +++ b/html/lib/object.js @@ -0,0 +1,64 @@ +export class Object { + constructor(app) { + this.gl = app.gl; + this.app = app; + } + + initBuffer(data, draw = this.gl.STATIC_DRAW) { + const buffer = this.gl.createBuffer(); + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer); + this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(data), draw); + + return buffer; + } + + attachShader(shader) { + this.shader = shader; + this.vertexPosition = shader.location("aVertexPosition"); + this.textureCoord = shader.location("aTextureCoord"); + + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer); + this.gl.vertexAttribPointer( + this.vertexPosition, + 2, + this.gl.FLOAT, + false, + 0, + 0 + ); + this.gl.enableVertexAttribArray(this.vertexPosition); + + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.textureBuffer); + this.gl.vertexAttribPointer( + this.textureCoord, + 2, + this.gl.FLOAT, + false, + 0, + 0 + ); + this.gl.enableVertexAttribArray(this.textureCoord); + + return this; + } + + draw2D() { + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer); + this.shader.activate(this.app.projectionMatrix, this.app.modelViewMatrix); + this.gl.drawArrays( + this.gl.TRIANGLE_STRIP, + 0, + this.vertexPositions.length / 2 + ); + } + + draw3D() { + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer); + this.shader.activate(this.app.projectionMatrix, this.app.modelViewMatrix); + this.gl.drawArrays( + this.gl.TRIANGLE_STRIP, + 0, + this.vertexPositions.length / 3 + ); + } +} diff --git a/html/lib/platform.js b/html/lib/platform.js deleted file mode 100644 index e42085f..0000000 --- a/html/lib/platform.js +++ /dev/null @@ -1,50 +0,0 @@ -export const clear = (gl) => {}; - -export const main = (config = { fov: 45 }, next = () => {}) => { - const canvas = document.querySelector("canvas"); - - canvas.width = window.innerWidth; - canvas.height = window.innerHeight; - - // Initialize the GL context - const gl = canvas.getContext("webgl"); - - // Only continue if WebGL is available and working - if (gl === null) { - document.querySelector( - "main" - ).innerHTML = `your browser didn't let me set up webgl`; - return; - } - - const core = renderingCore(gl, config); - core.clear(); - - next(gl, core); -}; - -const renderingCore = (gl, config = {}) => { - const fieldOfView = (config.fov * Math.PI) / 180; // in radians - const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight; - const zNear = 0.1; - const zFar = 100.0; - const projectionMatrix = glMatrix.mat4.create(); - glMatrix.mat4.perspective(projectionMatrix, fieldOfView, aspect, zNear, zFar); - - const modelViewMatrix = glMatrix.mat4.create(); - glMatrix.mat4.translate(modelViewMatrix, modelViewMatrix, [-0.0, 0.0, -6.0]); - - return { - projectionMatrix, - modelViewMatrix, - clear() { - gl.clearColor(0.0, 0.0, 0.0, 1.0); // Clear to black, fully opaque - gl.clearDepth(1.0); // Clear everything - gl.enable(gl.DEPTH_TEST); // Enable depth testing - gl.depthFunc(gl.LEQUAL); // Near things obscure far things - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - }, - }; -}; - -export const glMatrix = window.glMatrix; diff --git a/html/lib/shader.js b/html/lib/shader.js index f92e07b..f8af62a 100644 --- a/html/lib/shader.js +++ b/html/lib/shader.js @@ -1,8 +1,8 @@ export class Shader { - constructor(gl, core) { - this.gl = gl; - this.core = core; - this.program = gl.createProgram(); + constructor(app) { + this.gl = app.gl; + this.app = app; + this.program = this.gl.createProgram(); this.startTime = Date.now(); } diff --git a/package.json b/package.json index ee54cb8..23f688b 100644 --- a/package.json +++ b/package.json @@ -3,13 +3,18 @@ "type": "module", "main": "./generators/generate.js", "scripts": { - "serve": "serve ./html" + "build": "bun ./generators/generate.ts", + "build:watch": "bun run build --watch", + "serve": "serve ./html", + "dev": "run-p serve build:watch" }, "devDependencies": { - "bun-types": "latest" + "bun-types": "latest", + "npm-run-all2": "^6.0.6", + "prettier": "^3.0.3" }, "peerDependencies": { - "typescript": "^5.0.0" + "typescript": "^5.2.2" }, "dependencies": { "glob": "^10.3.10",