Updated supermodel emulator to the current version.

This commit is contained in:
sgiannop authored and Giannopoulos Mike committed 2026-02-09 12:28:00 +02:00
1 parent f980705438
commit ab088efe34
357 files changed
+161097 -87325

No files matched your search

+4
View File
@@ -5,6 +5,10 @@
*.dll
*.dylib
*.lib
*.zip
*.log
*.info
*.nv
*.pdb
*.exp
*.manifest
+18
View File
@@ -0,0 +1,18 @@
{
"configurations": [
{
"name": "Linux",
"includePath": [
"${workspaceFolder}/**",
"/usr/include/SDL2"
],
"defines": [],
"compilerPath": "/usr/bin/clang",
"cStandard": "c17",
"cppStandard": "c++17",
"intelliSenseMode": "linux-clang-x64",
"configurationProvider": "ms-vscode.makefile-tools"
}
],
"version": 4
}
+31
View File
@@ -0,0 +1,31 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug Libretro Supermodel",
"type": "cppdbg",
"request": "launch",
"program": "/usr/bin/retroarch", // Path to your RetroArch binary
"args": [
"-L", "${workspaceFolder}/supermodel_libretro.so",
"--verbose",
"--set-shader",
"${env:HOME}/.config/retroarch/shaders/shaders_glsl/crt/crt-mattias.glslp",
"${workspaceFolder}/vf3.zip" // Path to your test ROM
],
"cwd": "${workspaceFolder}",
"stopAtEntry": false,
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"preLaunchTask": "Build libretro-supermodel (Debug)"
}
]
}
+11
View File
@@ -0,0 +1,11 @@
{
"files.exclude": {
"**/*.o": true,
"**/*.log": true,
"**/*.info": true,
"**/*.nv": true,
"**/*.zip": true,
"**/*.so/**": true
}
}
+31
View File
@@ -0,0 +1,31 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Build libretro-supermodel (Debug)",
"type": "shell",
"command": "make",
"args": [
"DEBUG=1",
"-j$(nproc)"
],
"options": {
"cwd": "${workspaceFolder}"
},
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": [
"$gcc"
]
},
{
"label": "make watch (entr)",
"type": "shell",
"command": "find . -name '*.cpp' -o -name '*.h' | entr -c make DEBUG=1 -j",
"isBackground": true,
"problemMatcher": []
}
]
}
+4 -1
View File
@@ -47,7 +47,7 @@ else ifneq ($(findstring MINGW,$(shell uname -a)),)
endif
# Replace 'sample' with the name of the core
TARGET_NAME := sample
TARGET_NAME := supermodel
GIT_VERSION := " $(shell git rev-parse --short HEAD || echo unknown)"
ifneq ($(GIT_VERSION)," unknown")
CFLAGS += -DGIT_VERSION=\"$(GIT_VERSION)\"
@@ -585,6 +585,7 @@ include Makefile.common
OBJECTS := $(SOURCES_C:.c=.o) $(SOURCES_CXX:.cpp=.o)
DEFINES := $(COREDEFINES) $(PLATFORM_DEFINES)
DEFINES += -DSUPERMODEL_OSD_LIBRETRO
ifeq ($(STATIC_LINKING),1)
DEFINES += -DSTATIC_LINKING
@@ -631,6 +632,8 @@ ${LIBRARY_NAME}_FILES = $(SOURCES_C)
include $(THEOS_MAKE_PATH)/library.mk
else
all: $(TARGET)
LIBS += -lm -lGLEW -lGL -lGLU -lz -lSDL2
$(TARGET): $(OBJECTS)
ifeq ($(STATIC_LINKING),1)
ifneq (,$(findstring msvc,$(platform)))
+20 -18
View File
@@ -26,6 +26,8 @@
* class.
*/
#include "BlockFile.h"
#include <cstdio>
#include <cstring>
#include <cstdint>
@@ -43,7 +45,7 @@ void CBlockFile::ReadString(std::string *str, uint32_t length)
str->clear();
//TODO: use fstream to get rid of this ugly hack
bool keep_loading = true;
for (size_t i = 0; i < length; i++)
for (uint32_t i = 0; i < length; i++)
{
char c;
fread(&c, sizeof(char), 1, fp);
@@ -188,10 +190,10 @@ void CBlockFile::NewBlock(const std::string &name, const std::string &comment)
WriteBlockHeader(name, comment);
}
bool CBlockFile::FindBlock(const std::string &name)
Result CBlockFile::FindBlock(const std::string &name)
{
if (mode != 'r')
return FAIL;
return Result::FAIL;
fseek(fp, 0, SEEK_SET);
@@ -215,7 +217,7 @@ bool CBlockFile::FindBlock(const std::string &name)
{
fseek(fp, blockStartPos + 12 + name_length + comment_length, SEEK_SET); // move to beginning of data
dataStartPos = ftell(fp);
return OKAY;
return Result::OKAY;
}
// Move to next block
@@ -225,24 +227,24 @@ bool CBlockFile::FindBlock(const std::string &name)
break;
}
return FAIL;
return Result::FAIL;
}
bool CBlockFile::Create(const std::string &file, const std::string &headerName, const std::string &comment)
Result CBlockFile::Create(const std::string &file, const std::string &headerName, const std::string &comment)
{
fp = fopen(file.c_str(), "wb");
if (NULL == fp)
return FAIL;
return Result::FAIL;
mode = 'w';
WriteBlockHeader(headerName, comment);
return OKAY;
return Result::OKAY;
}
bool CBlockFile::Load(const std::string &file)
Result CBlockFile::Load(const std::string &file)
{
fp = fopen(file.c_str(), "rb");
if (NULL == fp)
return FAIL;
return Result::FAIL;
mode = 'r';
// TODO: is this a valid block file?
@@ -252,7 +254,7 @@ bool CBlockFile::Load(const std::string &file)
fileSize = ftell(fp);
fseek(fp, 0, SEEK_SET);
return OKAY;
return Result::OKAY;
}
void CBlockFile::Close(void)
@@ -269,10 +271,10 @@ CBlockFile::CBlockFile(void)
mode = 0; // neither reading nor writing (do nothing)
}
CBlockFile::~CBlockFile(void)
{
if (fp != NULL) // in case user forgot
fclose(fp);
fp = NULL;
mode = 0;
}
// CBlockFile::~CBlockFile(void)
// {
// if (fp != NULL) // in case user forgot
// fclose(fp);
// fp = NULL;
// mode = 0;
// }
+12 -11
View File
@@ -30,6 +30,7 @@
#include <cstdint>
#include <string>
#include "Types.h"
/*
* CBlockFile:
@@ -44,7 +45,7 @@
*/
class CBlockFile
{
public:
public:
/*
* Read(data, numBytes):
*
@@ -58,7 +59,7 @@ public:
* Number of bytes read. If not the same as numBytes, an error
* occurred.
*/
unsigned Read(void *data, uint32_t numBytes);
virtual unsigned Read(void *data, uint32_t numBytes);
/*
* Read(value):
@@ -71,7 +72,7 @@ public:
* Returns:
* Number of bytes read. If not 1, an error occurred.
*/
unsigned Read(bool *value);
virtual unsigned Read(bool *value);
/*
* FindBlock(name):
@@ -85,7 +86,7 @@ public:
* Returns:
* OKAY if found, FAIL if unable to locate.
*/
bool FindBlock(const std::string &name);
virtual Result FindBlock(const std::string &name);
/*
* Write(value):
@@ -96,7 +97,7 @@ public:
* Parameters:
* value A boolean value to write.
*/
void Write(bool value);
virtual void Write(bool value);
/*
* Write(data, numBytes):
@@ -108,7 +109,7 @@ public:
* data Data to write.
* numBytes Number of bytes to write.
*/
void Write(const void *data, uint32_t numBytes);
virtual void Write(const void *data, uint32_t numBytes);
/*
* Write(str):
@@ -119,7 +120,7 @@ public:
* Parameters:
* str String to write.
*/
void Write(const std::string &str);
virtual void Write(const std::string &str);
/*
* NewBlock(name, comment):
@@ -131,7 +132,7 @@ public:
* name Block name. Must be unique and not NULL.
* comment Comment string to embed in the block header.
*/
void NewBlock(const std::string &title, const std::string &comment);
virtual void NewBlock(const std::string &name, const std::string &comment);
/*
* Create(file, headerName, comment):
@@ -149,7 +150,7 @@ public:
* Returns:
* OKAY if successfully opened, otherwise FAIL.
*/
bool Create(const std::string &file, const std::string &headerName, const std::string &comment);
Result Create(const std::string &file, const std::string &headerName, const std::string &comment);
/*
* Load(file):
@@ -165,7 +166,7 @@ public:
* subsequent operations will be silently ignored (reads will return
* 0's). Write commands will be ignored.
*/
bool Load(const std::string &file);
Result Load(const std::string &file);
/*
* Close(void):
@@ -181,7 +182,7 @@ public:
* Constructor and destructor.
*/
CBlockFile(void);
~CBlockFile(void);
virtual ~CBlockFile() {}
private:
// Helper functions
+30 -25
View File
@@ -1,12 +1,12 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
** Copyright 2003-2022 The Supermodel Team
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
@@ -18,13 +18,13 @@
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* 68K.cpp
*
*
* 68K CPU interface. This is presently just a wrapper for the Musashi 68K core
* and therefore, only a single CPU is supported. In the future, we may want to
* add in another 68K core (eg., Turbo68K, A68K, or a recompiler).
* add in another 68K core (eg., Turbo68K, A68K, or a recompiler).
*
* To-Do List
* ----------
@@ -33,13 +33,15 @@
* accesses (interrupts pending, halted status, etc.)
*/
#include "68K.h"
#include "Supermodel.h"
#include "Musashi/m68k.h" // Musashi 68K core
#include "Debugger/CPU/Musashi68KDebug.h"
/******************************************************************************
Internal Context
An active context must be mapped before calling M68K interface functions. Only
the bus and IRQ handlers are copied here; the CPU context is passed directly
to Musashi.
@@ -56,8 +58,10 @@ static Debugger::CMusashi68KDebug *s_Debug = NULL;
// IRQ callback
static int (*IRQAck)(int nIRQ) = NULL;
#ifdef SUPERMODEL_DEBUGGER
// Cycles remaining in timeslice
static int s_lastCycles;
#endif
/******************************************************************************
@@ -65,11 +69,11 @@ static int s_lastCycles;
******************************************************************************/
// CPU state
UINT32 M68KGetARegister(int n)
{
m68k_register_t r;
switch (n)
{
case 0: r = M68K_REG_A0; break;
@@ -82,14 +86,14 @@ UINT32 M68KGetARegister(int n)
case 7: r = M68K_REG_A7; break;
default: r = M68K_REG_A7; break;
}
return m68k_get_reg(NULL, r);
}
UINT32 M68KGetDRegister(int n)
{
m68k_register_t r;
switch (n)
{
case 0: r = M68K_REG_D0; break;
@@ -102,7 +106,7 @@ UINT32 M68KGetDRegister(int n)
case 7: r = M68K_REG_D7; break;
default: r = M68K_REG_D7; break;
}
return m68k_get_reg(NULL, r);
}
@@ -114,22 +118,22 @@ UINT32 M68KGetPC(void)
void M68KSaveState(CBlockFile *StateFile, const char *name)
{
StateFile->NewBlock(name, __FILE__);
/*
* Rather than writing the context directly, the get/set register
* functions are used, ensuring that all context members are packed/
* unpacked correctly.
*
* Note: Some of these are undoubtedly 68010 or 68020 registers and not
* really necessary. But if the layout is changed now, the save state
* really necessary. But if the layout is changed now, the save state
* version has to be changed, so don't do it!
*/
UINT32 data[34];
m68ki_cpu_core Ctx;
m68k_get_context(&Ctx);
data[0] = Ctx.int_level;
data[1] = Ctx.int_cycles;
data[2] = Ctx.stopped;
@@ -164,13 +168,13 @@ void M68KSaveState(CBlockFile *StateFile, const char *name)
data[31] = m68k_get_reg(NULL, M68K_REG_PREF_DATA);
data[32] = m68k_get_reg(NULL, M68K_REG_PPC);
data[33] = m68k_get_reg(NULL, M68K_REG_IR);
StateFile->Write(data, sizeof(data));
}
void M68KLoadState(CBlockFile *StateFile, const char *name)
{
if (OKAY != StateFile->FindBlock(name))
if (Result::OKAY != StateFile->FindBlock(name))
{
ErrorLog("Unable to load 68K state. Save state file is corrupt.");
return;
@@ -178,10 +182,10 @@ void M68KLoadState(CBlockFile *StateFile, const char *name)
UINT32 data[34];
m68ki_cpu_core Ctx;
StateFile->Read(data, sizeof(data));
// These must be set first, to ensure another contexts' IRQs aren't active when PC is changed
// These must be set first, to ensure another contexts' IRQs aren't active when PC is changed
m68k_get_context(&Ctx);
Ctx.int_level = data[0];
Ctx.int_cycles = data[1];
@@ -293,7 +297,7 @@ void M68KSetContext(M68KCtx *Src)
// One-time initialization
bool M68KInit(void)
Result M68KInit(void)
{
m68k_init();
m68k_set_cpu_type(M68K_CPU_TYPE_68000);
@@ -301,9 +305,10 @@ bool M68KInit(void)
s_Bus = NULL;
#ifdef SUPERMODEL_DEBUGGER
s_Debug = NULL;
m68k_set_instr_hook_callback(M68KDebugCallback);
#endif // SUPERMODEL_DEBUGGER
DebugLog("Initialized 68K\n");
return OKAY;
return Result::OKAY;
}
#ifdef SUPERMODEL_DEBUGGER
@@ -364,7 +369,7 @@ UINT32 M68KSetRegister(M68KCtx *Src, unsigned reg, UINT32 val)
/******************************************************************************
Musashi 68K Handlers
Musashi/m68kconf.h has been configured to call these directly.
******************************************************************************/
+2 -1
View File
@@ -37,6 +37,7 @@
#include "Musashi/m68k.h"
#include "Musashi/m68kctx.h"
#include "CPU/Bus.h"
#include "BlockFile.h"
// This doesn't work for now (needs to be added to the prototypes in m68k.h for m68k_read_memory*)
//#ifndef FASTCALL
@@ -239,7 +240,7 @@ extern void M68KAttachBus(IBus *BusPtr);
* Returns:
* Always returns OKAY.
*/
extern bool M68KInit(void);
extern Result M68KInit(void);
/*
* M68KGetContext(M68KCtx *Dest):
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+130 -214
View File
@@ -235,8 +235,17 @@ void m68ki_build_opcode_table(void)
m68ki_instruction_jump_table[instr] = ostruct->opcode_handler;
for(k=0;k<NUM_CPU_TYPES;k++)
m68ki_cycles[k][instr] = ostruct->cycles[k];
// For all shift operations with known shift distance (encoded in instruction word)
if((instr & 0xf000) == 0xe000 && (!(instr & 0x20)))
m68ki_cycles[0][instr] = m68ki_cycles[1][instr] = ostruct->cycles[k] + ((((j-1)&7)+1)<<1);
{
// On the 68000 and 68010 shift distance affect execution time.
// Add the cycle cost of shifting; 2 times the shift distance
int cycle_cost = ((((i-1)&7)+1)<<1);
m68ki_cycles[0][instr] += cycle_cost;
m68ki_cycles[1][instr] += cycle_cost;
// On the 68020 shift distance does not affect execution time
m68ki_cycles[2][instr] += 0;
}
}
}
ostruct++;
@@ -1240,8 +1249,9 @@ M68KMAKE_OP(adda, 16, ., a)
M68KMAKE_OP(adda, 16, ., .)
{
uint* r_dst = &AX;
uint src = MAKE_INT_16(M68KMAKE_GET_OPER_AY_16);
*r_dst = MASK_OUT_ABOVE_32(*r_dst + MAKE_INT_16(M68KMAKE_GET_OPER_AY_16));
*r_dst = MASK_OUT_ABOVE_32(*r_dst + src);
}
@@ -1263,9 +1273,10 @@ M68KMAKE_OP(adda, 32, ., a)
M68KMAKE_OP(adda, 32, ., .)
{
uint src = M68KMAKE_GET_OPER_AY_32;
uint* r_dst = &AX;
*r_dst = MASK_OUT_ABOVE_32(*r_dst + M68KMAKE_GET_OPER_AY_32);
*r_dst = MASK_OUT_ABOVE_32(*r_dst + src);
}
@@ -1819,7 +1830,7 @@ M68KMAKE_OP(andi, 32, ., .)
M68KMAKE_OP(andi, 16, toc, .)
{
m68ki_set_ccr(m68ki_get_ccr() & OPER_I_16());
m68ki_set_ccr(m68ki_get_ccr() & OPER_I_8());
}
@@ -3397,8 +3408,8 @@ M68KMAKE_OP(cas2, 16, ., .)
return;
}
}
*compare1 = BIT_1F(word2) ? MAKE_INT_16(dest1) : MASK_OUT_BELOW_16(*compare1) | dest1;
*compare2 = BIT_F(word2) ? MAKE_INT_16(dest2) : MASK_OUT_BELOW_16(*compare2) | dest2;
*compare1 = BIT_1F(word2) ? (uint)MAKE_INT_16(dest1) : MASK_OUT_BELOW_16(*compare1) | dest1;
*compare2 = BIT_F(word2) ? (uint)MAKE_INT_16(dest2) : MASK_OUT_BELOW_16(*compare2) | dest2;
return;
}
m68ki_exception_illegal();
@@ -3537,24 +3548,18 @@ M68KMAKE_OP(chk2cmp2, 8, ., pcdi)
if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE))
{
uint word2 = OPER_I_16();
uint compare = REG_DA[(word2 >> 12) & 15]&0xff;
sint compare = REG_DA[(word2 >> 12) & 15]&0xff;
uint ea = EA_PCDI_8();
uint lower_bound = m68ki_read_pcrel_8(ea);
uint upper_bound = m68ki_read_pcrel_8(ea + 1);
sint lower_bound = m68ki_read_pcrel_8(ea);
sint upper_bound = m68ki_read_pcrel_8(ea + 1);
if(!BIT_F(word2))
FLAG_C = MAKE_INT_8(compare) - MAKE_INT_8(lower_bound);
else
FLAG_C = compare - lower_bound;
FLAG_Z = !((upper_bound==compare) | (lower_bound==compare));
if(COND_CS())
{
if(BIT_B(word2))
m68ki_exception_trap(EXCEPTION_CHK);
return;
}
compare = (INT32)(INT8)compare;
FLAG_Z = !((upper_bound==compare) || (lower_bound==compare)); // JFF: | => ||
FLAG_C = (lower_bound <= upper_bound ? compare < lower_bound || compare > upper_bound : compare > upper_bound || compare < lower_bound) << 8;
FLAG_C = upper_bound - compare;
if(COND_CS() && BIT_B(word2))
m68ki_exception_trap(EXCEPTION_CHK);
return;
@@ -3568,24 +3573,17 @@ M68KMAKE_OP(chk2cmp2, 8, ., pcix)
if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE))
{
uint word2 = OPER_I_16();
uint compare = REG_DA[(word2 >> 12) & 15]&0xff;
sint compare = REG_DA[(word2 >> 12) & 15]&0xff;
uint ea = EA_PCIX_8();
uint lower_bound = m68ki_read_pcrel_8(ea);
uint upper_bound = m68ki_read_pcrel_8(ea + 1);
sint lower_bound = m68ki_read_pcrel_8(ea);
sint upper_bound = m68ki_read_pcrel_8(ea + 1);
if(!BIT_F(word2))
FLAG_C = MAKE_INT_8(compare) - MAKE_INT_8(lower_bound);
else
FLAG_C = compare - lower_bound;
FLAG_Z = !((upper_bound==compare) | (lower_bound==compare));
if(COND_CS())
{
if(BIT_B(word2))
m68ki_exception_trap(EXCEPTION_CHK);
return;
}
compare = (INT32)(INT8)compare;
FLAG_Z = !((upper_bound==compare) || (lower_bound==compare)); // JFF: | => ||, faster operation short circuits
FLAG_C = (lower_bound <= upper_bound ? compare < lower_bound || compare > upper_bound : compare > upper_bound || compare < lower_bound) << 8;
FLAG_C = upper_bound - compare;
if(COND_CS() && BIT_B(word2))
m68ki_exception_trap(EXCEPTION_CHK);
return;
@@ -3599,24 +3597,18 @@ M68KMAKE_OP(chk2cmp2, 8, ., .)
if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE))
{
uint word2 = OPER_I_16();
uint compare = REG_DA[(word2 >> 12) & 15]&0xff;
sint compare = REG_DA[(word2 >> 12) & 15]&0xff;
uint ea = M68KMAKE_GET_EA_AY_8;
uint lower_bound = m68ki_read_8(ea);
uint upper_bound = m68ki_read_8(ea + 1);
sint lower_bound = (INT8)m68ki_read_8(ea);
sint upper_bound = (INT8)m68ki_read_8(ea + 1);
if(!BIT_F(word2))
FLAG_C = MAKE_INT_8(compare) - MAKE_INT_8(lower_bound);
else
FLAG_C = compare - lower_bound;
FLAG_Z = !((upper_bound==compare) | (lower_bound==compare));
if(COND_CS())
{
if(BIT_B(word2))
m68ki_exception_trap(EXCEPTION_CHK);
return;
}
compare = (INT32)(INT8)compare;
FLAG_Z = !((upper_bound==compare) || (lower_bound==compare)); // JFF: | => ||
FLAG_C = (lower_bound <= upper_bound ? compare < lower_bound || compare > upper_bound : compare > upper_bound || compare < lower_bound) << 8;
FLAG_C = upper_bound - compare;
if(COND_CS() && BIT_B(word2))
m68ki_exception_trap(EXCEPTION_CHK);
return;
@@ -3630,29 +3622,17 @@ M68KMAKE_OP(chk2cmp2, 16, ., pcdi)
if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE))
{
uint word2 = OPER_I_16();
uint compare = REG_DA[(word2 >> 12) & 15]&0xffff;
sint compare = REG_DA[(word2 >> 12) & 15]&0xffff;
uint ea = EA_PCDI_16();
uint lower_bound = m68ki_read_pcrel_16(ea);
uint upper_bound = m68ki_read_pcrel_16(ea + 2);
sint lower_bound = (INT16)m68ki_read_pcrel_16(ea);
sint upper_bound = (INT16)m68ki_read_pcrel_16(ea + 2);
if(!BIT_F(word2))
FLAG_C = MAKE_INT_16(compare) - MAKE_INT_16(lower_bound);
else
FLAG_C = compare - lower_bound;
FLAG_Z = !((upper_bound==compare) | (lower_bound==compare));
FLAG_C = CFLAG_16(FLAG_C);
if(COND_CS())
{
if(BIT_B(word2))
m68ki_exception_trap(EXCEPTION_CHK);
return;
}
compare = (INT32)(INT16)compare;
FLAG_Z = !((upper_bound==compare) || (lower_bound==compare)); // JFF: | => ||
FLAG_C = (lower_bound <= upper_bound ? compare < lower_bound || compare > upper_bound : compare > upper_bound || compare < lower_bound) << 8;
if(!BIT_F(word2))
FLAG_C = MAKE_INT_16(upper_bound) - MAKE_INT_16(compare);
else
FLAG_C = upper_bound - compare;
FLAG_C = CFLAG_16(FLAG_C);
if(COND_CS() && BIT_B(word2))
m68ki_exception_trap(EXCEPTION_CHK);
return;
@@ -3666,29 +3646,17 @@ M68KMAKE_OP(chk2cmp2, 16, ., pcix)
if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE))
{
uint word2 = OPER_I_16();
uint compare = REG_DA[(word2 >> 12) & 15]&0xffff;
sint compare = REG_DA[(word2 >> 12) & 15]&0xffff;
uint ea = EA_PCIX_16();
uint lower_bound = m68ki_read_pcrel_16(ea);
uint upper_bound = m68ki_read_pcrel_16(ea + 2);
sint lower_bound = (INT16)m68ki_read_pcrel_16(ea);
sint upper_bound = (INT16)m68ki_read_pcrel_16(ea + 2);
if(!BIT_F(word2))
FLAG_C = MAKE_INT_16(compare) - MAKE_INT_16(lower_bound);
else
FLAG_C = compare - lower_bound;
FLAG_Z = !((upper_bound==compare) | (lower_bound==compare));
FLAG_C = CFLAG_16(FLAG_C);
if(COND_CS())
{
if(BIT_B(word2))
m68ki_exception_trap(EXCEPTION_CHK);
return;
}
compare = (INT32)(INT16)compare;
FLAG_Z = !((upper_bound==compare) || (lower_bound==compare)); // JFF: | => ||
FLAG_C = (lower_bound <= upper_bound ? compare < lower_bound || compare > upper_bound : compare > upper_bound || compare < lower_bound) << 8;
if(!BIT_F(word2))
FLAG_C = MAKE_INT_16(upper_bound) - MAKE_INT_16(compare);
else
FLAG_C = upper_bound - compare;
FLAG_C = CFLAG_16(FLAG_C);
if(COND_CS() && BIT_B(word2))
m68ki_exception_trap(EXCEPTION_CHK);
return;
@@ -3702,30 +3670,17 @@ M68KMAKE_OP(chk2cmp2, 16, ., .)
if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE))
{
uint word2 = OPER_I_16();
uint compare = REG_DA[(word2 >> 12) & 15]&0xffff;
sint compare = REG_DA[(word2 >> 12) & 15]&0xffff;
uint ea = M68KMAKE_GET_EA_AY_16;
uint lower_bound = m68ki_read_16(ea);
uint upper_bound = m68ki_read_16(ea + 2);
sint lower_bound = (INT16)m68ki_read_16(ea);
sint upper_bound = (INT16)m68ki_read_16(ea + 2);
if(!BIT_F(word2))
FLAG_C = MAKE_INT_16(compare) - MAKE_INT_16(lower_bound);
else
FLAG_C = compare - lower_bound;
compare = (INT32)(INT16)compare;
FLAG_Z = !((upper_bound==compare) || (lower_bound==compare)); // JFF: | => ||
FLAG_Z = !((upper_bound==compare) | (lower_bound==compare));
FLAG_C = CFLAG_16(FLAG_C);
if(COND_CS())
{
if(BIT_B(word2))
m68ki_exception_trap(EXCEPTION_CHK);
return;
}
if(!BIT_F(word2))
FLAG_C = MAKE_INT_16(upper_bound) - MAKE_INT_16(compare);
else
FLAG_C = upper_bound - compare;
FLAG_C = (lower_bound <= upper_bound ? compare < lower_bound || compare > upper_bound : compare > upper_bound || compare < lower_bound) << 8;
FLAG_C = CFLAG_16(FLAG_C);
if(COND_CS() && BIT_B(word2))
m68ki_exception_trap(EXCEPTION_CHK);
return;
@@ -3739,23 +3694,15 @@ M68KMAKE_OP(chk2cmp2, 32, ., pcdi)
if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE))
{
uint word2 = OPER_I_16();
uint compare = REG_DA[(word2 >> 12) & 15];
sint compare = REG_DA[(word2 >> 12) & 15];
uint ea = EA_PCDI_32();
uint lower_bound = m68ki_read_pcrel_32(ea);
uint upper_bound = m68ki_read_pcrel_32(ea + 4);
sint lower_bound = m68ki_read_pcrel_32(ea);
sint upper_bound = m68ki_read_pcrel_32(ea + 4);
FLAG_C = compare - lower_bound;
FLAG_Z = !((upper_bound==compare) | (lower_bound==compare));
FLAG_C = CFLAG_SUB_32(lower_bound, compare, FLAG_C);
if(COND_CS())
{
if(BIT_B(word2))
m68ki_exception_trap(EXCEPTION_CHK);
return;
}
FLAG_Z = !((upper_bound==compare) || (lower_bound==compare)); // JFF: | => ||
FLAG_C = (lower_bound <= upper_bound ? compare < lower_bound || compare > upper_bound : compare > upper_bound || compare < lower_bound) << 8;
FLAG_C = upper_bound - compare;
FLAG_C = CFLAG_SUB_32(compare, upper_bound, FLAG_C);
if(COND_CS() && BIT_B(word2))
m68ki_exception_trap(EXCEPTION_CHK);
return;
@@ -3769,23 +3716,15 @@ M68KMAKE_OP(chk2cmp2, 32, ., pcix)
if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE))
{
uint word2 = OPER_I_16();
uint compare = REG_DA[(word2 >> 12) & 15];
sint compare = REG_DA[(word2 >> 12) & 15];
uint ea = EA_PCIX_32();
uint lower_bound = m68ki_read_pcrel_32(ea);
uint upper_bound = m68ki_read_pcrel_32(ea + 4);
sint lower_bound = m68ki_read_32(ea);
sint upper_bound = m68ki_read_32(ea + 4);
FLAG_C = compare - lower_bound;
FLAG_Z = !((upper_bound==compare) | (lower_bound==compare));
FLAG_C = CFLAG_SUB_32(lower_bound, compare, FLAG_C);
if(COND_CS())
{
if(BIT_B(word2))
m68ki_exception_trap(EXCEPTION_CHK);
return;
}
FLAG_Z = !((upper_bound==compare) || (lower_bound==compare)); // JFF: | => ||
FLAG_C = (lower_bound <= upper_bound ? compare < lower_bound || compare > upper_bound : compare > upper_bound || compare < lower_bound) << 8;
FLAG_C = upper_bound - compare;
FLAG_C = CFLAG_SUB_32(compare, upper_bound, FLAG_C);
if(COND_CS() && BIT_B(word2))
m68ki_exception_trap(EXCEPTION_CHK);
return;
@@ -3799,23 +3738,16 @@ M68KMAKE_OP(chk2cmp2, 32, ., .)
if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE))
{
uint word2 = OPER_I_16();
uint compare = REG_DA[(word2 >> 12) & 15];
// JFF changed the logic. chk2/cmp2 uses signed values, not unsigned
sint compare = REG_DA[(word2 >> 12) & 15];
uint ea = M68KMAKE_GET_EA_AY_32;
uint lower_bound = m68ki_read_32(ea);
uint upper_bound = m68ki_read_32(ea + 4);
sint lower_bound = m68ki_read_32(ea);
sint upper_bound = m68ki_read_32(ea + 4);
FLAG_C = compare - lower_bound;
FLAG_Z = !((upper_bound==compare) | (lower_bound==compare));
FLAG_C = CFLAG_SUB_32(lower_bound, compare, FLAG_C);
if(COND_CS())
{
if(BIT_B(word2))
m68ki_exception_trap(EXCEPTION_CHK);
return;
}
FLAG_Z = !((upper_bound==compare) || (lower_bound==compare)); // JFF: | => ||
FLAG_C = (lower_bound <= upper_bound ? compare < lower_bound || compare > upper_bound : compare > upper_bound || compare < lower_bound) << 8;
FLAG_C = upper_bound - compare;
FLAG_C = CFLAG_SUB_32(compare, upper_bound, FLAG_C);
if(COND_CS() && BIT_B(word2))
m68ki_exception_trap(EXCEPTION_CHK);
return;
@@ -4397,6 +4329,8 @@ M68KMAKE_OP(cptrapcc, 32, ., .)
M68K_DO_LOG((M68K_LOG_FILEHANDLE "%s at %08x: called unimplemented instruction %04x (%s)\n",
m68ki_cpu_names[CPU_TYPE], ADDRESS_68K(REG_PC - 2), REG_IR,
m68k_disassemble_quick(ADDRESS_68K(REG_PC - 2))));
// JFF: unsupported, but at least if the trap doesn't occur, app should still work, so at least PC increase is correct
REG_PC += 4;
return;
}
m68ki_exception_1111();
@@ -4421,9 +4355,11 @@ M68KMAKE_OP(dbf, 16, ., .)
REG_PC -= 2;
m68ki_trace_t0(); /* auto-disable (see m68kcpu.h) */
m68ki_branch_16(offset);
USE_CYCLES(CYC_DBCC_F_NOEXP);
return;
}
REG_PC += 2;
USE_CYCLES(CYC_DBCC_F_EXP);
}
@@ -5157,7 +5093,7 @@ M68KMAKE_OP(eori, 32, ., .)
M68KMAKE_OP(eori, 16, toc, .)
{
m68ki_set_ccr(m68ki_get_ccr() ^ OPER_I_16());
m68ki_set_ccr(m68ki_get_ccr() ^ OPER_I_8());
}
@@ -6434,7 +6370,8 @@ M68KMAKE_OP(move, 32, pd, d)
uint res = DY;
uint ea = EA_AX_PD_32();
m68ki_write_32(ea, res);
m68ki_write_16(ea+2, res & 0xFFFF );
m68ki_write_16(ea, (res >> 16) & 0xFFFF );
FLAG_N = NFLAG_32(res);
FLAG_Z = res;
@@ -6448,7 +6385,8 @@ M68KMAKE_OP(move, 32, pd, a)
uint res = AY;
uint ea = EA_AX_PD_32();
m68ki_write_32(ea, res);
m68ki_write_16(ea+2, res & 0xFFFF );
m68ki_write_16(ea, (res >> 16) & 0xFFFF );
FLAG_N = NFLAG_32(res);
FLAG_Z = res;
@@ -6462,7 +6400,8 @@ M68KMAKE_OP(move, 32, pd, .)
uint res = M68KMAKE_GET_OPER_AY_32;
uint ea = EA_AX_PD_32();
m68ki_write_32(ea, res);
m68ki_write_16(ea+2, res & 0xFFFF );
m68ki_write_16(ea, (res >> 16) & 0xFFFF );
FLAG_N = NFLAG_32(res);
FLAG_Z = res;
@@ -6863,6 +6802,7 @@ M68KMAKE_OP(movec, 32, rc, .)
REG_DFC = REG_DA[(word2 >> 12) & 15] & 7;
return;
case 0x002: /* CACR */
/* Only EC020 and later have CACR */
if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE))
{
REG_CACR = REG_DA[(word2 >> 12) & 15];
@@ -6973,7 +6913,8 @@ M68KMAKE_OP(movem, 32, re, pd)
if(register_list & (1 << i))
{
ea -= 4;
m68ki_write_32(ea, REG_DA[15-i]);
m68ki_write_16(ea+2, REG_DA[15-i] & 0xFFFF );
m68ki_write_16(ea, (REG_DA[15-i] >> 16) & 0xFFFF );
count++;
}
AY = ea;
@@ -8138,7 +8079,7 @@ M68KMAKE_OP(ori, 32, ., .)
M68KMAKE_OP(ori, 16, toc, .)
{
m68ki_set_ccr(m68ki_get_ccr() | OPER_I_16());
m68ki_set_ccr(m68ki_get_ccr() | OPER_I_8());
}
@@ -8553,7 +8494,7 @@ M68KMAKE_OP(rol, 32, r, .)
*r_dst = res;
FLAG_C = (src >> (32 - shift)) << 8;
FLAG_C = (src >> ((32 - shift) & 0x1f)) << 8;
FLAG_N = NFLAG_32(res);
FLAG_Z = res;
FLAG_V = VFLAG_CLEAR;
@@ -9086,7 +9027,7 @@ M68KMAKE_OP(rte, 32, ., .)
}
CPU_INSTR_MODE = INSTRUCTION_YES;
CPU_RUN_MODE = RUN_MODE_NORMAL;
/* Not handling bus fault (9) */
/* Not handling other exception types (9) */
m68ki_exception_format_error();
return;
}
@@ -9168,25 +9109,19 @@ M68KMAKE_OP(sbcd, 8, rr, .)
uint dst = *r_dst;
uint res = LOW_NIBBLE(dst) - LOW_NIBBLE(src) - XFLAG_AS_1();
// FLAG_V = ~res; /* Undefined V behavior */
FLAG_V = VFLAG_CLEAR; /* Undefined in Motorola's M68000PM/AD rev.1 and safer to assume cleared. */
FLAG_V = ~res; /* Undefined V behavior */
if(res > 9)
res -= 6;
res += HIGH_NIBBLE(dst) - HIGH_NIBBLE(src);
if(res > 0x99)
{
FLAG_X = FLAG_C = (res > 0x99) << 8;
if(FLAG_C)
res += 0xa0;
FLAG_X = FLAG_C = CFLAG_SET;
FLAG_N = NFLAG_SET; /* Undefined in Motorola's M68000PM/AD rev.1 and safer to follow carry. */
}
else
FLAG_N = FLAG_X = FLAG_C = 0;
res = MASK_OUT_ABOVE_8(res);
// FLAG_V &= res; /* Undefined V behavior part II */
// FLAG_N = NFLAG_8(res); /* Undefined N behavior */
FLAG_V &= res; /* Undefined V behavior part II */
FLAG_N = NFLAG_8(res); /* Undefined N behavior */
FLAG_Z |= res;
*r_dst = MASK_OUT_BELOW_8(*r_dst) | res;
@@ -9200,25 +9135,19 @@ M68KMAKE_OP(sbcd, 8, mm, ax7)
uint dst = m68ki_read_8(ea);
uint res = LOW_NIBBLE(dst) - LOW_NIBBLE(src) - XFLAG_AS_1();
// FLAG_V = ~res; /* Undefined V behavior */
FLAG_V = VFLAG_CLEAR; /* Undefined in Motorola's M68000PM/AD rev.1 and safer to return zero. */
FLAG_V = ~res; /* Undefined V behavior */
if(res > 9)
res -= 6;
res += HIGH_NIBBLE(dst) - HIGH_NIBBLE(src);
if(res > 0x99)
{
FLAG_X = FLAG_C = (res > 0x99) << 8;
if(FLAG_C)
res += 0xa0;
FLAG_X = FLAG_C = CFLAG_SET;
FLAG_N = NFLAG_SET; /* Undefined in Motorola's M68000PM/AD rev.1 and safer to follow carry. */
}
else
FLAG_N = FLAG_X = FLAG_C = 0;
res = MASK_OUT_ABOVE_8(res);
// FLAG_V &= res; /* Undefined V behavior part II */
// FLAG_N = NFLAG_8(res); /* Undefined N behavior */
FLAG_V &= res; /* Undefined V behavior part II */
FLAG_N = NFLAG_8(res); /* Undefined N behavior */
FLAG_Z |= res;
m68ki_write_8(ea, res);
@@ -9232,25 +9161,19 @@ M68KMAKE_OP(sbcd, 8, mm, ay7)
uint dst = m68ki_read_8(ea);
uint res = LOW_NIBBLE(dst) - LOW_NIBBLE(src) - XFLAG_AS_1();
// FLAG_V = ~res; /* Undefined V behavior */
FLAG_V = VFLAG_CLEAR; /* Undefined in Motorola's M68000PM/AD rev.1 and safer to return zero. */
FLAG_V = ~res; /* Undefined V behavior */
if(res > 9)
res -= 6;
res += HIGH_NIBBLE(dst) - HIGH_NIBBLE(src);
if(res > 0x99)
{
FLAG_X = FLAG_C = (res > 0x99) << 8;
if(FLAG_C)
res += 0xa0;
FLAG_X = FLAG_C = CFLAG_SET;
FLAG_N = NFLAG_SET; /* Undefined in Motorola's M68000PM/AD rev.1 and safer to follow carry. */
}
else
FLAG_N = FLAG_X = FLAG_C = 0;
res = MASK_OUT_ABOVE_8(res);
// FLAG_V &= res; /* Undefined V behavior part II */
// FLAG_N = NFLAG_8(res); /* Undefined N behavior */
FLAG_V &= res; /* Undefined V behavior part II */
FLAG_N = NFLAG_8(res); /* Undefined N behavior */
FLAG_Z |= res;
m68ki_write_8(ea, res);
@@ -9264,25 +9187,19 @@ M68KMAKE_OP(sbcd, 8, mm, axy7)
uint dst = m68ki_read_8(ea);
uint res = LOW_NIBBLE(dst) - LOW_NIBBLE(src) - XFLAG_AS_1();
// FLAG_V = ~res; /* Undefined V behavior */
FLAG_V = VFLAG_CLEAR; /* Undefined in Motorola's M68000PM/AD rev.1 and safer to return zero. */
FLAG_V = ~res; /* Undefined V behavior */
if(res > 9)
res -= 6;
res += HIGH_NIBBLE(dst) - HIGH_NIBBLE(src);
if(res > 0x99)
{
FLAG_X = FLAG_C = (res > 0x99) << 8;
if(FLAG_C)
res += 0xa0;
FLAG_X = FLAG_C = CFLAG_SET;
FLAG_N = NFLAG_SET; /* Undefined in Motorola's M68000PM/AD rev.1 and safer to follow carry. */
}
else
FLAG_N = FLAG_X = FLAG_C = 0;
res = MASK_OUT_ABOVE_8(res);
// FLAG_V &= res; /* Undefined V behavior part II */
// FLAG_N = NFLAG_8(res); /* Undefined N behavior */
FLAG_V &= res; /* Undefined V behavior part II */
FLAG_N = NFLAG_8(res); /* Undefined N behavior */
FLAG_Z |= res;
m68ki_write_8(ea, res);
@@ -9296,25 +9213,19 @@ M68KMAKE_OP(sbcd, 8, mm, .)
uint dst = m68ki_read_8(ea);
uint res = LOW_NIBBLE(dst) - LOW_NIBBLE(src) - XFLAG_AS_1();
// FLAG_V = ~res; /* Undefined V behavior */
FLAG_V = VFLAG_CLEAR; /* Undefined in Motorola's M68000PM/AD rev.1 and safer to return zero. */
FLAG_V = ~res; /* Undefined V behavior */
if(res > 9)
res -= 6;
res += HIGH_NIBBLE(dst) - HIGH_NIBBLE(src);
if(res > 0x99)
{
FLAG_X = FLAG_C = (res > 0x99) << 8;
if(FLAG_C)
res += 0xa0;
FLAG_X = FLAG_C = CFLAG_SET;
FLAG_N = NFLAG_SET; /* Undefined in Motorola's M68000PM/AD rev.1 and safer to follow carry. */
}
else
FLAG_N = FLAG_X = FLAG_C = 0;
res = MASK_OUT_ABOVE_8(res);
// FLAG_V &= res; /* Undefined V behavior part II */
// FLAG_N = NFLAG_8(res); /* Undefined N behavior */
FLAG_V &= res; /* Undefined V behavior part II */
FLAG_N = NFLAG_8(res); /* Undefined N behavior */
FLAG_Z |= res;
m68ki_write_8(ea, res);
@@ -9573,8 +9484,9 @@ M68KMAKE_OP(suba, 16, ., a)
M68KMAKE_OP(suba, 16, ., .)
{
uint* r_dst = &AX;
uint src = MAKE_INT_16(M68KMAKE_GET_OPER_AY_16);
*r_dst = MASK_OUT_ABOVE_32(*r_dst - MAKE_INT_16(M68KMAKE_GET_OPER_AY_16));
*r_dst = MASK_OUT_ABOVE_32(*r_dst - src);
}
@@ -9596,9 +9508,10 @@ M68KMAKE_OP(suba, 32, ., a)
M68KMAKE_OP(suba, 32, ., .)
{
uint src = M68KMAKE_GET_OPER_AY_32;
uint* r_dst = &AX;
*r_dst = MASK_OUT_ABOVE_32(*r_dst - M68KMAKE_GET_OPER_AY_32);
*r_dst = MASK_OUT_ABOVE_32(*r_dst - src);
}
@@ -10033,6 +9946,7 @@ M68KMAKE_OP(trapt, 16, ., .)
{
if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE))
{
REG_PC += 2; // JFF else stackframe & return addresses are incorrect
m68ki_exception_trap(EXCEPTION_TRAPV); /* HJB 990403 */
return;
}
@@ -10044,6 +9958,7 @@ M68KMAKE_OP(trapt, 32, ., .)
{
if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE))
{
REG_PC += 4; // JFF else stackframe & return addresses are incorrect
m68ki_exception_trap(EXCEPTION_TRAPV); /* HJB 990403 */
return;
}
@@ -10099,12 +10014,13 @@ M68KMAKE_OP(trapcc, 16, ., .)
{
if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE))
{
REG_PC += 2; /* JFF increase before or 1) stackframe is incorrect 2) RTE address is wrong if trap is taken */
if(M68KMAKE_CC)
{
m68ki_exception_trap(EXCEPTION_TRAPV); /* HJB 990403 */
return;
}
REG_PC += 2;
return;
}
m68ki_exception_illegal();
@@ -10115,12 +10031,12 @@ M68KMAKE_OP(trapcc, 32, ., .)
{
if(CPU_TYPE_IS_EC020_PLUS(CPU_TYPE))
{
REG_PC += 4; /* JFF increase before or 1) stackframe is incorrect 2) RTE address is wrong if trap is taken */
if(M68KMAKE_CC)
{
m68ki_exception_trap(EXCEPTION_TRAPV); /* HJB 990403 */
return;
}
REG_PC += 4;
return;
}
m68ki_exception_illegal();
+20 -13
View File
@@ -1,12 +1,12 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
** Copyright 2003-2022 The Supermodel Team
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
@@ -18,11 +18,11 @@
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* m68kconf.h
*
* Musashi configuration.
* Musashi configuration.
*
* Permission was obtained from Karl Stenerud to apply the GPL license to this
* code.
@@ -96,8 +96,8 @@
* If off, all interrupts will be autovectored and all interrupt requests will
* auto-clear when the interrupt is serviced.
*/
#define M68K_EMULATE_INT_ACK OPT_SPECIFY_HANDLER
#define M68K_INT_ACK_CALLBACK(A) M68KIRQCallback(A)
#define M68K_EMULATE_INT_ACK OPT_ON
#define M68K_INT_ACK_CALLBACK(A) your_int_ack_handler_function(A)
/* If ON, CPU will call the breakpoint acknowledge callback when it encounters
@@ -116,7 +116,7 @@
* instruction.
*/
#define M68K_EMULATE_RESET OPT_OFF
#define M68K_RESET_CALLBACK() M68KResetCallback()
#define M68K_RESET_CALLBACK() your_reset_instr_handler_function()
/* If ON, CPU will call the callback when it encounters a cmpi.l #v, dn
@@ -154,12 +154,12 @@
* instruction.
*/
#ifdef SUPERMODEL_DEBUGGER
#define M68K_INSTRUCTION_HOOK OPT_SPECIFY_HANDLER
#define M68K_INSTRUCTION_CALLBACK() M68KDebugCallback()
#define M68K_INSTRUCTION_HOOK OPT_ON
#else
#define M68K_INSTRUCTION_HOOK OPT_OFF
#endif // SUPERMODEL_DEBUGGER
#define M68K_INSTRUCTION_CALLBACK() your_instruction_hook_function()
#endif // SUPERMODEL_DEBUGGER
/* If ON, the CPU will emulate the 4-byte prefetch queue of a real 68000 */
#define M68K_EMULATE_PREFETCH OPT_OFF
@@ -194,14 +194,18 @@
#define M68K_USE_64_BIT OPT_ON
/* Set to your compiler's static inline keyword to enable it, or
* set it to blank to disable it.
* If you define INLINE in the makefile, it will override this value.
* NOTE: not enabling inline functions will SEVERELY slow down emulation.
*/
#ifndef INLINE
#define INLINE static __inline__ // defined for GCC; if using MSVC, pass INLINE as "static __inline" from Makefile
#endif /* INLINE */
#ifdef INLINE
#undef INLINE
#endif
#define INLINE static inline
/******************************************************************************
Supermodel Interface
@@ -212,6 +216,9 @@
#undef FASTCALL
#define FASTCALL
//#endif
extern int M68KIRQCallback(int nIRQ);
unsigned int FASTCALL M68KFetch8(unsigned int a);
unsigned int FASTCALL M68KFetch16(unsigned int a);
unsigned int FASTCALL M68KFetch32(unsigned int a);
+10 -10
View File
@@ -113,7 +113,7 @@ uint m68ki_aerr_write_mode;
uint m68ki_aerr_fc;
/* Used by shift & rotate instructions */
uint8 m68ki_shift_8_table[65] =
const uint8 m68ki_shift_8_table[65] =
{
0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
@@ -122,7 +122,7 @@ uint8 m68ki_shift_8_table[65] =
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff
};
uint16 m68ki_shift_16_table[65] =
const uint16 m68ki_shift_16_table[65] =
{
0x0000, 0x8000, 0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00, 0xff00,
0xff80, 0xffc0, 0xffe0, 0xfff0, 0xfff8, 0xfffc, 0xfffe, 0xffff, 0xffff,
@@ -133,7 +133,7 @@ uint16 m68ki_shift_16_table[65] =
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
0xffff, 0xffff
};
uint m68ki_shift_32_table[65] =
const uint m68ki_shift_32_table[65] =
{
0x00000000, 0x80000000, 0xc0000000, 0xe0000000, 0xf0000000, 0xf8000000,
0xfc000000, 0xfe000000, 0xff000000, 0xff800000, 0xffc00000, 0xffe00000,
@@ -152,10 +152,10 @@ uint m68ki_shift_32_table[65] =
/* Number of clock cycles to use for exception processing.
* I used 4 for any vectors that are undocumented for processing times.
*/
uint8 m68ki_exception_cycle_table[3][256] =
const uint8 m68ki_exception_cycle_table[3][256] =
{
{ /* 000 */
4, /* 0: Reset - Initial Stack Pointer */
40, /* 0: Reset - Initial Stack Pointer */
4, /* 1: Reset - Initial Program Counter */
50, /* 2: Bus Error (unemulated) */
50, /* 3: Address Error (unemulated) */
@@ -165,8 +165,8 @@ uint8 m68ki_exception_cycle_table[3][256] =
34, /* 7: TRAPV */
34, /* 8: Privilege Violation */
34, /* 9: Trace */
4, /* 10: 1010 */
4, /* 11: 1111 */
34, /* 10: 1010 */
34, /* 11: 1111 */
4, /* 12: RESERVED */
4, /* 13: Coprocessor Protocol Violation (unemulated) */
4, /* 14: Format Error */
@@ -228,7 +228,7 @@ uint8 m68ki_exception_cycle_table[3][256] =
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
},
{ /* 010 */
4, /* 0: Reset - Initial Stack Pointer */
40, /* 0: Reset - Initial Stack Pointer */
4, /* 1: Reset - Initial Program Counter */
126, /* 2: Bus Error (unemulated) */
126, /* 3: Address Error (unemulated) */
@@ -375,7 +375,7 @@ uint8 m68ki_exception_cycle_table[3][256] =
}
};
uint8 m68ki_ea_idx_cycle_table[64] =
const uint8 m68ki_ea_idx_cycle_table[64] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, /* ..01.000 no memory indirect, base NULL */
@@ -549,7 +549,7 @@ void m68k_set_reg(m68k_register_t regnum, unsigned int value)
case M68K_REG_A6: REG_A[6] = MASK_OUT_ABOVE_32(value); return;
case M68K_REG_A7: REG_A[7] = MASK_OUT_ABOVE_32(value); return;
case M68K_REG_PC: m68ki_jump(MASK_OUT_ABOVE_32(value)); return;
case M68K_REG_SR: m68ki_set_sr(value); return;
case M68K_REG_SR: m68ki_set_sr_noint_nosp(value); return;
case M68K_REG_SP: REG_SP = MASK_OUT_ABOVE_32(value); return;
case M68K_REG_USP: if(FLAG_S)
REG_USP = MASK_OUT_ABOVE_32(value);
+13 -13
View File
@@ -587,7 +587,7 @@
#if M68K_LOG_ENABLE
#include <stdio.h>
extern FILE* M68K_LOG_FILEHANDLE
extern char* m68ki_cpu_names[];
extern const char *const m68ki_cpu_names[];
#define M68K_DO_LOG(A) if(M68K_LOG_FILEHANDLE) fprintf A
#if M68K_LOG_1010_1111
@@ -842,12 +842,12 @@
extern m68ki_cpu_core m68ki_cpu;
extern sint m68ki_remaining_cycles;
extern uint m68ki_tracing;
extern uint8 m68ki_shift_8_table[];
extern uint16 m68ki_shift_16_table[];
extern uint m68ki_shift_32_table[];
extern uint8 m68ki_exception_cycle_table[][256];
extern const uint8 m68ki_shift_8_table[];
extern const uint16 m68ki_shift_16_table[];
extern const uint m68ki_shift_32_table[];
extern const uint8 m68ki_exception_cycle_table[][256];
extern uint m68ki_address_space;
extern uint8 m68ki_ea_idx_cycle_table[];
extern const uint8 m68ki_ea_idx_cycle_table[];
extern uint m68ki_aerr_address;
extern uint m68ki_aerr_write_mode;
@@ -1201,7 +1201,7 @@ INLINE uint m68ki_get_ea_ix(uint An)
/* Check if base displacement is present */
if(BIT_5(extension)) /* BD SIZE */
bd = BIT_4(extension) ? m68ki_read_imm_32() : MAKE_INT_16(m68ki_read_imm_16());
bd = BIT_4(extension) ? m68ki_read_imm_32() : (uint32)MAKE_INT_16(m68ki_read_imm_16());
/* If no indirect action, we are done */
if(!(extension&7)) /* No Memory Indirect */
@@ -1209,7 +1209,7 @@ INLINE uint m68ki_get_ea_ix(uint An)
/* Check if outer displacement is present */
if(BIT_1(extension)) /* I/IS: od */
od = BIT_0(extension) ? m68ki_read_imm_32() : MAKE_INT_16(m68ki_read_imm_16());
od = BIT_0(extension) ? m68ki_read_imm_32() : (uint32)MAKE_INT_16(m68ki_read_imm_16());
/* Postindex */
if(BIT_2(extension)) /* I/IS: 0 = preindex, 1 = postindex */
@@ -1533,7 +1533,7 @@ INLINE void m68ki_stack_frame_buserr(uint sr)
/* Format 8 stack frame (68010).
* 68010 only. This is the 29 word bus/address error frame.
*/
void m68ki_stack_frame_1000(uint pc, uint sr, uint vector)
static inline void m68ki_stack_frame_1000(uint pc, uint sr, uint vector)
{
/* VERSION
* NUMBER
@@ -1587,7 +1587,7 @@ void m68ki_stack_frame_1000(uint pc, uint sr, uint vector)
* if the error happens at an instruction boundary.
* PC stacked is address of next instruction.
*/
void m68ki_stack_frame_1010(uint sr, uint vector, uint pc)
static inline void m68ki_stack_frame_1010(uint sr, uint vector, uint pc)
{
/* INTERNAL REGISTER */
m68ki_push_16(0);
@@ -1634,7 +1634,7 @@ void m68ki_stack_frame_1010(uint sr, uint vector, uint pc)
* if the error happens during instruction execution.
* PC stacked is address of instruction in progress.
*/
void m68ki_stack_frame_1011(uint sr, uint vector, uint pc)
static inline void m68ki_stack_frame_1011(uint sr, uint vector, uint pc)
{
/* INTERNAL REGISTERS (18 words) */
m68ki_push_32(0);
@@ -1856,7 +1856,7 @@ INLINE void m68ki_exception_address_error(void)
uint sr = m68ki_init_exception();
/* If we were processing a bus error, address error, or reset,
* this is a catastrophic failure.
* while writing the stack frame, this is a catastrophic failure.
* Halt the CPU
*/
if(CPU_RUN_MODE == RUN_MODE_BERR_AERR_RESET)
@@ -1878,7 +1878,7 @@ m68k_read_memory_8(0x00ffff01);
/* Service an interrupt request and start exception processing */
void m68ki_exception_interrupt(uint int_level)
static inline void m68ki_exception_interrupt(uint int_level)
{
uint vector;
uint sr;
+2 -2
View File
@@ -251,8 +251,8 @@ static const char* g_cpcc[64] =
{/* 000 001 010 011 100 101 110 111 */
"f", "eq", "ogt", "oge", "olt", "ole", "ogl", "or", /* 000 */
"un", "ueq", "ugt", "uge", "ult", "ule", "ne", "t", /* 001 */
"sf", "seq", "gt", "ge", "lt", "le", "gl" "gle", /* 010 */
"ngle", "ngl", "nle", "nlt", "nge", "ngt", "sne", "st", /* 011 */
"sf", "seq", "gt", "ge", "lt", "le", "gl", "gle", /* 010 */
"ngle", "ngl", "nle", "nlt", "nge", "ngt", "sne","st", /* 011 */
"?", "?", "?", "?", "?", "?", "?", "?", /* 100 */
"?", "?", "?", "?", "?", "?", "?", "?", /* 101 */
"?", "?", "?", "?", "?", "?", "?", "?", /* 110 */
+4 -4
View File
@@ -632,7 +632,7 @@ int fgetline(char* buff, int nchars, FILE* file)
if(fgets(buff, nchars, file) == NULL)
return -1;
if(buff[0] == '\r')
memcpy(buff, buff + 1, nchars - 1);
memmove(buff, buff + 1, nchars - 1);
length = strlen(buff);
while(length && (buff[length-1] == '\r' || buff[length-1] == '\n'))
@@ -1126,13 +1126,13 @@ void populate_table(void)
/* Find the start of the table */
while(strcmp(buff, ID_TABLE_START) != 0)
if(fgetline(buff, MAX_LINE_LENGTH, g_input_file) < 0)
error_exit("Premature EOF while reading table");
error_exit("(table_start) Premature EOF while reading table");
/* Process the entire table */
for(op = g_opcode_input_table;;op++)
{
if(fgetline(buff, MAX_LINE_LENGTH, g_input_file) < 0)
error_exit("Premature EOF while reading table");
error_exit("(inline) Premature EOF while reading table");
if(strlen(buff) == 0)
continue;
/* We finish when we find an input separator */
@@ -1270,7 +1270,7 @@ int main(int argc, char **argv)
{
/* File stuff */
char output_path[M68K_MAX_DIR] = "";
char filename[M68K_MAX_PATH];
char filename[M68K_MAX_PATH*2];
/* Section identifier */
char section_id[MAX_LINE_LENGTH+1];
/* Inserts */
+1
View File
@@ -28,6 +28,7 @@
#ifndef INCLUDED_BUS_H
#define INCLUDED_BUS_H
#include "Types.h"
/*
* IBus:
+37 -36
View File
@@ -6,7 +6,7 @@
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
@@ -18,7 +18,7 @@
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* PPCDisasm.cpp
*
@@ -43,7 +43,7 @@
/******************************************************************************
Instruction Descriptions
The disassembler is primarily table-driven making it easily modifiable.
******************************************************************************/
@@ -147,8 +147,8 @@ enum
F_BCx, // BO, BI, target_addr used only by BCx
F_RT_RA_0_SIMM, // rT, rA|0, SIMM rA|0 means if rA == 0, print 0
F_ADDIS, // rT, rA, SIMM (printed as unsigned) only used by ADDIS
F_RT_RA_SIMM, // rT, rA, SIMM
F_RA_RT_UIMM, // rA, rT, UIMM
F_RT_RA_SIMM, // rT, rA, SIMM
F_RA_RT_UIMM, // rA, rT, UIMM
F_CMP_SIMM, // crfD, L, A, SIMM
F_CMP_UIMM, // crfD, L, A, UIMM
F_RT_RA_0_RB, // rT, rA|0, rB
@@ -517,7 +517,7 @@ static void SPR(char *dest, unsigned spr_field)
case 982: strcat(dest, "rpa"); break;
case 1010: strcat(dest, "iabr"); break;
default: sprintf(dest, "%s%d", dest, spr);
default: strcat(dest, std::to_string(spr).c_str());
break;
}
}
@@ -575,11 +575,11 @@ static uint32_t Mask(unsigned mb, unsigned me)
* Perform checks on the instruction as required by the flags. Returns 1 if
* the instruction failed.
*/
static bool Check(uint32_t op, unsigned flags)
static Result Check(uint32_t op, unsigned flags)
{
unsigned nb, rt, ra;
if (!flags) return OKAY; // nothing to check for!
if (!flags) return Result::OKAY; // nothing to check for!
rt = G_RT(op);
ra = G_RA(op);
@@ -587,13 +587,13 @@ static bool Check(uint32_t op, unsigned flags)
if (flags & FL_CHECK_RA_RT) // invalid if rA==0 or rA==rT
{
if ((G_RA(op) == 0) || (G_RA(op) == G_RT(op)))
return FAIL;
return Result::FAIL;
}
if (flags & FL_CHECK_RA) // invalid if rA==0
{
if (G_RA(op) == 0)
return FAIL;
return Result::FAIL;
}
if (flags & FL_CHECK_LSWI)
@@ -605,11 +605,11 @@ static bool Check(uint32_t op, unsigned flags)
nb = G_NB(op);
if (ra >= rt && ra <= (rt + nb - 1)) return FAIL;
if (ra >= rt && ra <= (rt + nb - 1)) return Result::FAIL;
if ((rt + nb - 1) > 31) // register wrap-around!
{
if (ra < ((rt + nb - 1) - 31))
return FAIL;
return Result::FAIL;
}
}
@@ -624,10 +624,10 @@ static bool Check(uint32_t op, unsigned flags)
*/
if (rt == ra || rt == G_RB(op) || ((rt == 0) && (ra == 0)))
return FAIL;
return Result::FAIL;
}
return OKAY; // passed checks
return Result::OKAY; // passed checks
}
/*
@@ -655,7 +655,7 @@ static bool Simplified(uint32_t op, uint32_t vpc, char *signed16, char *mnem, ch
sprintf(oprs, "r%d,r%d", G_RA(op), G_RT(op));
}
else
return 0;
return false;
}
else if ((op & ~(M_RT|M_RA|M_RB|M_RC)) == (D_OP(31)|D_XO(124)))
{
@@ -666,7 +666,7 @@ static bool Simplified(uint32_t op, uint32_t vpc, char *signed16, char *mnem, ch
sprintf(oprs, "r%d,r%d", G_RA(op), G_RT(op));
}
else
return 0;
return false;
}
else if ((op & ~(M_RT|M_RA|M_SIMM)) == D_OP(14))
{
@@ -676,7 +676,7 @@ static bool Simplified(uint32_t op, uint32_t vpc, char *signed16, char *mnem, ch
sprintf(oprs, "r%d,0x%08X", G_RT(op), value);
}
else
return 0;
return false;
}
else if ((op & ~(M_RT|M_RA|M_SIMM)) == D_OP(15))
{
@@ -705,7 +705,7 @@ static bool Simplified(uint32_t op, uint32_t vpc, char *signed16, char *mnem, ch
{
strcat(mnem, "xori"); // xoris rA,rT,UIMM -> xori rA,rT,UIMM<<16
sprintf(oprs, "r%d,r%d,0x%08X", G_RA(op), G_RT(op), G_UIMM(op) << 16);
}
}
else if ((op & ~(M_RT|M_RA|M_SH|M_MB|M_ME|M_RC)) == D_OP(20))
{
value = Mask(G_MB(op), G_ME(op));
@@ -757,7 +757,7 @@ static bool Simplified(uint32_t op, uint32_t vpc, char *signed16, char *mnem, ch
strcat(mnem, "bt");
break;
default:
return 0;
return false;
}
if (op & M_LK) strcat(mnem, "l");
@@ -780,14 +780,14 @@ static bool Simplified(uint32_t op, uint32_t vpc, char *signed16, char *mnem, ch
sprintf(oprs, "r%d,r%d,r%d", G_RT(op), G_RB(op), G_RA(op));
}
else
return 0; // no match
return 1;
return false; // no match
return true;
}
/*
* DisassemblePowerPC(op, vpc, mnem, oprs, simplify):
*
* Disassembles one PowerPC 603e instruction.
* Disassembles one PowerPC 603e instruction.
*
* A non-zero return code indicates that the instruction could not be
* recognized or that the operands to an instruction were invalid. To
@@ -807,8 +807,8 @@ static bool Simplified(uint32_t op, uint32_t vpc, char *signed16, char *mnem, ch
* Returns:
* Zero if successful, non-zero if the instruction was unrecognized or
* had an invalid form (see note above in function description.)
*/
bool DisassemblePowerPC(uint32_t op, uint32_t vpc, char *mnem, char *oprs,
*/
Result DisassemblePowerPC(uint32_t op, uint32_t vpc, char *mnem, char *oprs,
bool simplify)
{
char signed16[12];
@@ -821,7 +821,7 @@ bool DisassemblePowerPC(uint32_t op, uint32_t vpc, char *mnem, char *oprs,
* Decode signed 16-bit fields (SIMM and d) to spare us the work later
*/
DecodeSigned16(signed16, op, 0);
DecodeSigned16(signed16, op, false);
/*
* Try simplified forms first, then real instructions
@@ -830,7 +830,7 @@ bool DisassemblePowerPC(uint32_t op, uint32_t vpc, char *mnem, char *oprs,
if (simplify)
{
if (Simplified(op, vpc, signed16, mnem, oprs))
return OKAY;
return Result::OKAY;
}
/*
@@ -1053,7 +1053,8 @@ bool DisassemblePowerPC(uint32_t op, uint32_t vpc, char *mnem, char *oprs,
case F_MTSPR:
SPR(oprs, G_SPR(op));
sprintf(oprs, "%s,r%d", oprs, G_RT(op));
strcat(oprs, ",r");
strcat(oprs, std::to_string(G_RT(op)).c_str());
break;
case F_MTSR:
@@ -1097,13 +1098,13 @@ bool DisassemblePowerPC(uint32_t op, uint32_t vpc, char *mnem, char *oprs,
}
}
return FAIL; // no match found
return Result::FAIL; // no match found
}
/******************************************************************************
Standalone Disassembler
Define STANDALONE to build a command line-driven PowerPC disassembler.
******************************************************************************/
@@ -1134,8 +1135,8 @@ int main(int argc, char **argv)
char mnem[16], oprs[48];
FILE *fp;
uint8_t *buffer;
unsigned i, fsize, start = 0, len, org, file = 0;
uint32_t op;
unsigned fsize, start = 0, len, org, file = 0;
uint32_t op;
bool len_specified = 0, org_specified = 0, little = 0, simple = 1;
char *c;
@@ -1143,7 +1144,7 @@ int main(int argc, char **argv)
if (argc <= 1)
PrintUsage();
for (i = 1; i < argc; i++)
for (int i = 1; i < argc; i++)
{
if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-?"))
PrintUsage();
@@ -1194,7 +1195,7 @@ int main(int argc, char **argv)
fprintf(stderr, "ppcd: no input file specified\n");
exit(1);
}
/*
* Load file
*/
@@ -1209,7 +1210,7 @@ int main(int argc, char **argv)
rewind(fp);
if ((buffer = (uint8_t *) calloc(fsize, sizeof(uint8_t))) == NULL)
{
{
fprintf(stderr, "ppcd: not enough memory to load input file: %s, %lu bytes\n", argv[file], (unsigned long) fsize);
fclose(fp);
exit(1);
@@ -1229,7 +1230,7 @@ int main(int argc, char **argv)
* Disassemble!
*/
for (i = start; i < fsize && i < (start + len); i += 4, org += 4)
for (unsigned i = start; i < fsize && i < (start + len); i += 4, org += 4)
{
if (!little)
op = (buffer[i] << 24) | (buffer[i + 1] << 16) |
@@ -1238,7 +1239,7 @@ int main(int argc, char **argv)
op = (buffer[i + 3] << 24) | (buffer[i + 2] << 16) |
(buffer[i + 1] << 8) | buffer[i + 0];
if (DisassemblePowerPC(op, org, mnem, oprs, simple))
if (Result::OKAY == DisassemblePowerPC(op, org, mnem, oprs, simple))
{
if (mnem[0] != '\0') // invalid form
printf("0x%08X: 0x%08X\t%s*\t%s\n", org, op, mnem, oprs);
+3 -1
View File
@@ -28,6 +28,8 @@
#ifndef INCLUDED_PPCDISASM_H
#define INCLUDED_PPCDISASM_H
#include "Types.h"
/*
* DisassemblePowerPC(op, vpc, mnem, oprs, simplify):
*
@@ -52,7 +54,7 @@
* OKAY if successful, FAIL if the instruction was unrecognized or had an
* invalid form (see note above in function description.)
*/
extern bool DisassemblePowerPC(UINT32 op, UINT32 vpc, char *mnem, char *oprs,
extern Result DisassemblePowerPC(UINT32 op, UINT32 vpc, char *mnem, char *oprs,
bool simplify);
#endif // INCLUDED_PPCDISASM_H
+72 -70
View File
@@ -28,16 +28,15 @@
/* IBM/Motorola PowerPC 4xx/6xx Emulator */
#include "ppc.h"
#include <cstring> // memset()
#include "Supermodel.h"
#include "ppc.h"
#include "CPU/Bus.h"
// Typedefs that Supermodel no longer provides
typedef unsigned int UINT;
// C++ should allow this...
#define INLINE inline
// Model 3 context provides read/write handlers
static class IBus *Bus = NULL; // pointer to Model 3 bus object (for access handlers)
@@ -118,12 +117,12 @@ static UINT32 ppc_field_xlat[256];
#define BITMASK_0(n) (UINT32)(((UINT64)1 << n) - 1)
#define CRBIT(x) ((ppc.cr[x / 4] & (1 << (3 - (x % 4)))) ? 1 : 0)
#define BITMASK_0(n) (UINT32)(((UINT64)1 << (n)) - 1)
#define CRBIT(x) ((ppc.cr[(x) / 4] & (1 << (3 - ((x) % 4)))) ? 1 : 0)
#define _BIT(n) (1 << (n))
#define GET_ROTATE_MASK(mb,me) (ppc_rotate_mask[mb][me])
#define ADD_CA(r,a,b) ((UINT32)r < (UINT32)a)
#define SUB_CA(r,a,b) (!((UINT32)a < (UINT32)b))
#define ADD_CA(r,a,b) ((UINT32)(r) < (UINT32)(a))
#define SUB_CA(r,a,b) (!((UINT32)(a) < (UINT32)(b)))
#define ADD_OV(r,a,b) ((~((a) ^ (b)) & ((a) ^ (r))) & 0x80000000)
#define SUB_OV(r,a,b) (( ((a) ^ (b)) & ((a) ^ (r))) & 0x80000000)
@@ -155,8 +154,8 @@ static UINT32 ppc_field_xlat[256];
#define TSR_ENW 0x80000000
#define TSR_WIS 0x40000000
#define BYTE_REVERSE16(x) (((x >> 8) | (x << 8)) & 0xFFFF)
#define BYTE_REVERSE32(x) ((x >> 24) | ((x << 8) & 0x00FF0000) | ((x >> 8) & 0x0000FF00) | (x << 24))
#define BYTE_REVERSE16(x) ((((x) >> 8) | ((x) << 8)) & 0xFFFF)
#define BYTE_REVERSE32(x) (((x) >> 24) | (((x) << 8) & 0x00FF0000) | (((x) >> 8) & 0x0000FF00) | ((x) << 24))
typedef union {
UINT64 id;
@@ -290,25 +289,25 @@ static UINT32 ppc_rotate_mask[32][32];
static void ppc_change_pc(UINT32 newpc)
{
UINT i;
UINT32 offset = newpc - ppc.cur_fetch.start; // unsigned wrap around can happen, that's defined behavour
UINT32 range = ppc.cur_fetch.end - ppc.cur_fetch.start;
if (ppc.cur_fetch.start <= newpc && newpc <= ppc.cur_fetch.end)
if (offset <= range)
{
ppc.op = &ppc.cur_fetch.ptr[(newpc-ppc.cur_fetch.start)/4];
// ppc.op = (UINT32 *)((void *)ppc.cur_fetch.ptr + (UINT32)(newpc - ppc.cur_fetch.start));
ppc.op = &ppc.cur_fetch.ptr[offset / 4];
return;
}
for(i = 0; ppc.fetch[i].ptr != NULL; i++)
for(UINT32 i = 0; ppc.fetch[i].ptr != NULL; i++)
{
if (ppc.fetch[i].start <= newpc && newpc <= ppc.fetch[i].end)
{
ppc.cur_fetch.start = ppc.fetch[i].start;
ppc.cur_fetch.end = ppc.fetch[i].end;
ppc.cur_fetch.ptr = ppc.fetch[i].ptr;
offset = newpc - ppc.fetch[i].start;
range = ppc.fetch[i].end - ppc.fetch[i].start;
// ppc.op = (UINT32 *)((UINT32)ppc.cur_fetch.ptr + (UINT32)(newpc - ppc.cur_fetch.start));
ppc.op = &ppc.cur_fetch.ptr[(newpc-ppc.cur_fetch.start)/4];
if (offset <= range)
{
ppc.cur_fetch = ppc.fetch[i];
ppc.op = &ppc.cur_fetch.ptr[offset / 4];
return;
}
}
@@ -318,42 +317,42 @@ static void ppc_change_pc(UINT32 newpc)
ppc.fatalError = true;
}
INLINE UINT8 READ8(UINT32 address)
static inline UINT8 READ8(UINT32 address)
{
return Bus->Read8(address);
}
INLINE UINT16 READ16(UINT32 address)
static inline UINT16 READ16(UINT32 address)
{
return Bus->Read16(address);
}
INLINE UINT32 READ32(UINT32 address)
static inline UINT32 READ32(UINT32 address)
{
return Bus->Read32(address);
}
INLINE UINT64 READ64(UINT32 address)
static inline UINT64 READ64(UINT32 address)
{
return Bus->Read64(address);
}
INLINE void WRITE8(UINT32 address, UINT8 data)
static inline void WRITE8(UINT32 address, UINT8 data)
{
Bus->Write8(address,data);
}
INLINE void WRITE16(UINT32 address, UINT16 data)
static inline void WRITE16(UINT32 address, UINT16 data)
{
Bus->Write16(address,data);
}
INLINE void WRITE32(UINT32 address, UINT32 data)
static inline void WRITE32(UINT32 address, UINT32 data)
{
Bus->Write32(address,data);
}
INLINE void WRITE64(UINT32 address, UINT64 data)
static inline void WRITE64(UINT32 address, UINT64 data)
{
Bus->Write64(address,data);
}
@@ -362,7 +361,7 @@ INLINE void WRITE64(UINT32 address, UINT64 data)
/*********************************************************************/
INLINE void SET_CR0(INT32 rd)
static inline void SET_CR0(INT32 rd)
{
if( rd < 0 ) {
CR(0) = 0x8;
@@ -376,12 +375,12 @@ INLINE void SET_CR0(INT32 rd)
CR(0) |= 0x1;
}
INLINE void SET_CR1(void)
static inline void SET_CR1(void)
{
CR(1) = (ppc.fpscr >> 28) & 0xf;
}
INLINE void SET_ADD_OV(UINT32 rd, UINT32 ra, UINT32 rb)
static inline void SET_ADD_OV(UINT32 rd, UINT32 ra, UINT32 rb)
{
if( ADD_OV(rd, ra, rb) )
XER |= XER_SO | XER_OV;
@@ -389,7 +388,7 @@ INLINE void SET_ADD_OV(UINT32 rd, UINT32 ra, UINT32 rb)
XER &= ~XER_OV;
}
INLINE void SET_SUB_OV(UINT32 rd, UINT32 ra, UINT32 rb)
static inline void SET_SUB_OV(UINT32 rd, UINT32 ra, UINT32 rb)
{
if( SUB_OV(rd, ra, rb) )
XER |= XER_SO | XER_OV;
@@ -397,7 +396,7 @@ INLINE void SET_SUB_OV(UINT32 rd, UINT32 ra, UINT32 rb)
XER &= ~XER_OV;
}
INLINE void SET_ADD_CA(UINT32 rd, UINT32 ra, UINT32 rb)
static inline void SET_ADD_CA(UINT32 rd, UINT32 ra, UINT32 rb)
{
if( ADD_CA(rd, ra, rb) )
XER |= XER_CA;
@@ -405,7 +404,7 @@ INLINE void SET_ADD_CA(UINT32 rd, UINT32 ra, UINT32 rb)
XER &= ~XER_CA;
}
INLINE void SET_SUB_CA(UINT32 rd, UINT32 ra, UINT32 rb)
static inline void SET_SUB_CA(UINT32 rd, UINT32 ra, UINT32 rb)
{
if( SUB_CA(rd, ra, rb) )
XER |= XER_CA;
@@ -413,25 +412,23 @@ INLINE void SET_SUB_CA(UINT32 rd, UINT32 ra, UINT32 rb)
XER &= ~XER_CA;
}
INLINE UINT32 check_condition_code(UINT32 bo, UINT32 bi)
static inline UINT32 check_condition_code(UINT32 bo, UINT32 bi)
{
UINT32 ctr_ok;
UINT32 condition_ok;
UINT32 bo0 = (bo & 0x10) ? 1 : 0;
UINT32 bo1 = (bo & 0x08) ? 1 : 0;
UINT32 bo2 = (bo & 0x04) ? 1 : 0;
UINT32 bo3 = (bo & 0x02) ? 1 : 0;
if( bo2 == 0 )
if (bo2 == 0)
--CTR;
ctr_ok = bo2 | ((CTR != 0) ^ bo3);
condition_ok = bo0 | (CRBIT(bi) ^ (~bo1 & 0x1));
UINT32 ctr_ok = bo2 | ((CTR != 0) ^ bo3);
UINT32 condition_ok = bo0 | (CRBIT(bi) ^ (bo1 ^ 0x1));
return ctr_ok && condition_ok;
return ctr_ok & condition_ok;
}
INLINE UINT64 ppc_read_timebase(void)
static inline UINT64 ppc_read_timebase(void)
{
int cycles = ppc.tb_base_icount - ppc.icount;
@@ -439,7 +436,7 @@ INLINE UINT64 ppc_read_timebase(void)
return ppc.tb + (cycles / ppc.timer_ratio);
}
INLINE void ppc_write_timebase_l(UINT32 tbl)
static inline void ppc_write_timebase_l(UINT32 tbl)
{
UINT64 tb = ppc_read_timebase();
@@ -448,7 +445,7 @@ INLINE void ppc_write_timebase_l(UINT32 tbl)
ppc.tb = (tb&~0xffffffff)|tbl;
}
INLINE void ppc_write_timebase_h(UINT32 tbh)
static inline void ppc_write_timebase_h(UINT32 tbh)
{
UINT64 tb = ppc_read_timebase();
@@ -457,7 +454,7 @@ INLINE void ppc_write_timebase_h(UINT32 tbh)
ppc.tb = (tb&0xffffffff)|((UINT64)(tbh) << 32);
}
INLINE UINT32 read_decrementer(void)
static inline UINT32 read_decrementer(void)
{
int cycles = ppc.dec_base_icount - ppc.icount;
@@ -465,7 +462,7 @@ INLINE UINT32 read_decrementer(void)
return DEC - (cycles / ppc.timer_ratio);
}
INLINE void write_decrementer(UINT32 value)
static inline void write_decrementer(UINT32 value)
{
if (((value&0x80000000) && !(read_decrementer()&0x80000000)))
{
@@ -488,7 +485,7 @@ INLINE void write_decrementer(UINT32 value)
/*********************************************************************/
INLINE void ppc_set_spr(int spr, UINT32 value)
static inline void ppc_set_spr(int spr, UINT32 value)
{
switch (spr)
{
@@ -561,7 +558,7 @@ INLINE void ppc_set_spr(int spr, UINT32 value)
ppc.fatalError = true;
}
INLINE UINT32 ppc_get_spr(int spr)
static inline UINT32 ppc_get_spr(int spr)
{
switch(spr)
{
@@ -624,7 +621,7 @@ INLINE UINT32 ppc_get_spr(int spr)
return 0;
}
INLINE void ppc_set_msr(UINT32 value)
static inline void ppc_set_msr(UINT32 value)
{
if( value & (MSR_ILE | MSR_LE) )
{
@@ -638,12 +635,12 @@ INLINE void ppc_set_msr(UINT32 value)
ppc603_check_interrupts();
}
INLINE UINT32 ppc_get_msr(void)
static inline UINT32 ppc_get_msr(void)
{
return MSR;
}
INLINE void ppc_set_cr(UINT32 value)
static inline void ppc_set_cr(UINT32 value)
{
CR(0) = (value >> 28) & 0xf;
CR(1) = (value >> 24) & 0xf;
@@ -655,7 +652,7 @@ INLINE void ppc_set_cr(UINT32 value)
CR(7) = (value >> 0) & 0xf;
}
INLINE UINT32 ppc_get_cr(void)
static inline UINT32 ppc_get_cr(void)
{
return CR(0) << 28 | CR(1) << 24 | CR(2) << 20 | CR(3) << 16 | CR(4) << 12 | CR(5) << 8 | CR(6) << 4 | CR(7);
}
@@ -719,10 +716,9 @@ void ppc_base_init(void)
/* Calculate rotate mask table */
for( i=0; i < 32; i++ ) {
for( j=0; j < 32; j++ ) {
UINT32 mask;
int mb = i;
int me = j;
mask = ((UINT32)0xFFFFFFFF >> mb) ^ ((me >= 31) ? 0 : ((UINT32)0xFFFFFFFF >> (me + 1)));
UINT32 mask = ((UINT32)0xFFFFFFFF >> mb) ^ ((me >= 31) ? 0 : ((UINT32)0xFFFFFFFF >> (me + 1)));
if( mb > me )
mask = ~mask;
@@ -835,15 +831,15 @@ void ppc_init(const PPC_CONFIG *config)
switch (config->bus_frequency)
{
case BUS_FREQUENCY_16MHZ: ppc.cycles_per_second = multiplier * 16000000; break;
case BUS_FREQUENCY_20MHZ: ppc.cycles_per_second = multiplier * 20000000; break;
case BUS_FREQUENCY_25MHZ: ppc.cycles_per_second = multiplier * 25000000; break;
case BUS_FREQUENCY_33MHZ: ppc.cycles_per_second = multiplier * 33000000; break;
case BUS_FREQUENCY_40MHZ: ppc.cycles_per_second = multiplier * 40000000; break;
case BUS_FREQUENCY_50MHZ: ppc.cycles_per_second = multiplier * 50000000; break;
case BUS_FREQUENCY_60MHZ: ppc.cycles_per_second = multiplier * 60000000; break;
case BUS_FREQUENCY_66MHZ: ppc.cycles_per_second = multiplier * 66000000; break;
case BUS_FREQUENCY_75MHZ: ppc.cycles_per_second = multiplier * 75000000; break;
case BUS_FREQUENCY_16MHZ: ppc.cycles_per_second = (int)(multiplier * 16000000); break;
case BUS_FREQUENCY_20MHZ: ppc.cycles_per_second = (int)(multiplier * 20000000); break;
case BUS_FREQUENCY_25MHZ: ppc.cycles_per_second = (int)(multiplier * 25000000); break;
case BUS_FREQUENCY_33MHZ: ppc.cycles_per_second = (int)(multiplier * 33000000); break;
case BUS_FREQUENCY_40MHZ: ppc.cycles_per_second = (int)(multiplier * 40000000); break;
case BUS_FREQUENCY_50MHZ: ppc.cycles_per_second = (int)(multiplier * 50000000); break;
case BUS_FREQUENCY_60MHZ: ppc.cycles_per_second = (int)(multiplier * 60000000); break;
case BUS_FREQUENCY_66MHZ: ppc.cycles_per_second = (int)(multiplier * 66000000); break;
case BUS_FREQUENCY_75MHZ: ppc.cycles_per_second = (int)(multiplier * 75000000); break;
}
switch(config->pvr)
@@ -869,9 +865,15 @@ void ppc_shutdown(void)
void ppc_set_irq_line(int irqline)
{
ppc.interrupt_pending |= 0x1;
ppc603_check_interrupts();
if (irqline)
{
ppc.interrupt_pending |= 0x1;
ppc603_check_interrupts();
}
else
{
ppc.interrupt_pending &= ~0x1;
}
}
UINT32 ppc_get_pc(void)
@@ -995,7 +997,7 @@ void ppc_save_state(CBlockFile *SaveState)
void ppc_load_state(CBlockFile *SaveState)
{
if (OKAY != SaveState->FindBlock("PowerPC"))
if (Result::OKAY != SaveState->FindBlock("PowerPC"))
{
ErrorLog("Unable to load PowerPC state. Save state file is corrupt.");
return;
@@ -1086,7 +1088,7 @@ UINT32 ppc_get_lr(void)
{
return ppc.lr;
}
UINT32 ppc_read_spr(unsigned spr)
{
return ppc_get_spr(spr);
+3
View File
@@ -28,6 +28,9 @@
#ifndef INCLUDED_PPC_H
#define INCLUDED_PPC_H
#include "BlockFile.h"
#include "Types.h"
#include "Debugger/CPU/PPCDebug.h"
/******************************************************************************
Definitions
+2 -9
View File
@@ -53,7 +53,7 @@ void ppc603_exception(int exception)
else
ppc.npc = 0x00000000 | 0x0500;
ppc.interrupt_pending &= ~0x1;
//MAME has this: ppc.interrupt_pending &= ~0x1;
ppc_change_pc(ppc.npc);
}
break;
@@ -206,13 +206,6 @@ void ppc603_exception(int exception)
}
}
static void ppc603_set_smi_line(int state)
{
if( state ) {
ppc.interrupt_pending |= 0x4;
}
}
static void ppc603_check_interrupts(void)
{
if (MSR & MSR_EE)
@@ -226,7 +219,7 @@ static void ppc603_check_interrupts(void)
else if (ppc.interrupt_pending & 0x2)
{
ppc603_exception(EXCEPTION_DECREMENTER);
}
}
else if (ppc.interrupt_pending & 0x4)
{
ppc603_exception(EXCEPTION_SMI);
+364 -338
View File
File diff suppressed because it is too large. Load diff
+498 -496
View File
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -157,7 +157,7 @@ public:
* Returns:
* Current value of PC register.
*/
UINT16 GetPC(void);
UINT16 GetPC(void) const;
#ifdef SUPERMODEL_DEBUGGER
/*
View File
File mode changed.
View File
File mode changed.
+1 -1
View File
@@ -270,7 +270,7 @@ namespace Debugger
char valStr[40];
UINT32 opcode = m_bus->Read32(addr);
operands[0] = '\0';
if (!::DisassemblePowerPC(opcode, addr, mnemonic, opStr, true))
if (DisassemblePowerPC(opcode, addr, mnemonic, opStr, true) == Result::OKAY)
{
char *o = opStr;
char *s = strstr(o, "0x");
Regular → Executable
+14 -12
View File
@@ -19,8 +19,8 @@
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* Z80Debug.cpp
/*
* Z80Debug.cpp
*/
#ifdef SUPERMODEL_DEBUGGER
@@ -33,7 +33,7 @@
namespace Debugger
{
// Instruction templates
static char *templates[5][256] = {
static const char *templates[5][256] = {
{
// Table 0: single byte instructions
"NOP","LD BC,@a","LD (BC),A","INC BC","INC B","DEC B","LD B,@d","RLCA",
@@ -393,10 +393,10 @@ namespace Debugger
switch (opcode)
{
case 0xCB:
templ = templates[1][m_bus->Read8(dAddr++)];
templ = (char*)templates[1][m_bus->Read8(dAddr++)];
break;
case 0xED:
templ = templates[2][m_bus->Read8(dAddr++)];
templ = (char*)templates[2][m_bus->Read8(dAddr++)];
break;
case 0xDD:
xyChr = 'X';
@@ -405,10 +405,10 @@ namespace Debugger
{
offs = m_bus->Read8(dAddr++);
notJump = true;
templ = templates[4][m_bus->Read8(dAddr++)];
templ = (char*)templates[4][m_bus->Read8(dAddr++)];
}
else
templ = templates[3][nextCode];
templ = (char*)templates[3][nextCode];
break;
case 0xFD:
xyChr = 'Y';
@@ -417,13 +417,13 @@ namespace Debugger
{
offs = m_bus->Read8(dAddr++);
notJump = true;
templ = templates[4][m_bus->Read8(dAddr++)];
templ = (char*)templates[4][m_bus->Read8(dAddr++)];
}
else
templ = templates[3][nextCode];
templ = (char*)templates[3][nextCode];
break;
default:
templ = templates[0][opcode];
templ = (char*)templates[0][opcode];
break;
}
@@ -476,12 +476,14 @@ namespace Debugger
}
break;
case 'j': // #H
tAddr = m_bus->Read8(dAddr++) + (m_bus->Read8(dAddr++)<<8);
tAddr = m_bus->Read8(dAddr++);
tAddr += m_bus->Read8(dAddr++) << 8;
opFlags = GetOpFlags(addr, opcode);
FormatJumpAddress(valStr, tAddr, opFlags);
break;
case 'a': // #h
tAddr = m_bus->Read8(dAddr++) + (m_bus->Read8(dAddr++)<<8);
tAddr = m_bus->Read8(dAddr++);
tAddr += m_bus->Read8(dAddr++) << 8;
FormatAddress(valStr, tAddr, true);
break;
}
Regular → Executable
+2 -2
View File
@@ -19,8 +19,8 @@
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* Z80KDebug.h
/*
* Z80KDebug.h
*/
#ifdef SUPERMODEL_DEBUGGER
+2 -2
View File
@@ -1321,7 +1321,7 @@ namespace Debugger
// Load labels
sprintf(blockStr, "%s.labels", name);
if (state->FindBlock(blockStr) == OKAY)
if (state->FindBlock(blockStr) == Result::OKAY)
{
labels.clear();
UINT32 numLabels;
@@ -1340,7 +1340,7 @@ namespace Debugger
// Load comments
sprintf(blockStr, "%s.comments", name);
if (state->FindBlock(blockStr) == OKAY)
if (state->FindBlock(blockStr) == Result::OKAY)
{
comments.clear();
UINT32 numComments;
+1 -1
View File
@@ -705,7 +705,7 @@ namespace Debugger
// Load custom entry addresses
char blockStr[255];
sprintf(blockStr, "%s.entryaddrs", cpu->name);
if (state->FindBlock(blockStr) == OKAY)
if (state->FindBlock(blockStr) == Result::OKAY)
{
m_customEntryAddrs.clear();
UINT32 numAddrs;
+2 -2
View File
@@ -40,7 +40,7 @@ namespace Debugger
CConsoleDebugger::CConsoleDebugger() : CDebugger(),
m_nextFrame(false), m_listDism(0), m_listMem(0), m_analyseCode(true),
m_addrFmt(HexDollar), m_portFmt(Decimal), m_dataFmt(HexDollar),
m_showLabels(true), m_labelsOverAddr(true), m_showOpCodes(false), m_memBytesPerRow(12), m_file(NULL)
m_showLabels(true), m_labelsOverAddr(true), m_showOpCodes(false), m_memBytesPerRow(16), m_file(NULL)
{
//
}
@@ -1538,7 +1538,7 @@ namespace Debugger
else if (CheckToken(token, "h", "help")) // help
{
// TODO - improve the following
const char *fmt = " %-6s %-25s %s\n";
static const char *fmt = " %-6s %-25s %s\n";
Print("Debugger Commands:\n");
+3 -3
View File
@@ -437,9 +437,9 @@ namespace Debugger
#ifdef DEBUGGER_HASBLOCKFILE
// Open file and find header
CBlockFile state;
if (state.Load(fileName) != OKAY)
if (state.Load(fileName) != Result::OKAY)
return false;
if (state.FindBlock("Debugger State") != OKAY)
if (state.FindBlock("Debugger State") != Result::OKAY)
{
state.Close();
return false;
@@ -483,7 +483,7 @@ namespace Debugger
#ifdef DEBUGGER_HASBLOCKFILE
// Create file with header
CBlockFile state;
if (state.Create(fileName, "Debugger State", __FILE__) != OKAY)
if (state.Create(fileName, "Debugger State", __FILE__) != Result::OKAY)
return false;
// Write out version in header
+1 -1
View File
@@ -60,7 +60,7 @@
#ifndef stricmp
#ifdef _MSC_VER // MS VisualC++
#define stricmp _stricmp
#else // Assume GC
#elif defined(__GNUC__)
#define stricmp strcasecmp
#endif // _MSC_VER
#endif // stricmp
+2 -2
View File
@@ -19,8 +19,8 @@
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* Exception.cpp
/*
* Exception.cpp
*/
#ifdef SUPERMODEL_DEBUGGER
Regular → Executable
View File
File mode changed.
+57 -42
View File
@@ -6,7 +6,7 @@
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
@@ -25,11 +25,23 @@
#ifdef SUPERMODEL_DEBUGGER
#include "Supermodel.h"
#include "SupermodelDebugger.h"
#include "Supermodel.h"
#include "BlockFile.h"
#include "ConsoleDebugger.h"
#include "CPUDebug.h"
#include "Label.h"
#ifdef NET_BOARD
#include "Network/NetBoard.h"
#endif // NET_BOARD
#include "CPU/Musashi68KDebug.h"
#include "CPU/PPCDebug.h"
#include "CPU/Z80Debug.h"
#include "Inputs/Input.h"
#include "Inputs/InputSystem.h"
#include "Model3/SoundBoard.h"
#include "OSD/Audio.h"
#include <cstdio>
#include <string>
@@ -42,7 +54,7 @@ namespace Debugger
static char s_mSlotStr[32][20];
static char s_sSlotStr[32][20];
CCPUDebug *CSupermodelDebugger::CreateMainBoardCPUDebug(::CModel3 *model3)
{
CPPCDebug *cpu = new CPPCDebug("MainPPC");
@@ -50,8 +62,8 @@ namespace Debugger
// Interrupts
cpu->AddInterrupt("VD0", 0, "Unknown video-related");
cpu->AddInterrupt("VBL", 1, "VBlank start");
cpu->AddInterrupt("VD2", 2, "Unknown video-related");
cpu->AddInterrupt("VD3", 3, "Unknown video-related");
cpu->AddInterrupt("VDP", 2, "DP done (display processing)");
cpu->AddInterrupt("VGP", 3, "GP done (geometry processing)");
cpu->AddInterrupt("NET", 4, "Network");
cpu->AddInterrupt("UN5", 5, "Unknown");
cpu->AddInterrupt("SND", 6, "SCSP (sound)");
@@ -70,8 +82,8 @@ namespace Debugger
cpu->AddRegion(0xC0000000, 0xC00000FF, false, false, "SCSI (Step 1.x)");
#endif
#ifdef NET_BOARD
cpu->AddRegion(0xC0000000, 0xC001FFFF, false, false, "Network Buffer");
cpu->AddRegion(0xC0020000, 0xC003FFFF, false, false, "Network RAM");
cpu->AddRegion(0xC0000000, 0xC000FFFF, false, false, "Netboard Shared RAM (Step 1.5+)");
cpu->AddRegion(0xC0020000, 0xC002FFFF, false, false, "Netboard Program RAM (Step 1.5+)");
#endif
cpu->AddRegion(0xC1000000, 0xC10000FF, false, false, "SCSI (Step 1.x) (Lost World expects it here)");
cpu->AddRegion(0xC2000000, 0xC20000FF, false, false, "Real3D DMA (Step 2.x)");
@@ -79,7 +91,7 @@ namespace Debugger
cpu->AddRegion(0xF0080000, 0xF0080007, false, false, "Sound Board Registers");
cpu->AddRegion(0xF00C0000, 0xF00DFFFF, false, false, "Backup RAM");
cpu->AddRegion(0xF0100000, 0xF010003F, false, false, "System Registers");
cpu->AddRegion(0xF0140000, 0xF014003F, false, false, "Real, 0xTime Clock");
cpu->AddRegion(0xF0140000, 0xF014003F, false, false, "Real-Time Clock");
cpu->AddRegion(0xF0180000, 0xF019FFFF, false, false, "Security Board RAM");
cpu->AddRegion(0xF01A0000, 0xF01A003F, false, false, "Security Board Registers");
cpu->AddRegion(0xF0800CF8, 0xF0800CFF, false, false, "MPC105 CONFIG_cpu->AddR (Step 1.x)");
@@ -91,6 +103,7 @@ namespace Debugger
cpu->AddRegion(0xF8FFF000, 0xF8FFF0FF, false, false, "MPC105 (Step 1.x) or MPC106 (Step 2.x) Registers");
cpu->AddRegion(0xF9000000, 0xF90000FF, false, false, "NCR 53C810 Registers (Step 1.x?)");
cpu->AddRegion(0xFE040000, 0xFE04003F, false, false, "Mirrored Input Registers");
cpu->AddRegion(0xFE100000, 0xFE10003F, false, false, "Mirrored System Registers");
cpu->AddRegion(0xFEC00000, 0xFEDFFFFF, false, false, "MPC106 CONFIG_cpu->AddR (Step 2.x)");
cpu->AddRegion(0xFEE00000, 0xFEFFFFFF, false, false, "MPC106 CONFIG_DATA (Step 2.x)");
cpu->AddRegion(0xFF000000, 0xFF7FFFFF, true, true, "Banked CROM (CROMxx)");
@@ -170,7 +183,7 @@ namespace Debugger
cpu->AddMappedIO(addr + 0x17, 1, "EFSDL,EFPAN", s_mSlotStr[slot]);
}
// SCSP Master control registers
const char *masterCtl = "SCSP Master Control Registers";
static const char *masterCtl = "SCSP Master Control Registers";
cpu->AddMappedIO(0x100400, 1, "MEM4MB,DAC18B", masterCtl);
cpu->AddMappedIO(0x100401, 1, "VER,MVOL", masterCtl);
cpu->AddMappedIO(0x100402, 2, "RBL,RBP", masterCtl);
@@ -220,7 +233,7 @@ namespace Debugger
}
// SCSP Master control registers
const char *slaveCtl = "SCSP Slave Control Registers";
static const char *slaveCtl = "SCSP Slave Control Registers";
cpu->AddMappedIO(0x300400, 1, "MEM4MB,DAC18B", slaveCtl);
cpu->AddMappedIO(0x300401, 1, "VER,MVOL", slaveCtl);
cpu->AddMappedIO(0x300402, 2, "RBL,RBP", slaveCtl);
@@ -254,12 +267,12 @@ namespace Debugger
{
CSoundBoard *sndBrd = model3->GetSoundBoard();
CDSB *dsb = sndBrd->GetDSB();
CDSB1 *dsb1 = dynamic_cast<CDSB1*>(dsb);
if (dsb1 != NULL)
{
CZ80Debug *cpu = new CZ80Debug("DSBZ80", dsb1->GetZ80());
// Regions
cpu->AddRegion(0x0000, 0x7FFF, true, true, "ROM");
cpu->AddRegion(0x8000, 0xFFFF, false, false, "RAM");
@@ -267,12 +280,12 @@ namespace Debugger
// TODO - rename some I/O ports
return cpu;
}
CDSB2 *dsb2 = dynamic_cast<CDSB2*>(dsb);
if (dsb2 != NULL)
{
CMusashi68KDebug *cpu = new CMusashi68KDebug("DSB68K", dsb2->GetM68K());
// Regions
cpu->AddRegion(0x000000, 0x020000, true, true, "ROM");
cpu->AddRegion(0xF00000, 0xF10000, false, false, "RAM");
@@ -290,7 +303,7 @@ namespace Debugger
if (!drvBrd->IsAttached())
return NULL;
CZ80Debug *cpu = new CZ80Debug("DrvZ80", drvBrd->GetZ80());
// Regions
cpu->AddRegion(0x0000, 0x7FFF, true, true, "ROM");
cpu->AddRegion(0xE000, 0xFFFF, false, false, "RAM");
@@ -302,7 +315,9 @@ namespace Debugger
#ifdef NET_BOARD
CCPUDebug *CSupermodelDebugger::CreateNetBoardCPUDebug(::CModel3 * model3)
{
CNetBoard *netBrd = model3->GetNetBoard();
CNetBoard *netBrd = dynamic_cast<CNetBoard*>(model3->GetNetBoard());
if (!netBrd)
return NULL;
if (!netBrd->IsAttached())
return NULL;
CMusashi68KDebug *cpu = new CMusashi68KDebug("NET68K", netBrd->GetM68K());
@@ -314,7 +329,7 @@ namespace Debugger
cpu->AddRegion(0x080000, 0x0bffff, false, false, "Net 2"); // commram ???
cpu->AddRegion(0x0c0000, 0x0c01ff, false, false, "Net 3"); // ??? size unknown
const char *NetReg = "NetBoard Control Registers";
static const char *NetReg = "NetBoard Control Registers";
cpu->AddMappedIO(0x010110, 4, "Reg 1", NetReg);
cpu->AddMappedIO(0x010114, 4, "Reg 2", NetReg);
cpu->AddMappedIO(0x010180, 4, "Reg 3", NetReg);
@@ -323,8 +338,8 @@ namespace Debugger
}
#endif
CSupermodelDebugger::CSupermodelDebugger(::CModel3 *model3, ::CInputs *inputs, ::CLogger *logger) :
CConsoleDebugger(), m_model3(model3), m_inputs(inputs), m_logger(logger),
CSupermodelDebugger::CSupermodelDebugger(::CModel3 *model3, ::CInputs *inputs, std::shared_ptr<CLogger> logger) :
CConsoleDebugger(), m_model3(model3), m_inputs(inputs), m_logger(logger),
m_loadEmuState(false), m_saveEmuState(false), m_resetEmu(false)
{
//
@@ -337,7 +352,7 @@ namespace Debugger
// Add main board CPU
cpu = CreateMainBoardCPUDebug(m_model3);
if (cpu) AddCPU(cpu);
// Add sound board CPU (if attached)
cpu = CreateSoundBoardCPUDebug(m_model3);
if (cpu) AddCPU(cpu);
@@ -345,7 +360,7 @@ namespace Debugger
// Add sound daughter board CPU (if attached)
cpu = CreateDSBCPUDebug(m_model3);
if (cpu) AddCPU(cpu);
// Add drive board CPU (if attached)
cpu = CreateDriveBoardCPUDebug(m_model3);
if (cpu) AddCPU(cpu);
@@ -427,7 +442,7 @@ namespace Debugger
Print("Mising input name.\n");
return false;
}
::CInput *input = (*m_inputs)[token];
std::shared_ptr<CInput> input = (*m_inputs)[token];
if (input == NULL)
{
Print("No input with id or label '%s'.\n", token);
@@ -458,7 +473,7 @@ namespace Debugger
Print("Mising input id or label.\n");
return false;
}
::CInput *input = (*m_inputs)[token];
std::shared_ptr<CInput> input = (*m_inputs)[token];
if (input == NULL)
{
Print("No input with id or label '%s'.\n", token);
@@ -490,7 +505,7 @@ namespace Debugger
Print("Mising input id or label.\n");
return false;
}
::CInput *input = (*m_inputs)[token];
std::shared_ptr<CInput> input = (*m_inputs)[token];
if (input == NULL)
{
Print("No input with id or label '%s'.\n", token);
@@ -516,7 +531,7 @@ namespace Debugger
Print("Mising input id or label.\n");
return false;
}
CInput *input = (*m_inputs)[token];
std::shared_ptr<CInput> input = (*m_inputs)[token];
if (input == NULL)
{
Print("No input with id or label '%s'.\n", token);
@@ -533,7 +548,7 @@ namespace Debugger
append = false;
else if (CheckToken(token, "a", "append"))
append = true;
else
else
{
Print("Enter a valid mode (s)et or (a)ppend.\n");
return false;
@@ -541,7 +556,7 @@ namespace Debugger
Print("Configure input %s [%s]: %s...", input->label, input->GetMapping(), (append ? "Appending" : "Setting"));
fflush(stdout); // required on terminals that use buffering
// Configure the input
if (input->Configure(append, "KEY_ESCAPE"))
Print(" %s\n", input->GetMapping());
@@ -551,7 +566,7 @@ namespace Debugger
}
else if (CheckToken(token, "caip", "configallinputs")) // configallinputs
{
m_inputs->ConfigureInputs(&m_model3->GetGame());
m_inputs->ConfigureInputs(m_model3->GetGame());
return false;
}
//
@@ -561,7 +576,7 @@ namespace Debugger
{
CConsoleDebugger::ProcessToken(token, cmd);
const char *fmt = " %-6s %-25s %s\n";
static const char *fmt = " %-6s %-25s %s\n";
Print(" Emulator:\n");
Print(fmt, "les", "loademustate", "<filename>");
Print(fmt, "ses", "saveemustate", "<filename>");
@@ -572,14 +587,14 @@ namespace Debugger
Print(fmt, "sip", "setinput", "(<id>|<label>) <mapping>");
Print(fmt, "rip", "resetinput", "(<id>|<label>)");
Print(fmt, "cip", "configinput", "(<id>|<label>) [(s)et|(a)ppend]");
Print(fmt, "caip", "configallinputs", "");
Print(fmt, "caip", "configallinputs", "");
return false;
}
else
return CConsoleDebugger::ProcessToken(token, cmd);
return CConsoleDebugger::ProcessToken(token, cmd);
}
bool CSupermodelDebugger::InputIsValid(::CInput *input)
bool CSupermodelDebugger::InputIsValid(std::shared_ptr<CInput> input)
{
return input->IsUIInput() || (input->gameFlags & m_model3->GetGame().inputs);
}
@@ -595,7 +610,7 @@ namespace Debugger
size_t mappingWidth = 0;
for (unsigned i = 0; i < m_inputs->Count(); i++)
{
::CInput *input = (*m_inputs)[i];
std::shared_ptr<CInput> input = (*m_inputs)[i];
if (!InputIsValid(input))
continue;
@@ -604,14 +619,14 @@ namespace Debugger
mappingWidth = std::max<size_t>(mappingWidth, strlen(input->GetMapping()));
}
mappingWidth = std::min<size_t>(mappingWidth, 20);
// Print labels, mappings and values for each input
const char *groupLabel = NULL;
char idAndLabel[255];
char mapping[21];
for (unsigned i = 0; i < m_inputs->Count(); i++)
{
::CInput *input = (*m_inputs)[i];
std::shared_ptr<CInput> input = (*m_inputs)[i];
if (!InputIsValid(input))
continue;
@@ -645,17 +660,17 @@ namespace Debugger
char fileName[25];
sprintf(fileName, "Debug/%s.ds", m_model3->GetGame().name.c_str());
SaveState(fileName);
CConsoleDebugger::Detaching();
}
bool CSupermodelDebugger::LoadModel3State(const char *fileName)
{
// Open file and find header
// Open file and find header
CBlockFile state;
if (state.Load(fileName) != OKAY)
if (state.Load(fileName) != Result::OKAY)
return false;
if (state.FindBlock("Debugger Model3 State") != OKAY)
if (state.FindBlock("Debugger Model3 State") != Result::OKAY)
{
state.Close();
return false;
@@ -683,7 +698,7 @@ namespace Debugger
{
// Create file with header
CBlockFile state;
if (state.Create(fileName, "Debugger Model3 State", __FILE__) != OKAY)
if (state.Create(fileName, "Debugger Model3 State", __FILE__) != Result::OKAY)
return false;
// Write out version in header
@@ -713,7 +728,7 @@ namespace Debugger
else
CConsoleDebugger::DebugLog(fmt, vl);
}
void CSupermodelDebugger::InfoLog(const char *fmt, va_list vl)
{
// Use the supplied logger, if any
@@ -722,7 +737,7 @@ namespace Debugger
else
CConsoleDebugger::InfoLog(fmt, vl);
}
void CSupermodelDebugger::ErrorLog(const char *fmt, va_list vl)
{
// Use the supplied logger, if any
+9 -9
View File
@@ -6,7 +6,7 @@
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
@@ -28,12 +28,12 @@
#define INCLUDED_SUPERMODELDEBUGGER_H
#include "ConsoleDebugger.h"
#include "Model3/Model3.h"
#include <stdarg.h>
#define MODEL3_STATEFILE_VERSION 0
class CModel3;
class CInputs;
class CInput;
@@ -49,14 +49,14 @@ namespace Debugger
private:
::CModel3 *m_model3;
::CInputs *m_inputs;
::CLogger *m_logger;
std::shared_ptr<CLogger> m_logger;
bool m_loadEmuState;
bool m_saveEmuState;
bool m_resetEmu;
char m_stateFile[255];
bool InputIsValid(CInput *input);
bool InputIsValid(std::shared_ptr<CInput> input);
void ListInputs();
@@ -73,7 +73,7 @@ namespace Debugger
public:
static CCPUDebug *CreateMainBoardCPUDebug(::CModel3 *model3);
static CCPUDebug *CreateSoundBoardCPUDebug(::CModel3 *model3);
static CCPUDebug *CreateDSBCPUDebug(::CModel3 *model3);
@@ -83,10 +83,10 @@ namespace Debugger
static CCPUDebug *CreateNetBoardCPUDebug(::CModel3 *model3);
#endif
CSupermodelDebugger(::CModel3 *model3, ::CInputs *inputs, ::CLogger *logger);
CSupermodelDebugger(::CModel3 *model3, ::CInputs *inputs, std::shared_ptr<CLogger> logger);
void Poll();
bool LoadModel3State(const char *fileName);
bool SaveModel3State(const char *fileName);
@@ -94,9 +94,9 @@ namespace Debugger
void ResetModel3();
void DebugLog(const char *fmt, va_list vl);
void InfoLog(const char *fmt, va_list vl);
void ErrorLog(const char *fmt, va_list vl);
};
}
+28 -1
View File
@@ -3,6 +3,7 @@
#include <string>
#include <memory>
#include <cstdint>
struct Game
{
@@ -14,13 +15,29 @@ struct Game
unsigned year = 0;
std::string stepping;
std::string mpeg_board;
enum AudioTypes
{
MONO = 0, // Merge DSB+SCSP1+SCSP2 to 1 channel mono
STEREO_LR, // Merge DSP+SCSP1+SCSP2 to 2 channels stereo Left/Right (most common)
STEREO_RL, // Merge DSP+SCSP1+SCSP2 to 2 channels stereo reversed Right/Left
QUAD_1_FLR_2_RLR, // Split DSB+SCSP1 to FrontLeft/FrontRight and SCSP2 to RearLeft/RearRight (Daytona2)
QUAD_1_FRL_2_RRL, // Split DSB+SCSP1 to FrontRight/FrontLeft and SCSP2 to RearRight/RearLeft
QUAD_1_RLR_2_FLR, // Split DSB+SCSP1 to RearLeft/RearRight and SCSP2 to FrontLeft/FrontRight
QUAD_1_RRL_2_FRL, // Split DSB+SCSP1 to RearRight/RearLeft and SCSP2 to FrontRight/FrontLeft
QUAD_1_LR_2_FR_MIX, // Specific srally2: Split SCSP2 and mix first channel to DSB+SCP11 Front Left/Right and second to Read Left/Right
};
AudioTypes audio = STEREO_LR;
std::string pci_bridge; // overrides default PCI bridge type for stepping (empty string for default)
uint32_t real3d_pci_id = 0; // overrides default Real3D PCI ID for stepping (0 for default)
uint32_t encryption_key = 0;
bool netboard_present = false;
enum Inputs
{
INPUT_UI = 0, // special code reserved for Supermodel UI inputs
INPUT_COMMON = 0x00000001, // common controls (coins, service, test)
INPUT_VEHICLE = 0x00000002, // vehicle controls
INPUT_JOYSTICK1 = 0x00000004, // joystick 1
INPUT_JOYSTICK1 = 0x00000004, // joystick 1
INPUT_JOYSTICK2 = 0x00000008, // joystick 2
INPUT_FIGHTING = 0x00000010, // fighting game controls
INPUT_VR4 = 0x00000020, // four VR view buttons
@@ -43,6 +60,16 @@ struct Game
INPUT_ALL = 0x003FFFFF
};
uint32_t inputs = 0;
enum DriveBoardType
{
DRIVE_BOARD_NONE = 0,
DRIVE_BOARD_WHEEL,
DRIVE_BOARD_JOYSTICK,
DRIVE_BOARD_SKI,
DRIVE_BOARD_BILLBOARD
};
DriveBoardType driveboard_type = DriveBoardType::DRIVE_BOARD_NONE;
};
#endif // INCLUDED_GAME_H
+300 -139
View File
@@ -5,6 +5,7 @@
#include "Util/ByteSwap.h"
#include "Util/Format.h"
#include <algorithm>
#include <cctype>
#include <cstring>
#include <iostream>
@@ -18,9 +19,9 @@ bool GameLoader::LoadZipArchive(ZipArchive *zip, const std::string &zipfilename)
}
zip->zipfilenames.push_back(zipfilename);
zip->zfs.push_back(zf);
// Identify all files in zip archive
int err = UNZ_OK;
int err;
for (err = unzGoToFirstFile(zf); err == UNZ_OK; err = unzGoToNextFile(zf))
{
unz_file_info file_info;
@@ -50,7 +51,7 @@ bool GameLoader::FileExistsInZipArchive(const File::ptr_t &file, const ZipArchiv
auto it = zip.files_by_crc.find(file->crc32);
return it != zip.files_by_crc.end();
}
// Try to lookup by name
for (auto &v: zip.files_by_crc)
{
@@ -75,7 +76,7 @@ const GameLoader::ZippedFile *GameLoader::LookupFile(const File::ptr_t &file, co
}
return &it->second;
}
// Try to lookup by name
for (auto &v: zip.files_by_crc)
{
@@ -100,7 +101,7 @@ bool GameLoader::LoadZippedFile(std::shared_ptr<uint8_t> *buffer, size_t *file_s
ErrorLog("Unable to locate '%s' in '%s'. Is zip file corrupt?", zipped_file->filename.c_str(), zipped_file->zipfilename.c_str());
return true;
}
// Read it in
if (UNZ_OK != unzOpenCurrentFile(zipped_file->zf))
{
@@ -109,14 +110,14 @@ bool GameLoader::LoadZippedFile(std::shared_ptr<uint8_t> *buffer, size_t *file_s
}
*file_size = zipped_file->uncompressed_size;
buffer->reset(new uint8_t[*file_size], std::default_delete<uint8_t[]>());
ZPOS64_T bytes_read = unzReadCurrentFile(zipped_file->zf, buffer->get(), *file_size);
size_t bytes_read = (size_t) unzReadCurrentFile(zipped_file->zf, buffer->get(), *file_size);
if (bytes_read != *file_size)
{
ErrorLog("Unable to read '%s' from '%s'. Is zip file corrupt?", zipped_file->filename.c_str(), zipped_file->zipfilename.c_str());
unzCloseCurrentFile(zipped_file->zf);
return true;
}
// And close it
if (UNZ_CRCERROR == unzCloseCurrentFile(zipped_file->zf))
ErrorLog("CRC error reading '%s' from '%s'. File may be corrupt.", zipped_file->filename.c_str(), zipped_file->zipfilename.c_str());
@@ -135,7 +136,7 @@ bool GameLoader::MissingAttrib(const GameLoader &loader, const Util::Config::Nod
GameLoader::File::ptr_t GameLoader::File::Create(const GameLoader &loader, const Util::Config::Node &file_node)
{
if (GameLoader::MissingAttrib(loader, file_node, "name") | GameLoader::MissingAttrib(loader, file_node, "offset"))
if (GameLoader::MissingAttrib(loader, file_node, "name") | GameLoader::MissingAttrib(loader, file_node, "offset")) // no || to easier detect errors
return ptr_t();
ptr_t file = std::make_shared<File>();
file->offset = file_node["offset"].ValueAs<uint32_t>();
@@ -159,19 +160,70 @@ bool GameLoader::File::operator==(const File &rhs) const
GameLoader::Region::ptr_t GameLoader::Region::Create(const GameLoader &loader, const Util::Config::Node &region_node)
{
if (GameLoader::MissingAttrib(loader, region_node, "name") | MissingAttrib(loader, region_node, "stride") | GameLoader::MissingAttrib(loader, region_node, "chunk_size"))
if (GameLoader::MissingAttrib(loader, region_node, "name") | MissingAttrib(loader, region_node, "stride") | GameLoader::MissingAttrib(loader, region_node, "chunk_size")) // no || to easier detect errors
return ptr_t();
if (region_node["byte_swap"].Exists() && region_node["byte_layout"].Exists())
{
ErrorLog("%s: '%s' region has both 'byte_swap' and 'byte_layout' attributes. Use one or the other.", loader.m_xml_filename.c_str(), region_node["name"].Value<std::string>().c_str());
return ptr_t();
}
ptr_t region = std::make_shared<Region>();
region->region_name = region_node["name"].Value<std::string>();
region->stride = region_node["stride"].ValueAs<size_t>();
if (region->stride == 0)
{
ErrorLog("%s: '%s' stride length must be greater than 0.", loader.m_xml_filename.c_str(), region->region_name.c_str());
return ptr_t();
}
region->chunk_size = region_node["chunk_size"].ValueAs<size_t>();
region->byte_swap = region_node["byte_swap"].ValueAsDefault<bool>(false);
if (region->chunk_size == 0)
{
ErrorLog("%s: '%s' chunk size must be greater than 0.", loader.m_xml_filename.c_str(), region->region_name.c_str());
return ptr_t();
}
region->required = region_node["required"].ValueAsDefault<bool>(true);
// Byte layout. If byte_swap was specified, construct the byte swapped layout string based on
// stride size. If byte_swap is set to false, empty layout string is fine.
if (region_node["byte_swap"].Exists())
{
if (region_node["byte_swap"].ValueAs<bool>())
{
// Special case: if chunk size and stride are both 1, change them both to 2 so we can allow byte
// swapping (these values are used for singular ROMs that don't need to be merged; technically,
// the stride and chunk size should be 2 since they are 16-bit ROMs).
if (region->stride == 1 && region->chunk_size == 1)
{
region->stride = 2;
region->chunk_size = 2;
}
std::string byte_layout;
for (size_t i = 0; i < region->stride; i++)
{
byte_layout += '0' + (i ^ 1);
}
region->byte_layout = byte_layout;
}
}
else
{
region->byte_layout = region_node["byte_layout"].ValueAsDefault<std::string>(std::string());
}
return region;
}
bool GameLoader::Region::AttribsMatch(const ptr_t &other) const
{
return stride == other->stride && chunk_size == other->chunk_size && byte_swap == other->byte_swap;
return stride == other->stride && chunk_size == other->chunk_size && byte_layout == other->byte_layout;
}
bool GameLoader::Region::FindFileIndexByOffset(size_t *idx, uint32_t offset) const
@@ -190,14 +242,32 @@ bool GameLoader::Region::FindFileIndexByOffset(size_t *idx, uint32_t offset) con
static void PopulateGameInfo(Game *game, const Util::Config::Node &game_node)
{
game->name = game_node["name"].ValueAs<std::string>();
game->parent = game_node["parent"].ValueAsDefault<std::string>(std::string());
game->parent = game_node["parent"].ValueAsDefault<std::string>("");
game->title = game_node["identity/title"].ValueAsDefault<std::string>("Unknown");
game->version = game_node["identity/version"].ValueAsDefault<std::string>("");
game->manufacturer = game_node["identity/manufacturer"].ValueAsDefault<std::string>("Unknown");
game->year = game_node["identity/year"].ValueAsDefault<unsigned>(0);
game->stepping = game_node["hardware/stepping"].ValueAsDefault<std::string>("");
game->mpeg_board = game_node["hardware/mpeg_board"].ValueAsDefault<std::string>("");
std::map<std::string, Game::AudioTypes> audio_types
{
{ "", Game::STEREO_LR }, // default to stereo
{ "Mono", Game::MONO },
{ "Stereo", Game::STEREO_LR },
{ "StereoReversed", Game::STEREO_RL },
{ "QuadFrontRear", Game::QUAD_1_FLR_2_RLR },
{ "QuadFrontRearReversed", Game::QUAD_1_FRL_2_RRL },
{ "QuadRearFront", Game::QUAD_1_RLR_2_FLR },
{ "QuadRearFrontReversed", Game::QUAD_1_RRL_2_FRL },
{ "QuadMix", Game::QUAD_1_LR_2_FR_MIX}
};
std::string audio_type = game_node["hardware/audio"].ValueAsDefault<std::string>("");
game->audio = audio_types[audio_type];
game->pci_bridge = game_node["hardware/pci_bridge"].ValueAsDefault<std::string>("");
game->real3d_pci_id = game_node["hardware/real3d_pci_id"].ValueAsDefault<uint32_t>(0);
game->encryption_key = game_node["hardware/encryption_key"].ValueAsDefault<uint32_t>(0);
game->netboard_present = game_node["hardware/netboard"].ValueAsDefault<bool>(false);
std::map<std::string, uint32_t> input_flags
{
{ "common", Game::INPUT_COMMON },
@@ -231,96 +301,106 @@ static void PopulateGameInfo(Game *game, const Util::Config::Node &game_node)
game->inputs |= input_flags[input_type];
}
}
std::map<std::string, Game::DriveBoardType> drive_board_types
{
{ "Wheel", Game::DRIVE_BOARD_WHEEL },
{ "Joystick", Game::DRIVE_BOARD_JOYSTICK },
{ "Ski", Game::DRIVE_BOARD_SKI },
{ "Billboard", Game::DRIVE_BOARD_BILLBOARD}
};
std::string drive_board_type = game_node["hardware/drive_board"].ValueAsDefault<std::string>(std::string());
game->driveboard_type = drive_board_types[drive_board_type];
}
bool GameLoader::LoadGamesFromXML(const Util::Config::Node &xml)
{
for (auto it = xml.begin(); it != xml.end(); ++it)
{
// Root games node
auto &root_node = *it;
if (root_node.Key() != "games")
continue;
for (auto &game_node: root_node)
{
// Game node
if (game_node.Key() != "game")
continue;
if (game_node["name"].Empty())
{
//TODO: associate line numbers in config
//ErrorLog("%s: Ignoring <game> tag with missing 'name' attribute.", m_xml_filename.c_str());
continue;
}
std::string game_name = game_node["name"].ValueAs<std::string>();
if (m_regions_by_game.find(game_name) != m_regions_by_game.end())
{
ErrorLog("%s: Ignoring redefinition of game '%s'.", m_xml_filename.c_str(), game_name.c_str());
continue;
}
{
for (auto it = xml.begin(); it != xml.end(); ++it)
{
// Root games node
auto &root_node = *it;
if (root_node.Key() != "games")
continue;
for (auto &game_node: root_node)
{
// Game node
if (game_node.Key() != "game")
continue;
if (game_node["name"].Empty())
{
//TODO: associate line numbers in config
//ErrorLog("%s: Ignoring <game> tag with missing 'name' attribute.", m_xml_filename.c_str());
continue;
}
std::string game_name = game_node["name"].ValueAs<std::string>();
if (m_regions_by_game.find(game_name) != m_regions_by_game.end())
{
ErrorLog("%s: Ignoring redefinition of game '%s'.", m_xml_filename.c_str(), game_name.c_str());
continue;
}
RegionsByName_t &regions_by_name = m_regions_by_game[game_name];
PatchesByRegion_t &patches_by_region = m_patches_by_game[game_name];
PopulateGameInfo(&m_game_info_by_game[game_name], game_node);
for (auto &roms_node: game_node)
{
if (roms_node.Key() != "roms")
continue;
/*
* Regions define contiguous memory areas that individual ROM files are
* loaded into. It is possible to have multiple region tags identifying
* the same region. They will be aggregated. This is useful for parent
* and child ROM sets, which each may need to define the same region,
* with the child set loading in different files to overwrite the parent
* set.
*/
for (auto &region_node: roms_node)
{
if (region_node.Key() != "region")
continue;
// Look up region structure or create new one if needed
std::string region_name = region_node["name"].Value<std::string>();
auto it = regions_by_name.find(region_name);
Region::ptr_t region = (it != regions_by_name.end()) ? it->second : Region::Create(*this, region_node);
if (!region)
continue;
/*
* Files are defined by the offset they are loaded at. Normally, there
* should be one file per offset but parent/child ROM sets will violate
* this, and so it is allowed.
*/
std::vector<File::ptr_t> &files = region->files;
for (auto &file_node: region_node)
{
if (file_node.Key() != "file")
continue;
File::ptr_t file = File::Create(*this, file_node);
if (!file)
continue;
files.push_back(file);
}
// Check to ensure that some files were defined in the region
if (files.empty())
ErrorLog("%s: No files defined in region '%s' of '%s'.", m_xml_filename.c_str(), region->region_name.c_str(), game_name.c_str());
else
regions_by_name[region->region_name] = region;
PatchesByRegion_t &patches_by_region = m_patches_by_game[game_name];
PopulateGameInfo(&m_game_info_by_game[game_name], game_node);
for (auto &roms_node: game_node)
{
if (roms_node.Key() != "roms")
continue;
/*
* Regions define contiguous memory areas that individual ROM files are
* loaded into. It is possible to have multiple region tags identifying
* the same region. They will be aggregated. This is useful for parent
* and child ROM sets, which each may need to define the same region,
* with the child set loading in different files to overwrite the parent
* set.
*/
for (auto &region_node: roms_node)
{
if (region_node.Key() != "region")
continue;
// Look up region structure or create new one if needed
std::string region_name = region_node["name"].Value<std::string>();
auto it2 = regions_by_name.find(region_name);
Region::ptr_t region = (it2 != regions_by_name.end()) ? it2->second : Region::Create(*this, region_node);
if (!region)
continue;
/*
* Files are defined by the offset they are loaded at. Normally, there
* should be one file per offset but parent/child ROM sets will violate
* this, and so it is allowed.
*/
std::vector<File::ptr_t> &files = region->files;
for (auto &file_node: region_node)
{
if (file_node.Key() != "file")
continue;
File::ptr_t file = File::Create(*this, file_node);
if (!file)
continue;
files.push_back(file);
}
// Check to ensure that some files were defined in the region
if (files.empty())
ErrorLog("%s: No files defined in region '%s' of '%s'.", m_xml_filename.c_str(), region->region_name.c_str(), game_name.c_str());
else
regions_by_name[region->region_name] = region;
}
// ROM patches, if any
for (auto &patches_node: roms_node)
{
if (patches_node.Key() != "patches")
continue;
for (auto &patch_node: patches_node)
{
if (MissingAttrib(*this, patch_node, "region") ||
MissingAttrib(*this, patch_node, "bits") ||
MissingAttrib(*this, patch_node, "offset") ||
MissingAttrib(*this, patch_node, "offset") ||
MissingAttrib(*this, patch_node, "value"))
continue;
std::string region = patch_node["region"].ValueAs<std::string>();
@@ -332,17 +412,17 @@ bool GameLoader::LoadGamesFromXML(const Util::Config::Node &xml)
else
patches_by_region[region].push_back(ROM::BigEndianPatch(offset, value, bits));
}
}
}
// Check to ensure that some ROM regions were defined for the game
if (regions_by_name.empty())
ErrorLog("%s: No ROM regions defined for '%s'.", m_xml_filename.c_str(), game_name.c_str());
}
}
// Check to ensure some games were defined
if (m_regions_by_game.empty())
{
}
}
// Check to ensure that some ROM regions were defined for the game
if (regions_by_name.empty())
ErrorLog("%s: No ROM regions defined for '%s'.", m_xml_filename.c_str(), game_name.c_str());
}
}
// Check to ensure some games were defined
if (m_regions_by_game.empty())
{
ErrorLog("%s: No games defined.", m_xml_filename.c_str());
return true;
}
@@ -351,7 +431,7 @@ bool GameLoader::LoadGamesFromXML(const Util::Config::Node &xml)
static bool IsChildSet(const Game &game)
{
return game.parent.length() > 0;
return !game.parent.empty();
}
bool GameLoader::MergeChildrenWithParents()
@@ -366,7 +446,7 @@ bool GameLoader::MergeChildrenWithParents()
auto &child_regions = v1.second;
auto &parent_regions = m_regions_by_game[game.parent];
// Rebuild child regions by copying over all parent regions first, then
// merge in files from equivalent child regions
RegionsByName_t new_regions;
@@ -377,7 +457,7 @@ bool GameLoader::MergeChildrenWithParents()
auto &region_name = v2.first;
new_regions[region_name] = std::make_shared<Region>(*v2.second);
auto &new_region = new_regions[region_name];
// Replace equivalent files from child in parent region, appending any
// new ones
if (child_regions.find(region_name) != child_regions.end())
@@ -398,7 +478,7 @@ bool GameLoader::MergeChildrenWithParents()
}
}
}
// Simply append any region in child that does *not* exist in parent
for (auto &v2: child_regions)
{
@@ -408,11 +488,11 @@ bool GameLoader::MergeChildrenWithParents()
new_regions[v2.first] = v2.second;
}
}
// Save the final result
m_regions_by_merged_game[v1.first] = new_regions;
}
return error;
}
@@ -421,7 +501,7 @@ void GameLoader::LogROMDefinition(const std::string &game_name, const RegionsByN
InfoLog("%s:", game_name.c_str());
for (auto &v2: regions_by_name)
{
InfoLog(" %s: stride=%zu, chunk size=%zu, byte swap=%d", v2.first.c_str(), v2.second->stride, v2.second->chunk_size, v2.second->byte_swap ? 1 : 0);
InfoLog(" %s: stride=%zu, chunk size=%zu, byte layout=%s", v2.first.c_str(), v2.second->stride, v2.second->chunk_size, v2.second->byte_layout.c_str());
for (auto &file: v2.second->files)
{
InfoLog(" %s, crc32=0x%08x, offset=0x%08x", file->filename.c_str(), file->crc32, file->offset);
@@ -495,7 +575,8 @@ void GameLoader::IdentifyGamesInZipArchive(
std::map<std::string, std::set<File::ptr_t>> files_found_by_game;
// Determine which files each game requires and which files are present in
// the zip archive
// the zip archive. Files belonging to optional regions cannot be used to
// identify games.
for (auto &v1: regions_by_game)
{
const std::string &game_name = v1.first;
@@ -503,7 +584,9 @@ void GameLoader::IdentifyGamesInZipArchive(
for (auto &v2: regions_by_name)
{
Region::ptr_t region = v2.second;
for (auto file: region->files)
if (!region->required)
continue;
for (const auto &file: region->files)
{
// Add each file to the set of required files per game
files_required_by_game[game_name].insert(file);
@@ -513,7 +596,7 @@ void GameLoader::IdentifyGamesInZipArchive(
}
}
}
/*
* Corner case: some child ROM sets legitimately share files, which can fool
* us into thinking two games are partially present. Need to remove the one
@@ -536,7 +619,7 @@ void GameLoader::IdentifyGamesInZipArchive(
/*
* If the these two games have a different number of files in the zip
* archive, but one consists only of the overlapping files, we can safely
* conclude that these files represent only the game with the larger
* conclude that these files represent only the game with the larger
* number of files present. Otherwise, if only the overlapping files are
* present for both, we have a genuine ambiguity and hence do nothing.
*/
@@ -548,7 +631,7 @@ void GameLoader::IdentifyGamesInZipArchive(
{
files_found_by_game.erase(game_name);
}
// Find the missing files for each game we found in the zip archive, then use
// this to determine whether the complete game exists
auto compare = [](const File::ptr_t &a, const File::ptr_t &b) { return a->filename < b->filename; };
@@ -587,12 +670,12 @@ void GameLoader::ChooseGameInZipArchive(std::string *chosen_game, bool *missing_
std::set<std::string> complete_games;
std::map<std::string, std::set<File::ptr_t>> files_missing_by_game;
IdentifyGamesInZipArchive(&complete_games, &files_missing_by_game, zip, m_regions_by_game);
// Find complete, merged games
std::set<std::string> complete_merged_games;
std::map<std::string, std::set<File::ptr_t>> files_missing_by_merged_game;
IdentifyGamesInZipArchive(&complete_merged_games, &files_missing_by_merged_game, zip, m_regions_by_merged_game);
/*
* Find incomplete child games by sorting child games out from the unmerged
* games results and pruning out complete merged games. Don't care about
@@ -618,7 +701,7 @@ void GameLoader::ChooseGameInZipArchive(std::string *chosen_game, bool *missing_
{
incomplete_child_games.erase(game_name);
}
// Complete merged games take highest precedence
for (auto &game_name: complete_merged_games)
{
@@ -630,17 +713,17 @@ void GameLoader::ChooseGameInZipArchive(std::string *chosen_game, bool *missing_
// complete merged games from missing file list.
files_missing_by_game.erase(parent);
}
// Any remaining incomplete games from the unmerged set are legitimate errors
for (auto &v: files_missing_by_game)
{
for (auto &file: v.second)
{
ErrorLog("'%s' (CRC32 0x%08x) not found in '%s' for game '%s'.", file->filename.c_str(), file->crc32, zipfilename.c_str(), v.first.c_str());
ErrorLog("'%s' (CRC32 0x%08x) not found in '%s' for game '%s'.", file->filename.c_str(), file->crc32, zipfilename.c_str(), v.first.c_str());
}
ErrorLog("Ignoring game '%s' in '%s' because it is missing files.", v.first.c_str(), zipfilename.c_str());
}
// Choose game: complete merged game > incomplete child game > complete
// unmerged game
if (!complete_merged_games.empty())
@@ -658,7 +741,7 @@ void GameLoader::ChooseGameInZipArchive(std::string *chosen_game, bool *missing_
ErrorLog("No complete Model 3 games found in '%s'.", zipfilename.c_str());
return;
}
// Print out which game we chose from valid candidates in the zip file
std::set<std::string> candidates(complete_games);
candidates.insert(complete_merged_games.begin(), complete_merged_games.end());
@@ -673,7 +756,7 @@ bool GameLoader::ComputeRegionSize(uint32_t *region_size, const GameLoader::Regi
// use maximum end_addr = offset + stride * (num_chunks - 1) + chunk_size.
std::vector<uint32_t> end_addr;
bool error = false;
for (auto file: region->files)
for (const auto &file: region->files)
{
const ZippedFile *zipped_file = LookupFile(file, zip);
if (zipped_file)
@@ -683,7 +766,7 @@ bool GameLoader::ComputeRegionSize(uint32_t *region_size, const GameLoader::Regi
ErrorLog("File '%s' in '%s' is not sized in %d-byte chunks.", zipped_file->filename.c_str(), zipped_file->zipfilename.c_str(), region->chunk_size);
error = true;
}
uint32_t num_chunks = (uint32_t)(zipped_file->uncompressed_size / region->chunk_size);
uint32_t num_chunks = (uint32_t)(zipped_file->uncompressed_size / region->chunk_size);
end_addr.push_back(file->offset + region->stride * (num_chunks - 1) + region->chunk_size);
}
else
@@ -694,19 +777,78 @@ bool GameLoader::ComputeRegionSize(uint32_t *region_size, const GameLoader::Regi
return error;
}
// We need to preserve the absolute offsets in order for byte swapping to work
// properly when chunk size is 1
static inline void CopyBytes(uint8_t *dest_base, uint32_t dest_offset, const uint8_t *src_base, uint32_t src_offset, uint32_t size, uint32_t byte_swap)
static bool ApplyLayout(ROM *rom, const std::string &byte_layout, size_t stride, const std::string &region_name)
{
for (uint32_t i = 0; i < size; i++)
// Empty layout means do nothing
if (byte_layout.empty())
return false;
// Validate that the layout string includes the same number of bytes as the region stride. The
// stride is block size that the ROM files all contribute to. We also verify that each byte is
// used once and only once.
if (byte_layout.size() != stride)
{
dest_base[(dest_offset + i) ^ byte_swap] = src_base[src_offset + i];
ErrorLog("Byte layout of '%s' region does not match the stride length (%d bytes but should be %d bytes).", region_name.c_str(), byte_layout.size(), stride);
return true;
}
if (stride > 8)
{
ErrorLog("Region '%s' has stride larger than 8 (%d), which is currently unsupported.", region_name.c_str(), stride);
return true;
}
std::vector<size_t> byte_offsets;
for (char c: byte_layout)
{
if (isdigit(c))
{
byte_offsets.push_back(c - '0');
}
else
{
ErrorLog("Byte layout of '%s' region contains non-numeric characters. Use single-digit byte indices only.", region_name.c_str());
return true;
}
}
// Check all byte indices 0..N-1 are present
std::vector<size_t> sorted(byte_offsets);
std::sort(sorted.begin(), sorted.end()); // ascending order
size_t expected_offset = 0;
for (size_t offset: sorted)
{
if (offset != expected_offset)
{
ErrorLog("Byte layout of '%s' region must specify all byte offsets exactly once.", region_name.c_str());
return true;
}
expected_offset += 1;
}
// Okay, all good. Now we can reshuffle the region memory according to layout.
uint8_t *buffer = new uint8_t[stride];
uint8_t *dest = rom->data.get();
for (size_t dest_offset = 0; (dest_offset + stride) <= rom->size; dest_offset += stride)
{
// Copy current region bytes to temporary buffer. The layout offsets refer to this original layout.
memcpy(buffer, dest + dest_offset, stride);
// Place the bytes back into the ROM region in the layout order specified.
for (size_t i = 0; i < stride; i++)
{
dest[dest_offset + i] = buffer[byte_offsets[i]];
}
}
delete [] buffer;
return false; // no error
}
bool GameLoader::LoadRegion(ROM *rom, const GameLoader::Region::ptr_t &region, const ZipArchive &zip) const
{
bool error = false;
for (auto &file: region->files)
{
std::shared_ptr<uint8_t> tmp;
@@ -719,26 +861,29 @@ bool GameLoader::LoadRegion(ROM *rom, const GameLoader::Region::ptr_t &region, c
if (region->chunk_size == region->stride)
{
memcpy(dest + file->offset, src, file_size);
if (region->byte_swap)
Util::FlipEndian16(dest + file->offset, file_size);
}
else
{
uint32_t num_chunks = (uint32_t)file_size / region->chunk_size;
uint32_t num_chunks = (uint32_t)(file_size / region->chunk_size);
uint32_t dest_offset = file->offset;
uint32_t src_offset = 0;
uint32_t chunk_size = (uint32_t)region->chunk_size; // cache these as pointer dereferencing cripples performance in a tight loop
uint32_t stride = (uint32_t)region->stride;
uint32_t byte_swap = region->byte_swap;
for (uint32_t i = 0; i < num_chunks; i++)
{
CopyBytes(dest, dest_offset, src, src_offset, chunk_size, byte_swap);
memcpy(dest + dest_offset, src + src_offset, chunk_size);
dest_offset += stride;
src_offset += chunk_size;
}
}
}
}
if (!error)
{
error = ApplyLayout(rom, region->byte_layout, region->stride, region->region_name);
}
return error;
}
@@ -759,18 +904,34 @@ bool GameLoader::LoadROMs(ROMSet *rom_set, const std::string &game_name, const Z
{
auto &region = v.second;
uint32_t region_size = 0;
bool error_loading_region = false;
// Attempt to load the region
if (ComputeRegionSize(&region_size, region, zip))
error |= true;
error_loading_region = true;
else
{
// Load up the ROM region
auto &rom = rom_set->rom_by_region[region->region_name];
rom.data.reset(new uint8_t[region_size], std::default_delete<uint8_t[]>());
rom.size = region_size;
error |= LoadRegion(&rom, region, zip);
error_loading_region = LoadRegion(&rom, region, zip);
}
if (error_loading_region && !region->required)
{
// Failed to load the region but it wasn't required anyway, so remove it
// and proceed
rom_set->rom_by_region.erase(region->region_name);
ErrorLog("Optional ROM region '%s' in '%s' could not be loaded.", region->region_name.c_str(), game_name.c_str());
}
else
{
// Proceed normally: accumulate errors
error |= error_loading_region;
}
}
// Attach the patches and do some more error checking here
auto &patches_by_region = m_patches_by_game.find(game_name)->second;
for (auto &v: patches_by_region)
@@ -797,10 +958,10 @@ std::string StripFilename(const std::string &filepath)
break;
}
}
// If none found, there is directory component here
if (last_slash == std::string::npos)
return "";
return std::string();
// Otherwise, strip everything after the slash
return std::string(filepath, 0, last_slash + 1);
@@ -809,12 +970,12 @@ std::string StripFilename(const std::string &filepath)
bool GameLoader::Load(Game *game, ROMSet *rom_set, const std::string &zipfilename) const
{
*game = Game();
// Read the zip contents
ZipArchive zip;
if (LoadZipArchive(&zip, zipfilename))
return true;
// Pick the game to load (there could be multiple ROM sets in a zip file)
std::string chosen_game;
bool missing_parent_roms = false;
@@ -824,7 +985,7 @@ bool GameLoader::Load(Game *game, ROMSet *rom_set, const std::string &zipfilenam
// Return game information to caller
*game = m_game_info_by_game.find(chosen_game)->second;
// Bring in additional parent ROM set if needed
if (missing_parent_roms)
{
@@ -836,7 +997,7 @@ bool GameLoader::Load(Game *game, ROMSet *rom_set, const std::string &zipfilenam
}
}
// Load
// Load
bool error = LoadROMs(rom_set, game->name, zip);
if (error)
*game = Game();
+6 -7
View File
@@ -31,13 +31,14 @@ private:
std::string region_name;
size_t stride;
size_t chunk_size;
bool byte_swap;
std::string byte_layout;
bool required;
std::vector<File::ptr_t> files;
static ptr_t Create(const GameLoader &loader, const Util::Config::Node &region_node);
bool AttribsMatch(const ptr_t &other) const;
bool FindFileIndexByOffset(size_t *idx, uint32_t offset) const;
};
// Game information from XML
std::map<std::string, Game> m_game_info_by_game;
@@ -45,18 +46,18 @@ private:
// sets do not inherit parent patches, which may complicate merging.
typedef std::map<std::string, std::vector<ROM::BigEndianPatch>> PatchesByRegion_t;
std::map<std::string, PatchesByRegion_t> m_patches_by_game;
// Parsed XML
typedef std::map<std::string, Region::ptr_t> RegionsByName_t;
std::map<std::string, RegionsByName_t> m_regions_by_game; // all games as defined in XML
std::map<std::string, RegionsByName_t> m_regions_by_merged_game; // only child sets merged w/ parents
std::string m_xml_filename;
// Single compressed file inside of a zip archive
struct ZippedFile
{
unzFile zf = nullptr;
std::string zipfilename; // zip archive
std::string zipfilename; // zip archive
std::string filename; // file inside the zip archive
size_t uncompressed_size = 0;
uint32_t crc32 = 0;
@@ -98,8 +99,6 @@ private:
void ChooseGameInZipArchive(std::string *chosen_game, bool *missing_parent_roms, const ZipArchive &zip, const std::string &zipfilename) const;
bool LoadRegion(ROM *buffer, const GameLoader::Region::ptr_t &region, const ZipArchive &zip) const;
bool LoadROMs(ROMSet *rom_set, const std::string &game_name, const ZipArchive &zip) const;
std::string ChooseGame(const std::set<std::string> &games_found, const std::string &zipfilename) const;
static bool CompareFilesByName(const File::ptr_t &a,const File::ptr_t &b);
public:
GameLoader(const std::string &xml_file);
+73
View File
@@ -0,0 +1,73 @@
#include "FBO.h"
FBO::FBO() :
m_frameBufferID(0),
m_textureID(0)
{
}
bool FBO::Create(int width, int height)
{
CreateTexture(width, height);
glGenFramebuffers(1, &m_frameBufferID);
glBindFramebuffer(GL_FRAMEBUFFER, m_frameBufferID);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_textureID, 0);
auto frameBufferStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
glBindFramebuffer(GL_FRAMEBUFFER, 0); //created FBO now disable it
return frameBufferStatus == GL_FRAMEBUFFER_COMPLETE;
}
void FBO::Destroy()
{
if (m_frameBufferID) {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glDeleteFramebuffers(1, &m_frameBufferID);
}
if (m_textureID) {
glDeleteTextures(1, &m_textureID);
}
m_frameBufferID = 0;
m_textureID = 0;
}
void FBO::BindTexture()
{
glBindTexture(GL_TEXTURE_2D, m_textureID);
}
void FBO::Set()
{
glBindFramebuffer(GL_FRAMEBUFFER, m_frameBufferID);
}
void FBO::Disable()
{
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
GLuint FBO::GetFBOID()
{
return m_frameBufferID;
}
GLuint FBO::GetTextureID()
{
return m_textureID;
}
void FBO::CreateTexture(int width, int height)
{
glGenTextures (1, &m_textureID);
glBindTexture (GL_TEXTURE_2D, m_textureID);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
}
+28
View File
@@ -0,0 +1,28 @@
#ifndef _FBO_H_
#define _FBO_H_
#include <GL/glew.h>
class FBO
{
public:
FBO();
bool Create(int width, int height);
void Destroy();
void BindTexture();
void Set();
void Disable();
GLuint GetFBOID();
GLuint GetTextureID();
private:
void CreateTexture(int width, int height);
GLuint m_frameBufferID;
GLuint m_textureID;
};
#endif
+10 -8
View File
@@ -2,6 +2,7 @@
#define INCLUDED_IRENDER3D_H
#include <cstdint>
#include "Types.h"
/*
* IRender3D:
@@ -16,14 +17,15 @@ public:
virtual void EndFrame(void) = 0;
virtual void UploadTextures(unsigned level, unsigned x, unsigned y, unsigned width, unsigned height) = 0;
virtual void AttachMemory(const uint32_t *cullingRAMLoPtr, const uint32_t *cullingRAMHiPtr, const uint32_t *polyRAMPtr, const uint32_t *vromPtr, const uint16_t *textureRAMPtr) = 0;
virtual void SetStepping(int stepping) = 0;
virtual bool Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes, unsigned totalXRes, unsigned totalYRes) = 0;
virtual void SetSunClamp(bool enable) = 0;
virtual void SetSignedShade(bool enable) = 0;
virtual ~IRender3D()
{
}
virtual void SetStepping(int stepping) = 0;
virtual Result Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes, unsigned totalXRes, unsigned totalYRes, unsigned aaTarget) = 0;
virtual void SetSunClamp(bool enable) = 0;
virtual void SetBlockCulling(bool enable) = 0;
virtual float GetLosValue(int layer) = 0;
virtual ~IRender3D()
{
}
};
#endif // INCLUDED_IRENDER3D_H
+5 -4
View File
@@ -32,6 +32,7 @@
*/
#include "Supermodel.h"
#include "Legacy3D.h"
namespace Legacy3D {
@@ -41,19 +42,19 @@ namespace Legacy3D {
// Overflow in the local vertex buffer, which holds one model
bool CLegacy3D::ErrorLocalVertexOverflow(void)
Result CLegacy3D::ErrorLocalVertexOverflow(void)
{
if ((errorMsgFlags&ERROR_LOCAL_VERTEX_OVERFLOW))
return FAIL;
return Result::FAIL;
errorMsgFlags |= ERROR_LOCAL_VERTEX_OVERFLOW;
return ErrorLog("Overflow in local vertex buffer!");
}
// Model could not be cached, even after dumping display list and re-caching
bool CLegacy3D::ErrorUnableToCacheModel(UINT32 modelAddr)
Result CLegacy3D::ErrorUnableToCacheModel(UINT32 modelAddr)
{
if ((errorMsgFlags&ERROR_UNABLE_TO_CACHE_MODEL))
return FAIL;
return Result::FAIL;
errorMsgFlags |= ERROR_UNABLE_TO_CACHE_MODEL;
return ErrorLog("Encountered a model that was too large to cache (at %08X)!", modelAddr);
}
+297 -262
View File
@@ -156,24 +156,23 @@
* - Can some of the floating point flag attribs be replaced with ints?
*/
#include "Legacy3D.h"
#include "Supermodel.h"
#include "Graphics/Legacy3D/Shaders3D.h" // fragment and vertex shaders
#include "Shaders3D.h" // fragment and vertex shaders
#include "Graphics/Shader.h"
#include "Util/BitCast.h"
#include <algorithm>
#include <cmath>
#include <cstdint>
namespace Legacy3D {
// Microsoft doesn't provide isnan() and isinf()
#ifdef _MSC_VER
#include <float.h>
#define ISNAN(x) (_isnan(x))
#define ISINF(x) (!_finite(x))
#else
#define ISNAN(x) (std::isnan(x))
#define ISINF(x) (std::isinf(x))
#ifndef M_PI
#define M_PI 3.1415926535897932384626433832795
#endif
namespace Legacy3D {
/******************************************************************************
Definitions and Constants
******************************************************************************/
@@ -442,7 +441,7 @@ const UINT32 *CLegacy3D::TranslateModelAddress(UINT32 modelAddr)
******************************************************************************/
// Macro to generate column-major (OpenGL) index from y,x subscripts
#define CMINDEX(y,x) (x*4+y)
#define CMINDEX(y,x) ((x)*4+(y))
/*
* MultMatrix():
@@ -528,7 +527,7 @@ void CLegacy3D::InitMatrixStack(UINT32 matrixBaseAddr)
}
// Set matrix base address and apply matrix #0 (coordinate system matrix)
matrixBasePtr = (float *) TranslateCullingAddress(matrixBaseAddr);
matrixBasePtr = (const float *) TranslateCullingAddress(matrixBaseAddr);
MultMatrix(0);
}
@@ -548,7 +547,7 @@ static bool IsDynamicModel(const UINT32 *data)
{
if (data == NULL)
return false;
unsigned sharedVerts[16] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 };
static constexpr unsigned sharedVerts[16] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 };
// VROM models are only dynamic if they reference polygon RAM via color palette indices
bool done = false;
do
@@ -562,7 +561,7 @@ static bool IsDynamicModel(const UINT32 *data)
unsigned numVerts = (data[0]&0x40 ? 4 : 3);
// Deduct number of reused verts
numVerts -= sharedVerts[data[0]&0xf];
done = (data[1] & 4) > 0;
done = (data[1] & 4) > 0;
// Skip header and vertices to next polygon
data += 7 + numVerts * 4;
}
@@ -580,12 +579,12 @@ static bool IsDynamicModel(const UINT32 *data)
*
* Models are cached for each unique culling node texture offset state.
*/
bool CLegacy3D::DrawModel(UINT32 modelAddr)
Result CLegacy3D::DrawModel(UINT32 modelAddr)
{
//if (modelAddr==0x7FFF00) // Fighting Vipers (this is not polygon data!)
// return;
if (modelAddr == 0x200000) // Virtual On 2 (during boot-up, causes slow-down)
return OKAY;
return Result::OKAY;
const UINT32 *model = TranslateModelAddress(modelAddr);
// Determine whether model is in polygon RAM or VROM
@@ -679,9 +678,9 @@ void CLegacy3D::DescendCullingNode(UINT32 addr)
const UINT32 node1Ptr = node[0x07-offset];
const UINT32 node2Ptr = node[0x08-offset];
const UINT32 matrixOffset = node[0x03-offset]&0xFFF;
const float x = *(float *) &node[0x04-offset];
const float y = *(float *) &node[0x05-offset];
const float z = *(float *) &node[0x06-offset];
const float x = Util::Uint32AsFloat(node[0x04-offset]);
const float y = Util::Uint32AsFloat(node[0x05-offset]);
const float z = Util::Uint32AsFloat(node[0x06-offset]);
// Texture offset?
TextureOffset oldTextureOffset = m_textureOffset; // save old offsets
@@ -803,7 +802,7 @@ void CLegacy3D::DescendNodePtr(UINT32 nodeAddr)
// Draws viewports of the given priority
void CLegacy3D::RenderViewport(UINT32 addr, int pri, bool wideScreen)
{
static const GLfloat color[8][3] = {
static constexpr GLfloat color[8][3] = {
{ 0.0, 0.0, 0.0 }, // off
{ 0.0, 0.0, 1.0 }, // blue
{ 0.0, 1.0, 0.0 }, // green
@@ -816,7 +815,7 @@ void CLegacy3D::RenderViewport(UINT32 addr, int pri, bool wideScreen)
// Translate address and obtain pointer
const UINT32 *vpnode = TranslateCullingAddress(addr);
if (NULL == vpnode)
if (nullptr == vpnode)
return;
// Recursively process next viewport
@@ -843,9 +842,9 @@ void CLegacy3D::RenderViewport(UINT32 addr, int pri, bool wideScreen)
int vpHeight = (vpnode[0x14]>>18)&0x3FFF; // height (14.2)
// Field of view and clipping
GLfloat vpTopAngle = (float) asin(*(float *)&vpnode[0x0E]); // FOV Y upper half-angle (radians)
GLfloat vpBotAngle = (float) asin(*(float *)&vpnode[0x12]); // FOV Y lower half-angle
GLfloat fovYDegrees = (vpTopAngle+vpBotAngle)*(float)(180.0/3.14159265358979323846);
GLfloat vpTopAngle = asinf(Util::Uint32AsFloat(vpnode[0x0E])); // FOV Y upper half-angle (radians)
GLfloat vpBotAngle = asinf(Util::Uint32AsFloat(vpnode[0x12])); // FOV Y lower half-angle
GLfloat fovYDegrees = (vpTopAngle+vpBotAngle)*(float)(180.0/M_PI);
// TO-DO: investigate clipping planes
// Set up viewport and projection (TO-DO: near and far clipping)
@@ -867,15 +866,15 @@ void CLegacy3D::RenderViewport(UINT32 addr, int pri, bool wideScreen)
viewportY = yOffs + (GLint) ((float)(384-(vpY+vpHeight))*yRatio);
viewportWidth = (GLint) ((float)vpWidth*xRatio);
viewportHeight = (GLint) ((float)vpHeight*yRatio);
gluPerspective(fovYDegrees,(GLfloat)vpWidth/(GLfloat)vpHeight,0.1f,1e5); // use Model 3 viewport ratio
gluPerspective(fovYDegrees,(GLdouble)vpWidth/(GLdouble)vpHeight,0.1,1e5); // use Model 3 viewport ratio
}
// Lighting (note that sun vector points toward sun -- away from vertex)
lightingParams[0] = *(float *) &vpnode[0x05]; // sun X
lightingParams[1] = *(float *) &vpnode[0x06]; // sun Y
lightingParams[2] = *(float *) &vpnode[0x04]; // sun Z
lightingParams[3] = *(float *) &vpnode[0x07]; // sun intensity
lightingParams[4] = (float) ((vpnode[0x24]>>8)&0xFF) * (1.0f/255.0f); // ambient intensity
lightingParams[0] = Util::Uint32AsFloat(vpnode[0x05]); // sun X
lightingParams[1] = Util::Uint32AsFloat(vpnode[0x06]); // sun Y
lightingParams[2] = Util::Uint32AsFloat(vpnode[0x04]); // sun Z
lightingParams[3] = Util::Uint32AsFloat(vpnode[0x07]); // sun intensity
lightingParams[4] = (float) ((vpnode[0x24]>>8)&0xFF) * (float)(1.0/255.0); // ambient intensity
lightingParams[5] = 0.0; // reserved
// Spotlight
@@ -884,8 +883,8 @@ void CLegacy3D::RenderViewport(UINT32 addr, int pri, bool wideScreen)
spotEllipse[1] = (float) ((vpnode[0x1D]>>3)&0x1FFF); // spotlight Y
spotEllipse[2] = (float) ((vpnode[0x1E]>>16)&0xFFFF); // spotlight X size (16-bit? May have fractional component below bit 16)
spotEllipse[3] = (float) ((vpnode[0x1D]>>16)&0xFFFF); // spotlight Y size
spotRange[0] = 1.0f/(*(float *) &vpnode[0x21]); // spotlight start
spotRange[1] = *(float *) &vpnode[0x1F]; // spotlight extent
spotRange[0] = 1.0f/Util::Uint32AsFloat(vpnode[0x21]); // spotlight start
spotRange[1] = Util::Uint32AsFloat(vpnode[0x1F]); // spotlight extent
spotColor[0] = color[spotColorIdx][0]; // spotlight color
spotColor[1] = color[spotColorIdx][1];
spotColor[2] = color[spotColorIdx][2];
@@ -904,56 +903,56 @@ void CLegacy3D::RenderViewport(UINT32 addr, int pri, bool wideScreen)
spotEllipse[3] *= yRatio;
// Fog
fogParams[0] = (float) ((vpnode[0x22]>>16)&0xFF) * (1.0f/255.0f); // fog color R
fogParams[1] = (float) ((vpnode[0x22]>>8)&0xFF) * (1.0f/255.0f); // fog color G
fogParams[2] = (float) ((vpnode[0x22]>>0)&0xFF) * (1.0f/255.0f); // fog color B
fogParams[3] = *(float *) &vpnode[0x23]; // fog density
fogParams[4] = (float) (INT16) (vpnode[0x25]&0xFFFF)*(1.0f/255.0f); // fog start
if (ISINF(fogParams[3]) || ISNAN(fogParams[3]) || ISINF(fogParams[4]) || ISNAN(fogParams[4])) // Star Wars Trilogy
fogParams[0] = (float) ((vpnode[0x22]>>16)&0xFF) * (float)(1.0/255.0); // fog color R
fogParams[1] = (float) ((vpnode[0x22]>>8)&0xFF) * (float)(1.0/255.0); // fog color G
fogParams[2] = (float) ((vpnode[0x22]>>0)&0xFF) * (float)(1.0/255.0); // fog color B
fogParams[3] = Util::Uint32AsFloat(vpnode[0x23]); // fog density
fogParams[4] = (float) (INT16) (vpnode[0x25]&0xFFFF) * (float)(1.0/255.0); // fog start
if (std::isinf(fogParams[3]) || std::isnan(fogParams[3]) || std::isinf(fogParams[4]) || std::isnan(fogParams[4])) // Star Wars Trilogy
fogParams[3] = fogParams[4] = 0.0f;
// Unknown light/fog parameters
//GLfloat scrollFog = (float) (vpnode[0x20]&0xFF) * (1.0f/255.0f); // scroll fog
//GLfloat scrollAtt = (float) (vpnode[0x24]&0xFF) * (1.0f/255.0f); // scroll attenuation
//GLfloat scrollFog = (float) (vpnode[0x20]&0xFF) * (float)(1.0/255.0); // scroll fog
//GLfloat scrollAtt = (float) (vpnode[0x24]&0xFF) * (float)(1.0/255.0); // scroll attenuation
//printf("scrollFog = %g, scrollAtt = %g\n", scrollFog, scrollAtt);
//printf("Fog: R=%02X G=%02X B=%02X density=%g (%X) %d start=%g\n", ((vpnode[0x22]>>16)&0xFF), ((vpnode[0x22]>>8)&0xFF), ((vpnode[0x22]>>0)&0xFF), fogParams[3], vpnode[0x23], (fogParams[3]==fogParams[3]), fogParams[4]);
// Clear texture offsets before proceeding
m_textureOffset = TextureOffset();
// Set up coordinate system and base matrix
UINT32 matrixBase = vpnode[0x16] & 0xFFFFFF;
glMatrixMode(GL_MODELVIEW);
InitMatrixStack(matrixBase);
// Safeguard: weird coordinate system matrices usually indicate scenes that will choke the renderer
if (NULL != matrixBasePtr)
if (nullptr != matrixBasePtr)
{
float m21, m32, m13;
// Get the three elements that are usually set and see if their magnitudes are 1
m21 = matrixBasePtr[6];
m32 = matrixBasePtr[10];
m13 = matrixBasePtr[5];
m21 *= m21;
m32 *= m32;
m13 *= m13;
if ((m21>1.05) || (m21<0.95))
if ((m21>1.05f) || (m21<0.95f))
return;
if ((m32>1.05) || (m32<0.95))
if ((m32>1.05f) || (m32<0.95f))
return;
if ((m13>1.05) || (m13<0.95))
if ((m13>1.05f) || (m13<0.95f))
return;
}
// Render
AppendDisplayList(&VROMCache, true, 0); // add a viewport display list node
AppendDisplayList(&PolyCache, true, 0);
stackDepth = 0;
listDepth = 0;
// Descend down the node link: Use recursive traversal
DescendNodePtr(nodeAddr);
}
@@ -964,6 +963,23 @@ void CLegacy3D::RenderFrame(void)
// Begin frame
ClearErrors(); // must be cleared each frame
if (m_aaTarget) {
glBindFramebuffer(GL_FRAMEBUFFER, m_aaTarget); // if we have an AA target draw to it instead of the default back buffer
}
if (blockCulling && !m_config["NoWhiteFlash"].ValueAs<bool>()) // block culling disables 3D rendering
{
// clear screen to white
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
if (m_aaTarget) {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
return;
}
// Z buffering (Z buffer is cleared by display list viewport nodes)
glDepthFunc(GL_LESS);
@@ -1040,6 +1056,10 @@ void CLegacy3D::RenderFrame(void)
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
if (m_aaTarget) {
glBindFramebuffer(GL_FRAMEBUFFER, 0); // restore target if needed
}
}
void CLegacy3D::EndFrame(void)
@@ -1089,225 +1109,239 @@ void CLegacy3D::SetStepping(int stepping)
DebugLog("Legacy3D set to Step %d.%d\n", (step>>4)&0xF, step&0xF);
}
bool CLegacy3D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes, unsigned totalXResParam, unsigned totalYResParam)
Result CLegacy3D::SetupGLObjects()
{
// Allocate memory for texture buffer
textureBuffer = new(std::nothrow) GLfloat[1024*1024*4];
if (NULL == textureBuffer)
return ErrorLog("Insufficient memory for texture decode buffer.");
glGetError(); // clear error flag
// Create model caches and VBOs
if (CreateModelCache(&VROMCache, NUM_STATIC_VERTS, NUM_LOCAL_VERTS, NUM_STATIC_MODELS, 0x4000000/4, NUM_DISPLAY_LIST_ITEMS, false))
return FAIL;
if (CreateModelCache(&PolyCache, NUM_DYNAMIC_VERTS, NUM_LOCAL_VERTS, NUM_DYNAMIC_MODELS, 0x4000000/4, NUM_DISPLAY_LIST_ITEMS, true))
return FAIL;
// Allocate memory for texture buffer
textureBuffer = new(std::nothrow) GLfloat[1024 * 1024 * 4];
if (NULL == textureBuffer)
return ErrorLog("Insufficient memory for texture decode buffer.");
// Initialize lighting parameters (updated as viewports are traversed)
lightingParams[0] = 0.0;
lightingParams[1] = 0.0;
lightingParams[2] = 0.0;
lightingParams[3] = 0.0;
lightingParams[4] = 1.0; // full ambient intensity in case we want to render a standalone model
lightingParams[5] = 0.0;
glGetError(); // clear error flag
// Resolution and offset within physical display area
xRatio = (GLfloat) xRes / 496.0f;
yRatio = (GLfloat) yRes / 384.0f;
xOffs = xOffset;
yOffs = yOffset;
totalXRes = totalXResParam;
totalYRes = totalYResParam;
// Create model caches and VBOs
if (CreateModelCache(&VROMCache, NUM_STATIC_VERTS, NUM_LOCAL_VERTS, NUM_STATIC_MODELS, 0x4000000 / 4, NUM_DISPLAY_LIST_ITEMS, false) != Result::OKAY)
return Result::FAIL;
if (CreateModelCache(&PolyCache, NUM_DYNAMIC_VERTS, NUM_LOCAL_VERTS, NUM_DYNAMIC_MODELS, 0x4000000 / 4, NUM_DISPLAY_LIST_ITEMS, true) != Result::OKAY)
return Result::FAIL;
// Get ideal number of texture sheets required by default mapping from Model3 texture format to texture sheet
int idealTexSheets = 0;
for (size_t fmt = 0; fmt < 8; fmt++)
{
int sheetNum = defaultFmtToTexSheetNum[fmt];
idealTexSheets = std::max<int>(idealTexSheets, sheetNum + 1);
}
// Initialize lighting parameters (updated as viewports are traversed)
lightingParams[0] = 0.0;
lightingParams[1] = 0.0;
lightingParams[2] = 0.0;
lightingParams[3] = 0.0;
lightingParams[4] = 1.0; // full ambient intensity in case we want to render a standalone model
lightingParams[5] = 0.0;
// Get upper limit for number of texture maps to use from max number of texture units supported by video card
GLint glMaxTexUnits;
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &glMaxTexUnits);
int maxTexMaps = std::max<int>(1, std::min<int>(m_config["MaxTexMaps"].ValueAsDefault<int>(9), glMaxTexUnits));
// Get upper limit for extent of texture maps to use from max texture size supported by video card
GLint maxTexSize;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexSize);
int mapExtent = std::max<int>(1, std::min<unsigned>(m_config["MaxTexMapExtent"].ValueAsDefault<int>(4), maxTexSize / 2048));
int mapSize = 2048 * mapExtent;
while (mapExtent > 1)
{
if ((mapExtent - 1) * (mapExtent - 1) < idealTexSheets)
// Get ideal number of texture sheets required by default mapping from Model3 texture format to texture sheet
int idealTexSheets = 0;
for (size_t fmt = 0; fmt < 8; fmt++)
{
// Use a GL proxy texture to double check max texture size returned above
glTexImage2D(GL_PROXY_TEXTURE_2D, 0, GL_RGBA8, mapSize, mapSize, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, NULL);
GLint glTexWidth;
glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &glTexWidth);
if (glTexWidth == mapSize)
break;
int sheetNum = defaultFmtToTexSheetNum[fmt];
idealTexSheets = std::max<int>(idealTexSheets, sheetNum + 1);
}
mapExtent--;
mapSize -= 2048;
}
// Load shaders, using multi-sheet shader if requested.
const char *fragmentShaderSource = (m_config["MultiTexture"].ValueAs<bool>() ? fragmentShaderMultiSheetSource : fragmentShaderSingleSheetSource); // single texture shader
if (OKAY != LoadShaderProgram(&shaderProgram,&vertexShader,&fragmentShader,m_config["VertexShader"].ValueAs<std::string>(),m_config["FragmentShader"].ValueAs<std::string>(),vertexShaderSource,fragmentShaderSource))
return FAIL;
// Try locating default "textureMap" uniform in shader program
glUseProgram(shaderProgram); // bind program
textureMapLoc = glGetUniformLocation(shaderProgram, "textureMap");
// If exists, bind to first texture unit
int mapCount = 0;
if (textureMapLoc != -1)
glUniform1i(textureMapLoc, mapCount++);
// Try locating "textureMap[0-7]" uniforms in shader program
for (int mapNum = 0; mapNum < 8 && mapCount < maxTexMaps; mapNum++)
{
char uniformName[12];
sprintf(uniformName, "textureMap%u", mapNum);
textureMapLocs[mapNum] = glGetUniformLocation(shaderProgram, uniformName);
// If exist, bind to remaining texture units
if (textureMapLocs[mapNum] != -1)
glUniform1i(textureMapLocs[mapNum], mapCount++);
}
// Check sucessully located at least one "textureMap" uniform in shader program
if (mapCount == 0)
return ErrorLog("Fragment shader must contain at least one 'textureMap' uniform.");
InfoLog("Located and bound %u 'textureMap' uniform(s) in fragment shader.", mapCount);
// Get upper limit for number of texture maps to use from max number of texture units supported by video card
GLint glMaxTexUnits;
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &glMaxTexUnits);
int maxTexMaps = std::max<int>(1, std::min<int>(m_config["MaxTexMaps"].ValueAsDefault<int>(9), glMaxTexUnits));
// Readjust map extent so as to utilise as many texture maps found in shader program as possible
while (mapExtent > 1 && mapCount * (mapExtent - 1) * (mapExtent - 1) >= idealTexSheets)
{
mapExtent--;
mapSize -= 2048;
}
// Create required number of GL textures for texture maps, decreasing map extent if memory is insufficent
unsigned sheetsPerMap = mapExtent * mapExtent;
while (true)
{
numTexMaps = std::min<unsigned>(mapCount, 1 + (idealTexSheets - 1) / sheetsPerMap);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glGenTextures(numTexMaps, texMapIDs);
bool okay = true;
for (unsigned mapNum = 0; mapNum < numTexMaps; mapNum++)
// Get upper limit for extent of texture maps to use from max texture size supported by video card
GLint maxTexSize;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexSize);
int mapExtent = std::max<int>(1, std::min<unsigned>(m_config["MaxTexMapExtent"].ValueAsDefault<int>(4), maxTexSize / 2048));
int mapSize = 2048 * mapExtent;
while (mapExtent > 1)
{
glActiveTexture(GL_TEXTURE0 + mapNum); // activate correct texture unit
glBindTexture(GL_TEXTURE_2D, texMapIDs[mapNum]); // bind correct texture sheet
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // fragment shader performs its own interpolation
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, mapSize, mapSize, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, 0);
if (glGetError() != GL_NO_ERROR)
{
// Ran out of video memory or texture size is too large
numTexMaps = mapNum;
okay = false;
break;
}
if ((mapExtent - 1) * (mapExtent - 1) < idealTexSheets)
{
// Use a GL proxy texture to double check max texture size returned above
glTexImage2D(GL_PROXY_TEXTURE_2D, 0, GL_RGBA8, mapSize, mapSize, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, NULL);
GLint glTexWidth;
glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &glTexWidth);
if (glTexWidth == mapSize)
break;
}
mapExtent--;
mapSize -= 2048;
}
if (okay || mapExtent == 1)
break;
// Delete textures, decrease extent and try again
glDeleteTextures(numTexMaps, texMapIDs);
mapExtent--;
mapSize -= 2048;
sheetsPerMap = mapExtent * mapExtent;
}
// Load shaders, using multi-sheet shader if requested.
const char* fragmentShaderSource = (m_config["MultiTexture"].ValueAs<bool>() ? fragmentShaderMultiSheetSource : fragmentShaderSingleSheetSource); // single texture shader
if (Result::OKAY != LoadShaderProgram(&shaderProgram, &vertexShader, &fragmentShader, m_config["VertexShader"].ValueAs<std::string>(), m_config["FragmentShader"].ValueAs<std::string>(), vertexShaderSource, fragmentShaderSource))
return Result::FAIL;
// Try locating default "textureMap" uniform in shader program
glUseProgram(shaderProgram); // bind program
textureMapLoc = glGetUniformLocation(shaderProgram, "textureMap");
// If exists, bind to first texture unit
int mapCount = 0;
if (textureMapLoc != -1)
glUniform1i(textureMapLoc, mapCount++);
// Try locating "textureMap[0-7]" uniforms in shader program
for (int mapNum = 0; mapNum < 8 && mapCount < maxTexMaps; mapNum++)
{
char uniformName[12];
sprintf(uniformName, "textureMap%u", mapNum);
textureMapLocs[mapNum] = glGetUniformLocation(shaderProgram, uniformName);
// If exist, bind to remaining texture units
if (textureMapLocs[mapNum] != -1)
glUniform1i(textureMapLocs[mapNum], mapCount++);
}
// Check sucessully located at least one "textureMap" uniform in shader program
if (mapCount == 0)
return ErrorLog("Fragment shader must contain at least one 'textureMap' uniform.");
InfoLog("Located and bound %u 'textureMap' uniform(s) in fragment shader.", mapCount);
// Readjust map extent so as to utilise as many texture maps found in shader program as possible
while (mapExtent > 1 && mapCount * (mapExtent - 1) * (mapExtent - 1) >= idealTexSheets)
{
mapExtent--;
mapSize -= 2048;
}
// Create required number of GL textures for texture maps, decreasing map extent if memory is insufficent
unsigned sheetsPerMap = mapExtent * mapExtent;
while (true)
{
numTexMaps = std::min<unsigned>(mapCount, 1 + (idealTexSheets - 1) / sheetsPerMap);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glGenTextures(numTexMaps, texMapIDs);
bool okay = true;
for (unsigned mapNum = 0; mapNum < numTexMaps; mapNum++)
{
glActiveTexture(GL_TEXTURE0 + mapNum); // activate correct texture unit
glBindTexture(GL_TEXTURE_2D, texMapIDs[mapNum]); // bind correct texture sheet
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // fragment shader performs its own interpolation
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, mapSize, mapSize, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, 0);
if (glGetError() != GL_NO_ERROR)
{
// Ran out of video memory or texture size is too large
numTexMaps = mapNum;
okay = false;
break;
}
}
if (okay || mapExtent == 1)
break;
// Delete textures, decrease extent and try again
glDeleteTextures(numTexMaps, texMapIDs);
mapExtent--;
mapSize -= 2048;
sheetsPerMap = mapExtent * mapExtent;
}
// Check successfully created at least one texture map
if (numTexMaps == 0)
return ErrorLog("OpenGL was unable to provide any 2048x2048-texel texture maps.");
InfoLog("Created %u %ux%u-texel GL texture map(s).", numTexMaps, mapSize, mapSize);
// Create texture sheet objects and assign them to texture maps
numTexSheets = std::min<unsigned>(numTexMaps * sheetsPerMap, idealTexSheets);
texSheets = new(std::nothrow) TexSheet[numTexSheets];
if (texSheets == NULL)
return ErrorLog("Unable to assign memory for %u texture sheet objects.", numTexSheets);
for (unsigned sheetNum = 0; sheetNum < numTexSheets; sheetNum++)
{
unsigned mapNum = sheetNum / sheetsPerMap;
unsigned posInMap = sheetNum % sheetsPerMap;
texSheets[sheetNum].sheetNum = sheetNum;
texSheets[sheetNum].mapNum = mapNum;
texSheets[sheetNum].xOffset = 2048 * (posInMap % mapExtent);
texSheets[sheetNum].yOffset = 2048 * (posInMap / mapExtent);
}
// Assign Model3 texture formats to texture sheets (cannot just use default mapping as may have ended up with fewer
// texture sheets than anticipated)
for (unsigned fmt = 0; fmt < 8; fmt++)
{
int sheetNum = defaultFmtToTexSheetNum[fmt] % numTexSheets;
fmtToTexSheet[fmt] = &texSheets[sheetNum];
}
InfoLog("Mapped %u Model3 texture formats to %u texture sheet(s) in %u %ux%u-texel texture map(s).", 8, numTexSheets, numTexMaps, mapSize, mapSize);
// Get location of the rest of the uniforms
modelViewMatrixLoc = glGetUniformLocation(shaderProgram, "modelViewMatrix");
projectionMatrixLoc = glGetUniformLocation(shaderProgram, "projectionMatrix");
lightingLoc = glGetUniformLocation(shaderProgram, "lighting");
mapSizeLoc = glGetUniformLocation(shaderProgram, "mapSize");
spotEllipseLoc = glGetUniformLocation(shaderProgram, "spotEllipse");
spotRangeLoc = glGetUniformLocation(shaderProgram, "spotRange");
spotColorLoc = glGetUniformLocation(shaderProgram, "spotColor");
// Get locations of custom vertex attributes
subTextureLoc = glGetAttribLocation(shaderProgram, "subTexture");
texParamsLoc = glGetAttribLocation(shaderProgram, "texParams");
texFormatLoc = glGetAttribLocation(shaderProgram, "texFormat");
texMapLoc = glGetAttribLocation(shaderProgram, "texMap");
transLevelLoc = glGetAttribLocation(shaderProgram, "transLevel");
lightEnableLoc = glGetAttribLocation(shaderProgram, "lightEnable");
specularLoc = glGetAttribLocation(shaderProgram, "specular");
shininessLoc = glGetAttribLocation(shaderProgram, "shininess");
fogIntensityLoc = glGetAttribLocation(shaderProgram, "fogIntensity");
// Set map size
if (mapSizeLoc != -1)
glUniform1f(mapSizeLoc, (GLfloat)mapSize);
// Additional OpenGL stuff
glFrontFace(GL_CW); // polygons are uploaded w/ clockwise winding
glCullFace(GL_BACK);
glEnable(GL_CULL_FACE);
glClearDepth(1.0);
glEnable(GL_TEXTURE_2D);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// Mark all textures as dirty
UploadTextures(0, 0, 0, 2048, 2048);
DebugLog("Legacy3D initialized\n");
return Result::OKAY;
}
// Check successfully created at least one texture map
if (numTexMaps == 0)
return ErrorLog("OpenGL was unable to provide any 2048x2048-texel texture maps.");
InfoLog("Created %u %ux%u-texel GL texture map(s).", numTexMaps, mapSize, mapSize);
Result CLegacy3D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes, unsigned totalXResParam, unsigned totalYResParam, unsigned aaTarget)
{
// Resolution and offset within physical display area
xRatio = (GLfloat)xRes / 496.0f;
yRatio = (GLfloat)yRes / 384.0f;
xOffs = xOffset;
yOffs = yOffset;
totalXRes = totalXResParam;
totalYRes = totalYResParam;
// Create texture sheet objects and assign them to texture maps
numTexSheets = std::min<unsigned>(numTexMaps * sheetsPerMap, idealTexSheets);
texSheets = new(std::nothrow) TexSheet[numTexSheets];
if (texSheets == NULL)
return ErrorLog("Unable to assign memory for %u texture sheet objects.", numTexSheets);
for (unsigned sheetNum = 0; sheetNum < numTexSheets; sheetNum++)
{
unsigned mapNum = sheetNum / sheetsPerMap;
unsigned posInMap = sheetNum % sheetsPerMap;
texSheets[sheetNum].sheetNum = sheetNum;
texSheets[sheetNum].mapNum = mapNum;
texSheets[sheetNum].xOffset = 2048 * (posInMap % mapExtent);
texSheets[sheetNum].yOffset = 2048 * (posInMap / mapExtent);
}
m_aaTarget = aaTarget;
// Assign Model3 texture formats to texture sheets (cannot just use default mapping as may have ended up with fewer
// texture sheets than anticipated)
for (unsigned fmt = 0; fmt < 8; fmt++)
{
int sheetNum = defaultFmtToTexSheetNum[fmt] % numTexSheets;
fmtToTexSheet[fmt] = &texSheets[sheetNum];
}
InfoLog("Mapped %u Model3 texture formats to %u texture sheet(s) in %u %ux%u-texel texture map(s).", 8, numTexSheets, numTexMaps, mapSize, mapSize);
// Get location of the rest of the uniforms
modelViewMatrixLoc = glGetUniformLocation(shaderProgram,"modelViewMatrix");
projectionMatrixLoc = glGetUniformLocation(shaderProgram,"projectionMatrix");
lightingLoc = glGetUniformLocation(shaderProgram, "lighting");
mapSizeLoc = glGetUniformLocation(shaderProgram, "mapSize");
spotEllipseLoc = glGetUniformLocation(shaderProgram, "spotEllipse");
spotRangeLoc = glGetUniformLocation(shaderProgram, "spotRange");
spotColorLoc = glGetUniformLocation(shaderProgram, "spotColor");
// Get locations of custom vertex attributes
subTextureLoc = glGetAttribLocation(shaderProgram,"subTexture");
texParamsLoc = glGetAttribLocation(shaderProgram,"texParams");
texFormatLoc = glGetAttribLocation(shaderProgram,"texFormat");
texMapLoc = glGetAttribLocation(shaderProgram,"texMap");
transLevelLoc = glGetAttribLocation(shaderProgram,"transLevel");
lightEnableLoc = glGetAttribLocation(shaderProgram,"lightEnable");
specularLoc = glGetAttribLocation(shaderProgram,"specular");
shininessLoc = glGetAttribLocation(shaderProgram,"shininess");
fogIntensityLoc = glGetAttribLocation(shaderProgram,"fogIntensity");
// Set map size
if (mapSizeLoc != -1)
glUniform1f(mapSizeLoc, (GLfloat)mapSize);
// Additional OpenGL stuff
glFrontFace(GL_CW); // polygons are uploaded w/ clockwise winding
glCullFace(GL_BACK);
glEnable(GL_CULL_FACE);
glClearDepth(1.0);
glEnable(GL_TEXTURE_2D);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// Mark all textures as dirty
UploadTextures(0, 0, 0, 2048, 2048);
DebugLog("Legacy3D initialized\n");
return OKAY;
return Result::OKAY;
}
void CLegacy3D::SetSunClamp(bool enable)
{
}
void CLegacy3D::SetSignedShade(bool enable)
{
}
CLegacy3D::CLegacy3D(const Util::Config::Node &config)
: m_config(config)
{
{
}
void CLegacy3D::SetBlockCulling(bool enable)
{
blockCulling = enable;
}
float CLegacy3D::GetLosValue(int layer)
{
return 0.0f;
}
CLegacy3D::CLegacy3D(const Util::Config::Node &config)
: m_config(config),
m_aaTarget(0)
{
cullingRAMLo = NULL;
cullingRAMHi = NULL;
polyRAM = NULL;
@@ -1332,6 +1366,8 @@ CLegacy3D::CLegacy3D(const Util::Config::Node &config)
VROMCache.ListTail[i] = NULL;
PolyCache.ListTail[i] = NULL;
}
SetupGLObjects();
DebugLog("Built Legacy3D\n");
}
@@ -1342,22 +1378,21 @@ CLegacy3D::~CLegacy3D(void)
if (glBindBuffer != NULL) // we may have failed earlier due to lack of OpenGL 2.0 functions
glBindBuffer(GL_ARRAY_BUFFER, 0); // disable VBOs by binding to 0
glDeleteTextures(numTexMaps, texMapIDs);
DestroyModelCache(&VROMCache);
DestroyModelCache(&PolyCache);
cullingRAMLo = NULL;
cullingRAMHi = NULL;
polyRAM = NULL;
vrom = NULL;
textureRAM = NULL;
if (texSheets != NULL)
delete [] texSheets;
if (textureBuffer != NULL)
delete [] textureBuffer;
textureBuffer = NULL;
delete [] texSheets;
texSheets = nullptr;
delete [] textureBuffer;
textureBuffer = nullptr;
DebugLog("Destroyed Legacy3D\n");
}
+47 -28
View File
@@ -29,9 +29,11 @@
#ifndef INCLUDED_LEGACY3D_H
#define INCLUDED_LEGACY3D_H
#include "TextureRefs.h"
#include "Graphics/IRender3D.h"
#include "Pkgs/glew.h"
#include <GL/glew.h>
#include "Util/NewConfig.h"
#include "Types.h"
namespace Legacy3D {
@@ -229,7 +231,7 @@ struct TexSheet
*
* 3D renderer. Lots of work to do here :)
*/
class CLegacy3D: public IRender3D
class CLegacy3D final : public IRender3D
{
friend class CTextureRefs;
@@ -326,7 +328,7 @@ public:
* occurred. Any allocated memory will not be freed until the
* destructor is called. Prints own error messages.
*/
bool Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes, unsigned totalXRes, unsigned totalYRes);
Result Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes, unsigned totalXRes, unsigned totalYRes, unsigned aaTarget);
/*
* SetSunClamp(bool enable);
@@ -334,23 +336,33 @@ public:
* Sets or unsets the clamped light model
*
* Parameters:
* enable Set clamp mode
*/
void SetSunClamp(bool enable);
/*
* SetSignedShade(bool enable);
*
* Sets the sign-ness of fixed shading value
*
* Parameters:
* enable Fixed shading is expressed as signed value
*/
void SetSignedShade(bool enable);
/*
* CLegacy3D(void):
* ~CLegacy3D(void):
* enable Set clamp mode
*/
void SetSunClamp(bool enable);
/*
* SetBlockCulling(bool enable);
*
* When enabled, clears screen to white and disables 3D rendering
*
* Parameters:
* enable Set block culling
*/
void SetBlockCulling(bool enable);
/*
* GetLosValue(int layer);
*
* Gets the line of sight value for the priority layer
*
* Parameters:
* layer Priority layer to read from
*/
float GetLosValue(int layer);
/*
* CLegacy3D(void):
* ~CLegacy3D(void):
*
* Parameters:
* config Configuration object.
@@ -371,18 +383,18 @@ private:
// Model caching and display list management
void DrawDisplayList(ModelCache *Cache, POLY_STATE state);
bool AppendDisplayList(ModelCache *Cache, bool isViewport, const struct VBORef *Model);
Result AppendDisplayList(ModelCache *Cache, bool isViewport, const struct VBORef *Model);
void ClearDisplayList(ModelCache *Cache);
int GetTextureBaseX(const Poly *P) const;
int GetTextureBaseY(const Poly *P) const;
bool InsertPolygon(ModelCache *cache, const Poly *p);
int GetTextureBaseX(const Poly *P) const;
int GetTextureBaseY(const Poly *P) const;
Result InsertPolygon(ModelCache *cache, const Poly *p);
void InsertVertex(ModelCache *cache, const Vertex *v, const Poly *p, float normFlip);
struct VBORef *BeginModel(ModelCache *cache);
void EndModel(ModelCache *cache, struct VBORef *Model, int lutIdx, UINT16 textureOffsetState, bool useStencil);
struct VBORef *CacheModel(ModelCache *cache, int lutIdx, UINT16 textureOffsetState, const UINT32 *data);
struct VBORef *LookUpModel(ModelCache *cache, int lutIdx, UINT16 textureOffsetState);
void ClearModelCache(ModelCache *cache);
bool CreateModelCache(ModelCache *cache, unsigned vboMaxVerts, unsigned localMaxVerts, unsigned maxNumModels, unsigned numLUTEntries, unsigned displayListSize, bool isDynamic);
Result CreateModelCache(ModelCache *cache, unsigned vboMaxVerts, unsigned localMaxVerts, unsigned maxNumModels, unsigned numLUTEntries, unsigned displayListSize, bool isDynamic);
void DestroyModelCache(ModelCache *cache);
// Texture management
@@ -393,16 +405,19 @@ private:
void InitMatrixStack(UINT32 matrixBaseAddr);
// Scene database traversal
bool DrawModel(UINT32 modelAddr);
Result DrawModel(UINT32 modelAddr);
void DescendCullingNode(UINT32 addr);
void DescendPointerList(UINT32 addr);
void DescendNodePtr(UINT32 nodeAddr);
void RenderViewport(UINT32 addr, int pri, bool wideScreen);
// In-frame error reporting
bool ErrorLocalVertexOverflow(void);
bool ErrorUnableToCacheModel(UINT32 modelAddr);
Result ErrorLocalVertexOverflow(void);
Result ErrorUnableToCacheModel(UINT32 modelAddr);
void ClearErrors(void);
// init
Result SetupGLObjects();
/*
* Data
@@ -445,6 +460,7 @@ private:
GLfloat spotColor[3];
GLint viewportX, viewportY;
GLint viewportWidth, viewportHeight;
GLuint m_aaTarget;
// Scene graph processing
int listDepth; // how many lists have we recursed into
@@ -517,6 +533,9 @@ private:
* before being uploaded. Dimensions are 512x512.
*/
GLfloat *textureBuffer; // RGBA8 format
// JTAG configuration settings
bool blockCulling;
};
} // Legacy3D
+22 -26
View File
@@ -106,6 +106,7 @@
#include <cmath>
#include <cstring>
#include "Supermodel.h"
#include "Legacy3D.h"
#ifdef DEBUG
extern int g_testPolyHeaderIdx;
@@ -159,7 +160,7 @@ namespace Legacy3D {
******************************************************************************/
// Macro to generate column-major (OpenGL) index from y,x subscripts
#define CMINDEX(y,x) (x*4+y)
#define CMINDEX(y,x) ((x)*4+(y))
static void CrossProd(GLfloat out[3], const GLfloat a[3], const GLfloat b[3])
{
@@ -189,12 +190,11 @@ static GLfloat Sign(GLfloat x)
// 4x4 matrix with the extra components undefined (do not use them!)
static void InvertTransposeMat3(GLfloat out[4*4], GLfloat m[4*4])
{
GLfloat invDet;
GLfloat a00 = m[CMINDEX(0,0)], a01 = m[CMINDEX(0,1)], a02 = m[CMINDEX(0,2)];
GLfloat a10 = m[CMINDEX(1,0)], a11 = m[CMINDEX(1,1)], a12 = m[CMINDEX(1,2)];
GLfloat a20 = m[CMINDEX(2,0)], a21 = m[CMINDEX(2,1)], a22 = m[CMINDEX(2,2)];
invDet = 1.0f/(a00*(a22*a11-a21*a12)-a10*(a22*a01-a21*a02)+a20*(a12*a01-a11*a02));
GLfloat invDet = 1.0f/(a00*(a22*a11-a21*a12)-a10*(a22*a01-a21*a02)+a20*(a12*a01-a11*a02));
out[CMINDEX(0,0)] = invDet*(a22*a11-a21*a12); out[CMINDEX(1,0)] = invDet*(-(a22*a01-a21*a02)); out[CMINDEX(2,0)] = invDet*(a12*a01-a11*a02);
out[CMINDEX(0,1)] = invDet*(-(a22*a10-a20*a12)); out[CMINDEX(1,1)] = invDet*(a22*a00-a20*a02); out[CMINDEX(2,1)] = invDet*(-(a12*a00-a10*a02));
out[CMINDEX(0,2)] = invDet*(a21*a10-a20*a11); out[CMINDEX(1,2)] = invDet*(-(a21*a00-a20*a01)); out[CMINDEX(2,2)] = invDet*(a11*a00-a10*a01);
@@ -314,10 +314,10 @@ void CLegacy3D::DrawDisplayList(ModelCache *Cache, POLY_STATE state)
}
// Appends an instance of a model or viewport to the display list, copying over the required state information
bool CLegacy3D::AppendDisplayList(ModelCache *Cache, bool isViewport, const struct VBORef *Model)
Result CLegacy3D::AppendDisplayList(ModelCache *Cache, bool isViewport, const struct VBORef *Model)
{
if ((Cache->listSize+2) > Cache->maxListSize) // a model may have 2 states (viewports are added to both display lists)
return FAIL;
return Result::FAIL;
//return ErrorLog("Display list is full.");
// Insert states into the display list
@@ -389,8 +389,8 @@ bool CLegacy3D::AppendDisplayList(ModelCache *Cache, bool isViewport, const stru
* This is described further in InsertPolygon(), where the vertices
* are ordered in clockwise fashion.
*/
static const GLfloat x[3] = { 1.0f, 0.0f, 0.0f };
static const GLfloat y[3] = { 0.0f, 1.0f, 0.0f };
static constexpr GLfloat x[3] = { 1.0f, 0.0f, 0.0f };
static constexpr GLfloat y[3] = { 0.0f, 1.0f, 0.0f };
const GLfloat z[3] = { 0.0f, 0.0f, -1.0f*matrixBasePtr[0x5] };
GLfloat m[4*4];
GLfloat xT[3], yT[3], zT[3], pT[3];
@@ -427,7 +427,7 @@ bool CLegacy3D::AppendDisplayList(ModelCache *Cache, bool isViewport, const stru
}
}
return OKAY;
return Result::OKAY;
}
// Clears the display list in preparation for a new frame
@@ -724,7 +724,7 @@ void CLegacy3D::InsertVertex(ModelCache *Cache, const Vertex *V, const Poly *P,
// Specular shininess
GLfloat specularCoefficient = (GLfloat) ((P->header[0]>>26) & 0x3F) * (1.0f/63.0f);
int shinyBits = (P->header[6] >> 5) & 3;
float shininess = pow(2.0f, 1+shinyBits);
float shininess = std::exp2f(1.0f + shinyBits);
if (!(P->header[0]&0x80)) //|| (shininess == 0)) // bit 0x80 seems to enable specular lighting
{
specularCoefficient = 0.; // disable
@@ -808,13 +808,13 @@ void CLegacy3D::InsertVertex(ModelCache *Cache, const Vertex *V, const Poly *P,
Cache->vboCurOffset += VBO_VERTEX_SIZE*sizeof(GLfloat);
}
bool CLegacy3D::InsertPolygon(ModelCache *Cache, const Poly *P)
Result CLegacy3D::InsertPolygon(ModelCache *Cache, const Poly *P)
{
// Bounds testing: up to 12 triangles will be inserted (worst case: double sided quad is 6 triangles)
if ((Cache->curVertIdx[P->state]+6*2) >= Cache->maxVertIdx)
return ErrorLocalVertexOverflow(); // local buffers are not expected to overflow
if ((Cache->vboCurOffset+6*2*VBO_VERTEX_SIZE*sizeof(GLfloat)) >= Cache->vboMaxOffset)
return FAIL; // this just indicates we may need to re-cache
return Result::FAIL; // this just indicates we may need to re-cache
// Is the polygon double sided?
bool doubleSided = (P->header[1]&0x10) ? true : false;
@@ -910,7 +910,7 @@ bool CLegacy3D::InsertPolygon(ModelCache *Cache, const Poly *P)
}
}
return OKAY;
return Result::OKAY;
}
// Begins caching a new model by resetting to the start of the local vertex buffer
@@ -1133,7 +1133,7 @@ struct VBORef *CLegacy3D::CacheModel(ModelCache *Cache, int lutIdx, UINT16 textu
Prev[i] = P.Vert[i];
// Copy this polygon into the model buffer
if (OKAY != InsertPolygon(Cache,&P))
if (Result::OKAY != InsertPolygon(Cache,&P))
return NULL;
++numPolys;
}
@@ -1184,7 +1184,7 @@ void CLegacy3D::ClearModelCache(ModelCache *Cache)
ClearDisplayList(Cache);
}
bool CLegacy3D::CreateModelCache(ModelCache *Cache, unsigned vboMaxVerts,
Result CLegacy3D::CreateModelCache(ModelCache *Cache, unsigned vboMaxVerts,
unsigned localMaxVerts, unsigned maxNumModels, unsigned numLUTEntries,
unsigned displayListSize, bool isDynamic)
{
@@ -1223,7 +1223,7 @@ bool CLegacy3D::CreateModelCache(ModelCache *Cache, unsigned vboMaxVerts,
{
// Last ditch attempt: try the local buffer size
vboBytes = localBytes;
glBufferData(GL_ARRAY_BUFFER, vboBytes, 0, isDynamic?GL_STREAM_DRAW:GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, vboBytes, nullptr, isDynamic?GL_STREAM_DRAW:GL_STATIC_DRAW);
if (glGetError() != GL_NO_ERROR)
return ErrorLog("OpenGL was unable to provide a %s vertex buffer.", isDynamic?"dynamic":"static");
}
@@ -1267,9 +1267,9 @@ bool CLegacy3D::CreateModelCache(ModelCache *Cache, unsigned vboMaxVerts,
// Clear LUT (MUST be done here because ClearModelCache() won't do it for dynamic models)
for (size_t i = 0; i < numLUTEntries; i++)
Cache->lut[i] = -1;
// All good!
return OKAY;
return Result::OKAY;
}
void CLegacy3D::DestroyModelCache(ModelCache *Cache)
@@ -1278,16 +1278,12 @@ void CLegacy3D::DestroyModelCache(ModelCache *Cache)
for (size_t i = 0; i < 2; i++)
{
if (Cache->verts[i] != NULL)
delete [] Cache->verts[i];
delete [] Cache->verts[i];
}
if (Cache->Models != NULL)
delete [] Cache->Models;
if (Cache->lut != NULL)
delete [] Cache->lut;
if (Cache->List != NULL)
delete [] Cache->List;
delete [] Cache->Models;
delete [] Cache->lut;
delete [] Cache->List;
memset(Cache, 0, sizeof(ModelCache));
}
+3 -3
View File
@@ -31,7 +31,7 @@
namespace Legacy3D {
// Vertex shader
static const char vertexShaderSource[] =
static constexpr char vertexShaderSource[] =
{
"/**\n"
" ** Supermodel\n"
@@ -246,7 +246,7 @@ static const char vertexShaderSource[] =
// Fragment shader (single texture sheet)
static const char fragmentShaderSingleSheetSource[] =
static constexpr char fragmentShaderSingleSheetSource[] =
{
"/**\n"
" ** Supermodel\n"
@@ -446,7 +446,7 @@ static const char fragmentShaderSingleSheetSource[] =
// Fragment shader (8 texture sheets)
static const char fragmentShaderMultiSheetSource[] =
static constexpr char fragmentShaderMultiSheetSource[] =
{
"/**\n"
" ** Supermodel\n"
+5 -3
View File
@@ -30,13 +30,16 @@
* to using a hashset to store the texture references, but this requires extra memory allocation.
*/
#include "TextureRefs.h"
#include "Supermodel.h"
#include "Legacy3D.h"
namespace Legacy3D {
CTextureRefs::CTextureRefs() : m_size(0), m_hashCapacity(0), m_hashEntries(NULL)
{
//
memset(m_array, 0, sizeof(m_array));
}
CTextureRefs::~CTextureRefs()
@@ -258,8 +261,7 @@ void CTextureRefs::DeleteAllHashEntries()
for (unsigned i = 0; i < m_hashCapacity; i++)
{
HashEntry *entry = m_hashEntries[i];
if (entry)
delete entry;
delete entry;
}
delete[] m_hashEntries;
}
+3 -1
View File
@@ -28,6 +28,8 @@
#ifndef INCLUDED_TEXTUREREFS_H
#define INCLUDED_TEXTUREREFS_H
namespace Legacy3D {
#define TEXREFS_ARRAY_SIZE 12
@@ -38,7 +40,7 @@ struct HashEntry
const unsigned texRef; // Texture reference as a bitfield
HashEntry *nextEntry; // Next entry with the same hash
HashEntry(unsigned theTexRef) : texRef(theTexRef), nextEntry(NULL) { }
HashEntry(unsigned theTexRef) : texRef(theTexRef), nextEntry(nullptr) { }
};
class CLegacy3D;
+150
View File
@@ -0,0 +1,150 @@
#include "GLSLShader.h"
#include <cstdio>
GLSLShader::GLSLShader()
{
Reset();
}
GLSLShader::~GLSLShader()
{
UnloadShaders();
}
GLSLShader::GLSLShader(GLSLShader&& other) noexcept
{
*this = std::move(other);
}
GLSLShader& GLSLShader::operator=(GLSLShader&& other) noexcept
{
if (this != &other) {
m_program = other.m_program;
m_vShader = other.m_vShader;
m_fShader = other.m_fShader;
uniformLocMap = other.uniformLocMap;
attribLocMap = other.attribLocMap;
std::copy(std::begin(other.uniformLoc), std::end(other.uniformLoc), std::begin(uniformLoc));
std::copy(std::begin(other.attribLoc), std::end(other.attribLoc), std::begin(attribLoc));
other.Reset();
}
return *this;
}
bool GLSLShader::LoadShaders(const char* vertexShader, const char* fragmentShader)
{
m_program = glCreateProgram();
m_vShader = glCreateShader(GL_VERTEX_SHADER);
m_fShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(m_vShader, 1, &vertexShader, NULL);
glShaderSource(m_fShader, 1, &fragmentShader, NULL);
glCompileShader(m_vShader);
glCompileShader(m_fShader);
glAttachShader(m_program, m_vShader);
glAttachShader(m_program, m_fShader);
glLinkProgram(m_program);
PrintShaderInfoLog(m_vShader);
PrintShaderInfoLog(m_fShader);
PrintProgramInfoLog(m_program);
return true;
}
void GLSLShader::UnloadShaders()
{
if (m_program) {
glDeleteShader(m_vShader);
glDeleteShader(m_fShader);
glDeleteProgram(m_program);
}
m_vShader = 0;
m_fShader = 0;
m_program = 0;
}
void GLSLShader::EnableShader()
{
glUseProgram(m_program);
}
void GLSLShader::DisableShader()
{
glUseProgram(0);
}
void GLSLShader::Reset()
{
m_vShader = 0;
m_fShader = 0;
m_program = 0;
for (auto& i : uniformLoc) {
i = -1;
}
for (auto& i : attribLoc) {
i = -1;
}
uniformLocMap.clear();
attribLocMap.clear();
}
void GLSLShader::PrintShaderInfoLog(GLuint obj)
{
int infologLength = 0;
int charsWritten = 0;
glGetShaderiv(obj, GL_INFO_LOG_LENGTH, &infologLength);
if (infologLength > 0) {
char* infoLog = new char[infologLength];
glGetShaderInfoLog(obj, infologLength, &charsWritten, infoLog);
printf("%s\n", infoLog);
delete[] infoLog;
}
}
void GLSLShader::PrintProgramInfoLog(GLuint obj)
{
int infologLength = 0;
int charsWritten = 0;
glGetProgramiv(obj, GL_INFO_LOG_LENGTH, &infologLength);
if (infologLength > 0) {
char* infoLog = new char[infologLength];
glGetProgramInfoLog(obj, infologLength, &charsWritten, infoLog);
printf("%s\n", infoLog);
delete[] infoLog;
}
}
int GLSLShader::GetUniformLocation(const char* str)
{
return glGetUniformLocation(m_program, str);
}
void GLSLShader::GetUniformLocationMap(const char* str)
{
uniformLocMap[str] = GetUniformLocation(str);
}
int GLSLShader::GetAttributeLocation(const char* str)
{
return glGetAttribLocation(m_program, str);
}
void GLSLShader::GetAttributeLocationMap(const char* str)
{
attribLocMap[str] = GetAttributeLocation(str);
}
+61
View File
@@ -0,0 +1,61 @@
#ifndef _GLSLSHADER_H_
#define _GLSLSHADER_H_
#include <GL/glew.h>
#include <map>
#include <cstring>
class GLSLShader {
public:
GLSLShader();
~GLSLShader();
GLSLShader(GLSLShader&& other) noexcept;
GLSLShader(const GLSLShader&) = delete;
GLSLShader& operator=(const GLSLShader&) = delete;
GLSLShader& operator=(GLSLShader&& other) noexcept;
bool LoadShaders(const char* vertexShader, const char* fragmentShader);
void UnloadShaders();
void EnableShader();
void DisableShader();
int GetUniformLocation(const char* str);
void GetUniformLocationMap(const char* str);
int GetAttributeLocation(const char* str);
void GetAttributeLocationMap(const char* str);
int uniformLoc[64];
int attribLoc[16];
private:
struct cmp_str
{
bool operator()(char const* a, char const* b) const
{
return std::strcmp(a, b) < 0;
}
};
public:
std::map<const char*, int, cmp_str> uniformLocMap;
std::map<const char*, int, cmp_str> attribLocMap;
private:
void Reset();
void PrintShaderInfoLog(GLuint obj);
void PrintProgramInfoLog(GLuint obj);
GLuint m_program;
GLuint m_vShader;
GLuint m_fShader;
};
#endif
+125 -113
View File
@@ -3,7 +3,7 @@
#include <utility>
#ifndef M_PI
#define M_PI 3.14159265359
#define M_PI 3.1415926535897932384626433832795
#endif
namespace New3D {
@@ -17,10 +17,10 @@ void Mat4::LoadIdentity()
{
float *m = currentMatrix;
m[0] = 1; m[4] = 0; m[8 ] = 0; m[12] = 0;
m[1] = 0; m[5] = 1; m[9 ] = 0; m[13] = 0;
m[2] = 0; m[6] = 0; m[10] = 1; m[14] = 0;
m[3] = 0; m[7] = 0; m[11] = 0; m[15] = 1;
m[0] = 1.f; m[4] = 0.f; m[8 ] = 0.f; m[12] = 0.f;
m[1] = 0.f; m[5] = 1.f; m[9 ] = 0.f; m[13] = 0.f;
m[2] = 0.f; m[6] = 0.f; m[10] = 1.f; m[14] = 0.f;
m[3] = 0.f; m[7] = 0.f; m[11] = 0.f; m[15] = 1.f;
}
void Mat4::MultiMatrices(const float a[16], const float b[16], float r[16])
@@ -29,8 +29,7 @@ void Mat4::MultiMatrices(const float a[16], const float b[16], float r[16])
#define B(row,col) b[(col<<2)+row]
#define P(row,col) r[(col<<2)+row]
int i;
for (i = 0; i < 4; i++) {
for (int i = 0; i < 4; i++) {
const float ai0 = A(i, 0), ai1 = A(i, 1), ai2 = A(i, 2), ai3 = A(i, 3);
P(i, 0) = ai0 * B(0, 0) + ai1 * B(1, 0) + ai2 * B(2, 0) + ai3 * B(3, 0);
P(i, 1) = ai0 * B(0, 1) + ai1 * B(1, 1) + ai2 * B(2, 1) + ai3 * B(3, 1);
@@ -56,25 +55,25 @@ void Mat4::Translate(float x, float y, float z)
float m[16];
//==========
m[0] = 1;
m[1] = 0;
m[2] = 0;
m[3] = 0;
m[0] = 1.f;
m[1] = 0.f;
m[2] = 0.f;
m[3] = 0.f;
m[4] = 0;
m[5] = 1;
m[6] = 0;
m[7] = 0;
m[4] = 0.f;
m[5] = 1.f;
m[6] = 0.f;
m[7] = 0.f;
m[8] = 0;
m[9] = 0;
m[10] = 1;
m[11] = 0;
m[8] = 0.f;
m[9] = 0.f;
m[10] = 1.f;
m[11] = 0.f;
m[12] = x;
m[13] = y;
m[14] = z;
m[15] = 1;
m[15] = 1.f;
Mat4::MultiMatrices(currentMatrix, m, currentMatrix);
}
@@ -86,155 +85,168 @@ void Mat4::Scale(float x, float y, float z)
//==========
m[0] = x;
m[1] = 0;
m[2] = 0;
m[3] = 0;
m[1] = 0.f;
m[2] = 0.f;
m[3] = 0.f;
m[4] = 0;
m[4] = 0.f;
m[5] = y;
m[6] = 0;
m[7] = 0;
m[6] = 0.f;
m[7] = 0.f;
m[8] = 0;
m[9] = 0;
m[8] = 0.f;
m[9] = 0.f;
m[10] = z;
m[11] = 0;
m[11] = 0.f;
m[12] = 0;
m[13] = 0;
m[14] = 0;
m[15] = 1;
m[12] = 0.f;
m[13] = 0.f;
m[14] = 0.f;
m[15] = 1.f;
Mat4::MultiMatrices(currentMatrix, m, currentMatrix);
}
void Mat4::Rotate(float angle, float x, float y, float z)
{
//===========
float c;
float s;
float m[16];
//===========
// normalise vector first
{
//========
float len;
float inv_len;
//========
len = std::sqrt(x * x + y * y + z * z);
inv_len = 1.f/std::sqrt(x * x + y * y + z * z);
x /= len;
y /= len;
z /= len;
x *= inv_len;
y *= inv_len;
z *= inv_len;
}
c = std::cos(angle*3.14159265f / 180.0f);
s = std::sin(angle*3.14159265f / 180.0f);
float c = std::cos(angle*(float)(M_PI / 180.0));
float s = std::sin(angle*(float)(M_PI / 180.0));
m[0] = (x*x) * (1 - c) + c;
m[1] = (y*x) * (1 - c) + (z*s);
m[2] = (x*z) * (1 - c) - (y*s);
m[3] = 0;
float m[16];
m[0] = (x*x) * (1.f - c) + c;
m[1] = (y*x) * (1.f - c) + (z*s);
m[2] = (x*z) * (1.f - c) - (y*s);
m[3] = 0.f;
m[4] = (x*y) * (1 - c) - (z*s);
m[5] = (y*y) * (1 - c) + c;
m[6] = (y*z) * (1 - c) + (x*s);
m[7] = 0;
m[4] = (x*y) * (1.f - c) - (z*s);
m[5] = (y*y) * (1.f - c) + c;
m[6] = (y*z) * (1.f - c) + (x*s);
m[7] = 0.f;
m[8] = (x*z) * (1 - c) + (y*s);
m[9] = (y*z) * (1 - c) - (x*s);
m[10] = (z*z) * (1 - c) + c;
m[11] = 0;
m[8] = (x*z) * (1.f - c) + (y*s);
m[9] = (y*z) * (1.f - c) - (x*s);
m[10] = (z*z) * (1.f - c) + c;
m[11] = 0.f;
m[12] = 0;
m[13] = 0;
m[14] = 0;
m[15] = 1;
m[12] = 0.f;
m[13] = 0.f;
m[14] = 0.f;
m[15] = 1.f;
Mat4::MultiMatrices(currentMatrix, m, currentMatrix);
}
void Mat4::Frustum(float left, float right, float bottom, float top, float nearVal, float farVal)
void Mat4::Frustum(float left, float right, float bottom, float top, float nearVal, float farVal)
{
//=====================
float x = 2.0F / (right - left);
float y = 2.0F / (top - bottom);
float a = (right + left) / (right - left);
float b = (top + bottom) / (top - bottom);
float c = -(farVal + nearVal) / (farVal - nearVal);
float d = -(2.0F * farVal * nearVal) / (farVal - nearVal);
float m[16];
float x, y, a, b, c, d;
//=====================
x = (2.0F*nearVal) / (right - left);
y = (2.0F*nearVal) / (top - bottom);
a = (right + left) / (right - left);
b = (top + bottom) / (top - bottom);
c = -(farVal + nearVal) / (farVal - nearVal);
d = -(2.0F*farVal*nearVal) / (farVal - nearVal);
m[0] = x;
m[1] = 0;
m[2] = 0;
m[3] = 0;
m[1] = 0.f;
m[2] = 0.f;
m[3] = 0.f;
m[4] = 0;
m[4] = 0.f;
m[5] = y;
m[6] = 0;
m[7] = 0;
m[6] = 0.f;
m[7] = 0.f;
m[8] = a;
m[9] = b;
m[10] = c;
m[11] = -1;
m[11] = -1.f;
m[12] = 0;
m[13] = 0;
m[12] = 0.f;
m[13] = 0.f;
m[14] = d;
m[15] = 0;
m[15] = 0.f;
Mat4::MultiMatrices(currentMatrix, m, currentMatrix);
}
void Mat4::FrustumRZ(float left, float right, float bottom, float top, float nearVal)
{
float x = 2.0F / (right - left);
float y = 2.0F / (top - bottom);
float a = (right + left) / (right - left);
float b = (top + bottom) / (top - bottom);
float m[16];
m[0] = x;
m[1] = 0.f;
m[2] = 0.f;
m[3] = 0.f;
m[4] = 0.f;
m[5] = y;
m[6] = 0.f;
m[7] = 0.f;
m[8] = a;
m[9] = b;
m[10] = 0.f;
m[11] = -1.f;
m[12] = 0.f;
m[13] = 0.f;
m[14] = nearVal;
m[15] = 0.f;
Mat4::MultiMatrices(currentMatrix, m, currentMatrix);
}
void Mat4::Perspective(float fovy, float aspect, float zNear, float zFar)
{
//=========
float ymax;
float xmax;
//=========
ymax = zNear * tanf(fovy * (float)M_PI / 360.0f);
xmax = ymax * aspect;
float ymax = tanf(fovy * (float)(M_PI / 360.0));
float xmax = ymax * aspect;
Frustum(-xmax, xmax, -ymax, ymax, zNear, zFar);
}
void Mat4::Ortho(float left, float right, float bottom, float top, float nearVal, float farVal)
{
//================
float tx = -(right + left) / (right - left);
float ty = -(top + bottom) / (top - bottom);
float tz = -(farVal + nearVal) / (farVal - nearVal);
float m[16];
float tx, ty, tz;
//================
m[0] = 2.f/(right-left);
m[1] = 0.f;
m[2] = 0.f;
m[3] = 0.f;
tx = -(right + left) / (right - left);
ty = -(top + bottom) / (top - bottom);
tz = -(farVal + nearVal) / (farVal - nearVal);
m[4] = 0.f;
m[5] = 2.f/(top-bottom);
m[6] = 0.f;
m[7] = 0.f;
m[0] = 2/(right-left);
m[1] = 0;
m[2] = 0;
m[3] = 0;
m[4] = 0;
m[5] = 2/(top-bottom);
m[6] = 0;
m[7] = 0;
m[8] = 0;
m[9] = 0;
m[10] = -2/(farVal-nearVal);
m[11] = 0;
m[8] = 0.f;
m[9] = 0.f;
m[10] = -2.f/(farVal-nearVal);
m[11] = 0.f;
m[12] = tx;
m[13] = ty;
m[14] = tz;
m[15] = 1;
m[15] = 1.f;
Mat4::MultiMatrices(currentMatrix, m, currentMatrix);
}
@@ -324,4 +336,4 @@ void Mat4::Release()
m_vMat4.clear();
}
}// New3D
}// New3D
+1
View File
@@ -16,6 +16,7 @@ public:
void Rotate (float angle, float x, float y, float z);
void Scale (float x, float y, float z);
void Frustum (float left, float right, float bottom, float top, float nearVal, float farVal);
void FrustumRZ (float left, float right, float bottom, float top, float nearVal);
void Perspective (float fovy, float aspect, float zNear, float zFar);
void Ortho (float left, float right, float bottom, float top, float nearVal, float farVal);
void MultMatrix (const float *m);
+25 -21
View File
@@ -4,11 +4,12 @@ namespace New3D {
NodeAttributes::NodeAttributes()
{
currentTexOffsetX = 0;
currentTexOffsetY = 0;
currentPage = 0;
currentClipStatus = Clip::INTERCEPT;
currentModelScale = 1.0f;
currentTexOffsetX = 0;
currentTexOffsetY = 0;
currentPage = 0;
currentModelScale = 1.0f;
currentModelAlpha = 1.0;
currentDisableCulling = false;
}
bool NodeAttributes::Push()
@@ -22,11 +23,12 @@ bool NodeAttributes::Push()
return false;
}
na.page = currentPage;
na.texOffsetX = currentTexOffsetX;
na.texOffsetY = currentTexOffsetY;
na.clip = currentClipStatus;
na.modelScale = currentModelScale;
na.page = currentPage;
na.texOffsetX = currentTexOffsetX;
na.texOffsetY = currentTexOffsetY;
na.modelScale = currentModelScale;
na.modelAlpha = currentModelAlpha;
na.disableCulling = currentDisableCulling;
m_vecAttribs.emplace_back(na);
@@ -41,29 +43,31 @@ bool NodeAttributes::Pop()
auto &last = m_vecAttribs.back();
currentPage = last.page;
currentTexOffsetX = last.texOffsetX;
currentTexOffsetY = last.texOffsetY;
currentClipStatus = last.clip;
currentModelScale = last.modelScale;
currentPage = last.page;
currentTexOffsetX = last.texOffsetX;
currentTexOffsetY = last.texOffsetY;
currentModelScale = last.modelScale;
currentModelAlpha = last.modelAlpha;
currentDisableCulling = last.disableCulling;
m_vecAttribs.pop_back();
return true;
}
bool NodeAttributes::StackLimit()
bool NodeAttributes::StackLimit() const
{
return m_vecAttribs.size() >= 1024;
}
void NodeAttributes::Reset()
{
currentPage = 0;
currentTexOffsetX = 0;
currentTexOffsetY = 0;
currentClipStatus = Clip::INTERCEPT;
currentModelScale = 1.0f;
currentPage = 0;
currentTexOffsetX = 0;
currentTexOffsetY = 0;
currentModelScale = 1.0f;
currentModelAlpha = 1.0f;
currentDisableCulling = false;
m_vecAttribs.clear();
}
+109 -80
View File
@@ -2,43 +2,35 @@
#define _MODEL_H_
#include <vector>
#include <unordered_map>
#include <map>
#include <memory>
#include <string.h>
#include "Texture.h"
#include <cstring>
#include "Types.h"
#include "Mat4.h"
namespace New3D {
struct ClipVertex
{
float pos[4];
};
struct ClipPoly
{
ClipVertex list[12]; // what's the max number we can hit for a triangle + 4 planes?
int count = 0;
};
struct Vertex // half vertex
{
float pos[4];
float normal[3];
float texcoords[2];
float fixedShade;
};
struct FVertex : Vertex // full vertex including face attributes
{
float faceNormal[3];
UINT8 faceColour[4];
FVertex& operator=(const Vertex& vertex)
static bool Equal(const Vertex& p1, const Vertex& p2)
{
memcpy(this, &vertex, sizeof(Vertex));
return *this;
return (p1.pos[0] == p2.pos[0] &&
p1.pos[1] == p2.pos[1] &&
p1.pos[2] == p2.pos[2]);
}
static void Average(const Vertex& p1, const Vertex& p2, Vertex& p3)
{
p3.pos[3] = 1.0f; //always 1
p3.fixedShade = (p1.fixedShade + p2.fixedShade) * 0.5f;
for (int i = 0; i < 3; i++) { p3.pos[i] = (p1.pos[i] + p2.pos[i]) * 0.5f; }
for (int i = 0; i < 3; i++) { p3.normal[i] = (p1.normal[i] + p2.normal[i]) * 0.5f; }
for (int i = 0; i < 2; i++) { p3.texcoords[i] = (p1.texcoords[i] + p2.texcoords[i]) * 0.5f; }
}
};
@@ -47,87 +39,116 @@ struct R3DPoly
Vertex v[4]; // just easier to have them as an array
float faceNormal[3]; // we need this to help work out poly winding, i assume the h/w uses this instead of calculating normals itself
UINT8 faceColour[4]; // per face colour
float textureNP;
int number = 4;
};
struct Poly // our polys are always 3 triangles, unlike the real h/w
struct FVertex : Vertex // full vertex including face attributes
{
Poly() {}; // default
float faceNormal[3];
UINT8 faceColour[4];
float textureNP;
Poly(bool firstTriangle, const R3DPoly& r3dPoly) {
if (firstTriangle) {
p1 = r3dPoly.v[0];
p2 = r3dPoly.v[1];
p3 = r3dPoly.v[2];
}
else {
p1 = r3dPoly.v[0];
p2 = r3dPoly.v[2];
p3 = r3dPoly.v[3];
}
// copy face attributes
for (int i = 0; i < 4; i++) {
p1.faceColour[i] = r3dPoly.faceColour[i];
p2.faceColour[i] = r3dPoly.faceColour[i];
p3.faceColour[i] = r3dPoly.faceColour[i];
}
for (int i = 0; i < 3; i++) {
p1.faceNormal[i] = r3dPoly.faceNormal[i];
p2.faceNormal[i] = r3dPoly.faceNormal[i];
p3.faceNormal[i] = r3dPoly.faceNormal[i];
}
FVertex& operator=(const Vertex& vertex)
{
memcpy(this, &vertex, sizeof(Vertex));
return *this;
}
FVertex p1;
FVertex p2;
FVertex p3;
FVertex() {}
FVertex(const R3DPoly& r3dPoly, int index)
{
for (int i = 0; i < 4; i++) { faceColour[i] = r3dPoly.faceColour[i]; }
for (int i = 0; i < 3; i++) { faceNormal[i] = r3dPoly.faceNormal[i]; }
textureNP = r3dPoly.textureNP;
*this = r3dPoly.v[index];
}
FVertex(const R3DPoly& r3dPoly, int index1, int index2) // average of 2 points
{
Vertex::Average(r3dPoly.v[index1], r3dPoly.v[index2], *this);
// copy face attributes
for (int i = 0; i < 4; i++) { faceColour[i] = r3dPoly.faceColour[i]; }
for (int i = 0; i < 3; i++) { faceNormal[i] = r3dPoly.faceNormal[i]; }
textureNP = r3dPoly.textureNP;
}
static void Average(const FVertex& p1, const FVertex& p2, FVertex& p3)
{
Vertex::Average(p1, p2, p3);
for (int i = 0; i < 4; i++) { p3.faceColour[i] = p1.faceColour[i]; }
for (int i = 0; i < 3; i++) { p3.faceNormal[i] = p1.faceNormal[i]; }
}
};
enum class Layer { colour, trans1, trans2, trans12 /*both 1&2*/, all, none };
struct Mesh
{
//helper funcs
bool Render(bool alpha)
bool Render(Layer layer, float nodeAlpha)
{
if (alpha) {
if (!textureAlpha && !polyAlpha) {
bool nAlpha = nodeAlpha < 1.0f;
switch (layer)
{
case Layer::colour:
if (polyAlpha || nAlpha) {
return false;
}
}
else {
if (polyAlpha) {
break;
case Layer::trans1:
if ((!textureAlpha && !polyAlpha && !nAlpha) || transLSelect) {
return false;
}
break;
case Layer::trans2:
if ((!textureAlpha && !polyAlpha && !nAlpha) || !transLSelect) {
return false;
}
break;
default: // not using these types
return false;
}
return true;
}
enum TexWrapMode : int { repeat = 0, repeatClamp, mirror, mirrorClamp };
// texture
int format, x, y, width, height = 0;
bool mirrorU = false;
bool mirrorV = false;
bool inverted = false;
int format = 0;
int x = 0;
int y = 0;
int width = 0;
int height = 0;
int page = 0;
bool inverted = false;
TexWrapMode wrapModeU = TexWrapMode::repeat;
TexWrapMode wrapModeV = TexWrapMode::repeat;
// microtexture
bool microTexture = false;
int microTextureID = 0;
float microTextureScale = 0;
float microTextureMinLOD = 0;
// attributes
bool doubleSided = false;
bool textured = false;
bool polyAlpha = false; // specified in the rgba colour
bool textureAlpha = false; // use alpha in texture
bool alphaTest = false; // discard fragment based on alpha (ogl does this with fixed function)
bool clockWise = true; // we need to check if the matrix will change the winding
bool layered = false; // stencil poly
bool highPriority = false; // rendered over the top
bool transLSelect = false; // actually the transparency layer, false = layer 0, true = layer 1
bool translatorMap = false; // colours are multiplied by 16
bool noLosReturn = false; // line of sight test
// lighting
bool fixedShading = false;
bool smoothShading = false;
bool lighting = false;
bool specular = false;
float shininess = 0;
@@ -138,12 +159,12 @@ struct Mesh
// opengl resources
int vboOffset = 0; // this will be calculated later
int triangleCount = 0;
int vertexCount = 0; // /3 for triangles /4 for quads
};
struct SortingMesh : public Mesh // This struct temporarily holds the model data, before it gets copied to the main buffer
{
std::vector<Poly> polys;
std::vector<FVertex> verts;
};
struct Model
@@ -163,18 +184,22 @@ struct Model
//model scale step 1.5+
float scale = 1.0f;
//node transparency
float alpha = 1.0f;
};
struct Viewport
{
int vpX; // these are the original hardware values
int vpY;
int vpWidth;
int vpHeight;
float vpX; // these are the original hardware values
float vpY;
float vpWidth;
float vpHeight;
float angle_left;
float angle_right;
float angle_top;
float angle_bottom;
float cota;
Mat4 projectionMatrix; // projection matrix, we will calc this later when we have scene near/far vals
@@ -184,7 +209,11 @@ struct Viewport
float spotEllipse[4]; // spotlight ellipse (see RenderViewport())
float spotRange[2]; // Z range
float spotColor[3]; // color
float fogParams[7]; // fog parameters (...)
float fogColour[3];
float fogStart;
float fogDensity;
float fogAttenuation;
float fogAmbient;
float scrollFog; // a transparency value that determines if fog is blended over the bottom 2D layer
int losPosX, losPosY; // line of sight position
int x, y; // viewport coordinates (scaled and in OpenGL format)
@@ -198,8 +227,6 @@ struct Viewport
int hardwareStep; // not really a viewport param but will do here
};
enum class Clip { INSIDE, OUTSIDE, INTERCEPT, NOT_SET };
class NodeAttributes
{
public:
@@ -208,14 +235,15 @@ public:
bool Push();
bool Pop();
bool StackLimit();
bool StackLimit() const;
void Reset();
int currentTexOffsetX;
int currentTexOffsetY;
int currentPage;
Clip currentClipStatus;
float currentModelScale;
float currentModelAlpha;
bool currentDisableCulling;
private:
@@ -224,8 +252,9 @@ private:
int texOffsetX;
int texOffsetY;
int page;
Clip clip;
float modelScale;
float modelAlpha; // from culling node
bool disableCulling;
};
std::vector<NodeAttribs> m_vecAttribs;
};
@@ -239,4 +268,4 @@ struct Node
} // New3D
#endif
#endif
File diff suppressed because it is too large. Load diff
+77 -49
View File
@@ -28,9 +28,10 @@
#ifndef INCLUDED_NEW3D_H
#define INCLUDED_NEW3D_H
#include "Pkgs/glew.h"
#include <unordered_map>
#include <atomic>
#include <GL/glew.h>
#include "Types.h"
#include "TextureSheet.h"
#include "Graphics/IRender3D.h"
#include "Model.h"
#include "Mat4.h"
@@ -41,10 +42,13 @@
#include "Vec.h"
#include "R3DScrollFog.h"
#include "PolyHeader.h"
#include "R3DFrameBuffers.h"
#include <mutex>
#include "TextureBank.h"
namespace New3D {
class CNew3D : public IRender3D
class CNew3D final : public IRender3D
{
public:
/*
@@ -135,11 +139,11 @@ public:
* totalYRes Vertical resolution.
*
* Returns:
* OKAY is successful, otherwise FAILED if a non-recoverable error
* OKAY if successful, otherwise FAILED if a non-recoverable error
* occurred. Any allocated memory will not be freed until the
* destructor is called. Prints own error messages.
*/
bool Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes, unsigned totalXRes, unsigned totalYRes);
Result Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes, unsigned totalXRes, unsigned totalYRes, unsigned aaTarget);
/*
* SetSunClamp(bool enable);
@@ -152,14 +156,24 @@ public:
void SetSunClamp(bool enable);
/*
* SetSignedShade(bool enable);
* SetBlockCulling(bool enable);
*
* Sets the sign-ness of fixed shading value
* When enabled, clears screen to white and disables 3D rendering
*
* Parameters:
* enable Fixed shading is expressed as signed value
* enable Set block culling
*/
void SetSignedShade(bool enable);
void SetBlockCulling(bool enable);
/*
* GetLosValue(int layer);
*
* Gets the line of sight value for the priority layer
*
* Parameters:
* layer Priority layer to read from
*/
float GetLosValue(int layer);
/*
* CRender3D(config):
@@ -170,7 +184,7 @@ public:
* Parameters:
* config Run-time configuration.
*/
CNew3D(const Util::Config::Node &config, std::string gameName);
CNew3D(const Util::Config::Node &config, const std::string& gameName);
~CNew3D(void);
private:
@@ -185,6 +199,7 @@ private:
// Matrix stack
void MultMatrix(UINT32 matrixOffset, Mat4& mat);
void InitMatrixStack(UINT32 matrixBaseAddr, Mat4& mat);
void ResetMatrix(Mat4& mat) const;
// Scene database traversal
bool DrawModel(UINT32 modelAddr);
@@ -194,18 +209,24 @@ private:
void RenderViewport(UINT32 addr);
// building the scene
int GetTexFormat(int originalFormat, bool contour) const;
void SetMeshValues(SortingMesh *currentMesh, PolyHeader &ph);
void CacheModel(Model *m, const UINT32 *data);
void CopyVertexData(const R3DPoly& r3dPoly, std::vector<Poly>& polyArray);
void OffsetTexCoords(R3DPoly& r3dPoly, float offset[2]);
void CopyVertexData(const R3DPoly& r3dPoly, std::vector<FVertex>& vertexArray);
void GetCoordinates(int width, int height, UINT16 uIn, UINT16 vIn, float uvScale, float& uOut, float& vOut) const;
bool RenderScene(int priority, bool renderOverlay, bool alpha); // returns if has overlay plane
float Determinant3x3(const float m[16]);
bool IsDynamicModel(UINT32 *data); // check if the model has a colour palette
bool IsVROMModel(UINT32 modelAddr);
bool RenderScene(int priority, bool renderOverlay, Layer layer); // returns if has overlay plane
bool IsDynamicModel(UINT32 *data) const; // check if the model has a colour palette
bool IsVROMModel(UINT32 modelAddr) const;
void DrawScrollFog();
void CalcTexOffset(int offX, int offY, int page, int x, int y, int& newX, int& newY);
void DrawAmbientFog();
bool SkipLayer(int layer);
void SetRenderStates();
void DisableRenderStates();
void TranslateLosPosition(int inX, int inY, int& outX, int& outY) const;
bool ProcessLos(int priority);
void CalcViewport(Viewport* vp);
void TranslateTexture(unsigned& x, unsigned& y, int width, int height, int& page) const;
/*
* Data
@@ -213,15 +234,19 @@ private:
// Misc
std::string m_gameName;
int m_numPolyVerts;
GLenum m_primType;
// GPU configuration
bool m_sunClamp;
bool m_shadeIsSigned;
std::atomic_bool m_sunClamp;
std::atomic_bool m_blockCulling;
bool m_noWhiteFlash;
// Stepping
int m_step;
int m_offset; // offset to subtract for words 3 and higher of culling nodes
float m_vertexFactor; // fixed-point conversion factor for vertices
float m_textureNPFactor; // fixed-point conversion factor for texture NP values
// Memory (passed from outside)
const UINT32 *m_cullingRAMLo; // 4 MB
@@ -235,50 +260,53 @@ private:
unsigned m_xOffs, m_yOffs;
unsigned m_xRes, m_yRes; // resolution of Model 3's 496x384 display area within the window
unsigned m_totalXRes, m_totalYRes; // total OpenGL window resolution
bool m_wideScreen;
// Real3D Base Matrix Pointer
const float *m_matrixBasePtr;
UINT32 m_colorTableAddr = 0x400; // address of color table in polygon RAM
LODBlendTable* m_LODBlendTable;
TextureSheet m_texSheet;
NodeAttributes m_nodeAttribs;
Mat4 m_modelMat; // current modelview matrix
std::vector<Node> m_nodes; // this represents the entire render frame
std::vector<Poly> m_polyBufferRam; // dynamic polys
std::vector<Poly> m_polyBufferRom; // rom polys
std::unordered_map<UINT32, std::shared_ptr<std::vector<Mesh>>> m_romMap; // a hash table for all the ROM models. The meshes don't have model matrices or tex offsets yet
struct LOS
{
float value[4] = { 0,0,0,0 }; // line of sight value for each priority layer
} m_los[2];
LOS* m_losFront = &m_los[0]; // we need to double buffer this because 3d works in separate thread
LOS* m_losBack = &m_los[1];
std::mutex m_losMutex;
Vertex m_prev[4]; // these are class variables because sega bass fishing starts meshes with shared vertices from the previous one
UINT16 m_prevTexCoords[4][2]; // basically relying on undefined behavour
std::vector<Node> m_nodes; // this represents the entire render frame
std::vector<FVertex> m_polyBufferRam; // dynamic polys
std::vector<FVertex> m_polyBufferRom; // rom polys
std::unordered_map<UINT32, std::shared_ptr<std::vector<Mesh>>> m_romMap; // a hash table for all the ROM models. The meshes don't have model matrices or tex offsets yet
TextureBank m_textureBank[2];
GLuint m_vao;
VBO m_vbo; // large VBO to hold our poly data, start of VBO is ROM data, ram polys follow
R3DShader m_r3dShader;
R3DScrollFog m_r3dScrollFog;
R3DFrameBuffers m_r3dFrameBuffers;
GLuint m_aaTarget; // optional, maybe zero
Plane m_planes[4];
struct BBox
struct
{
V4::Vec4 points[8];
};
struct NFPair
{
float zNear;
float zFar;
};
NFPair m_nfPairs[4];
int m_currentPriority;
void CalcFrustumPlanes (Plane p[4], const float* matrix);
void CalcBox (float distance, BBox& box);
void TransformBox (const float *m, BBox& box);
void MultVec (const float matrix[16], const float in[4], float out[4]);
Clip ClipBox (BBox& box, Plane planes[4]);
void ClipModel (const Model *m);
void ClipPolygon (ClipPoly& clipPoly, Plane planes[4]);
void CalcBoxExtents (const BBox& box);
void CalcViewport (Viewport* vp, float near, float far);
float bnlu;
float bnlv;
float bntu;
float bntw;
float bnru;
float bnrv;
float bnbu;
float bnbw;
float correction;
} m_planes;
};
} // New3D
+6 -6
View File
@@ -8,11 +8,11 @@ struct Plane
float a, b, c, d;
void Normalise() {
float temp = std::sqrt((a * a) + (b * b) + (c * c));
a /= temp;
b /= temp;
c /= temp;
d /= temp;
float temp = 1.f/std::sqrt((a * a) + (b * b) + (c * c));
a *= temp;
b *= temp;
c *= temp;
d *= temp;
}
float DistanceToPoint(const float v[3]) {
@@ -24,4 +24,4 @@ struct Plane
}
};
#endif
#endif
+39 -47
View File
@@ -5,7 +5,7 @@ namespace New3D {
PolyHeader::PolyHeader()
{
header = NULL;
header = nullptr;
}
PolyHeader::PolyHeader(UINT32* h)
@@ -50,10 +50,6 @@ int PolyHeader::NumPolysTotal()
int PolyHeader::NumTrianglesTotal()
{
if (header[6] == 0) {
return 0; // no poly data
}
UINT32* start = header; // save start address
int count = (NumVerts() == 4) ? 2 : 1;
@@ -117,7 +113,7 @@ int PolyHeader::NumVerts()
int PolyHeader::NumSharedVerts()
{
int sharedVerts[] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 };
static const int sharedVerts[] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 };
return sharedVerts[header[0] & 0xf];
}
@@ -135,9 +131,9 @@ bool PolyHeader::SharedVertex(int vertex)
void PolyHeader::FaceNormal(float n[3])
{
n[0] = (float)(((INT32)header[1]) >> 8) * (1.0f / 4194304.0f);
n[1] = (float)(((INT32)header[2]) >> 8) * (1.0f / 4194304.0f);
n[2] = (float)(((INT32)header[3]) >> 8) * (1.0f / 4194304.0f);
n[0] = (float)(((INT32)header[1]) >> 8) * (float)(1.0 / 4194304.0);
n[1] = (float)(((INT32)header[2]) >> 8) * (float)(1.0 / 4194304.0);
n[2] = (float)(((INT32)header[3]) >> 8) * (float)(1.0 / 4194304.0);
}
float PolyHeader::UVScale()
@@ -152,7 +148,7 @@ bool PolyHeader::DoubleSided()
bool PolyHeader::LastPoly()
{
if ((header[1] & 4) > 0 || header[6] == 0) {
if ((header[1] & 4) > 0) {
return true;
}
@@ -174,6 +170,16 @@ bool PolyHeader::SmoothShading()
return (header[1] & 0x8) > 0;
}
bool PolyHeader::NoLosReturn()
{
return (header[1] & 0x1) > 0;
}
bool PolyHeader::EdgeOnTranslucency()
{
return (header[1] & 0x80) > 0;
}
//
// header 2
//
@@ -286,24 +292,21 @@ int PolyHeader::X()
int PolyHeader::Y()
{
//=======
//====
int y;
int page;
//=======
//====
if (Page()) {
page = 1024;
}
else {
page = 0;
}
y = (32 * (header[5] & 0x1F) + page); // if we hit 2nd page add 1024 to y coordinate
y = 32 * (header[5] & 0x1F); // if we hit 2nd page add 1024 to y coordinate
y &= 2047;
return y;
}
float PolyHeader::TextureNP()
{
return (float)(header[5] >> 8);
}
//
// header 6
//
@@ -383,38 +386,27 @@ bool PolyHeader::TranslucencyPatternSelect()
}
//
// misc
// hashing
//
UINT64 PolyHeader::Hash()
{
UINT64 hash = 0;
hash |= (header[2] & 3); // bits 0-1 uv mirror bits
hash |= (UINT64)((header[3] >> 0) & 7) << 2; // bits 2-4 tex height
hash |= (UINT64)((header[3] >> 3) & 7) << 5; // bits 5-7 tex width
hash |= (UINT64)X() << 8; // bits 8-17 x offset
hash |= (UINT64)Y() << 18; // bits 18-27 y offset
hash |= (UINT64)TexFormat() << 28; // bits 28-30 tex format
hash |= (UINT64)TexEnabled() << 31; // bits 31 textures enabled
hash |= (UINT64)LightEnabled() << 32; // bits 32 light enabled
hash |= (UINT64)DoubleSided() << 33; // bits 33 double sided
hash |= (UINT64)AlphaTest() << 34; // bits 34 contour processing
hash |= (UINT64)PolyAlpha() << 35; // bits 35 poly alpha processing
hash |= (UINT64)TextureAlpha() << 36; // bits 36 texture alpha processing
hash |= (UINT64)MicroTexture() << 37; // bits 37 microtexture enable
hash |= (UINT64)HighPriority() << 38; // bits 38 high priority enable
hash |= (UINT64)SpecularEnabled() << 39; // bits 39 enable specular reflection
hash |= (UINT64)SmoothShading() << 40; // bits 40 smooth shading
hash |= (UINT64)((header[6] >> 11) & 0x1F) << 41; // bits 41-45 light modifier
hash |= (UINT64)FixedShading() << 46; // bits 46 fixed shading
hash |= (UINT64)TranslatorMap() << 47; // bits 47 translator map
hash |= (UINT64)Layered() << 48; // bits 48 layered texture
hash |= (UINT64)(header[0] >> 26) << 49; // bits 49-54 specular coefficient (opacity)
hash |= (UINT64)((header[6] >> 5) & 3) << 55; // bits 55-56 specular exponent
hash |= (UINT64)MicroTextureID() << 57; // bits 57-59 Microtexture texture number 0-7
hash |= (UINT64)MicroTextureMinLOD() << 60; // bits 60-61 Microtexture min lod index
hash |= (UINT64)((header[3] >> 6) & 3) << 62; // bits 62-63 uv smooth bits
hash |= (UINT64)(header[3] & 0xFF); // bits 0-7 tex width / height / uv smooth
hash |= (UINT64)(((header[4] & 0x1F) << 1) | ((header[5] >> 7) & 1)) << 8; // bits 8-13 x offset
hash |= (UINT64)(header[5] & 0x1F) << 14; // bits 14-18 y offset
hash |= (UINT64)((header[4] & 0xC0) >> 6) << 19; // bits 19-20 page / translatormap
hash |= (UINT64)DoubleSided() << 21; // bits 21 double sided
hash |= (UINT64)AlphaTest() << 22; // bits 22 contour processing
hash |= (UINT64)PolyAlpha() << 23; // bits 23 poly alpha processing
hash |= (UINT64)(header[2] & 0xFF) << 24; // bits 24-31 microtexture / uv mirror
hash |= (UINT64)SpecularEnabled() << 32; // bits 32 enable specular reflection
hash |= (UINT64)SmoothShading() << 33; // bits 33 smooth shading
hash |= (UINT64)FixedShading() << 34; // bits 34 fixed shading
hash |= (UINT64)(header[0] >> 26) << 35; // bits 35-40 specular coefficient (opacity)
hash |= (UINT64)(header[6] & 0x3FFFF) << 41; // bits 41-58 Translucency pattern select / disable lighting / Polygon light modifier / Texture enable / Texture format / Shininess / High priority / Layered polygon / Translucency mode
hash |= (UINT64)NoLosReturn() << 59; // bits 59 no line of sight return
return hash;
}
+9 -4
View File
@@ -1,6 +1,8 @@
#ifndef _POLY_HEADER_H_
#define _POLY_HEADER_H_
#include "Types.h"
namespace New3D {
/*
@@ -29,7 +31,7 @@ xxxxxxxx xxxxxxxx xxxxxxxx-------- Polygon normal X coordinate(2.22 fixed point
-------- -------- -------- ----x--- 1 = smooth shading, 0 = flat shading
-------- -------- -------- -----x-- If set, this is the last polygon
-------- -------- -------- ------x- Poly color, 1 = RGB, 0 = color table
-------- -------- -------- -------x No los return ?
-------- -------- -------- -------x No los return (line of sight)
0x02:
xxxxxxxx xxxxxxxx xxxxxxxx -------- Polygon normal Y coordinate(2.22 fixed point)
@@ -48,12 +50,12 @@ xxxxxxxx xxxxxxxx xxxxxxxx -------- Polygon normal Z coordinate(2.22 fixed poin
0x04:
xxxxxxxx xxxxxxxx xxxxxxxx -------- Color (RGB888 or two 12-bit indexes, sensor color and color)
-------- -------- -------- x------- Translator map enable ? (Seems to be some 4/5bit colour format. Max observed value=16)
-------- -------- -------- x------- Translator map enable (multiplies colour values by 16)
-------- -------- -------- -x------ Texture page
-------- -------- -------- ---xxxxx Upper 5 bits of texture U coordinate
0x05 :
xxxxxxxx xxxxxxxx xxxxxxxx -------- Texture NP ?
xxxxxxxx xxxxxxxx xxxxxxxx -------- Texture NP
-------- -------- -------- x------- Low bit of texture U coordinate
-------- -------- -------- ---xxxxx Low 5 bits of texture V coordinate
@@ -105,13 +107,15 @@ public:
bool PolyColor(); // if false uses LUT from ram
bool FixedShading();
bool SmoothShading();
bool NoLosReturn(); // no line of sight return (still not exactly how sure this attribute is used yet)
bool EdgeOnTranslucency();
//header 2
bool TexUMirror();
bool TexVMirror();
bool MicroTexture();
int MicroTextureID();
int MicroTextureMinLOD(); // basically how many times it repeats compared to the base texture (i assume)
int MicroTextureMinLOD();
// header 3
int TexWidth();
@@ -129,6 +133,7 @@ public:
// header 5
int X();
int Y();
float TextureNP();
//header 6
bool Layered();
+2 -2
View File
@@ -3,8 +3,8 @@
struct LOD
{
float startRange; // possibly specified as angles also, yeah who knows >_<
float deleteRange;
float deleteSize;
float blendFactor;
};
struct LODFeatureType
+8 -6
View File
@@ -1,5 +1,8 @@
#include "Types.h"
#include "R3DFloat.h"
#include "Util/BitCast.h"
#include <cstdint>
float R3DFloat::GetFloat16(UINT16 f)
{
@@ -13,17 +16,16 @@ float R3DFloat::GetFloat32(UINT32 f)
UINT32 R3DFloat::ConvertProFloat(UINT32 a1)
{
int exponent = (a1 & 0x7E000000) >> 25;
UINT32 exponent = (a1 & 0x7E000000) >> 25;
if (exponent <= 31) { // positive
exponent += 127;
}
else { // negative exponent
exponent -= 64;
exponent += 127;
exponent += 127 - 64;
}
int mantissa = (a1 & 0x1FFFFFF) >> 2;
UINT32 mantissa = (a1 & 0x1FFFFFF) >> 2;
return (a1 & 0x80000000) | (exponent << 23) | mantissa;
}
@@ -35,5 +37,5 @@ UINT32 R3DFloat::Convert16BitProFloat(UINT32 a1)
float R3DFloat::ToFloat(UINT32 a1)
{
return *(float*)(&a1);
}
return Util::Uint32AsFloat(a1);
}
+3 -3
View File
@@ -3,8 +3,8 @@
namespace R3DFloat
{
static const UINT16 Pro16BitMax = 0x7fff;
static const float Pro16BitFltMin = 1e-7f; // float min in IEEE
constexpr UINT16 Pro16BitMax = 0x7fff;
constexpr float Pro16BitFltMin = 1e-7f; // float min in IEEE
float GetFloat16(UINT16 f);
float GetFloat32(UINT32 f);
@@ -14,4 +14,4 @@ namespace R3DFloat
float ToFloat(UINT32 a1); // integer float to actual IEEE 754 float
}
#endif
#endif
+330
View File
@@ -0,0 +1,330 @@
#include "R3DFrameBuffers.h"
namespace New3D {
R3DFrameBuffers::R3DFrameBuffers()
{
m_frameBufferID = 0;
m_renderBufferID = 0;
m_frameBufferIDCopy = 0;
m_renderBufferIDCopy = 0;
m_width = 0;
m_height = 0;
m_vao = 0;
for (auto &i : m_texIDs) {
i = 0;
}
m_lastLayer = Layer::none;
AllocShaderTrans();
AllocShaderBase();
glGenVertexArrays(1, &m_vao);
glBindVertexArray(m_vao);
// no states needed since we do it in the shader
glBindVertexArray(0);
}
R3DFrameBuffers::~R3DFrameBuffers()
{
DestroyFBO();
m_shaderTrans.UnloadShaders();
m_shaderBase.UnloadShaders();
if (m_vao) {
glDeleteVertexArrays(1, &m_vao);
m_vao = 0;
}
}
Result R3DFrameBuffers::CreateFBO(int width, int height)
{
m_width = width;
m_height = height;
m_texIDs[0] = CreateTexture(width, height); // colour buffer
m_texIDs[1] = CreateTexture(width, height); // trans layer1
m_texIDs[2] = CreateTexture(width, height); // trans layer2
glGenFramebuffers(1, &m_frameBufferID);
glBindFramebuffer(GL_FRAMEBUFFER, m_frameBufferID);
// colour attachments
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_texIDs[0], 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, m_texIDs[1], 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, m_texIDs[2], 0);
// depth/stencil attachment
glGenRenderbuffers(1, &m_renderBufferID);
glBindRenderbuffer(GL_RENDERBUFFER, m_renderBufferID);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH32F_STENCIL8, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_renderBufferID);
// check setup was successful
auto fboStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
glBindFramebuffer(GL_FRAMEBUFFER, 0); //created R3DFrameBuffers now disable it
return ((CreateFBODepthCopy(width, height) == Result::OKAY) && (fboStatus == GL_FRAMEBUFFER_COMPLETE)) ? Result::OKAY : Result::FAIL;
}
Result R3DFrameBuffers::CreateFBODepthCopy(int width, int height)
{
glGenFramebuffers(1, &m_frameBufferIDCopy);
glBindFramebuffer(GL_FRAMEBUFFER, m_frameBufferIDCopy);
glGenRenderbuffers(1, &m_renderBufferIDCopy);
glBindRenderbuffer(GL_RENDERBUFFER, m_renderBufferIDCopy);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH32F_STENCIL8, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_renderBufferIDCopy);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// check setup was successful
auto fboStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
return (fboStatus == GL_FRAMEBUFFER_COMPLETE) ? Result::OKAY : Result::FAIL;
}
void R3DFrameBuffers::StoreDepth()
{
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_frameBufferID);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_frameBufferIDCopy);
glBlitFramebuffer(0, 0, m_width, m_height, 0, 0, m_width, m_height, GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST);
}
void R3DFrameBuffers::RestoreDepth()
{
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_frameBufferIDCopy);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_frameBufferID);
glBlitFramebuffer(0, 0, m_width, m_height, 0, 0, m_width, m_height, GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST);
}
void R3DFrameBuffers::DestroyFBO()
{
if (m_frameBufferID) {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glDeleteRenderbuffers(1, &m_renderBufferID);
glDeleteFramebuffers(1, &m_frameBufferID);
}
if (m_frameBufferIDCopy) {
glDeleteRenderbuffers(1, &m_renderBufferIDCopy);
glDeleteFramebuffers(1, &m_frameBufferIDCopy);
}
for (auto &i : m_texIDs) {
if (i) {
glDeleteTextures(1, &i);
i = 0;
}
}
m_frameBufferID = 0;
m_renderBufferID = 0;
m_frameBufferIDCopy = 0;
m_renderBufferIDCopy = 0;
m_width = 0;
m_height = 0;
}
GLuint R3DFrameBuffers::CreateTexture(int width, int height)
{
GLuint texId;
glGenTextures(1, &texId);
glBindTexture(GL_TEXTURE_2D, texId);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
return texId;
}
void R3DFrameBuffers::BindTexture(Layer layer)
{
glBindTexture(GL_TEXTURE_2D, m_texIDs[(int)layer]);
}
void R3DFrameBuffers::SetFBO(Layer layer)
{
if (m_lastLayer == layer) {
return;
}
switch (layer)
{
case Layer::colour:
{
glBindFramebuffer(GL_FRAMEBUFFER, m_frameBufferID);
GLenum buffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 };
glDrawBuffers((GLsizei)std::size(buffers), buffers);
break;
}
case Layer::trans1:
{
glBindFramebuffer(GL_FRAMEBUFFER, m_frameBufferID);
GLenum buffers[] = { GL_NONE, GL_COLOR_ATTACHMENT1, GL_NONE };
glDrawBuffers((GLsizei)std::size(buffers), buffers);
break;
}
case Layer::trans2:
{
glBindFramebuffer(GL_FRAMEBUFFER, m_frameBufferID);
GLenum buffers[] = { GL_NONE, GL_NONE, GL_COLOR_ATTACHMENT2 };
glDrawBuffers((GLsizei)std::size(buffers), buffers);
break;
}
case Layer::none:
{
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glDrawBuffer(GL_BACK);
break;
}
}
m_lastLayer = layer;
}
void R3DFrameBuffers::AllocShaderBase()
{
static const char *vertexShader = R"glsl(
#version 410 core
void main(void)
{
const vec4 vertices[] = vec4[](vec4(-1.0, -1.0, 0.0, 1.0),
vec4(-1.0, 1.0, 0.0, 1.0),
vec4( 1.0, -1.0, 0.0, 1.0),
vec4( 1.0, 1.0, 0.0, 1.0));
gl_Position = vertices[gl_VertexID % 4];
}
)glsl";
static const char *fragmentShader = R"glsl(
#version 410 core
// inputs
uniform sampler2D tex1; // base tex
// outputs
out vec4 fragColor;
void main()
{
ivec2 tc = ivec2(gl_FragCoord.xy /*-vec2(0.5)*/);
fragColor = texelFetch(tex1, tc, 0);
}
)glsl";
m_shaderBase.LoadShaders(vertexShader, fragmentShader);
m_shaderBase.uniformLoc[0] = m_shaderBase.GetUniformLocation("tex1");
}
void R3DFrameBuffers::AllocShaderTrans()
{
static const char *vertexShader = R"glsl(
#version 410 core
void main(void)
{
const vec4 vertices[] = vec4[](vec4(-1.0, -1.0, 0.0, 1.0),
vec4(-1.0, 1.0, 0.0, 1.0),
vec4( 1.0, -1.0, 0.0, 1.0),
vec4( 1.0, 1.0, 0.0, 1.0));
gl_Position = vertices[gl_VertexID % 4];
}
)glsl";
static const char *fragmentShader = R"glsl(
#version 410 core
uniform sampler2D tex1; // trans layer 1
uniform sampler2D tex2; // trans layer 2
// outputs
out vec4 fragColor;
void main()
{
ivec2 tc = ivec2(gl_FragCoord.xy /*-vec2(0.5)*/);
vec4 colTrans1 = texelFetch(tex1, tc, 0);
vec4 colTrans2 = texelFetch(tex2, tc, 0);
// if both transparency layers overlap, the result is opaque
if (colTrans1.a * colTrans2.a > 0.0) {
vec3 mixCol = mix(colTrans1.rgb, colTrans2.rgb, (colTrans2.a + (1.0 - colTrans1.a)) / 2.0);
fragColor = vec4(mixCol, 1.0);
}
else if (colTrans1.a > 0.0) {
fragColor = colTrans1;
}
else {
fragColor = colTrans2; // if alpha is zero it will have no effect anyway
}
}
)glsl";
m_shaderTrans.LoadShaders(vertexShader, fragmentShader);
m_shaderTrans.uniformLoc[0] = m_shaderTrans.GetUniformLocation("tex1");
m_shaderTrans.uniformLoc[1] = m_shaderTrans.GetUniformLocation("tex2");
}
void R3DFrameBuffers::Draw()
{
glViewport (0, 0, m_width, m_height); // cover the entire screen
glDisable (GL_DEPTH_TEST); // disable depth testing / writing
glDisable (GL_CULL_FACE);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable (GL_BLEND);
for (int i = 0; i < (int)std::size(m_texIDs); i++) { // bind our textures to correct texture units
glActiveTexture(GL_TEXTURE0 + i);
glBindTexture(GL_TEXTURE_2D, m_texIDs[i]);
}
glActiveTexture (GL_TEXTURE0);
glBindVertexArray (m_vao);
DrawBaseLayer ();
DrawAlphaLayer ();
glDisable (GL_BLEND);
glBindVertexArray (0);
}
void R3DFrameBuffers::DrawBaseLayer()
{
m_shaderBase.EnableShader();
glUniform1i(m_shaderBase.uniformLoc[0], 0);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
m_shaderBase.DisableShader();
}
void R3DFrameBuffers::DrawAlphaLayer()
{
m_shaderTrans.EnableShader();
glUniform1i(m_shaderTrans.uniformLoc[0], 1); // tex unit 1
glUniform1i(m_shaderTrans.uniformLoc[1], 2); // tex unit 2
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
m_shaderTrans.DisableShader();
}
}
+55
View File
@@ -0,0 +1,55 @@
#ifndef FBO_H
#define FBO_H
#include <GL/glew.h>
#include "GLSLShader.h"
#include "Model.h"
namespace New3D {
class R3DFrameBuffers {
public:
R3DFrameBuffers();
~R3DFrameBuffers();
void Draw(); // draw and composite the transparent layers
Result CreateFBO(int width, int height);
void DestroyFBO();
void BindTexture(Layer layer);
void SetFBO(Layer layer);
void StoreDepth();
void RestoreDepth();
private:
Result CreateFBODepthCopy(int width, int height);
GLuint CreateTexture(int width, int height);
void AllocShaderTrans();
void AllocShaderBase();
void DrawBaseLayer();
void DrawAlphaLayer();
GLuint m_frameBufferID;
GLuint m_renderBufferID;
GLuint m_texIDs[3];
GLuint m_frameBufferIDCopy;
GLuint m_renderBufferIDCopy;
Layer m_lastLayer;
int m_width;
int m_height;
// shaders
GLSLShader m_shaderBase;
GLSLShader m_shaderTrans;
// vao
GLuint m_vao; // this really needed if we don't actually use vertex attribs?
};
}
#endif
+96 -95
View File
@@ -1,22 +1,27 @@
#include "R3DScrollFog.h"
#include "Graphics/Shader.h"
#include "Mat4.h"
namespace New3D {
static const char *vertexShaderFog = R"glsl(
static const char* vertexShaderFog = R"glsl(
uniform mat4 mvp;
attribute vec3 inVertex;
#version 410 core
void main(void)
{
gl_Position = mvp * vec4(inVertex,1.0);
};
const vec4 vertices[] = vec4[](vec4(-1.0, -1.0, 0.0, 1.0),
vec4(-1.0, 1.0, 0.0, 1.0),
vec4( 1.0, -1.0, 0.0, 1.0),
vec4( 1.0, 1.0, 0.0, 1.0));
gl_Position = vertices[gl_VertexID % 4];
}
)glsl";
static const char *fragmentShaderFog = R"glsl(
static const char* fragmentShaderFog = R"glsl(
#version 410 core
uniform float fogAttenuation;
uniform float fogAmbient;
@@ -34,6 +39,20 @@ float lfogAttenuation;
vec3 lFogColor;
vec4 scrollFog;
// outputs
layout(location = 0) out vec4 out0; // opaque
layout(location = 1) out vec4 out1; // trans layer 1
layout(location = 2) out vec4 out2; // trans layer 2
void WriteOutputs(vec4 colour)
{
vec4 blank = vec4(0.0);
out0 = colour;
out1 = blank;
out2 = blank;
}
void main()
{
// Scroll fog base color
@@ -43,110 +62,92 @@ void main()
position = spotEllipse.xy;
size = spotEllipse.zw;
ellipse = length((gl_FragCoord.xy - position) / size);
ellipse = pow(ellipse, 2.0); // decay rate = square of distance from center
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);
lSpotFogColor = mix(vec3(0.0), spotFogColor * ellipse * fogColour.rgb, fogAttenuation);
// Scroll fog density
scrollFog = vec4(lFogColor + lSpotFogColor, fogColour.a);
// Final Color
gl_FragColor = scrollFog;
};
WriteOutputs(scrollFog);
}
)glsl";
R3DScrollFog::R3DScrollFog(const Util::Config::Node &config)
: m_config(config)
{
//default coordinates are NDC -1,1 etc
R3DScrollFog::R3DScrollFog(const Util::Config::Node& config)
: m_config(config),
m_vao(0)
{
m_shaderProgram = 0;
m_vertexShader = 0;
m_fragmentShader = 0;
m_triangles[0].p1.Set(-1,-1, 0);
m_triangles[0].p2.Set(-1, 1, 0);
m_triangles[0].p3.Set( 1, 1, 0);
AllocResources();
m_triangles[1].p1.Set(-1,-1, 0);
m_triangles[1].p2.Set( 1, 1, 0);
m_triangles[1].p3.Set( 1,-1, 0);
m_shaderProgram = 0;
m_vertexShader = 0;
m_fragmentShader = 0;
AllocResources();
}
R3DScrollFog::~R3DScrollFog()
{
DeallocResources();
}
void R3DScrollFog::DrawScrollFog(float rgba[4], float attenuation, float ambient, float *spotRGB, float *spotEllipse)
{
//=======
Mat4 mvp;
//=======
// yeah this would have been much easier with immediate mode and fixed function .. >_<
// some ogl states
glDepthMask (GL_FALSE); // disable z writes
glDisable (GL_DEPTH_TEST); // disable depth testing
glEnable (GL_BLEND);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
m_vbo.Bind (true);
glUseProgram (m_shaderProgram);
glUniform4f (m_locFogColour, rgba[0], rgba[1], rgba[2], rgba[3]);
glUniform1f (m_locFogAttenuation, attenuation);
glUniform1f (m_locFogAmbient, ambient);
glUniform3f (m_locSpotFogColor, spotRGB[0], spotRGB[1], spotRGB[2]);
glUniform4f (m_locSpotEllipse, spotEllipse[0], spotEllipse[1], spotEllipse[2], spotEllipse[3]);
glUniformMatrix4fv (m_locMVP, 1, GL_FALSE, mvp);
glEnableVertexAttribArray (0);
glVertexAttribPointer (m_locInVertex, 3, GL_FLOAT, GL_FALSE, sizeof(SFVertex), 0);
glDrawArrays (GL_TRIANGLES, 0, 6);
glDisableVertexAttribArray (0);
glUseProgram (0);
m_vbo.Bind (false);
glDisable (GL_BLEND);
glDepthMask (GL_TRUE);
}
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);
m_locMVP = glGetUniformLocation(m_shaderProgram, "mvp");
m_locFogColour = glGetUniformLocation(m_shaderProgram, "fogColour");
m_locFogAttenuation = glGetUniformLocation(m_shaderProgram, "fogAttenuation");
m_locFogAmbient = glGetUniformLocation(m_shaderProgram, "fogAmbient");
m_locSpotFogColor = glGetUniformLocation(m_shaderProgram, "spotFogColor");
m_locSpotEllipse = glGetUniformLocation(m_shaderProgram, "spotEllipse");
m_locInVertex = glGetAttribLocation(m_shaderProgram, "inVertex");
m_vbo.Create(GL_ARRAY_BUFFER, GL_STATIC_DRAW, sizeof(SFTriangle) * (2), m_triangles);
}
void R3DScrollFog::DeallocResources()
{
if (m_shaderProgram) {
DestroyShaderProgram(m_shaderProgram, m_vertexShader, m_fragmentShader);
glGenVertexArrays(1, &m_vao);
glBindVertexArray(m_vao);
// no states needed since we do it in the shader
glBindVertexArray(0);
}
m_shaderProgram = 0;
m_vertexShader = 0;
m_fragmentShader = 0;
R3DScrollFog::~R3DScrollFog()
{
DeallocResources();
if (m_vao) {
glDeleteVertexArrays(1, &m_vao);
m_vao = 0;
}
}
void R3DScrollFog::DrawScrollFog(float rgba[4], float attenuation, float ambient, float spotRGB[3], float spotEllipse[4])
{
// some ogl states
glDepthMask (GL_FALSE); // disable z writes
glDisable (GL_DEPTH_TEST); // disable depth testing
glBindVertexArray (m_vao);
glUseProgram (m_shaderProgram);
glUniform4fv (m_locFogColour, 1, rgba);
glUniform1f (m_locFogAttenuation, attenuation);
glUniform1f (m_locFogAmbient, ambient);
glUniform3fv (m_locSpotFogColor, 1, spotRGB);
glUniform4fv (m_locSpotEllipse, 1, spotEllipse);
glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
glUseProgram (0);
glBindVertexArray (0);
glDisable (GL_BLEND);
glDepthMask (GL_TRUE);
}
void R3DScrollFog::AllocResources()
{
auto success = LoadShaderProgram(&m_shaderProgram, &m_vertexShader, &m_fragmentShader, "", "", vertexShaderFog, fragmentShaderFog);
m_locFogColour = glGetUniformLocation(m_shaderProgram, "fogColour");
m_locFogAttenuation = glGetUniformLocation(m_shaderProgram, "fogAttenuation");
m_locFogAmbient = glGetUniformLocation(m_shaderProgram, "fogAmbient");
m_locSpotFogColor = glGetUniformLocation(m_shaderProgram, "spotFogColor");
m_locSpotEllipse = glGetUniformLocation(m_shaderProgram, "spotEllipse");
}
void R3DScrollFog::DeallocResources()
{
if (m_shaderProgram) {
DestroyShaderProgram(m_shaderProgram, m_vertexShader, m_fragmentShader);
}
m_shaderProgram = 0;
m_vertexShader = 0;
m_fragmentShader = 0;
}
m_vbo.Destroy();
}
}
+26 -50
View File
@@ -2,63 +2,39 @@
#define _R3DSCROLLFOG_H_
#include "Util/NewConfig.h"
#include "VBO.h"
#include <GL/glew.h>
namespace New3D {
class R3DScrollFog
{
public:
R3DScrollFog(const Util::Config::Node &config);
~R3DScrollFog();
void DrawScrollFog(float rbga[4], float attenuation, float ambient, float *spotRGB, float *spotEllipse);
private:
void AllocResources();
void DeallocResources();
const Util::Config::Node &m_config;
struct SFVertex
class R3DScrollFog
{
void Set(float x, float y, float z) {
v[0] = x;
v[1] = y;
v[2] = z;
}
public:
float v[3];
R3DScrollFog(const Util::Config::Node& config);
~R3DScrollFog();
void DrawScrollFog(float rbga[4], float attenuation, float ambient, float spotRGB[3], float spotEllipse[4]);
private:
void AllocResources();
void DeallocResources();
const Util::Config::Node& m_config;
GLuint m_shaderProgram;
GLuint m_vertexShader;
GLuint m_fragmentShader;
GLint m_locFogColour;
GLint m_locFogAttenuation;
GLint m_locFogAmbient;
GLint m_locSpotFogColor;
GLint m_locSpotEllipse;
GLuint m_vao;
};
struct SFTriangle
{
SFVertex p1;
SFVertex p2;
SFVertex p3;
};
SFTriangle m_triangles[2];
GLuint m_shaderProgram;
GLuint m_vertexShader;
GLuint m_fragmentShader;
GLint m_locFogColour;
GLint m_locMVP;
GLint m_locFogAttenuation;
GLint m_locFogAmbient;
GLint m_locSpotFogColor;
GLint m_locSpotEllipse;
// vertex attrib locs
GLint m_locInVertex;
VBO m_vbo;
};
}
#endif
+281 -363
View File
@@ -1,301 +1,19 @@
#include "R3DShader.h"
#include "Graphics/Shader.h"
#include "R3DShaderQuads.h"
#include "R3DShaderTriangles.h"
#include "R3DShaderCommon.h"
// having 2 sets of shaders to maintain is really less than ideal
// but hopefully not too many breaking changes at this point
namespace New3D {
static const char *vertexShaderR3D = R"glsl(
#version 120
// uniforms
uniform float modelScale;
// attributes
attribute vec4 inVertex;
attribute vec3 inNormal;
attribute vec2 inTexCoord;
attribute vec4 inColour;
attribute vec3 inFaceNormal; // used to emulate r3d culling
attribute float inFixedShade;
// outputs to fragment shader
varying vec3 fsViewVertex;
varying vec3 fsViewNormal; // per vertex normal vector
varying vec2 fsTexCoord;
varying vec4 fsColor;
varying float fsDiscard; // can't have varying bool (glsl spec)
varying float fsFixedShade;
float CalcBackFace(in vec3 viewVertex)
{
vec3 vt = viewVertex - vec3(0.0);
vec3 vn = (mat3(gl_ModelViewMatrix) * inFaceNormal);
// dot product of face normal with view direction
return dot(vt, vn);
}
void main(void)
{
fsViewVertex = vec3(gl_ModelViewMatrix * inVertex);
fsViewNormal = (mat3(gl_ModelViewMatrix) * inNormal) / modelScale;
fsDiscard = CalcBackFace(fsViewVertex);
fsColor = inColour;
fsTexCoord = inTexCoord;
fsFixedShade = inFixedShade;
gl_Position = gl_ModelViewProjectionMatrix * inVertex;
}
)glsl";
static const char *fragmentShaderR3D = R"glsl(
#version 120
uniform sampler2D tex1; // base tex
uniform sampler2D tex2; // micro tex (optional)
// texturing
uniform bool textureEnabled;
uniform bool microTexture;
uniform float microTextureScale;
uniform vec2 baseTexSize;
uniform bool textureInverted;
uniform bool textureAlpha;
uniform bool alphaTest;
uniform bool discardAlpha;
// general
uniform vec3 fogColour;
uniform vec4 spotEllipse; // spotlight ellipse position: .x=X position (screen coordinates), .y=Y position, .z=half-width, .w=half-height)
uniform vec2 spotRange; // spotlight Z range: .x=start (viewspace coordinates), .y=limit
uniform vec3 spotColor; // spotlight RGB color
uniform vec3 spotFogColor; // spotlight RGB color on fog
uniform vec3 lighting[2]; // lighting state (lighting[0] = sun direction, lighting[1].x,y = diffuse, ambient intensities from 0-1.0)
uniform bool lightEnabled; // lighting enabled (1.0) or luminous (0.0), drawn at full intensity
uniform bool sunClamp; // not used by daytona and la machine guns
uniform bool intensityClamp; // some games such as daytona and
uniform bool specularEnabled; // specular enabled
uniform float specularValue; // specular coefficient
uniform float shininess; // specular shininess
uniform float fogIntensity;
uniform float fogDensity;
uniform float fogStart;
uniform float fogAttenuation;
uniform float fogAmbient;
uniform bool fixedShading;
uniform int hardwareStep;
//interpolated inputs from vertex shader
varying float fsFogFactor;
varying vec3 fsViewVertex;
varying vec3 fsViewNormal; // per vertex normal vector
varying vec4 fsColor;
varying vec2 fsTexCoord;
varying float fsDiscard;
varying float fsFixedShade;
vec4 GetTextureValue()
{
vec4 tex1Data = texture2D( tex1, fsTexCoord.st);
if(textureInverted) {
tex1Data.rgb = vec3(1.0) - vec3(tex1Data.rgb);
}
if (microTexture) {
vec2 scale = (baseTexSize / 128.0) * microTextureScale;
vec4 tex2Data = texture2D( tex2, fsTexCoord.st * scale);
tex1Data = (tex1Data+tex2Data)/2.0;
}
if (alphaTest) {
if (tex1Data.a < (8.0/16.0)) {
discard;
}
}
if(discardAlpha && textureAlpha) {
if (tex1Data.a < 1.0) {
discard;
}
}
if (textureAlpha == false) {
tex1Data.a = 1.0;
}
return tex1Data;
}
void Step15Luminous(inout vec4 colour)
{
// luminous polys seem to behave very differently on step 1.5 hardware
// when fixed shading is enabled the colour is modulated by the vp ambient + fixed shade value
// when disabled it appears to be multiplied by 1.5, presumably to allow a higher range
if(hardwareStep==0x15) {
if(!lightEnabled && textureEnabled) {
if(fixedShading) {
colour.rgb *= 1.0 + fsFixedShade + lighting[1].y;
}
else {
colour.rgb *= vec3(1.5);
}
}
}
}
float CalcFog()
{
float z = -fsViewVertex.z;
float fog = fogIntensity * clamp(fogStart + z * fogDensity, 0.0, 1.0);
return fog;
}
void main()
{
vec4 tex1Data;
vec4 colData;
vec4 finalData;
vec4 fogData;
if(fsDiscard>=0) {
discard; //emulate back face culling here
}
fogData = vec4(fogColour.rgb * fogAmbient, CalcFog());
tex1Data = vec4(1.0, 1.0, 1.0, 1.0);
if(textureEnabled) {
tex1Data = GetTextureValue();
}
colData = fsColor;
Step15Luminous(colData); // no-op for step 2.0+
finalData = tex1Data * colData;
if (finalData.a < (1.0/16.0)) { // basically chuck out any totally transparent pixels value = 1/16 the smallest transparency level h/w supports
discard;
}
float ellipse;
ellipse = length((gl_FragCoord.xy - spotEllipse.xy) / spotEllipse.zw);
ellipse = pow(ellipse, 2.0); // decay rate = square of distance from center
ellipse = 1.0 - ellipse; // invert
ellipse = max(0.0, ellipse); // clamp
// Compute spotlight and apply lighting
float enable, absExtent, d, inv_r, range;
// start of spotlight
enable = step(spotRange.x, -fsViewVertex.z);
if (spotRange.y == 0.0) {
range = 0.0;
}
else {
absExtent = abs(spotRange.y);
d = spotRange.x + absExtent + fsViewVertex.z;
d = min(d, 0.0);
// slope of decay function
inv_r = 1.0 / (1.0 + absExtent);
// inverse-linear falloff
// Reference: https://imdoingitwrong.wordpress.com/2011/01/31/light-attenuation/
// y = 1 / (d/r + 1)^2
range = 1.0 / pow(d * inv_r - 1.0, 2.0);
range *= enable;
}
float lobeEffect = range * ellipse;
float lobeFogEffect = enable * ellipse;
if (lightEnabled) {
vec3 lightIntensity;
vec3 sunVector; // sun lighting vector (as reflecting away from vertex)
float sunFactor; // sun light projection along vertex normal (0.0 to 1.0)
// Sun angle
sunVector = lighting[0];
// Compute diffuse factor for sunlight
if(fixedShading) {
sunFactor = fsFixedShade;
}
else {
sunFactor = dot(sunVector, fsViewNormal);
}
// Clamp ceil, fix for upscaled models without "modelScale" defined
sunFactor = clamp(sunFactor,-1.0,1.0);
// Optional clamping, value is allowed to be negative
if(sunClamp) {
sunFactor = max(sunFactor,0.0);
}
// Total light intensity: sum of all components
lightIntensity = vec3(sunFactor*lighting[1].x + lighting[1].y); // diffuse + ambient
lightIntensity.rgb += spotColor*lobeEffect;
// Upper clamp is optional, step 1.5+ games will drive brightness beyond 100%
if(intensityClamp) {
lightIntensity = min(lightIntensity,1.0);
}
finalData.rgb *= lightIntensity;
// for now assume fixed shading doesn't work with specular
if (specularEnabled) {
float exponent, NdotL, specularFactor;
vec4 biasIndex, expIndex, multIndex;
// Always clamp floor to zero, we don't want deep black areas
NdotL = max(0.0,sunFactor);
expIndex = vec4(8.0, 16.0, 32.0, 64.0);
multIndex = vec4(2.0, 2.0, 3.0, 4.0);
biasIndex = vec4(0.95, 0.95, 1.05, 1.0);
exponent = expIndex[int(shininess)] / biasIndex[int(shininess)];
specularFactor = pow(NdotL, exponent);
specularFactor *= multIndex[int(shininess)];
specularFactor *= biasIndex[int(shininess)];
specularFactor *= specularValue;
specularFactor *= lighting[1].x;
if (colData.a < 1.0) {
/// Specular hi-light affects translucent polygons alpha channel ///
finalData.a = max(finalData.a, specularFactor);
}
finalData.rgb += vec3(specularFactor);
}
}
// Final clamp: we need it for proper shading in dimmed light and dark ambients
finalData.rgb = min(finalData.rgb, vec3(1.0));
// Spotlight on fog
vec3 lSpotFogColor = spotFogColor * fogAttenuation * fogColour.rgb * lobeFogEffect;
// Fog & spotlight applied
finalData.rgb = mix(finalData.rgb, fogData.rgb + lSpotFogColor, fogData.a);
gl_FragColor = finalData;
}
)glsl";
R3DShader::R3DShader(const Util::Config::Node &config)
: m_config(config)
: m_config(config)
{
m_shaderProgram = 0;
m_vertexShader = 0;
m_geoShader = 0;
m_fragmentShader = 0;
Start(); // reset attributes
@@ -310,83 +28,167 @@ void R3DShader::Start()
m_lightEnabled = false;
m_specularEnabled = false;
m_layered = false;
m_noLosReturn = false;
m_textureInverted = false;
m_fixedShading = false;
m_smoothShading = false;
m_translatorMap = false;
m_modelScale = 1.0f;
m_nodeAlpha = 1.0f;
m_shininess = 0;
m_specularValue = 0;
m_microTexScale = 0;
m_microTexMinLOD = 0;
m_fogIntensity = 0.0f;
m_microTexID = -1;
m_texturePage = -1;
m_baseTexSize[0] = 0;
m_baseTexSize[1] = 0;
m_baseTexInfo[0] = -1;
m_baseTexInfo[1] = -1;
m_baseTexInfo[2] = -1;
m_baseTexInfo[3] = -1;
m_dirtyMesh = true; // dirty means all the above are dirty, ie first run
m_dirtyModel = true;
m_baseTexType = -1;
m_transX = -1;
m_transY = -1;
m_transPage = -1;
m_texWrapMode[0] = 0;
m_texWrapMode[1] = 0;
m_dirtyMesh = true; // dirty means all the above are dirty, ie first run
m_dirtyModel = true;
}
bool R3DShader::LoadShader(const char* vertexShader, const char* fragmentShader)
{
const char* vShader;
const char* fShader;
bool success;
bool quads = m_config["QuadRendering"].ValueAs<bool>();
if (vertexShader) {
vShader = vertexShader;
}
else {
vShader = vertexShaderR3D;
const char* vShader = vertexShaderR3D;
const char* gShader = "";
const char* fShader = fragmentShaderR3D;
if (quads) {
vShader = vertexShaderR3DQuads;
gShader = geometryShaderR3DQuads;
fShader = fragmentShaderR3DQuads;
}
if (fragmentShader) {
fShader = fragmentShader;
}
else {
fShader = fragmentShaderR3D;
m_shaderProgram = glCreateProgram();
m_vertexShader = glCreateShader(GL_VERTEX_SHADER);
m_fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
const char* shaderArray[] = { fShader, fragmentShaderR3DCommon };
glShaderSource(m_vertexShader, 1, (const GLchar **)&vShader, nullptr);
glShaderSource(m_fragmentShader, (GLsizei)std::size(shaderArray), shaderArray, nullptr);
glCompileShader(m_vertexShader);
glCompileShader(m_fragmentShader);
if (quads) {
m_geoShader = glCreateShader(GL_GEOMETRY_SHADER);
glShaderSource(m_geoShader, 1, (const GLchar **)&gShader, nullptr);
glCompileShader(m_geoShader);
glAttachShader(m_shaderProgram, m_geoShader);
PrintShaderResult(m_geoShader);
}
success = LoadShaderProgram(&m_shaderProgram, &m_vertexShader, &m_fragmentShader, m_config["VertexShader"].ValueAs<std::string>(), m_config["FragmentShader"].ValueAs<std::string>(), vShader, fShader);
PrintShaderResult(m_vertexShader);
PrintShaderResult(m_fragmentShader);
m_locTexture1 = glGetUniformLocation(m_shaderProgram, "tex1");
m_locTexture2 = glGetUniformLocation(m_shaderProgram, "tex2");
m_locTexture1Enabled= glGetUniformLocation(m_shaderProgram, "textureEnabled");
m_locTexture2Enabled= glGetUniformLocation(m_shaderProgram, "microTexture");
m_locTextureAlpha = glGetUniformLocation(m_shaderProgram, "textureAlpha");
m_locAlphaTest = glGetUniformLocation(m_shaderProgram, "alphaTest");
m_locMicroTexScale = glGetUniformLocation(m_shaderProgram, "microTextureScale");
m_locBaseTexSize = glGetUniformLocation(m_shaderProgram, "baseTexSize");
m_locTextureInverted= glGetUniformLocation(m_shaderProgram, "textureInverted");
glAttachShader(m_shaderProgram, m_vertexShader);
glAttachShader(m_shaderProgram, m_fragmentShader);
glLinkProgram(m_shaderProgram);
m_locFogIntensity = glGetUniformLocation(m_shaderProgram, "fogIntensity");
m_locFogDensity = glGetUniformLocation(m_shaderProgram, "fogDensity");
m_locFogStart = glGetUniformLocation(m_shaderProgram, "fogStart");
m_locFogColour = glGetUniformLocation(m_shaderProgram, "fogColour");
m_locFogAttenuation = glGetUniformLocation(m_shaderProgram, "fogAttenuation");
m_locFogAmbient = glGetUniformLocation(m_shaderProgram, "fogAmbient");
PrintProgramResult(m_shaderProgram);
m_locLighting = glGetUniformLocation(m_shaderProgram, "lighting");
m_locLightEnabled = glGetUniformLocation(m_shaderProgram, "lightEnabled");
m_locSunClamp = glGetUniformLocation(m_shaderProgram, "sunClamp");
m_locIntensityClamp = glGetUniformLocation(m_shaderProgram, "intensityClamp");
m_locShininess = glGetUniformLocation(m_shaderProgram, "shininess");
m_locSpecularValue = glGetUniformLocation(m_shaderProgram, "specularValue");
m_locSpecularEnabled= glGetUniformLocation(m_shaderProgram, "specularEnabled");
m_locFixedShading = glGetUniformLocation(m_shaderProgram, "fixedShading");
m_locTextureBank[0] = glGetUniformLocation(m_shaderProgram, "textureBank[0]");
m_locTextureBank[1] = glGetUniformLocation(m_shaderProgram, "textureBank[1]");
m_locTexturePage = glGetUniformLocation(m_shaderProgram, "texturePage");
m_locTexture1Enabled = glGetUniformLocation(m_shaderProgram, "textureEnabled");
m_locTexture2Enabled = glGetUniformLocation(m_shaderProgram, "microTexture");
m_locTextureAlpha = glGetUniformLocation(m_shaderProgram, "textureAlpha");
m_locAlphaTest = glGetUniformLocation(m_shaderProgram, "alphaTest");
m_locMicroTexMinLOD = glGetUniformLocation(m_shaderProgram, "microTextureMinLOD");
m_locMicroTexID = glGetUniformLocation(m_shaderProgram, "microTextureID");
m_locBaseTexInfo = glGetUniformLocation(m_shaderProgram, "baseTexInfo");
m_locBaseTexType = glGetUniformLocation(m_shaderProgram, "baseTexType");
m_locTextureInverted = glGetUniformLocation(m_shaderProgram, "textureInverted");
m_locTexWrapMode = glGetUniformLocation(m_shaderProgram, "textureWrapMode");
m_locColourLayer = glGetUniformLocation(m_shaderProgram, "colourLayer");
m_locPolyAlpha = glGetUniformLocation(m_shaderProgram, "polyAlpha");
m_locSpotEllipse = glGetUniformLocation(m_shaderProgram, "spotEllipse");
m_locSpotRange = glGetUniformLocation(m_shaderProgram, "spotRange");
m_locSpotColor = glGetUniformLocation(m_shaderProgram, "spotColor");
m_locSpotFogColor = glGetUniformLocation(m_shaderProgram, "spotFogColor");
m_locModelScale = glGetUniformLocation(m_shaderProgram, "modelScale");
m_locFogIntensity = glGetUniformLocation(m_shaderProgram, "fogIntensity");
m_locFogDensity = glGetUniformLocation(m_shaderProgram, "fogDensity");
m_locFogStart = glGetUniformLocation(m_shaderProgram, "fogStart");
m_locFogColour = glGetUniformLocation(m_shaderProgram, "fogColour");
m_locFogAttenuation = glGetUniformLocation(m_shaderProgram, "fogAttenuation");
m_locFogAmbient = glGetUniformLocation(m_shaderProgram, "fogAmbient");
m_locHardwareStep = glGetUniformLocation(m_shaderProgram, "hardwareStep");
m_locDiscardAlpha = glGetUniformLocation(m_shaderProgram, "discardAlpha");
return success;
m_locLighting = glGetUniformLocation(m_shaderProgram, "lighting");
m_locLightEnabled = glGetUniformLocation(m_shaderProgram, "lightEnabled");
m_locSunClamp = glGetUniformLocation(m_shaderProgram, "sunClamp");
m_locIntensityClamp = glGetUniformLocation(m_shaderProgram, "intensityClamp");
m_locShininess = glGetUniformLocation(m_shaderProgram, "shininess");
m_locSpecularValue = glGetUniformLocation(m_shaderProgram, "specularValue");
m_locSpecularEnabled = glGetUniformLocation(m_shaderProgram, "specularEnabled");
m_locFixedShading = glGetUniformLocation(m_shaderProgram, "fixedShading");
m_locSmoothShading = glGetUniformLocation(m_shaderProgram, "smoothShading");
m_locTranslatorMap = glGetUniformLocation(m_shaderProgram, "translatorMap");
m_locSpotEllipse = glGetUniformLocation(m_shaderProgram, "spotEllipse");
m_locSpotRange = glGetUniformLocation(m_shaderProgram, "spotRange");
m_locSpotColor = glGetUniformLocation(m_shaderProgram, "spotColor");
m_locSpotFogColor = glGetUniformLocation(m_shaderProgram, "spotFogColor");
m_locModelScale = glGetUniformLocation(m_shaderProgram, "modelScale");
m_locNodeAlpha = glGetUniformLocation(m_shaderProgram, "nodeAlpha");
m_locProjMat = glGetUniformLocation(m_shaderProgram, "projMat");
m_locModelMat = glGetUniformLocation(m_shaderProgram, "modelMat");
m_locHardwareStep = glGetUniformLocation(m_shaderProgram, "hardwareStep");
m_locDiscardAlpha = glGetUniformLocation(m_shaderProgram, "discardAlpha");
m_locCota = glGetUniformLocation(m_shaderProgram, "cota");
return true;
}
GLint R3DShader::GetVertexAttribPos(const char* attrib)
void R3DShader::UnloadShader()
{
return glGetAttribLocation(m_shaderProgram, attrib); // probably should cache this but only called 1x per frame anyway
// make sure no shader is bound
glUseProgram(0);
if (m_vertexShader) {
glDeleteShader(m_vertexShader);
m_vertexShader = 0;
}
if (m_geoShader) {
glDeleteShader(m_geoShader);
m_geoShader = 0;
}
if (m_fragmentShader) {
glDeleteShader(m_fragmentShader);
m_fragmentShader = 0;
}
if (m_shaderProgram) {
glDeleteProgram(m_shaderProgram);
m_shaderProgram = 0;
}
}
GLint R3DShader::GetVertexAttribPos(const std::string& attrib)
{
if (m_vertexLocCache.count(attrib)==0) {
auto pos = glGetAttribLocation(m_shaderProgram, attrib.c_str());
m_vertexLocCache[attrib] = pos;
}
return m_vertexLocCache[attrib];
}
void R3DShader::SetShader(bool enable)
@@ -408,8 +210,8 @@ void R3DShader::SetMeshUniforms(const Mesh* m)
}
if (m_dirtyMesh) {
glUniform1i(m_locTexture1, 0);
glUniform1i(m_locTexture2, 1);
glUniform1i(m_locTextureBank[0], 0);
glUniform1i(m_locTextureBank[1], 1);
}
if (m_dirtyMesh || m->textured != m_textured1) {
@@ -422,15 +224,34 @@ void R3DShader::SetMeshUniforms(const Mesh* m)
m_textured2 = m->microTexture;
}
if (m_dirtyMesh || m->microTextureScale != m_microTexScale) {
glUniform1f(m_locMicroTexScale, m->microTextureScale);
m_microTexScale = m->microTextureScale;
if (m_dirtyMesh || (m->page ^ m_transPage) != m_texturePage) {
glUniform1i(m_locTexturePage, m->page ^ m_transPage);
m_texturePage = (m->page ^ m_transPage);
}
if (m_dirtyMesh || m->microTexture && (m_baseTexSize[0] != m->width || m_baseTexSize[1] != m->height)) {
m_baseTexSize[0] = (float)m->width;
m_baseTexSize[1] = (float)m->height;
glUniform2fv(m_locBaseTexSize, 1, m_baseTexSize);
if (m_dirtyMesh || m->microTextureMinLOD != m_microTexMinLOD) {
glUniform1f(m_locMicroTexMinLOD, m->microTextureMinLOD);
m_microTexMinLOD = m->microTextureMinLOD;
}
if (m_dirtyMesh || m->microTextureID != m_microTexID) {
glUniform1i(m_locMicroTexID, m->microTextureID);
m_microTexID = m->microTextureID;
}
if (m_dirtyMesh || (m_baseTexInfo[0] != m->x || m_baseTexInfo[1] != m->y) || m_baseTexInfo[2] != m->width || m_baseTexInfo[3] != m->height) {
m_baseTexInfo[0] = m->x;
m_baseTexInfo[1] = m->y;
m_baseTexInfo[2] = m->width;
m_baseTexInfo[3] = m->height;
glUniform4i(m_locBaseTexInfo, (m->x + m_transX), (m->y + m_transY), m->width, m->height);
}
if (m_dirtyMesh || m_baseTexType != m->format) {
m_baseTexType = m->format;
glUniform1i(m_locBaseTexType, m_baseTexType);
}
if (m_dirtyMesh || m->inverted != m_textureInverted) {
@@ -478,13 +299,46 @@ void R3DShader::SetMeshUniforms(const Mesh* m)
m_fixedShading = m->fixedShading;
}
if (m_dirtyMesh || m->smoothShading != m_smoothShading) {
glUniform1i(m_locSmoothShading, m->smoothShading);
m_smoothShading = m->smoothShading;
}
if (m_dirtyMesh || m->translatorMap != m_translatorMap) {
glUniform1i(m_locTranslatorMap, m->translatorMap);
m_translatorMap = m->translatorMap;
}
if (m_dirtyMesh || m->polyAlpha != m_polyAlpha) {
glUniform1i(m_locPolyAlpha, m->polyAlpha);
m_polyAlpha = m->polyAlpha;
}
if (m_dirtyMesh || m->wrapModeU != m_texWrapMode[0] || m->wrapModeV != m_texWrapMode[1]) {
m_texWrapMode[0] = m->wrapModeU;
m_texWrapMode[1] = m->wrapModeV;
glUniform2iv(m_locTexWrapMode, 1, m_texWrapMode);
}
if (m_dirtyMesh || m->noLosReturn != m_noLosReturn) {
m_noLosReturn = m->noLosReturn;
glStencilFunc(GL_ALWAYS, m_noLosReturn << 7, 0b10000000);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
glStencilMask(0b10000000);
}
if (m_dirtyMesh || m->layered != m_layered) {
m_layered = m->layered;
// i think it should just disable z write, but the polys I think must be written first
if (m_layered) {
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_EQUAL, 0, 0b01111111); // basically stencil test passes if the value is zero
glStencilOp(GL_KEEP, GL_INCR, GL_INCR); // if the stencil test passes, we increment the value
glStencilMask(0b01111111);
}
else {
glDisable(GL_STENCIL_TEST);
glStencilFunc(GL_ALWAYS, m_noLosReturn << 7, 0b10000000);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
glStencilMask(0b10000000);
}
}
@@ -492,23 +346,27 @@ void R3DShader::SetMeshUniforms(const Mesh* m)
}
void R3DShader::SetViewportUniforms(const Viewport *vp)
{
{
//didn't bother caching these, they don't get frequently called anyway
glUniform1f (m_locFogDensity, vp->fogParams[3]);
glUniform1f (m_locFogStart, vp->fogParams[4]);
glUniform3fv(m_locFogColour, 1, vp->fogParams);
glUniform1f (m_locFogAttenuation, vp->fogParams[5]);
glUniform1f (m_locFogAmbient, vp->fogParams[6]);
glUniform1f(m_locFogDensity, vp->fogDensity);
glUniform1f(m_locFogStart, vp->fogStart);
glUniform3fv(m_locFogColour, 1, vp->fogColour);
glUniform1f(m_locFogAttenuation, vp->fogAttenuation);
glUniform1f(m_locFogAmbient, vp->fogAmbient);
glUniform3fv(m_locLighting, 2, vp->lightingParams);
glUniform1i (m_locSunClamp, vp->sunClamp);
glUniform1i (m_locIntensityClamp, vp->intensityClamp);
glUniform1i(m_locSunClamp, vp->sunClamp);
glUniform1i(m_locIntensityClamp, vp->intensityClamp);
glUniform4fv(m_locSpotEllipse, 1, vp->spotEllipse);
glUniform2fv(m_locSpotRange, 1, vp->spotRange);
glUniform3fv(m_locSpotColor, 1, vp->spotColor);
glUniform3fv(m_locSpotFogColor, 1, vp->spotFogColor);
glUniform1i (m_locHardwareStep, vp->hardwareStep);
glUniformMatrix4fv(m_locProjMat, 1, GL_FALSE, vp->projectionMatrix);
glUniform1i(m_locHardwareStep, vp->hardwareStep);
glUniform1f(m_locCota, vp->cota);
}
void R3DShader::SetModelStates(const Model* model)
@@ -518,6 +376,20 @@ void R3DShader::SetModelStates(const Model* model)
m_modelScale = model->scale;
}
if (m_dirtyModel || model->alpha != m_nodeAlpha) {
glUniform1f(m_locNodeAlpha, model->alpha);
m_nodeAlpha = model->alpha;
}
m_transX = model->textureOffsetX;
m_transY = model->textureOffsetY;
m_transPage = model->page;
// reset texture values
for (auto& i : m_baseTexInfo) { i = -1; }
glUniformMatrix4fv(m_locModelMat, 1, GL_FALSE, model->modelMat);
m_dirtyModel = false;
}
@@ -526,4 +398,50 @@ void R3DShader::DiscardAlpha(bool discard)
glUniform1i(m_locDiscardAlpha, discard);
}
void R3DShader::SetLayer(Layer layer)
{
glUniform1i(m_locColourLayer, (GLint)layer);
}
void R3DShader::PrintShaderResult(GLuint shader)
{
//===========
GLint result;
GLint length;
//===========
glGetShaderiv(shader, GL_COMPILE_STATUS, &result);
if (result == GL_FALSE) {
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length);
if (length > 0) {
std::vector<char> msg(length);
glGetShaderInfoLog(shader, length, NULL, msg.data());
printf("%s\n", msg.data());
}
}
}
void R3DShader::PrintProgramResult(GLuint program)
{
//===========
GLint result;
//===========
glGetProgramiv(program, GL_LINK_STATUS, &result);
if (result == GL_FALSE) {
GLint maxLength = 0;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength);
//The maxLength includes the NULL character
std::vector<GLchar> infoLog(maxLength);
glGetProgramInfoLog(program, maxLength, &maxLength, infoLog.data());
printf("%s\n", infoLog.data());
}
}
} // New3D
+48 -12
View File
@@ -1,9 +1,11 @@
#ifndef _R3DSHADER_H_
#define _R3DSHADER_H_
#include "Pkgs/glew.h"
#include <GL/glew.h>
#include "Util/NewConfig.h"
#include "Model.h"
#include <map>
#include <string>
namespace New3D {
@@ -13,34 +15,46 @@ public:
R3DShader(const Util::Config::Node &config);
bool LoadShader (const char* vertexShader = nullptr, const char* fragmentShader = nullptr);
void UnloadShader ();
void SetMeshUniforms (const Mesh* m);
void SetModelStates (const Model* model);
void SetViewportUniforms (const Viewport *vp);
void Start ();
void SetShader (bool enable = true);
GLint GetVertexAttribPos (const char* attrib);
GLint GetVertexAttribPos (const std::string& attrib);
void DiscardAlpha (bool discard); // use to remove alpha from texture alpha only polys for 1st pass
void SetLayer (Layer layer);
private:
void PrintShaderResult(GLuint shader);
void PrintProgramResult(GLuint program);
// run-time config
const Util::Config::Node &m_config;
// shader IDs
GLuint m_shaderProgram;
GLuint m_vertexShader;
GLuint m_geoShader;
GLuint m_fragmentShader;
// mesh uniform locations
GLint m_locTexture1;
GLint m_locTexture2;
GLint m_locTexture1Enabled;
GLint m_locTexture2Enabled;
GLint m_locTextureBank[2]; // 2 banks
GLint m_locTexture1Enabled; // base texture
GLint m_locTexture2Enabled; // micro texture
GLint m_locTexturePage;
GLint m_locTextureAlpha;
GLint m_locAlphaTest;
GLint m_locMicroTexScale;
GLint m_locBaseTexSize;
GLint m_locMicroTexMinLOD;
GLint m_locMicroTexID;
GLint m_locBaseTexInfo;
GLint m_locBaseTexType;
GLint m_locTextureInverted;
GLint m_locTexWrapMode;
GLint m_locTranslatorMap;
GLint m_locColourLayer;
GLint m_locPolyAlpha;
// cached mesh values
bool m_textured1;
@@ -53,14 +67,26 @@ private:
float m_specularValue;
bool m_specularEnabled;
bool m_fixedShading;
bool m_smoothShading;
bool m_translatorMap;
bool m_polyAlpha;
int m_texturePage;
bool m_layered;
float m_microTexScale;
float m_baseTexSize[2];
bool m_noLosReturn;
float m_microTexMinLOD;
int m_microTexID;
int m_baseTexInfo[4];
int m_baseTexType;
int m_texWrapMode[2];
bool m_textureInverted;
// cached model values
float m_modelScale;
float m_nodeAlpha;
int m_transX;
int m_transY;
int m_transPage;
// are our cache values dirty
bool m_dirtyMesh;
@@ -73,6 +99,8 @@ private:
GLint m_locFogColour;
GLint m_locFogAttenuation;
GLint m_locFogAmbient;
GLint m_locProjMat;
GLint m_locCota;
// lighting / other
GLint m_locLighting;
@@ -83,20 +111,28 @@ private:
GLint m_locSpecularValue;
GLint m_locSpecularEnabled;
GLint m_locFixedShading;
GLint m_locSmoothShading;
GLint m_locSpotEllipse;
GLint m_locSpotRange;
GLint m_locSpotColor;
GLint m_locSpotFogColor;
// model uniforms
GLint m_locModelScale;
GLint m_locNodeAlpha;
GLint m_locModelMat;
// global uniforms
GLint m_locHardwareStep;
GLint m_locDiscardAlpha;
// vertex attribute position cache
std::map<std::string, GLint> m_vertexLocCache;
};
} // New3D
#endif
+374
View File
@@ -0,0 +1,374 @@
#pragma once
// I altered this code a bit to make sure it always compiles with gl 4.1. Version 4.5 allows you to specify arrays differently.
// Ripped out most of the common code, people have been pushing changes to the shaders but we are ending up with diverging implementations
// between triangle / quad version which is less than ideal.
static const char* fragmentShaderR3DCommon = R"glsl(
#define LayerColour 0x0
#define LayerTrans0 0x1
#define LayerTrans1 0x2
vec4 ExtractColour(int type, uint value)
{
vec4 c = vec4(0.0);
if(type==0) { // T1RGB5
c.r = float((value >> 10) & 0x1Fu);
c.g = float((value >> 5 ) & 0x1Fu);
c.b = float((value ) & 0x1Fu);
c.rgb *= (1.0/31.0);
c.a = 1.0 - float((value >> 15) & 0x1u);
}
else if(type==1) { // Interleaved A4L4 (low byte)
c.rgb = vec3(float(value&0xFu));
c.a = float((value >> 4) & 0xFu);
c *= (1.0/15.0);
}
else if(type==2) {
c.a = float(value&0xFu);
c.rgb = vec3(float((value >> 4) & 0xFu));
c *= (1.0/15.0);
}
else if(type==3) {
c.rgb = vec3(float((value>>8)&0xFu));
c.a = float((value >> 12) & 0xFu);
c *= (1.0/15.0);
}
else if(type==4) {
c.a = float((value>>8)&0xFu);
c.rgb = vec3(float((value >> 12) & 0xFu));
c *= (1.0/15.0);
}
else if(type==5) {
c = vec4(float(value&0xFFu) / 255.0);
if(c.a==1.0) { c.a = 0.0; }
else { c.a = 1.0; }
}
else if(type==6) {
c = vec4(float((value>>8)&0xFFu) / 255.0);
if(c.a==1.0) { c.a = 0.0; }
else { c.a = 1.0; }
}
else if(type==7) { // RGBA4
c.r = float((value>>12)&0xFu);
c.g = float((value>> 8)&0xFu);
c.b = float((value>> 4)&0xFu);
c.a = float((value>> 0)&0xFu);
c *= (1.0/15.0);
}
else if(type==8) { // low byte, low nibble
c = vec4(float(value&0xFu) / 15.0);
if(c.a==1.0) { c.a = 0.0; }
else { c.a = 1.0; }
}
else if(type==9) { // low byte, high nibble
c = vec4(float((value>>4)&0xFu) / 15.0);
if(c.a==1.0) { c.a = 0.0; }
else { c.a = 1.0; }
}
else if(type==10) { // high byte, low nibble
c = vec4(float((value>>8)&0xFu) / 15.0);
if(c.a==1.0) { c.a = 0.0; }
else { c.a = 1.0; }
}
else if(type==11) { // high byte, high nibble
c = vec4(float((value>>12)&0xFu) / 15.0);
if(c.a==1.0) { c.a = 0.0; }
else { c.a = 1.0; }
}
return c;
}
// hardware testing would be useful because i don't know exactly what happens if you try to read outside the texture sheet
// wrap around is a good guess
ivec2 WrapTexCoords(ivec2 pos, ivec2 coordinate, int level)
{
ivec2 newCoord;
newCoord.x = coordinate.x & (2047 >> level);
newCoord.y = coordinate.y & (1023 >> level);
return newCoord;
}
ivec2 GetTextureSize(int level, ivec2 size)
{
int mipDivisor = 1 << level;
return size / mipDivisor;
}
ivec2 GetTexturePosition(int level, ivec2 pos)
{
int mipDivisor = 1 << level;
ivec2 retPos;
retPos.x = pos.x / mipDivisor;
retPos.y = pos.y / mipDivisor;
return retPos;
}
ivec2 GetMicroTexturePos(int id)
{
const int xCoords[8] = int[](0, 0, 128, 128, 0, 0, 128, 128);
const int yCoords[8] = int[](0, 128, 0, 128, 256, 384, 256, 384);
return ivec2(xCoords[id],yCoords[id]);
}
float LinearTexLocations(int wrapMode, float size, float u, out float u0, out float u1)
{
float texelSize = 1.0 / size;
float halfTexelSize = 0.5 / size;
if(wrapMode==0) { // repeat
u = u * size - 0.5;
u0 = (floor(u) + 0.5) / size; // + 0.5 offset added to push us into the centre of a pixel, without we'll get rounding errors
u0 = fract(u0);
u1 = u0 + texelSize;
u1 = fract(u1);
return fract(u); // return weight
}
else if(wrapMode==1) { // repeat + clamp
u = fract(u); // must force into 0-1 to start
u = u * size - 0.5;
u0 = (floor(u) + 0.5) / size; // + 0.5 offset added to push us into the centre of a pixel, without we'll get rounding errors
u1 = u0 + texelSize;
if(u0 < 0.0) u0 = 0.0;
if(u1 >= 1.0) u1 = 1.0 - halfTexelSize;
return fract(u); // return weight
}
else { // mirror + mirror clamp - both are the same since the edge pixels are repeated anyway
float odd = floor(mod(u, 2.0)); // odd values are mirrored
if(odd > 0.0) {
u = 1.0 - fract(u);
}
else {
u = fract(u);
}
u = u * size - 0.5;
u0 = (floor(u) + 0.5) / size; // + 0.5 offset added to push us into the centre of a pixel, without we'll get rounding errors
u1 = u0 + texelSize;
if(u0 < 0.0) u0 = 0.0;
if(u1 >= 1.0) u1 = 1.0 - halfTexelSize;
return fract(u); // return weight
}
}
vec4 texBiLinear(usampler2D texSampler, ivec2 wrapMode, vec2 texSize, ivec2 texPos, vec2 texCoord, int level)
{
float tx[2], ty[2];
float a = LinearTexLocations(wrapMode.s, texSize.x, texCoord.x, tx[0], tx[1]);
float b = LinearTexLocations(wrapMode.t, texSize.y, texCoord.y, ty[0], ty[1]);
vec4 p0q0 = ExtractColour(baseTexType,texelFetch(texSampler, WrapTexCoords(texPos,ivec2(vec2(tx[0],ty[0]) * texSize + texPos),level), level).r);
vec4 p1q0 = ExtractColour(baseTexType,texelFetch(texSampler, WrapTexCoords(texPos,ivec2(vec2(tx[1],ty[0]) * texSize + texPos),level), level).r);
vec4 p0q1 = ExtractColour(baseTexType,texelFetch(texSampler, WrapTexCoords(texPos,ivec2(vec2(tx[0],ty[1]) * texSize + texPos),level), level).r);
vec4 p1q1 = ExtractColour(baseTexType,texelFetch(texSampler, WrapTexCoords(texPos,ivec2(vec2(tx[1],ty[1]) * texSize + texPos),level), level).r);
if(alphaTest) {
if(p0q0.a > p1q0.a) { p1q0.rgb = p0q0.rgb; }
if(p0q0.a > p0q1.a) { p0q1.rgb = p0q0.rgb; }
if(p1q0.a > p0q0.a) { p0q0.rgb = p1q0.rgb; }
if(p1q0.a > p1q1.a) { p1q1.rgb = p1q0.rgb; }
if(p0q1.a > p0q0.a) { p0q0.rgb = p0q1.rgb; }
if(p0q1.a > p1q1.a) { p1q1.rgb = p0q1.rgb; }
if(p1q1.a > p0q1.a) { p0q1.rgb = p1q1.rgb; }
if(p1q1.a > p1q0.a) { p1q0.rgb = p1q1.rgb; }
}
// Interpolation in X direction.
vec4 pInterp_q0 = mix( p0q0, p1q0, a ); // Interpolates top row in X direction.
vec4 pInterp_q1 = mix( p0q1, p1q1, a ); // Interpolates bottom row in X direction.
return mix( pInterp_q0, pInterp_q1, b ); // Interpolate in Y direction.
}
vec4 GetTextureValue()
{
float lod = -log2(gl_FragCoord.w * gl_FragCoord.w * fsLODBase);
float numLevels = floor(log2(min(float(baseTexInfo.z), float(baseTexInfo.w)))) - 1.0; // r3d only generates down to 2:2 for square textures, otherwise its the min dimension
float fLevel = clamp(lod, 0.0, numLevels);
int iLevel = int(fLevel);
ivec2 tex1Pos = GetTexturePosition(iLevel, baseTexInfo.xy);
ivec2 tex1Size = GetTextureSize(iLevel, baseTexInfo.zw);
vec4 tex1Data = texBiLinear(textureBank[texturePage], textureWrapMode, vec2(tex1Size), tex1Pos, fsTexCoord, iLevel);
// init second texel with blank data to avoid any potentially undefined behavior
vec4 tex2Data = vec4(0.0);
float blendFactor = 0.0;
// if LOD < 0, no need to blend with next mipmap level; slight performance boost
// while at it, just generalize to all cases where only one mip level needs to be touched
float ffL = fract(fLevel);
if (ffL > 0.0)
{
ivec2 tex2Pos = GetTexturePosition(iLevel+1, baseTexInfo.xy);
ivec2 tex2Size = GetTextureSize(iLevel+1, baseTexInfo.zw);
tex2Data = texBiLinear(textureBank[texturePage], textureWrapMode, vec2(tex2Size), tex2Pos, fsTexCoord, iLevel+1);
blendFactor = ffL;
}
else if (microTexture && lod < 0.0)
{
vec4 scaleIndex = vec4(2.0, 4.0, 16.0, 256.0); // unsure if minLOD=4 has 256x scale? No games appear to use it
vec2 scale = (vec2(baseTexInfo.zw) / 128.0) * scaleIndex[int(microTextureMinLOD)];
// microtextures are always 128x128 and only use LOD 0 mipmap
ivec2 tex2Pos = GetMicroTexturePos(microTextureID);
tex2Data = texBiLinear(textureBank[(texturePage+1)&1], ivec2(0), ivec2(128), tex2Pos, fsTexCoord * scale, 0);
blendFactor = -lod * exp2(-microTextureMinLOD) * 0.5;
blendFactor = min(blendFactor, 0.5);
}
tex1Data = mix(tex1Data, tex2Data, blendFactor);
if(textureInverted) {
tex1Data.rgb = vec3(1.0) - tex1Data.rgb;
}
if (alphaTest) {
if (tex1Data.a < (32.0/255.0)) {
discard;
}
}
if(textureAlpha) {
if(discardAlpha) { // opaque 1st pass
if (tex1Data.a < 1.0) {
discard;
}
}
else { // transparent 2nd pass
if ((tex1Data.a * fsColor.a) >= 1.0) {
discard;
}
}
}
if (textureAlpha == false && alphaTest == false) {
tex1Data.a = 1.0;
}
return tex1Data;
}
void Step15Luminous(inout vec4 colour)
{
// luminous polys seem to behave very differently on step 1.5 hardware
// when fixed shading is enabled the colour is modulated by the vp ambient + fixed shade value
// when disabled it appears to be multiplied by 1.5, presumably to allow a higher range
if(hardwareStep==0x15) {
if(!lightEnabled) {
if(fixedShading) {
colour.rgb *= 1.0 + fsFixedShade + lighting[1].y;
}
else {
colour.rgb *= 1.5;
}
}
}
}
float CalcFog()
{
float z = -fsViewVertex.z;
float fog = fogIntensity * clamp(fogStart + z * fogDensity, 0.0, 1.0);
return fog;
}
float Sqr(float a)
{
return a*a;
}
float SqrLength(vec2 a)
{
return a.x*a.x + a.y*a.y;
}
// Computes spotlight lobe + fog lobe effects
// ------------------------------------------------------------
// Inputs:
// spotEllipse - vec4(center.xy, radius.xy)
// spotRange - vec2(startZ, extent)
// viewZ - fsViewVertex.z
//
// Outputs:
// lobeEffect - spotlight intensity
// lobeFogEffect - spotlight on fog intensity
// ------------------------------------------------------------
void ComputeSpotlight(vec4 spotEllipse, vec2 spotRange, float viewZ,
out float lobeEffect,
out float lobeFogEffect)
{
// Ellipse falloff
float ellipse = SqrLength((gl_FragCoord.xy - spotEllipse.xy) / spotEllipse.zw);
ellipse = 1.0 - ellipse;
ellipse = max(0.0, ellipse);
// Spotlight enable
float enable = step(spotRange.x, -viewZ);
// Spotlight range falloff
float range = 0.0;
if (spotRange.y != 0.0) {
float absExtent = abs(spotRange.y);
float d = spotRange.x + absExtent + viewZ;
d = min(d, 0.0);
float inv_r = 1.0 / (1.0 + absExtent);
// inverse-linear falloff
range = 1.0 / Sqr(d * inv_r - 1.0);
range *= enable;
}
// Final outputs
lobeEffect = range * ellipse;
lobeFogEffect = enable * ellipse;
}
void WriteOutputs(vec4 colour, int layer)
{
vec4 blank = vec4(0.0);
if(layer==LayerColour) {
colour.a = 1.0; // always have alpha of 1
out0 = colour;
out1 = blank;
out2 = blank;
}
else if(layer==LayerTrans0) {
out0 = blank;
out1 = colour;
out2 = blank;
}
else if(layer==LayerTrans1) {
out0 = blank;
out1 = blank;
out2 = colour;
}
}
)glsl";
+510
View File
@@ -0,0 +1,510 @@
#ifndef _R3DSHADERQUADS_H_
#define _R3DSHADERQUADS_H_
static const char* vertexShaderR3DQuads = R"glsl(
#version 450 core
// uniforms
uniform float modelScale;
uniform float nodeAlpha;
uniform float cota;
uniform mat4 modelMat;
uniform mat4 projMat;
uniform bool translatorMap;
// attributes
in vec4 inVertex;
in vec3 inNormal;
in vec2 inTexCoord;
in vec3 inFaceNormal; // used to emulate r3d culling
in float inFixedShade;
in vec4 inColour;
in float inTextureNP;
// outputs to geometry shader
out VS_OUT
{
vec3 viewVertex;
vec3 viewNormal; // per vertex normal vector
vec2 texCoord;
vec4 color;
float fixedShade;
float discardPoly; // can't have varying bool (glsl spec)
float LODBase;
} vs_out;
vec4 GetColour(vec4 colour)
{
vec4 c = colour;
if(translatorMap) {
c.rgb *= 16.0;
}
c.a *= nodeAlpha;
return c;
}
float CalcBackFace(in vec3 viewVertex)
{
vec3 vt = viewVertex; // - vec3(0.0);
vec3 vn = mat3(modelMat) * inFaceNormal;
// dot product of face normal with view direction
return dot(vt, vn);
}
void main(void)
{
vs_out.viewVertex = (modelMat * inVertex).xyz;
vs_out.viewNormal = (mat3(modelMat) / modelScale) * inNormal;
vs_out.discardPoly = CalcBackFace(vs_out.viewVertex);
vs_out.color = GetColour(inColour);
vs_out.texCoord = inTexCoord;
vs_out.fixedShade = inFixedShade;
vs_out.LODBase = vs_out.discardPoly * -cota * inTextureNP;
gl_Position = (projMat * modelMat) * inVertex;
}
)glsl";
static const char* geometryShaderR3DQuads = R"glsl(
#version 450 core
layout (lines_adjacency) in;
layout (triangle_strip, max_vertices = 4) out;
in VS_OUT
{
vec3 viewVertex;
vec3 viewNormal; // per vertex normal vector
vec2 texCoord;
vec4 color;
float fixedShade;
float discardPoly; // can't have varying bool (glsl spec)
float LODBase;
} gs_in[4];
out GS_OUT
{
noperspective vec2 v[4];
noperspective float area[4];
flat float oneOverW[4];
//our regular attributes
flat vec3 viewVertex[4];
flat vec3 viewNormal[4]; // per vertex normal vector
flat vec2 texCoord[4];
flat vec4 color;
flat float fixedShade[4];
flat float LODBase;
vec3 barycentricCoords;
} gs_out;
//a*b - c*d, computed in a stable fashion (Kahan)
float DifferenceOfProducts(float a, float b, float c, float d)
{
precise float cd = c * d;
precise float err = fma(-c, d, cd);
precise float dop = fma(a, b, -cd);
return dop + err;
}
void main(void)
{
if(gs_in[0].discardPoly > 0.0) {
return; //emulate back face culling here (all vertices in poly have same value)
}
vec2 v[4];
for (int i=0; i<4; i++) {
float oneOverW = 1.0 / gl_in[i].gl_Position.w;
gs_out.oneOverW[i] = oneOverW;
v[i] = gl_in[i].gl_Position.xy * oneOverW;
// our regular vertex attribs
gs_out.viewVertex[i] = gs_in[i].viewVertex * oneOverW;
gs_out.viewNormal[i] = gs_in[i].viewNormal * oneOverW;
gs_out.texCoord[i] = gs_in[i].texCoord * oneOverW;
gs_out.fixedShade[i] = gs_in[i].fixedShade * oneOverW;
}
// flat attributes
gs_out.color = gs_in[0].color;
gs_out.LODBase = gs_in[0].LODBase;
// precompute crossproducts for all vertex combinations to be looked up in loop below for area computation
precise float cross[4][4];
for (int i=0; i<4; i++)
{
cross[i][i] = 0.0;
for (int j=i+1; j<4; j++)
cross[i][j] = DifferenceOfProducts(gl_in[i].gl_Position.x, gl_in[j].gl_Position.y, gl_in[j].gl_Position.x, gl_in[i].gl_Position.y) / (gl_in[i].gl_Position.w * gl_in[j].gl_Position.w);
}
for (int i=1; i<4; i++)
for (int j=0; j<i; j++)
cross[i][j] = -cross[j][i];
for (int i=0; i<4; i++) {
// Mapping of polygon vertex order to triangle strip vertex order.
//
// Quad (lines adjacency) Triangle strip
// vertex order: vertex order:
//
// 1----2 0----2
// | | ===> | / |
// | | | / |
// 0----3 1----3
//
const int reorder[4] = int[4]( 1, 0, 2, 3 );
int ii = reorder[i];
for (int j=0; j<4; j++) {
gs_out.v[j] = v[j] - v[ii];
int j_next = (j+1) % 4;
// compute area via shoelace algorithm BUT divided by w afterwards to improve precision!
// in addition also use Kahans algorithm to further improve precision of the 2D crossproducts
gs_out.area[j] = cross[j][j_next] + cross[j_next][ii] + cross[ii][j];
}
const vec3 bary[4] = vec3[](
vec3(1.0, 0.0, 0.0),
vec3(0.0, 1.0, 0.0),
vec3(0.0, 0.0, 1.0),
vec3(0.0,-1.0, 0.0) // make the last coordinate negative so we can tell the difference between the triangles
);
gs_out.barycentricCoords = bary[ii];
gl_Position = gl_in[ii].gl_Position;
EmitVertex();
}
}
)glsl";
static const char* fragmentShaderR3DQuads = R"glsl(
#version 450 core
uniform usampler2D textureBank[2]; // entire texture sheet
// texturing
uniform bool textureEnabled;
uniform bool microTexture;
uniform float microTextureMinLOD;
uniform int microTextureID;
uniform ivec4 baseTexInfo; // x/y are x,y positions in the texture sheet. z/w are with and height
uniform int baseTexType;
uniform bool textureInverted;
uniform bool textureAlpha;
uniform bool alphaTest;
uniform bool discardAlpha;
uniform ivec2 textureWrapMode;
uniform int texturePage;
// general
uniform vec3 fogColour;
uniform vec4 spotEllipse; // spotlight ellipse position: .x=X position (screen coordinates), .y=Y position, .z=half-width, .w=half-height)
uniform vec2 spotRange; // spotlight Z range: .x=start (viewspace coordinates), .y=limit
uniform vec3 spotColor; // spotlight RGB color
uniform vec3 spotFogColor; // spotlight RGB color on fog
uniform vec3 lighting[2]; // lighting state (lighting[0] = sun direction, lighting[1].x,y = diffuse, ambient intensities from 0-1.0)
uniform bool lightEnabled; // lighting enabled (1.0) or luminous (0.0), drawn at full intensity
uniform bool sunClamp; // not used by daytona and la machine guns
uniform bool intensityClamp; // some games such as daytona and
uniform bool specularEnabled; // specular enabled
uniform float specularValue; // specular coefficient
uniform float shininess; // specular shininess
uniform float fogIntensity;
uniform float fogDensity;
uniform float fogStart;
uniform float fogAttenuation;
uniform float fogAmbient;
uniform bool fixedShading;
uniform bool smoothShading;
uniform int hardwareStep;
uniform int colourLayer;
uniform bool polyAlpha;
// matrices (shared with vertex shader)
uniform mat4 projMat;
//interpolated inputs from geometry shader
in GS_OUT
{
noperspective vec2 v[4];
noperspective float area[4];
flat float oneOverW[4];
//our regular attributes
flat vec3 viewVertex[4];
flat vec3 viewNormal[4]; // per vertex normal vector
flat vec2 texCoord[4];
flat vec4 color;
flat float fixedShade[4];
flat float LODBase;
vec3 barycentricCoords;
} fs_in;
//our calculated vertex attributes from the above
vec3 fsViewVertex;
vec3 fsViewNormal;
vec2 fsTexCoord;
vec4 fsColor;
float fsFixedShade;
float fsLODBase;
//outputs
layout(location = 0) out vec4 out0; // opaque
layout(location = 1) out vec4 out1; // trans layer 1
layout(location = 2) out vec4 out2; // trans layer 2
// forward declarations (see common file)
float CalcFog();
void Step15Luminous(inout vec4 colour);
vec4 GetTextureValue();
void WriteOutputs(vec4 colour, int layer);
float Sqr(float a);
float SqrLength(vec2 a);
void ComputeSpotlight(vec4 spotEllipse, vec2 spotRange, float viewZ, out float lobeEffect, out float lobeFogEffect);
void QuadraticInterpolation()
{
// init interpolated vertex attributes
fsViewVertex = vec3(0.0);
fsViewNormal = vec3(0.0);
fsTexCoord = vec2(0.0);
fsFixedShade = 0.0;
float interp_oneOverW = 0.0;
float uSum = 0.0;
const float hpFMin = 0.00006103515625; // half precision FLT_MIN
bool extreme_edge = (fs_in.barycentricCoords.x <= hpFMin || fs_in.barycentricCoords.z <= hpFMin);
// As the coordinates get closer to the poly edge the interpolated areas can approach zero which can blow up the maths leading to bad interpolation.
// In this case we fall back to 3 point triangle interpolation (really 2 because 1 coord will be zero).
// This code will only get triggered in rarer corner cases
float u[4];
if(extreme_edge)
{
for (int i=0; i<4; i++) {
int b = (i == 3) ? 1 // 2nd triangle
: i; // 1st triangle
float baryCoord = abs(fs_in.barycentricCoords[b]);
u[i] = baryCoord / fs_in.oneOverW[i];
if((i == 1 && !(fs_in.barycentricCoords.y>0.0)) // ignore u[1] if 2nd triangle
||(i == 3 && (fs_in.barycentricCoords.y>0.0)) // ignore u[3] if 1st triangle
||(baryCoord <= hpFMin))
{
u[i] = 0.0;
}
}
}
else
{
for (int i=0; i<4; i++)
u[i] = length(fs_in.v[i]) * sign(fs_in.oneOverW[i]); // is w[i] negative?
precise float t[4];
for (int i=0; i<4; i++) {
int i_next = (i+1)%4;
if(fs_in.area[i]==0.0) t[i] = 0.0; // check for zero area to avoid div by zero
else t[i] = fma(u[i],u[i_next], -dot(fs_in.v[i],fs_in.v[i_next])) / fs_in.area[i];
}
int lambdaSignCount = 0; // to discard fragments if all the weights are neither all negative nor all positive (=outside the convex/concave/crossed quad).
for (uint i=0; i<4; i++) {
uint i_prev = (i-1)%4;
u[i] = (t[i_prev] + t[i]) / u[i];
lambdaSignCount += (t[i_prev] < -t[i]) ? -1 : 1;
}
if (lambdaSignCount == 0) { // one can either check for == 0 or abs(...) != 4, both should(!) be equivalent (but in practice its not due to precision issues, but these cases are extremely rare)
if(!gl_HelperInvocation) {
discard;
}
}
for (int i=0; i<4; i++) {
interp_oneOverW = fma(fs_in.oneOverW[i], u[i], interp_oneOverW);
uSum += u[i];
}
}
// shared computations
for (int i=0; i<4; i++) {
fsViewVertex = fma(fs_in.viewVertex[i], vec3(u[i]), fsViewVertex);
fsViewNormal = fma(fs_in.viewNormal[i], vec3(u[i]), fsViewNormal);
fsTexCoord = fma(fs_in.texCoord[i], vec2(u[i]), fsTexCoord);
fsFixedShade = fma(fs_in.fixedShade[i], u[i], fsFixedShade);
}
// finalize the 2 cases
if(extreme_edge)
{
float w = (projMat * vec4(fsViewVertex,1.0)).w;
interp_oneOverW = 1.0 / w;
uSum = 1.0;
}
else
{
float inv = 1.0/interp_oneOverW;
fsViewVertex *= inv;
fsViewNormal *= inv;
fsTexCoord *= inv;
fsFixedShade *= inv;
}
vec4 vertex;
float depth;
// dirty hack for co-planar polys that really need 100% identical values to depth test correctly
// the reason we waste cycles and calculate depth value here is because we have run out of vertex attribs
if(fs_in.oneOverW[0]==fs_in.oneOverW[1] &&
fs_in.oneOverW[1]==fs_in.oneOverW[2] &&
fs_in.oneOverW[2]==fs_in.oneOverW[3]) {
fsViewVertex.z = fs_in.viewVertex[0].z / fs_in.oneOverW[0];
vertex = projMat * vec4(fsViewVertex,1.0);
depth = vertex.z / vertex.w;
}
else {
vertex.z = projMat[2][2] * fsViewVertex.z + projMat[3][2]; // standard projMat * vertex - but just using Z components
depth = vertex.z * (interp_oneOverW/uSum);
}
fsColor = fs_in.color;
fsLODBase = fs_in.LODBase;
gl_FragDepth = depth;
}
void main()
{
vec4 tex1Data;
vec4 colData;
vec4 finalData;
vec4 fogData;
QuadraticInterpolation(); // calculate our vertex attributes
fogData = vec4(fogColour.rgb * fogAmbient, CalcFog());
tex1Data = vec4(1.0, 1.0, 1.0, 1.0);
if(textureEnabled) {
tex1Data = GetTextureValue();
}
colData = fsColor;
Step15Luminous(colData); // no-op for step 2.0+
finalData = tex1Data * colData;
if (finalData.a < (1.0/32.0)) { // basically chuck out any totally transparent pixels value = 1/16 the smallest transparency level h/w supports
discard;
}
float lobeEffect;
float lobeFogEffect;
ComputeSpotlight(spotEllipse, spotRange, fsViewVertex.z, lobeEffect, lobeFogEffect);
if (lightEnabled) {
vec3 lightIntensity;
vec3 sunVector; // sun lighting vector (as reflecting away from vertex)
float sunFactor; // sun light projection along vertex normal (0.0 to 1.0)
// Sun angle
sunVector = lighting[0];
// Compute diffuse factor for sunlight
if(fixedShading) {
sunFactor = fsFixedShade;
}
else {
sunFactor = dot(sunVector, fsViewNormal);
}
// Clamp ceil, fix for upscaled models without "modelScale" defined
sunFactor = clamp(sunFactor,-1.0,1.0);
// Optional clamping, value is allowed to be negative
// We suspect that translucent polygons are always clamped (e.g. lasers in Daytona 2)
if(sunClamp || finalData.a<0.999) {
sunFactor = max(sunFactor,0.0);
}
// Total light intensity: sum of all components
lightIntensity = vec3(sunFactor*lighting[1].x + lighting[1].y); // diffuse + ambient
lightIntensity += spotColor*lobeEffect;
// Upper clamp is optional, step 1.5+ games will drive brightness beyond 100%
if(intensityClamp) {
lightIntensity = min(lightIntensity,1.0);
}
finalData.rgb *= lightIntensity;
// for now assume fixed shading doesn't work with specular
if (specularEnabled) {
float specularFactor;
if (smoothShading)
{
// Always clamp floor to zero
float NdotL = max(0.0, sunFactor);
const float expIndex[4] = float[4](8.0, 16.0, 32.0, 64.0);
const float multIndex[4] = float[4](1.6, 1.6, 2.4, 3.2);
float exponent = expIndex[int(shininess)];
specularFactor = pow(NdotL, exponent);
specularFactor *= multIndex[int(shininess)];
}
else
{
// flat shaded polys use Phong reflection model (R dot V) without any exponent or multiplier
// V = (0.0, 0.0, 1.0) is used by Model 3 as a fast approximation, so R dot V = R.z
vec3 R = reflect(-sunVector, fsViewNormal);
specularFactor = max(0.0, R.z);
}
specularFactor *= specularValue;
specularFactor *= lighting[1].x;
if (colData.a < 1.0) {
/// Specular hi-light affects translucent polygons alpha channel ///
finalData.a = max(finalData.a, specularFactor);
}
finalData.rgb += specularFactor;
}
}
// Final clamp: we need it for proper shading in dimmed light and dark ambients
finalData.rgb = min(finalData.rgb, vec3(1.0));
// Spotlight on fog
vec3 lSpotFogColor = spotFogColor * fogAttenuation * fogColour.rgb * lobeFogEffect;
// Fog & spotlight applied
finalData.rgb = mix(finalData.rgb, fogData.rgb + lSpotFogColor, fogData.a);
// Write outputs to colour buffers
WriteOutputs(finalData,colourLayer);
}
)glsl";
#endif
+260
View File
@@ -0,0 +1,260 @@
#ifndef _R3DSHADERTRIANGLES_H_
#define _R3DSHADERTRIANGLES_H_
static const char *vertexShaderR3D = R"glsl(
#version 410 core
// uniforms
uniform float modelScale;
uniform float nodeAlpha;
uniform float cota;
uniform mat4 modelMat;
uniform mat4 projMat;
uniform bool translatorMap;
// attributes
in vec4 inVertex;
in vec3 inNormal;
in vec2 inTexCoord;
in vec3 inFaceNormal; // used to emulate r3d culling
in float inFixedShade;
in vec4 inColour;
in float inTextureNP;
// outputs to fragment shader
out vec3 fsViewVertex;
out vec3 fsViewNormal; // per vertex normal vector
out vec2 fsTexCoord;
out vec4 fsColor;
out float fsFixedShade;
out float fsDiscardPoly; // can't have varying bool (glsl spec)
out float fsLODBase;
vec4 GetColour(vec4 colour)
{
vec4 c = colour;
if(translatorMap) {
c.rgb *= 16.0;
}
c.a *= nodeAlpha;
return c;
}
float CalcBackFace(in vec3 viewVertex)
{
vec3 vt = viewVertex; // - vec3(0.0);
vec3 vn = mat3(modelMat) * inFaceNormal;
// dot product of face normal with view direction
return dot(vt, vn);
}
void main(void)
{
fsViewVertex = (modelMat * inVertex).xyz;
fsViewNormal = (mat3(modelMat) / modelScale) * inNormal;
fsDiscardPoly = CalcBackFace(fsViewVertex);
fsColor = GetColour(inColour);
fsTexCoord = inTexCoord;
fsFixedShade = inFixedShade;
fsLODBase = fsDiscardPoly * -cota * inTextureNP;
gl_Position = (projMat * modelMat) * inVertex;
}
)glsl";
static const char *fragmentShaderR3D = R"glsl(
#version 410 core
uniform usampler2D textureBank[2]; // entire texture sheet
// texturing
uniform bool textureEnabled;
uniform bool microTexture;
uniform float microTextureMinLOD;
uniform int microTextureID;
uniform ivec4 baseTexInfo; // x/y are x,y positions in the texture sheet. z/w are with and height
uniform int baseTexType;
uniform bool textureInverted;
uniform bool textureAlpha;
uniform bool alphaTest;
uniform bool discardAlpha;
uniform ivec2 textureWrapMode;
uniform int texturePage;
// general
uniform vec3 fogColour;
uniform vec4 spotEllipse; // spotlight ellipse position: .x=X position (screen coordinates), .y=Y position, .z=half-width, .w=half-height)
uniform vec2 spotRange; // spotlight Z range: .x=start (viewspace coordinates), .y=limit
uniform vec3 spotColor; // spotlight RGB color
uniform vec3 spotFogColor; // spotlight RGB color on fog
uniform vec3 lighting[2]; // lighting state (lighting[0] = sun direction, lighting[1].x,y = diffuse, ambient intensities from 0-1.0)
uniform bool lightEnabled; // lighting enabled (1.0) or luminous (0.0), drawn at full intensity
uniform bool sunClamp; // not used by daytona and la machine guns
uniform bool intensityClamp; // some games such as daytona and
uniform bool specularEnabled; // specular enabled
uniform float specularValue; // specular coefficient
uniform float shininess; // specular shininess
uniform float fogIntensity;
uniform float fogDensity;
uniform float fogStart;
uniform float fogAttenuation;
uniform float fogAmbient;
uniform bool fixedShading;
uniform bool smoothShading;
uniform int hardwareStep;
uniform int colourLayer;
uniform bool polyAlpha;
// matrices (shared with vertex shader)
uniform mat4 projMat;
//interpolated inputs from vertex shader
in vec3 fsViewVertex;
in vec3 fsViewNormal; // per vertex normal vector
in vec2 fsTexCoord;
in vec4 fsColor;
in float fsFixedShade;
in float fsDiscardPoly;
in float fsLODBase;
//outputs
layout(location = 0) out vec4 out0; // opaque
layout(location = 1) out vec4 out1; // trans layer 1
layout(location = 2) out vec4 out2; // trans layer 2
// forward declarations (see common file)
float CalcFog();
void Step15Luminous(inout vec4 colour);
vec4 GetTextureValue();
void WriteOutputs(vec4 colour, int layer);
float Sqr(float a);
float SqrLength(vec2 a);
void ComputeSpotlight(vec4 spotEllipse, vec2 spotRange, float viewZ, out float lobeEffect, out float lobeFogEffect);
void main()
{
vec4 tex1Data;
vec4 colData;
vec4 finalData;
vec4 fogData;
if(fsDiscardPoly > 0.0) {
discard; //emulate back face culling here
}
gl_FragDepth = projMat[3][2] * gl_FragCoord.w;
fogData = vec4(fogColour.rgb * fogAmbient, CalcFog());
tex1Data = vec4(1.0, 1.0, 1.0, 1.0);
if(textureEnabled) {
tex1Data = GetTextureValue();
}
colData = fsColor;
Step15Luminous(colData); // no-op for step 2.0+
finalData = tex1Data * colData;
if (finalData.a < (1.0/32.0)) { // basically chuck out any totally transparent pixels value = 1/16 the smallest transparency level h/w supports
discard;
}
float lobeEffect;
float lobeFogEffect;
ComputeSpotlight(spotEllipse, spotRange, fsViewVertex.z, lobeEffect, lobeFogEffect);
if (lightEnabled) {
vec3 lightIntensity;
vec3 sunVector; // sun lighting vector (as reflecting away from vertex)
float sunFactor; // sun light projection along vertex normal (0.0 to 1.0)
// Sun angle
sunVector = lighting[0];
// Compute diffuse factor for sunlight
if(fixedShading) {
sunFactor = fsFixedShade;
}
else {
sunFactor = dot(sunVector, fsViewNormal);
}
// Clamp ceil, fix for upscaled models without "modelScale" defined
sunFactor = clamp(sunFactor,-1.0,1.0);
// Optional clamping, value is allowed to be negative
// We suspect that translucent polygons are always clamped (e.g. lasers in Daytona 2)
if(sunClamp || finalData.a<0.999) {
sunFactor = max(sunFactor,0.0);
}
// Total light intensity: sum of all components
lightIntensity = vec3(sunFactor*lighting[1].x + lighting[1].y); // diffuse + ambient
lightIntensity += spotColor*lobeEffect;
// Upper clamp is optional, step 1.5+ games will drive brightness beyond 100%
if(intensityClamp) {
lightIntensity = min(lightIntensity,1.0);
}
finalData.rgb *= lightIntensity;
// for now assume fixed shading doesn't work with specular
if (specularEnabled) {
float specularFactor;
if (smoothShading)
{
// Always clamp floor to zero
float NdotL = max(0.0, sunFactor);
const float expIndex[4] = float[4](8.0, 16.0, 32.0, 64.0);
const float multIndex[4] = float[4](1.6, 1.6, 2.4, 3.2);
float exponent = expIndex[int(shininess)];
specularFactor = pow(NdotL, exponent);
specularFactor *= multIndex[int(shininess)];
}
else
{
// flat shaded polys use Phong reflection model (R dot V) without any exponent or multiplier
// V = (0.0, 0.0, 1.0) is used by Model 3 as a fast approximation, so R dot V = R.z
vec3 R = reflect(-sunVector, fsViewNormal);
specularFactor = max(0.0, R.z);
}
specularFactor *= specularValue;
specularFactor *= lighting[1].x;
if (colData.a < 1.0) {
/// Specular hi-light affects translucent polygons alpha channel ///
finalData.a = max(finalData.a, specularFactor);
}
finalData.rgb += specularFactor;
}
}
// Final clamp: we need it for proper shading in dimmed light and dark ambients
finalData.rgb = min(finalData.rgb, vec3(1.0));
// Spotlight on fog
vec3 lSpotFogColor = spotFogColor * fogAttenuation * fogColour.rgb * lobeFogEffect;
// Fog & spotlight applied
finalData.rgb = mix(finalData.rgb, fogData.rgb + lSpotFogColor, fogData.a);
// Write outputs to colour buffers
WriteOutputs(finalData,colourLayer);
}
)glsl";
#endif
-395
View File
@@ -1,395 +0,0 @@
#include "Texture.h"
#include <stdio.h>
#include <math.h>
#include <algorithm>
namespace New3D {
Texture::Texture()
{
Reset();
}
Texture::~Texture()
{
DeleteTexture(); // make sure to have valid context before destroying
}
void Texture::DeleteTexture()
{
if (m_textureID) {
glDeleteTextures(1, &m_textureID);
Reset();
}
}
void Texture::Reset()
{
m_x = 0;
m_y = 0;
m_width = 0;
m_height = 0;
m_format = 0;
m_textureID = 0;
m_mirrorU = false;
m_mirrorV = false;
}
void Texture::BindTexture()
{
glBindTexture(GL_TEXTURE_2D, m_textureID);
}
void Texture::GetCoordinates(UINT16 uIn, UINT16 vIn, float uvScale, float& uOut, float& vOut)
{
uOut = (uIn*uvScale) / m_width;
vOut = (vIn*uvScale) / m_height;
}
void Texture::GetCoordinates(int width, int height, UINT16 uIn, UINT16 vIn, float uvScale, float& uOut, float& vOut)
{
uOut = (uIn*uvScale) / width;
vOut = (vIn*uvScale) / height;
}
void Texture::SetWrapMode(bool mirrorU, bool mirrorV)
{
if (mirrorU != m_mirrorU) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, mirrorU ? GL_MIRRORED_REPEAT : GL_REPEAT);
m_mirrorU = mirrorU;
}
if (mirrorV != m_mirrorV) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, mirrorV ? GL_MIRRORED_REPEAT : GL_REPEAT);
m_mirrorV = mirrorV;
}
}
void Texture::UploadTextureMip(int level, const UINT16* src, UINT8* scratch, int format, int x, int y, int width, int height)
{
int xi, yi, i;
int subWidth, subHeight;
GLubyte texel;
GLubyte c, a;
i = 0;
subWidth = width;
subHeight = height;
if (subWidth + x > 2048) {
subWidth = 2048 - x;
}
if (subHeight + y > 2048) {
subHeight = 2048 - y;
}
switch (format)
{
default: // Debug texture
for (yi = y; yi < (y + subHeight); yi++)
{
for (xi = x; xi < (x + subWidth); xi++)
{
scratch[i++] = 255; // R
scratch[i++] = 0; // G
scratch[i++] = 0; // B
scratch[i++] = 255; // A
}
}
break;
case 0: // T1RGB5 <- correct
for (yi = y; yi < (y + subHeight); yi++)
{
for (xi = x; xi < (x + subWidth); xi++)
{
scratch[i++] = ((src[yi * 2048 + xi] >> 10) & 0x1F) * 255 / 0x1F; // R
scratch[i++] = ((src[yi * 2048 + xi] >> 5) & 0x1F) * 255 / 0x1F; // G
scratch[i++] = ((src[yi * 2048 + xi] >> 0) & 0x1F) * 255 / 0x1F; // B
scratch[i++] = ((src[yi * 2048 + xi] & 0x8000) ? 0 : 255); // T
}
}
break;
case 1: // Interleaved A4L4 (low byte)
for (yi = y; yi < (y + subHeight); yi++)
{
for (xi = x; xi < (x + subWidth); xi++)
{
// Interpret as A4L4
texel = src[yi * 2048 + xi] & 0xFF;
c = (texel & 0xF) * 17;
a = (texel >> 4) * 17;
scratch[i++] = c;
scratch[i++] = c;
scratch[i++] = c;
scratch[i++] = a;
}
}
break;
case 2: // luminance alpha texture <- this one is correct
for (yi = y; yi < (y + subHeight); yi++)
{
for (xi = x; xi < (x + subWidth); xi++)
{
texel = src[yi * 2048 + xi] & 0xFF;
c = ((texel >> 4) & 0xF) * 17;
a = (texel & 0xF) * 17;
scratch[i++] = c;
scratch[i++] = c;
scratch[i++] = c;
scratch[i++] = a;
}
}
break;
case 3: // 8-bit, A4L4 (high byte)
for (yi = y; yi < (y + subHeight); yi++)
{
for (xi = x; xi < (x + subWidth); xi++)
{
texel = src[yi * 2048 + xi] >> 8;
c = (texel & 0xF) * 17;
a = (texel >> 4) * 17;
scratch[i++] = c;
scratch[i++] = c;
scratch[i++] = c;
scratch[i++] = a;
}
}
break;
case 4: // 8-bit, L4A4 (high byte)
for (yi = y; yi < (y + subHeight); yi++)
{
for (xi = x; xi < (x + subWidth); xi++)
{
texel = src[yi * 2048 + xi] >> 8;
c = ((texel >> 4) & 0xF) * 17;
a = (texel & 0xF) * 17;
scratch[i++] = c;
scratch[i++] = c;
scratch[i++] = c;
scratch[i++] = a;
}
}
break;
case 5: // 8-bit grayscale
for (yi = y; yi < (y + subHeight); yi++)
{
for (xi = x; xi < (x + subWidth); xi++)
{
texel = src[yi * 2048 + xi] & 0xFF;
scratch[i++] = texel;
scratch[i++] = texel;
scratch[i++] = texel;
scratch[i++] = (texel == 255 ? 0 : 255);
}
}
break;
case 6: // 8-bit grayscale <-- this one is correct
for (yi = y; yi < (y + subHeight); yi++)
{
for (xi = x; xi < (x + subWidth); xi++)
{
texel = src[yi * 2048 + xi] >> 8;
scratch[i++] = texel;
scratch[i++] = texel;
scratch[i++] = texel;
scratch[i++] = (texel == 255 ? 0 : 255);
}
}
break;
case 7: // RGBA4
for (yi = y; yi < (y + subHeight); yi++)
{
for (xi = x; xi < (x + subWidth); xi++)
{
scratch[i++] = ((src[yi * 2048 + xi] >> 12) & 0xF) * 17;// R
scratch[i++] = ((src[yi * 2048 + xi] >> 8) & 0xF) * 17; // G
scratch[i++] = ((src[yi * 2048 + xi] >> 4) & 0xF) * 17; // B
scratch[i++] = ((src[yi * 2048 + xi] >> 0) & 0xF) * 17; // A
}
}
break;
//
// 4 bit texture types - all luminance textures (no alpha), only seem to be enabled when contour is enabled ( white = contour value )
//
case 8: // low byte, low nibble
for (yi = y; yi < (y + subHeight); yi++)
{
for (xi = x; xi < (x + subWidth); xi++)
{
texel = src[yi * 2048 + xi] & 0xFF;
c = (texel & 0xF) * 17;
scratch[i++] = c;
scratch[i++] = c;
scratch[i++] = c;
scratch[i++] = (c == 255 ? 0 : 255);
}
}
break;
case 9: // low byte, high nibble
for (yi = y; yi < (y + subHeight); yi++)
{
for (xi = x; xi < (x + subWidth); xi++)
{
texel = src[yi * 2048 + xi] & 0xFF;
c = ((texel >> 4) & 0xF) * 17;
scratch[i++] = c;
scratch[i++] = c;
scratch[i++] = c;
scratch[i++] = (c == 255 ? 0 : 255);
}
}
break;
case 10: // high byte, low nibble
for (yi = y; yi < (y + subHeight); yi++)
{
for (xi = x; xi < (x + subWidth); xi++)
{
texel = src[yi * 2048 + xi] >> 8;
c = (texel & 0xF) * 17;
scratch[i++] = c;
scratch[i++] = c;
scratch[i++] = c;
scratch[i++] = (c == 255 ? 0 : 255);
}
}
break;
case 11: // high byte, high nibble
for (yi = y; yi < (y + subHeight); yi++)
{
for (xi = x; xi < (x + subWidth); xi++)
{
texel = src[yi * 2048 + xi] >> 8;
c = ((texel >> 4) & 0xF) * 17;
scratch[i++] = c;
scratch[i++] = c;
scratch[i++] = c;
scratch[i++] = (c == 255 ? 0 : 255);
}
}
break;
}
glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, subWidth, subHeight, GL_RGBA, GL_UNSIGNED_BYTE, scratch);
}
UINT32 Texture::UploadTexture(const UINT16* src, UINT8* scratch, int format, bool mirrorU, bool mirrorV, int x, int y, int width, int height)
{
const int mipXBase[] = { 0, 1024, 1536, 1792, 1920, 1984, 2016, 2032, 2040, 2044, 2046, 2047 };
const int mipYBase[] = { 0, 512, 768, 896, 960, 992, 1008, 1016, 1020, 1022, 1023 };
const int mipDivisor[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024 };
if (!src || !scratch) {
return 0; // sanity checking
}
DeleteTexture(); // free any existing texture
CreateTextureObject(format, mirrorU, mirrorV, x, y, width, height);
int page = y / 1024;
y -= (page * 1024); // remove page from tex y
for (int i = 0; width > 0 && height > 0; i++) {
int xPos = mipXBase[i] + (x / mipDivisor[i]);
int yPos = mipYBase[i] + (y / mipDivisor[i]);
UploadTextureMip(i, src, scratch, format, xPos, yPos + (page * 1024), width, height);
width /= 2;
height /= 2;
}
return m_textureID;
}
void Texture::GetDetails(int& x, int&y, int& width, int& height, int& format)
{
x = m_x;
y = m_y;
width = m_width;
height = m_height;
format = m_format;
}
bool Texture::Compare(int x, int y, int width, int height, int format)
{
if (m_x == x && m_y == y && m_width == width && m_height == height && m_format == format) {
return true;
}
return false;
}
bool Texture::CheckMapPos(int ax1, int ax2, int ay1, int ay2)
{
int bx1 = m_x;
int bx2 = m_x + m_width;
int by1 = m_y;
int by2 = m_y + m_height;
if (ax1<bx2 && ax2>bx1 &&
ay1<by2 && ay2>by1) {
return true; // rectangles overlap
}
return false;
}
void Texture::CreateTextureObject(int format, bool mirrorU, bool mirrorV, int x, int y, int width, int height)
{
GLfloat maxAnistrophy;
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnistrophy);
if (maxAnistrophy > 8) {
maxAnistrophy = 8.0f; //anymore than 8 can get expensive for little gain
}
glPixelStorei(GL_UNPACK_ALIGNMENT, 4); // rgba is always 4 byte aligned
glGenTextures(1, &m_textureID);
glBindTexture(GL_TEXTURE_2D, m_textureID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, mirrorU ? GL_MIRRORED_REPEAT : GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, mirrorV ? GL_MIRRORED_REPEAT : GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, maxAnistrophy);
int minD = std::min(width, height);
int count = 0;
while (minD > 1) {
minD /= 2;
count++;
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, count);
m_x = x;
m_y = y;
m_width = width;
m_height = height;
m_format = format;
m_mirrorU = mirrorU;
m_mirrorV = mirrorV;
}
} // New3D
-45
View File
@@ -1,45 +0,0 @@
#ifndef _TEXTURE_H_
#define _TEXTURE_H_
#include "Types.h"
#include "Pkgs/glew.h" //arg
namespace New3D {
class Texture
{
public:
Texture();
~Texture();
UINT32 UploadTexture (const UINT16* src, UINT8* scratch, int format, bool mirrorU, bool mirrorV, int x, int y, int width, int height);
void DeleteTexture ();
void BindTexture ();
void GetCoordinates (UINT16 uIn, UINT16 vIn, float uvScale, float& uOut, float& vOut);
void GetDetails (int& x, int&y, int& width, int& height, int& format);
void SetWrapMode (bool mirrorU, bool mirrorV);
bool Compare (int x, int y, int width, int height, int format);
bool CheckMapPos (int ax1, int ax2, int ay1, int ay2); //check to see if textures overlap
static void GetCoordinates(int width, int height, UINT16 uIn, UINT16 vIn, float uvScale, float& uOut, float& vOut);
private:
void CreateTextureObject(int format, bool mirrorU, bool mirrorV, int x, int y, int width, int height);
void UploadTextureMip(int level, const UINT16* src, UINT8* scratch, int format, int x, int y, int width, int height);
void Reset();
int m_x;
int m_y;
int m_width;
int m_height;
int m_format;
bool m_mirrorU;
bool m_mirrorV;
GLuint m_textureID;
};
} // New3D
#endif
+71
View File
@@ -0,0 +1,71 @@
#include "TextureBank.h"
New3D::TextureBank::TextureBank()
{
glGenTextures(1, &m_texID);
glBindTexture(GL_TEXTURE_2D, m_texID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
int width = 2048;
int height = 1024;
int level = 0;
while (width>=1 && height>=1) {
glTexImage2D(GL_TEXTURE_2D, level, GL_R16UI, width, height, 0, GL_RED_INTEGER, GL_UNSIGNED_SHORT, nullptr); // allocate storage
width = (width > 1) ? width / 2 : 1;
height = (height > 1) ? height / 2 : 1;
level++;
if (width == 1 && height == 1) {
glTexImage2D(GL_TEXTURE_2D, level, GL_R16UI, width, height, 0, GL_RED_INTEGER, GL_UNSIGNED_SHORT, nullptr); // allocate storage
break;
}
}
m_numLevels = level;
}
New3D::TextureBank::~TextureBank()
{
if (m_texID) {
glDeleteTextures(1, &m_texID);
m_texID = 0;
}
}
void New3D::TextureBank::AttachMemory(const UINT16* textureRam)
{
m_textureRam = textureRam;
}
void New3D::TextureBank::Bind()
{
glBindTexture(GL_TEXTURE_2D, m_texID);
}
void New3D::TextureBank::UploadTextures(int level, int x, int y, int width, int height)
{
glBindTexture(GL_TEXTURE_2D, m_texID);
glPixelStorei(GL_UNPACK_ALIGNMENT, 2);
static constexpr int mipXBase[] = { 0, 1024, 1536, 1792, 1920, 1984, 2016, 2032, 2040, 2044, 2046, 2047 };
static constexpr int mipYBase[] = { 0, 512, 768, 896, 960, 992, 1008, 1016, 1020, 1022, 1023 };
int subX = x - mipXBase[level];
int subY = y - mipYBase[level];
for (int i = 0; i < height; i++) {
glTexSubImage2D(GL_TEXTURE_2D, level, subX, subY + i, width, 1, GL_RED_INTEGER, GL_UNSIGNED_SHORT, m_textureRam + ((y + i) * 2048) + x);
}
}
int New3D::TextureBank::GetNumberOfLevels() const
{
return m_numLevels;
}
+33
View File
@@ -0,0 +1,33 @@
#pragma once
#ifndef _TEXTUREBANK_H_
#define _TEXTUREBANK_H_
#include "Types.h"
#include <GL/glew.h>
// texture banks are a fixed size
// 2048x1024 pixels, each pixel is 16bits in size
namespace New3D {
class TextureBank
{
public:
TextureBank();
~TextureBank();
void AttachMemory(const UINT16* textureRam);
void Bind();
void UploadTextures(int level, int x, int y, int width, int height);
int GetNumberOfLevels() const;
private:
const UINT16* m_textureRam = nullptr;
GLuint m_texID = 0;
int m_numLevels = 0;
};
}
#endif
-181
View File
@@ -1,181 +0,0 @@
#include "TextureSheet.h"
namespace New3D {
TextureSheet::TextureSheet()
{
m_temp.resize(1024 * 1024 * 4); // temporay buffer for textures
}
int TextureSheet::ToIndex(int x, int y)
{
return (y * 2048) + x;
}
std::shared_ptr<Texture> TextureSheet::BindTexture(const UINT16* src, int format, bool mirrorU, bool mirrorV, int x, int y, int width, int height)
{
//========
int index;
//========
x &= 2047;
y &= 2047;
if (width > 1024 || height > 1024) { // sanity checking
return nullptr;
}
index = ToIndex(x, y);
auto range = m_texMap.equal_range(index);
if (range.first == range.second) {
// nothing found so create a new texture
std::shared_ptr<Texture> t(new Texture());
m_texMap.insert(std::pair<int, std::shared_ptr<Texture>>(index, t));
t->UploadTexture(src, m_temp.data(), format, mirrorU, mirrorV, x, y, width, height);
return t;
}
else {
// iterate to try and find a match
for (auto it = range.first; it != range.second; ++it) {
int x2, y2, width2, height2, format2;
it->second->GetDetails(x2, y2, width2, height2, format2);
if (width == width2 && height == height2 && format == format2) {
return it->second;
}
}
// nothing found so create a new entry
std::shared_ptr<Texture> t(new Texture());
m_texMap.insert(std::pair<int, std::shared_ptr<Texture>>(index, t));
t->UploadTexture(src, m_temp.data(), format, mirrorU, mirrorV, x, y, width, height);
return t;
}
}
void TextureSheet::Release()
{
m_texMap.clear();
}
void TextureSheet::Invalidate(int x, int y, int width, int height)
{
//============
int newX;
int newY;
int newWidth;
int newHeight;
int count;
int sWidth; // sample width
int sHeight; // sample height
//============
if (width <= 512) {
newX = (x + width) - 512;
newWidth = 512;
}
else {
newX = x;
newWidth = width;
}
if (height <= 512) {
newY = (y + height) - 512;
newHeight = 512;
}
else {
newY = y;
newHeight = height;
}
CropTile(x, y, newX, newY, newWidth, newHeight);
sWidth = newWidth / 32;
sHeight = newHeight / 32;
count = sWidth * sHeight;
for (int i = 0; i < count; i++) {
int posX = newX + ((i%sWidth) * 32);
int posY = newY + ((i / sWidth) * 32);
int index = ToIndex(posX, posY);
if (posX >= x && posY >= y) { // invalidate this area of memory
m_texMap.erase(index);
}
else { // check for overlapping data tiles and invalidate as necessary
auto range = m_texMap.equal_range(index);
for (auto it = range.first; it != range.second; ++it) {
if (it->second->CheckMapPos(x, x + width, y, y + height)) {
m_texMap.erase(index);
break;
}
}
}
}
}
void TextureSheet::CropTile(int oldX, int oldY, int &newX, int &newY, int &newWidth, int &newHeight)
{
if (newX < 0) {
newWidth += newX;
newX = 0;
}
if (newY < 0) {
newHeight += newY;
newY = 0;
}
if (oldY >= 1024 && newY < 1024) { // gone into next memory page, limitation of our flat model
newHeight -= 1024 - newY;
newY = 1024;
}
}
int TextureSheet::GetTexFormat(int originalFormat, bool contour)
{
if (!contour) {
return originalFormat; // the same
}
switch (originalFormat)
{
case 1:
case 2:
case 3:
case 4:
return originalFormat + 7; // these formats are identical to 1-4, except they lose the 4 bit alpha part when contour is enabled
default:
return originalFormat;
}
}
void TextureSheet::GetMicrotexPos(int basePage, int id, int& x, int& y)
{
int xCoords[8] = { 0, 0, 128, 128, 0, 0, 128, 128 };
int yCoords[8] = { 0, 128, 0, 128, 256, 384, 256, 384 };
// i'm assuming .. the micro texture map is always on the other memory bank to the base texture
// this logic works for all our current games
// the microtextures are always on the top left of each texture sheet
basePage = (basePage + 1) & 1; // wrap around base page
x = xCoords[id];
y = yCoords[id] + (basePage * 1024);
}
} // New3D
-38
View File
@@ -1,38 +0,0 @@
#ifndef _TEXTURE_SHEET_H_
#define _TEXTURE_SHEET_H_
#include "Types.h"
#include <unordered_map>
#include <vector>
#include <memory>
#include "Texture.h"
namespace New3D {
class TextureSheet
{
public:
TextureSheet();
std::shared_ptr<Texture> BindTexture (const UINT16* src, int format, bool mirrorU, bool mirrorV, int x, int y, int width, int height);
void Invalidate (int x, int y, int width, int height); // release parts of the memory
void Release (); // release all texture objects and memory
int GetTexFormat (int originalFormat, bool contour);
void GetMicrotexPos (int basePage, int id, int& x, int& y);
private:
int ToIndex(int x, int y);
void CropTile(int oldX, int oldY, int &newX, int &newY, int &newWidth, int &newHeight);
std::unordered_multimap<int, std::shared_ptr<Texture>> m_texMap;
// the key for the above maps is the x/y position in the 2048x2048 texture
// array of 8 planes for each texture type
std::vector<UINT8> m_temp;
};
} // New3D
#endif
+4 -8
View File
@@ -1,7 +1,5 @@
#include "VBO.h"
namespace New3D {
VBO::VBO()
{
m_id = 0;
@@ -17,7 +15,7 @@ void VBO::Create(GLenum target, GLenum usage, GLsizeiptr size, const void* data)
glBufferData(target, size, data, usage); // upload data to video card
m_target = target;
m_capacity = size;
m_capacity = (int)size;
m_size = 0;
Bind(false); // unbind
@@ -40,7 +38,7 @@ bool VBO::AppendData(GLsizeiptr size, const GLvoid* data)
BufferSubData(m_size, size, data);
m_size += size;
m_size += (int)size;
return true;
}
@@ -71,14 +69,12 @@ void VBO::Bind(bool enable)
}
}
int VBO::GetSize()
int VBO::GetSize() const
{
return m_size;
}
int VBO::GetCapacity()
int VBO::GetCapacity() const
{
return m_capacity;
}
} // New3D
+7 -11
View File
@@ -1,9 +1,7 @@
#ifndef _VBO_H_
#define _VBO_H_
#include "Pkgs/glew.h"
namespace New3D {
#include <GL/glew.h>
class VBO
{
@@ -16,16 +14,14 @@ public:
void Reset (); // don't delete data, just go back to start
void Destroy ();
void Bind (bool enable);
int GetSize ();
int GetCapacity ();
int GetSize () const;
int GetCapacity () const;
private:
GLuint m_id;
GLenum m_target;
int m_capacity;
int m_size;
GLuint m_id;
GLenum m_target;
int m_capacity;
int m_size;
};
} // New3D
#endif
+21 -50
View File
@@ -1,33 +1,9 @@
#include "Vec.h"
#include <math.h>
#include <cmath>
#include <algorithm>
namespace New3D {
static float fastSqrt(float number) {
long i;
float x, y;
const float f = 1.5F;
x = number * 0.5F;
y = number;
i = * ( long * ) &y;
i = 0x5f375a86 - ( i >> 1 );
y = * ( float * ) &i;
y = y * ( f - ( x * y * y ) );
y = y * ( f - ( x * y * y ) );
return number * y;
}
static float fastInvSqrt(float x)
{
float xhalf = 0.5f*x;
int i = *(int*)&x; // get bits for floating value
i = 0x5f375a86- (i>>1); // gives initial guess y0
x = *(float*)&i; // convert bits back to float
x = x*(1.5f-xhalf*x*x); // Newton step, repeating increases accuracy
return x;
}
void V3::subtract(const Vec3 a, const Vec3 b, Vec3 out) {
out[0] = a[0] - b[0];
@@ -58,7 +34,7 @@ void V3::add(Vec3 a, const Vec3 b) {
void V3::divide(Vec3 a, float number) {
multiply(a,1/number);
multiply(a,1.f/number);
}
void V3::multiply(Vec3 a, float number) {
@@ -115,7 +91,7 @@ float V3::length(const Vec3 v) {
//===========
length = v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
length = sqrt(length);
length = std::sqrt(length);
return length;
}
@@ -123,13 +99,13 @@ float V3::length(const Vec3 v) {
void V3::normalise(Vec3 v) {
//========
float len;
float inv_len;
//========
len = v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
len = fastInvSqrt(len);
inv_len = v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
inv_len = 1.f/std::sqrt(inv_len);
multiply(v,len);
multiply(v,inv_len);
}
void V3::multiplyAdd(const Vec3 a, float scale, const Vec3 b, Vec3 out) {
@@ -141,9 +117,9 @@ void V3::multiplyAdd(const Vec3 a, float scale, const Vec3 b, Vec3 out) {
void V3::reset(Vec3 v) {
v[0] = 0;
v[1] = 0;
v[2] = 0;
v[0] = 0.f;
v[1] = 0.f;
v[2] = 0.f;
}
void V3::set(Vec3 v, float value) {
@@ -171,7 +147,7 @@ void V3::reflect(const Vec3 a, const Vec3 b, Vec3 out) {
V3::copy(a,v);
temp = V3::dotProduct(a,b) * 2;
temp = V3::dotProduct(a,b) * 2.f;
V3::multiply(v,temp);
V3::subtract(b,v,out);
@@ -191,16 +167,16 @@ void V3::createNormal(const Vec3 a, const Vec3 b, const Vec3 c, Vec3 outNormal)
void V3::_max(Vec3 a, const Vec3 compare) {
if(a[0] < compare[0]) a[0] = compare[0];
if(a[1] < compare[1]) a[1] = compare[1];
if(a[2] < compare[2]) a[2] = compare[2];
a[0] = std::max(compare[0], a[0]);
a[1] = std::max(compare[1], a[1]);
a[2] = std::max(compare[2], a[2]);
}
void V3::_min(Vec3 a, const Vec3 compare) {
if(a[0] > compare[0]) a[0] = compare[0];
if(a[1] > compare[1]) a[1] = compare[1];
if(a[2] > compare[2]) a[2] = compare[2];
a[0] = std::min(compare[0], a[0]);
a[1] = std::min(compare[1], a[1]);
a[2] = std::min(compare[2], a[2]);
}
bool V3::cmp(const Vec3 a, float b) {
@@ -223,14 +199,9 @@ bool V3::cmp(const Vec3 a, const Vec3 b) {
void V3::clamp(Vec3 a, float _min, float _max) {
if(a[0] < _min) a[0] = _min;
if(a[0] > _max) a[0] = _max;
if(a[1] < _min) a[1] = _min;
if(a[1] > _max) a[1] = _max;
if(a[2] < _min) a[2] = _min;
if(a[2] > _max) a[2] = _max;
a[0] = std::min(std::max(_min, a[0]), _max);
a[1] = std::min(std::max(_min, a[1]), _max);
a[2] = std::min(std::max(_min, a[2]), _max);
}
} // New3D
+394 -622
View File
File diff suppressed because it is too large. Load diff
+175 -174
View File
@@ -1,12 +1,12 @@
/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011-2012 Bart Trzynadlowski, Nik Henson
** Copyright 2011-2012 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
@@ -18,196 +18,197 @@
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* Render2D.h
*
* Header file defining the CRender2D class: OpenGL tile generator graphics.
*/
/*
* Render2D.h
*
* Header file defining the CRender2D class: OpenGL tile generator graphics.
*/
#ifndef INCLUDED_RENDER2D_H
#define INCLUDED_RENDER2D_H
#include "Pkgs/glew.h"
#include <GL/glew.h>
#include <memory>
#include "Supermodel.h"
#include "Util/NewConfig.h"
#include "New3D/GLSLShader.h"
#include "FBO.h"
#include "../Model3/TileGenBuffer.h"
/*
* CRender2D:
*
* Tile generator graphics engine. This must be constructed and initialized
* before being attached to any objects that want to make use of it. Apart from
* the constructor, all members assume that a global GL device
* context is available and that GL functions may be called.
*/
/*
* CRender2D:
*
* Tile generator graphics engine. This must be constructed and initialized
* before being attached to any objects that want to make use of it. Apart from
* the constructor, all members assume that a global GL device
* context is available and that GL functions may be called.
*/
class CRender2D
{
public:
/*
* BeginFrame(void):
*
* Prepare to render a new frame. Must be called once per frame prior to
* drawing anything.
*/
void BeginFrame(void);
/*
* BeginFrame(void):
*
* Prepare to render a new frame. Must be called once per frame prior to
* drawing anything.
*/
void BeginFrame(void);
/*
* PreRenderFrame(void):
*
* Draws the all top layers (above 3D graphics) and bottom layers (below 3D
* graphics) but does not yet display them. May send data to the GPU.
*/
void PreRenderFrame(void);
/*
* PreRenderFrame(void):
*
* Draws the all top layers (above 3D graphics) and bottom layers (below 3D
* graphics) but does not yet display them. May send data to the GPU.
*/
void PreRenderFrame(void);
/*
* RenderFrameBottom(void):
*
* Overwrites the color buffer with bottom surface that was pre-rendered by
* the last call to PreRenderFrame().
*/
void RenderFrameBottom(void);
/*
* RenderFrameBottom(void):
*
* Overwrites the color buffer with bottom surface that was pre-rendered by
* the last call to PreRenderFrame().
*/
void RenderFrameBottom(void);
/*
* RenderFrameTop(void):
*
* Draws the top surface (if it exists) that was pre-rendered by the last
* call to PreRenderFrame(). Previously drawn graphics layers will be visible
* through transparent regions.
*/
void RenderFrameTop(void);
/*
* EndFrame(void):
*
* Signals the end of rendering for this frame. Must be called last during
* the frame.
*/
void EndFrame(void);
/*
* WriteVRAM(addr, data):
*
* Indicates what will be written next to the tile generator's RAM. The
* VRAM address must not have yet been updated, to allow the renderer to
* check for changes. Data is accepted in the same form as the tile
* generator: the MSB is what was written to addr+3. This function is
* intended to facilitate on-the-fly decoding of tiles and palette data.
*
* Parameters:
* addr Address in tile generator RAM. Caller must ensure it is
* clamped to the range 0x000000 to 0x11FFFF because this
* function does not.
* data The data to write.
*/
void WriteVRAM(unsigned addr, uint32_t data);
/*
* AttachRegisters(regPtr):
*
* Attaches tile generator registers. This must be done prior to any
* rendering otherwise the program may crash with an access violation.
*
* Parameters:
* regPtr Pointer to the base of the tile generator registers. There
* are assumed to be 64 in all.
*/
void AttachRegisters(const uint32_t *regPtr);
/*
* AttachPalette(palPtr):
*
* Attaches tile generator palettes. This must be done prior to any
* rendering.
*
* Parameters:
* palPtr Pointer to two palettes. The first is for layers A/A' and
* the second is for B/B'.
*/
void AttachPalette(const uint32_t *palPtr[2]);
/*
* RenderFrameTop(void):
*
* Draws the top surface (if it exists) that was pre-rendered by the last
* call to PreRenderFrame(). Previously drawn graphics layers will be visible
* through transparent regions.
*/
void RenderFrameTop(void);
/*
* AttachVRAM(vramPtr):
*
* Attaches tile generator RAM. This must be done prior to any rendering
* otherwise the program may crash with an access violation.
*
* Parameters:
* vramPtr Pointer to the base of the tile generator RAM (0x120000
* bytes). VRAM is assumed to be in little endian format.
*/
void AttachVRAM(const uint8_t *vramPtr);
/*
* EndFrame(void):
*
* Signals the end of rendering for this frame. Must be called last during
* the frame.
*/
void EndFrame(void);
/*
* WriteVRAM(addr, data):
*
* Indicates what will be written next to the tile generator's RAM. The
* VRAM address must not have yet been updated, to allow the renderer to
* check for changes. Data is accepted in the same form as the tile
* generator: the MSB is what was written to addr+3. This function is
* intended to facilitate on-the-fly decoding of tiles and palette data.
*
* Parameters:
* addr Address in tile generator RAM. Caller must ensure it is
* clamped to the range 0x000000 to 0x11FFFF because this
* function does not.
* data The data to write.
*/
void WriteVRAM(unsigned addr, uint32_t data);
/*
* AttachRegisters(regPtr):
*
* Attaches tile generator registers. This must be done prior to any
* rendering otherwise the program may crash with an access violation.
*
* Parameters:
* regPtr Pointer to the base of the tile generator registers. There
* are assumed to be 64 in all.
*/
void AttachRegisters(const uint32_t* regPtr);
/*
* AttachPalette(palPtr):
*
* Attaches tile generator palettes. This must be done prior to any
* rendering.
*
* Parameters:
* palPtr Pointer to two palettes. The first is for layers A/A' and
* the second is for B/B'.
*/
void AttachPalette(const uint32_t* palPtr[2]);
/*
* AttachVRAM(vramPtr):
*
* Attaches tile generator RAM. This must be done prior to any rendering
* otherwise the program may crash with an access violation.
*
* Parameters:
* vramPtr Pointer to the base of the tile generator RAM (0x120000
* bytes). VRAM is assumed to be in little endian format.
*/
void AttachVRAM(const uint8_t* vramPtr);
/*
* AttachDrawBuffers(bottom,top):
*
* The tilegen is double buffered (for threading). We attach the current draw buffer for drawing.
*
* Parameters:
* bottom Bottom surface
* top Top surface
*/
void AttachDrawBuffers(std::shared_ptr<TileGenBuffer> bottom, std::shared_ptr<TileGenBuffer> top);
/*
* Init(xOffset, yOffset, xRes, yRes, totalXRes, totalYRes);
*
* One-time initialization of the context. Must be called before any other
* members (meaning it should be called even before being attached to any
* other objects that want to use it).
*
* Parameters:
* xOffset X offset of the viewable area within OpenGL display
* surface, in pixels.
* yOffset Y offset.
* xRes Horizontal resolution of the viewable area.
* yRes Vertical resolution.
* totalXRes Horizontal resolution of the complete display area.
* totalYRes Vertical resolution.
*
* Returns:
* OKAY is successful, otherwise FAILED if a non-recoverable error
* occurred. Prints own error messages.
*/
Result Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes, unsigned totalXRes, unsigned totalYRes, unsigned aaTarget, UpscaleMode upscaleMode);
/*
* CRender2D(config):
* ~CRender2D(void):
*
* Constructor and destructor.
*
* Parameters:
* config Run-time configuration.
*/
CRender2D(const Util::Config::Node& config);
~CRender2D(void);
/*
* Init(xOffset, yOffset, xRes, yRes, totalXRes, totalYRes);
*
* One-time initialization of the context. Must be called before any other
* members (meaning it should be called even before being attached to any
* other objects that want to use it).
*
* Parameters:
* xOffset X offset of the viewable area within OpenGL display
* surface, in pixels.
* yOffset Y offset.
* xRes Horizontal resolution of the viewable area.
* yRes Vertical resolution.
* totalXRes Horizontal resolution of the complete display area.
* totalYRes Vertical resolution.
*
* Returns:
* OKAY is successful, otherwise FAILED if a non-recoverable error
* occurred. Prints own error messages.
*/
bool Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes, unsigned totalXRes, unsigned totalYRes);
/*
* CRender2D(config):
* ~CRender2D(void):
*
* Constructor and destructor.
*
* Parameters:
* config Run-time configuration.
*/
CRender2D(const Util::Config::Node &config);
~CRender2D(void);
private:
// Private member functions
std::pair<bool, bool> DrawTilemaps(uint32_t *destBottom, uint32_t *destTop);
void DisplaySurface(int surface);
void Setup2D(bool isBottom, bool clearAll);
// Run-time configuration
const Util::Config::Node &m_config;
// Data received from tile generator device object
const uint32_t *m_vram;
const uint32_t *m_palette[2]; // palettes for A/A' and B/B'
const uint32_t *m_regs;
// OpenGL data
GLuint m_texID[2]; // IDs for the 2 layer textures (top and bottom)
unsigned m_xPixels = 496; // display surface resolution
unsigned m_yPixels = 384; // ...
unsigned m_xOffset = 0; // offset
unsigned m_yOffset = 0;
unsigned m_totalXPixels; // total display surface resolution
unsigned m_totalYPixels;
unsigned m_correction = 0;
// Shader programs and input data locations
GLuint m_shaderProgram; // shader program object
GLuint m_vertexShader; // vertex shader handle
GLuint m_fragmentShader; // fragment shader
GLuint m_textureMapLoc; // location of "textureMap" uniform
void Setup2D (bool isBottom);
void DrawSurface (GLuint textureID);
// PreRenderFrame() tracks which surfaces exist in current frame
std::pair<bool, bool> m_surfaces_present = std::pair<bool, bool>(false, false);
// Run-time configuration
const Util::Config::Node& m_config;
// Buffers
uint8_t *m_memoryPool = 0; // all memory is allocated here
uint32_t *m_topSurface = 0; // 512x384x32bpp pixel surface for top layers
uint32_t *m_bottomSurface = 0; // bottom layers
// OpenGL data
unsigned m_xPixels = 496; // display surface resolution
unsigned m_yPixels = 384; // ...
unsigned m_xOffset = 0; // offset
unsigned m_yOffset = 0;
unsigned m_totalXPixels = 0; // total display surface resolution
unsigned m_totalYPixels = 0;
unsigned m_correction = 0;
GLuint m_aaTarget = 0;
UpscaleMode m_upscaleMode = UpscaleMode::Biquintic;
GLuint m_vao;
GLuint m_textureIDs[2];
GLSLShader m_drawShader;
std::shared_ptr<TileGenBuffer> m_drawBuffers[2];
};
+33 -33
View File
@@ -25,18 +25,18 @@
* OpenGL shader management.
*
* To-Do List
* ----------
* - Mesa crashes because, evidently, the function pointers are invalid. Mesa
* returns the following information:
* Vendor: Mesa project: www.mesa3d.org
* Renderer: Mesa GLX Indirect
* Version: 1.2 (1.5 Mesa 6.5.1)
* Shading Language Version: (null)
* Maximum Vertex Array Size: -1 vertices
* Maximum Texture Size: 2048 texels
* Maximum Vertex Attributes: 16
* Maximum Vertex Uniforms: 16
* - Check for OpenGL 2.0 and perhaps check some of the function pointers,
* ----------
* - Mesa crashes because, evidently, the function pointers are invalid. Mesa
* returns the following information:
* Vendor: Mesa project: www.mesa3d.org
* Renderer: Mesa GLX Indirect
* Version: 1.2 (1.5 Mesa 6.5.1)
* Shading Language Version: (null)
* Maximum Vertex Array Size: -1 vertices
* Maximum Texture Size: 2048 texels
* Maximum Vertex Attributes: 16
* Maximum Vertex Uniforms: 16
* - Check for OpenGL 2.0 and perhaps check some of the function pointers,
* which will be NULL, if GL 2.0 and shaders are not supported.
* - Keep in mind that all these checks should probably go somewhere else...
* - Turn this into a class.
@@ -44,7 +44,7 @@
#include <new>
#include <cstdio>
#include "Pkgs/glew.h"
#include <GL/glew.h>
#include "Supermodel.h"
@@ -82,14 +82,14 @@ static char *LoadShaderSource(const char *file)
return buf;
}
bool LoadShaderProgram(GLuint *shaderProgramPtr, GLuint *vertexShaderPtr, GLuint *fragmentShaderPtr, std::string vsFile, std::string fsFile, const char *vsString, const char *fsString)
Result LoadShaderProgram(GLuint *shaderProgramPtr, GLuint *vertexShaderPtr, GLuint *fragmentShaderPtr, const std::string& vsFile, const std::string& fsFile, const char *vsString, const char *fsString)
{
char infoLog[2048];
const char *vsSource, *fsSource; // source code
GLuint shaderProgram, vertexShader, fragmentShader;
GLint result, len;
bool ret = OKAY;
Result ret = Result::OKAY;
// Load shaders from files if specified
if (!vsFile.empty())
vsSource = LoadShaderSource(vsFile.c_str());
@@ -101,16 +101,16 @@ bool LoadShaderProgram(GLuint *shaderProgramPtr, GLuint *vertexShaderPtr, GLuint
fsSource = fsString;
if (vsSource == NULL || fsSource == NULL)
{
ret = FAIL;
ret = Result::FAIL;
goto Quit;
}
// Ensure that shader support exists
if ((glCreateProgram==NULL) || (glCreateShader==NULL) || (glShaderSource==NULL) || (glCompileShader==NULL))
{
ret = Result::FAIL;
ErrorLog("OpenGL 2.x does not appear to be present. Unable to proceed.");
goto Quit;
}
// Ensure that shader support exists
if ((glCreateProgram==NULL) || (glCreateShader==NULL) || (glShaderSource==NULL) || (glCompileShader==NULL))
{
ret = FAIL;
ErrorLog("OpenGL 2.x does not appear to be present. Unable to proceed.");
goto Quit;
}
// Create the shaders and shader program
@@ -129,7 +129,7 @@ bool LoadShaderProgram(GLuint *shaderProgramPtr, GLuint *vertexShaderPtr, GLuint
{
glGetShaderInfoLog(vertexShader, 2048, &len, infoLog);
ErrorLog("Vertex shader failed to compile. Your OpenGL driver said:\n%s", infoLog);
ret = FAIL; // error
ret = Result::FAIL; // error
}
// Attempt to compile fragment shader
@@ -140,7 +140,7 @@ bool LoadShaderProgram(GLuint *shaderProgramPtr, GLuint *vertexShaderPtr, GLuint
{
glGetShaderInfoLog(fragmentShader, 2048, &len, infoLog);
ErrorLog("Fragment shader failed to compile. Your OpenGL driver said:\n%s", infoLog);
ret = FAIL; // error
ret = Result::FAIL; // error
}
// Link
@@ -152,11 +152,11 @@ bool LoadShaderProgram(GLuint *shaderProgramPtr, GLuint *vertexShaderPtr, GLuint
{
glGetProgramInfoLog(shaderProgram, 2048, &len, infoLog);
ErrorLog("Failed to link shader objects. Your OpenGL driver said:\n%s\n", infoLog);
ret = FAIL; // error
ret = Result::FAIL; // error
}
// Enable the shader (if no errors)
if (ret == OKAY)
if (ret == Result::OKAY)
glUseProgram(shaderProgram);
// Clean up and quit
@@ -169,10 +169,10 @@ Quit:
}
void DestroyShaderProgram(GLuint shaderProgram, GLuint vertexShader, GLuint fragmentShader)
{
// In case LoadShaderProgram() failed above due to lack of OpenGL 2.0+ functions...
if ((glUseProgram==NULL) || (glDeleteShader==NULL) || (glDeleteProgram==NULL))
return;
{
// In case LoadShaderProgram() failed above due to lack of OpenGL 2.0+ functions...
if ((glUseProgram==NULL) || (glDeleteShader==NULL) || (glDeleteProgram==NULL))
return;
glUseProgram(0); // return to fixed function pipeline
glDeleteShader(vertexShader);
+5 -4
View File
@@ -28,8 +28,9 @@
#ifndef INCLUDED_SHADER_H
#define INCLUDED_SHADER_H
#include "Pkgs/glew.h"
#include <GL/glew.h>
#include <string>
#include "Types.h"
/*
* LoadShaderProgram(shaderProgramPtr, vertexShaderPtr, fragmentShaderPtr,
@@ -52,9 +53,9 @@
* Returns:
* OKAY is successfully loaded, otherwise FAIL. Prints own error messages.
*/
extern bool LoadShaderProgram(GLuint *shaderProgramPtr, GLuint *vertexShaderPtr,
GLuint *fragmentShaderPtr, std::string vsFile,
std::string fsFile, const char *vsString,
extern Result LoadShaderProgram(GLuint *shaderProgramPtr, GLuint *vertexShaderPtr,
GLuint *fragmentShaderPtr, const std::string& vsFile,
const std::string& fsFile, const char *vsString,
const char *fsString);
/*
+101 -82
View File
@@ -28,90 +28,109 @@
#ifndef INCLUDED_SHADERS2D_H
#define INCLUDED_SHADERS2D_H
// Vertex shader
static const char s_vertexShaderSource[] =
{
"/**\n"
" ** Supermodel\n"
" ** A Sega Model 3 Arcade Emulator.\n"
" ** Copyright 2011-2012 Bart Trzynadlowski, Nik Henson \n"
" **\n"
" ** This file is part of Supermodel.\n"
" **\n"
" ** Supermodel is free software: you can redistribute it and/or modify it under\n"
" ** the terms of the GNU General Public License as published by the Free \n"
" ** Software Foundation, either version 3 of the License, or (at your option)\n"
" ** any later version.\n"
" **\n"
" ** Supermodel is distributed in the hope that it will be useful, but WITHOUT\n"
" ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n"
" ** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for\n"
" ** more details.\n"
" **\n"
" ** You should have received a copy of the GNU General Public License along\n"
" ** with Supermodel. If not, see <http://www.gnu.org/licenses/>.\n"
" **/\n"
"\n"
"/*\n"
" * Vertex2D.glsl\n"
" *\n"
" * Vertex shader for 2D tilemap rendering.\n"
" */\n"
" \n"
"#version 120\n"
"\n"
"void main(void)\n"
"{\n"
"\tgl_TexCoord[0] = gl_MultiTexCoord0;\n"
"\tgl_Position = gl_ModelViewProjectionMatrix*gl_Vertex;\n"
"}\n"
};
static constexpr char s_vertexShader[] = R"glsl(
#version 410 core
// outputs
out vec2 fsTexCoord;
void main(void)
{
const vec4 vertices[] = vec4[](vec4(-1.0, -1.0, 0.0, 1.0),
vec4(-1.0, 1.0, 0.0, 1.0),
vec4( 1.0, -1.0, 0.0, 1.0),
vec4( 1.0, 1.0, 0.0, 1.0));
const vec2 texCoords[] = vec2[](vec2(0.0, 1.0), // flip upside down
vec2(0.0, 0.0),
vec2(1.0, 1.0),
vec2(1.0, 0.0));
fsTexCoord = texCoords[gl_VertexID % 4];
gl_Position = vertices[gl_VertexID % 4];
}
)glsl";
// Fragment shader
static const char s_fragmentShaderSource[] =
{
"/**\n"
" ** Supermodel\n"
" ** A Sega Model 3 Arcade Emulator.\n"
" ** Copyright 2011-2012 Bart Trzynadlowski, Nik Henson \n"
" **\n"
" ** This file is part of Supermodel.\n"
" **\n"
" ** Supermodel is free software: you can redistribute it and/or modify it under\n"
" ** the terms of the GNU General Public License as published by the Free \n"
" ** Software Foundation, either version 3 of the License, or (at your option)\n"
" ** any later version.\n"
" **\n"
" ** Supermodel is distributed in the hope that it will be useful, but WITHOUT\n"
" ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n"
" ** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for\n"
" ** more details.\n"
" **\n"
" ** You should have received a copy of the GNU General Public License along\n"
" ** with Supermodel. If not, see <http://www.gnu.org/licenses/>.\n"
" **/\n"
" \n"
"/*\n"
" * Fragment2D.glsl\n"
" *\n"
" * Fragment shader for 2D tilemap rendering.\n"
" */\n"
"\n"
"#version 120\n"
"\n"
"// Global uniforms\n"
"uniform sampler2D\ttextureMap;\t\t// 512x512 layer surface\n"
"\n"
"/*\n"
" * main():\n"
" *\n"
" * Fragment shader entry point.\n"
" */\n"
"\n"
"void main(void)\n"
"{\t\n"
"\tgl_FragColor = texture2D(textureMap, gl_TexCoord[0].st);\n"
"}\n"
};
static constexpr char s_fragmentShaderHeader[] = R"glsl(
#version 410 core
)glsl";
static constexpr char s_fragmentShader[] = R"glsl(
// inputs
uniform sampler2D tex1; // texture
in vec2 fsTexCoord;
// outputs
out vec4 fragColor;
#if (UPSCALEMODE == 1)
// smoothstep instead of linear, remapping to a bilinear lookup
vec4 biquintic(sampler2D sampler, vec2 uv)
{
vec2 ts = vec2(textureSize(sampler,0));
uv = uv*ts + 0.5;
vec2 i = floor(uv);
vec2 f = uv - i;
f = f*f*f*(f*(f*6.0-15.0)+10.0) - 0.5;
uv = (i + f)/ts;
return texture(sampler, uv);
}
#endif
#if (UPSCALEMODE == 3)
// 4x4 bicubic filter using 4 bilinear texture lookups
vec4 cubic(float v) {
vec4 n = vec4(1.0, 2.0, 3.0, 4.0) - v;
vec4 s = n * n * n;
float x = s.x;
float y = s.y - 4.0 * s.x;
float z = s.z - 4.0 * s.y + 6.0 * s.x;
float w = 6.0 - x - y - z;
return vec4(x, y, z, w) * (1.0 / 6.0);
}
vec4 bicubic(sampler2D sampler, vec2 uv) {
vec2 invTexSize = 1.0 / vec2(textureSize(sampler,0));
uv = uv * vec2(textureSize(sampler,0)) - 0.5;
vec2 uvi = floor(uv);
vec2 fxy = uv-uvi;
vec4 xcubic = cubic(fxy.x);
vec4 ycubic = cubic(fxy.y);
vec4 c = uvi.xxyy + vec2(-0.5, 1.5).xyxy;
vec4 s = vec4(xcubic.xz + xcubic.yw, ycubic.xz + ycubic.yw);
vec4 offset = c + vec4(xcubic.yw, ycubic.yw) / s;
offset *= invTexSize.xxyy;
vec4 sample0 = texture(sampler, offset.xz);
vec4 sample1 = texture(sampler, offset.yz);
vec4 sample2 = texture(sampler, offset.xw);
vec4 sample3 = texture(sampler, offset.yw);
float sx = s.x / (s.x + s.y);
float sy = s.z / (s.z + s.w);
return mix(mix(sample3, sample2, sx), mix(sample1, sample0, sx), sy);
}
#endif
void main()
{
#if (UPSCALEMODE == 3)
fragColor = bicubic(tex1, fsTexCoord);
#elif (UPSCALEMODE == 1)
fragColor = vec4(biquintic(tex1, fsTexCoord).rgb, texture(tex1, fsTexCoord).a);
#else
fragColor = texture(tex1, fsTexCoord);
#endif
}
)glsl";
#endif // INCLUDED_SHADERS2D_H
+229
View File
@@ -0,0 +1,229 @@
#include "SuperAA.h"
#include <string>
SuperAA::SuperAA(int aaValue, CRTcolor CRTcolors) :
m_aa(aaValue),
m_crtcolors(CRTcolors),
m_vao(0),
m_width(0),
m_height(0)
{
if ((m_aa > 1) || (m_crtcolors != CRTcolor::None)) {
static const char* vertexShader = R"glsl(
#version 410 core
void main(void)
{
const vec4 vertices[] = vec4[](vec4(-1.0, -1.0, 0.0, 1.0),
vec4(-1.0, 1.0, 0.0, 1.0),
vec4( 1.0, -1.0, 0.0, 1.0),
vec4( 1.0, 1.0, 0.0, 1.0));
gl_Position = vertices[gl_VertexID % 4];
}
)glsl";
static const std::string fragmentShaderVersion = R"glsl(
#version 410 core
)glsl";
std::string aaString = "const int aa = ";
aaString += std::to_string(m_aa);
aaString += ";\n";
std::string ccString = "#define CRTCOLORS ";
ccString += std::to_string((int)m_crtcolors);
ccString += '\n';
static const std::string fragmentShader = R"glsl(
// inputs
uniform sampler2D tex1; // base tex
// outputs
out vec4 fragColor;
#if (CRTCOLORS == 1)
const float cgamma = 2.5; // what is the 'real' gamma/EOTF of a japanese arcade CRT of that time? 2.4 or 2.5 is what the net so far agrees on
#elif (CRTCOLORS == 2)
const float cgamma = 2.25;
#elif (CRTCOLORS > 2)
const float cgamma = -1.0;
#endif
// see https://www.retrorgb.com/colour-malarkey.html
// and accompanying matrices https://github.com/danmons/colour_matrix_adaptations/tree/main/csv
#if (CRTCOLORS == 1)
const mat3 colmatrix = mat3(1.3272128334714093, // ARI(D93/9300K) (JP) -> sRGB(D65/6500K) // what can be found in some of the sega or corresponding CRT manuals of the time is a range of ~9700K-10600K, so even cooler than D93
-0.3802108879412366,
-0.1003607696463202,
-0.0241848600268417,
0.9544506228550125,
0.0807358585208939,
-0.0239585810555497,
-0.0409736005706461,
1.4076563728858242);
#elif (CRTCOLORS == 2)
const mat3 colmatrix = mat3(0.9241392201737613, // PVM_20M2U(D93/9300K) (JP) -> sRGB(D65/6500K), gamma 2.25
-0.0690701363506526,
-0.0084279079392561,
0.0231962420140721,
0.9729221778325590,
0.0148832015024337,
-0.0054875731998806,
0.0098220960740544,
1.3383896683854550);
#elif (CRTCOLORS == 3)
const mat3 colmatrix = mat3(0.7822490684754086, // BT601_525_D93 (JP) -> sRGB(D65/6500K), gamma sRGB
0.0506168576073398,
0.0137752498011040,
0.0147968947158740,
0.9741745313046067,
0.0220301953285839,
-0.0013501205469208,
-0.0044076726925543,
1.3484819844991036);
#elif (CRTCOLORS == 4)
const mat3 colmatrix = mat3(0.9395420637732393, // BT601_525 (US) -> sRGB(D65/6500K), gamma sRGB // extremely subtle
0.0501813568598678,
0.0102765793668928,
0.0177722231435608,
0.9657928624969044,
0.0164349143595347,
-0.0016215999431855,
-0.0043697496597356,
1.0059913496029214);
#elif (CRTCOLORS == 5)
const mat3 colmatrix = mat3(1.0440432087628346, // BT601_625 (EUR,AUS) -> sRGB(D65/6500K), gamma sRGB // barely noticeable at all
-0.0440432087628348,
0.0000000000000001,
0.0000000000000000,
1.0000000000000002,
0.0000000000000000,
0.0000000000000000,
0.0117933782840052,
0.9882066217159947);
#endif
vec3 GetTextureValue(sampler2D s)
{
ivec2 texPos = ivec2(gl_FragCoord.xy /*-vec2(0.5)*/) * aa;
vec3 texColour = vec3(0.0);
for(int i=0; i < aa; i++) {
for(int j=0; j < aa; j++) {
texColour += texelFetch(s,ivec2(texPos.x+i,texPos.y+j),0).rgb;
}
}
texColour *= 1.0/float(aa * aa);
return texColour;
}
float sRGB(float color)
{
if (color <= 0.0031308)
return 12.92 * color;
else
return 1.055 * pow(color, 1.0/2.4) - 0.055;
}
float invsRGB(float color)
{
if (color <= 0.04045) // 0.03928 ?
return color * (1.0/12.92);
else
return pow(color * (1.0/1.055) + (0.055/1.055), 2.4);
}
void main()
{
vec3 finalColor = GetTextureValue(tex1);
#if (CRTCOLORS != 0)
{
// transform input space to linear space
if (cgamma != -1.0)
finalColor = pow(finalColor, vec3(cgamma));
else
finalColor = vec3(invsRGB(finalColor.r),invsRGB(finalColor.g),invsRGB(finalColor.b));
// do color transformation
finalColor *= colmatrix;
// transform from linear to sRGB output space
finalColor = vec3(sRGB(finalColor.r),sRGB(finalColor.g),sRGB(finalColor.b));
}
#endif
fragColor = vec4(finalColor, 1.0);
}
)glsl";
std::string fragmentShaderString = fragmentShaderVersion + aaString + ccString + fragmentShader;
// load shaders
m_shader.LoadShaders(vertexShader, fragmentShaderString.c_str());
m_shader.GetUniformLocationMap("tex1");
// setup uniform memory
m_shader.EnableShader();
glUniform1i(m_shader.attribLocMap["tex1"], 0); // texture will be bound to unit zero
m_shader.DisableShader();
glGenVertexArrays(1, &m_vao);
glBindVertexArray(m_vao);
// no states needed since we do it in the shader
glBindVertexArray(0);
}
}
// need an active context bound to the current thread to destroy our objects
SuperAA::~SuperAA()
{
m_shader.UnloadShaders();
m_fbo.Destroy();
if (m_vao) {
glDeleteVertexArrays(1, &m_vao);
m_vao = 0;
}
}
void SuperAA::Init(int width, int height)
{
if ((m_aa > 1) || (m_crtcolors != CRTcolor::None)) {
m_fbo.Destroy();
m_fbo.Create(width * m_aa, height * m_aa);
m_width = width;
m_height = height;
}
}
void SuperAA::Draw()
{
if ((m_aa > 1) || (m_crtcolors != CRTcolor::None)) {
glDisable(GL_DEPTH_TEST);
glDisable(GL_STENCIL_TEST);
glDisable(GL_SCISSOR_TEST);
glDisable(GL_BLEND);
glBindTexture(GL_TEXTURE_2D, m_fbo.GetTextureID());
glBindVertexArray(m_vao);
glViewport(0, 0, m_width, m_height);
m_shader.EnableShader();
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
m_shader.DisableShader();
glBindVertexArray(0);
}
}
GLuint SuperAA::GetTargetID()
{
return m_fbo.GetFBOID(); // will return 0 if no render target which will be our default frame buffer (back buffer)
}
+32
View File
@@ -0,0 +1,32 @@
#pragma once
#include "Supermodel.h"
#include "FBO.h"
#include "New3D/GLSLShader.h"
// This class just implements super sampling. Super sampling looks fantastic but is quite expensive.
// 8x and beyond values can start to eat ridiculous amounts of memory / gpu time, for less and less noticable returns
// 4x works and looks great
// values such as 3 are also possible, that works out 9 samples per pixel
// The algorithm is super simple, just add up all samples and divide by the number
class SuperAA
{
public:
SuperAA(int aaValue, CRTcolor CRTcolors);
~SuperAA();
void Init(int width, int height); // width & height are real window dimensions
void Draw(); // this is a no-op if AA is 1 and CRTcolors 0, since we'll be drawing straight on the back buffer anyway
GLuint GetTargetID();
private:
FBO m_fbo;
GLSLShader m_shader;
const int m_aa;
const CRTcolor m_crtcolors;
GLuint m_vao;
int m_width;
int m_height;
};
+15 -7
View File
@@ -26,7 +26,10 @@
* from this.
*/
#include "Input.h"
#include "Supermodel.h"
#include "InputSystem.h"
CInput::CInput(const char *inputId, const char *inputLabel, unsigned inputFlags, unsigned inputGameFlags, const char *defaultMapping, UINT16 initValue) :
id(inputId), label(inputLabel), flags(inputFlags), gameFlags(inputGameFlags), m_defaultMapping(defaultMapping), value(initValue), prevValue(initValue),
@@ -72,13 +75,18 @@ void CInput::CreateSource()
}
}
void CInput::Initialize(CInputSystem *system)
void CInput::Initialize(std::shared_ptr<CInputSystem> system)
{
m_system = system;
CreateSource();
}
std::shared_ptr<CInputSystem> CInput::GetInputSystem()
{
return m_system;
}
const char* CInput::GetInputGroup()
{
switch (gameFlags)
@@ -88,7 +96,7 @@ const char* CInput::GetInputGroup()
case Game::INPUT_JOYSTICK1: // Fall through to below
case Game::INPUT_JOYSTICK2: return "4-Way Joysticks";
case Game::INPUT_FIGHTING: return "Fighting Game Buttons";
case Game::INPUT_SPIKEOUT: return "Spikeout Buttons";
case Game::INPUT_SPIKEOUT: return "Spikeout Buttons";
case Game::INPUT_SOCCER: return "Virtua Striker Buttons";
case Game::INPUT_VEHICLE: return "Racing Game Steering Controls";
case Game::INPUT_SHIFT4: return "Racing Game Gear 4-Way Shift";
@@ -105,8 +113,8 @@ const char* CInput::GetInputGroup()
case Game::INPUT_ANALOG_GUN2: return "Analog Guns";
case Game::INPUT_SKI: return "Ski Controls";
case Game::INPUT_MAGTRUCK: return "Magical Truck Controls";
case Game::INPUT_FISHING: return "Fishing Controls";
default: return "Misc";
case Game::INPUT_FISHING: return "Fishing Controls";
default: return "Misc";
}
}
@@ -135,7 +143,7 @@ void CInput::AppendMapping(const char *mapping)
else
{
// Otherwise, append to mapping string and recreate source from new mapping string
int size = MAX_MAPPING_LENGTH - strlen(m_mapping);
size_t size = MAX_MAPPING_LENGTH - strlen(m_mapping);
strncat(m_mapping, ",", size--);
strncat(m_mapping, mapping, size);
CreateSource();
@@ -197,7 +205,7 @@ bool CInput::Configure(bool append, const char *escapeMapping)
return true;
}
bool CInput::Changed()
bool CInput::Changed() const
{
return value != prevValue;
}
@@ -207,4 +215,4 @@ bool CInput::SendForceFeedbackCmd(ForceFeedbackCmd ffCmd)
if (m_source == NULL)
return false;
return m_source->SendForceFeedbackCmd(ffCmd);
}
}
+16 -10
View File
@@ -29,11 +29,12 @@
#ifndef INCLUDED_INPUT_H
#define INCLUDED_INPUT_H
#include "InputSource.h"
#include "Types.h"
#include "Game.h"
#include "Util/NewConfig.h"
#include <memory>
class CInputSource;
class CInputSystem;
// Flags for inputs
@@ -74,7 +75,7 @@ private:
const char *m_defaultMapping;
// Assigned input system
CInputSystem *m_system;
std::shared_ptr<CInputSystem> m_system;
/*
* Creates an input source using the current input system and assigns it to this input.
@@ -115,7 +116,12 @@ public:
/*
* Initializes this input with the given input system. Must be called before any other methods are used.
*/
void Initialize(CInputSystem *system);
void Initialize(std::shared_ptr<CInputSystem> system);
/*
* Initializes this input with the given input system. Must be called before any other methods are used.
*/
std::shared_ptr<CInputSystem> GetInputSystem();
/*
* Returns the name of the group of controls that this input belongs to.
@@ -164,17 +170,17 @@ public:
/*
* Returns true if the input is a UI input.
*/
bool IsUIInput();
bool IsUIInput() const;
/*
* Returns true if the input is configurable and can be set by the user.
*/
bool IsConfigurable();
bool IsConfigurable() const;
/*
* Returns true if the input is a virtual input, ie one which generates its value based on other inputs and so has no mapping.
*/
bool IsVirtual();
bool IsVirtual() const;
/*
* Configures the current mapping(s) assigned to this input by asking the user for input.
@@ -191,7 +197,7 @@ public:
/*
* Returns true if the value of this input changed during the last poll.
*/
bool Changed();
bool Changed() const;
/*
* Sends a force feedback command to the input source of this input.
@@ -203,18 +209,18 @@ public:
// Inlined methods
//
inline bool CInput::IsUIInput()
inline bool CInput::IsUIInput() const
{
return gameFlags == Game::INPUT_UI;
}
inline bool CInput::IsConfigurable()
inline bool CInput::IsConfigurable() const
{
// All inputs except UI and virtual ones can be configured by the user
return (gameFlags != Game::INPUT_UI) && !(flags & INPUT_FLAGS_VIRTUAL);
}
inline bool CInput::IsVirtual()
inline bool CInput::IsVirtual() const
{
return !!(flags & INPUT_FLAGS_VIRTUAL);
}
+9 -6
View File
@@ -27,12 +27,15 @@
* values for analog inputs.
*/
#include "InputSource.h"
#include "Supermodel.h"
#include "Input.h"
#include "InputSystem.h"
#include <vector>
using namespace std;
CInputSource::CInputSource(ESourceType sourceType) : type(sourceType), m_acquired(0)
CInputSource::CInputSource(ESourceType sourceType) : m_acquired(0), type(sourceType)
{
//
}
@@ -113,7 +116,7 @@ int CInputSource::Scale(int val, int fromMinVal, int fromOffVal, int fromMaxVal,
toRange = (double)(toMaxVal - toOffVal);
else
toRange = (double)(toOffVal - toMinVal);
return toOffVal + (int)(toRange * frac);
return toOffVal + (int)(toRange * frac);
}
else
{
@@ -121,11 +124,11 @@ int CInputSource::Scale(int val, int fromMinVal, int fromOffVal, int fromMaxVal,
toRange = (double)(toOffVal - toMaxVal);
else
toRange = (double)(toMinVal - toOffVal);
return toOffVal - (int)(toRange * frac);
return toOffVal - (int)(toRange * frac);
}
}
bool CInputSource::IsActive()
bool CInputSource::IsActive() const
{
bool boolVal;
return GetValueAsSwitch(boolVal);
@@ -134,4 +137,4 @@ bool CInputSource::IsActive()
bool CInputSource::SendForceFeedbackCmd(ForceFeedbackCmd ffCmd)
{
return false;
}
}
+4 -4
View File
@@ -30,7 +30,6 @@
#include <string>
class CInputSystem;
struct ForceFeedbackCmd;
/*
@@ -54,6 +53,7 @@ protected:
unsigned m_acquired;
CInputSource(ESourceType sourceType);
virtual ~CInputSource() {};
public:
/*
@@ -88,13 +88,13 @@ public:
/*
* Returns true if the source is active (taken from GetValueAsSwitch).
*/
bool IsActive();
bool IsActive() const;
/*
* Reads a boolean value for a switch input.
* Returns true if the value was set.
*/
virtual bool GetValueAsSwitch(bool &val) = 0;
virtual bool GetValueAsSwitch(bool &val) const = 0;
/*
* Reads an int value for an analog/axis input, guaranteeing that the value falls within the given range and has the correct off value when required.
@@ -108,4 +108,4 @@ public:
virtual bool SendForceFeedbackCmd(ForceFeedbackCmd ffCmd);
};
#endif // INCLUDED_INPUTSOURCE_H
#endif // INCLUDED_INPUTSOURCE_H
Loaded 100 of 357 files, more files were not shown because too many files have changed in this diff. Show more