1
0
Fork 0
mirror of https://git.coom.tech/drummyfish/small3dlib.git synced 2025-01-07 03:56:19 +01:00

Add near cross handling strats

This commit is contained in:
Miloslav Číž 2020-12-26 22:32:57 +01:00
parent 8b2bfc0cf4
commit 54c2b2a870
4 changed files with 151 additions and 41 deletions

View file

@ -10,7 +10,7 @@
#include <time.h> #include <time.h>
#define S3L_FLAT 0 #define S3L_FLAT 0
#define S3L_STRICT_NEAR_CULLING 0 #define S3L_NEAR_CROSS_STRATEGY 1
#define S3L_PERSPECTIVE_CORRECTION 2 #define S3L_PERSPECTIVE_CORRECTION 2
#define S3L_SORT 0 #define S3L_SORT 0
#define S3L_STENCIL_BUFFER 0 #define S3L_STENCIL_BUFFER 0

View file

@ -11,7 +11,7 @@
#include <stdio.h> #include <stdio.h>
#include <time.h> #include <time.h>
#define S3L_STRICT_NEAR_CULLING 0 #define S3L_NEAR_CROSS_STRATEGY 2
#if TEXTURES #if TEXTURES
#define S3L_PERSPECTIVE_CORRECTION 2 #define S3L_PERSPECTIVE_CORRECTION 2

View file

@ -12,7 +12,7 @@
#include <stdlib.h> #include <stdlib.h>
#define S3L_FLAT 0 #define S3L_FLAT 0
#define S3L_STRICT_NEAR_CULLING 1 #define S3L_NEAR_CROSS_STRATEGY 0
#define S3L_PERSPECTIVE_CORRECTION 2 #define S3L_PERSPECTIVE_CORRECTION 2
#define S3L_SORT 0 #define S3L_SORT 0
#define S3L_STENCIL_BUFFER 0 #define S3L_STENCIL_BUFFER 0

View file

@ -10,7 +10,7 @@
license: CC0 1.0 (public domain) license: CC0 1.0 (public domain)
found at https://creativecommons.org/publicdomain/zero/1.0/ found at https://creativecommons.org/publicdomain/zero/1.0/
+ additional waiver of all IP + additional waiver of all IP
version: 0.852 version: 0.852d
Before including the library, define S3L_PIXEL_FUNCTION to the name of the 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 function you'll be using to draw single pixels (this function will be called
@ -186,12 +186,30 @@ BE REDEFINED, so rather don't do it (otherwise things may overflow etc.). */
typedef int16_t S3L_ScreenCoord; typedef int16_t S3L_ScreenCoord;
typedef uint16_t S3L_Index; typedef uint16_t S3L_Index;
#ifndef S3L_STRICT_NEAR_CULLING #ifndef S3L_NEAR_CROSS_STRATEGY
/** If on, any triangle that only partially intersects the near plane will be /** Specifies how the library will handle triangles that partially cross the
culled. This can prevent errorneous rendering and artifacts, but also makes near plane. These are problematic and require special handling. Possible
triangles close to the camera disappear. */ values:
#define S3L_STRICT_NEAR_CULLING 1 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: NOT IMPLEMENTED YET
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 #endif
#ifndef S3L_FLAT #ifndef S3L_FLAT
@ -2464,7 +2482,7 @@ static inline int8_t S3L_triangleIsVisible(
(p0.c cmp (v) && p1.c cmp (v) && p2.c cmp (v)) (p0.c cmp (v) && p1.c cmp (v) && p2.c cmp (v))
if ( // outside frustum? if ( // outside frustum?
#if S3L_STRICT_NEAR_CULLING #if S3L_NEAR_CROSS_STRATEGY == 0
p0.z <= S3L_NEAR || p1.z <= S3L_NEAR || p2.z <= S3L_NEAR || p0.z <= S3L_NEAR || p1.z <= S3L_NEAR || p2.z <= S3L_NEAR ||
// ^ partially in front of NEAR? // ^ partially in front of NEAR?
#else #else
@ -2509,41 +2527,135 @@ void _S3L_projectVertex(
S3L_Index triangleIndex, S3L_Index triangleIndex,
uint8_t vertex, uint8_t vertex,
S3L_Mat4 *projectionMatrix, S3L_Mat4 *projectionMatrix,
S3L_Vec4 *result, S3L_Vec4 *result)
S3L_Unit focalLength)
{ {
uint32_t vertexIndex = model->triangles[triangleIndex * 3 + vertex] * 3; uint32_t vertexIndex = model->triangles[triangleIndex * 3 + vertex] * 3;
result->x = model->vertices[vertexIndex]; result->x = model->vertices[vertexIndex];
result->y = model->vertices[vertexIndex + 1]; result->y = model->vertices[vertexIndex + 1];
result->z = model->vertices[vertexIndex + 2]; result->z = model->vertices[vertexIndex + 2];
result->w = S3L_FRACTIONS_PER_UNIT; // for translation result->w = S3L_FRACTIONS_PER_UNIT; // needed for translation
S3L_vec3Xmat4(result,projectionMatrix); S3L_vec3Xmat4(result,projectionMatrix);
result->w = result->z; result->w = result->z;
/* We'll keep the non-clamped z in w for sorting. */ /* We'll keep the non-clamped z in w for sorting. */
}
result->z = result->z >= S3L_NEAR ? result->z : S3L_NEAR; void _S3L_mapProjectedVertexToScreen(S3L_Vec4 *vertex, S3L_Unit focalLength)
{
vertex->z = vertex->z >= S3L_NEAR ? vertex->z : S3L_NEAR;
/* ^ This firstly prevents zero division in the follwoing z-divide and /* ^ This firstly prevents zero division in the follwoing z-divide and
secondly "pushes" vertices that are in front of near a little bit forward, secondly "pushes" vertices that are in front of near a little bit forward,
which makes them behave a bit better. If all three vertices end up exactly which makes them behave a bit better. If all three vertices end up exactly
on NEAR, the triangle will be culled. */ on NEAR, the triangle will be culled. */
S3L_perspectiveDivide(result,focalLength); S3L_perspectiveDivide(vertex,focalLength);
S3L_ScreenCoord sX, sY; S3L_ScreenCoord sX, sY;
S3L_mapProjectionPlaneToScreen(*result,&sX,&sY); S3L_mapProjectionPlaneToScreen(*vertex,&sX,&sY);
result->x = sX; vertex->x = sX;
result->y = sY; vertex->y = sY;
}
/**
Projects a triangle to the screen. If enabled, a triangle can be potentially
subdivided into two if it crosses the near plane, in which case two projected
triangles are returned (return value will be 1).
*/
uint8_t _S3L_projectTriangle(
const S3L_Model3D *model,
S3L_Index triangleIndex,
S3L_Mat4 *matrix,
uint32_t focalLength,
S3L_Vec4 transformed[6])
{
_S3L_projectVertex(model,triangleIndex,0,matrix,&(transformed[0]));
_S3L_projectVertex(model,triangleIndex,1,matrix,&(transformed[1]));
_S3L_projectVertex(model,triangleIndex,2,matrix,&(transformed[2]));
uint8_t result = 0;
#if S3L_NEAR_CROSS_STRATEGY == 2
uint8_t infront = 0;
uint8_t behind = 0;
uint8_t infrontI[3];
uint8_t behindI[3];
for (uint8_t i = 0; i < 3; ++i)
if (transformed[i].z < S3L_NEAR)
{
infrontI[infront] = i;
infront++;
}
else
{
behindI[behind] = i;
behind++;
}
#define interpolateVertex \
S3L_Unit ratio =\
((transformed[be].z - S3L_NEAR) * S3L_FRACTIONS_PER_UNIT) /\
(transformed[be].z - transformed[in].z);\
transformed[in].x = transformed[be].x - \
((transformed[be].x - transformed[in].x) * ratio) /\
S3L_FRACTIONS_PER_UNIT;\
transformed[in].y = transformed[be].y -\
((transformed[be].y - transformed[in].y) * ratio) /\
S3L_FRACTIONS_PER_UNIT;\
transformed[in].z = S3L_NEAR;
if (infront == 2)
{
// shift the two vertices forward along the edge
for (uint8_t i = 0; i < 2; ++i)
{
uint8_t be = behindI[0], in = infrontI[i];
interpolateVertex
}
}
else if (infront == 1)
{
// create another triangle and do the shifts
transformed[3] = transformed[behindI[1]];
transformed[4] = transformed[infrontI[0]];
transformed[5] = transformed[infrontI[0]];
for (uint8_t i = 0; i < 2; ++i)
{
uint8_t be = behindI[i], in = i + 4;
interpolateVertex
}
transformed[infrontI[0]] = transformed[4];
_S3L_mapProjectedVertexToScreen(&transformed[3],focalLength);
_S3L_mapProjectedVertexToScreen(&transformed[4],focalLength);
_S3L_mapProjectedVertexToScreen(&transformed[5],focalLength);
result = 1;
}
#undef interpolateVertex
#endif // S3L_NEAR_CROSS_STRATEGY == 2
_S3L_mapProjectedVertexToScreen(&transformed[0],focalLength);
_S3L_mapProjectedVertexToScreen(&transformed[1],focalLength);
_S3L_mapProjectedVertexToScreen(&transformed[2],focalLength);
return result;
} }
void S3L_drawScene(S3L_Scene scene) void S3L_drawScene(S3L_Scene scene)
{ {
S3L_Mat4 matFinal, matCamera; S3L_Mat4 matFinal, matCamera;
S3L_Vec4 transformed0, transformed1, transformed2; S3L_Vec4 transformed[6]; // transformed triangle coords, for 2 triangles
const S3L_Model3D *model; const S3L_Model3D *model;
S3L_Index modelIndex, triangleIndex; S3L_Index modelIndex, triangleIndex;
@ -2591,31 +2703,31 @@ void S3L_drawScene(S3L_Scene scene)
already projected vertices, but after some testing this was abandoned, already projected vertices, but after some testing this was abandoned,
no gain was seen. */ no gain was seen. */
_S3L_projectVertex(model,triangleIndex,0,&matFinal, uint8_t split = _S3L_projectTriangle(model,triangleIndex,&matFinal,
&transformed0,scene.camera.focalLength); scene.camera.focalLength,transformed);
_S3L_projectVertex(model,triangleIndex,1,&matFinal, if (S3L_triangleIsVisible(transformed[0],transformed[1],transformed[2],
&transformed1,scene.camera.focalLength);
_S3L_projectVertex(model,triangleIndex,2,&matFinal,
&transformed2,scene.camera.focalLength);
if (S3L_triangleIsVisible(transformed0,transformed1,transformed2,
model->config.backfaceCulling)) model->config.backfaceCulling))
{ {
#if S3L_SORT == 0 #if S3L_SORT == 0
// without sorting draw right away // without sorting draw right away
S3L_drawTriangle(transformed0,transformed1,transformed2,modelIndex, S3L_drawTriangle(transformed[0],transformed[1],transformed[2],modelIndex,
triangleIndex); triangleIndex);
if (split) // draw potential subtriangle
S3L_drawTriangle(transformed[3],transformed[4],transformed[5],
modelIndex, triangleIndex);
#else #else
S3L_UNUSED(split);
if (S3L_sortArrayLength >= S3L_MAX_TRIANGES_DRAWN) if (S3L_sortArrayLength >= S3L_MAX_TRIANGES_DRAWN)
break; break;
// with sorting add to a sort list // with sorting add to a sort list
S3L_sortArray[S3L_sortArrayLength].modelIndex = modelIndex; S3L_sortArray[S3L_sortArrayLength].modelIndex = modelIndex;
S3L_sortArray[S3L_sortArrayLength].triangleIndex = triangleIndex; S3L_sortArray[S3L_sortArrayLength].triangleIndex = triangleIndex;
S3L_sortArray[S3L_sortArrayLength].sortValue = S3L_sortArray[S3L_sortArrayLength].sortValue = S3L_zeroClamp(
S3L_zeroClamp(transformed0.w + transformed1.w + transformed2.w) >> 2; transformed[0].w + transformed[1].w + transformed[2].w) >> 2;
/* ^ /* ^
The w component here stores non-clamped z. The w component here stores non-clamped z.
@ -2661,7 +2773,7 @@ void S3L_drawScene(S3L_Scene scene)
#undef cmp #undef cmp
for (S3L_Index i = 0; i < S3L_sortArrayLength; ++i) for (S3L_Index i = 0; i < S3L_sortArrayLength; ++i) // draw sorted triangles
{ {
modelIndex = S3L_sortArray[i].modelIndex; modelIndex = S3L_sortArray[i].modelIndex;
triangleIndex = S3L_sortArray[i].triangleIndex; triangleIndex = S3L_sortArray[i].triangleIndex;
@ -2680,18 +2792,16 @@ void S3L_drawScene(S3L_Scene scene)
already been projected above, but saving the projected points would already been projected above, but saving the projected points would
require a lot of memory, which for small resolutions could be even require a lot of memory, which for small resolutions could be even
worse than z-bufer. So this seems to be the best way memory-wise. */ worse than z-bufer. So this seems to be the best way memory-wise. */
_S3L_projectVertex(model,triangleIndex,0,&matFinal,
&transformed0,scene.camera.focalLength);
_S3L_projectVertex(model,triangleIndex,1,&matFinal, uint8_t split = _S3L_projectTriangle(model,triangleIndex,&matFinal,
&transformed1,scene.camera.focalLength); scene.camera.focalLength,transformed);
_S3L_projectVertex(model,triangleIndex,2,&matFinal, S3L_drawTriangle(transformed[0],transformed[1],transformed[2],modelIndex,
&transformed2,scene.camera.focalLength);
S3L_drawTriangle(transformed0,transformed1,transformed2,modelIndex,
triangleIndex); triangleIndex);
if (split)
S3L_drawTriangle(transformed[3],transformed[4],transformed[5],
modelIndex, triangleIndex);
} }
#endif #endif
} }