#include "Core.h" #if CC_GFX_BACKEND == CC_GFX_BACKEND_SOFTGPU #include "_GraphicsBase.h" #include "Errors.h" #include "Window.h" static cc_bool faceCulling; static int fb_width, fb_height; static struct Bitmap fb_bmp; static float vp_hwidth, vp_hheight; static int fb_maxX, fb_maxY; static BitmapCol* colorBuffer; static BitmapCol clearColor; static cc_bool colWrite = true; static int cb_stride; static float* depthBuffer; static cc_bool depthTest = true; static cc_bool depthWrite = true; static int db_stride; static void* gfx_vertices; static GfxResourceID white_square; void Gfx_RestoreState(void) { InitDefaultResources(); // 1x1 dummy white texture struct Bitmap bmp; BitmapCol pixels[1] = { BITMAPCOLOR_WHITE }; Bitmap_Init(bmp, 1, 1, pixels); white_square = Gfx_CreateTexture(&bmp, 0, false); } void Gfx_FreeState(void) { FreeDefaultResources(); Gfx_DeleteTexture(&white_square); } void Gfx_Create(void) { Gfx.MaxTexWidth = 4096; Gfx.MaxTexHeight = 4096; Gfx.Created = true; Gfx.BackendType = CC_GFX_BACKEND_SOFTGPU; Gfx_RestoreState(); } static void DestroyBuffers(void) { Window_FreeFramebuffer(&fb_bmp); Mem_Free(depthBuffer); depthBuffer = NULL; } void Gfx_Free(void) { Gfx_FreeState(); DestroyBuffers(); } typedef struct CCTexture { int width, height; BitmapCol pixels[]; } CCTexture; static CCTexture* curTexture; static BitmapCol* curTexPixels; static int curTexWidth, curTexHeight; static int texWidthMask, texHeightMask; void Gfx_BindTexture(GfxResourceID texId) { if (!texId) texId = white_square; CCTexture* tex = texId; curTexture = tex; curTexPixels = tex->pixels; curTexWidth = tex->width; curTexHeight = tex->height; texWidthMask = (1 << Math_ilog2(tex->width)) - 1; texHeightMask = (1 << Math_ilog2(tex->height)) - 1; } void Gfx_DeleteTexture(GfxResourceID* texId) { GfxResourceID data = *texId; if (data) Mem_Free(data); *texId = NULL; } static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, int rowWidth, cc_uint8 flags, cc_bool mipmaps) { CCTexture* tex = (CCTexture*)Mem_Alloc(2 + bmp->width * bmp->height, 4, "Texture"); tex->width = bmp->width; tex->height = bmp->height; CopyTextureData(tex->pixels, bmp->width * BITMAPCOLOR_SIZE, bmp, rowWidth * BITMAPCOLOR_SIZE); return tex; } void Gfx_UpdateTexture(GfxResourceID texId, int x, int y, struct Bitmap* part, int rowWidth, cc_bool mipmaps) { CCTexture* tex = (CCTexture*)texId; BitmapCol* dst = (tex->pixels + x) + y * tex->width; CopyTextureData(dst, tex->width * BITMAPCOLOR_SIZE, part, rowWidth * BITMAPCOLOR_SIZE); } void Gfx_EnableMipmaps(void) { } void Gfx_DisableMipmaps(void) { } /*########################################################################################################################* *------------------------------------------------------State management---------------------------------------------------* *#########################################################################################################################*/ void Gfx_SetFog(cc_bool enabled) { } void Gfx_SetFogCol(PackedCol col) { } void Gfx_SetFogDensity(float value) { } void Gfx_SetFogEnd(float value) { } void Gfx_SetFogMode(FogFunc func) { } void Gfx_SetFaceCulling(cc_bool enabled) { faceCulling = enabled; } static void SetAlphaTest(cc_bool enabled) { /* Uses value from Gfx_SetAlphaTest */ } static void SetAlphaBlend(cc_bool enabled) { /* Uses value from Gfx_SetAlphaBlending */ } void Gfx_SetAlphaArgBlend(cc_bool enabled) { } static void ClearColorBuffer(void) { int i, x, y, size = fb_width * fb_height; if (cb_stride == fb_width) { for (i = 0; i < size; i++) colorBuffer[i] = clearColor; } else { /* Slower partial buffer clear */ for (y = 0; y < fb_height; y++) { i = y * cb_stride; for (x = 0; x < fb_width; x++) { colorBuffer[i + x] = clearColor; } } } } static void ClearDepthBuffer(void) { int i, size = fb_width * fb_height; for (i = 0; i < size; i++) depthBuffer[i] = 100000000.0f; } void Gfx_ClearBuffers(GfxBuffers buffers) { if (buffers & GFX_BUFFER_COLOR) ClearColorBuffer(); if (buffers & GFX_BUFFER_DEPTH) ClearDepthBuffer(); } void Gfx_ClearColor(PackedCol color) { int R = PackedCol_R(color); int G = PackedCol_G(color); int B = PackedCol_B(color); int A = PackedCol_A(color); clearColor = BitmapCol_Make(R, G, B, A); } void Gfx_SetDepthTest(cc_bool enabled) { depthTest = enabled; } void Gfx_SetDepthWrite(cc_bool enabled) { depthWrite = enabled; } static void SetColorWrite(cc_bool r, cc_bool g, cc_bool b, cc_bool a) { // TODO } void Gfx_DepthOnlyRendering(cc_bool depthOnly) { colWrite = !depthOnly; } /*########################################################################################################################* *-------------------------------------------------------Index buffers-----------------------------------------------------* *#########################################################################################################################*/ GfxResourceID Gfx_CreateIb2(int count, Gfx_FillIBFunc fillFunc, void* obj) { return (void*)1; } void Gfx_BindIb(GfxResourceID ib) { } void Gfx_DeleteIb(GfxResourceID* ib) { } /*########################################################################################################################* *-------------------------------------------------------Vertex buffers----------------------------------------------------* *#########################################################################################################################*/ static GfxResourceID Gfx_AllocStaticVb(VertexFormat fmt, int count) { return Mem_TryAlloc(count, strideSizes[fmt]); } void Gfx_BindVb(GfxResourceID vb) { gfx_vertices = vb; } void Gfx_DeleteVb(GfxResourceID* vb) { GfxResourceID data = *vb; if (data) Mem_Free(data); *vb = 0; } void* Gfx_LockVb(GfxResourceID vb, VertexFormat fmt, int count) { return vb; } void Gfx_UnlockVb(GfxResourceID vb) { gfx_vertices = vb; } static GfxResourceID Gfx_AllocDynamicVb(VertexFormat fmt, int maxVertices) { return Mem_TryAlloc(maxVertices, strideSizes[fmt]); } void Gfx_BindDynamicVb(GfxResourceID vb) { Gfx_BindVb(vb); } void* Gfx_LockDynamicVb(GfxResourceID vb, VertexFormat fmt, int count) { return vb; } void Gfx_UnlockDynamicVb(GfxResourceID vb) { gfx_vertices = vb; } void Gfx_DeleteDynamicVb(GfxResourceID* vb) { Gfx_DeleteVb(vb); } /*########################################################################################################################* *---------------------------------------------------------Matrices--------------------------------------------------------* *#########################################################################################################################*/ static float texOffsetX, texOffsetY; static struct Matrix _view, _proj, _mvp; void Gfx_LoadMatrix(MatrixType type, const struct Matrix* matrix) { if (type == MATRIX_VIEW) _view = *matrix; if (type == MATRIX_PROJ) _proj = *matrix; Matrix_Mul(&_mvp, &_view, &_proj); } void Gfx_LoadMVP(const struct Matrix* view, const struct Matrix* proj, struct Matrix* mvp) { _view = *view; _proj = *proj; Matrix_Mul(mvp, view, proj); _mvp = *mvp; } void Gfx_EnableTextureOffset(float x, float y) { texOffsetX = x; texOffsetY = y; } void Gfx_DisableTextureOffset(void) { texOffsetX = 0; texOffsetY = 0; } void Gfx_CalcOrthoMatrix(struct Matrix* matrix, float width, float height, float zNear, float zFar) { /* Source https://learn.microsoft.com/en-us/windows/win32/direct3d9/d3dxmatrixorthooffcenterrh */ /* The simplified calculation below uses: L = 0, R = width, T = 0, B = height */ /* NOTE: This calculation is shared with Direct3D 11 backend */ *matrix = Matrix_Identity; matrix->row1.x = 2.0f / width; matrix->row2.y = -2.0f / height; matrix->row3.z = 1.0f / (zNear - zFar); matrix->row4.x = -1.0f; matrix->row4.y = 1.0f; matrix->row4.z = zNear / (zNear - zFar); } static float Cotangent(float x) { return Math_CosF(x) / Math_SinF(x); } void Gfx_CalcPerspectiveMatrix(struct Matrix* matrix, float fov, float aspect, float zFar) { float zNear = 0.1f; /* Source https://learn.microsoft.com/en-us/windows/win32/direct3d9/d3dxmatrixperspectivefovrh */ /* NOTE: This calculation is shared with Direct3D 11 backend */ float c = Cotangent(0.5f * fov); *matrix = Matrix_Identity; matrix->row1.x = c / aspect; matrix->row2.y = c; matrix->row3.z = zFar / (zNear - zFar); matrix->row3.w = -1.0f; matrix->row4.z = (zNear * zFar) / (zNear - zFar); matrix->row4.w = 0.0f; } /*########################################################################################################################* *---------------------------------------------------------Rendering-------------------------------------------------------* *#########################################################################################################################*/ typedef struct Vector3 { float x, y, z; } Vector3; typedef struct Vector2 { float x, y; } Vector2; typedef struct Vertex_ { float x, y, z, w; float u, v; PackedCol c; } Vertex; static void TransformVertex2D(int index, Vertex* vertex) { // TODO: avoid the multiply, just add down in DrawTriangles char* ptr = (char*)gfx_vertices + index * gfx_stride; Vector3* pos = (Vector3*)ptr; vertex->x = pos->x; vertex->y = pos->y; if (gfx_format != VERTEX_FORMAT_TEXTURED) { struct VertexColoured* v = (struct VertexColoured*)ptr; vertex->u = 0.0f; vertex->v = 0.0f; vertex->c = v->Col; } else { struct VertexTextured* v = (struct VertexTextured*)ptr; vertex->u = v->U; vertex->v = v->V; vertex->c = v->Col; } } static int TransformVertex3D(int index, Vertex* vertex) { // TODO: avoid the multiply, just add down in DrawTriangles char* ptr = (char*)gfx_vertices + index * gfx_stride; Vector3* pos = (Vector3*)ptr; vertex->x = pos->x * _mvp.row1.x + pos->y * _mvp.row2.x + pos->z * _mvp.row3.x + _mvp.row4.x; vertex->y = pos->x * _mvp.row1.y + pos->y * _mvp.row2.y + pos->z * _mvp.row3.y + _mvp.row4.y; vertex->z = pos->x * _mvp.row1.z + pos->y * _mvp.row2.z + pos->z * _mvp.row3.z + _mvp.row4.z; vertex->w = pos->x * _mvp.row1.w + pos->y * _mvp.row2.w + pos->z * _mvp.row3.w + _mvp.row4.w; if (gfx_format != VERTEX_FORMAT_TEXTURED) { struct VertexColoured* v = (struct VertexColoured*)ptr; vertex->u = 0.0f; vertex->v = 0.0f; vertex->c = v->Col; } else { struct VertexTextured* v = (struct VertexTextured*)ptr; vertex->u = (v->U + texOffsetX); vertex->v = (v->V + texOffsetY); vertex->c = v->Col; } return vertex->z >= 0.0f; } static void ViewportVertex3D(Vertex* vertex) { float invW = 1.0f / vertex->w; vertex->x = vp_hwidth * (1 + vertex->x * invW); vertex->y = vp_hheight * (1 - vertex->y * invW); vertex->z = vertex->z * invW; vertex->w = invW; vertex->u *= invW; vertex->v *= invW; } // Ensure it's inlined, whereas Math_FloorF might not be static CC_INLINE int FastFloor(float value) { int valueI = (int)value; return valueI > value ? valueI - 1 : valueI; } #define edgeFunction(ax,ay, bx,by, cx,cy) (((bx) - (ax)) * ((cy) - (ay)) - ((by) - (ay)) * ((cx) - (ax))) static void DrawTriangle2D(Vertex* V0, Vertex* V1, Vertex* V2) { int x0 = (int)V0->x, y0 = (int)V0->y; int x1 = (int)V1->x, y1 = (int)V1->y; int x2 = (int)V2->x, y2 = (int)V2->y; int minX = min(x0, min(x1, x2)); int minY = min(y0, min(y1, y2)); int maxX = max(x0, max(x1, x2)); int maxY = max(y0, max(y1, y2)); int area = edgeFunction(x0,y0, x1,y1, x2,y2); // Reject triangles completely outside if (maxX < 0 || minX > fb_maxX) return; if (maxY < 0 || minY > fb_maxY) return; // Perform scissoring minX = max(minX, 0); maxX = min(maxX, fb_maxX); minY = max(minY, 0); maxY = min(maxY, fb_maxY); float factor = 1.0f / area; float u0 = V0->u * curTexWidth, u1 = V1->u * curTexWidth, u2 = V2->u * curTexWidth; float v0 = V0->v * curTexHeight, v1 = V1->v * curTexHeight, v2 = V2->v * curTexHeight; PackedCol color = V0->c; // https://fgiesen.wordpress.com/2013/02/10/optimizing-the-basic-rasterizer/ // Essentially these are the deltas of edge functions between X/Y and X/Y + 1 (i.e. one X/Y step) int dx01 = y0 - y1, dy01 = x1 - x0; int dx12 = y1 - y2, dy12 = x2 - x1; int dx20 = y2 - y0, dy20 = x0 - x2; float bc0_start = edgeFunction(x1,y1, x2,y2, minX+0.5f,minY+0.5f); float bc1_start = edgeFunction(x2,y2, x0,y0, minX+0.5f,minY+0.5f); float bc2_start = edgeFunction(x0,y0, x1,y1, minX+0.5f,minY+0.5f); for (int y = minY; y <= maxY; y++, bc0_start += dy12, bc1_start += dy20, bc2_start += dy01) { float bc0 = bc0_start; float bc1 = bc1_start; float bc2 = bc2_start; for (int x = minX; x <= maxX; x++, bc0 += dx12, bc1 += dx20, bc2 += dx01) { float ic0 = bc0 * factor; float ic1 = bc1 * factor; float ic2 = bc2 * factor; if (ic0 < 0 || ic1 < 0 || ic2 < 0) continue; int cb_index = y * cb_stride + x; int R, G, B, A; if (gfx_format == VERTEX_FORMAT_TEXTURED) { float u = ic0 * u0 + ic1 * u1 + ic2 * u2; float v = ic0 * v0 + ic1 * v1 + ic2 * v2; int texX = ((int)u) & texWidthMask; int texY = ((int)v) & texHeightMask; int texIndex = texY * curTexWidth + texX; BitmapCol tColor = curTexPixels[texIndex]; int a1 = PackedCol_A(color), a2 = BitmapCol_A(tColor); A = ( a1 * a2 ) >> 8; int r1 = PackedCol_R(color), r2 = BitmapCol_R(tColor); R = ( r1 * r2 ) >> 8; int g1 = PackedCol_G(color), g2 = BitmapCol_G(tColor); G = ( g1 * g2 ) >> 8; int b1 = PackedCol_B(color), b2 = BitmapCol_B(tColor); B = ( b1 * b2 ) >> 8; } else { R = PackedCol_R(color); G = PackedCol_G(color); B = PackedCol_B(color); A = PackedCol_A(color); } if (gfx_alphaTest && A < 0x80) continue; if (gfx_alphaBlend) { BitmapCol dst = colorBuffer[cb_index]; int dstR = BitmapCol_R(dst); int dstG = BitmapCol_G(dst); int dstB = BitmapCol_B(dst); R = (R * A + dstR * (255 - A)) >> 8; G = (G * A + dstG * (255 - A)) >> 8; B = (B * A + dstB * (255 - A)) >> 8; } colorBuffer[cb_index] = BitmapCol_Make(R, G, B, 0xFF); } } } static void DrawTriangle3D(Vertex* V0, Vertex* V1, Vertex* V2) { int x0 = (int)V0->x, y0 = (int)V0->y; int x1 = (int)V1->x, y1 = (int)V1->y; int x2 = (int)V2->x, y2 = (int)V2->y; int minX = min(x0, min(x1, x2)); int minY = min(y0, min(y1, y2)); int maxX = max(x0, max(x1, x2)); int maxY = max(y0, max(y1, y2)); int area = edgeFunction(x0,y0, x1,y1, x2,y2); if (faceCulling) { // https://gamedev.stackexchange.com/questions/203694/how-to-make-backface-culling-work-correctly-in-both-orthographic-and-perspective if (area < 0) return; } // Reject triangles completely outside if (maxX < 0 || minX > fb_maxX) return; if (maxY < 0 || minY > fb_maxY) return; // Perform scissoring minX = max(minX, 0); maxX = min(maxX, fb_maxX); minY = max(minY, 0); maxY = min(maxY, fb_maxY); // NOTE: W in frag variables below is actually 1/W float factor = 1.0f / area; float w0 = V0->w, w1 = V1->w, w2 = V2->w; // TODO proper clipping if (w0 <= 0 || w1 <= 0 || w2 <= 0) { return; } float z0 = V0->z, z1 = V1->z, z2 = V2->z; float u0 = V0->u, u1 = V1->u, u2 = V2->u; float v0 = V0->v, v1 = V1->v, v2 = V2->v; PackedCol color = V0->c; // https://fgiesen.wordpress.com/2013/02/10/optimizing-the-basic-rasterizer/ // Essentially these are the deltas of edge functions between X/Y and X/Y + 1 (i.e. one X/Y step) int dx01 = y0 - y1, dy01 = x1 - x0; int dx12 = y1 - y2, dy12 = x2 - x1; int dx20 = y2 - y0, dy20 = x0 - x2; float bc0_start = edgeFunction(x1,y1, x2,y2, minX+0.5f,minY+0.5f); float bc1_start = edgeFunction(x2,y2, x0,y0, minX+0.5f,minY+0.5f); float bc2_start = edgeFunction(x0,y0, x1,y1, minX+0.5f,minY+0.5f); for (int y = minY; y <= maxY; y++, bc0_start += dy12, bc1_start += dy20, bc2_start += dy01) { float bc0 = bc0_start; float bc1 = bc1_start; float bc2 = bc2_start; for (int x = minX; x <= maxX; x++, bc0 += dx12, bc1 += dx20, bc2 += dx01) { float ic0 = bc0 * factor; float ic1 = bc1 * factor; float ic2 = bc2 * factor; if (ic0 < 0 || ic1 < 0 || ic2 < 0) continue; int db_index = y * db_stride + x; float w = 1 / (ic0 * w0 + ic1 * w1 + ic2 * w2); float z = (ic0 * z0 + ic1 * z1 + ic2 * z2) * w; if (depthTest && (z < 0 || z > depthBuffer[db_index])) continue; if (!colWrite) { if (depthWrite) depthBuffer[db_index] = z; continue; } int R, G, B, A; if (gfx_format == VERTEX_FORMAT_TEXTURED) { float u = (ic0 * u0 + ic1 * u1 + ic2 * u2) * w; float v = (ic0 * v0 + ic1 * v1 + ic2 * v2) * w; int texX = ((int)(Math_AbsF(u - FastFloor(u)) * curTexWidth )) & texWidthMask; int texY = ((int)(Math_AbsF(v - FastFloor(v)) * curTexHeight)) & texHeightMask; int texIndex = texY * curTexWidth + texX; BitmapCol tColor = curTexPixels[texIndex]; int a1 = PackedCol_A(color), a2 = BitmapCol_A(tColor); A = ( a1 * a2 ) >> 8; int r1 = PackedCol_R(color), r2 = BitmapCol_R(tColor); R = ( r1 * r2 ) >> 8; int g1 = PackedCol_G(color), g2 = BitmapCol_G(tColor); G = ( g1 * g2 ) >> 8; int b1 = PackedCol_B(color), b2 = BitmapCol_B(tColor); B = ( b1 * b2 ) >> 8; } else { R = PackedCol_R(color); G = PackedCol_G(color); B = PackedCol_B(color); A = PackedCol_A(color); } if (gfx_alphaTest && A < 0x80) continue; int cb_index = y * cb_stride + x; if (gfx_alphaBlend) { BitmapCol dst = colorBuffer[cb_index]; int dstR = BitmapCol_R(dst); int dstG = BitmapCol_G(dst); int dstB = BitmapCol_B(dst); R = (R * A + dstR * (255 - A)) >> 8; G = (G * A + dstG * (255 - A)) >> 8; B = (B * A + dstB * (255 - A)) >> 8; } if (depthWrite) depthBuffer[db_index] = z; colorBuffer[cb_index] = BitmapCol_Make(R, G, B, 0xFF); } } } #define V0_VIS (1 << 0) #define V1_VIS (1 << 1) #define V2_VIS (1 << 2) #define V3_VIS (1 << 3) // https://github.com/behindthepixels/EDXRaster/blob/master/EDXRaster/Core/Clipper.h static void ClipLine(Vertex* v1, Vertex* v2, Vertex* V) { float t = Math_AbsF(v1->z / (v2->z - v1->z)); float invt = 1.0f - t; V->x = invt * v1->x + t * v2->x; V->y = invt * v1->y + t * v2->y; //V->z = invt * v1->z + t * v2->z; V->z = 0.0f; // clipped against near plane anyways (I.e Z/W = 0 --> Z = 0) V->w = invt * v1->w + t * v2->w; V->u = invt * v1->u + t * v2->u; V->v = invt * v1->v + t * v2->v; V->c = v1->c; } // https://casual-effects.com/research/McGuire2011Clipping/clip.glsl static void DrawClipped(int mask, Vertex* v0, Vertex* v1, Vertex* v2, Vertex* v3) { Vertex tmp[2]; Vertex* a = &tmp[0]; Vertex* b = &tmp[1]; switch (mask) { case V0_VIS: { // v0 // / | // / | // .....A....B... // / | // v3--v2---v1 ClipLine(v3, v0, a); ClipLine(v0, v1, b); ViewportVertex3D(v0); ViewportVertex3D(a); ViewportVertex3D(b); DrawTriangle3D(v0, a, b); } break; case V1_VIS: { // v1 // / | // / | // ....A.....B... // / | // v0--v3---v2 ClipLine(v0, v1, a); ClipLine(v1, v2, b); ViewportVertex3D(v1); ViewportVertex3D(a); ViewportVertex3D(b); DrawTriangle3D(a, b, v1); } break; case V2_VIS: { // v2 // / | // / | // ....A.....B... // / | // v1--v0---v3 ClipLine(v1, v2, a); ClipLine(v2, v3, b); ViewportVertex3D(v2); ViewportVertex3D(a); ViewportVertex3D(b); DrawTriangle3D(a, b, v2); } break; case V3_VIS: { // v3 // / | // / | // ....A.....B... // / | // v2--v1---v0 ClipLine(v2, v3, a); ClipLine(v3, v0, b); ViewportVertex3D(v3); ViewportVertex3D(a); ViewportVertex3D(b); DrawTriangle3D(b, v3, a); } break; case V0_VIS | V1_VIS: { // v0-----------v1 // \ | // ...B.........A... // \ | // v3-----v2 ClipLine(v1, v2, a); ClipLine(v3, v0, b); ViewportVertex3D(v0); ViewportVertex3D(v1); ViewportVertex3D(a); ViewportVertex3D(b); DrawTriangle3D(v1, v0, a); DrawTriangle3D(a, b, v0); } break; // case V0_VIS | V2_VIS: degenerate case that should never happen case V0_VIS | V3_VIS: { // v3-----------v0 // \ | // ...B.........A... // \ | // v2-----v1 ClipLine(v0, v1, a); ClipLine(v2, v3, b); ViewportVertex3D(v0); ViewportVertex3D(v3); ViewportVertex3D(a); ViewportVertex3D(b); DrawTriangle3D(a, v0, b); DrawTriangle3D(b, v3, v0); } break; case V1_VIS | V2_VIS: { // v1-----------v2 // \ | // ...B.........A... // \ | // v0-----v3 ClipLine(v2, v3, a); ClipLine(v0, v1, b); ViewportVertex3D(v1); ViewportVertex3D(v2); ViewportVertex3D(a); ViewportVertex3D(b); DrawTriangle3D(v1, b, v2); DrawTriangle3D(v2, a, b); } break; // case V1_VIS | V3_VIS: degenerate case that should never happen case V2_VIS | V3_VIS: { // v2-----------v3 // \ | // ...B.........A... // \ | // v1-----v0 ClipLine(v3, v0, a); ClipLine(v1, v2, b); ViewportVertex3D(v2); ViewportVertex3D(v3); ViewportVertex3D(a); ViewportVertex3D(b); DrawTriangle3D( b, a, v2); DrawTriangle3D(v2, v3, a); } break; case V0_VIS | V1_VIS | V2_VIS: { // --v1-- // v0-- --v2 // \ / // ....B.....A... // \ / // v3 // v1,v2,v0 v2,v0,A v0,A,B ClipLine(v2, v3, a); ClipLine(v3, v0, b); ViewportVertex3D(v0); ViewportVertex3D(v1); ViewportVertex3D(v2); ViewportVertex3D(a); ViewportVertex3D(b); DrawTriangle3D(v1, v0, v2); DrawTriangle3D(v2, a, v0); DrawTriangle3D(v0, b, a); } break; case V0_VIS | V1_VIS | V3_VIS: { // --v0-- // v3-- --v1 // \ / // ....B.....A... // \ / // v2 // v0,v1,v3 v1,v3,A v3,A,B ClipLine(v1, v2, a); ClipLine(v2, v3, b); ViewportVertex3D(v0); ViewportVertex3D(v1); ViewportVertex3D(v3); ViewportVertex3D(a); ViewportVertex3D(b); DrawTriangle3D(v0, v3, v1); DrawTriangle3D(v1, a, v3); DrawTriangle3D(v3, b, a); } break; case V0_VIS | V2_VIS | V3_VIS: { // --v3-- // v2-- --v0 // \ / // ....B.....A... // \ / // v1 // v3,v0,v2 v0,v2,A v2,A,B ClipLine(v0, v1, a); ClipLine(v1, v2, b); ViewportVertex3D(v0); ViewportVertex3D(v2); ViewportVertex3D(v3); ViewportVertex3D(a); ViewportVertex3D(b); DrawTriangle3D(v3, v2, v0); DrawTriangle3D(v0, a, v2); DrawTriangle3D(v2, b, a); } break; case V1_VIS | V2_VIS | V3_VIS: { // --v2-- // v1-- --v3 // \ / // ....B.....A... // \ / // v0 // v2,v3,v1 v3,v1,A v1,A,B ClipLine(v3, v0, a); ClipLine(v0, v1, b); ViewportVertex3D(v1); ViewportVertex3D(v2); ViewportVertex3D(v3); ViewportVertex3D(a); ViewportVertex3D(b); DrawTriangle3D(v2, v1, v3); DrawTriangle3D(v3, a, v1); DrawTriangle3D(v1, b, a); } break; } } void DrawQuads(int startVertex, int verticesCount) { Vertex vertices[4]; int j = startVertex; if (gfx_rendering2D) { // 4 vertices = 1 quad = 2 triangles for (int i = 0; i < verticesCount / 4; i++, j += 4) { TransformVertex2D(j + 0, &vertices[0]); TransformVertex2D(j + 1, &vertices[1]); TransformVertex2D(j + 2, &vertices[2]); TransformVertex2D(j + 3, &vertices[3]); DrawTriangle2D(&vertices[0], &vertices[2], &vertices[1]); DrawTriangle2D(&vertices[2], &vertices[0], &vertices[3]); } } else { // 4 vertices = 1 quad = 2 triangles for (int i = 0; i < verticesCount / 4; i++, j += 4) { int clip = TransformVertex3D(j + 0, &vertices[0]) << 0 | TransformVertex3D(j + 1, &vertices[1]) << 1 | TransformVertex3D(j + 2, &vertices[2]) << 2 | TransformVertex3D(j + 3, &vertices[3]) << 3; if (clip == 0) { // Quad entirely clipped } else if (clip == 0x0F) { // Quad entirely visible ViewportVertex3D(&vertices[0]); ViewportVertex3D(&vertices[1]); ViewportVertex3D(&vertices[2]); ViewportVertex3D(&vertices[3]); DrawTriangle3D(&vertices[0], &vertices[2], &vertices[1]); DrawTriangle3D(&vertices[2], &vertices[0], &vertices[3]); } else { // Quad partially visible DrawClipped(clip, &vertices[0], &vertices[1], &vertices[2], &vertices[3]); } } } } void Gfx_SetVertexFormat(VertexFormat fmt) { gfx_format = fmt; gfx_stride = strideSizes[fmt]; } void Gfx_DrawVb_Lines(int verticesCount) { } /* TODO */ void Gfx_DrawVb_IndexedTris_Range(int verticesCount, int startVertex) { DrawQuads(startVertex, verticesCount); } void Gfx_DrawVb_IndexedTris(int verticesCount) { DrawQuads(0, verticesCount); } void Gfx_DrawIndexedTris_T2fC4b(int verticesCount, int startVertex) { DrawQuads(startVertex, verticesCount); } /*########################################################################################################################* *---------------------------------------------------------Other/Misc------------------------------------------------------* *#########################################################################################################################*/ static BitmapCol* CB_GetRow(struct Bitmap* bmp, int y, void* ctx) { return colorBuffer + cb_stride * y; } cc_result Gfx_TakeScreenshot(struct Stream* output) { struct Bitmap bmp; Bitmap_Init(bmp, fb_width, fb_height, NULL); return Png_Encode(&bmp, output, CB_GetRow, false, NULL); } cc_bool Gfx_WarnIfNecessary(void) { return false; } cc_bool Gfx_GetUIOptions(struct MenuOptionsScreen* s) { return false; } void Gfx_BeginFrame(void) { } void Gfx_EndFrame(void) { Rect2D r = { 0, 0, fb_width, fb_height }; Window_DrawFramebuffer(r, &fb_bmp); } void Gfx_SetVSync(cc_bool vsync) { gfx_vsync = vsync; } void Gfx_OnWindowResize(void) { if (depthBuffer) DestroyBuffers(); fb_width = Game.Width; fb_height = Game.Height; Window_AllocFramebuffer(&fb_bmp, Game.Width, Game.Height); colorBuffer = fb_bmp.scan0; cb_stride = fb_bmp.width; depthBuffer = Mem_Alloc(fb_width * fb_height, 4, "depth buffer"); db_stride = fb_width; Gfx_SetViewport(0, 0, Game.Width, Game.Height); Gfx_SetScissor (0, 0, Game.Width, Game.Height); } void Gfx_SetViewport(int x, int y, int w, int h) { vp_hwidth = w / 2.0f; vp_hheight = h / 2.0f; } void Gfx_SetScissor (int x, int y, int w, int h) { /* TODO minX/Y */ fb_maxX = x + w - 1; fb_maxY = y + h - 1; } void Gfx_GetApiInfo(cc_string* info) { int pointerSize = sizeof(void*) * 8; String_Format1(info, "-- Using software (%i bit) --\n", &pointerSize); PrintMaxTextureInfo(info); } cc_bool Gfx_TryRestoreContext(void) { return true; } #endif