3D graphics now working

This commit is contained in:
izzy2lost
2025-12-17 00:00:04 -05:00
parent ea0bfaed3f
commit f5f14fc051
16 changed files with 2018 additions and 29 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 186 KiB

+5 -1
View File
@@ -1,7 +1,11 @@
#ifndef _GLSLSHADER_H_
#define _GLSLSHADER_H_
#ifdef __ANDROID__
#include <GLES3/gl3.h>
#else
#include <GL/glew.h>
#endif
#include <map>
#include <cstring>
@@ -58,4 +62,4 @@ private:
};
#endif
#endif
+86 -1
View File
@@ -18,7 +18,9 @@ namespace New3D {
CNew3D::CNew3D(const Util::Config::Node &config, const std::string& gameName)
: m_r3dShader(config),
#ifndef __ANDROID__
m_r3dScrollFog(config),
#endif
m_gameName(gameName)
{
m_cullingRAMLo = nullptr;
@@ -31,10 +33,12 @@ CNew3D::CNew3D(const Util::Config::Node &config, const std::string& gameName)
m_numPolyVerts = 3;
m_primType = GL_TRIANGLES;
#ifndef __ANDROID__
if (config["QuadRendering"].ValueAs<bool>()) {
m_numPolyVerts = 4;
m_primType = GL_LINES_ADJACENCY;
}
#endif
}
CNew3D::~CNew3D()
@@ -84,8 +88,9 @@ bool CNew3D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yR
m_totalYRes = totalYResParam;
m_r3dShader.LoadShader();
#ifndef __ANDROID__
m_r3dFrameBuffers.CreateFBO(totalXResParam, totalYResParam);
#endif
glUseProgram(0);
@@ -117,6 +122,7 @@ void CNew3D::UploadTextures(unsigned level, unsigned x, unsigned y, unsigned wid
void CNew3D::DrawScrollFog()
{
#ifndef __ANDROID__
// this is my best guess at the logic based upon what games are doing
//
// ocean hunter - every viewport has scroll fog values set. Must start with lowest priority layers as the higher ones sometimes are garbage
@@ -166,6 +172,7 @@ CheckScroll:
}
}
}
#endif
}
bool CNew3D::RenderScene(int priority, bool renderOverlay, Layer layer)
@@ -306,6 +313,78 @@ void CNew3D::DisableRenderStates()
void CNew3D::RenderFrame(void)
{
#ifdef __ANDROID__
// Android/GLES: basic mesh path (no multi-pass transparency compositing yet).
for (int i = 0; i < 4; i++) {
m_nfPairs[i].zNear = -std::numeric_limits<float>::max();
m_nfPairs[i].zFar = std::numeric_limits<float>::max();
}
{
std::lock_guard<std::mutex> guard(m_losMutex);
std::swap(m_losBack, m_losFront);
for (int i = 0; i < 4; i++) {
m_losBack->value[i] = 0;
}
}
m_polyBufferRam.clear();
m_nodes.clear();
m_modelMat.Release();
m_nodeAttribs.Reset();
RenderViewport(0x800000);
m_vbo.Bind(true);
m_vbo.BufferSubData(MAX_ROM_VERTS*sizeof(FVertex), m_polyBufferRam.size()*sizeof(FVertex), m_polyBufferRam.data());
if (!m_polyBufferRom.empty()) {
int romBytes = (int)m_polyBufferRom.size() * sizeof(FVertex);
int vboBytes = m_vbo.GetSize();
int size = romBytes - vboBytes;
if (size) {
if (m_polyBufferRom.size() >= MAX_ROM_VERTS) {
m_polyBufferRom.clear();
m_romMap.clear();
m_vbo.Reset();
}
else {
m_vbo.AppendData(size, &m_polyBufferRom[vboBytes / sizeof(FVertex)]);
}
}
}
// Draw to default framebuffer.
glDisable(GL_STENCIL_TEST);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
for (int pri = 0; pri <= 3; pri++) {
if (SkipLayer(pri)) continue;
// Opaque pass.
SetRenderStates();
m_r3dShader.DiscardAlpha(true);
glDisable(GL_BLEND);
RenderScene(pri, false, Layer::colour);
RenderScene(pri, true, Layer::colour);
DisableRenderStates();
// Translucent pass (rough): draw both layers with blending.
SetRenderStates();
m_r3dShader.DiscardAlpha(false);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDepthMask(GL_FALSE);
RenderScene(pri, false, Layer::trans1);
RenderScene(pri, false, Layer::trans2);
RenderScene(pri, true, Layer::trans1);
RenderScene(pri, true, Layer::trans2);
glDepthMask(GL_TRUE);
DisableRenderStates();
}
return;
#else
for (int i = 0; i < 4; i++) {
m_nfPairs[i].zNear = -std::numeric_limits<float>::max();
m_nfPairs[i].zFar = std::numeric_limits<float>::max();
@@ -400,6 +479,7 @@ void CNew3D::RenderFrame(void)
}
m_r3dFrameBuffers.CompositeAlphaLayer();
#endif
}
void CNew3D::BeginFrame(void)
@@ -1636,7 +1716,12 @@ void CNew3D::CalcViewport(Viewport* vp, float near, float far)
// and only their scissor box differs)
float correction = windowAR / viewableAreaAR;
// Android: use x offset for letterboxed "viewable area" within the physical window.
#ifdef __ANDROID__
vp->x = m_xOffs;
#else
vp->x = 0;
#endif
vp->y = m_yOffs + (int)((float)(384 - (vp->vpY + vp->vpHeight))*m_yRatio);
vp->width = m_totalXRes;
vp->height = (int)((float)vp->vpHeight*m_yRatio);
+10
View File
@@ -29,7 +29,11 @@
#ifndef INCLUDED_NEW3D_H
#define INCLUDED_NEW3D_H
#ifdef __ANDROID__
#include <GLES3/gl3.h>
#else
#include <GL/glew.h>
#endif
#include "Types.h"
#include "TextureSheet.h"
#include "Graphics/IRender3D.h"
@@ -40,9 +44,13 @@
#include "R3DData.h"
#include "Plane.h"
#include "Vec.h"
#ifndef __ANDROID__
#include "R3DScrollFog.h"
#endif
#include "PolyHeader.h"
#ifndef __ANDROID__
#include "R3DFrameBuffers.h"
#endif
#include <mutex>
namespace New3D {
@@ -282,8 +290,10 @@ private:
VBO m_vbo; // large VBO to hold our poly data, start of VBO is ROM data, ram polys follow
R3DShader m_r3dShader;
#ifndef __ANDROID__
R3DScrollFog m_r3dScrollFog;
R3DFrameBuffers m_r3dFrameBuffers;
#endif
Plane m_planes[5];
+4
View File
@@ -1,7 +1,11 @@
#ifndef FBO_H
#define FBO_H
#ifdef __ANDROID__
#include <GLES3/gl3.h>
#else
#include <GL/glew.h>
#endif
#include "VBO.h"
#include "GLSLShader.h"
#include "Model.h"
+10
View File
@@ -1,5 +1,7 @@
#include "R3DShader.h"
#ifndef __ANDROID__
#include "R3DShaderQuads.h"
#endif
#include "R3DShaderTriangles.h"
// having 2 sets of shaders to maintain is really less than ideal
@@ -48,16 +50,22 @@ void R3DShader::Start()
bool R3DShader::LoadShader(const char* vertexShader, const char* fragmentShader)
{
bool quads = m_config["QuadRendering"].ValueAs<bool>();
#ifdef __ANDROID__
// GLES 3.0: no geometry shaders / adjacency in the baseline path.
quads = false;
#endif
const char* vShader = vertexShaderR3D;
const char* gShader = "";
const char* fShader = fragmentShaderR3D;
#ifndef __ANDROID__
if (quads) {
vShader = vertexShaderR3DQuads;
gShader = geometryShaderR3DQuads;
fShader = fragmentShaderR3DQuads;
}
#endif
m_shaderProgram = glCreateProgram();
m_vertexShader = glCreateShader(GL_VERTEX_SHADER);
@@ -69,6 +77,7 @@ bool R3DShader::LoadShader(const char* vertexShader, const char* fragmentShader)
glCompileShader(m_vertexShader);
glCompileShader(m_fragmentShader);
#ifndef __ANDROID__
if (quads) {
m_geoShader = glCreateShader(GL_GEOMETRY_SHADER);
glShaderSource(m_geoShader, 1, (const GLchar **)&gShader, NULL);
@@ -76,6 +85,7 @@ bool R3DShader::LoadShader(const char* vertexShader, const char* fragmentShader)
glAttachShader(m_shaderProgram, m_geoShader);
PrintShaderResult(m_geoShader);
}
#endif
PrintShaderResult(m_vertexShader);
PrintShaderResult(m_fragmentShader);
+4
View File
@@ -1,7 +1,11 @@
#ifndef _R3DSHADER_H_
#define _R3DSHADER_H_
#ifdef __ANDROID__
#include <GLES3/gl3.h>
#else
#include <GL/glew.h>
#endif
#include "Util/NewConfig.h"
#include "Model.h"
#include <unordered_map>
+83 -2
View File
@@ -1,6 +1,86 @@
#ifndef _R3DSHADERTRIANGLES_H_
#define _R3DSHADERTRIANGLES_H_
#ifdef __ANDROID__
// GLES 3.0 shaders (minimal path: textured vertex-colour with optional alpha discard).
static const char *vertexShaderR3D = R"glsl(
#version 300 es
precision highp float;
// uniforms
uniform float modelScale;
uniform mat4 modelMat;
uniform mat4 projMat;
uniform bool translatorMap;
// attributes
layout(location=0) in vec4 inVertex;
layout(location=1) in vec3 inNormal;
layout(location=2) in vec2 inTexCoord;
layout(location=3) in vec4 inColour; // normalized UBYTE -> float
layout(location=4) in vec3 inFaceNormal;
layout(location=5) in float inFixedShade;
out vec2 vTexCoord;
out vec4 vColor;
out float vAlpha;
out vec3 vDummy; // keeps attributes alive
void main()
{
vec4 c = inColour;
if (translatorMap) c.rgb *= 16.0;
vColor = c;
vTexCoord = inTexCoord;
vAlpha = c.a;
vDummy = inNormal + inFaceNormal + vec3(inFixedShade);
gl_Position = projMat * modelMat * inVertex;
}
)glsl";
static const char *fragmentShaderR3D = R"glsl(
#version 300 es
precision mediump float;
in vec2 vTexCoord;
in vec4 vColor;
in float vAlpha;
in vec3 vDummy;
uniform sampler2D tex1;
uniform bool textureEnabled;
uniform bool textureAlpha;
uniform bool alphaTest;
uniform bool discardAlpha;
out vec4 oColor;
void main()
{
vec4 col = vColor;
if (textureEnabled) {
vec4 t = texture(tex1, vTexCoord);
if (textureAlpha) {
col *= t;
} else {
col.rgb *= t.rgb;
}
}
// keep vDummy "used"
col.rgb += vDummy * 0.0;
if (alphaTest && col.a < 0.5) discard;
if (discardAlpha && col.a < 0.99) discard;
oColor = col;
}
)glsl";
#else
static const char *vertexShaderR3D = R"glsl(
#version 120
@@ -35,7 +115,7 @@ vec4 GetColour(vec4 colour)
c.rgb *= 16.0;
}
return c;
return c;
}
float CalcBackFace(in vec3 viewVertex)
@@ -424,5 +504,6 @@ void main()
gl_FragColor = finalData;
}
)glsl";
#endif
#endif
#endif
+4
View File
@@ -2,7 +2,11 @@
#define _TEXTURE_H_
#include "Types.h"
#ifdef __ANDROID__
#include <GLES3/gl3.h>
#else
#include <GL/glew.h>
#endif
namespace New3D {
+4
View File
@@ -1,7 +1,11 @@
#ifndef _VBO_H_
#define _VBO_H_
#ifdef __ANDROID__
#include <GLES3/gl3.h>
#else
#include <GL/glew.h>
#endif
namespace New3D {
+2
View File
@@ -54,9 +54,11 @@ public:
bool Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes, unsigned totalXRes, unsigned totalYRes);
const uint32_t* GetFrameBufferRGBA() const { return m_frame.data(); }
const uint32_t* GetTopSurfaceARGB() const { return m_topSurface.data(); }
unsigned GetFrameWidth() const { return m_xPixels; }
unsigned GetFrameHeight() const { return m_yPixels; }
bool HasFrame() const { return !m_frame.empty(); }
bool HasTopSurface() const { return m_surfacesPresent.first; }
private:
std::pair<bool, bool> DrawTilemaps(uint32_t *pixelsBottom, uint32_t *pixelsTop);
+10
View File
@@ -115,6 +115,16 @@ add_library(super3 SHARED
gles_presenter.cpp
gles_stub_render3d.cpp
render2d_android.cpp
"${REPO_ROOT}/Src/Graphics/New3D/Mat4.cpp"
"${REPO_ROOT}/Src/Graphics/New3D/Model.cpp"
"${REPO_ROOT}/Src/Graphics/New3D/New3D.cpp"
"${REPO_ROOT}/Src/Graphics/New3D/PolyHeader.cpp"
"${REPO_ROOT}/Src/Graphics/New3D/R3DFloat.cpp"
"${REPO_ROOT}/Src/Graphics/New3D/R3DShader.cpp"
"${REPO_ROOT}/Src/Graphics/New3D/Texture.cpp"
"${REPO_ROOT}/Src/Graphics/New3D/TextureSheet.cpp"
"${REPO_ROOT}/Src/Graphics/New3D/VBO.cpp"
"${REPO_ROOT}/Src/Graphics/New3D/Vec.cpp"
${SUPER3_SOURCES}
${M68K_GENERATED_SOURCES}
)
+10 -2
View File
@@ -206,7 +206,7 @@ void GlesPresenter::UpdateQuadVerts()
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void GlesPresenter::Render()
void GlesPresenter::Render(bool alphaBlend)
{
if (!m_program || !m_vao || !m_tex || m_outputW <= 0 || m_outputH <= 0)
return;
@@ -214,6 +214,15 @@ void GlesPresenter::Render()
glViewport(0, 0, m_outputW, m_outputH);
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glDisable(GL_STENCIL_TEST);
if (alphaBlend) {
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
} else {
glDisable(GL_BLEND);
}
glUseProgram(m_program);
glActiveTexture(GL_TEXTURE0);
@@ -227,4 +236,3 @@ void GlesPresenter::Render()
glBindTexture(GL_TEXTURE_2D, 0);
glUseProgram(0);
}
+1 -2
View File
@@ -16,7 +16,7 @@ public:
// pixelsARGB: 0xAARRGGBB packed.
void UpdateFrameARGB(const uint32_t* pixelsARGB, int width, int height);
void Render();
void Render(bool alphaBlend);
private:
bool CreateProgram();
@@ -40,4 +40,3 @@ private:
// Converted upload buffer in RGBA8.
std::vector<uint8_t> m_uploadRGBA;
};
+75 -21
View File
@@ -28,6 +28,7 @@
#include "android_input_system.h"
#include "gles_presenter.h"
#include "gles_stub_render3d.h"
#include "Graphics/New3D/New3D.h"
// Minimal OSD glue -----------------------------------------------------------
@@ -62,7 +63,9 @@ struct Super3Host {
AndroidInputSystem inputSystem;
CInputs inputs{&inputSystem};
StubOutputs outputs;
GlesStubRender3D render3d;
GlesStubRender3D stub3d;
std::unique_ptr<New3D::CNew3D> new3d;
IRender3D* render3d = &stub3d;
CRender2D render2d{config};
std::unique_ptr<GameLoader> loader;
@@ -240,7 +243,7 @@ struct Super3Host {
// TileGen allocates and wires VRAM/palette/register pointers during Init();
// attach renderers after Init()/LoadGame() so Render2D sees valid pointers.
SDL_Log("Attaching renderers...");
model3->AttachRenderers(&render2d, &render3d);
model3->AttachRenderers(&render2d, render3d);
// Establish initial CPU/device state (ppc_reset(), etc).
SDL_Log("Model3 Reset...");
@@ -270,6 +273,42 @@ struct Super3Host {
model3->RunFrame();
}
}
bool InstallNew3D(unsigned totalXRes, unsigned totalYRes)
{
if (!model3 || new3d)
return !!new3d;
// Letterbox the Model 3 496x384 output into the physical drawable area.
// We pass the "viewable area" size to New3D so it doesn't wide-screen expand the frustum.
const float targetAR = 496.0f / 384.0f;
unsigned viewW = totalXRes;
unsigned viewH = totalYRes;
const float windowAR = (float)totalXRes / (float)totalYRes;
if (windowAR > targetAR) {
viewH = totalYRes;
viewW = (unsigned)std::lround((double)totalYRes * targetAR);
} else {
viewW = totalXRes;
viewH = (unsigned)std::lround((double)totalXRes / targetAR);
}
const unsigned xOff = (totalXRes - viewW) / 2;
const unsigned yOff = (totalYRes - viewH) / 2;
SDL_Log("Initializing New3D (GLES) ...");
new3d = std::make_unique<New3D::CNew3D>(config, game.name);
if (new3d->Init(xOff, yOff, viewW, viewH, viewW, viewH) != 0)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "New3D Init failed");
new3d.reset();
return false;
}
render3d = new3d.get();
model3->AttachRenderers(&render2d, render3d);
SDL_Log("New3D attached");
return true;
}
};
static std::string JoinPath(const std::string& a, const std::string& b)
@@ -352,7 +391,6 @@ extern "C" int SDL_main(int argc, char* argv[]) {
// Initialize renderer backends up-front. The core will attach VRAM/palette/register
// pointers later (after it has initialized the tile generator).
host.render2d.Init(0, 0, 496, 384, 496, 384);
host.render3d.Init(0, 0, 496, 384, 496, 384);
// Locate resources. Prefer app-specific external storage (no runtime perms),
// but also probe common legacy locations for dev convenience.
@@ -412,6 +450,7 @@ extern "C" int SDL_main(int argc, char* argv[]) {
bool backgrounded = false;
bool loggedControls = false;
bool audioOpened = false;
bool new3dAttached = false;
while (running) {
SDL_Event ev;
while (SDL_PollEvent(&ev)) {
@@ -457,6 +496,11 @@ extern "C" int SDL_main(int argc, char* argv[]) {
continue;
}
int winW = 0, winH = 0;
SDL_GL_GetDrawableSize(window, &winW, &winH);
if (winW <= 0) winW = 1;
if (winH <= 0) winH = 1;
const int state = loadState.load(std::memory_order_acquire);
if (state == 1) {
if (!audioOpened) {
@@ -470,27 +514,37 @@ extern "C" int SDL_main(int argc, char* argv[]) {
loggedControls = true;
SDL_Log("Controls (touch): bottom-left=COIN, bottom-right=START, top-left=SERVICE, top-right=TEST, left-middle=DPAD/STEER, right-middle=THROTTLE/BRAKE");
}
host.RunFrame();
}
int winW = 0, winH = 0;
SDL_GL_GetDrawableSize(window, &winW, &winH);
presenter.Resize(winW, winH);
if (!new3dAttached) {
new3dAttached = host.InstallNew3D((unsigned)winW, (unsigned)winH);
}
glClearColor(0.f, 0.f, 0.f, 1.f);
glClear(GL_COLOR_BUFFER_BIT);
if (new3dAttached) {
// 3D path: let New3D draw into the default framebuffer from inside the core.
glViewport(0, 0, winW, winH);
glClearColor(0.f, 0.f, 0.f, 1.f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
host.RunFrame();
if (state == 1 && host.render2d.HasFrame()) {
const uint32_t* pixels = host.render2d.GetFrameBufferRGBA();
presenter.UpdateFrameARGB(pixels, (int)host.render2d.GetFrameWidth(), (int)host.render2d.GetFrameHeight());
presenter.Render();
// Debug: draw a small triangle on top so we can verify GLES is working
// and we have a valid 3D hook. The real integration will happen inside
// the GPU/TileGen render path once New3D is ported.
host.render3d.BeginFrame();
host.render3d.RenderFrame();
host.render3d.EndFrame();
// Overlay TileGen top layers (HUD/menus) on top of 3D.
presenter.Resize(winW, winH);
if (host.render2d.HasTopSurface()) {
const uint32_t* pixels = host.render2d.GetTopSurfaceARGB();
presenter.UpdateFrameARGB(pixels, (int)host.render2d.GetFrameWidth(), (int)host.render2d.GetFrameHeight());
presenter.Render(true);
}
} else {
// 2D-only path: keep showing TileGen software output.
host.RunFrame();
presenter.Resize(winW, winH);
glClearColor(0.f, 0.f, 0.f, 1.f);
glClear(GL_COLOR_BUFFER_BIT);
if (host.render2d.HasFrame()) {
const uint32_t* pixels = host.render2d.GetFrameBufferRGBA();
presenter.UpdateFrameARGB(pixels, (int)host.render2d.GetFrameWidth(), (int)host.render2d.GetFrameHeight());
presenter.Render(false);
}
}
}
SDL_GL_SwapWindow(window);
File diff suppressed because it is too large Load Diff