Implement OpenGL 1.5 backend renderer (#223)

This commit is contained in:
Anders Jenbo 2025-06-03 03:28:53 +02:00 committed by GitHub
parent 1ab11ed091
commit b169a4d637
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 340 additions and 32 deletions

View File

@ -64,7 +64,7 @@ jobs:
sudo apt-get update sudo apt-get update
sudo apt-get install -y \ sudo apt-get install -y \
libx11-dev libxext-dev libxrandr-dev libxrender-dev libxfixes-dev libxi-dev libxinerama-dev \ libx11-dev libxext-dev libxrandr-dev libxrender-dev libxfixes-dev libxi-dev libxinerama-dev \
libxcursor-dev libwayland-dev libxkbcommon-dev wayland-protocols libxcursor-dev libwayland-dev libxkbcommon-dev wayland-protocols libgl1-mesa-dev libglew-dev
- name: Install macOS dependencies (brew) - name: Install macOS dependencies (brew)
if: ${{ matrix.brew }} if: ${{ matrix.brew }}
@ -122,7 +122,7 @@ jobs:
run: | run: |
action_headers=$(find LEGO1/lego/legoomni/include/actions \ action_headers=$(find LEGO1/lego/legoomni/include/actions \
-name '*.h' -print0 | xargs -0 echo) -name '*.h' -print0 | xargs -0 echo)
python3 tools/ncc/ncc.py \ python3 tools/ncc/ncc.py \
--clang-lib ${{ env.LLVM_PATH }}/lib/libclang.so \ --clang-lib ${{ env.LLVM_PATH }}/lib/libclang.so \
--recurse \ --recurse \

View File

@ -23,6 +23,18 @@ add_library(miniwin STATIC EXCLUDE_FROM_ALL
src/d3drm/backends/sdl3gpu/shaders/generated/ShaderIndex.cpp src/d3drm/backends/sdl3gpu/shaders/generated/ShaderIndex.cpp
) )
find_package(OpenGL)
find_package(GLEW)
if(OpenGL_FOUND AND GLEW_FOUND)
target_sources(miniwin PRIVATE src/d3drm/backends/opengl15/renderer.cpp)
target_compile_definitions(miniwin PRIVATE USE_OPENGL15)
# Find and link OpenGL (1.5)
target_link_libraries(miniwin PRIVATE OpenGL::GL)
# Glew is used for getting a FBO for off screen rendering
target_include_directories(miniwin PRIVATE ${GLEW_INCLUDE_DIRS})
target_link_libraries(miniwin PRIVATE ${GLEW_LIBRARIES})
endif()
# Force reported render mods from MiniWin # Force reported render mods from MiniWin
target_compile_definitions(miniwin PRIVATE MINIWIN_PIXELFORMAT=SDL_PIXELFORMAT_RGB565) target_compile_definitions(miniwin PRIVATE MINIWIN_PIXELFORMAT=SDL_PIXELFORMAT_RGB565)
target_compile_definitions(miniwin PUBLIC MINIWIN) target_compile_definitions(miniwin PUBLIC MINIWIN)

View File

@ -0,0 +1,257 @@
#include "d3drmrenderer_opengl15.h"
#include "ddraw_impl.h"
#include <GL/glew.h>
#include <cstring>
#include <vector>
Direct3DRMRenderer* OpenGL15Renderer::Create(DWORD width, DWORD height)
{
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 5);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
SDL_Window* window = DDWindow;
bool testWindow = false;
if (!window) {
window = SDL_CreateWindow("OpenGL 1.5 test", width, height, SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL);
testWindow = true;
}
SDL_GLContext context = SDL_GL_CreateContext(window);
if (!context) {
if (testWindow) {
SDL_DestroyWindow(window);
}
return nullptr;
}
SDL_GL_MakeCurrent(window, context);
GLenum err = glewInit();
if (err != GLEW_OK) {
if (testWindow) {
SDL_DestroyWindow(window);
}
return nullptr;
}
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
glEnable(GL_COLOR_MATERIAL);
// Setup FBO
GLuint fbo;
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
// Create color texture
GLuint colorTex;
glGenTextures(1, &colorTex);
glBindTexture(GL_TEXTURE_2D, colorTex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTex, 0);
// Create depth renderbuffer
GLuint depthRb;
glGenRenderbuffers(1, &depthRb);
glBindRenderbuffer(GL_RENDERBUFFER, depthRb);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRb);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
return nullptr;
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
if (testWindow) {
SDL_DestroyWindow(window);
}
return new OpenGL15Renderer(width, height, context, fbo, colorTex, depthRb);
}
OpenGL15Renderer::OpenGL15Renderer(
int width,
int height,
SDL_GLContext context,
GLuint fbo,
GLuint colorTex,
GLuint depthRb
)
: m_width(width), m_height(height), m_context(context), m_fbo(fbo), m_colorTex(colorTex), m_depthRb(depthRb)
{
m_renderedImage = SDL_CreateSurface(m_width, m_height, SDL_PIXELFORMAT_ABGR8888);
}
OpenGL15Renderer::~OpenGL15Renderer()
{
if (m_renderedImage) {
SDL_DestroySurface(m_renderedImage);
}
}
void OpenGL15Renderer::SetBackbuffer(SDL_Surface* surface)
{
m_backbuffer = surface;
}
void OpenGL15Renderer::PushVertices(const PositionColorVertex* verts, size_t count)
{
m_vertices.assign(verts, verts + count);
}
void OpenGL15Renderer::PushLights(const SceneLight* lightsArray, size_t count)
{
m_lights.assign(lightsArray, lightsArray + count);
}
void OpenGL15Renderer::SetProjection(D3DRMMATRIX4D perspective, D3DVALUE front, D3DVALUE back)
{
memcpy(&m_projection, perspective, sizeof(D3DRMMATRIX4D));
m_projection[1][1] *= -1.0f; // OpenGL is upside down
}
Uint32 OpenGL15Renderer::GetTextureId(IDirect3DRMTexture* texture)
{
return NO_TEXTURE_ID; // Stub
}
DWORD OpenGL15Renderer::GetWidth()
{
return m_width;
}
DWORD OpenGL15Renderer::GetHeight()
{
return m_height;
}
void OpenGL15Renderer::GetDesc(D3DDEVICEDESC* halDesc, D3DDEVICEDESC* helDesc)
{
halDesc->dcmColorModel = D3DCOLORMODEL::RGB;
halDesc->dwFlags = D3DDD_DEVICEZBUFFERBITDEPTH;
halDesc->dwDeviceZBufferBitDepth = DDBD_24; // Todo add support for other depths
helDesc->dwDeviceRenderBitDepth = DDBD_8 | DDBD_16 | DDBD_24 | DDBD_32;
halDesc->dpcTriCaps.dwTextureCaps = D3DPTEXTURECAPS_PERSPECTIVE;
halDesc->dpcTriCaps.dwShadeCaps = D3DPSHADECAPS_ALPHAFLATBLEND;
halDesc->dpcTriCaps.dwTextureFilterCaps = D3DPTFILTERCAPS_LINEAR;
memset(helDesc, 0, sizeof(D3DDEVICEDESC));
}
const char* OpenGL15Renderer::GetName()
{
return "OpenGL 1.5 Renderer";
}
HRESULT OpenGL15Renderer::Render()
{
if (!m_backbuffer) {
return DDERR_GENERIC;
}
SDL_GL_MakeCurrent(DDWindow, m_context);
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
glViewport(0, 0, m_width, m_height);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Disable all lights and reset global ambient
for (int i = 0; i < 8; ++i) {
glDisable(GL_LIGHT0 + i);
}
const GLfloat zeroAmbient[4] = {0.f, 0.f, 0.f, 1.f};
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, zeroAmbient);
// Setup lights
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
int lightIdx = 0;
for (const auto& l : m_lights) {
if (lightIdx > 7) {
break;
}
GLenum lightId = GL_LIGHT0 + lightIdx++;
const FColor& c = l.color;
GLfloat col[4] = {c.r, c.g, c.b, c.a};
GLfloat pos[4];
if (l.positional == 0.f && l.directional == 0.f) {
// Ambient light only
glLightfv(lightId, GL_AMBIENT, col);
const GLfloat black[4] = {0.f, 0.f, 0.f, 1.f};
glLightfv(lightId, GL_DIFFUSE, black);
glLightfv(lightId, GL_SPECULAR, black);
const GLfloat dummyPos[4] = {0.f, 0.f, 1.f, 0.f};
glLightfv(lightId, GL_POSITION, dummyPos);
}
else {
glLightfv(lightId, GL_AMBIENT, zeroAmbient);
glLightfv(lightId, GL_DIFFUSE, col);
glLightfv(lightId, GL_SPECULAR, col);
if (l.directional == 1.f) {
pos[0] = -l.direction.x;
pos[1] = -l.direction.y;
pos[2] = -l.direction.z;
pos[3] = 0.f;
}
else {
pos[0] = l.position.x;
pos[1] = l.position.y;
pos[2] = l.position.z;
pos[3] = 1.f;
}
glLightfv(lightId, GL_POSITION, pos);
}
glEnable(lightId);
}
glPopMatrix();
// Projection and view
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(&m_projection[0][0]);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// Render geometry
glBegin(GL_TRIANGLES);
for (const auto& v : m_vertices) {
glColor4ub(v.colors.r, v.colors.g, v.colors.b, v.colors.a);
glNormal3f(v.normals.x, v.normals.y, v.normals.z);
glTexCoord2f(v.texCoord.u, v.texCoord.v);
// Set per-vertex specular material
glMaterialf(GL_FRONT, GL_SHININESS, v.shininess);
if (v.shininess != 0.0f) {
GLfloat whiteSpec[] = {v.shininess, v.shininess, v.shininess, v.shininess};
glMaterialfv(GL_FRONT, GL_SPECULAR, whiteSpec);
}
else {
GLfloat noSpec[] = {0.f, 0.f, 0.f, 1.f};
glMaterialfv(GL_FRONT, GL_SPECULAR, noSpec);
}
glVertex3f(v.position.x, v.position.y, v.position.z);
}
glEnd();
glReadPixels(0, 0, m_width, m_height, GL_RGBA, GL_UNSIGNED_BYTE, m_renderedImage->pixels);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Composite onto SDL backbuffer
SDL_BlitSurface(m_renderedImage, nullptr, m_backbuffer, nullptr);
return DD_OK;
}

View File

@ -388,36 +388,7 @@ HRESULT Direct3DRMSDL3GPURenderer::Render()
SDL_DestroySurface(m_renderedImage); SDL_DestroySurface(m_renderedImage);
SDL_UnmapGPUTransferBuffer(m_device, m_downloadTransferBuffer); SDL_UnmapGPUTransferBuffer(m_device, m_downloadTransferBuffer);
m_renderedImage = convertedRender; m_renderedImage = convertedRender;
return Blit(); SDL_BlitSurface(m_renderedImage, nullptr, m_backbuffer, nullptr);
}
HRESULT Direct3DRMSDL3GPURenderer::Blit()
{
// Blit the render back to our backbuffer
SDL_Rect srcRect{0, 0, (int) m_width, (int) m_height};
const SDL_PixelFormatDetails* details = SDL_GetPixelFormatDetails(m_backbuffer->format);
if (details->Amask != 0) {
// Backbuffer supports transparnacy
SDL_Surface* convertedRender = SDL_ConvertSurface(m_renderedImage, m_backbuffer->format);
SDL_DestroySurface(m_renderedImage);
m_renderedImage = convertedRender;
return DD_OK;
}
if (m_renderedImage->format == m_backbuffer->format) {
// No conversion needed
SDL_BlitSurface(m_renderedImage, &srcRect, m_backbuffer, &srcRect);
return DD_OK;
}
// Convert backbuffer to a format that supports transparancy
SDL_Surface* tempBackbuffer = SDL_ConvertSurface(m_backbuffer, m_renderedImage->format);
SDL_BlitSurface(m_renderedImage, &srcRect, tempBackbuffer, &srcRect);
// Then convert the result back to the backbuffer format and write it back
SDL_Surface* newBackBuffer = SDL_ConvertSurface(tempBackbuffer, m_backbuffer->format);
SDL_DestroySurface(tempBackbuffer);
SDL_BlitSurface(newBackBuffer, &srcRect, m_backbuffer, &srcRect);
SDL_DestroySurface(newBackBuffer);
return DD_OK; return DD_OK;
} }

View File

@ -7,6 +7,9 @@
#include "d3drmmesh_impl.h" #include "d3drmmesh_impl.h"
#include "d3drmobject_impl.h" #include "d3drmobject_impl.h"
#include "d3drmrenderer.h" #include "d3drmrenderer.h"
#ifdef USE_OPENGL15
#include "d3drmrenderer_opengl15.h"
#endif
#include "d3drmrenderer_sdl3gpu.h" #include "d3drmrenderer_sdl3gpu.h"
#include "d3drmrenderer_software.h" #include "d3drmrenderer_software.h"
#include "d3drmtexture_impl.h" #include "d3drmtexture_impl.h"
@ -141,6 +144,11 @@ HRESULT Direct3DRMImpl::CreateDeviceFromSurface(
else if (SDL_memcmp(&guid, &SOFTWARE_GUID, sizeof(GUID)) == 0) { else if (SDL_memcmp(&guid, &SOFTWARE_GUID, sizeof(GUID)) == 0) {
renderer = new Direct3DRMSoftwareRenderer(DDSDesc.dwWidth, DDSDesc.dwHeight); renderer = new Direct3DRMSoftwareRenderer(DDSDesc.dwWidth, DDSDesc.dwHeight);
} }
#ifdef USE_OPENGL15
else if (SDL_memcmp(&guid, &OPENGL15_GUID, sizeof(GUID)) == 0) {
renderer = OpenGL15Renderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight);
}
#endif
else { else {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Device GUID not recognized"); SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Device GUID not recognized");
return E_NOINTERFACE; return E_NOINTERFACE;

View File

@ -1,3 +1,6 @@
#ifdef USE_OPENGL15
#include "d3drmrenderer_opengl15.h"
#endif
#include "d3drmrenderer_sdl3gpu.h" #include "d3drmrenderer_sdl3gpu.h"
#include "d3drmrenderer_software.h" #include "d3drmrenderer_software.h"
#include "ddpalette_impl.h" #include "ddpalette_impl.h"
@ -224,6 +227,9 @@ void EnumDevice(LPD3DENUMDEVICESCALLBACK cb, void* ctx, Direct3DRMRenderer* devi
HRESULT DirectDrawImpl::EnumDevices(LPD3DENUMDEVICESCALLBACK cb, void* ctx) HRESULT DirectDrawImpl::EnumDevices(LPD3DENUMDEVICESCALLBACK cb, void* ctx)
{ {
Direct3DRMSDL3GPU_EnumDevice(cb, ctx); Direct3DRMSDL3GPU_EnumDevice(cb, ctx);
#ifdef USE_OPENGL15
OpenGL15Renderer_EnumDevice(cb, ctx);
#endif
Direct3DRMSoftware_EnumDevice(cb, ctx); Direct3DRMSoftware_EnumDevice(cb, ctx);
return S_OK; return S_OK;
@ -314,6 +320,11 @@ HRESULT DirectDrawImpl::CreateDevice(
if (SDL_memcmp(&guid, &SDL3_GPU_GUID, sizeof(GUID)) == 0) { if (SDL_memcmp(&guid, &SDL3_GPU_GUID, sizeof(GUID)) == 0) {
renderer = Direct3DRMSDL3GPURenderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight); renderer = Direct3DRMSDL3GPURenderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight);
} }
#ifdef USE_OPENGL15
else if (SDL_memcmp(&guid, &OPENGL15_GUID, sizeof(GUID)) == 0) {
renderer = OpenGL15Renderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight);
}
#endif
else if (SDL_memcmp(&guid, &SOFTWARE_GUID, sizeof(GUID)) == 0) { else if (SDL_memcmp(&guid, &SOFTWARE_GUID, sizeof(GUID)) == 0) {
renderer = new Direct3DRMSoftwareRenderer(DDSDesc.dwWidth, DDSDesc.dwHeight); renderer = new Direct3DRMSoftwareRenderer(DDSDesc.dwWidth, DDSDesc.dwHeight);
} }

View File

@ -0,0 +1,49 @@
#pragma once
#include "d3drmrenderer.h"
#include "d3drmtexture_impl.h"
#include "ddraw_impl.h"
#include <GL/glew.h>
#include <SDL3/SDL.h>
#include <vector>
DEFINE_GUID(OPENGL15_GUID, 0x682656F3, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03);
class OpenGL15Renderer : public Direct3DRMRenderer {
public:
static Direct3DRMRenderer* Create(DWORD width, DWORD height);
OpenGL15Renderer(int width, int height, SDL_GLContext context, GLuint fbo, GLuint colorTex, GLuint depthRb);
~OpenGL15Renderer() override;
void SetBackbuffer(SDL_Surface* surface) override;
void PushVertices(const PositionColorVertex* verts, size_t count) override;
void PushLights(const SceneLight* lightsArray, size_t count) override;
void SetProjection(D3DRMMATRIX4D perspective, D3DVALUE front, D3DVALUE back) override;
Uint32 GetTextureId(IDirect3DRMTexture* texture) override;
DWORD GetWidth() override;
DWORD GetHeight() override;
void GetDesc(D3DDEVICEDESC* halDesc, D3DDEVICEDESC* helDesc) override;
const char* GetName() override;
HRESULT Render() override;
private:
SDL_GLContext m_context;
D3DRMMATRIX4D m_projection;
SDL_Surface* m_backbuffer = nullptr;
SDL_Surface* m_renderedImage;
int m_width, m_height;
std::vector<PositionColorVertex> m_vertices;
std::vector<SceneLight> m_lights;
GLuint m_fbo = 0;
GLuint m_colorTex = 0;
GLuint m_depthRb = 0;
};
inline static void OpenGL15Renderer_EnumDevice(LPD3DENUMDEVICESCALLBACK cb, void* ctx)
{
Direct3DRMRenderer* device = OpenGL15Renderer::Create(640, 480);
if (device) {
EnumDevice(cb, ctx, device, OPENGL15_GUID);
delete device;
}
}