Licar/renderer.h

399 lines
10 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
2023-09-17 15:42:46 +02:00
#define S3L_Z_BUFFER 2
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
2023-09-13 20:51:07 +02:00
struct LCR_Renderer
{
// TODO
};
2023-09-16 20:35:01 +02:00
S3L_Scene LCR_scene3D;
S3L_Model3D LCR_models3D[3]; // TODO
2024-07-22 01:16:16 +02:00
S3L_Model3D *LCR_mapModel;
S3L_Unit LCR_mapVertices[LCR_SETTING_MAX_MAP_VERTICES * 3];
S3L_Index LCR_mapTriangles[LCR_SETTING_MAX_MAP_TRIANGLES * 3];
2023-09-16 20:35:01 +02:00
void LCR_pixelFunc3D(S3L_PixelInfo *pixel)
{
2024-07-24 20:28:57 +02:00
LCR_drawPixelXYSafe(pixel->x,pixel->y,0xff00 + (pixel->triangleID % 16) * 16 );
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;
S3L_Unit *vertices = LCR_mapVertices;
while (index < LCR_mapModel->vertexCount) // if exists, return vertex index
{
if (vertices[0] == x && vertices[1] == y && vertices[2] == z)
return index;
vertices += 3;
index++;
}
// if it doesn't exist, add it
if (LCR_mapModel->vertexCount < LCR_SETTING_MAX_MAP_VERTICES)
{
*vertices = x;
vertices++;
*vertices = y;
vertices++;
*vertices = z;
LCR_mapModel->vertexCount++;
return LCR_mapModel->vertexCount - 1;
}
// TODO: error print
return 0;
}
void _LCR_addMapTriangle(S3L_Index a, S3L_Index b, S3L_Index c)
{
if (LCR_mapModel->triangleCount < LCR_SETTING_MAX_MAP_TRIANGLES)
{
2024-07-24 20:28:57 +02:00
S3L_Index *t = &(LCR_mapTriangles[LCR_mapModel->triangleCount * 3]);
2024-07-22 01:16:16 +02:00
*t = a;
t++;
*t = b;
t++;
*t = c;
LCR_mapModel->triangleCount++;
}
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;
}
}
/**
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]))
return 0x03; // same indices => both are covered
return 0;
}
/**
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.
*/
void _LCR_rendererPruneHiddenMapTriangles(void)
{
int n = 0; // number of removed elements
int i = 0;
S3L_Index *t1 = LCR_mapTriangles, *t2;
/*
We'll be moving the covered triangles to the end of the array, then at the
end we'll just shorten the array.
*/
while (i < LCR_mapModel->triangleCount - n)
{
t2 = t1 + 3;
int t1Covered = 0;
for (int j = i + 1; j < LCR_mapModel->triangleCount; ++j)
{
uint8_t cover = _LCR_rendererCheckMapTriangleCover(t1,t2);
t1Covered |= cover & 0x01;
if (cover & 0x02)
{
if (j < LCR_mapModel->triangleCount - n)
{
_LCR_rendererSwapTriangles(t2,
LCR_mapTriangles + (LCR_mapModel->triangleCount - 1 - n) * 3);
n++;
}
}
t2 += 3;
}
if (t1Covered)
{
_LCR_rendererSwapTriangles(t1,
LCR_mapTriangles + (LCR_mapModel->triangleCount - 1 - n) * 3);
n++;
}
else
{
t1 += 3;
i++;
}
}
LCR_mapModel->triangleCount -= n;
// remove unused vertices:
i = 0;
printf("ASASA %d\n",LCR_mapModel->vertexCount);
while (i < LCR_mapModel->vertexCount)
{
int used = 0;
for (int j = 0; j < LCR_mapModel->triangleCount * 3; ++j)
if (LCR_mapTriangles[j] == i)
{
used = 1;
break;
}
if (used)
i++;
else
{
for (int j = 0; j < 3; ++j)
LCR_mapVertices[3 * i + j] =
LCR_mapVertices[(LCR_mapModel->vertexCount - 1) * 3 + j];
for (int j = 0; j < LCR_mapModel->triangleCount * 3; ++j)
if (LCR_mapTriangles[j] == LCR_mapModel->vertexCount - 1)
LCR_mapTriangles[j] = i;
LCR_mapModel->vertexCount--;
}
}
printf("ASASA %d\n",LCR_mapModel->vertexCount);
}
2024-07-24 20:28:57 +02:00
/**
Builds an internal 3D model of the currently loaded map. Returns 1 on success,
otherwise 0 (e.g. not enough space).
*/
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;
S3L_model3DInit(LCR_mapVertices,0,LCR_mapTriangles,0,LCR_mapModel);
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);
uint8_t vx, vy, vz, vi = 0;
uint8_t *bytes = blockShapeBytes;
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);
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
{
_LCR_addMapTriangle(
triangleIndices[0],triangleIndices[1],triangleIndices[2]);
vi = 0;
}
}
2024-07-22 01:16:16 +02:00
}
2023-09-16 20:35:01 +02:00
2024-07-24 23:16:13 +02:00
_LCR_rendererPruneHiddenMapTriangles();
2023-09-16 20:35:01 +02:00
return 1;
}
uint8_t LCR_rendererInit(void)
{
2024-07-22 01:16:16 +02:00
LCR_mapModel = LCR_models3D;
if (!_LCR_rendererBuildMapModel())
2023-09-16 20:35:01 +02:00
return 0;
S3L_sceneInit(LCR_models3D,1,&LCR_scene3D);
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;
S3L_rotationToDirections(LCR_scene3D.camera.transform.rotation,
S3L_FRACTIONS_PER_UNIT,&f,&r,&u);
LCR_scene3D.camera.transform.translation.x +=
((f.x * forwRightUpOffset[0] + r.x * forwRightUpOffset[1] +
u.x * forwRightUpOffset[2]) * S3L_FRACTIONS_PER_UNIT) / LCR_SQUARE_SIDE_LEN;
LCR_scene3D.camera.transform.translation.y +=
((f.y * forwRightUpOffset[0] + r.y * forwRightUpOffset[1] +
u.y * forwRightUpOffset[2]) * S3L_FRACTIONS_PER_UNIT) / LCR_SQUARE_SIDE_LEN;
LCR_scene3D.camera.transform.translation.z +=
((f.z * forwRightUpOffset[0] + r.z * forwRightUpOffset[1] +
u.z * forwRightUpOffset[2]) * S3L_FRACTIONS_PER_UNIT) / LCR_SQUARE_SIDE_LEN;
LCR_scene3D.camera.transform.rotation.y +=
(yawPitchOffset[0] * S3L_FRACTIONS_PER_UNIT) / LCR_SQUARE_SIDE_LEN;
LCR_scene3D.camera.transform.rotation.x +=
(yawPitchOffset[1] * S3L_FRACTIONS_PER_UNIT) / LCR_SQUARE_SIDE_LEN;
2023-09-17 15:42:46 +02:00
if (LCR_scene3D.camera.transform.rotation.x > S3L_FRACTIONS_PER_UNIT / 4)
LCR_scene3D.camera.transform.rotation.x = S3L_FRACTIONS_PER_UNIT / 4;
2023-09-16 22:52:03 +02:00
2023-09-17 15:42:46 +02:00
if (LCR_scene3D.camera.transform.rotation.x < -1 * S3L_FRACTIONS_PER_UNIT / 4)
LCR_scene3D.camera.transform.rotation.x = -1 * S3L_FRACTIONS_PER_UNIT / 4;
2023-09-16 22:52:03 +02:00
}
2023-09-17 13:21:19 +02:00
void LCR_rendererDraw(void)
2023-09-16 20:35:01 +02:00
{
S3L_newFrame();
S3L_drawScene(LCR_scene3D);
}
2023-09-13 20:51:07 +02:00
void LCR_drawBackground(int verticalOffset)
{
2024-07-22 01:16:16 +02:00
// TODO
for (int y = 0; y < LCR_EFFECTIVE_RESOLUTION_Y; ++y)
for (int x = 0; x < LCR_EFFECTIVE_RESOLUTION_X; ++x)
LCR_drawPixelXYUnsafe(x,y,0xffff);
/*
2023-09-13 20:51:07 +02:00
uint16_t color = LCR_skyImages[LCR_skyImages[256] & 0x00ff] ; // TODO
int limit = verticalOffset - LCR_SETTING_SKY_SIZE * LCR_SKY_IMAGE_SIZE / 2;
if (limit >= LCR_EFFECTIVE_RESOLUTION_Y)
limit = LCR_EFFECTIVE_RESOLUTION_Y - 1;
for (int y = 0; y <= limit; ++y)
for (int x = 0; x < LCR_EFFECTIVE_RESOLUTION_X; ++x)
LCR_drawPixelUnsafe(x,y,color);
color = LCR_skyImages[LCR_skyImages[255 + (128 * 128) / 2] >> 8]; // TODO
limit = verticalOffset + LCR_SETTING_SKY_SIZE * LCR_SKY_IMAGE_SIZE / 2 -
LCR_SETTING_SKY_SIZE;
if (limit < 0)
limit = 0;
for (int y = LCR_EFFECTIVE_RESOLUTION_Y - 1; y >= limit; --y)
for (int x = 0; x < LCR_EFFECTIVE_RESOLUTION_X; ++x)
LCR_drawPixelUnsafe(x,y,color);
2024-07-22 01:16:16 +02:00
*/
2023-09-13 20:51:07 +02:00
}
void LCR_drawSkyStrip(int verticalOffset, uint8_t horizontalOffset)
{
#if LCR_SETTING_SKY_SIZE != 0
2024-07-22 01:16:16 +02:00
/*
2023-09-13 20:51:07 +02:00
verticalOffset -= (LCR_SETTING_SKY_SIZE * LCR_SKY_IMAGE_SIZE) / 2;
int finalY = verticalOffset + LCR_SETTING_SKY_SIZE * LCR_SKY_IMAGE_SIZE;
finalY = (finalY / LCR_SETTING_SKY_SIZE) * LCR_SETTING_SKY_SIZE;
if (finalY >= LCR_EFFECTIVE_RESOLUTION_Y)
finalY = LCR_EFFECTIVE_RESOLUTION_Y - 1;
const uint16_t *skyLine = LCR_skyImages + 256;
if (verticalOffset < 0)
{
skyLine += (-1 * verticalOffset / LCR_SETTING_SKY_SIZE) * (LCR_SKY_IMAGE_SIZE / 2);
verticalOffset = 0;
}
while (verticalOffset < finalY)
{
for (int i = 0; i < LCR_EFFECTIVE_RESOLUTION_X; ++i)
{
2023-09-17 15:42:46 +02:00
// only diraw background on empty pixels
if (S3L_zBufferRead(i,verticalOffset) == S3L_MAX_DEPTH)
{
int offsetX = (i / LCR_SETTING_SKY_SIZE + horizontalOffset)
% LCR_SKY_IMAGE_SIZE;
2023-09-13 20:51:07 +02:00
2023-09-17 15:42:46 +02:00
uint16_t pixel = *(skyLine + offsetX / 2);
2023-09-13 20:51:07 +02:00
2023-09-17 15:42:46 +02:00
pixel = offsetX % 2 ? (pixel >> 8) : (pixel & 0x00ff);
2023-09-13 20:51:07 +02:00
2023-09-17 15:42:46 +02:00
LCR_drawPixelUnsafe(i,verticalOffset,LCR_skyImages[pixel]);
}
2023-09-13 20:51:07 +02:00
}
verticalOffset++;
if (verticalOffset % LCR_SETTING_SKY_SIZE == 0)
skyLine += LCR_SKY_IMAGE_SIZE / 2;
}
2024-07-22 01:16:16 +02:00
*/
2023-09-13 20:51:07 +02:00
#endif
}
2023-09-17 15:42:46 +02:00
LCR_SpaceUnit LCR_rendererGetCameraYaw(void)
{
return (LCR_scene3D.camera.transform.rotation.y * LCR_SQUARE_SIDE_LEN) /
S3L_FRACTIONS_PER_UNIT;
}
LCR_SpaceUnit LCR_rendererGetCameraPitch(void)
{
return (LCR_scene3D.camera.transform.rotation.x * LCR_SQUARE_SIDE_LEN) /
S3L_FRACTIONS_PER_UNIT;
}
2023-09-13 20:51:07 +02:00
#endif // guard