mirror of
https://github.com/isledecomp/isle-portable.git
synced 2025-09-22 11:31:57 -04:00
Add texture rendering to the software rendere (#211)
This commit is contained in:
parent
403ead7453
commit
ec7204ed38
@ -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<int>(count));
|
||||
count = 3;
|
||||
}
|
||||
memcpy(&m_lights.lights, vertices, sizeof(SceneLight) * count);
|
||||
|
@ -42,17 +42,18 @@ void Direct3DRMSoftwareRenderer::SetProjection(D3DRMMATRIX4D perspective, D3DVAL
|
||||
|
||||
void Direct3DRMSoftwareRenderer::ClearZBuffer()
|
||||
{
|
||||
std::fill(m_zBuffer.begin(), m_zBuffer.end(), std::numeric_limits<double>::infinity());
|
||||
std::fill(m_zBuffer.begin(), m_zBuffer.end(), std::numeric_limits<float>::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<Uint8*>(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<Uint8*>(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<float>(((w0 * v0.u / p0.w) + (w1 * v1.u / p1.w) + (w2 * v2.u / p2.w)) * invW);
|
||||
float v = static_cast<float>(((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<int>(u * texWidthScale);
|
||||
int texY = static_cast<int>(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<TextureDestroyContext*>(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<Direct3DRMTextureImpl*>(iTexture);
|
||||
auto surface = static_cast<DirectDrawSurfaceImpl*>(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<Uint32>(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<Uint32>(m_textures.size() - 1), texture);
|
||||
return static_cast<Uint32>(m_textures.size() - 1);
|
||||
}
|
||||
|
||||
DWORD Direct3DRMSoftwareRenderer::GetWidth()
|
||||
|
@ -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<Direct3DRMFrameImpl*>(m_rootFrame)->m_backgroundColor;
|
||||
|
||||
std::vector<SceneLight> 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);
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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<SceneLight> m_lights;
|
||||
std::vector<SDL_Surface*> m_textures;
|
||||
std::vector<TextureCache> m_textures;
|
||||
D3DVALUE m_front;
|
||||
D3DVALUE m_back;
|
||||
std::vector<PositionColorVertex> m_vertexBuffer;
|
||||
float proj[4][4] = {0};
|
||||
std::vector<double> m_zBuffer;
|
||||
std::vector<float> m_zBuffer;
|
||||
};
|
||||
|
@ -9,7 +9,5 @@ struct Direct3DRMTextureImpl : public Direct3DRMObjectBaseImpl<IDirect3DRMTextur
|
||||
HRESULT Changed(BOOL pixels, BOOL palette) override;
|
||||
|
||||
IDirectDrawSurface* m_surface = nullptr;
|
||||
|
||||
private:
|
||||
Uint8 m_version = 0;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user