/** 3D renderer: implements 3D rendering. */ #ifndef _LCR_RENDERER_H #define _LCR_RENDERER_H #define S3L_RESOLUTION_X LCR_SETTING_RESOLUTION_X #define S3L_RESOLUTION_Y LCR_SETTING_RESOLUTION_Y #define S3L_PIXEL_FUNCTION LCR_pixelFunc3D #define S3L_Z_BUFFER 2 #include "small3dlib.h" /// Renderer specific unit, length of one map square. #define LCR_RENDERER_UNIT S3L_FRACTIONS_PER_UNIT struct LCR_Renderer { // TODO }; S3L_Scene LCR_scene3D; S3L_Model3D LCR_models3D[3]; // TODO S3L_Model3D *LCR_mapModel; S3L_Unit LCR_mapVertices[LCR_SETTING_MAX_MAP_VERTICES * 3]; S3L_Index LCR_mapTriangles[LCR_SETTING_MAX_MAP_TRIANGLES * 3]; void LCR_pixelFunc3D(S3L_PixelInfo *pixel) { LCR_drawPixelXYSafe(pixel->x,pixel->y,0xff00 + (pixel->triangleID % 16) * 16 ); } 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) { S3L_Index *t = &(LCR_mapTriangles[LCR_mapModel->triangleCount * 3]); *t = a; t++; *t = b; t++; *t = c; LCR_mapModel->triangleCount++; } } /** Builds an internal 3D model of the currently loaded map. Returns 1 on success, otherwise 0 (e.g. not enough space). */ uint8_t _LCR_rendererBuildMapModel(void) { uint8_t blockShapeBytes[LCR_MAP_BLOCK_SHAPE_MAX_BYTES]; uint8_t blockShapeByteCount; S3L_model3DInit(LCR_mapVertices,0,LCR_mapTriangles,0,LCR_mapModel); for (int j = 0; j < LCR_currentMap.blockCount; ++j) { uint8_t bx, by, bz; LCR_mapBlockGetCoords(LCR_currentMap.blocks + j * LCR_BLOCK_SIZE,&bx,&by,&bz); LCR_mapGetBlockShape(LCR_currentMap.blocks[j * LCR_BLOCK_SIZE],0, 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; } } } return 1; } uint8_t LCR_rendererInit(void) { LCR_mapModel = LCR_models3D; if (!_LCR_rendererBuildMapModel()) return 0; S3L_sceneInit(LCR_models3D,1,&LCR_scene3D); return 1; } 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; if (LCR_scene3D.camera.transform.rotation.x > S3L_FRACTIONS_PER_UNIT / 4) LCR_scene3D.camera.transform.rotation.x = S3L_FRACTIONS_PER_UNIT / 4; 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; } void LCR_rendererDraw(void) { S3L_newFrame(); S3L_drawScene(LCR_scene3D); } void LCR_drawBackground(int verticalOffset) { // 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); /* 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); */ } void LCR_drawSkyStrip(int verticalOffset, uint8_t horizontalOffset) { #if LCR_SETTING_SKY_SIZE != 0 /* 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) { // 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; uint16_t pixel = *(skyLine + offsetX / 2); pixel = offsetX % 2 ? (pixel >> 8) : (pixel & 0x00ff); LCR_drawPixelUnsafe(i,verticalOffset,LCR_skyImages[pixel]); } } verticalOffset++; if (verticalOffset % LCR_SETTING_SKY_SIZE == 0) skyLine += LCR_SKY_IMAGE_SIZE / 2; } */ #endif } 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; } #endif // guard