mirror of
https://github.com/libretro/Libretro-Supermodel.git
synced 2026-11-07 11:15:58 -10:00
Updated supermodel emulator to the current version.
This commit is contained in:
1 parent
f980705438
commit
ab088efe34
357 files changed
+161097
-87325
No files matched your search
@@ -5,6 +5,10 @@
|
||||
*.dll
|
||||
*.dylib
|
||||
*.lib
|
||||
*.zip
|
||||
*.log
|
||||
*.info
|
||||
*.nv
|
||||
*.pdb
|
||||
*.exp
|
||||
*.manifest
|
||||
|
||||
Vendored
+18
@@ -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
|
||||
}
|
||||
Vendored
+31
@@ -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)"
|
||||
}
|
||||
]
|
||||
}
|
||||
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"files.exclude": {
|
||||
"**/*.o": true,
|
||||
"**/*.log": true,
|
||||
"**/*.info": true,
|
||||
"**/*.nv": true,
|
||||
"**/*.zip": true,
|
||||
"**/*.so/**": true
|
||||
|
||||
}
|
||||
}
|
||||
Vendored
+31
@@ -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": []
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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):
|
||||
|
||||
+11901
-12062
File diff suppressed because it is too large.
Load diff
+13255
-13239
File diff suppressed because it is too large.
Load diff
+8873
-8867
File diff suppressed because it is too large.
Load diff
+2102
-2093
File diff suppressed because it is too large.
Load diff
+1984
-1984
File diff suppressed because it is too large.
Load diff
+130
-214
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#ifndef INCLUDED_BUS_H
|
||||
#define INCLUDED_BUS_H
|
||||
|
||||
#include "Types.h"
|
||||
|
||||
/*
|
||||
* IBus:
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -28,6 +28,9 @@
|
||||
#ifndef INCLUDED_PPC_H
|
||||
#define INCLUDED_PPC_H
|
||||
|
||||
#include "BlockFile.h"
|
||||
#include "Types.h"
|
||||
#include "Debugger/CPU/PPCDebug.h"
|
||||
|
||||
/******************************************************************************
|
||||
Definitions
|
||||
|
||||
@@ -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
File diff suppressed because it is too large.
Load diff
+498
-496
File diff suppressed because it is too large.
Load diff
+1
-1
@@ -157,7 +157,7 @@ public:
|
||||
* Returns:
|
||||
* Current value of PC register.
|
||||
*/
|
||||
UINT16 GetPC(void);
|
||||
UINT16 GetPC(void) const;
|
||||
|
||||
#ifdef SUPERMODEL_DEBUGGER
|
||||
/*
|
||||
|
||||
Regular → Executable
File mode changed.
Regular → Executable
File mode changed.
@@ -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
@@ -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
@@ -19,8 +19,8 @@
|
||||
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||
**/
|
||||
|
||||
/*
|
||||
* Z80KDebug.h
|
||||
/*
|
||||
* Z80KDebug.h
|
||||
*/
|
||||
|
||||
#ifdef SUPERMODEL_DEBUGGER
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||
**/
|
||||
|
||||
/*
|
||||
* Exception.cpp
|
||||
/*
|
||||
* Exception.cpp
|
||||
*/
|
||||
|
||||
#ifdef SUPERMODEL_DEBUGGER
|
||||
|
||||
Regular → Executable
File mode changed.
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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 ®ion_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 ®ions_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 ®ion_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 ®ion_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 ®ion_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 ®ion_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 ®ion, 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 ®ion, 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 ®ion = v.second;
|
||||
uint32_t region_size = 0;
|
||||
bool error_loading_region = false;
|
||||
|
||||
// Attempt to load the region
|
||||
if (ComputeRegionSize(®ion_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
@@ -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 ®ion_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 ®ion, 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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
|
||||
+868
-785
File diff suppressed because it is too large.
Load diff
+77
-49
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
|
||||
struct LOD
|
||||
{
|
||||
float startRange; // possibly specified as angles also, yeah who knows >_<
|
||||
float deleteRange;
|
||||
float deleteSize;
|
||||
float blendFactor;
|
||||
};
|
||||
|
||||
struct LODFeatureType
|
||||
|
||||
@@ -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,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
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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";
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
File diff suppressed because it is too large.
Load diff
+175
-174
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
Reference in new issue
Block a user