From 88d329637146b579ab4f604f4c4827358a4dbff4 Mon Sep 17 00:00:00 2001 From: StepanovPlaton Date: Sun, 5 Oct 2025 07:51:25 +0400 Subject: [PATCH] Initial commit --- .gitignore | 2 + Makefile | 29 +++++ README.md | 41 ++++++ builds/v0.1.exe | Bin 0 -> 76563 bytes main.c | 176 ++++++++++++++++++++++++++ screenshots/demo_render.png | Bin 0 -> 7223 bytes utils/utils.c | 246 ++++++++++++++++++++++++++++++++++++ utils/utils.h | 80 ++++++++++++ 8 files changed, 574 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README.md create mode 100644 builds/v0.1.exe create mode 100644 main.c create mode 100644 screenshots/demo_render.png create mode 100644 utils/utils.c create mode 100644 utils/utils.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1ff9a75 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +main.exe +*.o \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..bc9395b --- /dev/null +++ b/Makefile @@ -0,0 +1,29 @@ +CC = gcc +CFLAGS = -std=gnu23 -Wall -Wextra +LIBS = -lgdi32 + +TARGET = main.exe +SOURCES = main.c utils/utils.c +OBJECTS = $(SOURCES:.c=.o) + +all: $(TARGET) + +$(TARGET): $(OBJECTS) + $(CC) -o $@ $^ $(LIBS) + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +clean: + @if command -v rm >/dev/null 2>&1; then \ + rm -f $(TARGET) main.o utils/utils.o; \ + else \ + powershell -Command "Remove-Item -ErrorAction SilentlyContinue '$(TARGET)', 'main.o', 'utils/utils.o'"; \ + fi + +rebuild: clean all + +run: $(TARGET) + .\$(TARGET) + +.PHONY: all clean rebuild run \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..c400f5b --- /dev/null +++ b/README.md @@ -0,0 +1,41 @@ +# C 3D Graphic Engine +> **C3DGraphicEngine** - это графический 3D движок, написанный с полного нуля на чистом С! +### Скачайте и попробуйте сами! + +![](./screenshots/demo_render.png) + + +## Стек: +- [C23](https://ru.wikipedia.org/wiki/C23) +- [windows.h](https://ru.wikipedia.org/wiki/Windows.h) +- ВСЁ! :smirk: + +## О проекте: +- Графический 3D движок в 75 Kb! +- Первая версия написана за 8 часов +- Рендерит произвольные объекты, заданные как набор вершин и ребер +- Реализует сцену с произвольным позиционированием камеры +- Использует перспективную проекцию +- Создан с полного нуля: + - Не использует продвинутые графические библиотеки + - Основан на [Windows API](https://ru.wikipedia.org/wiki/Windows_API) (использует функции создания окон, рисования точек, линий) + +## Запуск: +- Make: + ```bash + make + make run + ``` +- GCC: + ```bash + gcc -o main.exe .\main.c .\utils\utils.c -lgdi32 + .\main.exe + ``` + +## Идеи: +- [ ] Добавить непрозрачность (скрыть невидимые грани) +- [ ] Добавить алгоритмы создания сложных фигур (додекаэдр, шар и тд.) +- [ ] Добавить физику +- [ ] Have fun! + +### Над проектом работали [StepanovPlaton](https://github.com/StepanovPlaton) и [Fluorouacil](https://github.com/Fluorouacil)! \ No newline at end of file diff --git a/builds/v0.1.exe b/builds/v0.1.exe new file mode 100644 index 0000000000000000000000000000000000000000..983f82b93bf1a5fd91c231b62503ecd8ec15027f GIT binary patch literal 76563 zcmeFa3w)H-nLmCe7YuTlAfVC0I@+MY3Is?XsMr~jfj2rKV=fr+G8vLdGMdYDW;JdEzwamSoO7P@oac6)^PF?u_s#oOY#m^kjIk`7V`GdRLQ035|4#nXgX+1L zK0B8^HS;@{9dh`-b6HJGIOd8(JDQ`xHdjNiy}cvu+7@y}JKJ61c2`+twX3b8F|>Tn zob&S~X}_1T#!u$5vJ*dRVGl62@jS*x*%fS7M#j~w1V~ro%)q$-XD&{N=Ad8715R?& zk=v9w`i>g&yBO_>8tEe={SkYV#7C#ZjYREw97-DRn%8!=@87mkE zKJo-($4QR~j^*)CcN}#?Yw<&}k?zpLe7G2^U)~sug9USuOn#hXfa`IZ4mZdwj|#+= z^HEWcb2iQmI8BEewp<<&cw9&5%IR%3AtB50x&_`a>Cl6d_*UUG9d4qxEfyot2rSPA zC}%%0oW#G03dZ5)`i5FN8f4!Lp^orcaGDM` zEU=JG1vcPjqP0O8^3deC?>SkK+TLoG&`>QpKt$g7qBdk~6qi!=VaYQ}m?9C823A+7iMg69UFA&F8~0Q=2 z(+=mZJAsp&0nvB8fZWmSiy#BpPFB|=-#6ymwF4<+?L#Qnl1xJ{&GxPwdX_qBBXRH_`I9vND^Jc|l==f=mewC}Xo=sZR-=7+ zVie|E%*vAs7HNsYTJLi?sjsgB&bt5T{I2Hsjdm2P24b_U0<=@zi;+)eAHIp$Wv>D# zIp;cJnqyAvXq+3RBzG@lVP_aZqugXBnk0&Pxwn8*xhiK8ceja=ezYA2xN zuQZ;2;8z1U_1EaO0rJ5D_@F-Cc|B;c)Sb5a)I}>un$wPp#XuP_9q@yhXm#RMKD6BI zIa3{FYqgzk4U>=ce=)xYh+3kb+#vB-3x0ixz6ekHyOHetCc)<8bnfbanM~zR9m!H< za$@JMf3%iAwHM`H-rgDCA~ViU?be}=_yq!*;oS8nP&n~1RB6dRlEw23CeOcs3@EgXVx|f8s%b z4)W#}<%wg0+)<95d@K)Sl6^#M>5(SqZ-(A>UUlSc=e)i$f zc3wS{=o|!>@Z@8> z#0_;=Aru1PXrB+ibet?n^1Rjn0}X43M=~%uAjw3)oN+BNo}CL(NJcGjB=w04(Sy8! z;}DIJbv%F1*A0$DUf8Kpa7^|YLM#%Xar)@ahm3wa0Ln_Q`%Ulxz>oH6Qt}fQp_t@F zyYPu4u-F~fkSVeP|ZCvMii@B(TB3!q7PV)$9^5@?jSaI!}Kj#a4U z0L#8Nxt$KDZiJmcgG@&tgZoD&<^CuqMBH=D-2bly6LaU9-b3w#rh}y8t#c-B(7$lG z&Sj9e&>n>v6@d#l`*oSg&{WZdoz_H#?q4{9J&>@N-z@Vl=TF>-%REHwxXgUSdJZXe zIg_&cm}19UnK{P6v6uUU@=bCT=kDV)1Ii%6nLG=s+$4tauA({|N^gWPr1X6!Lry_|REz7SHL>{Z>QrG1B zH30dBh%#=fMOsovgSITckha1|3jppx>Y|il`uDJIKwv$hWGYX}K;|a8z2BsSb)L`r z=EOFF_4f?`gemRYYe>c;sWLc>*6)Sm&iQ8v%FN{$S)G)K83738i5K87#Eiyu>f_lI zPxMF_nots%x zKCQ13YPG~Kutv#EROD)j+B{#v>+&Tk3XXcu5T%p;r1y-rN9$kEM@22^&4bpM(pWm& zCEz*f@B$9M?J%ZK*6TgvK>fAe+F7P)o2a8;-cPI-yayYx*p&a7(w(}Oa zEU%+-uB_vDsC)5k=WSH)B0eTOPQ%>Z)RySryyZn|CwK}usXf&p&6Bd2`4dmhKdn7z ziS`3lwanzugh(ADm?Ujhh3c@D=P8o&U$n&Q16qG5=T_Mn zy>AstH+zO6RjPUkLWK14t?6E?yc~)SH8~58;XF+GzKC%AqMM>~0WBF*f4K}n!kgQh za;)>7i91pIWzpiD|qL%QEN&!!N z1`-RgKZnMuXIn^Ym>T%bFJq0O4^s5-5#GD##;p6tQzF}>#5%5oiz{)OEAgw33MDS( zO6YwLduG2Ps}?~HT|bv|_Z?Wb@-bavH@FHaNvxV0j!4}`l887B*h_LvkHd<-hM{tl zfIX}_rd+AIstmeTG#mp?h4agYH}}ux0m+y27F0M6qw;h`19}RsU+SEfwUZo{fMpbG zo+mk}Jum%}bg>hou<21R{jw9U@cdgm{}g_KeTs8ClEy7haC-~fz$th3PGF({xeljc zx&-E^#LU`B!(OL5fffe#q?j}?fgrKDzHgC+Z;^)1-PfZpdcC6r85Iy0hnynFDFMk) z{Y73V<^Y!|4AUGyA<7uYs!J|Q*23vt!S3NPGMAP_;BmYtqfQ2QiP|&Q*5=;xL)mvH zJ_|H8uJ23Mp4J*(B)ZNof8Rs|Bkudj+EbtmpPl`%$eQXgzaYlvW{?#*jdTg9a)JsT z#UL7jL+vSu=G?uV+XIeWdzzPTSJrUp)<9InBrvanPl-zT4ay%^$f1WdhhvBq0uEGVVKsnWc#$=VKUQP0ss<65h8I=&yB9O1Wx{ z!*JDWkmoTjH;xECiVp8=<>k@NBBW&5iG&)}_P|Qn^RA;^>AbA_Py7l!Gj$tXkrTAA zki1q--449GZwW>_^@jJ(=)E^LBR*G+g%e%_Iue&MfsXq4f9D)lO{B1_cMTkfqIA z>MOMNPgdk%sY{JPgOH8YE=TGI(g2orBWR)fub_=o>``-y(g!y24$FcYYnpl9 zS5*n}&hs9J(%HXQ$9={7y072+rq*A92DL7b6Wb-sb3=a`8&jj>uV_$^iZeqRTm3YN+EF5`eg;)}TGE{h%a6cAMvac=+niE#Jn+3E z_^H59yMr&+UM0|*77+C%9~B1H>x4k4*$&Q)f@JQ(ImpAqL5fzzc}WB(e)$VaJ6aUt z5|;O6YRH}A7;cq`HDd0^nF^EsH0)_{HhQ8eCl; zR2S)9yjyIX&`})Om9!u$w7=zO19TBrXBScIIQ4#Qwn%H=Do=c0?p67K`p@6dQ1iY; z`$-OX=oAe@NYt-kDAuAcyl8_+dn9%3+Z=a9$88|o{)$%(Jib(%FYsQ~@vh}~UNrO` z1U*!E4+*B;AvF-Pc;M&jcp0p1uF2g}9|)LTGCL7ePiH#&zB5p`%hqF z&iI({VR+{X|4)|v>p@z`u7cb&L&<4B@qO*^&(KG;7=$z-XTOA6k!%bS zTEdgCL`Tlchu@$}kP{HXAjdX^%OfXDjHua`@%1|us3Urh7OEaqF{nh%&`ZV{#%Z<^ z^K#~WKTt#XKAONO*t_%bGlxMn8dR|EPq2tSPCEel!My$Zi)qJ`JLeZgl-_|2!f%GK z1LEUP4QQM~G0#ojP9?2CalV>Hl!9I@GyVeXE%}JVY~zvx zodyTrpc0?#21r5_2}l&(1$;P)tt6E}SZtw*-QGO(n|ctB9wT%psFE zU{shFMGh##rwoi243|6pC?Y1cKw!hR>7FAzW*Y4LX?=QP2<|C4h{T;Eo#^lzndDLG z5{9b;2Ol6;+$#01AlzD^{af+U7K zANE8#Ph3q*&By#m?nT^^?gh}-xw}x!6UH>C2SPcLy>F?^sLGsDL4jsO^kKzY#AYI^ zBZSk{0-Iu$xiM*@G8@X3uQCGW z636y>U~I`=nLZ-Z`(+xH={BCq`*V-cH3@P5^(xaH(WU|1T}ozmQEfjl#qdfjY@s5q zsr?)R6j*QIGk!0*Kua!+6%oaZ#Npo4WASx;FT`DmW4$LFTK}RW+R|fMfBeX4x_>@4 zU+X=Yqpf?p^Y^&O*-mx?(Od60k@+UwE62TYlQ<}s^}8L3!@k7O4ZYVLVV$EV_gm`) z|73Q4J#KRj#mPpXX1;&A@JFk$y2>64i2KpHjKz>~kxfRceBVU`mhv_H{OsiWytRK; z4p}>l?BDxtX8a=D@SV@E;R~ax`|YV4&&NY?w4mnj0cGFpw{g3$wLuJ> zHA76TssGaT;qrAmm&Wtm&ZYO{`^%h5%kt}c*UfN#>N`*(2?to%S_nxRvhGLd8XM7{ zhgj@ifw`p2nd@<3X4#R4pN}D^ek?~kwGq33*UrNQ_wvMXPU+oe;6?Bs4C4m?@veLUQz$my@|hbSo=I5uTy(yc^yeh2a>^aU zC_3-QWpn1~J?sE(foI)sJCAFLAEk!jinzZIe{t^mC3$2ryO@~tFM3)#{Bz7i!0L7# zMGb`dIo&_FdA8>eIvXgs*P6^ek7L*1vS-4Vmp8VsIf5#pb}w*}W%*_O{xiLA{3)K( z?;q-Y<6k;2=*|A#Z5XELJU@`1X6ut@%dWfEr{*8 z&2ziwj@u!983mqqZWVzi?kN@%?!qp2Ha^QTm*(fxQN=Qc`~K7Ta( zIW(h#{HRb%WPcrHAFg;WJ&q?M&b#kX{LXSd)QO%>&d1Y6eLa*b&4P9NIIubg(ZSjG z1UB9n>9c+7e(vnO4VwFs#mAxV$!53&O07Y2d7?uX9)4WjWDYTWx=fzhh-MPL$!i!e}i@}Icf%969ZeJ^y* zDTB@FfvsKiWDGa*A0Q-*9D@VFf0MDSvgAk5FvpvD^_m;{XRk?>pBWqTCazvXT8x~y z7JULd4>TurKZhyWN z2kGP8<9xc4xp%#@V-58r5m4rJQct4oCpxs=zx3P_CK_KN8V`WRdeGo{D)jjGPyUq0 zL)qN9s}00Xep1ytcl{iCpE!-5pzVnTb7++`Fd!d&o0r7p%a;;|cm51oxOV=rv~&|Z@*&V& zk9!Y8U7OY?k!%lUV_x`Z@9CLccVPx{LBjNTY~?G@H$r{B#%E!8-OHd?Bnwh2IhQ`@(IL^ zMD|W(#}fN;I7!F4VJ+#&?L1il$Q9)YcW$bm=8*n57jm+rEr@aC4_K5N z`e*;B783qIt`9t0)bpNLF*I3{}_7f-}qp$zO(};KQ zdHCJT&J>>89b4#w44Ae;C}@2@iC?EBmwk+b)?b69X&^ak><3!n+haeZ zV7_Z3sMFBkV+8X89=N0p$E~?&+llSuob{aOMQD1Wi`-}+nSB9g*?AwH-$0IrSRc(} zU^-eOm@YDzkp>y)r5dq}C8ECp(G=kw&Hg45I*w+4UFOCX_BDgk9FSA?OMT>>u`#v& zHR%sBkE7Mt!duY-yyHX@3a^I3Jsh;Y9EqVX+~G#(9N(y=>-jop)pA-Zhjz1lp zgFfsb<*9y0-mDDgqeB3>5zjn8JLdeFHlqoyxxUQ#HTMF(Z=0RCp}&mf`TBjw3Jm$m zlJ40)Y{=FfiRNT#*g(7(okfdA-@0R+KgZ@kJzSjlZt8J7fP;{j)AezTsYcu~$9%l= z8pw8w*02Fg<&)c;H#F_+hD4nk@N6}NcqP6L(k#&WZ_CN+-+-AR3+ttOF!9de^3v?Q z?oaV6J%@6jj+XcxtyHfED;WF@4Lb#jn|$pk^!0vC$GV(nAH$k!mkH9*^1t#|!5cWCZTXz`t z!47)e_oH7l+XoZEEyVwYaUWuznK(v68T~D7@*C#wm0PxgJseWfdomDdaLuWIB5=vr;vkwu5b&rhZ~YB;3oKn>#zwyXQbKRBb?$lUhbOI0dHbu>RzBN{k}4Ox%5vc zp^u+&>_6LV|D-$r55tF3m#XpLOD=xl!!(&3Lzj4VUQ0u#yrCK{@nh){b2PL4rC*#( z-ai2MgIRxOc*tw;75Ht<`EZYNSa^2A*I)CZ**%)zA2(q9ynx{X|F{AEaV)w(>pg<; z^9sh#3&e}t+2=TZau%%%>3M!QK&q&)<7r)~F;>s6NW3<$# zeedFG)Q`O^|be813H^M?mLmf{sQTb9rF? z)M$%SPXy`(N1B^mBm;KKw0}jhB=vFGR&OZ?z%Nb1uW3Neb_HdL?`VnFe2M3@#P?GF zg0{VXc5rEXe>9f*6w0Zs_`!cqoR_+VDC1gKVn*tE-2LF$Os)5x{0NKBrgcChnUl&w zjhJic34LR}9hcwDY5XoFe!s%p$NBvh8OiT8#oEU2QPhmbPpn@PFQtCMr_w`kLWWU> zCunH7i1kP^`)@A8Dzb#dv*=pQiGKv1$wIt+;w5oy!HGH#zqr!r0vTLzncWhp#2AT{J?n^ggeddxX|Ukm;+SKbD)q|Ac@|_7CTR!JQ)i9Obc`A^4Y( z2LUW+^zkg)lYD4@y}g=p#W|`KPC9z8_n%Ro1ICLzbtT zUR5vn{qK_Aj|-oqze>09vZ4RwvVQW^z<==$L2sh;5m~R&s4Q=jX_ZVj$aIxVFPG^B zGR=_b?`{|T-jL}lJT>I0lJ?#p(^WFPT&5SuG()Dplls0P(^q7AM5bSn>0X&WBGdb2 z8kOlbp3-_}=UX%vtb8Zcabj#NbqR)$mdvBY=du$|qJqcsT`$CIdVlGNulSY&pj0Q2 zFnQ$keB6QX{uA0LJDlH|)$>Q!eLqNy_KrGw{&=~wZ1m)7XlGvD4mh7KMT6%YyN2Rf z*SYQ+MU3dbC*So9wzuqU?H~DWcx>UfaX||6l8++l@psDdZ*v^N#sN`h&g+Z#wX+uBsRj?iSu8f9r$7wZt|V{k}-BDcOv<+Wk-${@8Qw&QG*!e)5}J!s8jU{SEYn! zjbs$^aS(oVIsM}Q+3RIW zi)dWBU_-JzEYlI0x^5EqWDCNrm*t2|0h;7ceZo`dtZXv60rGjM;Xi)i3l`n+ztL{e zUme&S@k5nKU-Lng<}#>S+7G3bb2nJ)haVQ@<1#&1D$1iW^-KI7Sss?rW&gTbh2FGO4;8NAsrO;IWm2DK$P8Sem_xjPqewSEz}-&HFmTG z@s5j7G};k$6_iz0c*-{~WqNfi+}_;T8jObHJ6r|T<(p8)kIL<#Xj5xPmn#}`G8F;_>ED;92U4>yJJZjFYJtHAH8tghyiYC7T*V%6wa zc$Y;BUdIuNb+*P`cvVNdqr(+z3%0f*$6wSDil7qegc@B1wVTV76t(SlwRdz$?YP{5 zwqU#k$`&lW#*zQ$#NbiHF-luBKqPwKE#Jh9*cXhZ`FqQOva@;%W)TTP zmTK7*j<>iP@RAZ&VfT`5!c)3OVxi8)4j2#bnFtbZdeKR|qoJeKg+7cCU`gY0A@$_# zwuR&9$Gbyje3BMmI}=C`-8(wt)cf2?u6D(P(dJMbmP6Imt{ve}Yawg_8kIh(-Yw&)D*MyQBVpTkv&8@OdRw{P+I~+O zeb zaFaD|D0o^pV1@Inc&a`(cYL_MzDkQjs@(_a|K=9R#oihxt-8_2Y&jlp%0eD~z;@uA zjeHN%5&R4M0^}+EGtPYE>78tgah4(PLQ4PU--x^$sSjr>@_wW@;fx?(kMs##(}*Ka zX&0`2^dR4Zlr3g#H}aJJ5a%A`DP8wh*qI_vY2&4g9YCJazuvks?p&>}9Y6@|3dW&>MM5H{ki9+mP=;dIV=T^210s6at34AL$!7 zcOyTF^lhAbkY_6yTY>X)$YYGL|AliO@C>b9z%SJ@<{&;=gY{CAT7s3 zmcz*Vk$$C!vDc9wLYiNKt6j(!B5lXX?nmDsy$5Fw@>pN6_18cT$x2lfk`Pa%(M5A2FE#=e1k zA<`zC&m%v)k+Hp-Aj3}JBmFAQmjVBubT}Za1OMicwLzxake)w-oxf^EVRn61B-5WU zG*i|`yr={In;8Fpz)8MBX?lIu0>&=FN%fTK^obXpxmkU=nK_PYn8R<$=VWH*T#%LN z@Kc+NY|82BtSs-W%o+D*6aq$gRT6JjRx#k)Y;eL^C2=Sp)$y}uITkTao8XU_a4sz87YRO_V-jMC74dIeCh~9Zw?7T+=N5}k`-uanxqvsdStj~#@Hgl24_;`QSJ=3*9Wud6 z02=^o!~|;v>=0nNdkr1~fTaL)nPA@l>@;8{CRo8gq1}GOSQD%jumgbQK}K~v3fL%M zg(lcBz;d3%{9%F>d`WRbSwJ(xoO{RXC*2%P9rV76)tsIx- z-D&X8$nujieM+WBWI8O9TGX0iJzb#WG z|FA5-A=BTf`Y%YnGF>B6w@h!4siIRQ%eSS$n`AjI)B9!mU>e>YS$vfccT0`+r<+eL zc*kmHHQF`A6;{5aQ;NGZxWm#~E$=bh;PB#(NHiVHdWnxO7>j%9rVr*Y!54RIIwR%n zO&uQgaAtWsXa!qwwLiW2Z!&$M;C36BeLA~36t8V>3AQ)3h8n%y4WS6#ZP*xYg$(S8 z?CRD~D1wJ(Yg%JqzlE;%vv;!PT@ZOU1F|Rxzb`_49SB4Mfl&MQa1^&quvX*cE(N%d z1!AFipgq(TpjWiBix>>R->99(nxdf)V;3{JQPk0Z^*-7Hxic1u%DX^}J;)k5VojK{ z9k|nlbiO0@G5Sx(g^qZz9WT!ggkx>nwzY)R4P2pzTS_-rW(P3Rg4ec*6*K~y&LlXX1fJY%iJn*s3aKl{zLFZBh<;5jTfQz7? z-qDJ@G%ej7WNeKEgR`;JuNC#^;x@edoA*Vy9XE5rje$rIZ_|f+M-5BWvf#E3n0E^k zQZ+KVFBohU{Tw8@!yVfKO`Yuxs;3(|+IYvZ3}K-rQOZOq7H=5TvsN0+zT!=7Me zp(as;&o_85)3LH}EE0q;6}VxDdxU_EGH-jMq%qrpA=FyMBg6^^hE8c~7+u2wAE&b3 zY-UEeFg@anz`riT*AZ+i?Tp4cq8|28hQA{gzo|2qy@x-faL|u~^->m(B7fF$Mz2I zJ-&Bj@95ssdvl)1rRyv8xF3Ur4dx8y4Y~&1gW6!-VEy38AbULL@!ZGr9xvSI-sj&} zzb~?{XWziS!F>n!4ecA=H?nVZAKRb1-?hJRzk9!bfBpW*{+|5<`v>zhOEp=h`P#bC6d zWqo(anq_#bZ&|bYt6b$;R=FA{r83{NtuxFYUR%~0-qzfJ#!c-V%VP0h!(Geh@j9&5 zv9n&Bx#_}8F38`3{cSii95eW)BO~X$)($-S#(z57X%~dY|7g#Uk&|uIa6ra+u@-Ea zTvoKKY+jiqutoknD@tw_r_|Wdi481R=jhc^hAdT;m4#G(p9 zY#|2~GQ%)afXs&tWMjA?6k{i$-bWlSIbroW)Ya5(_6Dj;HE&t1&ufPHJykX39$%o! zyD3oS^?UKO=H}8{n8Vbn+8dYvD`R4;js8Gslfd@zApj^fGUCO<<$P z2WcQ@U}I%fV3V(uWg8gPsH*n+%4IzRysTL3oW*P_^v6KyzR`>&+&1HcNRi4d2TD=N4=X7h2hq0!J&6*se zx{`#fX0y}KJib~JD>f&s8uIgMps~4Wb=IbV3U38-PKgpIuc*Lf7^^mEl&Z@8u%^#b zQ@+K^<{7x2($dH-7DUsdU=+*Iog)K*h9zo#rvtyNaRFKaiK)|6LnW*@TFR(We`t2S4&MFx0dWo=d9hFW-zue_qXhAlR# zJU%?p9jGbaq}2p`m6iT#_E$zF9HZuDPZboY@^19PC&_FZyqmon%S$nU*`)@muhcIc z)@1;zypXn{vaEdLE$lJ_OqML)Tw3NWWtSV!^365gP2Q?N*+#VR^4_UtA2#YM{WSs4 zrdpP7fIM3&%gd-!_|Wu}l~u7NMr}17Lx)S%`pB`J3XkVUTsD*YH~ zH0(>cvlJLOvd^k(s$dgz(Nd#|8>=dC(?cU)!QVW4yCd&x#y z5JxyI*t#;HT(KJQRQFFZTeU~q$gZ}+N_{nDY?&3dp@uCtAVMK)Alpz;&Q?s0QnR&m za}7Sxr%Ny{9bYwDX+kNxp%y{2b~DDBkF7E)Dlr+9Z{z_QonO6~tv2eU!8c+W@UkKU z?yd7vsPk;7X2k}S?4Xr%C)rpI)332sRhz2TTB|m0tnzyOti*tN>uLfeg{w=}l3)n9 zn`^4sI-`0Me7#D{RMi0y>&v0mH3kL&Yd5QY^i{&);i%;m7#-J6gauE)m{MrhS<(ES z(i`Cs(!a=3I_7#SX3Z^rug_Zz!H8ZpyWUz&vJ?h3+!FBDRaUW&8kIEAsvtBbE%LkS zKuu*}3&x^*5|oWql@%PxGcJ;tN2?Kys@VnuL(Ghpfn=JaS*d|ZeF_<%NWhFyHZF#m zwVBtb=7Zf|t<`Saz&09S;aNo+Zo#}jjq8Vv?nMKg9a)2x-%rJfox zm#i(f*2<-e|3rO-n>mpS4mom9-T{tx#y?s*;TroSEg4NuUuuS_8Zk(R+Vi;#44-h6N`xYRRIlw)nCSLGVsXh;X0+Im`S|>Olh0T zwy-Lr-m|3+B1|+cswY9T4viWcrfIEJYpH}q^l`GqQbUrcv39eqM%WXs7w~(lDp;Kb z0fsWK?ryPE5?vm*X^543OW{~sZ3sdi1LIZ;2GLI=ahnYxoy6@njGCLxGsYc8y<9@v zTwbFE(1|QyRC#CM2oI zO?--BciC&Sax}%Nv(>1QT9b`gn*kP{?5mUu(Q4Li)X}J_E7dk_U>#N<)GlNZ14hb< zuu0P)`760rJ5`*bcepq&>Q2&-qJ7Rj%A$daAG|qQ#oE z>?do=4W>llppp&P6qCzyU>%JSv0LQj4b?Y${7mczIe?3S?{zz7$nCn=MdxM*AWs}2 zh-KuSr}Fx4on>TX1vb*|dgU6Hk(G(tm66s^x66R#=rFMf&&bsQYJcw1A(kC|uA)r5 z4ID`t!1L)&Y9JDivW%;8mt$+6^%vOc5?m9R#UM7Haed_&fC07wuh}M88M%yv6%vd$ zsX}HpeUQY#W>E8cW^cgn%*>&O#lKg~py%)JmB51%2=xE9!!eKLUNp~ffirXNhvqJs zJ3kwo90e>hXVENNWyxo=W?erk6IX{avmBW@*I#zttjn^>=U;P~(fffLI?K@0|teKnhRb z(!VNTWIO&ecMc_VB@ibv^r{>@xsNjgd1nS5p4W3H$pUmem;M(F z{nhQ7eO}I>v^L!rphI=Bx*y<~n|ptTXKr3kX8YXSAwXW(OVv3w4KJeajJ)B?oh1ac`;83p6rp& z2XNd|aNiG{LY#&Tzk<^6B$V;UUWBB?(pt(ao@-^yWrj4M!0+1|dc6qB5XYqHtAGql z!i#A91~@%94GA6SO4k&vt^iKq6gWQM(2ZzA!YzOdOkuHmfs;E0<^8}JoP-X&x(BbU z;ofU-e;%#;Q&9dfaNJYii~=Vz1rGi_j@3_rvj{jNILY!LiDNlRZ*mA(ZFwu6?{4g3 zf6yU>Q-Tt8vuFhz++B~xvpB?Ph5yvlA$W+AKQrypTM?a2pmT5v3EO}(GzrdfP{!lD z{D2D~CKQ2(M?yrh91p?Uq;^6(|Q0r9%g>AYGLJ$dbTJd|nfq>j{ zdg@j(gCM}E!S6x~4i);KS1y{8DbwSaLVgI91ykVs4RA(u`&^)!>Zyvx^?=A|yrY>` zPV{i@wotP|+y^>t)jMR!aSr-&oL}KEWqHwxw7`W1_=Nd57t0r-p|HG}g2*e&lf8xI z`Qs%PL}huhx3Ij5Uluex8I3X+QId)OpG`lM#mF6m#b_+2&t8b2AW*NYVO5)x#FN(* zXvm(JsgD%ttD+TgzX)oqu?S$d*aEn$Ra@n&ZnP8@qn!0v9yR<>XiBzAqa*#9#*eTn zSrWtT&unS2ajZ(xy+sPmQC1~L21NUz1#%%E z>*ktqt^}l!o`GR1)={MRiv{8Vszt z$WrQFyzfHbsF5q|On!I-8s?KB^+Wlg3*I+X%^BJ*ek;vE^YJP5EoTh--!C ziBQtYk!f+KZ=qN;El%0E=i0bGZ{yxQE$(}4+(&HOb8OrX z8QkTL?0fKB5qq10cZEpWa;?>a z#t;(YJ)H{HQDS_pq8iTKv4Aiwh!UsBkXb^CP4$2bB3ejO$gAv58;0tk7D^|%2zEod(09lDNSKW>pYFg564;gRNJ)Pm-edN`_( z2UBI*q3nDfcnLe>>dZKc#uo4rHpB{K9JM0yU5fgg%lX`mR>Ryg)w*RjAhZq_^3a;j zAVza_0IyP|UiG`wj8B9vFKpZ%tFY*Dk%0rE)FM7_RFXn-c=!^DXobOL{m)CV4b;&gVaMKT^d!fGm zv96s7U+F%8yoh7uKlX!U9y|+~Kcvgtg-6&Mg3-q5$?Vo;w#%Yq9z>Op+1_8&iew%- z3zn{|Qh3A#E5)>CP^j+7Hi7ce`F>4eC=0lax%h^Sxb{Io=wJ zbw&({i3+Z-lJU6AF3{m1+t2gBDU>)AlkhG%f6m!Gybn0SfAWE8jbqD8 ztLS^NWt!3}?K(CZFo{#@o(xCMl_S6xR@IP8)1C7w+p61eytRbD@#vPH5XZOGZ;=P= zOox=&Q!*J2Tq=L-)~(e4bc$LO z%sqNJk&4m9XH0+2X zz1u%Ne7Rg_0yq7TC``xES6>1`BaZa79`Bh|Hh+2?O4ogaTkO9cZAL(Is%SHG7BU~!Wfq?cSjN9eVD$7ma5o{dYupG) zx@AKU|A&Pqh#vhAAg7ds4BryKOK2KmG={j$)QDY)>`=b+9}e*VGK!pm<3l!rt`Xyc zW(0^U53#nu6OO>?nFL40OvCbJ(CJD=hNtJ^b-IN7RmT=GOutqM;e^b$8UjmHHI|1_ zBgS%}l?Y18Ge>|UJQIdWTT4(Qiiaa-(aZk^I$|uFUbJL%GW!B-SXeOeSSDUW)GeSx ziRk6;A{$YybPpGFfaCD#n8@(sT|mCi+Xx}wPKJkFk)Xz}&WS;$QH*X3Fw8dtA; zGWGi}z(mMA`8;Uo^-WNm{ygZ?^@8wehZ+~N^*$X3N3DC&S)#vSaE3*L=}`T?NbmQF z=Rx;b*pU23*bs};c#C;!&biVxe)mD(Qfg$5N=@6bI-G2&cy9C-)4K)3wFQLFweEI-$5 z`8Hg^Gh;TbP&&&IM{&S<=vPIQ<&lne(1`0f3z_qDnTg6h=r7|q7n%K_gTL|h zl2_3Ob(sm%evr(AXCX6P=@K3u!J@9gY-Ub$09pt;j~^un=?UNr@V)>JZ}k;GMl6t{ zfb?KB~{V=ERnVomp94J#?A+I zJ9EV>KWbbQL1{5AtmBW6RYam^lEh5Z3Mg*nrXAE@Lub)n8}P@K+m z6yVoB>)k1Fcx>c%Sg3{d=$1IRy$(9`stnT5ICML&RAZ3ev@sBnQ?uTG)y_n8my{dHTTX;_RM`=gf1 zK7+b_bn<#;diHVY?adkL_W}S!zu!A=qJF3QX`)8DV4|1gSh4P$oXP2 zy;K|{IwFn{9qXau*dgGEIL5umii8r`$8{F=*#$ac>~Q;t7lO(;UcDW3h>xAg?IUnV zcMaUh3dW)A69JfN{;T$B0TE#z(%pI}`#cYvsqEuE3;R3@Iwt$bm#GQ^sW+?UBINRU za1#F!IHWtpXDgzzPY-zE@4svx^7*O!XBaqB*{A+2>@x^D!anpLp14%nZoUVF@4>)@ zb)V;5^%5Y*)#%pO&wg%0(%sshR8saC05ALjhoTwfj6a8*MI1G z(YV+ajD_fhx{;vb_+w}xWOj`kHwf?7z^PZh!5jXGtf4~!cwa6(nCo6G{fFb659#VH zkUT)BLq)6u36uXQE35}jkEN9c$Uz+NdQGPmB9E zY}|8g-07KhEBCmKd(X7E581fq*|@uH-0!z>e_&eNpSN*$*|=*q?hhK=4@W*5b_^cJ`*f0L!wi|n@l2`v zOxR~4-BnC@AN4BotN!AvI4Eg4lokcb@)P5%J*Irafyal^4x3)*G~RbB%KCU8MG&)$ zucADqg~C}=Wq6(RmOaW_&WZbgjr(aEck(fdJZo+mfB=`>65_ z$7NOolx%vPlh^qaW#!Ms2W(zPYg((<4P(xg5a}%kl((D{cfXB0{pXa0SJ81B_cv_Z z4^E4Fjg9*u8}|_#_utvLADR~TIve*P8+Td_TCA~#E?ihGKQt}wx7oNKvvH?&rj`38 zHtxr!#l7Cfeb~nRw2k{38~5R9ac{J7KW^iWMPHikZX5UG)8gJ@<33{Jo@3*FgTY>KRqq(Z`rsP*tq*`+>h9} zvtn_5;@tMIqc-k^Htuyc?!z|jIn&~P%ErCK#=YLg{iinWxzpl)+Q!{&<4)H+E#qQM z%Emo!THMdrxNA1<5gYdm+v>tKE$(#l!lGBP-^RV$#{Ci-_kwA0&$4l^vvKdSaWAxS zFPs+l92@s~8}|on+^@HBFPRqi**5MiHtqv9?i+2~-P7WpYvUfVao=O(-eTjfO^f>i z8~1J-_dy%?2W;H^)8d|I8~3Me-0P>s-DTrG zVB>zs#{FA1?k&^eo^Rv6$Hslg#{Js{ce$?1#`WHb)^%!4TcXoZYua3cmt50Elos#9 znszsqXWV}#S<@Oat2M1jW~uvSljyG2e($4RMaPt#Ctj}>4=T$~jI*{t`G({23D>l` zUgxx`e9AV%o`wxAvtE%)X`xoRYvQ)~y<2(9IdMN^d6tAKJ8~5D?ciHbNCh2$O z^M)46=S`YQpYKsxybnH4Pd-fL^M=gI=S?z8-ODD?UHSa`s8>;;>aU6Yxj0Al^29i6 zzoC3%5}()gIw!B&ttcxWD^A&bK38d>aMm>0ysl~z%PX%l=qRr<@seIQ@IHE7<5_r} zA+z#2lg!fVdMD9cdENV{S5cnouZcaXcvSWB#5ijQly5llCycbZUgzX>b&9g`sNzFP z9tA0)`=^%JyQawSI_WKYl((D{_uFjThiu$aHtw2@`{1;=*W0)svvD7_arfJ}AD9;R zMjQ8G8~4*T?sYcq2dBlo#m4=(jXP7`t2|*%y}@0MxQEeds&$>=KPuXod`$Y!kkSGg zrysUE`{Uu9!+ltKa$%}D&X8F}8Z`!=BMaiQ)yl6map&)CH8(t^9<+SpabK*W?<6dv${(z19 zw{6_%HJ{|B=XOu(bsP6O8}}X?_d_=BIn&~P+{WE+ng*~EuyG%l7WXV0_ZA!X0UP%jHtu_-#hux>*W0*1VB`Ld@l>arn+B)F{fxTD zs^VC2osD~sjr*@`+z(8P`)M0@zm0phjr&h++z(ER`zafD&Bi@qN8(^N$FN|C?~ptvejQ0F4nzM?kVLMkD-bXNBVgMD3R|SnOlbIJFe_ zG!@Pg;0#$>T@Q#_M+wTC0a4Gz3CLDJ)Gk0kwo@y)cMy;SAaqbkKq!iww#fVsfE-7q zf%7yVbS=ezJP*i_h0ZI0jKYpImIdYi1VlaGA|MySg>z+W5s+1YsE92f8vyB%<3d1a zJv$67s0Rh)Zb0@}^!gMahu{qc_dS5*SsOq5KHg0 z140fjGz~O}FK6XglctVK*XIs)n9i5GGytmPZPu<&ldryw^(Bc5Ox1TQ2rhu1!!eR zn1MGvwOG9O0zh0AI+p@cXTezsh})v650KNA7ESBc%lpzo7+U zPc#S#En~S4I3pJS831I+Liz6jIcTB$WkBk6tCB3=0)$onOnM6Fk09~oQ3(<3h4AB_Dqa2RwqBzzPQ zYY#pNNH_nciReY>4agyjk9`9W8mER9DL|OThQ9~Ih%LTwd#I)Z7Sk!?v(fO382`Gc z@D@;D^A|u99jCfA6p8?vW3jvoIK#T->CHRzuGeAUK}f0TO5iX{53UDfkA=>SfVf}^ zgL@qyJr|sE1Ed8|)kO2$i5FqMl4bg*d0z%KL8}gh6 z1b-%87c2xXeFniy9j3LhdULy=^H;zbw(wdCNTG$Y2N3IgdJ`a{Xl3|D6p({OV&@_# z-%G7@%af*mOArgMrvZ5xHKH$K;pX;WE23Yqna*M0?6Fw&6+i|pyxt%joeuH(BOvNM z_E^2gqwNiC5w^$DYSxA6eQ^mOWO+lcYXDLI*&+1W1jtk1VDxDxAaodz-GHbo45HO% z0U5DSJ_Ja;1@bZ=>VF4Bt3LyxcF+Pc7hf7Iuy~XU5IU${;9L#Js0C*oAbyJtH9%5O z(~$XAK#p5_Cj`g=3nU82K}(-L49F1J8r+`*B*((*Yk&}f5w%_fq{Kq`SAeLed4(2> z^B`fd*%LkjNQnh<10XI79OeL2BbvSg0H$U zmep50Xs*NpTh5gbNlI9OR>$;K1aAdIvsnH?Kyty|(Cc%6Sa+IV0OWw)r}1DrkzxmR z33+S@!iE;zzll~OmJ#WX1d3q0mas%zrjIm6d$AELlLg&Iu2%c-| zixq%aXDbgNew{MmZw7?My70_E6U_~vqQ^{cd!rwp;EErxw7QKbTe!Ca(qn<#56FOS zAL8|CKyq~(5@a8>g6<@N@Pw}t#NsW_0g`8-bDZc{?0fHm`KWV7?42=fZSliGKwd{H z!x|bO>UlqmCi#u0gBI@Ffure~Hg!eul0u9x3*{c*oVMuoARvQ)7!uM~UJqDs{uvND zj0pLX4e~l5br%0Q2}u1)bF4cJ$dJX(nfQL}04N()y%-Sn)l^}_D*!oQ(PA|q1r{5Y z0%Bda)B;kkWjJPf_=0fY_%@+cs=7HfPS5WfxyNYaHC%13|`(ML*T zM>N^V&VRAf>>JZ0i*@34ZXfVIJ$j^@=-u`BQ^Z_9YC!8 zm4}sb%HkVK0U6M3NOZ0Nq#h~RM%c#-NW>yx4Yjh^rvVVn0=XLy>On*11Rw`3y!HZO zjn7{J3VbfnYS)-W-alA_828$_KY?i-xwVQY&^TD&GY6(+hAA1t**UmGLP@ ze5<_+FE-sCYz<>lp$73#R13B@wuYh%AK(cz@YkNoua4v|kA|8e@#u1+WjNj%k2OX+ z;(?})Xam^s&nHZXxTF27@M4`E6XBJ&1)D<~@WRrGSOp`IKzv6e#M*-GKy5_XhAQUN zbXny-%!XZ2zIoHlf%47OH6EWYQ0CoGyD3oNsnNVuRl-K#NH2%AHIxAxEB#PTF^#NRayUWHi9SO*o5L$wk=xB6>H3BJprXyZT^>jg5TyxBOIIsmCL8<&+a9y!-CE z(P{V^YakkG4#zNX0%FJsU}Go^44Kj0{wS`PZpVd#j%Xm(xeep6vmwqJq8+hVAQJ6p zL@6MuW2_OwDjg=9ws%Bv^)-BV2p)*Fpll)w5I!32h9M{kv~{+|>5F9PAVCSNL-3G5 zusa+JU>s9kBFh>pFpf|Z!&q+%L^{A&HRs?~bK{Qo?IFC9Jl+wAfl#Pj;x|TvT>&1Z zB>C;(P*)m9=uDSZsDGp=23K*_g>f=652tPDjzDv3$F^W=pb^~?L$4YT>L>)>^ifknbdqX29%sZozn#%D+q)f$e z1BAR>LRI3wahkeGL2&=XsH`PU|YRqf`Q@(ORjU+t|es|-|meU+s`AMfTe=yVPZ0?<-K zhiEX27fGYbS|B{4UTrKCtw70(lET3!-Ao^dVuA68BSJ{mnj%3KAH)d+8yf?6g?8}% zQKSgoAT+dsn8Jjg#p0;5(u#)KFhkqO1!{cNdiXPcscach!7I@NTWFSqiyKo=U?aw~ z-z)oqS&JKXY^DiVDZVKbmv|dHqMpV^eBF*bO<2yTEbTx)rcv^Chrk{ zyK;&0_R`L16zPU- z7C3)pI+JBp3^a%0fsW2N?mE#l4~K!kfKlYeH+{)0UFKpi9{5;ixZy6b>bAM2Fd??d zovon&q8KjFvp}#d)-3R0<5-}nOTO;k)|yXP;{D+y9|m%uF}R~O+}sjZBRfC?>u$Wx z+yL;qfU&f4VOn`5vrYj4F$43EvV2W(@#I}y#;AU6`0g;-EdnW<%1q?6z-W7_rhgw&!3Iq``iH9u(6nLIXrDGeti^ShtYe^#o#(P^gjLbwS{d!LFQ;t^$@WmHhfS z!#50rjfOzMNSOKyt`ndyTAI?`$5-3l!dIw`UNw#5HVvj-4atTVnsU&2AHcwAwUKZ%SICb3-e}$A#Mx{jfL_s6fLayEW|^qT0Ez% z^`_2H zbcdo!?nU0T9RTk5xG!Rr)DqDYinzXNuo=%_My(jxdKA{3lc_iOCMT%`~({5V5zC%R@x8j+iBsnPP=Ll+yYbOEO&B(M>6P+1rj- zZVPskUEdJ4;*SUM$cIZB0e(ZGEF6mj@ff4D9_@U4EGimo*TvEkEevK7l zRqlSU`fhIL4npBv&Y_s4Xw`^aPcWvhF!&vSiTyu)O`#6~-I%mPF$X4!lz`FpPDI{D z-RaD;YPqLIdFrIvTBbm$t$c24vRFowEX`JO;iMZ}U1s8Ilwd?~-DVKbpu_ zuqzC8ORVaajxMn>0w73>kZ{MgKvQQsEgaK`%ax#XvIgDB;CAMYFoaVV8%P!7;+?dO zcyeL#?nsLh>8o)$L9-3G!qv|2IvNApYDJPnT>@5tz_eL4=KQokr7l}2m(T|z&1fdJ zLMQIxfi6Dj(Gy+bq7ns4LWeSR7eo}i@;0d~T856mpj*ZwCm&?g?K;s#2|lx1_ojk? zWuHd>QxLF5D{5FSc9&Q_+amCU)}d>6;cYui8z9|2aMdn-herJ`E;pLYTQ3cLDRIUH--hD&5Yiq*Wk8ceE8xLX#ulkF`fHTg}m@wULVbXhT^ z4|UqknHEkYi+CDLQlb^V4Azq@S!nvRUNFC&^=7 zm2+#6REAsGnF}O+x37;Fb?-zFR-X>U(t5nbs_NlL#L~xLZ6(7Mf=&P5#;#=tVHk$( zAK?ea1)+@-{J(JGCQZ_=;)p6(+C1zyosfT#yN2@_b&OdOoAk;_*`X6rd@qa}W0|t! zS;r=(gH63*S?2Oy2%#c}>5h|&QUKX(SdO^i^U6LUt?rAGipF6UCzr6;o*eciTSraR zTWWqfSp=zo#*BW+kSlw`*Y{q=w`3Js|zygRJTO*a09FeG3B# zVS~DG<8aK>u_$!F&09ia`0Y|I;M%wmu}zb*CU*a +#include +#include + +#include "utils/utils.h" + +#define WINDOW_WIDTH 800.0f +#define WINDOW_HEIGHT 600.0f + +#define BG_COLOR BLACK_BRUSH +#define COLOR RGB(255, 255, 255) + +Point3D cube_points[] = {{0.0f, 0.0f, 0.0f}, {1.0f, 0.0f, 0.0f}, + {1.0f, 1.0f, 0.0f}, {0.0f, 1.0f, 0.0f}, + {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f, 1.0f}, + {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f, 1.0f}}; +int cube_edges[][2] = {{0, 1}, {1, 2}, {2, 3}, {3, 0}, {4, 5}, {5, 6}, + {6, 7}, {7, 4}, {0, 4}, {1, 5}, {2, 6}, {3, 7}}; +Point3D cube_position = {.coordinates = {3.0f, 6.0f, 5.0f}}; +float cube_speed[] = {0.03f, 0.0f, 0.0f}; + +const Object cube = {.points = cube_points, + .edges = cube_edges, + .number_of_points = 8, + .number_of_edges = 12, + .position = &cube_position, + .rotate_speed = &cube_speed}; + +Point3D pyramid_points[] = {{-1.0f, 0.0f, -1.0f}, + {1.0f, 0.0f, -1.0f}, + {1.0f, 0.0f, 1.0f}, + {-1.0f, 0.0f, 1.0f}, + {0.0f, 1.5f, 0.0f}}; +int pyramid_edges[][2] = {{0, 1}, {1, 2}, {2, 3}, {3, 0}, + {0, 4}, {1, 4}, {2, 4}, {3, 4}}; +Point3D pyramid_position = {.coordinates = {6.0f, 3.0f, 5.0f}}; +float pyramid_speed[] = {0.0f, 0.0f, 0.03f}; + +const Object pyramid = {.points = pyramid_points, + .edges = pyramid_edges, + .number_of_points = 5, + .number_of_edges = 8, + .position = &pyramid_position, + .rotate_speed = &pyramid_speed}; + +const int number_of_objects = 2; +Object objects[] = {cube, pyramid}; + +Camera camera = {.position = {0.0f, 0.0f, 0.0f}, + .target = {1.0f, 1.0f, 1.0f}, + .up = {-1.0f, -1.0f, 1.0f}, + .fov = 120.0f, + .max_distance = 20.0f, + .min_distance = .0f, + .aspect_ratio = WINDOW_WIDTH / WINDOW_HEIGHT}; + +float render_matrix[4][4]; + +LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, + LPARAM lParam) { + switch (uMsg) { + case WM_CREATE: + SetTimer(hwnd, 1, 50, NULL); + return 0; + case WM_PAINT: { + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hwnd, &ps); + + RECT rect; + GetClientRect(hwnd, &rect); + FillRect(hdc, &rect, (HBRUSH)GetStockObject(BG_COLOR)); + float width = rect.right - rect.left; + float height = rect.bottom - rect.top; + + Screen screen = {.width = width, + .height = height, + .hdc = &hdc, + .render_matrix = &render_matrix}; + + HPEN hPen = CreatePen(PS_SOLID, 1, COLOR); + HPEN hOldPen = (HPEN)SelectObject(hdc, hPen); + + for (int i = 0; i < number_of_objects; i++) { + draw_object(&(objects[i]), &screen); + } + + SelectObject(hdc, hOldPen); + DeleteObject(hPen); + + EndPaint(hwnd, &ps); + return 0; + } + + case WM_DESTROY: + PostQuitMessage(0); + return 0; + + case WM_TIMER: + for (int i = 0; i < number_of_objects; i++) { + float reverse_translate_matrix[4][4]; + create_translate_matrix(objects[i].position, reverse_translate_matrix, + -1); + transform_object(&(objects[i]), 4, reverse_translate_matrix); + + Point3D center_of_object = calculate_centroid(&(objects[i])); + float reverse_center_translate_matrix[4][4]; + create_translate_matrix(¢er_of_object, + reverse_center_translate_matrix, -1); + transform_object(&(objects[i]), 4, reverse_center_translate_matrix); + + float rotate_matrix[3][3]; + create_rotate_matrix(*(objects[i].rotate_speed), rotate_matrix); + transform_object(&(objects[i]), 3, rotate_matrix); + + float center_translate_matrix[4][4]; + create_translate_matrix(¢er_of_object, center_translate_matrix, 1); + transform_object(&(objects[i]), 4, center_translate_matrix); + + float translate_matrix[4][4]; + create_translate_matrix(objects[i].position, translate_matrix, 1); + transform_object(&(objects[i]), 4, translate_matrix); + } + + InvalidateRect(hwnd, NULL, TRUE); + return 0; + + case WM_KEYDOWN: + return 0; + } + + return DefWindowProc(hwnd, uMsg, wParam, lParam); +} + +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, + LPSTR lpCmdLine, int nCmdShow) { + const char CLASS_NAME[] = "C3DGraphicEngine"; + + WNDCLASS wc = {0}; + wc.lpfnWndProc = WindowProc; + wc.hInstance = hInstance; + wc.lpszClassName = CLASS_NAME; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + RegisterClass(&wc); + + HWND hwnd = CreateWindowEx(0, CLASS_NAME, CLASS_NAME, WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH, + WINDOW_HEIGHT, NULL, NULL, hInstance, NULL); + + if (hwnd == NULL) { + return 0; + } + + float view_matrix[4][4]; + create_view_matrix(&camera, view_matrix); + float projection_matrix[4][4]; + create_projection_matrix(&camera, projection_matrix); + matrix_mult_matrix(4, projection_matrix, view_matrix, render_matrix); + + for (int i = 0; i < number_of_objects; i++) { + float translate_matrix[4][4]; + create_translate_matrix(objects[i].position, translate_matrix, 1); + transform_object(&(objects[i]), 4, translate_matrix); + } + + ShowWindow(hwnd, nCmdShow); + UpdateWindow(hwnd); + + MSG msg = {0}; + while (GetMessage(&msg, NULL, 0, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + return 0; +} diff --git a/screenshots/demo_render.png b/screenshots/demo_render.png new file mode 100644 index 0000000000000000000000000000000000000000..5d1cca762a25cc7355235275fb96a39c6f7c2073 GIT binary patch literal 7223 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 literal 0 HcmV?d00001 diff --git a/utils/utils.c b/utils/utils.c new file mode 100644 index 0000000..a104d5a --- /dev/null +++ b/utils/utils.c @@ -0,0 +1,246 @@ +#include "utils.h" + +#include +#include +#include + +// === Vectors === +void vector_substruct(int size, float v1[size], float v2[size], float r[size]) { + for (int i = 0; i < size; i++) + r[i] = v1[i] - v2[i]; +} +void cross_product_vectors(float v1[3], float v2[3], float r[3]) { + r[0] = v1[1] * v2[2] - v1[2] * v2[1]; + r[1] = v1[2] * v2[0] - v1[0] * v2[2]; + r[2] = v1[0] * v2[1] - v1[1] * v2[0]; +} +float dot_product_vectors(float v1[3], float v2[3]) { + return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; +} +void vector_normalize(int size, float coordinates[size]) { + float divider = 0; + for (int i = 0; i < size; i++) + divider += coordinates[i] * coordinates[i]; + divider = sqrt(divider); + float new_coordinates[size]; + for (int i = 0; i < size; i++) + coordinates[i] /= divider; +} +void print_vector(int size, float vector[size]) { + printf("("); + for (int i = 0; i < size; i++) { + printf("%f", vector[i]); + if (i != size - 1) + printf(",\t"); + } + printf(")\n"); +} + +// === Matrices === +void print_matrix(int size, float matrix[size][size]) { + for (int i = 0; i < size; i++) { + printf("("); + for (int j = 0; j < size; j++) { + printf("%f", matrix[i][j]); + if (j != size - 1) + printf(",\t"); + } + printf(")\n"); + } +} +void matrix_mult_matrix(int size, float matrix1[size][size], + float matrix2[size][size], float result[size][size]) { + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + result[i][j] = 0; + } + } + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + for (int k = 0; k < size; k++) { + result[i][j] += matrix1[i][k] * matrix2[k][j]; + } + } + } +} +void matrix_mult_vector(int size, float matrix[size][size], float vector[size], + float result[size]) { + for (int i = 0; i < size; i++) + result[i] = 0; + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + result[i] += matrix[i][j] * vector[j]; + } + } +} +void create_axis_rotate_matrix(int axis, float angle, + float rotate_matrix[3][3]) { + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + rotate_matrix[i][j] = 0; + } + } + + float cos_angle = cosf(angle); + float sin_angle = sinf(angle); + + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + if (i == axis || j == axis) { + if (i == j) + rotate_matrix[i][j] = 1; + else + rotate_matrix[i][j] = 0; + } else { + if (i == j) { + rotate_matrix[i][j] = cos_angle; + } else { + if ((i < j && axis == 2) || (i > j && axis != 2)) { + rotate_matrix[i][j] = sin_angle; + } else { + rotate_matrix[i][j] = -sin_angle; + } + } + } + } + } +} + +void create_rotate_matrix(float rotate_speed[3], float rotate_matrix[3][3]) { + float x_rotate[3][3]; + create_axis_rotate_matrix(0, rotate_speed[0], x_rotate); + float y_rotate[3][3]; + create_axis_rotate_matrix(1, rotate_speed[1], y_rotate); + float z_rotate[3][3]; + create_axis_rotate_matrix(2, rotate_speed[2], z_rotate); + float xy_rotate[3][3]; + matrix_mult_matrix(3, x_rotate, y_rotate, xy_rotate); + matrix_mult_matrix(3, xy_rotate, z_rotate, rotate_matrix); +} + +// === Points in scene === +void transform_point(Point3D *point, int size, + float translate_matrix[size][size]) { + float new_coordinates[3]; + float old_coordinates[] = {point->coordinates[0], point->coordinates[1], + point->coordinates[2], 1.0f}; + matrix_mult_vector(size, translate_matrix, old_coordinates, new_coordinates); + for (int i = 0; i < 3; i++) + point->coordinates[i] = new_coordinates[i]; +} +void create_translate_matrix(Point3D *position, float translate_matrix[4][4], + int k) { + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + translate_matrix[i][j] = 0; + if (i == j) + translate_matrix[i][j] = 1; + else if (j == 3) + translate_matrix[i][j] = position->coordinates[i] * k; + } + } +} + +// === Points on screen === +ScreenPoint convert_to_screen_point(Point3D *point, Screen *screen) { + float tmp[] = {point->coordinates[0], point->coordinates[1], + point->coordinates[2], 1.0f}; + float point_projection_view[4]; + matrix_mult_vector(4, *(screen->render_matrix), tmp, point_projection_view); + + float perspective_coordinates[3] = { + point_projection_view[0] / point_projection_view[3], + point_projection_view[1] / point_projection_view[3], + point_projection_view[2] / point_projection_view[3], + }; + ScreenPoint spoint = { + .coordinates = {((perspective_coordinates[0] + 1.0f) / 2.0f) * + (float)screen->width, + ((perspective_coordinates[1] + 1.0f) / 2.0f) * + (float)screen->height}}; + return spoint; +} + +void draw_line(Screen *screen, ScreenPoint *sp1, ScreenPoint *sp2) { + MoveToEx(*(screen->hdc), sp1->coordinates[0], sp1->coordinates[1], NULL); + LineTo(*(screen->hdc), sp2->coordinates[0], sp2->coordinates[1]); +} + +// === Camera === +void create_view_matrix(Camera *camera, float view_matrix[4][4]) { + float forward[3]; + vector_substruct(3, camera->position.coordinates, camera->target.coordinates, + forward); + vector_normalize(3, forward); + float right[3]; + cross_product_vectors(camera->up, forward, right); + vector_normalize(3, right); + + float up[3]; + cross_product_vectors(forward, right, up); + vector_normalize(3, up); + + float *vectors[] = {right, up, forward}; + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + view_matrix[i][j] = 0; + if (i == 3) { + if (j == 3) + view_matrix[i][j] = 1; + } else if (j == 3) { + view_matrix[i][j] = + -1 * dot_product_vectors(vectors[i], camera->position.coordinates); + } else { + view_matrix[i][j] = vectors[i][j]; + } + } + } +} +void create_projection_matrix(Camera *camera, float projection_matrix[4][4]) { + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + projection_matrix[i][j] = 0; + } + } + float f = 1 / tan(camera->fov / 2); + projection_matrix[0][0] = f / camera->aspect_ratio; + projection_matrix[1][1] = f; + projection_matrix[2][2] = (camera->max_distance + camera->min_distance) / + (camera->min_distance - camera->max_distance); + projection_matrix[3][2] = -1; + projection_matrix[2][3] = (2 * camera->max_distance * camera->min_distance) / + (camera->min_distance - camera->max_distance); +} + +// === Composite objects === +void transform_object(const Object *object, int size, + float translate_matrix[size][size]) { + for (int i = 0; i < object->number_of_points; i++) { + transform_point(&(object->points[i]), size, translate_matrix); + } +} +void draw_object(const Object *object, Screen *screen) { + for (int i = 0; i < object->number_of_edges; i++) { + Point3D point1 = object->points[object->edges[i][0]]; + ScreenPoint screen_point1 = convert_to_screen_point(&point1, screen); + Point3D point2 = object->points[object->edges[i][1]]; + ScreenPoint screen_point2 = convert_to_screen_point(&point2, screen); + draw_line(screen, &screen_point1, &screen_point2); + } +} +Point3D calculate_centroid(const Object *object) { + Point3D centroid = {{0.0f, 0.0f, 0.0f}}; + + for (int i = 0; i < object->number_of_points; i++) { + centroid.coordinates[0] += object->points[i].coordinates[0]; + centroid.coordinates[1] += object->points[i].coordinates[1]; + centroid.coordinates[2] += object->points[i].coordinates[2]; + } + + float inv_n = 1.0f / (float)object->number_of_points; + centroid.coordinates[0] *= inv_n; + centroid.coordinates[1] *= inv_n; + centroid.coordinates[2] *= inv_n; + + return centroid; +} \ No newline at end of file diff --git a/utils/utils.h b/utils/utils.h new file mode 100644 index 0000000..95c2f4f --- /dev/null +++ b/utils/utils.h @@ -0,0 +1,80 @@ +#ifndef OBJECTS_H +#define OBJECTS_H + +#include + +// === Vectors === +void vector_substruct(int size, float v1[size], float v2[size], float r[size]); +void cross_product_vectors(float v1[3], float v2[3], float r[3]); +float dot_product_vectors(float v1[3], float v2[3]); +void vector_normalize(int size, float coordinates[size]); +void print_vector(int size, float vector[size]); + +// === Matrix === +void print_matrix(int size, float matrix[size][size]); +void matrix_mult_matrix(int size, float matrix1[size][size], + float matrix2[size][size], float result[size][size]); +void matrix_mult_vector(int size, float matrix[size][size], float vector[size], + float result[size]); + +void create_axis_rotate_matrix(int axis, float angle, + float rotate_matrix[3][3]); +void create_rotate_matrix(float rotate_speed[3], float rotate_matrix[3][3]); + +// === Point in scene === +struct Point3D { + float coordinates[3]; +}; +typedef struct Point3D Point3D; +void transform_point(Point3D *point, int size, + float translate_matrix[size][size]); +void create_translate_matrix(Point3D *position, float translate_matrix[4][4], + int k); + +// === Screen summary === +struct Screen { + int width; + int height; + HDC *hdc; + float (*render_matrix)[4][4]; +}; +typedef struct Screen Screen; + +// === Point on screen === +struct ScreenPoint { + int coordinates[2]; +}; +typedef struct ScreenPoint ScreenPoint; +ScreenPoint convert_to_screen_point(Point3D *point, Screen *screen); +void draw_line(Screen *screen, ScreenPoint *sp1, ScreenPoint *sp2); + +// === Camera in scene === +struct Camera { + Point3D position; + Point3D target; + float up[3]; + float fov; + float max_distance; + float min_distance; + float aspect_ratio; +}; +typedef struct Camera Camera; +void create_view_matrix(Camera *camera, float view_matrix[4][4]); +void create_projection_matrix(Camera *camera, float projection_matrix[4][4]); + +// === Composite object in scene === +struct Object { + Point3D *points; + int (*edges)[2]; + int number_of_points; + int number_of_edges; + Point3D *position; + float (*rotate_speed)[3]; +}; +typedef struct Object Object; +void transform_object(const Object *object, int size, + float translate_matrix[size][size]); +void draw_object(const Object *object, Screen *screen); +Point3D calculate_centroid(const Object *object); + +#endif \ No newline at end of file