Added experimental real3d option

This commit is contained in:
izzy2lost
2025-12-19 15:20:17 -05:00
parent 04184d8567
commit a3dfb04eb8
11 changed files with 435 additions and 63 deletions
+9 -6
View File
@@ -13,21 +13,24 @@ This app does **not** include any games, ROMs, BIOS files, or copyrighted game c
- In-game pause/resume button.
## Video settings (Resolution & Widescreen)
From the main game list, open the side menu (toolbar menu icon) and look under **Video**.
From the main game list, open the side menu (toolbar "menu" icon) and look under **Video**.
- **Resolution:** Native (496×384) and integer scale options (2× through 8×). Higher values look sharper but can reduce performance.
- **Match device resolution:** Sets the internal resolution to your devices exact pixel resolution (landscape). This also enables **Widescreen** and **Wide background**.
- **Resolution:** Native (496x384) and integer scale options (2x through 8x). Higher values look sharper but can reduce performance.
- **Match device resolution:** Sets the internal resolution to your device's exact pixel resolution (landscape). This also enables **Widescreen** and **Wide background**.
- **Widescreen:** Expands the horizontal field of view (Supermodel `-wide-screen`). At very wide aspect ratios some games may cull objects at the edges.
- **Wide background:** Stretches the 2D background layers when using widescreen (Supermodel `-wide-bg`).
- **Enhanced Real3D (experimental):** Enables more desktop-like Real3D rendering (multi-pass transparency compositing + scroll fog). This can improve visuals in some games/areas, but may reduce performance on lower-end devices.
These settings are written to `.../Android/data/<package>/files/super3/Config/Supermodel.ini` (`XResolution`, `YResolution`, `WideScreen`, `WideBackground`, `New3DAccurate`).
## Files & Storage
- Save states and thumbnails are stored under the apps user data directory in `Saves/` (for example: `.../Android/data/<package>/files/super3/Saves/`).
- The app may offer an optional sync/export workflow using Androids Storage Access Framework. If enabled, the app only accesses the folder(s) you explicitly choose.
- Save states and thumbnails are stored under the app's user data directory in `Saves/` (for example: `.../Android/data/<package>/files/super3/Saves/`).
- The app may offer an optional "sync"/export workflow using Android's Storage Access Framework. If enabled, the app only accesses the folder(s) you explicitly choose.
## Legal / Google Play Policy Notes
- **No games included:** SUPER3 does not ship with ROMs or game data, and it is not intended to facilitate piracy.
- **You must own the games:** Only use ROMs you are legally permitted to use in your region.
- **Trademarks:** SEGA and all associated game names, trademarks, and copyrighted works are the property of their respective owners.
- **Trademarks:** "SEGA" and all associated game names, trademarks, and copyrighted works are the property of their respective owners.
- **Not affiliated:** This project is not affiliated with or endorsed by SEGA or any arcade hardware manufacturer.
## Privacy
+43 -21
View File
@@ -17,10 +17,8 @@
namespace New3D {
CNew3D::CNew3D(const Util::Config::Node &config, const std::string& gameName)
: m_r3dShader(config),
#ifndef __ANDROID__
m_r3dScrollFog(config),
#endif
: m_config(config),
m_r3dShader(config),
m_gameName(gameName)
{
m_cullingRAMLo = nullptr;
@@ -30,6 +28,7 @@ CNew3D::CNew3D(const Util::Config::Node &config, const std::string& gameName)
m_textureRAM = nullptr;
m_sunClamp = true;
m_shadeIsSigned = true;
m_new3dAccurate = config["New3DAccurate"].ValueAsDefault<bool>(false);
m_numPolyVerts = 3;
m_primType = GL_TRIANGLES;
@@ -89,7 +88,23 @@ bool CNew3D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yR
m_r3dShader.LoadShader();
#ifndef __ANDROID__
m_r3dFrameBuffers.CreateFBO(totalXResParam, totalYResParam);
if (!m_r3dFrameBuffers)
m_r3dFrameBuffers = std::make_unique<R3DFrameBuffers>();
if (!m_r3dScrollFog)
m_r3dScrollFog = std::make_unique<R3DScrollFog>(m_config);
m_r3dFrameBuffers->CreateFBO((int)totalXResParam, (int)totalYResParam);
#else
if (m_new3dAccurate) {
if (!m_r3dFrameBuffers)
m_r3dFrameBuffers = std::make_unique<R3DFrameBuffers>();
if (!m_r3dScrollFog)
m_r3dScrollFog = std::make_unique<R3DScrollFog>(m_config);
if (!m_r3dFrameBuffers->CreateFBO((int)totalXResParam, (int)totalYResParam)) {
m_r3dFrameBuffers.reset();
m_r3dScrollFog.reset();
m_new3dAccurate = false;
}
}
#endif
glUseProgram(0);
@@ -122,7 +137,11 @@ void CNew3D::UploadTextures(unsigned level, unsigned x, unsigned y, unsigned wid
void CNew3D::DrawScrollFog()
{
#ifndef __ANDROID__
#ifdef __ANDROID__
if (!m_new3dAccurate) {
return;
}
#endif
// 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
@@ -164,7 +183,9 @@ CheckScroll:
rgba[2] == n.viewport.fogParams[2]) {
glViewport(n.viewport.x, n.viewport.y, n.viewport.width, n.viewport.height);
m_r3dScrollFog.DrawScrollFog(rgba, n.viewport.scrollAtt, n.viewport.fogParams[6], n.viewport.spotFogColor, n.viewport.spotEllipse);
if (m_r3dScrollFog) {
m_r3dScrollFog->DrawScrollFog(rgba, n.viewport.scrollAtt, n.viewport.fogParams[6], n.viewport.spotFogColor, n.viewport.spotEllipse);
}
return;
}
}
@@ -172,7 +193,6 @@ CheckScroll:
}
}
}
#endif
}
bool CNew3D::RenderScene(int priority, bool renderOverlay, Layer layer)
@@ -314,7 +334,8 @@ void CNew3D::DisableRenderStates()
void CNew3D::RenderFrame(void)
{
#ifdef __ANDROID__
// Android/GLES: basic mesh path (no multi-pass transparency compositing yet).
if (!m_new3dAccurate) {
// 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();
@@ -383,8 +404,9 @@ void CNew3D::RenderFrame(void)
glDepthMask(GL_TRUE);
DisableRenderStates();
}
return;
#else
return;
}
#endif
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();
@@ -431,7 +453,8 @@ void CNew3D::RenderFrame(void)
}
}
m_r3dFrameBuffers.SetFBO(Layer::trans12);
if (!m_r3dFrameBuffers) return;
m_r3dFrameBuffers->SetFBO(Layer::trans12);
glClear(GL_COLOR_BUFFER_BIT); // wipe both trans layers
for (int pri = 0; pri <= 3; pri++) {
@@ -442,7 +465,7 @@ void CNew3D::RenderFrame(void)
bool renderOverlay = (i == 1);
m_r3dFrameBuffers.SetFBO(Layer::colour);
m_r3dFrameBuffers->SetFBO(Layer::colour);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
SetRenderStates();
@@ -456,20 +479,20 @@ void CNew3D::RenderFrame(void)
DisableRenderStates();
m_r3dFrameBuffers.DrawOverTransLayers(); // mask trans layer with opaque pixels
m_r3dFrameBuffers.CompositeBaseLayer(); // copy opaque pixels to back buffer
m_r3dFrameBuffers->DrawOverTransLayers(); // mask trans layer with opaque pixels
m_r3dFrameBuffers->CompositeBaseLayer(); // copy opaque pixels to back buffer
SetRenderStates();
glDepthFunc(GL_LESS); // alpha polys seem to use gl_less (ocean hunter)
m_r3dShader.DiscardAlpha (false); // render only translucent pixels
m_r3dFrameBuffers.StoreDepth (); // save depth buffer for 1st trans pass
m_r3dFrameBuffers.SetFBO (Layer::trans1);
m_r3dFrameBuffers->StoreDepth (); // save depth buffer for 1st trans pass
m_r3dFrameBuffers->SetFBO (Layer::trans1);
RenderScene (pri, renderOverlay, Layer::trans1);
m_r3dFrameBuffers.RestoreDepth (); // restore depth buffer, trans layers don't seem to depth test against each other
m_r3dFrameBuffers.SetFBO (Layer::trans2);
m_r3dFrameBuffers->RestoreDepth (); // restore depth buffer, trans layers don't seem to depth test against each other
m_r3dFrameBuffers->SetFBO (Layer::trans2);
RenderScene (pri, renderOverlay, Layer::trans2);
DisableRenderStates();
@@ -478,8 +501,7 @@ void CNew3D::RenderFrame(void)
}
}
m_r3dFrameBuffers.CompositeAlphaLayer();
#endif
m_r3dFrameBuffers->CompositeAlphaLayer();
}
void CNew3D::BeginFrame(void)
+6 -8
View File
@@ -39,19 +39,17 @@
#include "Graphics/IRender3D.h"
#include "Model.h"
#include "Mat4.h"
#include "Util/NewConfig.h"
#include "R3DShader.h"
#include "VBO.h"
#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>
#include <memory>
namespace New3D {
@@ -236,6 +234,7 @@ private:
*/
// Misc
const Util::Config::Node &m_config;
std::string m_gameName;
int m_numPolyVerts;
GLenum m_primType;
@@ -243,6 +242,7 @@ private:
// GPU configuration
bool m_sunClamp;
bool m_shadeIsSigned;
bool m_new3dAccurate;
// Stepping
int m_step;
@@ -290,10 +290,8 @@ 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
std::unique_ptr<R3DScrollFog> m_r3dScrollFog;
std::unique_ptr<R3DFrameBuffers> m_r3dFrameBuffers;
Plane m_planes[5];
+154 -21
View File
@@ -189,7 +189,12 @@ void R3DFrameBuffers::SetFBO(Layer layer)
case Layer::none:
{
glBindFramebuffer(GL_FRAMEBUFFER, 0);
#ifdef __ANDROID__
GLenum buffers[] = { GL_BACK };
glDrawBuffers(countof(buffers), buffers);
#else
glDrawBuffer(GL_BACK);
#endif
break;
}
}
@@ -199,6 +204,43 @@ void R3DFrameBuffers::SetFBO(Layer layer)
void R3DFrameBuffers::AllocShaderBase()
{
#ifdef __ANDROID__
const char *vertexShader = R"glsl(
#version 300 es
precision highp float;
in vec3 inVertex;
in vec2 inTexCoord;
out vec2 fsTexCoord;
void main(void)
{
fsTexCoord = inTexCoord;
gl_Position = vec4(inVertex,1.0);
}
)glsl";
const char *fragmentShader = R"glsl(
#version 300 es
precision mediump float;
uniform sampler2D tex1;
in vec2 fsTexCoord;
out vec4 oColor;
void main()
{
vec4 colBase = texture(tex1, fsTexCoord);
if(colBase.a < 1.0) discard;
oColor = colBase;
}
)glsl";
#else
const char *vertexShader = R"glsl(
#version 120
@@ -234,15 +276,62 @@ void R3DFrameBuffers::AllocShaderBase()
}
)glsl";
#endif
m_shaderBase.LoadShaders(vertexShader, fragmentShader);
m_shaderBase.uniformLoc[0] = m_shaderTrans.GetUniformLocation("tex1");
m_shaderBase.attribLoc[0] = m_shaderTrans.GetAttributeLocation("inVertex");
m_shaderBase.attribLoc[1] = m_shaderTrans.GetAttributeLocation("inTexCoord");
m_shaderBase.uniformLoc[0] = m_shaderBase.GetUniformLocation("tex1");
m_shaderBase.attribLoc[0] = m_shaderBase.GetAttributeLocation("inVertex");
m_shaderBase.attribLoc[1] = m_shaderBase.GetAttributeLocation("inTexCoord");
}
void R3DFrameBuffers::AllocShaderTrans()
{
#ifdef __ANDROID__
const char *vertexShader = R"glsl(
#version 300 es
precision highp float;
in vec3 inVertex;
in vec2 inTexCoord;
out vec2 fsTexCoord;
void main(void)
{
fsTexCoord = inTexCoord;
gl_Position = vec4(inVertex,1.0);
}
)glsl";
const char *fragmentShader = R"glsl(
#version 300 es
precision mediump float;
uniform sampler2D tex1;
uniform sampler2D tex2;
in vec2 fsTexCoord;
out vec4 oColor;
void main()
{
vec4 colTrans1 = texture(tex1, fsTexCoord);
vec4 colTrans2 = texture(tex2, fsTexCoord);
if(colTrans1.a + colTrans2.a > 0.0) {
vec3 col1 = colTrans1.rgb * colTrans1.a;
vec3 col2 = colTrans2.rgb * colTrans2.a;
colTrans1 = vec4((col1 + col2) / (colTrans1.a + colTrans2.a), colTrans1.a + colTrans2.a);
}
oColor = colTrans1;
}
)glsl";
#else
const char *vertexShader = R"glsl(
#version 120
@@ -288,6 +377,7 @@ void R3DFrameBuffers::AllocShaderTrans()
}
)glsl";
#endif
m_shaderTrans.LoadShaders(vertexShader, fragmentShader);
@@ -300,6 +390,48 @@ void R3DFrameBuffers::AllocShaderTrans()
void R3DFrameBuffers::AllocShaderWipe()
{
#ifdef __ANDROID__
const char *vertexShader = R"glsl(
#version 300 es
precision highp float;
in vec3 inVertex;
in vec2 inTexCoord;
out vec2 fsTexCoord;
void main(void)
{
fsTexCoord = inTexCoord;
gl_Position = vec4(inVertex,1.0);
}
)glsl";
const char *fragmentShader = R"glsl(
#version 300 es
precision mediump float;
uniform sampler2D texColor;
in vec2 fsTexCoord;
layout(location=0) out vec4 oLayer1;
layout(location=1) out vec4 oLayer2;
void main()
{
vec4 colBase = texture(texColor, fsTexCoord);
if(colBase.a == 0.0) {
discard;
}
oLayer1 = vec4(0.0);
oLayer2 = vec4(0.0);
}
)glsl";
#else
const char *vertexShader = R"glsl(
#version 120
@@ -339,13 +471,14 @@ void R3DFrameBuffers::AllocShaderWipe()
}
)glsl";
#endif
m_shaderWipe.LoadShaders(vertexShader, fragmentShader);
m_shaderWipe.uniformLoc[0] = m_shaderTrans.GetUniformLocation("texColor");
m_shaderWipe.uniformLoc[0] = m_shaderWipe.GetUniformLocation("texColor");
m_shaderWipe.attribLoc[0] = m_shaderTrans.GetAttributeLocation("inVertex");
m_shaderWipe.attribLoc[1] = m_shaderTrans.GetAttributeLocation("inTexCoord");
m_shaderWipe.attribLoc[0] = m_shaderWipe.GetAttributeLocation("inVertex");
m_shaderWipe.attribLoc[1] = m_shaderWipe.GetAttributeLocation("inTexCoord");
}
void R3DFrameBuffers::Draw()
@@ -437,16 +570,16 @@ void R3DFrameBuffers::DrawOverTransLayers()
m_shaderWipe.EnableShader();
glUniform1i(m_shaderWipe.uniformLoc[0], 0);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(m_shaderWipe.attribLoc[0]);
glEnableVertexAttribArray(m_shaderWipe.attribLoc[1]);
glVertexAttribPointer(m_shaderWipe.attribLoc[0], 3, GL_FLOAT, GL_FALSE, sizeof(FBVertex), (void*)offsetof(FBVertex, verts));
glVertexAttribPointer(m_shaderWipe.attribLoc[1], 2, GL_FLOAT, GL_FALSE, sizeof(FBVertex), (void*)offsetof(FBVertex, texCoords));
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(m_shaderWipe.attribLoc[0]);
glDisableVertexAttribArray(m_shaderWipe.attribLoc[1]);
m_shaderWipe.DisableShader();
@@ -456,18 +589,18 @@ void R3DFrameBuffers::DrawOverTransLayers()
void R3DFrameBuffers::DrawBaseLayer()
{
m_shaderBase.EnableShader();
glUniform1i(m_shaderTrans.uniformLoc[0], 0);
glUniform1i(m_shaderBase.uniformLoc[0], 0);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(m_shaderBase.attribLoc[0]);
glEnableVertexAttribArray(m_shaderBase.attribLoc[1]);
glVertexAttribPointer(m_shaderTrans.attribLoc[0], 3, GL_FLOAT, GL_FALSE, sizeof(FBVertex), (void*)offsetof(FBVertex, verts));
glVertexAttribPointer(m_shaderTrans.attribLoc[1], 2, GL_FLOAT, GL_FALSE, sizeof(FBVertex), (void*)offsetof(FBVertex, texCoords));
glVertexAttribPointer(m_shaderBase.attribLoc[0], 3, GL_FLOAT, GL_FALSE, sizeof(FBVertex), (void*)offsetof(FBVertex, verts));
glVertexAttribPointer(m_shaderBase.attribLoc[1], 2, GL_FLOAT, GL_FALSE, sizeof(FBVertex), (void*)offsetof(FBVertex, texCoords));
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(m_shaderBase.attribLoc[0]);
glDisableVertexAttribArray(m_shaderBase.attribLoc[1]);
m_shaderBase.DisableShader();
}
@@ -478,16 +611,16 @@ void R3DFrameBuffers::DrawAlphaLayer()
glUniform1i(m_shaderTrans.uniformLoc[0], 1); // tex unit 1
glUniform1i(m_shaderTrans.uniformLoc[1], 2); // tex unit 2
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(m_shaderTrans.attribLoc[0]);
glEnableVertexAttribArray(m_shaderTrans.attribLoc[1]);
glVertexAttribPointer(m_shaderTrans.attribLoc[0], 3, GL_FLOAT, GL_FALSE, sizeof(FBVertex), (void*)offsetof(FBVertex, verts));
glVertexAttribPointer(m_shaderTrans.attribLoc[1], 2, GL_FLOAT, GL_FALSE, sizeof(FBVertex), (void*)offsetof(FBVertex, texCoords));
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(m_shaderTrans.attribLoc[0]);
glDisableVertexAttribArray(m_shaderTrans.attribLoc[1]);
m_shaderTrans.DisableShader();
}
+173 -4
View File
@@ -1,9 +1,149 @@
#include "R3DScrollFog.h"
#include "Graphics/Shader.h"
#include "Mat4.h"
#include <cstdio>
#include <string>
namespace New3D {
#ifndef __ANDROID__
#include "Graphics/Shader.h"
#endif
#ifdef __ANDROID__
namespace {
static void LogShaderError(GLuint shader, const char* label)
{
GLint ok = 0;
glGetShaderiv(shader, GL_COMPILE_STATUS, &ok);
if (ok == GL_TRUE) return;
GLint len = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len);
if (len <= 0) return;
std::string msg;
msg.resize(static_cast<size_t>(len));
GLsizei written = 0;
glGetShaderInfoLog(shader, len, &written, msg.data());
fprintf(stderr, "R3DScrollFog %s compile error: %s\n", label ? label : "shader", msg.c_str());
}
static void LogProgramError(GLuint program)
{
GLint ok = 0;
glGetProgramiv(program, GL_LINK_STATUS, &ok);
if (ok == GL_TRUE) return;
GLint len = 0;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &len);
if (len <= 0) return;
std::string msg;
msg.resize(static_cast<size_t>(len));
GLsizei written = 0;
glGetProgramInfoLog(program, len, &written, msg.data());
fprintf(stderr, "R3DScrollFog link error: %s\n", msg.c_str());
}
static bool CompileShaderProgram(GLuint* programOut, GLuint* vsOut, GLuint* fsOut, const char* vsSrc, const char* fsSrc)
{
if (!programOut || !vsOut || !fsOut) return false;
*programOut = 0;
*vsOut = 0;
*fsOut = 0;
GLuint vs = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vs, 1, &vsSrc, nullptr);
glCompileShader(vs);
LogShaderError(vs, "vertex");
GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fs, 1, &fsSrc, nullptr);
glCompileShader(fs);
LogShaderError(fs, "fragment");
GLuint program = glCreateProgram();
glAttachShader(program, vs);
glAttachShader(program, fs);
glLinkProgram(program);
LogProgramError(program);
GLint ok = 0;
glGetProgramiv(program, GL_LINK_STATUS, &ok);
if (ok != GL_TRUE) {
glDeleteProgram(program);
glDeleteShader(vs);
glDeleteShader(fs);
return false;
}
*programOut = program;
*vsOut = vs;
*fsOut = fs;
return true;
}
} // namespace
#endif
#ifdef __ANDROID__
static const char *vertexShaderFog = R"glsl(
#version 300 es
precision highp float;
uniform mat4 mvp;
in vec3 inVertex;
void main(void)
{
gl_Position = mvp * vec4(inVertex,1.0);
}
)glsl";
static const char *fragmentShaderFog = R"glsl(
#version 300 es
precision mediump float;
uniform float fogAttenuation;
uniform float fogAmbient;
uniform vec4 fogColour;
uniform vec3 spotFogColor;
uniform vec4 spotEllipse;
out vec4 oColor;
// Spotlight on fog
float ellipse;
vec2 position, size;
vec3 lSpotFogColor;
// Scroll fog
vec3 lFogColor;
vec4 scrollFog;
void main()
{
// Scroll fog base color
lFogColor = fogColour.rgb * fogAmbient;
// Spotlight on fog (area)
position = spotEllipse.xy;
size = spotEllipse.zw;
ellipse = length((gl_FragCoord.xy - position) / size);
ellipse = ellipse * ellipse; // decay rate = square of distance from center
ellipse = 1.0 - ellipse; // invert
ellipse = max(0.0, ellipse); // clamp
// Spotlight on fog (color)
lSpotFogColor = mix(spotFogColor * ellipse * fogColour.rgb, vec3(0.0), fogAttenuation);
// Scroll fog density
scrollFog = vec4(lFogColor + lSpotFogColor, fogColour.a);
// Final Color
oColor = scrollFog;
}
)glsl";
#else
static const char *vertexShaderFog = R"glsl(
#version 120
@@ -62,6 +202,7 @@ void main()
}
)glsl";
#endif
R3DScrollFog::R3DScrollFog(const Util::Config::Node &config)
@@ -112,10 +253,10 @@ void R3DScrollFog::DrawScrollFog(float rgba[4], float attenuation, float ambient
glUniform4f (m_locSpotEllipse, spotEllipse[0], spotEllipse[1], spotEllipse[2], spotEllipse[3]);
glUniformMatrix4fv (m_locMVP, 1, GL_FALSE, mvp);
glEnableVertexAttribArray (0);
glEnableVertexAttribArray (m_locInVertex);
glVertexAttribPointer (m_locInVertex, 3, GL_FLOAT, GL_FALSE, sizeof(SFVertex), 0);
glDrawArrays (GL_TRIANGLES, 0, 6);
glDisableVertexAttribArray (0);
glDisableVertexAttribArray (m_locInVertex);
glUseProgram (0);
m_vbo.Bind (false);
@@ -126,7 +267,26 @@ void R3DScrollFog::DrawScrollFog(float rgba[4], float attenuation, float ambient
void R3DScrollFog::AllocResources()
{
bool success = LoadShaderProgram(&m_shaderProgram, &m_vertexShader, &m_fragmentShader, m_config["VertexShaderFog"].ValueAs<std::string>(), m_config["FragmentShaderFog"].ValueAs<std::string>(), vertexShaderFog, fragmentShaderFog);
#ifdef __ANDROID__
bool success = CompileShaderProgram(&m_shaderProgram, &m_vertexShader, &m_fragmentShader, vertexShaderFog, fragmentShaderFog);
#else
bool success =
LoadShaderProgram(
&m_shaderProgram,
&m_vertexShader,
&m_fragmentShader,
m_config["VertexShaderFog"].ValueAsDefault<std::string>(""),
m_config["FragmentShaderFog"].ValueAsDefault<std::string>(""),
vertexShaderFog,
fragmentShaderFog
);
#endif
if (!success) {
m_shaderProgram = 0;
m_vertexShader = 0;
m_fragmentShader = 0;
return;
}
m_locMVP = glGetUniformLocation(m_shaderProgram, "mvp");
m_locFogColour = glGetUniformLocation(m_shaderProgram, "fogColour");
@@ -143,7 +303,16 @@ void R3DScrollFog::AllocResources()
void R3DScrollFog::DeallocResources()
{
if (m_shaderProgram) {
#ifdef __ANDROID__
glUseProgram(0);
glDetachShader(m_shaderProgram, m_vertexShader);
glDetachShader(m_shaderProgram, m_fragmentShader);
glDeleteShader(m_vertexShader);
glDeleteShader(m_fragmentShader);
glDeleteProgram(m_shaderProgram);
#else
DestroyShaderProgram(m_shaderProgram, m_vertexShader, m_fragmentShader);
#endif
}
m_shaderProgram = 0;
+7 -1
View File
@@ -1,6 +1,12 @@
#ifndef _R3DSCROLLFOG_H_
#define _R3DSCROLLFOG_H_
#ifdef __ANDROID__
#include <GLES3/gl3.h>
#else
#include <GL/glew.h>
#endif
#include "Util/NewConfig.h"
#include "VBO.h"
@@ -61,4 +67,4 @@ private:
}
#endif
#endif
@@ -31,6 +31,8 @@ YResolution = 384
VSync = 1
WideScreen = 0
WideBackground = 0
; Enhanced Real3D compositing (FBO transparency + scroll fog). More accurate but may be slower.
New3DAccurate = 0
; Input system (Android uses SDL input backend)
InputSystem = sdl
+3
View File
@@ -116,11 +116,14 @@ add_library(super3 SHARED
gles_stub_render3d.cpp
render2d_android.cpp
"${REPO_ROOT}/Src/Graphics/New3D/Mat4.cpp"
"${REPO_ROOT}/Src/Graphics/New3D/GLSLShader.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/R3DFrameBuffers.cpp"
"${REPO_ROOT}/Src/Graphics/New3D/R3DShader.cpp"
"${REPO_ROOT}/Src/Graphics/New3D/R3DScrollFog.cpp"
"${REPO_ROOT}/Src/Graphics/New3D/Texture.cpp"
"${REPO_ROOT}/Src/Graphics/New3D/TextureSheet.cpp"
"${REPO_ROOT}/Src/Graphics/New3D/VBO.cpp"
+2
View File
@@ -174,6 +174,7 @@ struct Super3Host {
config.Set("MusicVolume", "200");
config.Set("LegacySoundDSP", false);
config.Set("New3DEngine", false);
config.Set("New3DAccurate", false);
config.Set("QuadRendering", false);
config.Set("FlipStereo", false);
// The core expects this node to exist (throws std::range_error otherwise).
@@ -573,6 +574,7 @@ struct Super3Host {
return !!new3d;
SDL_Log("Initializing New3D (GLES) ...");
SDL_Log("New3DAccurate=%d", config["New3DAccurate"].ValueAsDefault<bool>(false) ? 1 : 0);
new3d = std::make_unique<New3D::CNew3D>(config, game.name);
if (new3d->Init(xOff, yOff, xRes, yRes, totalXRes, totalYRes) != 0)
{
@@ -45,6 +45,7 @@ class MainActivity : AppCompatActivity() {
private lateinit var btnResolutionMatchDevice: MaterialButton
private lateinit var btnWidescreen: MaterialButton
private lateinit var btnWideBackground: MaterialButton
private lateinit var btnEnhancedReal3d: MaterialButton
private lateinit var gamesAdapter: GamesAdapter
@@ -62,6 +63,7 @@ class MainActivity : AppCompatActivity() {
val yResolution: Int,
val wideScreen: Boolean,
val wideBackground: Boolean,
val enhancedReal3d: Boolean,
val matchDevice: Boolean,
)
@@ -125,6 +127,7 @@ class MainActivity : AppCompatActivity() {
btnResolutionMatchDevice = headerView.findViewById(R.id.btn_resolution_match_device)
btnWidescreen = headerView.findViewById(R.id.btn_widescreen)
btnWideBackground = headerView.findViewById(R.id.btn_wide_background)
btnEnhancedReal3d = headerView.findViewById(R.id.btn_enhanced_real3d)
val btnShowTouchControls: MaterialButton = headerView.findViewById(R.id.btn_show_touch_controls)
val btnShowShifterOverlay: MaterialButton = headerView.findViewById(R.id.btn_show_shifter_overlay)
@@ -230,7 +233,8 @@ class MainActivity : AppCompatActivity() {
val wideScreen = prefs.getBoolean("video_wideScreen", false)
val wideBackground = prefs.getBoolean("video_wideBackground", false)
val matchDevice = prefs.getBoolean("video_matchDevice", false)
return VideoSettings(x, y, wideScreen, wideBackground, matchDevice)
val enhancedReal3d = prefs.getBoolean("video_enhancedReal3d", false)
return VideoSettings(x, y, wideScreen, wideBackground, enhancedReal3d, matchDevice)
}
val ini = supermodelIniFile()
@@ -238,7 +242,8 @@ class MainActivity : AppCompatActivity() {
val y = readIniInt(ini, "YResolution") ?: 384
val wideScreen = readIniBool(ini, "WideScreen") ?: false
val wideBackground = readIniBool(ini, "WideBackground") ?: false
return VideoSettings(x, y, wideScreen, wideBackground, matchDevice = false)
val enhancedReal3d = readIniBool(ini, "New3DAccurate") ?: false
return VideoSettings(x, y, wideScreen, wideBackground, enhancedReal3d, matchDevice = false)
}
private fun saveVideoSettings(settings: VideoSettings) {
@@ -247,6 +252,7 @@ class MainActivity : AppCompatActivity() {
.putInt("video_yResolution", settings.yResolution)
.putBoolean("video_wideScreen", settings.wideScreen)
.putBoolean("video_wideBackground", settings.wideBackground)
.putBoolean("video_enhancedReal3d", settings.enhancedReal3d)
.putBoolean("video_matchDevice", settings.matchDevice)
.apply()
}
@@ -271,6 +277,7 @@ class MainActivity : AppCompatActivity() {
btnResolutionMatchDevice.isChecked = settings.matchDevice
btnWidescreen.isChecked = settings.wideScreen
btnWideBackground.isChecked = settings.wideBackground
btnEnhancedReal3d.isChecked = settings.enhancedReal3d
}
fun persistAndApply(settings: VideoSettings) {
@@ -334,6 +341,23 @@ class MainActivity : AppCompatActivity() {
persistAndApply(updated)
applyUi(updated)
}
btnEnhancedReal3d.setOnClickListener {
val cur = loadVideoSettings()
val enabled = btnEnhancedReal3d.isChecked
val updated = cur.copy(enhancedReal3d = enabled, matchDevice = false)
persistAndApply(updated)
applyUi(updated)
Toast.makeText(
this,
if (enabled) {
"Enhanced Real3D enabled (restart game to apply)"
} else {
"Enhanced Real3D disabled (restart game to apply)"
},
Toast.LENGTH_SHORT,
).show()
}
}
private fun applyVideoSettingsToIni(internalRoot: File, settings: VideoSettings) {
@@ -345,6 +369,7 @@ class MainActivity : AppCompatActivity() {
"YResolution" to settings.yResolution.toString(),
"WideScreen" to if (settings.wideScreen) "1" else "0",
"WideBackground" to if (settings.wideBackground) "1" else "0",
"New3DAccurate" to if (settings.enhancedReal3d) "1" else "0",
),
)
}
@@ -101,6 +101,15 @@
android:text="Wide background"
style="?attr/materialButtonElevatedStyle" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_enhanced_real3d"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:checkable="true"
android:text="Enhanced Real3D (experimental)"
style="?attr/materialButtonElevatedStyle" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/controls_title"
android:layout_width="match_parent"