Licar/renderer.h

642 lines
18 KiB
C
Raw Normal View History

2023-09-16 22:52:03 +02:00
/**
3D renderer: implements 3D rendering.
*/
2023-09-13 20:51:07 +02:00
#ifndef _LCR_RENDERER_H
#define _LCR_RENDERER_H
2023-09-16 20:35:01 +02:00
#define S3L_RESOLUTION_X LCR_SETTING_RESOLUTION_X
#define S3L_RESOLUTION_Y LCR_SETTING_RESOLUTION_Y
#define S3L_PIXEL_FUNCTION LCR_pixelFunc3D
2024-07-29 17:42:40 +02:00
#define S3L_PERSPECTIVE_CORRECTION 2
#define S3L_NEAR_CROSS_STRATEGY 0
2024-07-30 02:47:42 +02:00
#define S3L_Z_BUFFER 1
2023-09-17 15:42:46 +02:00
2023-09-16 20:35:01 +02:00
#include "small3dlib.h"
2024-07-24 20:28:57 +02:00
/// Renderer specific unit, length of one map square.
2024-07-23 19:57:29 +02:00
#define LCR_RENDERER_UNIT S3L_FRACTIONS_PER_UNIT
2024-07-30 02:47:42 +02:00
struct
2023-09-13 20:51:07 +02:00
{
2024-07-30 02:47:42 +02:00
S3L_Scene scene3D;
S3L_Model3D models3D[3]; // TODO
2023-09-13 20:51:07 +02:00
2024-07-30 02:47:42 +02:00
S3L_Model3D *mapModel;
S3L_Unit mapVertices[LCR_SETTING_MAX_MAP_VERTICES * 3];
S3L_Index mapTriangles[LCR_SETTING_MAX_MAP_TRIANGLES * 3];
2023-09-16 20:35:01 +02:00
2024-07-30 02:47:42 +02:00
// pixel function precomputed values:
int previousTriangleID;
int triangleUVs[6];
} LCR_renderer;
2024-07-29 17:42:40 +02:00
2023-09-16 20:35:01 +02:00
void LCR_pixelFunc3D(S3L_PixelInfo *pixel)
{
2024-07-29 17:42:40 +02:00
// once we get a new triangle, we precompute things for it:
2024-07-30 02:47:42 +02:00
if (pixel->triangleIndex != LCR_renderer.previousTriangleID)
2024-07-29 17:42:40 +02:00
{
2024-07-30 02:47:42 +02:00
S3L_Index *t = LCR_renderer.mapTriangles + 3 * pixel->triangleIndex;
2024-07-29 17:42:40 +02:00
S3L_Unit *v[3];
2024-07-30 02:47:42 +02:00
for (int i = 0; i < 3; ++i)
v[i] = LCR_renderer.mapVertices + 3 * t[i];
2024-07-29 17:42:40 +02:00
uint8_t type = // 0: floor, 1: wall, 2: wall 90 degrees
(v[0][0] == v[1][0] && v[1][0] == v[2][0]) +
2 * (v[0][2] == v[1][2] && v[1][2] == v[2][2]);
if (type == 0)
{
2024-07-30 02:47:42 +02:00
LCR_loadImage(3);
2024-07-29 17:42:40 +02:00
2024-07-30 02:47:42 +02:00
if (v[0][1] != v[1][1] || v[1][1] != v[2][1])
LCR_imageChangeBrightness(1);
2024-07-29 17:42:40 +02:00
for (int i = 0; i < 6; ++i)
2024-07-30 02:47:42 +02:00
LCR_renderer.triangleUVs[i] = ((
2024-07-29 17:42:40 +02:00
(v[i / 2][(i % 2) * 2]) *
LCR_IMAGE_SIZE) / LCR_RENDERER_UNIT);
}
else
{
2024-07-30 02:47:42 +02:00
LCR_loadImage(1);
2024-07-29 17:42:40 +02:00
2024-07-30 02:47:42 +02:00
if (type == 1)
LCR_imageChangeBrightness(0);
2024-07-29 17:42:40 +02:00
for (int i = 0; i < 6; ++i)
{
2024-07-30 02:47:42 +02:00
LCR_renderer.triangleUVs[i] = ((
2024-07-29 17:42:40 +02:00
(v[i / 2][i % 2 ? 1 : (type == 1 ? 2 : 0)]) *
LCR_IMAGE_SIZE) / LCR_RENDERER_UNIT);
if (i % 2)
2024-07-30 02:47:42 +02:00
LCR_renderer.triangleUVs[i] = LCR_IMAGE_SIZE -
LCR_renderer.triangleUVs[i];
2024-07-29 17:42:40 +02:00
}
}
2024-07-30 02:47:42 +02:00
LCR_renderer.previousTriangleID = pixel->triangleIndex;
2024-07-29 17:42:40 +02:00
}
int barycentric[3];
barycentric[0] = pixel->barycentric[0] / 8;
barycentric[1] = pixel->barycentric[1] / 8;
barycentric[2] = pixel->barycentric[2] / 8;
uint16_t color = LCR_sampleImage(
2024-07-30 02:47:42 +02:00
(barycentric[0] * LCR_renderer.triangleUVs[0] +
barycentric[1] * LCR_renderer.triangleUVs[2] +
barycentric[2] * LCR_renderer.triangleUVs[4])
2024-07-29 17:42:40 +02:00
/ (S3L_FRACTIONS_PER_UNIT / 8),
2024-07-30 02:47:42 +02:00
(barycentric[0] * LCR_renderer.triangleUVs[1] +
barycentric[1] * LCR_renderer.triangleUVs[3] +
barycentric[2] * LCR_renderer.triangleUVs[5])
2024-07-29 17:42:40 +02:00
/ (S3L_FRACTIONS_PER_UNIT / 8)
2024-07-30 02:47:42 +02:00
);
2024-07-29 17:42:40 +02:00
LCR_drawPixelXYUnsafe(pixel->x,pixel->y,color);
2024-07-22 01:16:16 +02:00
}
S3L_Index _LCR_addMapVertex(S3L_Unit x, S3L_Unit y, S3L_Unit z)
{
S3L_Index index = 0;
2024-07-30 02:47:42 +02:00
S3L_Unit *vertices = LCR_renderer.mapVertices;
2024-07-22 01:16:16 +02:00
2024-07-30 02:47:42 +02:00
while (index < LCR_renderer.mapModel->vertexCount) // if exists, return vertex index
2024-07-22 01:16:16 +02:00
{
if (vertices[0] == x && vertices[1] == y && vertices[2] == z)
return index;
vertices += 3;
index++;
}
// if it doesn't exist, add it
2024-07-30 02:47:42 +02:00
if (LCR_renderer.mapModel->vertexCount < LCR_SETTING_MAX_MAP_VERTICES)
2024-07-22 01:16:16 +02:00
{
*vertices = x;
vertices++;
*vertices = y;
vertices++;
*vertices = z;
2024-07-30 02:47:42 +02:00
LCR_renderer.mapModel->vertexCount++;
return LCR_renderer.mapModel->vertexCount - 1;
2024-07-22 01:16:16 +02:00
}
// TODO: error print
return 0;
}
void _LCR_addMapTriangle(S3L_Index a, S3L_Index b, S3L_Index c)
{
2024-07-30 02:47:42 +02:00
if (LCR_renderer.mapModel->triangleCount < LCR_SETTING_MAX_MAP_TRIANGLES)
2024-07-22 01:16:16 +02:00
{
2024-07-30 02:47:42 +02:00
S3L_Index *t = &(LCR_renderer.mapTriangles[LCR_renderer.mapModel->triangleCount * 3]);
2024-07-22 01:16:16 +02:00
2024-07-31 16:07:25 +02:00
*t = a; t++;
*t = b; t++;
2024-07-22 01:16:16 +02:00
*t = c;
2024-07-30 02:47:42 +02:00
LCR_renderer.mapModel->triangleCount++;
2024-07-22 01:16:16 +02:00
}
2023-09-16 20:35:01 +02:00
}
2024-07-24 23:16:13 +02:00
void _LCR_rendererSwapTriangles(S3L_Index *t1, S3L_Index *t2)
{
S3L_Index tmp;
for (int i = 0; i < 3; ++i)
{
tmp = t1[i];
t1[i] = t2[i];
t2[i] = tmp;
}
}
2024-07-29 17:42:40 +02:00
int _LCR_quadCoversTriangle(const S3L_Unit quad[8], const S3L_Unit tri[6])
{
for (int i = 0; i < 3; ++i) // for each triangle point
{
int covered = 0;
for (int j = 0; j < 3; ++j) // for each quad subtriangle
{
uint8_t winds = 0;
for (int k = 0; k < 3; ++k) // for each subtriangle side
{
S3L_Unit w = // triangle winding
(quad[(2 * (j + ((k + 1) % 3))) % 8 + 1] -
quad[(2 * (j + k)) % 8 + 1]) *
(tri[2 * i] - quad[(2 * (j + (k + 1) % 3)) % 8]) -
(quad[(2 * (j + ((k + 1) % 3))) % 8] - quad[(2 * (j + k)) % 8])
* (tri[2 * i + 1] - quad[(2 * (j + (k + 1) % 3)) % 8 + 1]);
if (w > 0)
winds |= 1;
else if (w < 0)
winds |= 2;
}
if (winds != 3) // no opposite winds?
{
covered = 1;
break;
}
}
if (!covered)
return 0;
}
return 1;
}
2024-07-24 23:16:13 +02:00
/**
Checks whether two triangles (and potenrially their neighbors) cover each
other, in return values lowest bit means whether t1 is covered and the second
lowest bit means whether t2 is covered.
*/
uint8_t _LCR_rendererCheckMapTriangleCover(const S3L_Index *t1,
const S3L_Index *t2)
{
if ((t1[0] == t2[0] || t1[0] == t2[1] || t1[0] == t2[2]) &&
(t1[1] == t2[0] || t1[1] == t2[1] || t1[1] == t2[2]) &&
(t1[2] == t2[0] || t1[2] == t2[1] || t1[2] == t2[2]))
2024-07-28 02:00:36 +02:00
return 0x03;
2024-07-24 23:16:13 +02:00
2024-07-28 02:00:36 +02:00
uint8_t result = 0;
int plane = -1;
for (int i = 0; i < 3; ++i)
2024-07-30 02:47:42 +02:00
if (LCR_renderer.mapVertices[3 * t1[0] + i] == LCR_renderer.mapVertices[3 * t1[1] + i] &&
LCR_renderer.mapVertices[3 * t1[1] + i] == LCR_renderer.mapVertices[3 * t1[2] + i] &&
LCR_renderer.mapVertices[3 * t1[2] + i] == LCR_renderer.mapVertices[3 * t2[0] + i] &&
LCR_renderer.mapVertices[3 * t2[0] + i] == LCR_renderer.mapVertices[3 * t2[1] + i] &&
LCR_renderer.mapVertices[3 * t2[1] + i] == LCR_renderer.mapVertices[3 * t2[2] + i])
2024-07-28 02:00:36 +02:00
{
plane = i;
break;
}
if (plane >= 0) // both triangles in the same plane => then do more checks
{
for (int j = 0; j < 2; ++j)
{
S3L_Unit points2D[14]; // tri1, quad (tri2 + 1 extra vert)
int coordX = plane == 0 ? 1 : 0,
coordY = plane == 2 ? 1 : 2;
for (int i = 0; i < 3; ++i)
{
2024-07-30 02:47:42 +02:00
points2D[i * 2] = LCR_renderer.mapVertices[3 * t1[i] + coordX];
points2D[i * 2 + 1] = LCR_renderer.mapVertices[3 * t1[i] + coordY];
points2D[6 + i * 2] = LCR_renderer.mapVertices[3 * t2[i] + coordX];
points2D[6 + i * 2 + 1] = LCR_renderer.mapVertices[3 * t2[i] + coordY];
2024-07-28 02:00:36 +02:00
}
points2D[12] = (4 * points2D[6] + 3 * points2D[8] + points2D[10]) / 8;
points2D[13] = (4 * points2D[7] + 3 * points2D[9] + points2D[11]) / 8;
// first: does the triangle alone cover the other one?
if (_LCR_quadCoversTriangle(points2D + 6,points2D))
result |= 1 << j;
else
{
// now check if this triangle along with a neighbor cover the other one
2024-07-30 02:47:42 +02:00
S3L_Index *t3 = LCR_renderer.mapTriangles;
2024-07-28 02:00:36 +02:00
2024-07-30 02:47:42 +02:00
for (int i = 0; i < LCR_renderer.mapModel->triangleCount; ++i)
2024-07-28 02:00:36 +02:00
{
uint8_t sharedVerts =
(t3[0] == t2[0] || t3[0] == t2[1] || t3[0] == t2[2]) |
((t3[1] == t2[0] || t3[1] == t2[1] || t3[1] == t2[2]) << 1) |
((t3[2] == t2[0] || t3[2] == t2[1] || t3[2] == t2[2]) << 2);
if (
t3 != t1 && t3 != t2 &&
(sharedVerts == 3 || sharedVerts == 5 || sharedVerts == 6) &&
2024-07-30 02:47:42 +02:00
LCR_renderer.mapVertices[3 * t3[0] + plane] ==
LCR_renderer.mapVertices[3 * t3[1] + plane] &&
LCR_renderer.mapVertices[3 * t3[1] + plane] ==
LCR_renderer.mapVertices[3 * t3[2] + plane] &&
LCR_renderer.mapVertices[3 * t3[0] + plane] ==
LCR_renderer.mapVertices[3 * t1[0] + plane]
2024-07-28 02:00:36 +02:00
)
{
// here shares exactly two vertices and is in the same plane
uint8_t freeVert =
sharedVerts == 3 ? 2 : (sharedVerts == 5 ? 1 : 0);
2024-07-30 02:47:42 +02:00
points2D[12] = LCR_renderer.mapVertices[3 * t3[freeVert] + coordX];
points2D[13] = LCR_renderer.mapVertices[3 * t3[freeVert] + coordY];
2024-07-28 02:00:36 +02:00
if (_LCR_quadCoversTriangle(points2D + 6,points2D))
{
result |= 1 << j;
break;
}
}
t3 += 3;
}
}
const S3L_Index *tmp = t1;
t1 = t2;
t2 = tmp;
}
}
return result;
}
2024-07-24 23:16:13 +02:00
/**
Removes map triangles that are covered by other triangles (and also vertices
that by this become unused). This makes the map model smaller, faster and
prevents bleeding through due to z-bugger imprecisions.
*/
2024-07-30 21:47:50 +02:00
void _LCR_rendererCullHiddenMapTriangles(void)
2024-07-24 23:16:13 +02:00
{
int n = 0; // number of removed elements
int i = 0;
2024-07-30 02:47:42 +02:00
S3L_Index *t1 = LCR_renderer.mapTriangles, *t2;
2024-07-24 23:16:13 +02:00
/*
We'll be moving the covered triangles to the end of the array, then at the
2024-07-31 16:07:25 +02:00
end we'll just shorten the array by number of removed triangles.
2024-07-24 23:16:13 +02:00
*/
2024-07-30 02:47:42 +02:00
while (i < LCR_renderer.mapModel->triangleCount - n)
2024-07-24 23:16:13 +02:00
{
2024-07-31 16:07:25 +02:00
t2 = t1 + 3; // t2 is the the other triangle against which we check
2024-07-24 23:16:13 +02:00
int t1Covered = 0;
2024-07-30 02:47:42 +02:00
for (int j = i + 1; j < LCR_renderer.mapModel->triangleCount; ++j)
2024-07-24 23:16:13 +02:00
{
uint8_t cover = _LCR_rendererCheckMapTriangleCover(t1,t2);
t1Covered |= cover & 0x01;
if (cover & 0x02)
{
2024-07-30 02:47:42 +02:00
if (j < LCR_renderer.mapModel->triangleCount - n)
2024-07-24 23:16:13 +02:00
{
2024-07-31 16:07:25 +02:00
_LCR_rendererSwapTriangles(t2,LCR_renderer.mapTriangles +
(LCR_renderer.mapModel->triangleCount - 1 - n) * 3);
2024-07-24 23:16:13 +02:00
n++;
}
}
2024-07-31 16:07:25 +02:00
t2 += 3; // check next triangle
2024-07-24 23:16:13 +02:00
}
if (t1Covered)
{
2024-07-31 16:07:25 +02:00
_LCR_rendererSwapTriangles(t1,LCR_renderer.mapTriangles +
(LCR_renderer.mapModel->triangleCount - 1 - n) * 3);
2024-07-24 23:16:13 +02:00
n++;
2024-07-31 16:07:25 +02:00
// we stay at this position because we've swapped the triangle here
2024-07-24 23:16:13 +02:00
}
else
{
t1 += 3;
i++;
}
}
2024-07-31 16:07:25 +02:00
LCR_renderer.mapModel->triangleCount -= n; // cut off the removed triangles
2024-07-24 23:16:13 +02:00
// remove unused vertices:
i = 0;
2024-07-30 02:47:42 +02:00
while (i < LCR_renderer.mapModel->vertexCount)
2024-07-24 23:16:13 +02:00
{
int used = 0;
2024-07-30 02:47:42 +02:00
for (int j = 0; j < LCR_renderer.mapModel->triangleCount * 3; ++j)
if (LCR_renderer.mapTriangles[j] == i)
2024-07-24 23:16:13 +02:00
{
used = 1;
break;
}
if (used)
i++;
else
{
for (int j = 0; j < 3; ++j)
2024-07-30 02:47:42 +02:00
LCR_renderer.mapVertices[3 * i + j] =
LCR_renderer.mapVertices[(LCR_renderer.mapModel->vertexCount - 1) * 3 + j];
2024-07-24 23:16:13 +02:00
2024-07-30 02:47:42 +02:00
for (int j = 0; j < LCR_renderer.mapModel->triangleCount * 3; ++j)
if (LCR_renderer.mapTriangles[j] == LCR_renderer.mapModel->vertexCount - 1)
LCR_renderer.mapTriangles[j] = i;
2024-07-24 23:16:13 +02:00
2024-07-30 02:47:42 +02:00
LCR_renderer.mapModel->vertexCount--;
2024-07-24 23:16:13 +02:00
}
}
}
2024-07-24 20:28:57 +02:00
/**
2024-07-30 02:47:42 +02:00
Builds the internal 3D model of the currently loaded map. Returns 1 on
success, 0 otherwise (e.g. not enough space).
2024-07-24 20:28:57 +02:00
*/
2024-07-22 01:16:16 +02:00
uint8_t _LCR_rendererBuildMapModel(void)
2023-09-16 20:35:01 +02:00
{
2024-07-22 01:16:16 +02:00
uint8_t blockShapeBytes[LCR_MAP_BLOCK_SHAPE_MAX_BYTES];
uint8_t blockShapeByteCount;
2024-07-30 02:47:42 +02:00
S3L_model3DInit(LCR_renderer.mapVertices,0,LCR_renderer.mapTriangles,0,LCR_renderer.mapModel);
2024-07-22 01:16:16 +02:00
2024-07-24 20:28:57 +02:00
for (int j = 0; j < LCR_currentMap.blockCount; ++j)
2024-07-22 01:16:16 +02:00
{
2024-07-24 20:28:57 +02:00
uint8_t bx, by, bz;
2024-07-24 23:16:13 +02:00
LCR_mapBlockGetCoords(LCR_currentMap.blocks + j * LCR_BLOCK_SIZE,
&bx,&by,&bz);
2024-07-24 20:28:57 +02:00
2024-07-24 23:16:13 +02:00
LCR_mapGetBlockShape(LCR_currentMap.blocks[j * LCR_BLOCK_SIZE],
LCR_mapBlockGetTransform(LCR_currentMap.blocks + j * LCR_BLOCK_SIZE),
2024-07-24 20:28:57 +02:00
blockShapeBytes,&blockShapeByteCount);
2024-07-31 16:07:25 +02:00
uint8_t vx, vy, vz, vi = 0,
isAtFloor = by == 0,
isAtCeiling = by == LCR_MAP_SIZE_BLOCKS - 1;
2024-07-24 20:28:57 +02:00
S3L_Index triangleIndices[3];
S3L_Unit originOffset = -1 * LCR_MAP_SIZE_BLOCKS / 2 * LCR_RENDERER_UNIT;
for (int i = 0; i < blockShapeByteCount; ++i)
{
LCR_decodeMapBlockCoords(blockShapeBytes[i],&vx,&vy,&vz);
2024-07-31 16:07:25 +02:00
isAtFloor &= vy == 0;
isAtCeiling &= vy == 12;
2024-07-24 20:28:57 +02:00
triangleIndices[vi] = _LCR_addMapVertex(
originOffset + (((S3L_Unit) bx) * LCR_RENDERER_UNIT) + (LCR_RENDERER_UNIT * ((S3L_Unit) vx)) / 12,
(originOffset + (((S3L_Unit) by) * LCR_RENDERER_UNIT)) / 2 + (LCR_RENDERER_UNIT / 2 * ((S3L_Unit) vy)) / 12,
originOffset + (((S3L_Unit) bz) * LCR_RENDERER_UNIT) + (LCR_RENDERER_UNIT * ((S3L_Unit) vz)) / 12);
if (vi < 2)
vi++;
else
{
2024-07-31 16:07:25 +02:00
// don't add triangles completely at the floor or ceiling of the map
if (!isAtFloor && !isAtCeiling)
_LCR_addMapTriangle(
triangleIndices[0],triangleIndices[1],triangleIndices[2]);
2024-07-24 20:28:57 +02:00
vi = 0;
2024-07-31 16:07:25 +02:00
isAtFloor = by == 0;
isAtCeiling = by == LCR_MAP_SIZE_BLOCKS - 1;
2024-07-24 20:28:57 +02:00
}
}
2024-07-22 01:16:16 +02:00
}
2023-09-16 20:35:01 +02:00
2024-07-31 16:07:25 +02:00
// TODO: also cull the triangles in the loop by some N steps
2024-07-30 21:47:50 +02:00
_LCR_rendererCullHiddenMapTriangles();
2024-07-24 23:16:13 +02:00
2023-09-16 20:35:01 +02:00
return 1;
}
uint8_t LCR_rendererInit(void)
{
2024-07-30 02:47:42 +02:00
LCR_renderer.mapModel = LCR_renderer.models3D;
2024-07-22 01:16:16 +02:00
if (!_LCR_rendererBuildMapModel())
2023-09-16 20:35:01 +02:00
return 0;
2024-07-30 02:47:42 +02:00
S3L_sceneInit(LCR_renderer.models3D,1,&LCR_renderer.scene3D);
2023-09-16 20:35:01 +02:00
return 1;
}
2023-09-17 13:21:19 +02:00
void LCR_rendererMoveCamera(LCR_SpaceUnit forwRightUpOffset[3],
LCR_SpaceUnit yawPitchOffset[2])
{
S3L_Vec4 f, r, u;
2024-07-30 02:47:42 +02:00
S3L_rotationToDirections(LCR_renderer.scene3D.camera.transform.rotation,
2023-09-17 13:21:19 +02:00
S3L_FRACTIONS_PER_UNIT,&f,&r,&u);
2024-07-30 02:47:42 +02:00
LCR_renderer.scene3D.camera.transform.translation.x +=
2023-09-17 13:21:19 +02:00
((f.x * forwRightUpOffset[0] + r.x * forwRightUpOffset[1] +
u.x * forwRightUpOffset[2]) * S3L_FRACTIONS_PER_UNIT) / LCR_SQUARE_SIDE_LEN;
2024-07-30 02:47:42 +02:00
LCR_renderer.scene3D.camera.transform.translation.y +=
2023-09-17 13:21:19 +02:00
((f.y * forwRightUpOffset[0] + r.y * forwRightUpOffset[1] +
u.y * forwRightUpOffset[2]) * S3L_FRACTIONS_PER_UNIT) / LCR_SQUARE_SIDE_LEN;
2024-07-30 02:47:42 +02:00
LCR_renderer.scene3D.camera.transform.translation.z +=
2023-09-17 13:21:19 +02:00
((f.z * forwRightUpOffset[0] + r.z * forwRightUpOffset[1] +
u.z * forwRightUpOffset[2]) * S3L_FRACTIONS_PER_UNIT) / LCR_SQUARE_SIDE_LEN;
2024-07-30 02:47:42 +02:00
LCR_renderer.scene3D.camera.transform.rotation.y +=
2023-09-17 13:21:19 +02:00
(yawPitchOffset[0] * S3L_FRACTIONS_PER_UNIT) / LCR_SQUARE_SIDE_LEN;
2024-07-30 02:47:42 +02:00
LCR_renderer.scene3D.camera.transform.rotation.x +=
2023-09-17 13:21:19 +02:00
(yawPitchOffset[1] * S3L_FRACTIONS_PER_UNIT) / LCR_SQUARE_SIDE_LEN;
2024-07-30 02:47:42 +02:00
if (LCR_renderer.scene3D.camera.transform.rotation.x > S3L_FRACTIONS_PER_UNIT / 4)
LCR_renderer.scene3D.camera.transform.rotation.x = S3L_FRACTIONS_PER_UNIT / 4;
2023-09-16 22:52:03 +02:00
2024-07-30 02:47:42 +02:00
if (LCR_renderer.scene3D.camera.transform.rotation.x < -1 * S3L_FRACTIONS_PER_UNIT / 4)
LCR_renderer.scene3D.camera.transform.rotation.x = -1 * S3L_FRACTIONS_PER_UNIT / 4;
2023-09-16 22:52:03 +02:00
}
2024-07-30 02:47:42 +02:00
/**
Draws background sky, offsets are in multiples of screen dimensions
(e.g. S3L_FRACTIONS_PER_UNIT / 2 for offsetH means half the screen width).
*/
void LCR_rendererDrawSky(int sky, S3L_Unit offsetH, S3L_Unit offsetV)
2023-09-16 20:35:01 +02:00
{
2024-07-30 02:47:42 +02:00
int anchorPoint[2], y;
unsigned long pixelIndex;
unsigned int topColor, bottomColor;
2023-09-16 20:35:01 +02:00
2024-07-30 21:47:50 +02:00
sky = 8 + 4 * sky;
LCR_loadImage(sky);
2024-07-30 02:47:42 +02:00
topColor = LCR_sampleImage(0,0);
2024-07-22 01:16:16 +02:00
2024-07-30 21:47:50 +02:00
LCR_loadImage(sky + 3);
2024-07-30 02:47:42 +02:00
bottomColor = LCR_sampleImage(LCR_IMAGE_SIZE - 1,LCR_IMAGE_SIZE - 1);
2024-07-22 01:16:16 +02:00
2024-07-30 02:47:42 +02:00
anchorPoint[0] = ((LCR_EFFECTIVE_RESOLUTION_X * offsetH)
/ S3L_FRACTIONS_PER_UNIT) %
(2 * LCR_IMAGE_SIZE * LCR_SETTING_SKY_SIZE);
2024-07-22 01:16:16 +02:00
2024-07-30 02:47:42 +02:00
if (anchorPoint[0] < 0)
anchorPoint[0] += 2 * LCR_IMAGE_SIZE * LCR_SETTING_SKY_SIZE;
2023-09-13 20:51:07 +02:00
2024-07-30 02:47:42 +02:00
anchorPoint[1] =
2024-07-30 21:47:50 +02:00
(LCR_EFFECTIVE_RESOLUTION_Y) / 3 - // 3: we place the center a bit more up
2024-07-30 02:47:42 +02:00
(LCR_EFFECTIVE_RESOLUTION_Y * offsetV) / S3L_FRACTIONS_PER_UNIT
- LCR_IMAGE_SIZE * LCR_SETTING_SKY_SIZE;
pixelIndex = 0;
y = anchorPoint[1] < 0 ? anchorPoint[1] : 0;
2023-09-13 20:51:07 +02:00
2024-07-30 02:47:42 +02:00
while (y < anchorPoint[1] && y < LCR_EFFECTIVE_RESOLUTION_Y) // top strip
{
for (int x = 0; x < LCR_EFFECTIVE_RESOLUTION_X; ++x)
{
LCR_drawPixel(pixelIndex,topColor);
pixelIndex++;
}
y++;
}
anchorPoint[1] += 2 * LCR_IMAGE_SIZE * LCR_SETTING_SKY_SIZE;
int linesLeft = 0;
int skyPart = 0;
while (y < anchorPoint[1] && y < LCR_EFFECTIVE_RESOLUTION_Y) // image strip
{
if (!linesLeft)
{
2024-07-30 21:47:50 +02:00
LCR_loadImage(sky + skyPart);
2024-07-30 02:47:42 +02:00
linesLeft = LCR_IMAGE_SIZE / 2;
skyPart++;
}
if (y >= 0)
{
for (int ix = 0; ix < 2 * LCR_IMAGE_SIZE * LCR_SETTING_SKY_SIZE;
ix += LCR_SETTING_SKY_SIZE)
{
unsigned int color = LCR_getNextImagePixel();
2024-07-30 21:47:50 +02:00
unsigned long startIndex = pixelIndex;
2024-07-30 02:47:42 +02:00
2024-07-30 21:47:50 +02:00
for (int k = 0; k < LCR_SETTING_SKY_SIZE; ++k)
{
if (y + k >= LCR_EFFECTIVE_RESOLUTION_Y)
break;
2024-07-30 02:47:42 +02:00
2024-07-30 21:47:50 +02:00
for (int j = 0; j < LCR_SETTING_SKY_SIZE; ++j)
{
int x = anchorPoint[0] + ix + j;
2024-07-30 02:47:42 +02:00
2024-07-30 21:47:50 +02:00
if (x >= 2 * LCR_IMAGE_SIZE * LCR_SETTING_SKY_SIZE)
x -= 2 * LCR_IMAGE_SIZE * LCR_SETTING_SKY_SIZE;
while (x < LCR_EFFECTIVE_RESOLUTION_X)
{
LCR_drawPixel(startIndex + x,color);
x += 2 * LCR_IMAGE_SIZE * LCR_SETTING_SKY_SIZE;
}
}
startIndex += LCR_EFFECTIVE_RESOLUTION_X;
2024-07-30 02:47:42 +02:00
}
}
pixelIndex += LCR_EFFECTIVE_RESOLUTION_X * LCR_SETTING_SKY_SIZE;
2024-07-30 21:47:50 +02:00
y += LCR_SETTING_SKY_SIZE;
2024-07-30 02:47:42 +02:00
}
else
2024-07-30 21:47:50 +02:00
{
2024-07-30 02:47:42 +02:00
for (int ix = 0; ix < 2 * LCR_IMAGE_SIZE; ++ix)
LCR_getNextImagePixel();
2024-07-30 21:47:50 +02:00
for (int i = 0; i < LCR_SETTING_SKY_SIZE; ++i)
{
if (y >= 0)
for (int x = 0; x < LCR_EFFECTIVE_RESOLUTION_X; ++x)
{
LCR_drawPixel(pixelIndex,topColor);
pixelIndex++;
}
y++;
}
}
2024-07-30 02:47:42 +02:00
linesLeft--;
}
while (y < 0) // can still be the case
y = 0;
while (y < LCR_EFFECTIVE_RESOLUTION_Y) // bottom strip
{
for (int x = 0; x < LCR_EFFECTIVE_RESOLUTION_X; ++x)
{
LCR_drawPixel(pixelIndex,bottomColor);
pixelIndex++;
}
y++;
}
2023-09-17 15:42:46 +02:00
}
2024-07-30 02:47:42 +02:00
void LCR_rendererDraw(void)
2023-09-17 15:42:46 +02:00
{
2024-07-30 02:47:42 +02:00
LCR_renderer.previousTriangleID = -1;
S3L_newFrame();
2024-07-30 21:47:50 +02:00
LCR_rendererDrawSky(2,
LCR_renderer.scene3D.camera.transform.rotation.y,
2024-07-30 02:47:42 +02:00
-4 * LCR_renderer.scene3D.camera.transform.rotation.x);
S3L_drawScene(LCR_renderer.scene3D);
2023-09-17 15:42:46 +02:00
}
2023-09-13 20:51:07 +02:00
#endif // guard