diff --git a/miniwin/src/d3drm/backends/sdl3gpu/renderer.cpp b/miniwin/src/d3drm/backends/sdl3gpu/renderer.cpp index 7ad35f61..03d6656b 100644 --- a/miniwin/src/d3drm/backends/sdl3gpu/renderer.cpp +++ b/miniwin/src/d3drm/backends/sdl3gpu/renderer.cpp @@ -191,7 +191,7 @@ void Direct3DRMSDL3GPURenderer::SetBackbuffer(SDL_Surface* buf) void Direct3DRMSDL3GPURenderer::PushLights(const SceneLight* vertices, size_t count) { if (count > 3) { - SDL_LogError(LOG_CATEGORY_MINIWIN, "Unsupported number of lights (%d)", count); + SDL_LogError(LOG_CATEGORY_MINIWIN, "Unsupported number of lights (%d)", static_cast(count)); count = 3; } memcpy(&m_lights.lights, vertices, sizeof(SceneLight) * count); diff --git a/miniwin/src/d3drm/backends/software/renderer.cpp b/miniwin/src/d3drm/backends/software/renderer.cpp index 05c51043..1f7f29d9 100644 --- a/miniwin/src/d3drm/backends/software/renderer.cpp +++ b/miniwin/src/d3drm/backends/software/renderer.cpp @@ -42,17 +42,18 @@ void Direct3DRMSoftwareRenderer::SetProjection(D3DRMMATRIX4D perspective, D3DVAL void Direct3DRMSoftwareRenderer::ClearZBuffer() { - std::fill(m_zBuffer.begin(), m_zBuffer.end(), std::numeric_limits::infinity()); + std::fill(m_zBuffer.begin(), m_zBuffer.end(), std::numeric_limits::infinity()); } -void Direct3DRMSoftwareRenderer::ProjectVertex(const PositionColorVertex& v, float& out_x, float& out_y, float& out_z) - const +void Direct3DRMSoftwareRenderer::ProjectVertex(const PositionColorVertex& v, D3DRMVECTOR4D& p) const { float px = proj[0][0] * v.x + proj[1][0] * v.y + proj[2][0] * v.z + proj[3][0]; float py = proj[0][1] * v.x + proj[1][1] * v.y + proj[2][1] * v.z + proj[3][1]; float pz = proj[0][2] * v.x + proj[1][2] * v.y + proj[2][2] * v.z + proj[3][2]; float pw = proj[0][3] * v.x + proj[1][3] * v.y + proj[2][3] * v.z + proj[3][3]; + p.w = pw; + // Perspective divide if (pw != 0.0f) { px /= pw; @@ -61,9 +62,9 @@ void Direct3DRMSoftwareRenderer::ProjectVertex(const PositionColorVertex& v, flo } // Map from NDC [-1,1] to screen coordinates - out_x = (px * 0.5f + 0.5f) * m_width; - out_y = (1.0f - (py * 0.5f + 0.5f)) * m_height; - out_z = pz; + p.x = (px * 0.5f + 0.5f) * m_width; + p.y = (1.0f - (py * 0.5f + 0.5f)) * m_height; + p.z = pz; } PositionColorVertex SplitEdge(PositionColorVertex a, const PositionColorVertex& b, float plane) @@ -72,6 +73,21 @@ PositionColorVertex SplitEdge(PositionColorVertex a, const PositionColorVertex& a.x = a.x + t * (b.x - a.x); a.y = a.y + t * (b.y - a.y); a.z = plane; + + a.u = a.u + t * (b.u - a.u); + a.v = a.v + t * (b.v - a.v); + + a.nx = a.nx + t * (b.nx - a.nx); + a.ny = a.ny + t * (b.ny - a.ny); + a.nz = a.nz + t * (b.nz - a.nz); + + float len = std::sqrt(a.nx * a.nx + a.ny * a.ny + a.nz * a.nz); + if (len > 0.0001f) { + a.nx /= len; + a.ny /= len; + a.nz /= len; + } + return a; } @@ -203,26 +219,27 @@ void Direct3DRMSoftwareRenderer::DrawTriangleProjected( const PositionColorVertex& v2 ) { - float x0, y0, z0, x1, y1, z1, x2, y2, z2; - ProjectVertex(v0, x0, y0, z0); - ProjectVertex(v1, x1, y1, z1); - ProjectVertex(v2, x2, y2, z2); + D3DRMVECTOR4D p0, p1, p2; + + ProjectVertex(v0, p0); + ProjectVertex(v1, p1); + ProjectVertex(v2, p2); // Skip triangles outside the frustum - if ((z0 < m_front && z1 < m_front && z2 < m_front) || (z0 > m_back && z1 > m_back && z2 > m_back)) { + if ((p0.z < m_front && p1.z < m_front && p2.z < m_front) || (p0.z > m_back && p1.z > m_back && p2.z > m_back)) { return; } // Skip offscreen triangles - if ((x0 < 0 && x1 < 0 && x2 < 0) || (x0 >= m_width && x1 >= m_width && x2 >= m_width) || - (y0 < 0 && y1 < 0 && y2 < 0) || (y0 >= m_height && y1 >= m_height && y2 >= m_height)) { + if ((p0.x < 0 && p1.x < 0 && p2.x < 0) || (p0.x >= m_width && p1.x >= m_width && p2.x >= m_width) || + (p0.y < 0 && p1.y < 0 && p2.y < 0) || (p0.y >= m_height && p1.y >= m_height && p2.y >= m_height)) { return; } - int minX = std::max(0, (int) std::floor(std::min({x0, x1, x2}))); - int maxX = std::min((int) m_width - 1, (int) std::ceil(std::max({x0, x1, x2}))); - int minY = std::max(0, (int) std::floor(std::min({y0, y1, y2}))); - int maxY = std::min((int) m_height - 1, (int) std::ceil(std::max({y0, y1, y2}))); + int minX = std::max(0, (int) std::floor(std::min({p0.x, p1.x, p2.x}))); + int maxX = std::min((int) m_width - 1, (int) std::ceil(std::max({p0.x, p1.x, p2.x}))); + int minY = std::max(0, (int) std::floor(std::min({p0.y, p1.y, p2.y}))); + int maxY = std::min((int) m_height - 1, (int) std::ceil(std::max({p0.y, p1.y, p2.y}))); if (minX > maxX || minY > maxY) { return; } @@ -230,40 +247,29 @@ void Direct3DRMSoftwareRenderer::DrawTriangleProjected( auto edge = [](double x0, double y0, double x1, double y1, double x, double y) { return (x - x0) * (y1 - y0) - (y - y0) * (x1 - x0); }; - double area = edge(x0, y0, x1, y1, x2, y2); + float area = edge(p0.x, p0.y, p1.x, p1.y, p2.x, p2.y); if (area >= 0) { return; } - double invArea = 1.0f / area; + float invArea = 1.0f / area; // Per-vertex lighting using vertex normals SDL_Color c0 = ApplyLighting(v0); SDL_Color c1 = ApplyLighting(v1); SDL_Color c2 = ApplyLighting(v2); - SDL_Surface* texture = nullptr; Uint32 texId = v0.texId; + int texturePitch; + Uint8* texels = nullptr; + int texWidthScale; + int texHeightScale; if (texId != NO_TEXTURE_ID) { - texture = m_textures[texId]; - if (texture && SDL_LockSurface(texture)) { - // Pointer to first pixel data - Uint8* pixelAddr = static_cast(texture->pixels); - - Uint32 pixel; - memcpy(&pixel, pixelAddr, m_bytesPerPixel); - - Uint8 r, g, b, a; - SDL_GetRGBA(pixel, m_format, m_palette, &r, &g, &b, &a); - - // TODO use the UV to read out and blend texels on the triangle - c0.r = r; - c0.g = g; - c0.b = b; - c0.a = a; - c1 = c0; - c2 = c0; - - SDL_UnlockSurface(texture); + SDL_Surface* texture = m_textures[texId].cached; + if (texture) { + texturePitch = texture->pitch; + texels = static_cast(texture->pixels); + texWidthScale = texture->w - 1; + texHeightScale = texture->h - 1; } } @@ -272,23 +278,23 @@ void Direct3DRMSoftwareRenderer::DrawTriangleProjected( for (int y = minY; y <= maxY; ++y) { for (int x = minX; x <= maxX; ++x) { - double px = x + 0.5f; - double py = y + 0.5f; - double w0 = edge(x1, y1, x2, y2, px, py) * invArea; + float px = x + 0.5f; + float py = y + 0.5f; + float w0 = edge(p1.x, p1.y, p2.x, p2.y, px, py) * invArea; if (w0 < 0.0f || w0 > 1.0f) { continue; } - double w1 = edge(x2, y2, x0, y0, px, py) * invArea; + float w1 = edge(p2.x, p2.y, p0.x, p0.y, px, py) * invArea; if (w1 < 0.0f || w1 > 1.0f - w0) { continue; } - double w2 = 1.0f - w0 - w1; - double z = w0 * z0 + w1 * z1 + w2 * z2; + float w2 = 1.0f - w0 - w1; + float z = w0 * p0.z + w1 * p1.z + w2 * p2.z; int zidx = y * m_width + x; - double& zref = m_zBuffer[zidx]; + float& zref = m_zBuffer[zidx]; if (z >= zref) { continue; } @@ -301,10 +307,43 @@ void Direct3DRMSoftwareRenderer::DrawTriangleProjected( if (v0.a == 255) { zref = z; + + if (texels) { + // Perspective correct interpolate texture coords + float invW = w0 / p0.w + w1 / p1.w + w2 / p2.w; + if (invW == 0.0) { + continue; + } + invW = 1.0 / invW; + float u = static_cast(((w0 * v0.u / p0.w) + (w1 * v1.u / p1.w) + (w2 * v2.u / p2.w)) * invW); + float v = static_cast(((w0 * v0.v / p0.w) + (w1 * v1.v / p1.w) + (w2 * v2.v / p2.w)) * invW); + + // Tile textures + u = u - std::floor(u); + v = v - std::floor(v); + + int texX = static_cast(u * texWidthScale); + int texY = static_cast(v * texHeightScale); + + Uint8* texelAddr = texels + texY * texturePitch + texX * m_bytesPerPixel; + + Uint32 texelColor = 0; + memcpy(&texelColor, texelAddr, m_bytesPerPixel); + + Uint8 tr, tg, tb, ta; + SDL_GetRGBA(texelColor, m_format, m_palette, &tr, &tg, &tb, &ta); + + // Multiply vertex color by texel color + r = (r * tr + 127) / 255; + g = (g * tg + 127) / 255; + b = (b * tb + 127) / 255; + } + Uint32 finalColor = SDL_MapRGBA(m_format, m_palette, r, g, b, 255); memcpy(pixelAddr, &finalColor, m_bytesPerPixel); } else { + // Transparent alpha blending with vertex alpha BlendPixel(pixelAddr, r, g, b, v0.a); } } @@ -322,9 +361,13 @@ void Direct3DRMSoftwareRenderer::AddTextureDestroyCallback(Uint32 id, IDirect3DR texture->AddDestroyCallback( [](IDirect3DRMObject* obj, void* arg) { auto* ctx = static_cast(arg); - auto& sufRef = ctx->renderer->m_textures[ctx->textureId]; - SDL_DestroySurface(sufRef); - sufRef = nullptr; + auto& cacheEntry = ctx->renderer->m_textures[ctx->textureId]; + if (cacheEntry.cached) { + SDL_UnlockSurface(cacheEntry.cached); + SDL_DestroySurface(cacheEntry.cached); + cacheEntry.cached = nullptr; + cacheEntry.texture = nullptr; + } delete ctx; }, ctx @@ -335,29 +378,40 @@ Uint32 Direct3DRMSoftwareRenderer::GetTextureId(IDirect3DRMTexture* iTexture) { auto texture = static_cast(iTexture); auto surface = static_cast(texture->m_surface); - SDL_Surface* convertedRender = SDL_ConvertSurface(surface->m_surface, m_backbuffer->format); + // Check if already mapped for (Uint32 i = 0; i < m_textures.size(); ++i) { - if (m_textures[i] == convertedRender) { + auto& texRef = m_textures[i]; + if (texRef.texture == texture) { + if (texRef.version != texture->m_version) { + // Update animated textures + SDL_DestroySurface(texRef.cached); + texRef.cached = SDL_ConvertSurface(surface->m_surface, m_backbuffer->format); + SDL_LockSurface(texRef.cached); + texRef.version = texture->m_version; + } return i; } } + SDL_Surface* convertedRender = SDL_ConvertSurface(surface->m_surface, m_backbuffer->format); + SDL_LockSurface(convertedRender); + // Reuse freed slot for (Uint32 i = 0; i < m_textures.size(); ++i) { auto& texRef = m_textures[i]; - if (texRef == nullptr) { - texRef = convertedRender; + if (texRef.texture == nullptr) { + texRef.texture = texture; + texRef.cached = convertedRender; AddTextureDestroyCallback(i, texture); return i; } } // Append new - Uint32 newId = static_cast(m_textures.size()); - m_textures.push_back(convertedRender); - AddTextureDestroyCallback(newId, texture); - return newId; + m_textures.push_back({texture, texture->m_version, convertedRender}); + AddTextureDestroyCallback(static_cast(m_textures.size() - 1), texture); + return static_cast(m_textures.size() - 1); } DWORD Direct3DRMSoftwareRenderer::GetWidth() diff --git a/miniwin/src/d3drm/d3drmviewport.cpp b/miniwin/src/d3drm/d3drmviewport.cpp index 40636da0..55260a56 100644 --- a/miniwin/src/d3drm/d3drmviewport.cpp +++ b/miniwin/src/d3drm/d3drmviewport.cpp @@ -108,8 +108,6 @@ D3DVECTOR ComputeTriangleNormal(const D3DVECTOR& v0, const D3DVECTOR& v1, const HRESULT Direct3DRMViewportImpl::CollectSceneData() { - MINIWIN_NOT_IMPLEMENTED(); // Textures, Materials - m_backgroundColor = static_cast(m_rootFrame)->m_backgroundColor; std::vector lights; @@ -285,6 +283,8 @@ HRESULT Direct3DRMViewportImpl::CollectSceneData() vtx.b = (color >> 0) & 0xFF; vtx.a = (color >> 24) & 0xFF; vtx.texId = texId; + vtx.u = dv.tu; + vtx.v = dv.tv; verts.push_back(vtx); } } diff --git a/miniwin/src/internal/d3drmrenderer.h b/miniwin/src/internal/d3drmrenderer.h index 7be78a32..5910706e 100644 --- a/miniwin/src/internal/d3drmrenderer.h +++ b/miniwin/src/internal/d3drmrenderer.h @@ -11,6 +11,7 @@ typedef struct PositionColorVertex { float nx, ny, nz; Uint8 r, g, b, a; Uint32 texId = NO_TEXTURE_ID; + float u, v; } PositionColorVertex; struct FColor { diff --git a/miniwin/src/internal/d3drmrenderer_software.h b/miniwin/src/internal/d3drmrenderer_software.h index 08675adb..6879a0e1 100644 --- a/miniwin/src/internal/d3drmrenderer_software.h +++ b/miniwin/src/internal/d3drmrenderer_software.h @@ -9,6 +9,12 @@ DEFINE_GUID(SOFTWARE_GUID, 0x682656F3, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02); +struct TextureCache { + Direct3DRMTextureImpl* texture; + Uint8 version; + SDL_Surface* cached; +}; + class Direct3DRMSoftwareRenderer : public Direct3DRMRenderer { public: Direct3DRMSoftwareRenderer(DWORD width, DWORD height); @@ -31,7 +37,7 @@ private: const PositionColorVertex& v1, const PositionColorVertex& v2 ); - void ProjectVertex(const PositionColorVertex&, float&, float&, float&) const; + void ProjectVertex(const PositionColorVertex& v, D3DRMVECTOR4D& p) const; void BlendPixel(Uint8* pixelAddr, Uint8 r, Uint8 g, Uint8 b, Uint8 a); SDL_Color ApplyLighting(const PositionColorVertex& vertex); void AddTextureDestroyCallback(Uint32 id, IDirect3DRMTexture* texture); @@ -43,10 +49,10 @@ private: const SDL_PixelFormatDetails* m_format; int m_bytesPerPixel; std::vector m_lights; - std::vector m_textures; + std::vector m_textures; D3DVALUE m_front; D3DVALUE m_back; std::vector m_vertexBuffer; float proj[4][4] = {0}; - std::vector m_zBuffer; + std::vector m_zBuffer; }; diff --git a/miniwin/src/internal/d3drmtexture_impl.h b/miniwin/src/internal/d3drmtexture_impl.h index dc3d8b70..e81c3884 100644 --- a/miniwin/src/internal/d3drmtexture_impl.h +++ b/miniwin/src/internal/d3drmtexture_impl.h @@ -9,7 +9,5 @@ struct Direct3DRMTextureImpl : public Direct3DRMObjectBaseImpl