├── .gitignore
├── CMakeLists.txt
├── LICENSE
├── README.md
├── ascii-graphics.cpp
└── src
├── CMakeLists.txt
├── Camera.cpp
├── Camera.hpp
├── Lights.cpp
├── Lights.hpp
├── Mesh.cpp
├── Mesh.hpp
├── Screen.cpp
├── Screen.hpp
├── agm.cpp
└── agm.hpp
/.gitignore:
--------------------------------------------------------------------------------
1 | # Prerequisites
2 | *.d
3 |
4 | # Compiled Object files
5 | *.slo
6 | *.lo
7 | *.o
8 | *.obj
9 |
10 | # Precompiled Headers
11 | *.gch
12 | *.pch
13 |
14 | # Compiled Dynamic libraries
15 | *.so
16 | *.dylib
17 | *.dll
18 |
19 | # Fortran module files
20 | *.mod
21 | *.smod
22 |
23 | # Compiled Static libraries
24 | *.lai
25 | *.la
26 | *.a
27 | *.lib
28 |
29 | # Executables
30 | *.exe
31 | *.out
32 | *.app
33 |
34 | build/
35 | models/
36 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.23.1)
2 |
3 | project(Ascii-Graphics)
4 |
5 | add_executable(${PROJECT_NAME} ascii-graphics.cpp)
6 |
7 | add_subdirectory(src)
8 |
9 | target_link_libraries(${PROJECT_NAME} agm)
10 | target_link_libraries(${PROJECT_NAME} Camera)
11 | target_link_libraries(${PROJECT_NAME} Lights)
12 | target_link_libraries(${PROJECT_NAME} Mesh)
13 | target_link_libraries(${PROJECT_NAME} Screen)
14 |
15 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 addr0x414b
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |

7 |
8 |
ASCII Graphics
9 |
10 |
11 | C++ 3D graphics library that runs in a Linux terminal
12 |
13 |
14 |
15 |
16 | ## About The Project
17 |
18 |
19 |
20 | Ascii-graphics is a basic 3D graphics library designed to run in a linux terminal.
21 |
22 |
23 | ## Features
24 |
25 | Below shows some key features of the ascii-graphics library.
26 |
27 | ### Custom OBJ Model Loading
28 |
29 |
30 |
31 | ### Smooth Lighting
32 |
33 |
34 |
35 | ### Z Buffering
36 |
37 |
38 |
39 | ### Other Features
40 | * Outline meshes
41 | * Wireframe mode
42 | * Fill entire mesh with single character
43 | * Basic lighting
44 |
45 | ## Installation
46 |
47 | 1. Clone the repo
48 | ```
49 | git clone https://github.com/addr0x414b/ascii-graphics.git
50 | ```
51 | 2. In the cloned folder, create a build directory and cd to it
52 | ```
53 | mkdir build && cd build
54 | ```
55 | 3. Run cmake
56 | ```
57 | cmake ../
58 | ```
59 | 4. Run make
60 | ```
61 | make
62 | ```
63 | 5. Run the program
64 | ```
65 | ./Ascii-Graphics
66 | ```
67 |
68 |
69 | ## Usage
70 |
71 | Below is the default code the repo comes with:
72 |
73 | ```c++
74 | //ascii-graphics.cpp
75 |
76 | int gScreenWidth = 200; // Define the screens width and height
77 | int gScreenHeight = 50;
78 | float gAspect = (float)gScreenWidth / (float)gScreenHeight;
79 |
80 | int main() {
81 |
82 | Camera camera(0.0f, 0.0f, 0.0f, gAspect); // Define the scenes camera
83 | LightD light; // Define the scenes light
84 | Screen screen(gScreenWidth, gScreenHeight, camera, light); // Define the screen of the scene
85 |
86 | Cube cube; // Library comes with one default mesh
87 | cube.translate(0.0f, 0.0f, -4.f); // Must translate the mesh away from the camera (-z is into the screen)
88 |
89 | float deg = 0.1f;
90 | while (1) { // Screen loop
91 | screen.start(); // Begin calculating the delta time per frame
92 |
93 | cube.rotate(0.0f, deg, deg); // Rotate cube
94 | deg += 50 * screen.deltaTime; // Increase the degrees of rotation
95 |
96 | screen.shadeMesh(cube); // Print the cube into the buffer
97 |
98 | screen.print(); // Print the buffer
99 | screen.clear(); // Clear the screen
100 | }
101 |
102 | return 0;
103 | }
104 | ```
105 |
106 | ### Create Scene
107 |
108 | 1. Define size of the screen
109 | ```c++
110 | int gScreenWidth = 200; // Width and height should be no larger than terminal
111 | int gScreenHeight = 50; // Can increase size with decreased terminal font size
112 | float gAspect = (float)gScreenWidth / (float)gScreenHeight;
113 | ```
114 | 2. Define camera, light, and screen
115 | ```c++
116 | Camera camera(0.0f, 0.0f, 0.0f, gAspect); // Constructor uses default projection values
117 | LightD light; // Define the scenes light
118 | Screen screen(gScreenWidth, gScreenHeight, camera, light); // Define the screen
119 | ```
120 |
121 | ### To Load a Custom Mesh
122 |
123 | 1. Mesh MUST be in OBJ format
124 | 2. Mesh MUST be triangulated, with normals
125 | 3. OBJ file should be in the same directory as the ascii-graphics.cpp file
126 | 4. File path MUST have "../" in front (assuming file is in the same directory as ascii-graphics.cpp)
127 |
128 | * Flat Shaded Mesh:
129 | ```c++
130 | Mesh meshName("../path_to_flat_mesh.obj"); // Load a FLAT SHADED mesh
131 | meshName.translate(0.0f, 0.0f, -4.0f); // Translate the mesh away from the camera
132 | ```
133 |
134 | * Smooth Shaded Mesh:
135 | ```c++
136 | Mesh meshName(1, "../path_to_smooth_mesh.obj") // Load a SMOOTH SHADED mesh
137 | meshName.translate(0.0f, 0.0f, -4.0f); // Translate the mesh away from the camera
138 | ```
139 |
140 | ### Display the Graphics
141 |
142 | ```c++
143 | float deg = 0.1f;
144 | while (1) { // Screen loop
145 | screen.start(); // Begin calculating the delta time per frame
146 |
147 | cube.rotate(0.0f, deg, deg); // Rotate cube
148 | deg += 50 * screen.deltaTime; // Increase the degrees of rotation
149 |
150 | screen.shadeMesh(cube); // Print the cube into the buffer
151 |
152 | screen.print(); // Print the buffer
153 | screen.clear(); // Clear the screen
154 | }
155 |
156 | ```
157 |
158 | ### Different Display Methods
159 |
160 | * Display a smooth shaded mesh
161 | ```c++
162 | screen.shadeMeshSmooth(meshName);
163 | ```
164 |
165 | * Display a flat shaded mesh
166 | ```c++
167 | screen.shadeMesh(meshName);
168 | ```
169 |
170 | * Fill entire mesh with a single character, no shading
171 | ```c++
172 | screen.fillMesh(meshName, '*'); // Pick character here
173 | ```
174 |
175 | * Show mesh triangles
176 | ```c++
177 | screen.drawMesh(meshName, '*'); // Pick character here
178 | ```
179 |
180 | * Display wireframe of mesh
181 | ```c++
182 | screen.drawMeshWire(meshName, '*'); // Pick character here
183 | ```
184 |
185 | * Can combine different methods
186 | ```c++
187 | // Results in an outlined cube
188 | screen.drawMesh(cube, '#'); // Draw the triangles of the cube
189 | screen.fillMesh(cube, '*'); // Fill in the cube
190 | ```
191 |
192 |
193 | ## License
194 |
195 | Distributed under the MIT License. See `LICENSE` for more information.
196 |
197 |
198 | ## Contact
199 |
200 | YouTube: https://www.youtube.com/watch?v=X4QSm_p7Cy4
201 |
202 |
203 | ## Acknowledgments
204 |
205 | Thanks to the following for allowing use of their 3D models:
206 |
207 | * [Man model by printable_models](https://free3d.com/3d-model/male-base-mesh-6682.html)
208 | * [Rocket model by Paul Chen uploaded by nixor](https://free3d.com/3d-model/rocket-ship-v1--579030.html)
209 | * [Cat model by snippysnappets](https://free3d.com/3d-model/low-poly-cat-46138.html)
210 |
211 |
212 |
--------------------------------------------------------------------------------
/ascii-graphics.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include "src/Screen.hpp"
4 | #include "src/agm.hpp"
5 | #include "src/Camera.hpp"
6 | #include "src/Lights.hpp"
7 | #include "src/Mesh.hpp"
8 |
9 | int gScreenWidth = 200; // Define the screens width and height
10 | int gScreenHeight = 50;
11 | float gAspect = (float)gScreenWidth / (float)gScreenHeight;
12 |
13 | int main() {
14 |
15 | Camera camera(0.0f, 0.0f, 0.0f, gAspect); // Define the scenes camera
16 | LightD light; // Define the scenes light
17 | Screen screen(gScreenWidth, gScreenHeight, camera, light); // Define the
18 | // screen of the
19 | // scene
20 |
21 | Cube cube; // Library comes with one default mesh
22 | cube.translate(0.0f, 0.0f, -4.f); // Must translate the mesh away from the
23 | // camera (-z is into the screen)
24 |
25 | float deg = 0.1f;
26 | while (1) { // Screen loop
27 | screen.start(); // Begin calculating the delta time per frame
28 |
29 | cube.rotate(0.0f, deg, deg); // Rotate our cube
30 | deg += 50 * screen.deltaTime; // Increase the degrees of rotation
31 |
32 | screen.shadeMesh(cube);
33 |
34 | screen.print(); // Print the entire screen
35 | screen.clear(); // Clear the screen
36 | }
37 |
38 | return 0;
39 | }
40 |
--------------------------------------------------------------------------------
/src/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | add_library(agm SHARED agm.cpp)
2 | add_library(Camera SHARED Camera.cpp)
3 | add_library(Lights SHARED Lights.cpp)
4 | add_library(Mesh SHARED Mesh.cpp)
5 | add_library(Screen SHARED Screen.cpp)
6 |
--------------------------------------------------------------------------------
/src/Camera.cpp:
--------------------------------------------------------------------------------
1 | #include "Camera.hpp"
2 |
3 | /* Default constructor
4 | * @params x,y,z: position of our camera
5 | * @param aspect: the screen aspect ratio
6 | * @param fov: the field of view
7 | * @param zNear: the z near clipping
8 | * @param zFar: the z far clipping */
9 | Camera::Camera(float x, float y, float z, float aspect,
10 | float fov, float zNear, float zFar) {
11 | pos.x = x; // Set camera position
12 | pos.y = y;
13 | pos.z = z;
14 |
15 | /* Create the projection matrix based on the inputted values */
16 | projMat = perspective(aspect, fov, zNear, zFar);
17 |
18 | /* Assign the camera values */
19 | a = aspect;
20 | f = fov;
21 | zN = zNear;
22 | zF = zFar;
23 |
24 | }
25 |
26 | /* Default constructor - uses default values for projection
27 | * @params x,y,z: position of our camera
28 | * @param aspect: the screen aspect ratio
29 | * Default value: fov = 30.0f
30 | * Default value: zNear = 0.1f
31 | * Default value: zFar = 1000.f */
32 | Camera::Camera(float x, float y, float z, float aspect) {
33 | pos.x = x;
34 | pos.y = y;
35 | pos.z = z;
36 |
37 | projMat = perspective(aspect, 30.f, 0.1f, 1000.f);
38 |
39 | a = aspect;
40 | f = 30.f;
41 | zN = 0.1f;
42 | zF = 1000.f;
43 | }
44 |
45 | /* Default constructor - create camera w/o any projection
46 | * Default values: x,y,z = 0 */
47 | Camera::Camera() {
48 | pos.x = 0.0f;
49 | pos.y = 0.0f;
50 | pos.z = 0.0f;
51 | }
52 |
--------------------------------------------------------------------------------
/src/Camera.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "agm.hpp"
4 |
5 | // Camera class
6 | class Camera {
7 | public:
8 | Vert pos; // Camera position
9 |
10 | Mat4 projMat;
11 |
12 | /* Store the cameras aspect ratio, field of view, the zNear clipping plane,
13 | * and the zFar clipping plane */
14 | float a;
15 | float f;
16 | float zN;
17 | float zF;
18 |
19 | /* Default constructor
20 | * @params x,y,z: position of our camera
21 | * @param aspect: the screen aspect ratio
22 | * @param fov: the field of view
23 | * @param zNear: the z near clipping
24 | * @param zFar: the z far clipping */
25 | Camera(float x, float y, float z, float aspect,
26 | float fov, float zNear, float zFar);
27 |
28 | /* Default constructor - uses default values for projection
29 | * @params x,y,z: position of our camera
30 | * @param aspect: the screen aspect ratio
31 | * Default value: fov = 30.0f
32 | * Default value: zNear = 0.1f
33 | * Default value: zFar = 1000.f */
34 | Camera(float x, float y, float z, float aspect);
35 |
36 | /* Default constructor - create camera w/o any projection
37 | * Default values: x,y,z = 0 */
38 | Camera();
39 |
40 | };
41 |
--------------------------------------------------------------------------------
/src/Lights.cpp:
--------------------------------------------------------------------------------
1 | #include "Lights.hpp"
2 |
3 |
4 | /* Default Constructor - Create directional light
5 | * @param x,y,z: light location
6 | * @param xd,yd,zd: light directional vector */
7 | LightD::LightD(float x, float y, float z, float xd, float yd, float zd) {
8 | Vert p(x, y, z);
9 | position = p;
10 | Vert d(xd, yd, zd);
11 | direction = d;
12 | }
13 |
14 | /* Default Constructor - Create directional light
15 | * Default value: pos = 0,0,0
16 | * Default value: dir = 0,0,-1 */
17 | LightD::LightD() {
18 | Vert p(0.0f, 0.0f, 0.0f);
19 | position = p;
20 | Vert d(0.0f, 0.0f, -1.0f);
21 | direction = d;
22 | }
23 |
--------------------------------------------------------------------------------
/src/Lights.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "agm.hpp"
4 | #include "Mesh.hpp"
5 |
6 | // Simple directional light
7 | class LightD {
8 | public:
9 | Vert position; // Light position
10 | Vert direction; // Light direction vector
11 |
12 | /* Default Constructor - Create directional light
13 | * @param x,y,z: light location
14 | * @param xd,yd,zd: light directional vector */
15 | LightD(float x, float y, float z, float xd, float yd, float zd);
16 |
17 | /* Default Constructor - Create directional light
18 | * Default value: pos = 0,0,0
19 | * Default value: dir = 0,0,-1 */
20 | LightD();
21 | };
22 |
23 |
--------------------------------------------------------------------------------
/src/Mesh.cpp:
--------------------------------------------------------------------------------
1 | #include "Mesh.hpp"
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | /* Default constructor - create a vertex with NO normal
9 | * @param xx: x position
10 | * @param yy: y position
11 | * @param zz: z position */
12 | Vert::Vert(float xx, float yy, float zz) {
13 | x = xx;
14 | y = yy;
15 | z = zz;
16 | xn = 0.f;
17 | yn = 0.f;
18 | zn = 0.f;
19 | }
20 |
21 | /* Default constructor - create a vertex WITH a normal
22 | * @param xx: x position
23 | * @param yy: y position
24 | * @param zz: z position
25 | * @param nx: x normal location
26 | * @param ny: y normal location
27 | * @param nz: z normal location */
28 | Vert::Vert(float xx, float yy, float zz, float nx, float ny, float nz) {
29 | x = xx;
30 | y = yy;
31 | z = zz;
32 | xn = nx;
33 | yn = ny;
34 | zn = nz;
35 | }
36 |
37 | /* Default constructor - create a vertex
38 | * Default values: x,y,z, xn,yn,zn = 0 */
39 | Vert::Vert() {
40 | x = 0.0f;
41 | y = 0.0f;
42 | z = 0.0f;
43 | xn = 0.f;
44 | yn = 0.f;
45 | zn = 0.f;
46 | }
47 |
48 | /* Default constructor - create a triangle
49 | * @params p1,p2,p3: vertices of the triangle
50 | * @params x,y,z: the face normal of the triangle */
51 | Trig::Trig(Vert p1, Vert p2, Vert p3, float x, float y, float z) {
52 | verts[0] = p1;
53 | verts[1] = p2;
54 | verts[2] = p3;
55 |
56 | Vert p1n(p1.xn, p1.yn, p1.zn);
57 | Vert p2n(p2.xn, p2.yn, p2.zn);
58 | Vert p3n(p3.xn, p3.yn, p3.zn);
59 |
60 | norms[0] = p1n;
61 | norms[1] = p2n;
62 | norms[2] = p3n;
63 |
64 | Vert n(x, y, z);
65 | fNormal = n;
66 | }
67 |
68 | /* Default constructor - load an obj file mesh thats smoothed
69 | * @param s: any random number, doesn't matter - doesn't do anything
70 | * @param objFile: path to file */
71 | Mesh::Mesh(int s, std::string objFile) {
72 |
73 | std::fstream file;
74 | std::string index;
75 | std::string x;
76 | std::string y;
77 | std::string z;
78 |
79 | std::string nx, ny, nz;
80 |
81 | std::vector vertices;
82 | std::vector normals;
83 |
84 | file.open(objFile);
85 | while(file >> index) {
86 | if (index == "v") {
87 | file >> x;
88 | file >> y;
89 | file >> z;
90 | vertices.push_back(x + " " + y + " " + z);
91 | } else if (index == "vn") {
92 | file >> x;
93 | file >> y;
94 | file >> z;
95 | normals.push_back(x + " " + y + " " + z);
96 | } else if (index == "f") {
97 | file >> x;
98 | file >> y;
99 | file >> z;
100 | std::replace(x.begin(), x.end(), '/', ' ');
101 | std::replace(y.begin(), y.end(), '/', ' ');
102 | std::replace(z.begin(), z.end(), '/', ' ');
103 |
104 | std::istringstream a(x);
105 | std::istringstream b(y);
106 | std::istringstream c(z);
107 | std::string p1, n1, n2, n3;
108 | std::string p2;
109 | std::string p3;
110 | a >> p1 >> n1;
111 | b >> p2 >> n2;
112 | c >> p3 >> n3;
113 |
114 | std::istringstream v(vertices[std::stoi(p1)-1]);
115 | v >> x;
116 | v >> y;
117 | v >> z;
118 | std::istringstream norm1(normals[std::stoi(n1)-1]);
119 | norm1 >> nx;
120 | norm1 >> ny;
121 | norm1 >> nz;
122 | Vert v1(std::stof(x), std::stof(y), std::stof(z),
123 | std::stof(nx), std::stof(ny), std::stof(nz));
124 |
125 | std::istringstream vv(vertices[std::stoi(p2)-1]);
126 | vv >> x;
127 | vv >> y;
128 | vv >> z;
129 | std::istringstream norm2(normals[std::stoi(n2)-1]);
130 | norm2 >> nx;
131 | norm2 >> ny;
132 | norm2 >> nz;
133 | Vert v2(std::stof(x), std::stof(y), std::stof(z),
134 | std::stof(nx), std::stof(ny), std::stof(nz));
135 |
136 | std::istringstream vvv(vertices[std::stoi(p3)-1]);
137 | vvv >> x;
138 | vvv >> y;
139 | vvv >> z;
140 | std::istringstream norm3(normals[std::stoi(n3)-1]);
141 | norm3 >> nx;
142 | norm3 >> ny;
143 | norm3 >> nz;
144 | Vert v3(std::stof(x), std::stof(y), std::stof(z),
145 | std::stof(nx), std::stof(ny), std::stof(nz));
146 |
147 | Vert dir1(v2.x-v1.x, v2.y-v1.y, v2.z-v1.z);
148 | Vert dir2(v3.x-v1.x, v3.y-v1.y, v3.z-v1.z);
149 | Vert cross((dir1.y*dir2.z)-(dir1.z*dir2.y),
150 | (dir1.z*dir2.x)-(dir1.x*dir2.z), (dir1.x*dir2.y)-(dir1.y*dir2.x));
151 |
152 | float l = sqrtf(cross.x*cross.x + cross.y*cross.y + cross.z*cross.z);
153 | cross.x /= l;
154 | cross.y /= l;
155 | cross.z /= l;
156 |
157 | Trig t(v1, v2, v3, cross.x, cross.y, cross.z);
158 |
159 | trigs.push_back(t);
160 | }
161 | }
162 | }
163 |
164 | /* Default constructor - load an obj file mesh (MUST BE FLAT SHADED)
165 | * @param objFile: path to file */
166 | Mesh::Mesh(std::string objFile) {
167 |
168 | std::fstream file;
169 | std::string index;
170 | std::string x;
171 | std::string y;
172 | std::string z;
173 |
174 | std::vector vertices;
175 | std::vector normals;
176 |
177 | file.open(objFile);
178 | while(file >> index) {
179 | if (index == "v") {
180 | file >> x;
181 | file >> y;
182 | file >> z;
183 | vertices.push_back(x + " " + y + " " + z);
184 | } else if (index == "vn") {
185 | file >> x;
186 | file >> y;
187 | file >> z;
188 | normals.push_back(x + " " + y + " " + z);
189 | } else if (index == "f") {
190 | file >> x;
191 | file >> y;
192 | file >> z;
193 | std::replace(x.begin(), x.end(), '/', ' ');
194 | std::replace(y.begin(), y.end(), '/', ' ');
195 | std::replace(z.begin(), z.end(), '/', ' ');
196 |
197 | std::istringstream a(x);
198 | std::istringstream b(y);
199 | std::istringstream c(z);
200 | std::string p1, n;
201 | std::string p2;
202 | std::string p3;
203 | a >> p1 >> n;
204 | b >> p2;
205 | c >> p3;
206 |
207 | std::istringstream v(vertices[std::stoi(p1)-1]);
208 | v >> x;
209 | v >> y;
210 | v >> z;
211 | Vert v1(std::stof(x), std::stof(y), std::stof(z));
212 |
213 | std::istringstream vv(vertices[std::stoi(p2)-1]);
214 | vv >> x;
215 | vv >> y;
216 | vv >> z;
217 | Vert v2(std::stof(x), std::stof(y), std::stof(z));
218 |
219 | std::istringstream vvv(vertices[std::stoi(p3)-1]);
220 | vvv >> x;
221 | vvv >> y;
222 | vvv >> z;
223 | Vert v3(std::stof(x), std::stof(y), std::stof(z));
224 |
225 | std::istringstream norm(normals[std::stoi(n)-1]);
226 | norm >> x;
227 | norm >> y;
228 | norm >> z;
229 | Trig t(v1, v2, v3, std::stof(x), std::stof(y), std::stof(z));
230 |
231 | trigs.push_back(t);
232 | }
233 | }
234 | }
235 |
236 | /* Default constructor - Set default translation/rotation amounts */
237 | Mesh::Mesh() {
238 | transAmt.x = 0.0f;
239 | transAmt.y = 0.0f;
240 | transAmt.z = 0.0f;
241 |
242 | rotAmt.x = 0.0f;
243 | rotAmt.y = 0.0f;
244 | rotAmt.z = 0.0f;
245 | }
246 |
247 | /* Default constructor - creates a simple unit cube. Inherits from mesh */
248 | Cube::Cube() {
249 | // Front Face
250 | Vert p1(-1.0f, 1.0f, 1.0f);
251 | Vert p2(-1.0f, -1.0f, 1.0f);
252 | Vert p3(1.0f, -1.0f, 1.0f);
253 | Trig t1(p1, p2, p3, 0.0f, 0.0f, 1.0f);
254 | Vert p4(1.0f, -1.0f, 1.0f);
255 | Vert p5(1.0f, 1.0f, 1.0f);
256 | Vert p6(-1.0f, 1.0f, 1.0f);
257 | Trig t2(p4, p5, p6, 0.0f, 0.0f, 1.0f);
258 | // Top Face
259 | Vert p7(-1.0f, 1.0f, 1.0f);
260 | Vert p8(1.0f, 1.0f, 1.0f);
261 | Vert p9(-1.0f, 1.0f, -1.0f);
262 | Trig t3(p7, p8, p9, 0.0f, 1.0f, 0.0f);
263 | Vert p10(1.0f, 1.0f, 1.0f);
264 | Vert p11(1.0f, 1.0f, -1.0f);
265 | Vert p12(-1.0f, 1.0f, -1.0f);
266 | Trig t4(p10, p11, p12, 0.0f, 1.0f, 0.0f);
267 | // Right Face
268 | Vert p13(1.0f, 1.0f, 1.0f);
269 | Vert p14(1.0f, -1.0f, 1.0f);
270 | Vert p15(1.0f, -1.0f, -1.0f);
271 | Trig t5(p13, p14, p15, 1.0f, 0.0f, 0.0f);
272 | Vert p16(1.0f, 1.0f, 1.0f);
273 | Vert p17(1.0f, 1.0f, -1.0f);
274 | Vert p18(1.0f, -1.0f, -1.0f);
275 | Trig t6(p16, p17, p18, 1.0f, 0.0f, 0.0f);
276 | // Left Face
277 | Vert p19(-1.0f, 1.0f, 1.0f);
278 | Vert p20(-1.0f, -1.0f, 1.0f);
279 | Vert p21(-1.0f, -1.0f, -1.0f);
280 | Trig t7(p19, p20, p21, -1.0f, 0.0f, 0.0f);
281 | Vert p22(-1.0f, 1.0f, 1.0f);
282 | Vert p23(-1.0f, 1.0f, -1.0f);
283 | Vert p24(-1.0f, -1.0f, -1.0f);
284 | Trig t8(p22, p23, p24, -1.0f, 0.0f, 0.0f);
285 | // Back Face
286 | Vert p25(-1.0f, 1.0f, -1.0f);
287 | Vert p26(-1.0f, -1.0f, -1.0f);
288 | Vert p27(1.0f, 1.0f, -1.0f);
289 | Trig t9(p25, p26, p27, 0.0f, 0.0f, -1.0f);
290 | Vert p28(1.0f, 1.0f, -1.0f);
291 | Vert p29(1.0f, -1.0f, -1.0f);
292 | Vert p30(-1.0f, -1.0f, -1.0f);
293 | Trig t10(p28, p29, p30, 0.0f, 0.0f, -1.0f);
294 | // Bottom Face
295 | Vert p31(-1.0f, -1.0f, 1.0f);
296 | Vert p32(-1.0f, -1.0f, -1.0f);
297 | Vert p33(1.0f, -1.0f, 1.0f);
298 | Trig t11(p31, p32, p33, 0.0f, -1.0f, 0.0f);
299 | Vert p34(1.0f, -1.0f, 1.0f);
300 | Vert p35(1.0f, -1.0f, -1.0f);
301 | Vert p36(-1.0f, -1.0f, -1.0f);
302 | Trig t12(p34, p35, p36, 0.0f, -1.0f, 0.0f);
303 |
304 | trigs = {t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12};
305 |
306 | transAmt.x = 0.0f;
307 | transAmt.y = 0.0f;
308 | transAmt.z = 0.0f;
309 |
310 | rotAmt.x = 0.0f;
311 | rotAmt.y = 0.0f;
312 | rotAmt.z = 0.0f;
313 | }
314 |
--------------------------------------------------------------------------------
/src/Mesh.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | /* Vertices used in a triangle
7 | * Contains the vertex position and normal (if a smooth mesh) */
8 | class Vert {
9 | public:
10 | float x, y, z; // Points to the vertex
11 | float xn, yn, zn; // Vertex normal
12 |
13 | /* Default constructor - create a vertex with NO normal
14 | * @param xx: x position
15 | * @param yy: y position
16 | * @param zz: z position */
17 | Vert(float xx, float yy, float zz);
18 |
19 | /* Default constructor - create a vertex WITH a normal
20 | * @param xx: x position
21 | * @param yy: y position
22 | * @param zz: z position
23 | * @param nx: x normal location
24 | * @param ny: y normal location
25 | * @param nz: z normal location */
26 | Vert(float xx, float yy, float zz, float nx, float ny, float nz);
27 |
28 | /* Default constructor - create a vertex
29 | * Default values: x,y,z, xn,yn,zn = 0 */
30 | Vert();
31 | };
32 |
33 | // Triangle, consists of vertices along with their normals (if smooth mesh)
34 | class Trig {
35 | public:
36 | Vert verts[3]; // Vertices of the triangle
37 | Vert norms[3]; // The vertex normals
38 | Vert fNormal; // Face normal vector
39 |
40 | /* Default constructor - create a triangle
41 | * @params p1,p2,p3: vertices of the triangle
42 | * @params x,y,z: the face normal of the triangle */
43 | Trig(Vert p1, Vert p2, Vert p3, float x, float y, float z);
44 | };
45 |
46 | // Mesh, consists of triangles
47 | class Mesh {
48 | public:
49 | std::vector trigs; // All of the triangles in the mesh
50 |
51 | /* When the mesh is rotated or translated, set the amount. Then use it when
52 | * the mesh is untranslated or unrotated */
53 | Vert transAmt;
54 | Vert rotAmt;
55 |
56 | /* Default constructor - Set default translation/rotation amounts */
57 | Mesh();
58 |
59 | /* Default constructor - load an obj file mesh (MUST BE FLAT SHADED)
60 | * @param objFile: path to file */
61 | Mesh(std::string objFile);
62 |
63 | /* Default constructor - load an obj file mesh that's smoothed
64 | * @param s: any random number, doesn't matter - doesn't do anything
65 | * @param objFile: path to file */
66 | Mesh(int s, std::string objFile);
67 |
68 | /* Translate the mesh
69 | * @params x,y,z: amount to translate in the x,y,z axis */
70 | void translate(float x, float y, float z);
71 |
72 | /* Translate the mesh statically, as in set the points and that's it
73 | * @params x,y,z: amount to translate in the x,y,z axis */
74 | void staticTranslate(float x, float y, float z);
75 |
76 | /* Scale the mesh
77 | * @param amt: the amount to scale in x,y,z */
78 | void scale(float amt);
79 |
80 | /* Undo translation */
81 | void unTranslate();
82 |
83 | /* Undo rotation on the Z axis */
84 | void unRotateZ();
85 |
86 | /* Undo rotation on the Y axis */
87 | void unRotateY();
88 |
89 | /* Undo rotation on the X axis */
90 | void unRotateX();
91 |
92 | /* Rotate the mesh
93 | * @params x,y,z: degrees in each axis */
94 | void rotate(float x, float y, float z);
95 | };
96 |
97 | class Cube : public Mesh {
98 | public:
99 | /* Default constructor - creates a simple unit cube. Inherits from mesh */
100 | Cube();
101 | };
102 |
--------------------------------------------------------------------------------
/src/Screen.cpp:
--------------------------------------------------------------------------------
1 | #include "Screen.hpp"
2 | #include
3 | #include
4 | #include
5 |
6 | /* Default constructor
7 | * @param w: screen width
8 | * @param h: screen height
9 | * @param c: screens camera
10 | * @param l: scenes light */
11 | Screen::Screen(int w, int h, Camera& c, LightD l) {
12 | camera = c;
13 | width = w;
14 | height = h;
15 | fillChar = ' ';
16 | light = l;
17 | shadeValues = ".:+*=#%@";
18 | // Allocate memory to our screen buffer. Fill with empty chars
19 | buffer.resize(height, std::vector(width, fillChar));
20 | zBuffer.resize(height, std::vector(width, 100000.f));
21 | lastTime = std::chrono::steady_clock::now();
22 | currTime = std::chrono::steady_clock::now();
23 | renderMode = 1; // Use z buffering by default
24 | }
25 |
26 | // Calculate deltaTime
27 | void Screen::start() {
28 | lastTime = currTime;
29 | currTime = std::chrono::steady_clock::now();
30 | auto dT = std::chrono::duration_cast
31 | (currTime - lastTime);
32 | deltaTime = dT.count() / 1000.f;
33 | }
34 |
35 | // Print the contents of the screen buffer
36 | void Screen::print() {
37 | for (int y = 0; y < height; y++) {
38 | for (int x = 0; x < width; x++) {
39 | std::cout << buffer[y][x];
40 | }
41 | std::cout << "\n";
42 | }
43 | // Sleep values slow the program down so it is less jittery
44 | //usleep(8500);
45 | //usleep(9000);
46 | usleep(12000);
47 | //usleep(15000);
48 | }
49 |
50 | // Clear the contents of the screen buffer and z buffer
51 | void Screen::clear() {
52 | buffer.clear();
53 | buffer.resize(height, std::vector(width, fillChar));
54 | zBuffer.clear();
55 | zBuffer.resize(height, std::vector(width, 100000.f));
56 | }
57 |
58 | /* Draw to the buffer at a specific point. Does bounds checking
59 | * @param x: x position
60 | * @param y: y position
61 | * @param c: character to draw to the buffer */
62 | void Screen::drawToBuffer(float x, float y, char c) {
63 | if (x < width && y < height && x >= 0 && y >= 0) {
64 | x = round(x);
65 | y = round(y);
66 | if (renderMode == 0) { // Z buffer disabled, simply draw
67 | buffer[y][x] = c;
68 | } else if (renderMode == 1) {
69 | float z = calcZ(x, y, zCross, zVert); // Calculate the z value
70 | if(checkZB(x, y, z)) { // Check if z is < than current z buffer value
71 | zBuffer[y][x] = z; // Replace with new z value
72 | if (c == 'S') { // If smooth shading
73 | // All that's happening is calculating the shade of the pixel
74 | float w1, w2, w3;
75 | calcBary(tP1, tP2, tP3, x, y, w1, w2, w3);
76 |
77 | /* Use barycentric coords to calculate the weights between the normals
78 | * of the triangle */
79 | float xx = ((w1 * p1N.x) + (w2 * p2N.x) + (w3 * p3N.x));
80 | float yy = ((w1 * p1N.y) + (w2 * p2N.y) + (w3 * p3N.y));
81 | float zz = ((w1 * p1N.z) + (w2 * p2N.z) + (w3 * p3N.z));
82 | float l = sqrtf(xx*xx + yy*yy + zz*zz);
83 | xx /= l;
84 | yy /= l;
85 | zz /= l;
86 | Vert pNormal(xx, yy, zz); // The normal vector of the pixel
87 |
88 | float shade = round((abs(dot(pNormal, light.direction)) * 8)) - 1;
89 | if (shade <= 0) {
90 | shade = 0;
91 | } else if (shade >= 7) {
92 | shade = 7;
93 | } else if (std::isnan(shade)) {
94 | shade = 0;
95 | }
96 | c = shadeValues[shade];
97 | }
98 | buffer[y][x] = c;
99 | }
100 | }
101 | }
102 | }
103 |
104 | /* Check the Z Buffer at a specific location
105 | * @params x,y: the x and y coordinate in z buffer
106 | * @param z: z we want to check with
107 | * @return bool: if the z we check with is < than z in z buffer */
108 | bool Screen::checkZB(float x, float y, float z) {
109 | /*
110 | if (round(abs(zBuffer[y][x] - z)) == 0) {
111 | return false;
112 | } else {
113 | return z < zBuffer[y][x];
114 | }*/
115 | return z < zBuffer[y][x];
116 | }
117 |
118 | /* Draw a line to the buffer using individual coordinates
119 | * @param x1: x position of point 1
120 | * @param y1: y position of point 1
121 | * @param x2: x position of point 2
122 | * @param y2: y position of point 2
123 | * @param c: character to draw to the buffer */
124 | void Screen::drawLine(float x1, float y1, float x2, float y2, char c) {
125 | x1 = round(x1);
126 | y1 = round(y1);
127 | x2 = round(x2);
128 | y2 = round(y2);
129 | if (x1 == x2) { // If the x's are the same, either vertical line or point
130 | if (y1 == y2) {
131 | drawToBuffer(x1, y1, c);
132 | } else {
133 | if (y1 > y2) {
134 | std::swap(y1, y2); // Make sure y1 is smaller than y2 for the loop
135 | }
136 | for (int y = y1; y <= y2; y++) {
137 | drawToBuffer(x1, y, c);
138 | }
139 | }
140 | } else {
141 | float slope = (float)(y2 - y1) / (float)(x2 - x1);
142 | float intercept = y1 - (slope * x1);
143 | if (x1 > x2) {
144 | std::swap(x1, x2); // Make sure x1 is smaller than x2 so we loop properly
145 | }
146 | for (int x = x1; x <= x2; x++) {
147 | int y = round((slope * x) + intercept);
148 | drawToBuffer(x, y, c);
149 | }
150 | if (y1 > y2) {
151 | std::swap(y1, y2);
152 | }
153 | for (int y = y1; y <= y2; y++) {
154 | int x = round((y-intercept) / slope);
155 | drawToBuffer(x, y, c);
156 | }
157 | }
158 | }
159 |
160 | /* Draw a line to the buffer using verts
161 | * @param a: first vertex
162 | * @param b: second vertex
163 | * @param c: character to draw to the buffer */
164 | void Screen::drawLine(Vert a, Vert b, char c) {
165 | drawLine(a.x, a.y, b.x, b.y, c);
166 | }
167 |
168 | /* Draw a triangle to the buffer
169 | * @param t: triangle to draw
170 | * @param c: character to draw to the buffer */
171 | void Screen::drawTrig(Trig t, char c) {
172 | drawLine(t.verts[0], t.verts[1], c);
173 | drawLine(t.verts[1], t.verts[2], c);
174 | drawLine(t.verts[2], t.verts[0], c);
175 | }
176 |
177 | /* Draw all of the triangles in a mesh to the buffer
178 | * @param m: mesh
179 | * @param c: draw character */
180 | void Screen::drawMesh(Mesh m, char c) {
181 | for (auto &trig : m.trigs) {
182 | // Check if the triangle is visible to the camera via its normal
183 | if (dot(trig.fNormal, direc(trig.verts[0], camera.pos)) < 0.0f) {
184 |
185 | project(trig, camera.projMat);
186 |
187 | centerFlipY(trig);
188 |
189 | // Get zCross and zVert, which is used for z buffer calculations
190 | Vert v1 = direc(trig.verts[1], trig.verts[0]);
191 | Vert v2 = direc(trig.verts[2], trig.verts[0]);
192 | zCross = cross(v1, v2);
193 | zVert = trig.verts[0];
194 |
195 | drawTrig(trig, c);
196 | }
197 | }
198 | }
199 |
200 | /* Draw all of the triangles in wireframe. DOESN'T consider normals
201 | * @param m: mesh
202 | * @param c: draw character */
203 | void Screen::drawMeshWire(Mesh m, char c) {
204 | for (auto &trig : m.trigs) {
205 | project(trig, camera.projMat);
206 |
207 | centerFlipY(trig);
208 |
209 | // Get zCross and zVert, which is used for z buffer calculations
210 | Vert v1 = direc(trig.verts[1], trig.verts[0]);
211 | Vert v2 = direc(trig.verts[2], trig.verts[0]);
212 | zCross = cross(v1, v2);
213 | zVert = trig.verts[0];
214 |
215 | drawTrig(trig, c);
216 | }
217 | }
218 |
219 | /* Fill in a mesh via a character
220 | * @param m: mesh
221 | * @param c: draw character */
222 | void Screen::fillMesh(Mesh m, char c) {
223 | for (auto &trig : m.trigs) {
224 | // Check if triangle is visible to camera via its normal
225 | if (dot(trig.fNormal, direc(trig.verts[0], camera.pos)) < 0.0f) {
226 | project(trig, camera.projMat);
227 |
228 | centerFlipY(trig);
229 |
230 | // Get zCross and zVert, which is used for z buffer calculations
231 | Vert v1 = direc(trig.verts[1], trig.verts[0]);
232 | Vert v2 = direc(trig.verts[2], trig.verts[0]);
233 | zCross = cross(v1, v2);
234 | zVert = trig.verts[0];
235 |
236 | // Triangles must be sorted basted on y value. This allows to determine
237 | // is it's a flat bottom, flat top, or to split it via the middle point
238 | std::sort(trig.verts, trig.verts + 3,
239 | [](Vert const& a, Vert const& b) -> bool {
240 | return a.y < b.y;
241 | });
242 |
243 | if (trig.verts[1].y == trig.verts[2].y) {
244 | fillFb(trig, c);
245 | } else if (trig.verts[0].y == trig.verts[1].y) {
246 | fillFt(trig, c);
247 | } else {
248 | // Not flat bottom or top, thus split the triangle in half via mid point
249 | float m1 = (trig.verts[0].y - trig.verts[2].y) /
250 | (trig.verts[0].x - trig.verts[2].x);
251 |
252 | float b1 = trig.verts[0].y - (m1 * trig.verts[0].x);
253 | Vert n((trig.verts[1].y-b1)/m1, trig.verts[1].y, 0.0f);
254 | Trig fb(trig.verts[0], n, trig.verts[1], 0.0f, 0.0f, 0.0f);
255 | Trig ft(n, trig.verts[1], trig.verts[2], 0.0f, 0.0f, 0.0f);
256 | fillFt(ft, c);
257 | fillFb(fb, c);
258 | }
259 | drawTrig(trig, c);
260 | }
261 | }
262 | }
263 |
264 | /* Fill a flat bottom triangle
265 | * @param t: triangle
266 | * @param c: draw character */
267 | void Screen::fillFb(Trig t, char c) {
268 |
269 | float m1 = (t.verts[0].y - t.verts[1].y) / (t.verts[0].x - t.verts[1].x);
270 | float b1 = t.verts[0].y - (m1 * t.verts[0].x);
271 |
272 | float m2 = (t.verts[0].y - t.verts[2].y) / (t.verts[0].x - t.verts[2].x);
273 | float b2 = t.verts[0].y - (m2 * t.verts[0].x);
274 |
275 | drawLine(t.verts[0].x, t.verts[0].y, t.verts[0].x, t.verts[0].y, c);
276 |
277 | float x1;
278 | float x2;
279 |
280 | for (int y = t.verts[0].y+1; y <= t.verts[2].y; y++) {
281 | if (std::isfinite(m1)) { // If divide by 0 (vertical line)
282 | x1 = (y - b1) / m1;
283 | } else {
284 | x1 = t.verts[0].x;
285 | }
286 |
287 | if (std::isfinite(m2)) {
288 | x2 = (y - b2) / m2;
289 | } else {
290 | x2 = t.verts[0].x;
291 | }
292 | drawLine(x1, y, x2, y, c);
293 | }
294 | }
295 |
296 | /* Fill a flat top triangle
297 | * @param t: triangle
298 | * @param c: draw character */
299 | void Screen::fillFt(Trig t, char c) {
300 | float m1 = (t.verts[2].y - t.verts[0].y) / (t.verts[2].x - t.verts[0].x);
301 | float b1 = t.verts[2].y - (m1 * t.verts[2].x);
302 |
303 | float m2 = (t.verts[2].y - t.verts[1].y) / (t.verts[2].x - t.verts[1].x);
304 | float b2 = t.verts[2].y - (m2 * t.verts[2].x);
305 |
306 | drawLine(t.verts[2].x, t.verts[2].y, t.verts[2].x, t.verts[2].y, c);
307 |
308 | float x1;
309 | float x2;
310 | for (int y = t.verts[2].y; y >= t.verts[0].y; y--) {
311 | if (std::isfinite(m1)) { // If divide by 0 (vertical line)
312 | x1 = (y - b1) / m1;
313 | } else {
314 | x1 = t.verts[2].x;
315 | }
316 |
317 | if (std::isfinite(m2)) {
318 | x2 = (y - b2) / m2;
319 | } else {
320 | x2 = t.verts[2].x;
321 | }
322 | drawLine(x1, y, x2, y, c);
323 | }
324 | }
325 |
326 | /* Shade in the mesh based on the light direction. FLAT SHADED!
327 | * @param m: mesh */
328 | void Screen::shadeMesh(Mesh m) {
329 | for (auto &trig : m.trigs) {
330 | // Check if the triangle is visible via its normal vector
331 | if (dot(trig.fNormal, direc(trig.verts[0], camera.pos)) < 0.0f) {
332 |
333 | project(trig, camera.projMat);
334 |
335 | centerFlipY(trig);
336 |
337 | // zCross and zVert used for z buffer calculations
338 | Vert v1 = direc(trig.verts[1], trig.verts[0]);
339 | Vert v2 = direc(trig.verts[2], trig.verts[0]);
340 | zCross = cross(v1, v2);
341 | zVert = trig.verts[0];
342 |
343 | // Calculate the shade character based on the light direction
344 | float shade = round((abs(dot(trig.fNormal, light.direction)) * 8)) - 1;
345 | if (shade <= 0) {
346 | shade = 0;
347 | } else if(shade >= 7) {
348 | shade = 7;
349 | } else if (std::isnan(shade)) {
350 | shade = 0;
351 | }
352 | char c = shadeValues[shade];
353 |
354 | // Triangles must be sorted basted on y value. This allows to determine
355 | // is it's a flat bottom, flat top, or to split it via the middle point
356 | std::sort(trig.verts, trig.verts + 3,
357 | [](Vert const& a, Vert const& b) -> bool {
358 | return a.y < b.y;
359 | });
360 |
361 | if (trig.verts[1].y == trig.verts[2].y) {
362 | fillFb(trig, c);
363 | } else if (trig.verts[0].y == trig.verts[1].y) {
364 | fillFt(trig, c);
365 | } else {
366 | float m1 = (trig.verts[0].y - trig.verts[2].y) /
367 | (trig.verts[0].x - trig.verts[2].x);
368 |
369 | float b1 = trig.verts[0].y - (m1 * trig.verts[0].x);
370 | Vert n((trig.verts[1].y-b1)/m1, trig.verts[1].y, 0.0f);
371 | n.z = calcZ(n.x, n.y, zCross, zVert);
372 | Trig fb(trig.verts[0], n, trig.verts[1], 0.0f, 0.0f, 0.0f);
373 | Trig ft(n, trig.verts[1], trig.verts[2], 0.0f, 0.0f, 0.0f);
374 | fillFt(ft, c);
375 | fillFb(fb, c);
376 | }
377 | drawTrig(trig, c);
378 | }
379 | }
380 | }
381 |
382 | /* Shade in the mesh smooth based on the light direction
383 | * @param m: SMOOTHED mesh */
384 | void Screen::shadeMeshSmooth(Mesh m) {
385 | for (auto &trig : m.trigs) {
386 | // Check if the triangle is visible via its normal vector
387 | if (dot(trig.fNormal, direc(trig.verts[0], camera.pos)) < 0.0f) {
388 |
389 | project(trig, camera.projMat);
390 |
391 | centerFlipY(trig);
392 |
393 | // zCross and zVert used for z buffer calculations
394 | Vert v1 = direc(trig.verts[1], trig.verts[0]);
395 | Vert v2 = direc(trig.verts[2], trig.verts[0]);
396 | zCross = cross(v1, v2);
397 | zVert = trig.verts[0];
398 |
399 | // Store the triangle vertice normals and positions, this is used to
400 | // interpolate for each pixel and get the normals for each pixel
401 | p1N = trig.norms[0];
402 | p2N = trig.norms[1];
403 | p3N = trig.norms[2];
404 | tP1 = trig.verts[0];
405 | tP2 = trig.verts[1];
406 | tP3 = trig.verts[2];
407 |
408 | // Triangles must be sorted basted on y value. This allows to determine
409 | // is it's a flat bottom, flat top, or to split it via the middle point
410 | std::sort(trig.verts, trig.verts + 3,
411 | [](Vert const& a, Vert const& b) -> bool {
412 | return a.y < b.y;
413 | });
414 |
415 | // The draw character is set as 'S' to tell the draw functions it will
416 | // be a smooth shaded object
417 | if (trig.verts[1].y == trig.verts[2].y) {
418 | fillFb(trig, 'S');
419 | } else if (trig.verts[0].y == trig.verts[1].y) {
420 | fillFt(trig, 'S');
421 | } else {
422 | float m1 = (trig.verts[0].y - trig.verts[2].y) /
423 | (trig.verts[0].x - trig.verts[2].x);
424 |
425 | float b1 = trig.verts[0].y - (m1 * trig.verts[0].x);
426 | Vert n((trig.verts[1].y-b1)/m1, trig.verts[1].y, 0.0f);
427 | n.z = calcZ(n.x, n.y, zCross, zVert);
428 | Trig fb(trig.verts[0], n, trig.verts[1], 0.0f, 0.0f, 0.0f);
429 | Trig ft(n, trig.verts[1], trig.verts[2], 0.0f, 0.0f, 0.0f);
430 | fillFt(ft, 'S');
431 | fillFb(fb, 'S');
432 | }
433 | drawTrig(trig, 'S');
434 | }
435 | }
436 | }
437 |
438 | /* Center the triangles to the middle of the screen, flip the triangles so
439 | * +y is up, and scale the x based on aspect ratio
440 | * @param t: triangle */
441 | void Screen::centerFlipY(Trig& t) {
442 |
443 | // Flip triangle verts
444 | t.verts[0].y *= -1.f;
445 | t.verts[1].y *= -1.f;
446 | t.verts[2].y *= -1.f;
447 |
448 | // Scale by aspect ratio
449 | t.verts[0].x *= camera.a*2.5;
450 | t.verts[1].x *= camera.a*2.5;
451 | t.verts[2].x *= camera.a*2.5f;
452 |
453 | // Move the very center of screen
454 | t.verts[0].x += (float)width/2;
455 | t.verts[0].y += (float)height/2.f;
456 | t.verts[1].x += (float)width/2;
457 | t.verts[1].y += (float)height/2.f;
458 | t.verts[2].x += (float)width/2;
459 | t.verts[2].y += (float)height/2.f;
460 | }
461 |
--------------------------------------------------------------------------------
/src/Screen.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | /* Screen library for simulating screen in terminal */
4 |
5 | #include
6 | #include
7 | #include "agm.hpp"
8 | #include "Camera.hpp"
9 | #include "Lights.hpp"
10 | #include "Mesh.hpp"
11 | #include
12 |
13 | // Screen simulation that allows printing ascii characters to the terminal
14 | class Screen {
15 | public:
16 | // 2D vector that stores the 'pixels' to be printed
17 | std::vector> buffer;
18 | // 2D vector that stores the z values of each pixel
19 | std::vector> zBuffer;
20 |
21 | int renderMode; // 0 to disable zBuffer, 1 to enable zBuffer;
22 | Vert zCross; // Our cross product for zBuffer calculation
23 | Vert zVert; // Our vert for interpolating Z for pixel
24 |
25 | Vert p1N; // Current triangle vertex normals. Used for smooth shading
26 | Vert p2N; // Only used when we call the shadeMeshSmooth function
27 | Vert p3N;
28 |
29 | Vert tP1; // Our current triangle points. Used for calculating bary coords
30 | Vert tP2;
31 | Vert tP3;
32 |
33 | // Screen width and height
34 | int width, height;
35 |
36 | float deltaTime; // Delta time of the program
37 |
38 | std::string shadeValues; // Store the different ascii characters for shading
39 |
40 | // What the screen buffer is filled with. By default it's an empty space, so
41 | // that the screen is empty. Can change for debug/style purposes
42 | char fillChar;
43 |
44 | // The camera that the screen will use
45 | Camera camera;
46 |
47 | // The light in the scene
48 | LightD light;
49 |
50 | /* Default constructor
51 | * @param w: screen width
52 | * @param h: screen height
53 | * @param c: screens camera
54 | * @param l: scenes light */
55 | Screen(int w, int h, Camera& c, LightD l);
56 |
57 | // Print the contents of the screen buffer
58 | void print();
59 |
60 | // Clear the contents of the screen buffer and z buffer
61 | void clear();
62 |
63 | // Calculate deltaTime
64 | void start();
65 |
66 | // Used to calculate the deltaTime
67 | std::chrono::steady_clock::time_point lastTime;
68 | std::chrono::steady_clock::time_point currTime;
69 |
70 | /* Draw a line to the buffer using individual coordinates
71 | * @param x1: x position of point 1
72 | * @param y1: y position of point 1
73 | * @param x2: x position of point 2
74 | * @param y2: y position of point 2
75 | * @param c: character to draw to the buffer */
76 | void drawLine(float x1, float y1, float x2, float y2, char c);
77 |
78 | /* Draw a line to the buffer using verts
79 | * @param a: first vertex
80 | * @param b: second vertex
81 | * @param c: character to draw to the buffer */
82 | void drawLine(Vert a, Vert b, char c);
83 |
84 | /* Draw a triangle to the buffer
85 | * @param t: triangle to draw
86 | * @param c: character to draw to the buffer */
87 | void drawTrig(Trig t, char c);
88 |
89 | /* Draw all of the triangles in a mesh to the buffer
90 | * @param m: mesh
91 | * @param c: draw character */
92 | void drawMesh(Mesh m, char c);
93 |
94 | /* Draw all of the triangles in wireframe. DOESN'T consider normals
95 | * @param m: mesh
96 | * @param c: draw character */
97 | void drawMeshWire(Mesh m, char c);
98 |
99 | /* Fill in a mesh via a character
100 | * @param m: mesh
101 | * @param c: draw character */
102 | void fillMesh(Mesh m, char c);
103 |
104 | /* Shade in the mesh based on the light direction. FLAT SHADED!
105 | * @param m: mesh */
106 | void shadeMesh(Mesh m);
107 |
108 | /* Shade in the mesh smooth based on the light direction
109 | * @param m: SMOOTHED mesh */
110 | void shadeMeshSmooth(Mesh m);
111 |
112 | /* Fill a flat bottom triangle
113 | * @param t: triangle
114 | * @param c: draw character */
115 | void fillFb(Trig t, char c);
116 |
117 | /* Fill a flat top triangle
118 | * @param t: triangle
119 | * @param c: draw character */
120 | void fillFt(Trig t, char c);
121 |
122 | private:
123 | /* Draw to the buffer at a specific point. Does bounds checking
124 | * @param x: x position
125 | * @param y: y position
126 | * @param c: character to draw to the buffer */
127 | void drawToBuffer(float x, float y, char c);
128 |
129 | /* Center the triangles to the middle of the screen, flip the triangles so
130 | * +y is up, and scale the x based on aspect ratio
131 | * @param t: triangle */
132 | void centerFlipY(Trig& t);
133 |
134 | /* Check the Z Buffer at a specific location
135 | * @params x,y: the x and y coordinate in z buffer
136 | * @param z: z we want to check with
137 | * @return bool: if the z we check with is < than z in z buffer */
138 | bool checkZB(float x, float y, float z);
139 |
140 | };
141 |
--------------------------------------------------------------------------------
/src/agm.cpp:
--------------------------------------------------------------------------------
1 | #include "agm.hpp"
2 | #include
3 |
4 | /* Default constructor - Create 4x4 matrix with 0.0f */
5 | Mat4::Mat4() {
6 | m.resize(4, std::vector(4, 0.0f));
7 | }
8 |
9 | /* Creates a Mat4 with the perspective projection matrix values applied
10 | * @param aspect: the screen aspect ratio
11 | * @param fov: the field of view
12 | * @param zNear: the near clipping plane
13 | * @param zFar: the far clipping plane
14 | * @return Mat4: matrix with projection values */
15 | Mat4 perspective(float aspect, float fov, float zNear, float zFar) {
16 | Mat4 m;
17 | float fovRad = (fov/2.f) * (3.141592f / 180.f); // Convert to radians
18 |
19 | m.m[0][0] = (1.f / (tan(fovRad))) / aspect;
20 | m.m[1][1] = 1.f / tanf(fovRad);
21 | m.m[2][2] = ((-2.f * zNear) / (zFar - zNear)) - 1.f;
22 | m.m[3][2] = (-zNear * zFar) / (zFar - zNear);
23 | m.m[2][3] = -1.0f;
24 |
25 | return m;
26 | }
27 |
28 | /* Creates a Mat4 with the rotation matrix along the X axis
29 | * @param degrees: the number of degrees to rotate
30 | * @return Mat4: rotation matrix */
31 | Mat4 rotX(float degrees) {
32 | Mat4 m;
33 | float radians = (degrees) * (3.141592f / 180.f);
34 |
35 | m.m[0][0] = 1.0f;
36 | m.m[1][1] = cosf(radians);
37 | m.m[1][2] = -sinf(radians);
38 | m.m[2][1] = sinf(radians);
39 | m.m[2][2] = cosf(radians);
40 | m.m[3][3] = 1.0f;
41 |
42 | return m;
43 | }
44 |
45 | /* Creates a Mat4 with the rotation matrix along the Y axis
46 | * @param degrees: the number of degrees to rotate
47 | * @return Mat4: rotation matrix */
48 | Mat4 rotY(float degrees) {
49 | Mat4 m;
50 | float radians = (degrees) * (3.141592f / 180.f);
51 |
52 | m.m[0][0] = cosf(radians);
53 | m.m[0][2] = sinf(radians);
54 | m.m[1][1] = 1.0f;
55 | m.m[2][0] = -sinf(radians);
56 | m.m[2][2] = cosf(radians);
57 | m.m[3][3] = 1.0f;
58 |
59 | return m;
60 | }
61 |
62 | /* Creates a Mat4 with the rotation matrix along the Z axis
63 | * @param degrees: the number of degrees to rotate
64 | * @return Mat4: rotation matrix */
65 | Mat4 rotZ(float degrees) {
66 | Mat4 m;
67 | float radians = (degrees) * (3.141592f / 180.f);
68 |
69 | m.m[0][0] = cosf(radians);
70 | m.m[0][1] = -sinf(radians);
71 | m.m[1][0] = sinf(radians);
72 | m.m[1][1] = cosf(radians);
73 | m.m[2][2] = 1.f;
74 | m.m[3][3] = 1.f;
75 |
76 | return m;
77 | }
78 |
79 | /* Rotate the mesh
80 | * @params x,y,z: degrees in each axis */
81 | void Mesh::rotate(float x, float y, float z) {
82 | Mat4 xMat = ::rotX(x);
83 | Mat4 yMat = ::rotY(y);
84 | Mat4 zMat = ::rotZ(z);
85 |
86 | /* Since the mesh should rotate locally, it must untranslate back to the
87 | * origin, unrotate the mesh, then apply the new rotations, and then move the
88 | * mesh back to its original location */
89 | unTranslate();
90 | unRotateZ();
91 | unRotateX();
92 | unRotateY();
93 | for (auto &trig : trigs) {
94 | trig.verts[0] = mult4(trig.verts[0], yMat);
95 | trig.verts[1] = mult4(trig.verts[1], yMat);
96 | trig.verts[2] = mult4(trig.verts[2], yMat);
97 | trig.norms[0] = mult4(trig.norms[0], yMat);
98 | trig.norms[1] = mult4(trig.norms[1], yMat);
99 | trig.norms[2] = mult4(trig.norms[2], yMat);
100 | trig.fNormal = mult4(trig.fNormal, yMat);
101 |
102 | trig.verts[0] = mult4(trig.verts[0], xMat);
103 | trig.verts[1] = mult4(trig.verts[1], xMat);
104 | trig.verts[2] = mult4(trig.verts[2], xMat);
105 | trig.norms[0] = mult4(trig.norms[0], xMat);
106 | trig.norms[1] = mult4(trig.norms[1], xMat);
107 | trig.norms[2] = mult4(trig.norms[2], xMat);
108 | trig.fNormal = mult4(trig.fNormal, xMat);
109 |
110 | trig.verts[0] = mult4(trig.verts[0], zMat);
111 | trig.verts[1] = mult4(trig.verts[1], zMat);
112 | trig.verts[2] = mult4(trig.verts[2], zMat);
113 | trig.norms[0] = mult4(trig.norms[0], zMat);
114 | trig.norms[1] = mult4(trig.norms[1], zMat);
115 | trig.norms[2] = mult4(trig.norms[2], zMat);
116 | trig.fNormal = mult4(trig.fNormal, zMat);
117 | }
118 | staticTranslate(transAmt.x, transAmt.y, transAmt.z); // Bring the mesh back
119 |
120 | /* These amounts are used to unrotate the mesh. Set them here and then
121 | * use them next time the unrotate functions are called */
122 | rotAmt.x = x;
123 | rotAmt.y = y;
124 | rotAmt.z = z;
125 |
126 | }
127 |
128 | /* Translate the mesh
129 | * @params x,y,z: amount to translate in the x,y,z axis */
130 | void Mesh::translate(float x, float y, float z) {
131 | /* Set the translated amounts. These are used when the mesh is untranslated
132 | * for its local rotations */
133 | transAmt.x += x;
134 | transAmt.y += y;
135 | transAmt.z += z;
136 |
137 | for (auto &trig : trigs) {
138 | trig.verts[0].x += x;
139 | trig.verts[0].y += y;
140 | trig.verts[0].z += z;
141 |
142 | trig.verts[1].x += x;
143 | trig.verts[1].y += y;
144 | trig.verts[1].z += z;
145 |
146 | trig.verts[2].x += x;
147 | trig.verts[2].y += y;
148 | trig.verts[2].z += z;
149 | }
150 | }
151 |
152 | /* Translate the mesh statically, as in set the points and that's it
153 | * @params x,y,z: amount to translate in the x,y,z axis */
154 | void Mesh::staticTranslate(float x, float y, float z) {
155 | for (auto &trig : trigs) {
156 | trig.verts[0].x += x;
157 | trig.verts[0].y += y;
158 | trig.verts[0].z += z;
159 |
160 | trig.verts[1].x += x;
161 | trig.verts[1].y += y;
162 | trig.verts[1].z += z;
163 |
164 | trig.verts[2].x += x;
165 | trig.verts[2].y += y;
166 | trig.verts[2].z += z;
167 | }
168 | }
169 |
170 | /* Scale the mesh
171 | * @param amt: the amount to scale in x,y,z */
172 | void Mesh::scale(float amt) {
173 | unTranslate();
174 | for (auto &trig : trigs) {
175 | trig.verts[0].x *= amt;
176 | trig.verts[0].y *= amt;
177 | trig.verts[0].z *= amt;
178 |
179 | trig.verts[1].x *= amt;
180 | trig.verts[1].y *= amt;
181 | trig.verts[1].z *= amt;
182 |
183 | trig.verts[2].x *= amt;
184 | trig.verts[2].y *= amt;
185 | trig.verts[2].z *= amt;
186 | }
187 | staticTranslate(transAmt.x, transAmt.y, transAmt.z);
188 | }
189 |
190 | /* Undo rotation on the X axis */
191 | void Mesh::unRotateX() {
192 |
193 | Mat4 m = ::rotX(-rotAmt.x);
194 |
195 | for (auto &trig : trigs) {
196 | trig.verts[0] = mult4(trig.verts[0], m);
197 | trig.verts[1] = mult4(trig.verts[1], m);
198 | trig.verts[2] = mult4(trig.verts[2], m);
199 | trig.norms[0] = mult4(trig.norms[0], m);
200 | trig.norms[1] = mult4(trig.norms[1], m);
201 | trig.norms[2] = mult4(trig.norms[2], m);
202 | trig.fNormal = mult4(trig.fNormal, m);
203 | }
204 | rotAmt.x = 0.0f;
205 | }
206 |
207 | /* Undo rotation on the Y axis */
208 | void Mesh::unRotateY() {
209 | Mat4 m = ::rotY(-rotAmt.y);
210 | for (auto &trig : trigs) {
211 | trig.verts[0] = mult4(trig.verts[0], m);
212 | trig.verts[1] = mult4(trig.verts[1], m);
213 | trig.verts[2] = mult4(trig.verts[2], m);
214 | trig.norms[0] = mult4(trig.norms[0], m);
215 | trig.norms[1] = mult4(trig.norms[1], m);
216 | trig.norms[2] = mult4(trig.norms[2], m);
217 | trig.fNormal = mult4(trig.fNormal, m);
218 | }
219 | rotAmt.y = 0.0f;
220 |
221 | }
222 |
223 | /* Undo rotation on the Z axis */
224 | void Mesh::unRotateZ() {
225 |
226 | Mat4 m = ::rotZ(-rotAmt.z);
227 | for (auto &trig : trigs) {
228 | trig.verts[0] = mult4(trig.verts[0], m);
229 | trig.verts[1] = mult4(trig.verts[1], m);
230 | trig.verts[2] = mult4(trig.verts[2], m);
231 | trig.norms[0] = mult4(trig.norms[0], m);
232 | trig.norms[1] = mult4(trig.norms[1], m);
233 | trig.norms[2] = mult4(trig.norms[2], m);
234 | trig.fNormal = mult4(trig.fNormal, m);
235 | }
236 | rotAmt.z = 0.0f;
237 | }
238 |
239 | /* Undo translation */
240 | void Mesh::unTranslate() {
241 |
242 | for (auto &trig : trigs) {
243 | trig.verts[0].x -= transAmt.x;
244 | trig.verts[0].y -= transAmt.y;
245 | trig.verts[0].z -= transAmt.z;
246 |
247 | trig.verts[1].x -= transAmt.x;
248 | trig.verts[1].y -= transAmt.y;
249 | trig.verts[1].z -= transAmt.z;
250 |
251 | trig.verts[2].x -= transAmt.x;
252 | trig.verts[2].y -= transAmt.y;
253 | trig.verts[2].z -= transAmt.z;
254 |
255 | }
256 | }
257 |
258 | /* Multiply a vertex by a 4x4 matrix - performs division by w
259 | * @param v: the vertex
260 | * @param m: the 4x4 matrix
261 | * @return Vert: product of multiplcation */
262 | Vert mult4(Vert v, Mat4& m) {
263 |
264 | Vert a;
265 |
266 | // Temporary variables to multiply with
267 | float tx = v.x;
268 | float ty = v.y;
269 | float tz = v.z;
270 |
271 | a.x = (tx * m.m[0][0]) + (ty * m.m[0][1]) + (tz * m.m[0][2]) + m.m[0][3];
272 | a.y = tx * m.m[1][0] + ty * m.m[1][1] + tz * m.m[1][2] + m.m[1][3];
273 | a.z = tx * m.m[2][0] + ty * m.m[2][1] + tz * m.m[2][2] + m.m[2][3];
274 | float w = tx * m.m[3][0] + ty * m.m[3][1] + tz * m.m[3][2] + m.m[3][3];
275 |
276 | if (w != 0.0f) {
277 | a.x /= w;
278 | a.y /= w;
279 | a.z /= w;
280 | }
281 |
282 | a.xn = v.xn;
283 | a.yn = v.yn;
284 | a.zn = v.zn;
285 |
286 | return a;
287 | }
288 |
289 | /* Calculate the dot product between two vertices
290 | * @params a,b: vertices
291 | * @return float: result */
292 | float dot(Vert a, Vert b) {
293 | return ((a.x*b.x) + (a.y*b.y) + (a.z*b.z));
294 | }
295 |
296 | /* Calculate the direction vector a-b
297 | * @params a,b: vertices
298 | * @return Vert: direction vector result */
299 | Vert direc(Vert a, Vert b) {
300 | Vert r(a.x-b.x, a.y-b.y, a.z-b.z);
301 | return r;
302 | }
303 |
304 | /* Calculate the cross product of two vectors
305 | * @params a,b: vertices
306 | * @return Vert: cross product result */
307 | Vert cross(Vert a, Vert b) {
308 | Vert c((a.y*b.z)-(a.z*b.y), (a.z*b.x)-(a.x*b.z), (a.x*b.y)-(a.y*b.x));
309 | return c;
310 | }
311 |
312 | /* Project a triangle with a perspective matrix
313 | * @param t: the triangle
314 | * @param m: the perspective matrix */
315 | void project(Trig& t, Mat4 m) {
316 | t.verts[0] = mult4(t.verts[0], m);
317 | t.verts[1] = mult4(t.verts[1], m);
318 | t.verts[2] = mult4(t.verts[2], m);
319 | }
320 |
321 | /* Calculate the Z value from x,y using cross and vert of equation of plane
322 | * @params x,y: the input x and y values
323 | * @param c: cross product vertex from equation of plane
324 | * @param v: point from our original triangle from equation of plane
325 | * @return float: the z value */
326 | float calcZ(float x, float y, Vert c, Vert v) {
327 | float k = dot(c, v);
328 | float z = ((c.x*x + c.y*y - k) / (-c.z));
329 | return z;
330 | }
331 |
332 | /* Calculate the bary coordinates given 3 points of a triangle and x and y
333 | * inside of the triangle
334 | * @params p1,p2,p3: vertices of the triangle
335 | * @params x,y: current point within the triangle
336 | * @params w1,w2,w3: store the bary outputs */
337 | void calcBary(Vert p1, Vert p2, Vert p3, int x, int y, float& w1, float& w2,
338 | float& w3) {
339 | w1 = (((p2.y - p3.y) * (x - p3.x)) + (p3.x - p2.x) * (y - p3.y)) /
340 | (((p2.y - p3.y) * (p1.x - p3.x)) + ((p3.x - p2.x) * (p1.y - p3.y)));
341 | w2 = (((p3.y - p1.y) * (x - p3.x)) + ((p1.x - p3.x) * (y - p3.y))) /
342 | (((p2.y - p3.y) * (p1.x - p3.x)) + ((p3.x - p2.x) * (p1.y - p3.y)));
343 | w3 = 1 - w1 - w2;
344 | }
345 |
--------------------------------------------------------------------------------
/src/agm.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include "Mesh.hpp"
5 |
6 | /* Ascii Graphics Mathematics library */
7 |
8 | /* 4x4 matrix of float values. Uses std vector */
9 | class Mat4 {
10 | public:
11 | std::vector> m;
12 |
13 | /* Default constructor - Create 4x4 matrix with 0.0f */
14 | Mat4();
15 | };
16 |
17 |
18 | /* Creates a Mat4 with the perspective projection matrix values applied
19 | * @param aspect: the screen aspect ratio
20 | * @param fov: the field of view
21 | * @param zNear: the near clipping plane
22 | * @param zFar: the far clipping plane
23 | * @return Mat4: matrix with projection values */
24 | Mat4 perspective(float aspect, float fov, float zNear, float zFar);
25 |
26 | /* Creates a Mat4 with the rotation matrix along the Z axis
27 | * @param degrees: the number of degrees to rotate
28 | * @return Mat4: rotation matrix */
29 | Mat4 rotZ(float degrees);
30 |
31 | /* Creates a Mat4 with the rotation matrix along the Y axis
32 | * @param degrees: the number of degrees to rotate
33 | * @return Mat4: rotation matrix */
34 | Mat4 rotY(float degrees);
35 |
36 | /* Creates a Mat4 with the rotation matrix along the X axis
37 | * @param degrees: the number of degrees to rotate
38 | * @return Mat4: rotation matrix */
39 | Mat4 rotX(float degrees);
40 |
41 | /* Multiply a vertex by a 4x4 matrix - performs division by w
42 | * @param v: the vertex
43 | * @param m: the 4x4 matrix
44 | * @return Vert: product of multiplcation */
45 | Vert mult4(Vert v, Mat4& m);
46 |
47 | /* Calculate the dot product between two vertices
48 | * @params a,b: vertices
49 | * @return float: result */
50 | float dot(Vert a, Vert b);
51 |
52 | /* Calculate the direction vector a-b
53 | * @params a,b: vertices
54 | * @return Vert: direction vector result */
55 | Vert direc(Vert a, Vert b);
56 |
57 | /* Calculate the cross product of two vectors
58 | * @params a,b: vertices
59 | * @return Vert: cross product result */
60 | Vert cross(Vert a, Vert b);
61 |
62 | /* Calculate the Z value from x,y using cross and vert of equation of plane
63 | * @params x,y: the input x and y values
64 | * @param c: cross product vertex from equation of plane
65 | * @param v: point from our original triangle from equation of plane
66 | * @return float: the z value */
67 | float calcZ(float x, float y, Vert c, Vert v);
68 |
69 | /* Project a triangle with a perspective matrix
70 | * @param t: the triangle
71 | * @param m: the perspective matrix */
72 | void project(Trig& t, Mat4 m);
73 |
74 | /* Calculate the bary coordinates given 3 points of a triangle and x and y
75 | * inside of the triangle
76 | * @params p1,p2,p3: vertices of the triangle
77 | * @params x,y: current point within the triangle
78 | * @params w1,w2,w3: store the bary outputs */
79 | void calcBary(Vert p1, Vert p2, Vert p3, int x, int y, float& w1, float& w2,
80 | float& w3);
81 |
82 |
--------------------------------------------------------------------------------