-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathsmall3dlib.h
More file actions
3007 lines (2459 loc) · 86.5 KB
/
small3dlib.h
File metadata and controls
3007 lines (2459 loc) · 86.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#ifndef SMALL3DLIB_H
#define SMALL3DLIB_H
/*
Simple realtime 3D software rasterization renderer. It is fast, focused on
resource-limited computers, located in a single C header file, with no
dependencies, using only 32bit integer arithmetics.
author: Miloslav Ciz
license: CC0 1.0 (public domain)
found at https://creativecommons.org/publicdomain/zero/1.0/
+ additional waiver of all IP
version: 0.902d
Before including the library, define S3L_PIXEL_FUNCTION to the name of the
function you'll be using to draw single pixels (this function will be called
by the library to render the frames). Also either init S3L_resolutionX and
S3L_resolutionY or define S3L_RESOLUTION_X and S3L_RESOLUTION_Y.
You'll also need to decide what rendering strategy and other settings you
want to use, depending on your specific usecase. You may want to use a
z-buffer (full or reduced, S3L_Z_BUFFER), sorted-drawing (S3L_SORT), or even
none of these. See the description of the options in this file.
The rendering itself is done with S3L_drawScene, usually preceded by
S3L_newFrame (for clearing zBuffer etc.).
The library is meant to be used in not so huge programs that use single
translation unit and so includes both declarations and implementation at once.
If you for some reason use multiple translation units (which include the
library), you'll have to handle this yourself (e.g. create a wrapper, manually
split the library into .c and .h etc.).
--------------------
This work's goal is to never be encumbered by any exclusive intellectual
property rights. The work is therefore provided under CC0 1.0 + additional
WAIVER OF ALL INTELLECTUAL PROPERTY RIGHTS that waives the rest of
intellectual property rights not already waived by CC0 1.0. The WAIVER OF ALL
INTELLECTUAL PROPERTY RGHTS is as follows:
Each contributor to this work agrees that they waive any exclusive rights,
including but not limited to copyright, patents, trademark, trade dress,
industrial design, plant varieties and trade secrets, to any and all ideas,
concepts, processes, discoveries, improvements and inventions conceived,
discovered, made, designed, researched or developed by the contributor either
solely or jointly with others, which relate to this work or result from this
work. Should any waiver of such right be judged legally invalid or
ineffective under applicable law, the contributor hereby grants to each
affected person a royalty-free, non transferable, non sublicensable, non
exclusive, irrevocable and unconditional license to this right.
--------------------
CONVENTIONS:
This library should never draw pixels outside the specified screen
boundaries, so you don't have to check this (that would cost CPU time)!
You can safely assume that triangles are rasterized one by one and from top
down, left to right (so you can utilize e.g. various caches), and if sorting
is disabled the order of rasterization will be that specified in the scene
structure and model arrays (of course, some triangles and models may be
skipped due to culling etc.).
Angles are in S3L_Units, a full angle (2 pi) is S3L_FRACTIONS_PER_UNITs.
We use row vectors.
In 3D space, a left-handed coord. system is used. One spatial unit is split
into S3L_FRACTIONS_PER_UNIT fractions (fixed point arithmetic).
y ^
| _
| /| z
| /
| /
[0,0,0]-------> x
Untransformed camera is placed at [0,0,0], looking forward along +z axis. The
projection plane is centered at [0,0,0], stretrinch from
-S3L_FRACTIONS_PER_UNIT to S3L_FRACTIONS_PER_UNIT horizontally (x),
vertical size (y) depends on the aspect ratio (S3L_RESOLUTION_X and
S3L_RESOLUTION_Y). Camera FOV is defined by focal length in S3L_Units.
Rotations use Euler angles and are generally in the extrinsic Euler angles in
ZXY order (by Z, then by X, then by Y). Positive rotation about an axis
rotates CW (clock-wise) when looking in the direction of the axis.
Coordinates of pixels on the screen start at the top left, from [0,0].
There is NO subpixel accuracy (screen coordinates are only integer).
Triangle rasterization rules are these (mostly same as OpenGL, D3D etc.):
- Let's define:
- left side:
- not exactly horizontal, and on the left side of triangle
- exactly horizontal and above the topmost
(in other words: its normal points at least a little to the left or
completely up)
- right side: not left side
- Pixel centers are at integer coordinates and triangle for drawing are
specified with integer coordinates of pixel centers.
- A pixel is rasterized:
- if its center is inside the triangle OR
- if its center is exactly on the triangle side which is left and at the
same time is not on the side that's right (case of a triangle that's on
a single line) OR
- if its center is exactly on the triangle corner of sides neither of which
is right.
These rules imply among others:
- Adjacent triangles don't have any overlapping pixels, nor gaps between.
- Triangles of points that lie on a single line are NOT rasterized.
- A single "long" triangle CAN be rasterized as isolated islands of pixels.
- Transforming (e.g. mirroring, rotating by 90 degrees etc.) a result of
rasterizing triangle A is NOT generally equal to applying the same
transformation to triangle A first and then rasterizing it. Even the number
of rasterized pixels is usually different.
- If specifying a triangle with integer coordinates (which we are), then:
- The bottom-most corner (or side) of a triangle is never rasterized
(because it is connected to a right side).
- The top-most corner can only be rasterized on completely horizontal side
(otherwise it is connected to a right side).
- Vertically middle corner is rasterized if and only if it is on the left
of the triangle and at the same time is also not the bottom-most corner.
*/
#include <stdint.h>
#ifdef S3L_RESOLUTION_X
#ifdef S3L_RESOLUTION_Y
#define S3L_MAX_PIXELS (S3L_RESOLUTION_X * S3L_RESOLUTION_Y)
#endif
#endif
#ifndef S3L_RESOLUTION_X
#ifndef S3L_MAX_PIXELS
#error Dynamic resolution set (S3L_RESOLUTION_X not defined), but\
S3L_MAX_PIXELS not defined!
#endif
uint16_t S3L_resolutionX = 512; /**< If a static resolution is not set with
S3L_RESOLUTION_X, this variable can be
used to change X resolution at runtime,
in which case S3L_MAX_PIXELS has to be
defined (to allocate zBuffer etc.)! */
#define S3L_RESOLUTION_X S3L_resolutionX
#endif
#ifndef S3L_RESOLUTION_Y
#ifndef S3L_MAX_PIXELS
#error Dynamic resolution set (S3L_RESOLUTION_Y not defined), but\
S3L_MAX_PIXELS not defined!
#endif
uint16_t S3L_resolutionY = 512; /**< Same as S3L_resolutionX, but for Y
resolution. */
#define S3L_RESOLUTION_Y S3L_resolutionY
#endif
#ifndef S3L_USE_WIDER_TYPES
/** If true, the library will use wider data types which will largely supress
many rendering bugs and imprecisions happening due to overflows, but this will
also consumer more RAM and may potentially be slower on computers with smaller
native integer. */
#define S3L_USE_WIDER_TYPES 0
#endif
#ifndef S3L_SIN_METHOD
/** Says which method should be used for computing sin/cos functions, possible
values: 0 (lookup table, takes more program memory), 1 (Bhaskara's
approximation, slower). This may cause the trigonometric functions give
slightly different results. */
#define S3L_SIN_METHOD 0
#endif
/** Units of measurement in 3D space. There is S3L_FRACTIONS_PER_UNIT in one
spatial unit. By dividing the unit into fractions we effectively achieve a
fixed point arithmetic. The number of fractions is a constant that serves as
1.0 in floating point arithmetic (normalization etc.). */
typedef
#if S3L_USE_WIDER_TYPES
int64_t
#else
int32_t
#endif
S3L_Unit;
/** How many fractions a spatial unit is split into. This is NOT SUPPOSED TO
BE REDEFINED, so rather don't do it (otherwise things may overflow etc.). */
#define S3L_FRACTIONS_PER_UNIT 512
typedef
#if S3L_USE_WIDER_TYPES
int32_t
#else
int16_t
#endif
S3L_ScreenCoord;
typedef
#if S3L_USE_WIDER_TYPES
uint32_t
#else
uint16_t
#endif
S3L_Index;
#ifndef S3L_NEAR_CROSS_STRATEGY
/** Specifies how the library will handle triangles that partially cross the
near plane. These are problematic and require special handling. Possible
values:
0: Strictly cull any triangle crossing the near plane. This will make such
triangles disappear. This is good for performance or models viewed only
from at least small distance.
1: Forcefully push the vertices crossing near plane in front of it. This is
a cheap technique that can be good enough for displaying simple
environments on slow devices, but texturing and geometric artifacts/warps
will appear.
2: Geometrically correct the triangles crossing the near plane. This may
result in some triangles being subdivided into two and is a little more
expensive, but the results will be geometrically correct, even though
barycentric correction is not performed so texturing artifacts will
appear. Can be ideal with S3L_FLAT.
3: Perform both geometrical and barycentric correction of triangle crossing
the near plane. This is significantly more expensive but results in
correct rendering. */
#define S3L_NEAR_CROSS_STRATEGY 0
#endif
#ifndef S3L_FLAT
/** If on, disables computation of per-pixel values such as barycentric
coordinates and depth -- these will still be available but will be the same
for the whole triangle. This can be used to create flat-shaded renders and
will be a lot faster. With this option on you will probably want to use
sorting instead of z-buffer. */
#define S3L_FLAT 0
#endif
#if S3L_FLAT
#define S3L_COMPUTE_DEPTH 0
#define S3L_PERSPECTIVE_CORRECTION 0
// don't disable z-buffer, it makes sense to use it with no sorting
#endif
#ifndef S3L_PERSPECTIVE_CORRECTION
/** Specifies what type of perspective correction (PC) to use. Remember this
is an expensive operation! Possible values:
0: No perspective correction. Fastest, inaccurate from most angles.
1: Per-pixel perspective correction, accurate but very expensive.
2: Approximation (computing only at every S3L_PC_APPROX_LENGTHth pixel).
Quake-style approximation is used, which only computes the PC after
S3L_PC_APPROX_LENGTH pixels. This is reasonably accurate and fast. */
#define S3L_PERSPECTIVE_CORRECTION 0
#endif
#ifndef S3L_PC_APPROX_LENGTH
/** For S3L_PERSPECTIVE_CORRECTION == 2, this specifies after how many pixels
PC is recomputed. Should be a power of two to keep up the performance.
Smaller is nicer but slower. */
#define S3L_PC_APPROX_LENGTH 32
#endif
#if S3L_PERSPECTIVE_CORRECTION
#define S3L_COMPUTE_DEPTH 1 // PC inevitably computes depth, so enable it
#endif
#ifndef S3L_COMPUTE_DEPTH
/** Whether to compute depth for each pixel (fragment). Some other options
may turn this on automatically. If you don't need depth information, turning
this off can save performance. Depth will still be accessible in
S3L_PixelInfo, but will be constant -- equal to center point depth -- over
the whole triangle. */
#define S3L_COMPUTE_DEPTH 1
#endif
#ifndef S3L_Z_BUFFER
/** What type of z-buffer (depth buffer) to use for visibility determination.
Possible values:
0: Don't use z-buffer. This saves a lot of memory, but visibility checking
won't be pixel-accurate and has to mostly be done by other means (typically
sorting).
1: Use full z-buffer (of S3L_Units) for visibiltiy determination. This is the
most accurate option (and also a fast one), but requires a big amount of
memory.
2: Use reduced-size z-buffer (of bytes). This is fast and somewhat accurate,
but inaccuracies can occur and a considerable amount of memory is
needed. */
#define S3L_Z_BUFFER 0
#endif
#ifndef S3L_REDUCED_Z_BUFFER_GRANULARITY
/** For S3L_Z_BUFFER == 2 this sets the reduced z-buffer granularity. */
#define S3L_REDUCED_Z_BUFFER_GRANULARITY 5
#endif
#ifndef S3L_STENCIL_BUFFER
/** Whether to use stencil buffer for drawing -- with this a pixel that would
be resterized over an already rasterized pixel (within a frame) will be
discarded. This is mostly for front-to-back sorted drawing. */
#define S3L_STENCIL_BUFFER 0
#endif
#ifndef S3L_SORT
/** Defines how to sort triangles before drawing a frame. This can be used to
solve visibility in case z-buffer is not used, to prevent overwriting already
rasterized pixels, implement transparency etc. Note that for simplicity and
performance a relatively simple sorting is used which doesn't work completely
correctly, so mistakes can occur (even the best sorting wouldn't be able to
solve e.g. intersecting triangles). Note that sorting requires a bit of extra
memory -- an array of the triangles to sort -- the size of this array limits
the maximum number of triangles that can be drawn in a single frame
(S3L_MAX_TRIANGES_DRAWN). Possible values:
0: Don't sort triangles. This is fastest and doesn't use extra memory.
1: Sort triangles from back to front. This can in most cases solve visibility
without requiring almost any extra memory compared to z-buffer.
2: Sort triangles from front to back. This can be faster than back to front
because we prevent computing pixels that will be overwritten by nearer
ones, but we need a 1b stencil buffer for this (enable S3L_STENCIL_BUFFER),
so a bit more memory is needed. */
#define S3L_SORT 0
#endif
#ifndef S3L_MAX_TRIANGES_DRAWN
/** Maximum number of triangles that can be drawn in sorted modes. This
affects the size of the cache used for triangle sorting. */
#define S3L_MAX_TRIANGES_DRAWN 128
#endif
#ifndef S3L_NEAR
/** Distance of the near clipping plane. Points in front or EXATLY ON this
plane are considered outside the frustum. This must be >= 0. */
#define S3L_NEAR (S3L_FRACTIONS_PER_UNIT / 4)
#endif
#if S3L_NEAR <= 0
#define S3L_NEAR 1 // Can't be <= 0.
#endif
#ifndef S3L_NORMAL_COMPUTE_MAXIMUM_AVERAGE
/** Affects the S3L_computeModelNormals function. See its description for
details. */
#define S3L_NORMAL_COMPUTE_MAXIMUM_AVERAGE 6
#endif
#ifndef S3L_FAST_LERP_QUALITY
/** Quality (scaling) of SOME (stepped) linear interpolations. 0 will most
likely be a tiny bit faster, but artifacts can occur for bigger tris, while
higher values can fix this -- in theory all higher values will have the same
speed (it is a shift value), but it mustn't be too high to prevent
overflow. */
#define S3L_FAST_LERP_QUALITY 11
#endif
/** Vector that consists of four scalars and can represent homogenous
coordinates, but is generally also used as Vec3 and Vec2 for various
purposes. */
typedef struct
{
S3L_Unit x;
S3L_Unit y;
S3L_Unit z;
S3L_Unit w;
} S3L_Vec4;
#define S3L_logVec4(v)\
printf("Vec4: %d %d %d %d\n",((v).x),((v).y),((v).z),((v).w))
static inline void S3L_vec4Init(S3L_Vec4 *v);
static inline void S3L_vec4Set(S3L_Vec4 *v, S3L_Unit x, S3L_Unit y,
S3L_Unit z, S3L_Unit w);
static inline void S3L_vec3Add(S3L_Vec4 *result, S3L_Vec4 added);
static inline void S3L_vec3Sub(S3L_Vec4 *result, S3L_Vec4 substracted);
S3L_Unit S3L_vec3Length(S3L_Vec4 v);
/** Normalizes Vec3. Note that this function tries to normalize correctly
rather than quickly! If you need to normalize quickly, do it yourself in a
way that best fits your case. */
void S3L_vec3Normalize(S3L_Vec4 *v);
/** Like S3L_vec3Normalize, but doesn't perform any checks on the input vector,
which is faster, but can be very innacurate or overflowing. You are supposed
to provide a "nice" vector (not too big or small). */
static inline void S3L_vec3NormalizeFast(S3L_Vec4 *v);
S3L_Unit S3L_vec2Length(S3L_Vec4 v);
void S3L_vec3Cross(S3L_Vec4 a, S3L_Vec4 b, S3L_Vec4 *result);
static inline S3L_Unit S3L_vec3Dot(S3L_Vec4 a, S3L_Vec4 b);
/** Computes a reflection direction (typically used e.g. for specular component
in Phong illumination). The input vectors must be normalized. The result will
be normalized as well. */
void S3L_reflect(S3L_Vec4 toLight, S3L_Vec4 normal, S3L_Vec4 *result);
/** Determines the winding of a triangle, returns 1 (CW, clockwise), -1 (CCW,
counterclockwise) or 0 (points lie on a single line). */
static inline int8_t S3L_triangleWinding(
S3L_ScreenCoord x0,
S3L_ScreenCoord y0,
S3L_ScreenCoord x1,
S3L_ScreenCoord y1,
S3L_ScreenCoord x2,
S3L_ScreenCoord y2);
typedef struct
{
S3L_Vec4 translation;
S3L_Vec4 rotation; /**< Euler angles. Rortation is applied in this order:
1. z = by z (roll) CW looking along z+
2. x = by x (pitch) CW looking along x+
3. y = by y (yaw) CW looking along y+ */
S3L_Vec4 scale;
} S3L_Transform3D;
#define S3L_logTransform3D(t)\
printf("Transform3D: T = [%d %d %d], R = [%d %d %d], S = [%d %d %d]\n",\
(t).translation.x,(t).translation.y,(t).translation.z,\
(t).rotation.x,(t).rotation.y,(t).rotation.z,\
(t).scale.x,(t).scale.y,(t).scale.z)
static inline void S3L_transform3DInit(S3L_Transform3D *t);
void S3L_lookAt(S3L_Vec4 pointTo, S3L_Transform3D *t);
void S3L_transform3DSet(
S3L_Unit tx,
S3L_Unit ty,
S3L_Unit tz,
S3L_Unit rx,
S3L_Unit ry,
S3L_Unit rz,
S3L_Unit sx,
S3L_Unit sy,
S3L_Unit sz,
S3L_Transform3D *t);
/** Converts rotation transformation to three direction vectors of given length
(any one can be NULL, in which case it won't be computed). */
void S3L_rotationToDirections(
S3L_Vec4 rotation,
S3L_Unit length,
S3L_Vec4 *forw,
S3L_Vec4 *right,
S3L_Vec4 *up);
/** 4x4 matrix, used mostly for 3D transforms. The indexing is this:
matrix[column][row]. */
typedef S3L_Unit S3L_Mat4[4][4];
#define S3L_logMat4(m)\
printf("Mat4:\n %d %d %d %d\n %d %d %d %d\n %d %d %d %d\n %d %d %d %d\n"\
,(m)[0][0],(m)[1][0],(m)[2][0],(m)[3][0],\
(m)[0][1],(m)[1][1],(m)[2][1],(m)[3][1],\
(m)[0][2],(m)[1][2],(m)[2][2],(m)[3][2],\
(m)[0][3],(m)[1][3],(m)[2][3],(m)[3][3])
/** Initializes a 4x4 matrix to identity. */
static inline void S3L_mat4Init(S3L_Mat4 m);
void S3L_mat4Copy(S3L_Mat4 src, S3L_Mat4 dst);
void S3L_mat4Transpose(S3L_Mat4 m);
void S3L_makeTranslationMat(
S3L_Unit offsetX,
S3L_Unit offsetY,
S3L_Unit offsetZ,
S3L_Mat4 m);
/** Makes a scaling matrix. DON'T FORGET: scale of 1.0 is set with
S3L_FRACTIONS_PER_UNIT! */
void S3L_makeScaleMatrix(
S3L_Unit scaleX,
S3L_Unit scaleY,
S3L_Unit scaleZ,
S3L_Mat4 m);
/** Makes a matrix for rotation in the ZXY order. */
void S3L_makeRotationMatrixZXY(
S3L_Unit byX,
S3L_Unit byY,
S3L_Unit byZ,
S3L_Mat4 m);
void S3L_makeWorldMatrix(S3L_Transform3D worldTransform, S3L_Mat4 m);
void S3L_makeCameraMatrix(S3L_Transform3D cameraTransform, S3L_Mat4 m);
/** Multiplies a vector by a matrix with normalization by
S3L_FRACTIONS_PER_UNIT. Result is stored in the input vector. */
void S3L_vec4Xmat4(S3L_Vec4 *v, S3L_Mat4 m);
/** Same as S3L_vec4Xmat4 but faster, because this version doesn't compute the
W component of the result, which is usually not needed. */
void S3L_vec3Xmat4(S3L_Vec4 *v, S3L_Mat4 m);
/** Multiplies two matrices with normalization by S3L_FRACTIONS_PER_UNIT.
Result is stored in the first matrix. The result represents a transformation
that has the same effect as applying the transformation represented by m1 and
then m2 (in that order). */
void S3L_mat4Xmat4(S3L_Mat4 m1, S3L_Mat4 m2);
typedef struct
{
S3L_Unit focalLength; ///< Defines the field of view (FOV).
S3L_Transform3D transform;
} S3L_Camera;
void S3L_cameraInit(S3L_Camera *camera);
typedef struct
{
uint8_t backfaceCulling; /**< What backface culling to use. Possible
values:
- 0 none
- 1 clock-wise
- 2 counter clock-wise */
int8_t visible; /**< Can be used to easily hide the model. */
} S3L_DrawConfig;
void S3L_drawConfigInit(S3L_DrawConfig *config);
typedef struct
{
const S3L_Unit *vertices;
S3L_Index vertexCount;
const S3L_Index *triangles;
S3L_Index triangleCount;
S3L_Transform3D transform;
S3L_Mat4 *customTransformMatrix; /**< This can be used to override the
transform (if != 0) with a custom
transform matrix, which is more
general. */
S3L_DrawConfig config;
} S3L_Model3D; ///< Represents a 3D model.
void S3L_model3DInit(
const S3L_Unit *vertices,
S3L_Index vertexCount,
const S3L_Index *triangles,
S3L_Index triangleCount,
S3L_Model3D *model);
typedef struct
{
S3L_Model3D *models;
S3L_Index modelCount;
S3L_Camera camera;
} S3L_Scene; ///< Represent the 3D scene to be rendered.
void S3L_sceneInit(
S3L_Model3D *models,
S3L_Index modelCount,
S3L_Scene *scene);
typedef struct
{
S3L_ScreenCoord x; ///< Screen X coordinate.
S3L_ScreenCoord y; ///< Screen Y coordinate.
S3L_Unit barycentric[3]; /**< Barycentric coords correspond to the three
vertices. These serve to locate the pixel on a
triangle and interpolate values between its
three points. Each one goes from 0 to
S3L_FRACTIONS_PER_UNIT (including), but due to
rounding error may fall outside this range (you
can use S3L_correctBarycentricCoords to fix this
for the price of some performance). The sum of
the three coordinates will always be exactly
S3L_FRACTIONS_PER_UNIT. */
S3L_Index modelIndex; ///< Model index within the scene.
S3L_Index triangleIndex; ///< Triangle index within the model.
uint32_t triangleID; /**< Unique ID of the triangle withing the whole
scene. This can be used e.g. by a cache to
quickly find out if a triangle has changed. */
S3L_Unit depth; ///< Depth (only if depth is turned on).
S3L_Unit previousZ; /**< Z-buffer value (not necessarily world depth in
S3L_Units!) that was in the z-buffer on the
pixels position before this pixel was
rasterized. This can be used to set the value
back, e.g. for transparency. */
S3L_ScreenCoord triangleSize[2]; /**< Rasterized triangle width and height,
can be used e.g. for MIP mapping. */
} S3L_PixelInfo; /**< Used to pass the info about a rasterized pixel
(fragment) to the user-defined drawing func. */
static inline void S3L_pixelInfoInit(S3L_PixelInfo *p);
/** Corrects barycentric coordinates so that they exactly meet the defined
conditions (each fall into <0,S3L_FRACTIONS_PER_UNIT>, sum =
S3L_FRACTIONS_PER_UNIT). Note that doing this per-pixel can slow the program
down significantly. */
static inline void S3L_correctBarycentricCoords(S3L_Unit barycentric[3]);
// general helper functions
static inline S3L_Unit S3L_abs(S3L_Unit value);
static inline S3L_Unit S3L_min(S3L_Unit v1, S3L_Unit v2);
static inline S3L_Unit S3L_max(S3L_Unit v1, S3L_Unit v2);
static inline S3L_Unit S3L_clamp(S3L_Unit v, S3L_Unit v1, S3L_Unit v2);
static inline S3L_Unit S3L_wrap(S3L_Unit value, S3L_Unit mod);
static inline S3L_Unit S3L_nonZero(S3L_Unit value);
static inline S3L_Unit S3L_zeroClamp(S3L_Unit value);
S3L_Unit S3L_sin(S3L_Unit x);
S3L_Unit S3L_asin(S3L_Unit x);
static inline S3L_Unit S3L_cos(S3L_Unit x);
S3L_Unit S3L_vec3Length(S3L_Vec4 v);
S3L_Unit S3L_sqrt(S3L_Unit value);
/** Projects a single point from 3D space to the screen space (pixels), which
can be useful e.g. for drawing sprites. The w component of input and result
holds the point size. If this size is 0 in the result, the sprite is outside
the view. */
void S3L_project3DPointToScreen(
S3L_Vec4 point,
S3L_Camera camera,
S3L_Vec4 *result);
/** Computes a normalized normal of given triangle. */
void S3L_triangleNormal(S3L_Vec4 t0, S3L_Vec4 t1, S3L_Vec4 t2,
S3L_Vec4 *n);
/** Helper function for retrieving per-vertex indexed values from an array,
e.g. texturing (UV) coordinates. The 'indices' array contains three indices
for each triangle, each index pointing into 'values' array, which contains
the values, each one consisting of 'numComponents' components (e.g. 2 for
UV coordinates). The three values are retrieved into 'v0', 'v1' and 'v2'
vectors (into x, y, z and w, depending on 'numComponents'). This function is
meant to be used per-triangle (typically from a cache), NOT per-pixel, as it
is not as fast as possible! */
void S3L_getIndexedTriangleValues(
S3L_Index triangleIndex,
const S3L_Index *indices,
const S3L_Unit *values,
uint8_t numComponents,
S3L_Vec4 *v0,
S3L_Vec4 *v1,
S3L_Vec4 *v2);
/** Computes a normalized normal for every vertex of given model (this is
relatively slow and SHOUDN'T be done each frame). The dst array must have a
sufficient size preallocated! The size is: number of model vertices * 3 *
sizeof(S3L_Unit). Note that for advanced allowing sharp edges it is not
sufficient to have per-vertex normals, but must be per-triangle. This
function doesn't support this.
The function computes a normal for each vertex by averaging normals of
the triangles containing the vertex. The maximum number of these triangle
normals that will be averaged is set with
S3L_NORMAL_COMPUTE_MAXIMUM_AVERAGE. */
void S3L_computeModelNormals(S3L_Model3D model, S3L_Unit *dst,
int8_t transformNormals);
/** Interpolated between two values, v1 and v2, in the same ratio as t is to
tMax. Does NOT prevent zero division. */
static inline S3L_Unit S3L_interpolate(
S3L_Unit v1,
S3L_Unit v2,
S3L_Unit t,
S3L_Unit tMax);
/** Same as S3L_interpolate but with v1 == 0. Should be faster. */
static inline S3L_Unit S3L_interpolateFrom0(
S3L_Unit v2,
S3L_Unit t,
S3L_Unit tMax);
/** Like S3L_interpolate, but uses a parameter that goes from 0 to
S3L_FRACTIONS_PER_UNIT - 1, which can be faster. */
static inline S3L_Unit S3L_interpolateByUnit(
S3L_Unit v1,
S3L_Unit v2,
S3L_Unit t);
/** Same as S3L_interpolateByUnit but with v1 == 0. Should be faster. */
static inline S3L_Unit S3L_interpolateByUnitFrom0(
S3L_Unit v2,
S3L_Unit t);
static inline S3L_Unit S3L_distanceManhattan(S3L_Vec4 a, S3L_Vec4 b);
/** Returns a value interpolated between the three triangle vertices based on
barycentric coordinates. */
static inline S3L_Unit S3L_interpolateBarycentric(
S3L_Unit value0,
S3L_Unit value1,
S3L_Unit value2,
S3L_Unit barycentric[3]);
static inline void S3L_mapProjectionPlaneToScreen(
S3L_Vec4 point,
S3L_ScreenCoord *screenX,
S3L_ScreenCoord *screenY);
/** Draws a triangle according to given config. The vertices are specified in
Screen Space space (pixels). If perspective correction is enabled, each
vertex has to have a depth (Z position in camera space) specified in the Z
component. */
void S3L_drawTriangle(
S3L_Vec4 point0,
S3L_Vec4 point1,
S3L_Vec4 point2,
S3L_Index modelIndex,
S3L_Index triangleIndex);
/** This should be called before rendering each frame. The function clears
buffers and does potentially other things needed for the frame. */
void S3L_newFrame(void);
void S3L_zBufferClear(void);
void S3L_stencilBufferClear(void);
/** Writes a value (not necessarily depth! depends on the format of z-buffer)
to z-buffer (if enabled). Does NOT check boundaries! */
void S3L_zBufferWrite(S3L_ScreenCoord x, S3L_ScreenCoord y, S3L_Unit value);
/** Reads a value (not necessarily depth! depends on the format of z-buffer)
from z-buffer (if enabled). Does NOT check boundaries! */
S3L_Unit S3L_zBufferRead(S3L_ScreenCoord x, S3L_ScreenCoord y);
static inline void S3L_rotate2DPoint(S3L_Unit *x, S3L_Unit *y, S3L_Unit angle);
/** Predefined vertices of a cube to simply insert in an array. These come with
S3L_CUBE_TRIANGLES and S3L_CUBE_TEXCOORDS. */
#define S3L_CUBE_VERTICES(m)\
/* 0 front, bottom, right */\
m/2, -m/2, -m/2,\
/* 1 front, bottom, left */\
-m/2, -m/2, -m/2,\
/* 2 front, top, right */\
m/2, m/2, -m/2,\
/* 3 front, top, left */\
-m/2, m/2, -m/2,\
/* 4 back, bottom, right */\
m/2, -m/2, m/2,\
/* 5 back, bottom, left */\
-m/2, -m/2, m/2,\
/* 6 back, top, right */\
m/2, m/2, m/2,\
/* 7 back, top, left */\
-m/2, m/2, m/2
#define S3L_CUBE_VERTEX_COUNT 8
/** Predefined triangle indices of a cube, to be used with S3L_CUBE_VERTICES
and S3L_CUBE_TEXCOORDS. */
#define S3L_CUBE_TRIANGLES\
3, 0, 2, /* front */\
1, 0, 3,\
0, 4, 2, /* right */\
2, 4, 6,\
4, 5, 6, /* back */\
7, 6, 5,\
3, 7, 1, /* left */\
1, 7, 5,\
6, 3, 2, /* top */\
7, 3, 6,\
1, 4, 0, /* bottom */\
5, 4, 1
#define S3L_CUBE_TRIANGLE_COUNT 12
/** Predefined texture coordinates of a cube, corresponding to triangles (NOT
vertices), to be used with S3L_CUBE_VERTICES and S3L_CUBE_TRIANGLES. */
#define S3L_CUBE_TEXCOORDS(m)\
0,0, m,m, m,0,\
0,m, m,m, 0,0,\
m,m, m,0, 0,m,\
0,m, m,0, 0,0,\
m,0, 0,0, m,m,\
0,m, m,m, 0,0,\
0,0, 0,m, m,0,\
m,0, 0,m, m,m,\
0,0, m,m, m,0,\
0,m, m,m, 0,0,\
m,0, 0,m, m,m,\
0,0, 0,m, m,0
//=============================================================================
// privates
#define S3L_UNUSED(what) (void)(what) ///< helper macro for unused vars
#define S3L_HALF_RESOLUTION_X (S3L_RESOLUTION_X >> 1)
#define S3L_HALF_RESOLUTION_Y (S3L_RESOLUTION_Y >> 1)
#define S3L_PROJECTION_PLANE_HEIGHT\
((S3L_RESOLUTION_Y * S3L_FRACTIONS_PER_UNIT * 2) / S3L_RESOLUTION_X)
#if S3L_Z_BUFFER == 1
#define S3L_MAX_DEPTH 2147483647
S3L_Unit S3L_zBuffer[S3L_MAX_PIXELS];
#define S3L_zBufferFormat(depth) (depth)
#elif S3L_Z_BUFFER == 2
#define S3L_MAX_DEPTH 255
uint8_t S3L_zBuffer[S3L_MAX_PIXELS];
#define S3L_zBufferFormat(depth)\
S3L_min(255,(depth) >> S3L_REDUCED_Z_BUFFER_GRANULARITY)
#endif
#if S3L_Z_BUFFER
static inline int8_t S3L_zTest(
S3L_ScreenCoord x,
S3L_ScreenCoord y,
S3L_Unit depth)
{
uint32_t index = y * S3L_RESOLUTION_X + x;
depth = S3L_zBufferFormat(depth);
#if S3L_Z_BUFFER == 2
#define cmp <= /* For reduced z-buffer we need equality test, because
otherwise pixels at the maximum depth (255) would never be
drawn over the background (which also has the depth of
255). */
#else
#define cmp < /* For normal z-buffer we leave out equality test to not waste
time by drawing over already drawn pixls. */
#endif
if (depth cmp S3L_zBuffer[index])
{
S3L_zBuffer[index] = depth;
return 1;
}
#undef cmp
return 0;
}
#endif
S3L_Unit S3L_zBufferRead(S3L_ScreenCoord x, S3L_ScreenCoord y)
{
#if S3L_Z_BUFFER
return S3L_zBuffer[y * S3L_RESOLUTION_X + x];
#else
S3L_UNUSED(x);
S3L_UNUSED(y);
return 0;
#endif
}
void S3L_zBufferWrite(S3L_ScreenCoord x, S3L_ScreenCoord y, S3L_Unit value)
{
#if S3L_Z_BUFFER
S3L_zBuffer[y * S3L_RESOLUTION_X + x] = value;
#else
S3L_UNUSED(x);
S3L_UNUSED(y);
S3L_UNUSED(value);
#endif
}
#if S3L_STENCIL_BUFFER
#define S3L_STENCIL_BUFFER_SIZE\
((S3L_RESOLUTION_X * S3L_RESOLUTION_Y - 1) / 8 + 1)
uint8_t S3L_stencilBuffer[S3L_STENCIL_BUFFER_SIZE];
static inline int8_t S3L_stencilTest(
S3L_ScreenCoord x,
S3L_ScreenCoord y)
{
uint32_t index = y * S3L_RESOLUTION_X + x;
uint32_t bit = (index & 0x00000007);
index = index >> 3;
uint8_t val = S3L_stencilBuffer[index];
if ((val >> bit) & 0x1)
return 0;
S3L_stencilBuffer[index] = val | (0x1 << bit);
return 1;
}
#endif
#define S3L_COMPUTE_LERP_DEPTH\
(S3L_COMPUTE_DEPTH && (S3L_PERSPECTIVE_CORRECTION == 0))
#define S3L_SIN_TABLE_LENGTH 128
#if S3L_SIN_METHOD == 0
static const S3L_Unit S3L_sinTable[S3L_SIN_TABLE_LENGTH] =
{
/* 511 was chosen here as a highest number that doesn't overflow during
compilation for S3L_FRACTIONS_PER_UNIT == 1024 */
(0*S3L_FRACTIONS_PER_UNIT)/511, (6*S3L_FRACTIONS_PER_UNIT)/511,
(12*S3L_FRACTIONS_PER_UNIT)/511, (18*S3L_FRACTIONS_PER_UNIT)/511,
(25*S3L_FRACTIONS_PER_UNIT)/511, (31*S3L_FRACTIONS_PER_UNIT)/511,
(37*S3L_FRACTIONS_PER_UNIT)/511, (43*S3L_FRACTIONS_PER_UNIT)/511,
(50*S3L_FRACTIONS_PER_UNIT)/511, (56*S3L_FRACTIONS_PER_UNIT)/511,
(62*S3L_FRACTIONS_PER_UNIT)/511, (68*S3L_FRACTIONS_PER_UNIT)/511,
(74*S3L_FRACTIONS_PER_UNIT)/511, (81*S3L_FRACTIONS_PER_UNIT)/511,
(87*S3L_FRACTIONS_PER_UNIT)/511, (93*S3L_FRACTIONS_PER_UNIT)/511,
(99*S3L_FRACTIONS_PER_UNIT)/511, (105*S3L_FRACTIONS_PER_UNIT)/511,
(111*S3L_FRACTIONS_PER_UNIT)/511, (118*S3L_FRACTIONS_PER_UNIT)/511,
(124*S3L_FRACTIONS_PER_UNIT)/511, (130*S3L_FRACTIONS_PER_UNIT)/511,
(136*S3L_FRACTIONS_PER_UNIT)/511, (142*S3L_FRACTIONS_PER_UNIT)/511,
(148*S3L_FRACTIONS_PER_UNIT)/511, (154*S3L_FRACTIONS_PER_UNIT)/511,
(160*S3L_FRACTIONS_PER_UNIT)/511, (166*S3L_FRACTIONS_PER_UNIT)/511,
(172*S3L_FRACTIONS_PER_UNIT)/511, (178*S3L_FRACTIONS_PER_UNIT)/511,
(183*S3L_FRACTIONS_PER_UNIT)/511, (189*S3L_FRACTIONS_PER_UNIT)/511,
(195*S3L_FRACTIONS_PER_UNIT)/511, (201*S3L_FRACTIONS_PER_UNIT)/511,
(207*S3L_FRACTIONS_PER_UNIT)/511, (212*S3L_FRACTIONS_PER_UNIT)/511,
(218*S3L_FRACTIONS_PER_UNIT)/511, (224*S3L_FRACTIONS_PER_UNIT)/511,
(229*S3L_FRACTIONS_PER_UNIT)/511, (235*S3L_FRACTIONS_PER_UNIT)/511,
(240*S3L_FRACTIONS_PER_UNIT)/511, (246*S3L_FRACTIONS_PER_UNIT)/511,
(251*S3L_FRACTIONS_PER_UNIT)/511, (257*S3L_FRACTIONS_PER_UNIT)/511,
(262*S3L_FRACTIONS_PER_UNIT)/511, (268*S3L_FRACTIONS_PER_UNIT)/511,
(273*S3L_FRACTIONS_PER_UNIT)/511, (278*S3L_FRACTIONS_PER_UNIT)/511,
(283*S3L_FRACTIONS_PER_UNIT)/511, (289*S3L_FRACTIONS_PER_UNIT)/511,
(294*S3L_FRACTIONS_PER_UNIT)/511, (299*S3L_FRACTIONS_PER_UNIT)/511,
(304*S3L_FRACTIONS_PER_UNIT)/511, (309*S3L_FRACTIONS_PER_UNIT)/511,
(314*S3L_FRACTIONS_PER_UNIT)/511, (319*S3L_FRACTIONS_PER_UNIT)/511,
(324*S3L_FRACTIONS_PER_UNIT)/511, (328*S3L_FRACTIONS_PER_UNIT)/511,
(333*S3L_FRACTIONS_PER_UNIT)/511, (338*S3L_FRACTIONS_PER_UNIT)/511,
(343*S3L_FRACTIONS_PER_UNIT)/511, (347*S3L_FRACTIONS_PER_UNIT)/511,
(352*S3L_FRACTIONS_PER_UNIT)/511, (356*S3L_FRACTIONS_PER_UNIT)/511,
(361*S3L_FRACTIONS_PER_UNIT)/511, (365*S3L_FRACTIONS_PER_UNIT)/511,
(370*S3L_FRACTIONS_PER_UNIT)/511, (374*S3L_FRACTIONS_PER_UNIT)/511,
(378*S3L_FRACTIONS_PER_UNIT)/511, (382*S3L_FRACTIONS_PER_UNIT)/511,
(386*S3L_FRACTIONS_PER_UNIT)/511, (391*S3L_FRACTIONS_PER_UNIT)/511,
(395*S3L_FRACTIONS_PER_UNIT)/511, (398*S3L_FRACTIONS_PER_UNIT)/511,
(402*S3L_FRACTIONS_PER_UNIT)/511, (406*S3L_FRACTIONS_PER_UNIT)/511,
(410*S3L_FRACTIONS_PER_UNIT)/511, (414*S3L_FRACTIONS_PER_UNIT)/511,
(417*S3L_FRACTIONS_PER_UNIT)/511, (421*S3L_FRACTIONS_PER_UNIT)/511,
(424*S3L_FRACTIONS_PER_UNIT)/511, (428*S3L_FRACTIONS_PER_UNIT)/511,
(431*S3L_FRACTIONS_PER_UNIT)/511, (435*S3L_FRACTIONS_PER_UNIT)/511,
(438*S3L_FRACTIONS_PER_UNIT)/511, (441*S3L_FRACTIONS_PER_UNIT)/511,
(444*S3L_FRACTIONS_PER_UNIT)/511, (447*S3L_FRACTIONS_PER_UNIT)/511,
(450*S3L_FRACTIONS_PER_UNIT)/511, (453*S3L_FRACTIONS_PER_UNIT)/511,
(456*S3L_FRACTIONS_PER_UNIT)/511, (459*S3L_FRACTIONS_PER_UNIT)/511,
(461*S3L_FRACTIONS_PER_UNIT)/511, (464*S3L_FRACTIONS_PER_UNIT)/511,
(467*S3L_FRACTIONS_PER_UNIT)/511, (469*S3L_FRACTIONS_PER_UNIT)/511,
(472*S3L_FRACTIONS_PER_UNIT)/511, (474*S3L_FRACTIONS_PER_UNIT)/511,
(476*S3L_FRACTIONS_PER_UNIT)/511, (478*S3L_FRACTIONS_PER_UNIT)/511,
(481*S3L_FRACTIONS_PER_UNIT)/511, (483*S3L_FRACTIONS_PER_UNIT)/511,
(485*S3L_FRACTIONS_PER_UNIT)/511, (487*S3L_FRACTIONS_PER_UNIT)/511,
(488*S3L_FRACTIONS_PER_UNIT)/511, (490*S3L_FRACTIONS_PER_UNIT)/511,
(492*S3L_FRACTIONS_PER_UNIT)/511, (494*S3L_FRACTIONS_PER_UNIT)/511,
(495*S3L_FRACTIONS_PER_UNIT)/511, (497*S3L_FRACTIONS_PER_UNIT)/511,
(498*S3L_FRACTIONS_PER_UNIT)/511, (499*S3L_FRACTIONS_PER_UNIT)/511,
(501*S3L_FRACTIONS_PER_UNIT)/511, (502*S3L_FRACTIONS_PER_UNIT)/511,
(503*S3L_FRACTIONS_PER_UNIT)/511, (504*S3L_FRACTIONS_PER_UNIT)/511,
(505*S3L_FRACTIONS_PER_UNIT)/511, (506*S3L_FRACTIONS_PER_UNIT)/511,
(507*S3L_FRACTIONS_PER_UNIT)/511, (507*S3L_FRACTIONS_PER_UNIT)/511,
(508*S3L_FRACTIONS_PER_UNIT)/511, (509*S3L_FRACTIONS_PER_UNIT)/511,
(509*S3L_FRACTIONS_PER_UNIT)/511, (510*S3L_FRACTIONS_PER_UNIT)/511,
(510*S3L_FRACTIONS_PER_UNIT)/511, (510*S3L_FRACTIONS_PER_UNIT)/511,
(510*S3L_FRACTIONS_PER_UNIT)/511, (510*S3L_FRACTIONS_PER_UNIT)/511
};
#endif
#define S3L_SIN_TABLE_UNIT_STEP\
(S3L_FRACTIONS_PER_UNIT / (S3L_SIN_TABLE_LENGTH * 4))
void S3L_vec4Init(S3L_Vec4 *v)
{
v->x = 0; v->y = 0; v->z = 0; v->w = S3L_FRACTIONS_PER_UNIT;
}
void S3L_vec4Set(S3L_Vec4 *v, S3L_Unit x, S3L_Unit y, S3L_Unit z, S3L_Unit w)
{
v->x = x;
v->y = y;
v->z = z;
v->w = w;
}
void S3L_vec3Add(S3L_Vec4 *result, S3L_Vec4 added)
{
result->x += added.x;
result->y += added.y;