TinyGPU
Loading...
Searching...
No Matches
WireFrame3D.h
Go to the documentation of this file.
1#pragma once
2
3#include <stddef.h>
4#include <stdint.h>
5
6#include <algorithm>
7#include <cmath>
8#include <limits>
9
10#include "ISurface.h"
11#include "TinyGPUConfig.h"
12#include "TinyGPU/Vector.h"
13
15#include "esp_dsp.h"
16#endif
17
18namespace tinygpu {
19
20/**
21 * @brief Lightweight 3D wireframe renderer for TinyGPU drawing targets.
22 *
23 * WireFrame3D implements a compact CPU-side 3D pipeline intended for embedded
24 * and Arduino-style environments where a full 3D engine would be excessive.
25 * It stores no scene graph of its own; instead, it provides the math and
26 * rendering steps needed to transform user-supplied mesh data into 2D line
27 * output on any ISurface-compatible surface.
28 *
29 * The class covers the main stages of a simple wireframe renderer:
30 * - 3D vector and 4x4 matrix operations
31 * - model transforms such as translation, scaling, and rotation
32 * - camera setup via a look-at style view transform
33 * - perspective or orthographic projection into screen space
34 * - minimal per-pixel depth handling for overlapping wireframe edges
35 *
36 * Meshes are represented as vertices plus edge lists, which keeps memory usage
37 * predictable and makes the class suitable for simple geometry such as cubes,
38 * spheres, axes, grids, and custom debug or UI visualizations.
39 */
40template <typename RGB_T = RGB565>
42 public:
43 /// Represents a 3D point or vector.
44 struct Vec3 {
45 float x = 0.0f;
46 float y = 0.0f;
47 float z = 0.0f;
48
49 Vec3 operator+(const Vec3& other) const {
50 return {x + other.x, y + other.y, z + other.z};
51 }
52
53 Vec3 operator-(const Vec3& other) const {
54 return {x - other.x, y - other.y, z - other.z};
55 }
56
57 Vec3 operator*(float scalar) const {
58 return {x * scalar, y * scalar, z * scalar};
59 }
60
61 Vec3 operator/(float scalar) const {
62 return {x / scalar, y / scalar, z / scalar};
63 }
64 };
65
66 /// Represents a homogeneous 4D vector.
67 struct Vec4 {
68 float x = 0.0f;
69 float y = 0.0f;
70 float z = 0.0f;
71 float w = 1.0f;
72 };
73
74 /// Represents a 4x4 transformation matrix.
75 struct Mat4 {
76 float m[4][4] = {};
77
78 /// Returns an identity matrix.
79 static Mat4 identity() {
80 Mat4 result;
81 result.m[0][0] = 1.0f;
82 result.m[1][1] = 1.0f;
83 result.m[2][2] = 1.0f;
84 result.m[3][3] = 1.0f;
85 return result;
86 }
87
88 /// Multiplies two matrices.
89 Mat4 operator*(const Mat4& other) const {
91 return multiplyEspDsp(*this, other);
92#else
93 Mat4 result;
94 for (size_t row = 0; row < 4; ++row) {
95 for (size_t column = 0; column < 4; ++column) {
96 for (size_t index = 0; index < 4; ++index) {
97 result.m[row][column] += m[row][index] * other.m[index][column];
98 }
99 }
100 }
101 return result;
102#endif
103 }
104
105 /// Transforms a homogeneous vector.
106 Vec4 operator*(const Vec4& vector) const {
108 return transformEspDsp(*this, vector);
109#else
110 return {(m[0][0] * vector.x) + (m[0][1] * vector.y) +
111 (m[0][2] * vector.z) + (m[0][3] * vector.w),
112 (m[1][0] * vector.x) + (m[1][1] * vector.y) +
113 (m[1][2] * vector.z) + (m[1][3] * vector.w),
114 (m[2][0] * vector.x) + (m[2][1] * vector.y) +
115 (m[2][2] * vector.z) + (m[2][3] * vector.w),
116 (m[3][0] * vector.x) + (m[3][1] * vector.y) +
117 (m[3][2] * vector.z) + (m[3][3] * vector.w)};
118#endif
119 }
120
121 private:
123 static Mat4 multiplyEspDsp(const Mat4& left, const Mat4& right) {
124 Mat4 result;
125 const esp_err_t status =
126 dspm_mult_f32(reinterpret_cast<const float*>(left.m),
127 reinterpret_cast<const float*>(right.m),
128 reinterpret_cast<float*>(result.m), 4, 4, 4);
129 if (status == ESP_OK) {
130 return result;
131 }
132 return multiplyGeneric(left, right);
133 }
134
135 static Vec4 transformEspDsp(const Mat4& matrix, const Vec4& vector) {
136 const float input[4] = {vector.x, vector.y, vector.z, vector.w};
137 float output[4] = {};
138
140 reinterpret_cast<const float*>(matrix.m), input, output, 4, 4, 1);
141 if (status == ESP_OK) {
142 return {output[0], output[1], output[2], output[3]};
143 }
145 }
146#endif
147
148 static Mat4 multiplyGeneric(const Mat4& left, const Mat4& right) {
149 Mat4 result;
150 for (size_t row = 0; row < 4; ++row) {
151 for (size_t column = 0; column < 4; ++column) {
152 for (size_t index = 0; index < 4; ++index) {
153 result.m[row][column] +=
154 left.m[row][index] * right.m[index][column];
155 }
156 }
157 }
158 return result;
159 }
160
161 static Vec4 transformGeneric(const Mat4& matrix, const Vec4& vector) {
162 return {(matrix.m[0][0] * vector.x) + (matrix.m[0][1] * vector.y) +
163 (matrix.m[0][2] * vector.z) + (matrix.m[0][3] * vector.w),
164 (matrix.m[1][0] * vector.x) + (matrix.m[1][1] * vector.y) +
165 (matrix.m[1][2] * vector.z) + (matrix.m[1][3] * vector.w),
166 (matrix.m[2][0] * vector.x) + (matrix.m[2][1] * vector.y) +
167 (matrix.m[2][2] * vector.z) + (matrix.m[2][3] * vector.w),
168 (matrix.m[3][0] * vector.x) + (matrix.m[3][1] * vector.y) +
169 (matrix.m[3][2] * vector.z) + (matrix.m[3][3] * vector.w)};
170 }
171 };
172
173 /// Represents an edge between two mesh vertices.
174 struct Edge {
177 };
178
179 /// Represents a wireframe mesh.
180 struct Mesh {
183 };
184
185 /// Stores camera parameters for view transformation.
186 struct Camera {
187 Vec3 position{0.0f, 0.0f, 5.0f};
188 Vec3 target{0.0f, 0.0f, 0.0f};
189 Vec3 up{0.0f, 1.0f, 0.0f};
190 float nearPlane = 0.1f;
191 float farPlane = 100.0f;
192 };
193
194 /// Creates an empty wireframe renderer.
198 }
199
200 /// Creates a wireframe renderer with the specified viewport size.
201 WireFrame3D(size_t viewportWidth, size_t viewportHeight)
205 }
206
207 /// Creates a wireframe renderer with the dimensions of the given surface.
208 WireFrame3D(ISurface<RGB_T>& surface)
212 }
213
214 /// Sets the viewport size used for projection and depth buffering.
215 void setViewport(size_t viewportWidth, size_t viewportHeight) {
216 viewportWidth_ = viewportWidth;
217 viewportHeight_ = viewportHeight;
219 }
220
221 bool begin() {
223 return true;
224 }
225
226 /// Returns the current viewport width.
227 size_t viewportWidth() const { return viewportWidth_; }
228
229 /// Returns the current viewport height.
230 size_t viewportHeight() const { return viewportHeight_; }
231
232 /// Sets the active camera.
233 void setCamera(const Camera& camera) {
234 camera_ = camera;
237 }
238
239 /// Returns the active camera.
240 const Camera& camera() const { return camera_; }
241
242 /// Sets a perspective projection using the current viewport aspect ratio.
243 void setPerspective(float fovYDegrees, float nearPlane = 0.1f,
244 float farPlane = 100.0f) {
245 projectionMode_ = ProjectionMode::Perspective;
246 perspectiveFovYDegrees_ = fovYDegrees;
247 camera_.nearPlane = nearPlane;
248 camera_.farPlane = farPlane;
250 }
251
252 /// Sets an orthographic projection volume.
253 void setOrthographic(float left, float right, float bottom, float top,
254 float nearPlane = 0.1f, float farPlane = 100.0f) {
255 projectionMode_ = ProjectionMode::Orthographic;
256 orthographicLeft_ = left;
257 orthographicRight_ = right;
258 orthographicBottom_ = bottom;
259 orthographicTop_ = top;
260 camera_.nearPlane = nearPlane;
261 camera_.farPlane = farPlane;
263 }
264
265 /// Returns the current view matrix.
266 const Mat4& viewMatrix() const { return viewMatrix_; }
267
268 /// Returns the current projection matrix.
269 const Mat4& projectionMatrix() const { return projectionMatrix_; }
270
271 /// Clears the internal depth buffer.
273 std::fill(depthBuffer_.begin(), depthBuffer_.end(),
274 std::numeric_limits<float>::infinity());
275 }
276
277 /// Transforms a point by a matrix and divides by w when possible.
278 static Vec3 transformPoint(const Mat4& matrix, const Vec3& point) {
279 const Vec4 transformed = matrix * Vec4{point.x, point.y, point.z, 1.0f};
280 if (transformed.w != 0.0f) {
281 return {transformed.x / transformed.w, transformed.y / transformed.w,
282 transformed.z / transformed.w};
283 }
284 return {transformed.x, transformed.y, transformed.z};
285 }
286
287 /// Creates a translation matrix.
288 static Mat4 translation(float x, float y, float z) {
289 Mat4 result = Mat4::identity();
290 result.m[0][3] = x;
291 result.m[1][3] = y;
292 result.m[2][3] = z;
293 return result;
294 }
295
296 /// Creates a scale matrix.
297 static Mat4 scaling(float x, float y, float z) {
298 Mat4 result = Mat4::identity();
299 result.m[0][0] = x;
300 result.m[1][1] = y;
301 result.m[2][2] = z;
302 return result;
303 }
304
305 /// Creates a rotation matrix around the X axis.
306 static Mat4 rotationX(float radians) {
307 Mat4 result = Mat4::identity();
308 const float cosine = std::cos(radians);
309 const float sine = std::sin(radians);
310 result.m[1][1] = cosine;
311 result.m[1][2] = -sine;
312 result.m[2][1] = sine;
313 result.m[2][2] = cosine;
314 return result;
315 }
316
317 /// Creates a rotation matrix around the Y axis.
318 static Mat4 rotationY(float radians) {
319 Mat4 result = Mat4::identity();
320 const float cosine = std::cos(radians);
321 const float sine = std::sin(radians);
322 result.m[0][0] = cosine;
323 result.m[0][2] = sine;
324 result.m[2][0] = -sine;
325 result.m[2][2] = cosine;
326 return result;
327 }
328
329 /// Creates a rotation matrix around the Z axis.
330 static Mat4 rotationZ(float radians) {
331 Mat4 result = Mat4::identity();
332 const float cosine = std::cos(radians);
333 const float sine = std::sin(radians);
334 result.m[0][0] = cosine;
335 result.m[0][1] = -sine;
336 result.m[1][0] = sine;
337 result.m[1][1] = cosine;
338 return result;
339 }
340
341 /// Creates a wireframe cube centered at the origin.
342 static Mesh cube(float size = 1.0f) {
343 const float halfSize = size * 0.5f;
344 return {{{-halfSize, -halfSize, -halfSize},
345 {halfSize, -halfSize, -halfSize},
346 {halfSize, halfSize, -halfSize},
347 {-halfSize, halfSize, -halfSize},
348 {-halfSize, -halfSize, halfSize},
349 {halfSize, -halfSize, halfSize},
350 {halfSize, halfSize, halfSize},
351 {-halfSize, halfSize, halfSize}},
352 {{0, 1},
353 {1, 2},
354 {2, 3},
355 {3, 0},
356 {4, 5},
357 {5, 6},
358 {6, 7},
359 {7, 4},
360 {0, 4},
361 {1, 5},
362 {2, 6},
363 {3, 7}}};
364 }
365
366 /// Creates RGB-style axis lines centered at the origin.
367 static Mesh axis(float length = 1.0f) {
368 return {{{0.0f, 0.0f, 0.0f},
369 {length, 0.0f, 0.0f},
370 {0.0f, length, 0.0f},
371 {0.0f, 0.0f, length}},
372 {{0, 1}, {0, 2}, {0, 3}}};
373 }
374
375 /// Creates a wireframe grid on the XZ plane.
376 static Mesh grid(size_t subdivisions = 10, float spacing = 1.0f) {
377 Mesh mesh;
378 const float halfExtent =
379 (static_cast<float>(subdivisions) * spacing) * 0.5f;
380
381 for (size_t index = 0; index <= subdivisions; ++index) {
382 const float offset = (static_cast<float>(index) * spacing) - halfExtent;
383
384 const size_t lineStart = mesh.vertices.size();
385 mesh.vertices.push_back({-halfExtent, 0.0f, offset});
386 mesh.vertices.push_back({halfExtent, 0.0f, offset});
387 mesh.edges.push_back({lineStart, lineStart + 1});
388
389 const size_t columnStart = mesh.vertices.size();
390 mesh.vertices.push_back({offset, 0.0f, -halfExtent});
391 mesh.vertices.push_back({offset, 0.0f, halfExtent});
392 mesh.edges.push_back({columnStart, columnStart + 1});
393 }
394
395 return mesh;
396 }
397
398 /// Creates a wireframe sphere centered at the origin.
399 static Mesh sphere(float radius = 1.0f, size_t latitudeSteps = 8,
400 size_t longitudeSteps = 12) {
401 Mesh mesh;
402 const size_t latCount = std::max<size_t>(2, latitudeSteps);
403 const size_t lonCount = std::max<size_t>(3, longitudeSteps);
404 const float pi = 3.14159265358979323846f;
405
406 mesh.vertices.reserve((latCount + 1) * (lonCount + 1));
407 mesh.edges.reserve((latCount * lonCount) + ((latCount + 1) * lonCount));
408
409 for (size_t latitude = 0; latitude <= latCount; ++latitude) {
410 const float v =
411 static_cast<float>(latitude) / static_cast<float>(latCount);
412 const float phi = v * pi;
413 const float ringY = radius * std::cos(phi);
414 const float ringRadius = radius * std::sin(phi);
415
416 for (size_t longitude = 0; longitude <= lonCount; ++longitude) {
417 const float u =
418 static_cast<float>(longitude) / static_cast<float>(lonCount);
419 const float theta = u * (2.0f * pi);
420 mesh.vertices.push_back({ringRadius * std::cos(theta), ringY,
421 ringRadius * std::sin(theta)});
422 }
423 }
424
425 const size_t rowSize = lonCount + 1;
426 for (size_t latitude = 0; latitude <= latCount; ++latitude) {
427 for (size_t longitude = 0; longitude < lonCount; ++longitude) {
428 const size_t current = (latitude * rowSize) + longitude;
429 mesh.edges.push_back({current, current + 1});
430 }
431 }
432
433 for (size_t latitude = 0; latitude < latCount; ++latitude) {
434 for (size_t longitude = 0; longitude <= lonCount; ++longitude) {
435 const size_t current = (latitude * rowSize) + longitude;
436 mesh.edges.push_back({current, current + rowSize});
437 }
438 }
439
440 return mesh;
441 }
442
443 /// Creates a view matrix from camera parameters.
444 static Mat4 lookAt(const Vec3& eye, const Vec3& target, const Vec3& up) {
445 const Vec3 forward = normalize(target - eye);
446 const Vec3 right = normalize(cross(forward, up));
447 const Vec3 actualUp = cross(right, forward);
448
449 Mat4 result = Mat4::identity();
450 result.m[0][0] = right.x;
451 result.m[0][1] = right.y;
452 result.m[0][2] = right.z;
453 result.m[0][3] = -dot(right, eye);
454
455 result.m[1][0] = actualUp.x;
456 result.m[1][1] = actualUp.y;
457 result.m[1][2] = actualUp.z;
458 result.m[1][3] = -dot(actualUp, eye);
459
460 result.m[2][0] = -forward.x;
461 result.m[2][1] = -forward.y;
462 result.m[2][2] = -forward.z;
463 result.m[2][3] = dot(forward, eye);
464 return result;
465 }
466
467 /// Projects a world-space point into screen space.
468 bool projectPoint(const Vec3& point, int16_t& screenX, int16_t& screenY,
469 float& depth, const Mat4& model = Mat4::identity()) const {
470 ProjectedPoint projected;
471 Vec3 viewPoint = transformAffine(viewMatrix_ * model, point);
472 if (!clipPointToCameraRange(viewPoint)) {
473 return false;
474 }
475 if (!projectViewPoint(viewPoint, projected)) {
476 return false;
477 }
478
479 screenX = projected.x;
480 screenY = projected.y;
481 depth = projected.depth;
482 return true;
483 }
484
485 /// Renders a wireframe mesh with minimal depth buffering.
486 void renderWireframe(ISurface<RGB_T>& target, const Mesh& mesh,
487 const Mat4& model = Mat4::identity(),
488 RGB_T color = RGB_T(255, 255, 255),
489 bool clearDepth = true) {
490 if (viewportWidth_ != target.width() ||
491 viewportHeight_ != target.height()) {
492 setViewport(target.width(), target.height());
493 }
494 if (clearDepth) {
496 }
497
498 const Mat4 modelView = viewMatrix_ * model;
499 for (const Edge& edge : mesh.edges) {
500 if (edge.start >= mesh.vertices.size() ||
501 edge.end >= mesh.vertices.size()) {
502 continue;
503 }
504
505 Vec3 start = transformAffine(modelView, mesh.vertices[edge.start]);
506 Vec3 end = transformAffine(modelView, mesh.vertices[edge.end]);
507 if (!clipLineToCameraRange(start, end)) {
508 continue;
509 }
510
511 ProjectedPoint projectedStart;
512 ProjectedPoint projectedEnd;
513 if (!projectViewPoint(start, projectedStart) ||
514 !projectViewPoint(end, projectedEnd)) {
515 continue;
516 }
517
518 drawDepthLine(target, projectedStart, projectedEnd, color);
519 }
520 }
521
522 protected:
523 /// Represents a projected vertex on screen.
525 int16_t x = 0;
526 int16_t y = 0;
527 float depth = 0.0f;
528 };
529
531
535 Mat4 viewMatrix_ = Mat4::identity();
539 float orthographicLeft_ = -1.0f;
540 float orthographicRight_ = 1.0f;
541 float orthographicBottom_ = -1.0f;
542 float orthographicTop_ = 1.0f;
544
545 static float dot(const Vec3& first, const Vec3& second) {
547 return dotEspDsp(first, second);
548#else
549 return (first.x * second.x) + (first.y * second.y) + (first.z * second.z);
550#endif
551 }
552
553 static Vec3 cross(const Vec3& first, const Vec3& second) {
554 return {(first.y * second.z) - (first.z * second.y),
555 (first.z * second.x) - (first.x * second.z),
556 (first.x * second.y) - (first.y * second.x)};
557 }
558
560 static float dotEspDsp(const Vec3& first, const Vec3& second) {
561 float result = 0.0f;
563 if (status == ESP_OK) {
564 return result;
565 }
566 return (first.x * second.x) + (first.y * second.y) + (first.z * second.z);
567 }
568#endif
569
570 static float length(const Vec3& vector) {
571 return std::sqrt(dot(vector, vector));
572 }
573
574 static Vec3 normalize(const Vec3& vector) {
575 const float vectorLength = length(vector);
576 if (vectorLength == 0.0f) {
577 return {0.0f, 0.0f, 0.0f};
578 }
579 return vector / vectorLength;
580 }
581
582 static Vec3 transformAffine(const Mat4& matrix, const Vec3& point) {
583 const Vec4 transformed = matrix * Vec4{point.x, point.y, point.z, 1.0f};
584 return {transformed.x, transformed.y, transformed.z};
585 }
586
588 viewMatrix_ = lookAt(camera_.position, camera_.target, camera_.up);
589 }
590
592 if (projectionMode_ == ProjectionMode::Perspective) {
595 camera_.nearPlane, camera_.farPlane);
596 return;
597 }
598
601 orthographicTop_, camera_.nearPlane, camera_.farPlane);
602 }
603
604 float currentAspectRatio() const {
605 if (viewportWidth_ == 0 || viewportHeight_ == 0) {
606 return 1.0f;
607 }
608 return static_cast<float>(viewportWidth_) /
609 static_cast<float>(viewportHeight_);
610 }
611
613 size_t bytes = viewportWidth_ * viewportHeight_;
614 depthBuffer_.resize(bytes);
616 return depthBuffer_.size() == bytes;
617 }
618
619 static Mat4 createPerspectiveMatrix(float fovYDegrees, float aspect,
620 float nearPlane, float farPlane) {
621 Mat4 result;
622 const float halfFovRadians =
623 (fovYDegrees * 3.14159265358979323846f / 180.0f) * 0.5f;
624 const float focalLength = 1.0f / std::tan(halfFovRadians);
625
626 result.m[0][0] = focalLength / aspect;
627 result.m[1][1] = focalLength;
628 result.m[2][2] = (farPlane + nearPlane) / (nearPlane - farPlane);
629 result.m[2][3] = (2.0f * farPlane * nearPlane) / (nearPlane - farPlane);
630 result.m[3][2] = -1.0f;
631 return result;
632 }
633
634 static Mat4 createOrthographicMatrix(float left, float right, float bottom,
635 float top, float nearPlane,
636 float farPlane) {
637 Mat4 result = Mat4::identity();
638 result.m[0][0] = 2.0f / (right - left);
639 result.m[1][1] = 2.0f / (top - bottom);
640 result.m[2][2] = -2.0f / (farPlane - nearPlane);
641 result.m[0][3] = -(right + left) / (right - left);
642 result.m[1][3] = -(top + bottom) / (top - bottom);
643 result.m[2][3] = -(farPlane + nearPlane) / (farPlane - nearPlane);
644 return result;
645 }
646
647 bool clipPointToCameraRange(const Vec3& point) const {
648 return point.z <= -camera_.nearPlane && point.z >= -camera_.farPlane;
649 }
650
651 bool clipLineToCameraRange(Vec3& start, Vec3& end) const {
652 if (!clipLineToPlane(start, end, -camera_.nearPlane, true)) {
653 return false;
654 }
655 return clipLineToPlane(start, end, -camera_.farPlane, false);
656 }
657
658 static bool clipLineToPlane(Vec3& start, Vec3& end, float planeZ,
659 bool keepLessEqual) {
660 const bool startInside =
661 keepLessEqual ? start.z <= planeZ : start.z >= planeZ;
662 const bool endInside = keepLessEqual ? end.z <= planeZ : end.z >= planeZ;
663
664 if (!startInside && !endInside) {
665 return false;
666 }
667 if (startInside && endInside) {
668 return true;
669 }
670
671 const float deltaZ = end.z - start.z;
672 if (deltaZ == 0.0f) {
673 return false;
674 }
675
676 const float t = (planeZ - start.z) / deltaZ;
677 const Vec3 intersection = start + ((end - start) * t);
678 if (!startInside) {
679 start = intersection;
680 } else {
681 end = intersection;
682 }
683 return true;
684 }
685
686 bool projectViewPoint(const Vec3& viewPoint,
687 ProjectedPoint& projected) const {
688 const Vec4 clip =
689 projectionMatrix_ * Vec4{viewPoint.x, viewPoint.y, viewPoint.z, 1.0f};
690 if (clip.w == 0.0f) {
691 return false;
692 }
693
694 const float inverseW = 1.0f / clip.w;
695 const float ndcX = clip.x * inverseW;
696 const float ndcY = clip.y * inverseW;
697 const float ndcZ = clip.z * inverseW;
698 const float screenX =
699 ((ndcX * 0.5f) + 0.5f) *
700 static_cast<float>(viewportWidth_ > 0 ? viewportWidth_ - 1 : 0);
701 const float screenY =
702 (1.0f - ((ndcY * 0.5f) + 0.5f)) *
703 static_cast<float>(viewportHeight_ > 0 ? viewportHeight_ - 1 : 0);
704
705 projected.x = static_cast<int16_t>(std::lround(screenX));
706 projected.y = static_cast<int16_t>(std::lround(screenY));
707 projected.depth = (ndcZ + 1.0f) * 0.5f;
708 return true;
709 }
710
711 void drawDepthLine(ISurface<RGB_T>& target, const ProjectedPoint& start,
712 const ProjectedPoint& end, RGB_T color) {
713 const int deltaX = static_cast<int>(end.x) - static_cast<int>(start.x);
714 const int deltaY = static_cast<int>(end.y) - static_cast<int>(start.y);
715 const int steps = std::max(std::abs(deltaX), std::abs(deltaY));
716
717 if (steps == 0) {
718 plotDepthPixel(target, start.x, start.y, start.depth, color);
719 return;
720 }
721
722 for (int step = 0; step <= steps; ++step) {
723 const float factor = static_cast<float>(step) / static_cast<float>(steps);
724 const int x = static_cast<int>(std::lround(
725 static_cast<float>(start.x) + (static_cast<float>(deltaX) * factor)));
726 const int y = static_cast<int>(std::lround(
727 static_cast<float>(start.y) + (static_cast<float>(deltaY) * factor)));
728 const float depth = start.depth + ((end.depth - start.depth) * factor);
729 plotDepthPixel(target, x, y, depth, color);
730 }
731 }
732
733 void plotDepthPixel(ISurface<RGB_T>& target, int x, int y, float depth,
734 RGB_T color) {
735 if (x < 0 || y < 0 || static_cast<size_t>(x) >= viewportWidth_ ||
736 static_cast<size_t>(y) >= viewportHeight_) {
737 return;
738 }
739
740 const size_t bufferIndex =
741 (static_cast<size_t>(y) * viewportWidth_) + static_cast<size_t>(x);
742 if (bufferIndex >= depthBuffer_.size()) {
743 return;
744 }
745 if (depth >= depthBuffer_[bufferIndex]) {
746 return;
747 }
748
749 depthBuffer_[bufferIndex] = depth;
750 target.setPixel(static_cast<size_t>(x), static_cast<size_t>(y), color);
751 }
752};
753
754} // namespace tinygpu
#define TINYGPU_ENABLE_ESP32S3_OPTIMIZATIONS
Activate ESP32-S3-specific optimizations.
Definition: TinyGPUConfig.h:11
RGB color stored in 16-bit RGB565 format.
Definition: RGB565.h:13
Lightweight 3D wireframe renderer for TinyGPU drawing targets.
Definition: WireFrame3D.h:41
static float length(const Vec3 &vector)
Definition: WireFrame3D.h:570
size_t viewportWidth() const
Returns the current viewport width.
Definition: WireFrame3D.h:227
static Mat4 lookAt(const Vec3 &eye, const Vec3 &target, const Vec3 &up)
Creates a view matrix from camera parameters.
Definition: WireFrame3D.h:444
bool projectViewPoint(const Vec3 &viewPoint, ProjectedPoint &projected) const
Definition: WireFrame3D.h:686
void renderWireframe(ISurface< RGB_T > &target, const Mesh &mesh, const Mat4 &model=Mat4::identity(), RGB_T color=RGB_T(255, 255, 255), bool clearDepth=true)
Renders a wireframe mesh with minimal depth buffering.
Definition: WireFrame3D.h:486
bool clipLineToCameraRange(Vec3 &start, Vec3 &end) const
Definition: WireFrame3D.h:651
void setViewport(size_t viewportWidth, size_t viewportHeight)
Sets the viewport size used for projection and depth buffering.
Definition: WireFrame3D.h:215
static Mesh sphere(float radius=1.0f, size_t latitudeSteps=8, size_t longitudeSteps=12)
Creates a wireframe sphere centered at the origin.
Definition: WireFrame3D.h:399
static float dot(const Vec3 &first, const Vec3 &second)
Definition: WireFrame3D.h:545
ProjectionMode projectionMode_
Definition: WireFrame3D.h:537
static Vec3 transformAffine(const Mat4 &matrix, const Vec3 &point)
Definition: WireFrame3D.h:582
const Camera & camera() const
Returns the active camera.
Definition: WireFrame3D.h:240
bool projectPoint(const Vec3 &point, int16_t &screenX, int16_t &screenY, float &depth, const Mat4 &model=Mat4::identity()) const
Projects a world-space point into screen space.
Definition: WireFrame3D.h:468
static Mat4 createPerspectiveMatrix(float fovYDegrees, float aspect, float nearPlane, float farPlane)
Definition: WireFrame3D.h:619
float orthographicLeft_
Definition: WireFrame3D.h:539
void clearDepthBuffer()
Clears the internal depth buffer.
Definition: WireFrame3D.h:272
Camera camera_
Definition: WireFrame3D.h:534
WireFrame3D()
Creates an empty wireframe renderer.
Definition: WireFrame3D.h:195
void drawDepthLine(ISurface< RGB_T > &target, const ProjectedPoint &start, const ProjectedPoint &end, RGB_T color)
Definition: WireFrame3D.h:711
static Mat4 rotationX(float radians)
Creates a rotation matrix around the X axis.
Definition: WireFrame3D.h:306
static Mat4 translation(float x, float y, float z)
Creates a translation matrix.
Definition: WireFrame3D.h:288
WireFrame3D(ISurface< RGB_T > &surface)
Creates a wireframe renderer with the dimensions of the given surface.
Definition: WireFrame3D.h:208
WireFrame3D(size_t viewportWidth, size_t viewportHeight)
Creates a wireframe renderer with the specified viewport size.
Definition: WireFrame3D.h:201
bool begin()
Definition: WireFrame3D.h:221
ProjectionMode
Definition: WireFrame3D.h:530
static Vec3 cross(const Vec3 &first, const Vec3 &second)
Definition: WireFrame3D.h:553
static Mat4 rotationY(float radians)
Creates a rotation matrix around the Y axis.
Definition: WireFrame3D.h:318
void setCamera(const Camera &camera)
Sets the active camera.
Definition: WireFrame3D.h:233
static Mesh cube(float size=1.0f)
Creates a wireframe cube centered at the origin.
Definition: WireFrame3D.h:342
const Mat4 & viewMatrix() const
Returns the current view matrix.
Definition: WireFrame3D.h:266
size_t viewportWidth_
Definition: WireFrame3D.h:532
Vector< float > depthBuffer_
Definition: WireFrame3D.h:543
float orthographicRight_
Definition: WireFrame3D.h:540
Mat4 projectionMatrix_
Definition: WireFrame3D.h:536
static Mesh grid(size_t subdivisions=10, float spacing=1.0f)
Creates a wireframe grid on the XZ plane.
Definition: WireFrame3D.h:376
static bool clipLineToPlane(Vec3 &start, Vec3 &end, float planeZ, bool keepLessEqual)
Definition: WireFrame3D.h:658
size_t viewportHeight_
Definition: WireFrame3D.h:533
size_t viewportHeight() const
Returns the current viewport height.
Definition: WireFrame3D.h:230
float orthographicTop_
Definition: WireFrame3D.h:542
void updateViewMatrix()
Definition: WireFrame3D.h:587
static Mat4 createOrthographicMatrix(float left, float right, float bottom, float top, float nearPlane, float farPlane)
Definition: WireFrame3D.h:634
float perspectiveFovYDegrees_
Definition: WireFrame3D.h:538
void setPerspective(float fovYDegrees, float nearPlane=0.1f, float farPlane=100.0f)
Sets a perspective projection using the current viewport aspect ratio.
Definition: WireFrame3D.h:243
Mat4 viewMatrix_
Definition: WireFrame3D.h:535
float currentAspectRatio() const
Definition: WireFrame3D.h:604
static Mesh axis(float length=1.0f)
Creates RGB-style axis lines centered at the origin.
Definition: WireFrame3D.h:367
bool resizeDepthBuffer()
Definition: WireFrame3D.h:612
static Vec3 normalize(const Vec3 &vector)
Definition: WireFrame3D.h:574
void updateProjectionMatrix()
Definition: WireFrame3D.h:591
const Mat4 & projectionMatrix() const
Returns the current projection matrix.
Definition: WireFrame3D.h:269
void setOrthographic(float left, float right, float bottom, float top, float nearPlane=0.1f, float farPlane=100.0f)
Sets an orthographic projection volume.
Definition: WireFrame3D.h:253
float orthographicBottom_
Definition: WireFrame3D.h:541
static Vec3 transformPoint(const Mat4 &matrix, const Vec3 &point)
Transforms a point by a matrix and divides by w when possible.
Definition: WireFrame3D.h:278
static Mat4 rotationZ(float radians)
Creates a rotation matrix around the Z axis.
Definition: WireFrame3D.h:330
bool clipPointToCameraRange(const Vec3 &point) const
Definition: WireFrame3D.h:647
static Mat4 scaling(float x, float y, float z)
Creates a scale matrix.
Definition: WireFrame3D.h:297
void plotDepthPixel(ISurface< RGB_T > &target, int x, int y, float depth, RGB_T color)
Definition: WireFrame3D.h:733
Definition: AVIWriter.h:9
Stores camera parameters for view transformation.
Definition: WireFrame3D.h:186
Vec3 target
Definition: WireFrame3D.h:188
float farPlane
Definition: WireFrame3D.h:191
float nearPlane
Definition: WireFrame3D.h:190
Vec3 position
Definition: WireFrame3D.h:187
Vec3 up
Definition: WireFrame3D.h:189
Represents an edge between two mesh vertices.
Definition: WireFrame3D.h:174
size_t start
Definition: WireFrame3D.h:175
size_t end
Definition: WireFrame3D.h:176
Represents a 4x4 transformation matrix.
Definition: WireFrame3D.h:75
static Mat4 identity()
Returns an identity matrix.
Definition: WireFrame3D.h:79
float m[4][4]
Definition: WireFrame3D.h:76
Mat4 operator*(const Mat4 &other) const
Multiplies two matrices.
Definition: WireFrame3D.h:89
Vec4 operator*(const Vec4 &vector) const
Transforms a homogeneous vector.
Definition: WireFrame3D.h:106
Represents a wireframe mesh.
Definition: WireFrame3D.h:180
Vector< Edge > edges
Definition: WireFrame3D.h:182
Vector< Vec3 > vertices
Definition: WireFrame3D.h:181
Represents a projected vertex on screen.
Definition: WireFrame3D.h:524
float depth
Definition: WireFrame3D.h:527
int16_t x
Definition: WireFrame3D.h:525
int16_t y
Definition: WireFrame3D.h:526
Represents a 3D point or vector.
Definition: WireFrame3D.h:44
Vec3 operator*(float scalar) const
Definition: WireFrame3D.h:57
Vec3 operator/(float scalar) const
Definition: WireFrame3D.h:61
Vec3 operator+(const Vec3 &other) const
Definition: WireFrame3D.h:49
float y
Definition: WireFrame3D.h:46
Vec3 operator-(const Vec3 &other) const
Definition: WireFrame3D.h:53
float x
Definition: WireFrame3D.h:45
float z
Definition: WireFrame3D.h:47
Represents a homogeneous 4D vector.
Definition: WireFrame3D.h:67
float w
Definition: WireFrame3D.h:71
float y
Definition: WireFrame3D.h:69
float x
Definition: WireFrame3D.h:68
float z
Definition: WireFrame3D.h:70