1+ /*
2+ Example file for olcUTIL_Hardware3D.h
3+
4+ License (OLC-3)
5+ ~~~~~~~~~~~~~~~
6+
7+ Copyright 2018 - 2025 OneLoneCoder.com
8+
9+ Redistribution and use in source and binary forms, with or without
10+ modification, are permitted provided that the following conditions
11+ are met:
12+
13+ 1. Redistributions or derivations of source code must retain the above
14+ copyright notice, this list of conditions and the following disclaimer.
15+
16+ 2. Redistributions or derivative works in binary form must reproduce
17+ the above copyright notice. This list of conditions and the following
18+ disclaimer must be reproduced in the documentation and/or other
19+ materials provided with the distribution.
20+
21+ 3. Neither the name of the copyright holder nor the names of its
22+ contributors may be used to endorse or promote products derived
23+ from this software without specific prior written permission.
24+
25+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29+ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36+
37+ Links
38+ ~~~~~
39+ YouTube: https://www.youtube.com/javidx9
40+ Discord: https://discord.gg/WhwHUMV
41+ Twitter: https://www.twitter.com/javidx9
42+ Twitch: https://www.twitch.tv/javidx9
43+ GitHub: https://www.github.com/onelonecoder
44+ Homepage: https://www.onelonecoder.com
45+
46+ Author
47+ ~~~~~~
48+ David Barr, aka javidx9, ©OneLoneCoder 2019, 2020, 2021, 2022, 2023, 2024, 2025
49+
50+ */
51+
52+
53+ #define OLC_PGE_APPLICATION
54+ // #define OLC_GFX_OPENGL33
55+ #include < utilities/olcUTIL_Hardware3D.h>
56+ #include < olcPixelGameEngine.h>
57+
58+
59+
60+ class Quad3D : public olc ::PixelGameEngine
61+ {
62+ public:
63+ Quad3D ()
64+ {
65+ sAppName = " Hardware3D Assetless Demo" ;
66+ }
67+
68+ olc::mf4d matWorld;
69+ olc::mf4d matView;
70+ olc::mf4d matProject;
71+
72+ olc::Renderable texCube;
73+ olc::utils::hw3d::mesh meshCube;
74+ olc::utils::hw3d::mesh meshLightCube;
75+
76+ std::array<olc::vf3d, 64 > cubes;
77+ std::array<olc::vf3d, 3 > lights;
78+
79+ public:
80+ bool OnUserCreate () override
81+ {
82+ float fAspect = float (GetScreenSize ().y ) / float (GetScreenSize ().x );
83+ float S = 1 .0f / (tan (3 .14159f * 0 .25f ));
84+ float f = 1000 .0f ;
85+ float n = 0 .1f ;
86+
87+ matProject (0 , 0 ) = fAspect ; matProject (0 , 1 ) = 0 .0f ; matProject (0 , 2 ) = 0 .0f ; matProject (0 , 3 ) = 0 .0f ;
88+ matProject (1 , 0 ) = 0 .0f ; matProject (1 , 1 ) = 1 ; matProject (1 , 2 ) = 0 .0f ; matProject (1 , 3 ) = 0 .0f ;
89+ matProject (2 , 0 ) = 0 .0f ; matProject (2 , 1 ) = 0 .0f ; matProject (2 , 2 ) = -(f / (f - n)); matProject (2 , 3 ) = -1 .0f ;
90+ matProject (3 , 0 ) = 0 .0f ; matProject (3 , 1 ) = 0 .0f ; matProject (3 , 2 ) = -((f * n) / (f - n)); matProject (3 , 3 ) = 0 .0f ;
91+
92+ matWorld.identity ();
93+ matView.identity ();
94+
95+ // Create a unit cube, centered on origin
96+ meshCube = olc::utils::hw3d::CreateCube ({ 1 ,1 ,1 }, {-0.5 , -0.5 , -0.5 });
97+
98+ // Creat another cube, smaller
99+ meshLightCube = olc::utils::hw3d::CreateCube ({ 0.5 ,0.5 ,0.5 }, { -0.25 , -0.25 , -0.25 });
100+
101+ // Why 2 cubes? the regular ones will have their vertex information recoloured
102+
103+ // Create texture (so we dont need to load anything)
104+ texCube.Create (128 , 128 );
105+ SetDrawTarget (texCube.Sprite ());
106+ Clear (olc::WHITE);
107+ FillCircle (64 , 64 , 32 , olc::BLACK);
108+ FillCircle (64 , 64 , 24 , olc::BLUE);
109+ FillCircle (64 , 64 , 16 , olc::RED);
110+ FillCircle (64 , 64 , 8 , olc::YELLOW);
111+ SetDrawTarget (nullptr );
112+ texCube.Decal ()->Update ();
113+
114+ // Position cubes nicely
115+ for (int x=0 ; x<8 ; x++)
116+ for (int y = 0 ; y < 8 ; y++)
117+ {
118+ float z = sin (float (x)) + cos (float (y));
119+ cubes[y * 8 + x] = { float (x) - 4 .0f , float (z), float (y) - 4 .0f };
120+ }
121+
122+
123+ Clear (olc::VERY_DARK_BLUE);
124+ HW3D_Projection (matProject.m );
125+ HW3D_SetCullMode (olc::CullMode::CCW);
126+ return true ;
127+ }
128+
129+ float fThetaX = 1 ;
130+ float fThetaY = 2 ;
131+
132+ float fLightTime = 0 .0f ;
133+
134+
135+ bool OnUserUpdate (float fElapsedTime ) override
136+ {
137+ // spin stuff
138+ fThetaX += fElapsedTime * 0 .1f ;
139+ fThetaY += fElapsedTime * 0 .05f ;
140+
141+ olc::mf4d m1, m2, m3, m4;
142+
143+ // fake a pseudo-view matrix by transforming in an identity view
144+ m1.rotateY (fThetaY );
145+ m2.rotateX (fThetaX );
146+ m3.translate (0.0 , 0.0 , -10.0 );
147+ matView = m3 * m2 * m1;
148+
149+ // Clear background
150+ ClearBuffer (olc::CYAN, true );
151+
152+ // Update light positions
153+ fLightTime += fElapsedTime ;
154+ lights[0 ] = { 6 .0f * sin (fLightTime * 2 .5f ), 6 .0f * cos (fLightTime * 2 .5f ), 0 .0f };
155+ lights[1 ] = { 0 .0f , 6 .0f * sin (fLightTime ), 6 .0f * cos (fLightTime ) };
156+ lights[2 ] = { 6 .0f * cos (fLightTime * 1 .7f ), 0 .0f , 6 .0f * sin (fLightTime * 1 .7f ) };
157+
158+ // World Space lighting! The 3 lights are used as directional light sources
159+ // so i dont need to pre-compute all the geometry on the CPU. This is a
160+ // limitation of the hw3d approach but like I said, its for basic 3D usage.
161+ for (size_t i = 0 ; i < meshCube.pos .size (); i += 3 )
162+ {
163+ const auto & p0 = meshCube.pos [i + 0 ];
164+ const auto & p1 = meshCube.pos [i + 1 ];
165+ const auto & p2 = meshCube.pos [i + 2 ];
166+
167+ // Hand calculate surface normal (i know i know the norms are already there...)
168+ olc::vf3d vCross = olc::vf3d (p1[0 ] - p0[0 ], p1[1 ] - p0[1 ], p1[2 ] - p0[2 ]).cross (olc::vf3d (p2[0 ] - p0[0 ], p2[1 ] - p0[1 ], p2[2 ] - p0[2 ])).norm ();
169+
170+ // Additive colouring
171+ meshCube.col [i + 0 ] = olc::BLACK;
172+ meshCube.col [i + 1 ] = olc::BLACK;
173+ meshCube.col [i + 2 ] = olc::BLACK;
174+
175+ olc::Pixel c[] = { olc::RED, olc::GREEN, olc::BLUE };
176+ for (int j = 0 ; j < 3 ; j++)
177+ {
178+ olc::vf3d vLight = -lights[j].norm ();
179+ float illum = std::max (-vCross.dot (vLight), 0 .0f ) * 0 .8f + 0 .2f ;
180+ meshCube.col [i + 0 ] += olc::PixelF (illum, illum, illum, 1 .0f ) * c[j];
181+ meshCube.col [i + 1 ] += olc::PixelF (illum, illum, illum, 1 .0f ) * c[j];
182+ meshCube.col [i + 2 ] += olc::PixelF (illum, illum, illum, 1 .0f ) * c[j];
183+ }
184+ }
185+
186+
187+ // Draw all cubes
188+ for (const auto & cube : cubes)
189+ {
190+ matWorld.translate (cube);
191+ HW3D_DrawObject ((matView * matWorld).m , texCube.Decal (), meshCube.layout , meshCube.pos , meshCube.uv , meshCube.col );
192+ }
193+
194+ // Draw light cubes
195+ matWorld.translate (lights[0 ]);
196+ HW3D_DrawObject ((matView * matWorld).m , nullptr , meshLightCube.layout , meshLightCube.pos , meshLightCube.uv , meshLightCube.col , olc::RED);
197+ matWorld.translate (lights[1 ]);
198+ HW3D_DrawObject ((matView * matWorld).m , nullptr , meshLightCube.layout , meshLightCube.pos , meshLightCube.uv , meshLightCube.col , olc::GREEN);
199+ matWorld.translate (lights[2 ]);
200+ HW3D_DrawObject ((matView * matWorld).m , nullptr , meshLightCube.layout , meshLightCube.pos , meshLightCube.uv , meshLightCube.col , olc::BLUE);
201+ return true ;
202+ }
203+ };
204+
205+ int main ()
206+ {
207+ Quad3D demo;
208+ if (demo.Construct (1280 , 720 , 1 , 1 , false , false ))
209+ demo.Start ();
210+ return 0 ;
211+ }
0 commit comments