Add project files.
This commit is contained in:
parent
d40149497b
commit
54d6fc8c1e
22
CMakeLists.txt
Normal file
22
CMakeLists.txt
Normal file
|
@ -0,0 +1,22 @@
|
|||
# CMakeList.txt : CMake project for GameboyEmu, include source and define
|
||||
# project specific logic here.
|
||||
#
|
||||
cmake_minimum_required (VERSION 3.8)
|
||||
|
||||
project ("GameboyEmu")
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
|
||||
|
||||
find_package(SDL2 CONFIG REQUIRED)
|
||||
find_package(glad CONFIG REQUIRED)
|
||||
find_package(imgui CONFIG REQUIRED)
|
||||
|
||||
file(GLOB_RECURSE HEADER_FILES
|
||||
"include/*.hpp"
|
||||
)
|
||||
|
||||
# Add source to this project's executable.
|
||||
add_subdirectory(src)
|
||||
|
||||
# TODO: Add tests and install targets if needed.
|
29
CMakeSettings.json
Normal file
29
CMakeSettings.json
Normal file
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "x64-Debug",
|
||||
"generator": "Ninja",
|
||||
"configurationType": "Debug",
|
||||
"inheritEnvironments": [ "clang_cl_x64" ],
|
||||
"buildRoot": "${projectDir}\\out\\build\\${name}",
|
||||
"installRoot": "${projectDir}\\out\\install\\${name}",
|
||||
"cmakeCommandArgs": "",
|
||||
"buildCommandArgs": "",
|
||||
"ctestCommandArgs": "",
|
||||
"cmakeToolchain": "C:/Users/Robert/source/repos/vcpkg/scripts/buildsystems/vcpkg.cmake"
|
||||
},
|
||||
{
|
||||
"name": "x64-Clang-Release",
|
||||
"generator": "Ninja",
|
||||
"configurationType": "RelWithDebInfo",
|
||||
"buildRoot": "${projectDir}\\out\\build\\${name}",
|
||||
"installRoot": "${projectDir}\\out\\install\\${name}",
|
||||
"cmakeCommandArgs": "",
|
||||
"buildCommandArgs": "",
|
||||
"ctestCommandArgs": "",
|
||||
"cmakeToolchain": "C:/Users/Robert/source/repos/vcpkg/scripts/buildsystems/vcpkg.cmake",
|
||||
"inheritEnvironments": [ "clang_cl_x64_x64" ],
|
||||
"variables": []
|
||||
}
|
||||
]
|
||||
}
|
7
ReadMe.md
Normal file
7
ReadMe.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
# yabGBE
|
||||
(pronounced yab G. B. E.)
|
||||
|
||||
yabGBE stands for "Yet another bad gameboy emulator" and it already
|
||||
perfectly encapsulates what this is. This emulator is unfinished, missing a lot of features and the few features that are implemented seem to be of questionable quality.
|
||||
|
||||
This is my first ever emulator programming project I tackled all by myself, without looking at explicit tutorials. I only used hardware docs and the gbdev wiki to get all my informations and knowledge.
|
81
include/bus.hpp
Normal file
81
include/bus.hpp
Normal file
|
@ -0,0 +1,81 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "util.hpp"
|
||||
#include "cpu.hpp"
|
||||
#include "lcd.hpp"
|
||||
#include "rom.hpp"
|
||||
|
||||
typedef union
|
||||
{
|
||||
BYTE b;
|
||||
struct
|
||||
{
|
||||
BYTE select : 2;
|
||||
BYTE enable : 1;
|
||||
BYTE padding : 5;
|
||||
} w;
|
||||
} TimerControl;
|
||||
|
||||
typedef union
|
||||
{
|
||||
BYTE b;
|
||||
struct
|
||||
{
|
||||
BYTE rightA : 1;
|
||||
BYTE leftB : 1;
|
||||
BYTE upSelect : 1;
|
||||
BYTE downStart : 1;
|
||||
BYTE selectDirKeys : 1;
|
||||
BYTE selectButtonKeys : 1;
|
||||
BYTE unused : 2;
|
||||
} w;
|
||||
} JoypadReg;
|
||||
|
||||
struct Joypad
|
||||
{
|
||||
bool a, b, up, down, left, right, start, select;
|
||||
};
|
||||
|
||||
class Bus
|
||||
{
|
||||
public:
|
||||
Bus();
|
||||
~Bus();
|
||||
|
||||
void AttachCPU(CPU& cpu);
|
||||
void AttachLCD(LCD& lcd);
|
||||
void InsertROM(ROM& rom);
|
||||
|
||||
bool Tick();
|
||||
bool Execute();
|
||||
bool Frame();
|
||||
|
||||
BYTE Read(WORD addr);
|
||||
void Write(WORD addr, BYTE val);
|
||||
BYTE Fetch(WORD addr);
|
||||
|
||||
private:
|
||||
BYTE& GetReference(WORD addr);
|
||||
|
||||
public:
|
||||
ROM* rom;
|
||||
CPU* cpu;
|
||||
LCD* lcd;
|
||||
|
||||
BYTE invalid;
|
||||
BYTE div;
|
||||
BYTE tima;
|
||||
BYTE tma;
|
||||
BYTE dmg_rom;
|
||||
JoypadReg joypadReg;
|
||||
TimerControl tac;
|
||||
size_t internalCounter = 0;
|
||||
|
||||
Joypad joypad;
|
||||
|
||||
// std::array<BYTE, 0x2000> vram;
|
||||
std::array<BYTE, 0x2000> wram;
|
||||
std::array<BYTE, 0x80> hram;
|
||||
};
|
110
include/cpu.hpp
Normal file
110
include/cpu.hpp
Normal file
|
@ -0,0 +1,110 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include "util.hpp"
|
||||
|
||||
class Bus;
|
||||
|
||||
struct Register
|
||||
{
|
||||
union
|
||||
{
|
||||
WORD w;
|
||||
struct
|
||||
{
|
||||
BYTE lo, hi;
|
||||
} b;
|
||||
};
|
||||
|
||||
char name[3];
|
||||
};
|
||||
|
||||
typedef union {
|
||||
BYTE b;
|
||||
struct
|
||||
{
|
||||
BYTE vblank : 1;
|
||||
BYTE lcd_stat : 1;
|
||||
BYTE timer : 1;
|
||||
BYTE serial : 1;
|
||||
BYTE joypad : 1;
|
||||
BYTE padding : 3;
|
||||
} flags;
|
||||
} Interrupt;
|
||||
|
||||
typedef union
|
||||
{
|
||||
BYTE b;
|
||||
struct
|
||||
{
|
||||
BYTE unused : 4;
|
||||
BYTE carry : 1;
|
||||
BYTE halfCarry : 1;
|
||||
BYTE negative : 1;
|
||||
BYTE zero : 1;
|
||||
} f;
|
||||
} StatusFlag;
|
||||
|
||||
typedef union
|
||||
{
|
||||
BYTE b;
|
||||
|
||||
struct
|
||||
{
|
||||
BYTE z : 3;
|
||||
BYTE y : 3;
|
||||
BYTE x : 2;
|
||||
} xyz;
|
||||
|
||||
struct
|
||||
{
|
||||
BYTE padding1 : 3;
|
||||
BYTE q : 1;
|
||||
BYTE p : 2;
|
||||
BYTE padding2 : 2;
|
||||
} pq;
|
||||
} Opcode;
|
||||
|
||||
class CPU
|
||||
{
|
||||
public:
|
||||
void Powerup();
|
||||
void Tick();
|
||||
|
||||
friend class Bus;
|
||||
|
||||
public:
|
||||
Interrupt interruptEnable;
|
||||
Interrupt interruptFlag;
|
||||
|
||||
size_t totalCycles;
|
||||
BYTE cycles;
|
||||
|
||||
Register AF; // Acc & Flags
|
||||
Register BC;
|
||||
Register DE;
|
||||
Register HL;
|
||||
Register SP; // Stack pointer
|
||||
Register PC; // Program counter
|
||||
StatusFlag* flag;
|
||||
|
||||
Opcode opcode;
|
||||
|
||||
std::array<Register*, 4> rp;
|
||||
std::array<Register*, 4> rp2;
|
||||
|
||||
BYTE ime;
|
||||
|
||||
Bus* bus;
|
||||
|
||||
bool stopped;
|
||||
bool halted;
|
||||
bool justHaltedWithDI;
|
||||
|
||||
private:
|
||||
void WriteToRegister(BYTE reg, BYTE val);
|
||||
BYTE ReadFromRegister(BYTE reg);
|
||||
void ALU(BYTE operation, BYTE operand);
|
||||
void CBPrefixed();
|
||||
};
|
129
include/lcd.hpp
Normal file
129
include/lcd.hpp
Normal file
|
@ -0,0 +1,129 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include "util.hpp"
|
||||
|
||||
class Bus;
|
||||
|
||||
typedef union
|
||||
{
|
||||
BYTE b;
|
||||
struct
|
||||
{
|
||||
BYTE mode : 2;
|
||||
BYTE coincidence : 1;
|
||||
BYTE mode0 : 1;
|
||||
BYTE mode1 : 1;
|
||||
BYTE mode2 : 1;
|
||||
BYTE lyc : 1;
|
||||
} w;
|
||||
} STAT;
|
||||
|
||||
typedef union
|
||||
{
|
||||
BYTE b;
|
||||
struct
|
||||
{
|
||||
BYTE priority : 1;
|
||||
BYTE obj_enable : 1;
|
||||
BYTE obj_size : 1;
|
||||
BYTE bg_tilemap : 1;
|
||||
BYTE tiledata : 1;
|
||||
BYTE window : 1;
|
||||
BYTE window_tilemap : 1;
|
||||
BYTE enable : 1;
|
||||
} w;
|
||||
} LCDC;
|
||||
|
||||
typedef union
|
||||
{
|
||||
WORD w;
|
||||
|
||||
struct
|
||||
{
|
||||
BYTE lo, hi;
|
||||
} b;
|
||||
} LCDRegister;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
WORD sprite;
|
||||
WORD highByte, lowByte;
|
||||
WORD full;
|
||||
} PixelFIFO;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
WORD tile;
|
||||
BYTE cycle;
|
||||
BYTE x, y;
|
||||
BYTE lo, hi;
|
||||
} PixelFetcher;
|
||||
|
||||
typedef union
|
||||
{
|
||||
QWORD q;
|
||||
|
||||
struct
|
||||
{
|
||||
BYTE y;
|
||||
BYTE x;
|
||||
BYTE idx;
|
||||
|
||||
struct
|
||||
{
|
||||
BYTE padding : 4;
|
||||
BYTE palette : 1;
|
||||
BYTE xFlip : 1;
|
||||
BYTE yFlip : 1;
|
||||
BYTE bgPriority : 1;
|
||||
} attr;
|
||||
} b;
|
||||
} OAMEntry;
|
||||
|
||||
class LCD
|
||||
{
|
||||
public:
|
||||
void Setup();
|
||||
void Tick();
|
||||
|
||||
BYTE& GetReferenceToAddress(WORD addr, bool& handled);
|
||||
|
||||
bool Read(WORD addr, BYTE& val);
|
||||
bool Write(WORD addr, BYTE val);
|
||||
|
||||
DWORD cycles;
|
||||
WORD scanlineCycles;
|
||||
|
||||
friend class Bus;
|
||||
friend class CPU;
|
||||
|
||||
public:
|
||||
std::array<BYTE, 160 * 144> display;
|
||||
std::array<BYTE, 0x2000> vram;
|
||||
std::array<BYTE, 0xA0> oam;
|
||||
|
||||
public:
|
||||
Bus* bus;
|
||||
|
||||
// Registers
|
||||
LCDC lcdc;
|
||||
STAT stat;
|
||||
BYTE scy;
|
||||
BYTE scx;
|
||||
BYTE ly;
|
||||
BYTE lyc;
|
||||
BYTE wy;
|
||||
BYTE wx;
|
||||
BYTE bgp;
|
||||
BYTE obp0;
|
||||
BYTE obp1;
|
||||
BYTE dma;
|
||||
|
||||
PixelFetcher fetcher;
|
||||
PixelFIFO bgFIFO;
|
||||
PixelFIFO spriteFIFO;
|
||||
|
||||
BYTE x;
|
||||
BYTE dmaCycles;
|
||||
};
|
19
include/mbcs/Imbc.hpp
Normal file
19
include/mbcs/Imbc.hpp
Normal file
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
#include "util.hpp"
|
||||
|
||||
class IMBC
|
||||
{
|
||||
public:
|
||||
IMBC(WORD romBanks, WORD ramBanks, WORD ramSize) :
|
||||
romBanks(romBanks), ramBanks(ramBanks), ramSize(ramSize)
|
||||
{ }
|
||||
|
||||
virtual ~IMBC() {}
|
||||
|
||||
virtual bool GetMappedRead(WORD address, DWORD& mappedAddr) = 0;
|
||||
virtual bool GetMappedWrite(WORD address, BYTE val, DWORD& mappedAddr) = 0;
|
||||
|
||||
protected:
|
||||
WORD romBanks, ramBanks, ramSize;
|
||||
};
|
29
include/mbcs/mbc0.hpp
Normal file
29
include/mbcs/mbc0.hpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
#pragma once
|
||||
|
||||
#include "Imbc.hpp"
|
||||
|
||||
class MBC0 : public IMBC
|
||||
{
|
||||
public:
|
||||
MBC0(WORD ramBanks) : IMBC(2, ramBanks, 8) {}
|
||||
|
||||
virtual bool GetMappedRead(WORD address, DWORD& mappedAddress) override;
|
||||
virtual bool GetMappedWrite(WORD address, BYTE val, DWORD& mappedAddress) override;
|
||||
private:
|
||||
};
|
||||
|
||||
inline bool MBC0::GetMappedRead(WORD address, DWORD& mappedAddress)
|
||||
{
|
||||
mappedAddress = address;
|
||||
return (address < 0x8000);
|
||||
}
|
||||
|
||||
inline bool MBC0::GetMappedWrite(WORD address, BYTE val, DWORD& mappedAddress)
|
||||
{
|
||||
if (ramBanks && 0xA000 <= address && address < 0xC000)
|
||||
{
|
||||
mappedAddress = address;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
70
include/mbcs/mbc1.hpp
Normal file
70
include/mbcs/mbc1.hpp
Normal file
|
@ -0,0 +1,70 @@
|
|||
#pragma once
|
||||
|
||||
#include "Imbc.hpp"
|
||||
|
||||
class MBC1 : public IMBC
|
||||
{
|
||||
public:
|
||||
MBC1(WORD romBanks, WORD ramBanks, WORD ramSize) : IMBC(romBanks, ramBanks, ramSize) {}
|
||||
|
||||
virtual bool GetMappedRead(WORD address, DWORD& mappedAddr) override;
|
||||
virtual bool GetMappedWrite(WORD address, BYTE val, DWORD& mappedAddr) override;
|
||||
|
||||
private:
|
||||
BYTE RamEnable = 0x00;
|
||||
BYTE RomBankNumber = 0x01;
|
||||
BYTE RamBankNumber = 0x00;
|
||||
BYTE ModeSelect = 0x00;
|
||||
};
|
||||
|
||||
inline bool MBC1::GetMappedRead(WORD address, DWORD& mappedAddr)
|
||||
{
|
||||
if (address < 0x4000)
|
||||
{
|
||||
mappedAddr = address;
|
||||
return true;
|
||||
}
|
||||
else if(0x4000 <= address && address < 0x8000)
|
||||
{
|
||||
mappedAddr = ((DWORD)((RamBankNumber << (5 * !ModeSelect)) | RomBankNumber) * 0x4000) + (address & 0x3FFF);
|
||||
return true;
|
||||
}
|
||||
else if (0xA000 <= address && address < 0xC000)
|
||||
{
|
||||
mappedAddr = (DWORD)((RamBankNumber * ModeSelect) * 0x2000) + (address & 0x1FFF);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool MBC1::GetMappedWrite(WORD address, BYTE val, DWORD& mappedAddr)
|
||||
{
|
||||
if (0x0000 <= address && address < 0x2000)
|
||||
{
|
||||
RamEnable = val;
|
||||
return false;
|
||||
}
|
||||
else if (0x2000 <= address && address < 0x4000)
|
||||
{
|
||||
RomBankNumber = val;
|
||||
if (RomBankNumber == 0x00 || RomBankNumber == 0x20 || RomBankNumber == 0x40 || RomBankNumber == 0x60)
|
||||
RomBankNumber += 1;
|
||||
return false;
|
||||
}
|
||||
else if (0x4000 <= address && address < 0x6000)
|
||||
{
|
||||
RamBankNumber = val;
|
||||
return false;
|
||||
}
|
||||
else if (0x6000 <= address && address < 0x8000)
|
||||
{
|
||||
ModeSelect = val;
|
||||
return false;
|
||||
}
|
||||
else if (ramBanks == 0 && 0xA000 <= address && address < 0xC000)
|
||||
return false;
|
||||
|
||||
mappedAddr = (DWORD)((RamBankNumber * ModeSelect) * 0x2000) + (address & 0x1FFF);
|
||||
return true;
|
||||
}
|
37
include/rom.hpp
Normal file
37
include/rom.hpp
Normal file
|
@ -0,0 +1,37 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include "util.hpp"
|
||||
|
||||
#include "mbcs/Imbc.hpp"
|
||||
|
||||
class Bus;
|
||||
|
||||
struct MemoryBankController
|
||||
{
|
||||
BYTE w;
|
||||
struct
|
||||
{
|
||||
BYTE ROMBankNumber : 5;
|
||||
BYTE RAMBankNumber : 2;
|
||||
BYTE Mode : 1;
|
||||
} b;
|
||||
};
|
||||
|
||||
class ROM
|
||||
{
|
||||
public:
|
||||
ROM(FILE* f);
|
||||
|
||||
BYTE Read(WORD addr);
|
||||
void Write(WORD addr, BYTE val);
|
||||
|
||||
friend class Bus;
|
||||
|
||||
private:
|
||||
Bus* bus;
|
||||
std::unique_ptr<IMBC> mbc;
|
||||
|
||||
std::vector<BYTE> data, ram;
|
||||
};
|
19
include/util.hpp
Normal file
19
include/util.hpp
Normal file
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef unsigned char BYTE;
|
||||
typedef unsigned short WORD;
|
||||
typedef unsigned int DWORD;
|
||||
typedef unsigned long long QWORD;
|
||||
|
||||
#define EXIT_MSG(msg, ...) { \
|
||||
fprintf(stderr, (msg), __VA_ARGS__); \
|
||||
fprintf(stderr, "\n"); \
|
||||
fprintf(stderr, "%s\n", strerror(errno)); \
|
||||
fprintf(stderr, "Exiting.\n"); \
|
||||
}
|
||||
|
||||
inline BYTE undefined;
|
BIN
res/cpu_instrs.gb
Normal file
BIN
res/cpu_instrs.gb
Normal file
Binary file not shown.
BIN
res/mario.gb
Normal file
BIN
res/mario.gb
Normal file
Binary file not shown.
26
src/CMakeLists.txt
Normal file
26
src/CMakeLists.txt
Normal file
|
@ -0,0 +1,26 @@
|
|||
file(GLOB_RECURSE IMGUI_SRC
|
||||
"${CMAKE_SOURCE_DIR}/vendor/imgui/src/*.cpp"
|
||||
)
|
||||
|
||||
add_executable(yabgbe)
|
||||
target_sources(yabgbe
|
||||
PRIVATE
|
||||
"main.cpp" "bus.cpp" "cpu.cpp" "rom.cpp" "lcd.cpp"
|
||||
${IMGUI_SRC}
|
||||
PUBLIC
|
||||
${HEADER_FILES}
|
||||
)
|
||||
|
||||
target_include_directories(yabgbe
|
||||
PUBLIC
|
||||
"${CMAKE_SOURCE_DIR}/include"
|
||||
"${CMAKE_SOURCE_DIR}/vendor/imgui/include"
|
||||
SDL2::SDL2 glad::glad
|
||||
)
|
||||
|
||||
target_link_libraries(yabgbe
|
||||
SDL2::SDL2 SDL2::SDL2main
|
||||
glad::glad
|
||||
)
|
||||
|
||||
target_compile_options(yabgbe PRIVATE -Wimplicit-fallthrough)
|
195
src/bus.cpp
Normal file
195
src/bus.cpp
Normal file
|
@ -0,0 +1,195 @@
|
|||
#include "bus.hpp"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static WORD timerModuloLookup[4] = { 1024, 16, 64, 256 };
|
||||
|
||||
Bus::Bus()
|
||||
{
|
||||
// 8KB of VRAM
|
||||
invalid = 0;
|
||||
dmg_rom = 0;
|
||||
tac.b = 0;
|
||||
joypadReg.b = 0xFF;
|
||||
|
||||
joypad.a = true;
|
||||
joypad.b = true;
|
||||
joypad.down = true;
|
||||
joypad.up = true;
|
||||
joypad.right = true;
|
||||
joypad.left = true;
|
||||
joypad.start = true;
|
||||
joypad.select = true;
|
||||
}
|
||||
|
||||
Bus::~Bus()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Bus::AttachCPU(CPU& c)
|
||||
{
|
||||
cpu = &c;
|
||||
c.bus = this;
|
||||
}
|
||||
|
||||
void Bus::AttachLCD(LCD& l)
|
||||
{
|
||||
lcd = &l;
|
||||
l.bus = this;
|
||||
|
||||
lcd->Setup();
|
||||
}
|
||||
|
||||
void Bus::InsertROM(ROM& r)
|
||||
{
|
||||
rom = &r;
|
||||
r.bus = this;
|
||||
}
|
||||
|
||||
bool Bus::Tick()
|
||||
{
|
||||
internalCounter++;
|
||||
|
||||
if(!cpu->stopped)
|
||||
cpu->Tick();
|
||||
|
||||
lcd->Tick();
|
||||
|
||||
if (!(internalCounter % 0xFF))
|
||||
div++;
|
||||
|
||||
if (tac.w.enable && !(internalCounter % timerModuloLookup[tac.w.select]))
|
||||
{
|
||||
tima++;
|
||||
if (tima == 0x00)
|
||||
{
|
||||
tima = tma;
|
||||
cpu->interruptFlag.flags.timer = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Bus::Execute()
|
||||
{
|
||||
while (cpu->cycles > 0)
|
||||
{
|
||||
Tick();
|
||||
if (invalid)
|
||||
return false;
|
||||
}
|
||||
|
||||
Tick();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Bus::Frame()
|
||||
{
|
||||
while (lcd->cycles > 0)
|
||||
{
|
||||
Tick();
|
||||
if (invalid)
|
||||
return false;
|
||||
}
|
||||
|
||||
Tick();
|
||||
return true;
|
||||
}
|
||||
|
||||
BYTE Bus::Read(WORD addr)
|
||||
{
|
||||
BYTE returnVal;
|
||||
if (lcd->Read(addr, returnVal))
|
||||
{
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
if ((addr >= 0x0000 && addr < 0x8000) || (addr >= 0xA000 && addr < 0xC000))
|
||||
{
|
||||
return rom->Read(addr);
|
||||
}
|
||||
|
||||
if (addr == 0xFF00)
|
||||
{
|
||||
if (!joypadReg.w.selectButtonKeys) {
|
||||
joypadReg.w.rightA = joypad.a;
|
||||
joypadReg.w.leftB = joypad.b;
|
||||
joypadReg.w.upSelect = joypad.select;
|
||||
joypadReg.w.downStart = joypad.start;
|
||||
}
|
||||
else if (!joypadReg.w.selectDirKeys)
|
||||
{
|
||||
joypadReg.w.rightA = joypad.right;
|
||||
joypadReg.w.leftB = joypad.left;
|
||||
joypadReg.w.upSelect = joypad.up;
|
||||
joypadReg.w.downStart = joypad.down;
|
||||
}
|
||||
|
||||
return joypadReg.b;
|
||||
}
|
||||
|
||||
return GetReference(addr);
|
||||
}
|
||||
|
||||
BYTE Bus::Fetch(WORD addr)
|
||||
{
|
||||
return Read(addr);
|
||||
}
|
||||
|
||||
void Bus::Write(WORD addr, BYTE val)
|
||||
{
|
||||
if (lcd->Write(addr, val))
|
||||
return;
|
||||
|
||||
if ((addr >= 0x0000 && addr < 0x8000) || (addr >= 0xA000 && addr < 0xC000))
|
||||
{
|
||||
rom->Write(addr, val);
|
||||
return;
|
||||
}
|
||||
|
||||
GetReference(addr) = val;
|
||||
undefined = 0xFF;
|
||||
}
|
||||
|
||||
BYTE& Bus::GetReference(WORD addr)
|
||||
{
|
||||
if (addr >= 0xA000 && addr < 0xC000) // Accessing external RAM
|
||||
{
|
||||
return undefined;
|
||||
}
|
||||
else if (addr >= 0xC000 && addr < 0xFE00) // Accessing WRAM / ECHO RAM
|
||||
{
|
||||
return wram[addr & 0x1FFF];
|
||||
}
|
||||
else if (addr >= 0xFEA0 && addr < 0xFF00) // Accessing unusable area???
|
||||
{
|
||||
return undefined;
|
||||
}
|
||||
else if (addr >= 0xFF00 && addr < 0xFF80) // Accessing I/O regs
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
case 0xFF00: return joypadReg.b;
|
||||
case 0xFF04: return div;
|
||||
case 0xFF05: return tima;
|
||||
case 0xFF06: return tma;
|
||||
case 0xFF07: return tac.b;
|
||||
case 0xFF0F: return cpu->interruptFlag.b;
|
||||
case 0xFF50: return dmg_rom;
|
||||
}
|
||||
}
|
||||
else if (addr >= 0xFF80 && addr < 0xFFFF) // Accessing HRAM
|
||||
{
|
||||
return hram[addr & 0x7F];
|
||||
}
|
||||
else if (addr == 0xFFFF)
|
||||
{
|
||||
return cpu->interruptEnable.b;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
|
||||
}
|
1170
src/cpu.cpp
Normal file
1170
src/cpu.cpp
Normal file
File diff suppressed because it is too large
Load diff
345
src/lcd.cpp
Normal file
345
src/lcd.cpp
Normal file
|
@ -0,0 +1,345 @@
|
|||
#include "lcd.hpp"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "bus.hpp"
|
||||
|
||||
static BYTE colormap[4] = { 0b00000000, 0b00100101, 0b01001010, 0b10010011 };
|
||||
|
||||
BYTE Reverse(BYTE b) {
|
||||
b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
|
||||
b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
|
||||
b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
|
||||
return b;
|
||||
}
|
||||
|
||||
void LCD::Setup()
|
||||
{
|
||||
lcdc.b = 0;
|
||||
stat.b = 0;
|
||||
scy = 0;
|
||||
scx = 0;
|
||||
ly = 0;
|
||||
lyc = 0;
|
||||
wy = 0;
|
||||
wx = 0;
|
||||
|
||||
cycles = 0;
|
||||
scanlineCycles = 0;
|
||||
|
||||
fetcher.cycle = 0;
|
||||
fetcher.x = 0; fetcher.y = -1;
|
||||
x = 0;
|
||||
dmaCycles = 0;
|
||||
|
||||
bgFIFO.full = 0x00;
|
||||
spriteFIFO.full = 0x00;
|
||||
}
|
||||
|
||||
void LCD::Tick()
|
||||
{
|
||||
// Update cycles
|
||||
scanlineCycles++;
|
||||
cycles++;
|
||||
|
||||
if (scanlineCycles > 455)
|
||||
{
|
||||
fetcher.cycle = 0;
|
||||
scanlineCycles = 0;
|
||||
ly += 1;
|
||||
|
||||
if (ly > 153)
|
||||
{
|
||||
cycles = 0;
|
||||
ly = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Set modes
|
||||
stat.w.coincidence = (lyc == ly);
|
||||
|
||||
// Send interrupts
|
||||
if (ly == 144 && scanlineCycles == 0)
|
||||
{
|
||||
bus->cpu->interruptFlag.flags.vblank = 1;
|
||||
}
|
||||
|
||||
if (
|
||||
(stat.w.lyc && stat.w.coincidence) ||
|
||||
(stat.w.mode2 && stat.w.mode == 2) ||
|
||||
(stat.w.mode1 && stat.w.mode == 1) ||
|
||||
(stat.w.mode0 && stat.w.mode == 0)
|
||||
)
|
||||
{
|
||||
bus->cpu->interruptFlag.flags.lcd_stat = 1;
|
||||
}
|
||||
|
||||
// Screen
|
||||
if (ly >= 0 && ly < 144)
|
||||
{
|
||||
if (scanlineCycles == 0)
|
||||
stat.w.mode = 2;
|
||||
|
||||
else if (scanlineCycles == 81) {
|
||||
stat.w.mode = 3;
|
||||
bgFIFO.full = 0x00;
|
||||
|
||||
x = 0;
|
||||
fetcher.x = 0;
|
||||
fetcher.cycle = 0;
|
||||
|
||||
fetcher.y = (fetcher.y + 1) % 8;
|
||||
}
|
||||
|
||||
|
||||
// OAM Search
|
||||
if (stat.w.mode == 2)
|
||||
{
|
||||
OAMEntry* entry;
|
||||
int counter = 0;
|
||||
for (int i = 0; i < 40; i++)
|
||||
{
|
||||
entry = (OAMEntry*)(oam.data() + i * 4);
|
||||
|
||||
if (entry->b.y == ly)
|
||||
{
|
||||
counter++;
|
||||
if (counter > 10)
|
||||
entry->b.y = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pixel Fetcher
|
||||
else if (stat.w.mode == 3)
|
||||
{
|
||||
if (lcdc.w.obj_enable)
|
||||
{
|
||||
OAMEntry* entry;
|
||||
for (int i = 0; i < 40; i++)
|
||||
{
|
||||
entry = (OAMEntry*)(oam.data() + i * 4);
|
||||
|
||||
if (x == entry->b.x - 8 && entry->b.y <= ly + 16 && ly + 8 + (8 * lcdc.w.obj_size) < entry->b.y)
|
||||
{
|
||||
// Fetch Sprite!
|
||||
WORD yOffset = (ly - entry->b.y + 16);
|
||||
if (entry->b.attr.yFlip)
|
||||
{
|
||||
yOffset = 8 * (1 + lcdc.w.obj_size) - yOffset;
|
||||
}
|
||||
|
||||
BYTE lo = vram[2 * yOffset + (entry->b.idx * 16 * (1 + lcdc.w.obj_size))];
|
||||
BYTE hi = vram[2 * yOffset + (entry->b.idx * 16 * (1 + lcdc.w.obj_size)) + 1];
|
||||
if (entry->b.attr.xFlip)
|
||||
{
|
||||
lo = Reverse(lo);
|
||||
hi = Reverse(hi);
|
||||
}
|
||||
|
||||
spriteFIFO.lowByte = lo;
|
||||
spriteFIFO.highByte = hi;
|
||||
spriteFIFO.full = 0xFF;
|
||||
|
||||
BYTE counter = 0;
|
||||
while (spriteFIFO.full)
|
||||
{
|
||||
BYTE color = ((spriteFIFO.highByte & 0x80) >> 6) | ((spriteFIFO.lowByte & 0x80) >> 7);
|
||||
|
||||
if (color != 0x00)
|
||||
{
|
||||
BYTE bgPriority = (bgFIFO.sprite & (0x80 >> counter)) >> 6;
|
||||
if (!bgPriority)
|
||||
{
|
||||
BYTE bgColor = ((bgFIFO.highByte & (0x80 >> counter)) >> 6) | ((bgFIFO.lowByte & (0x80 >> counter)) >> 7);
|
||||
if (entry->b.attr.bgPriority)
|
||||
{
|
||||
if (bgColor == 0x00)
|
||||
{
|
||||
bgFIFO.highByte ^= ((-((spriteFIFO.highByte & 0x80) >> 7) ^ bgFIFO.highByte) & (0x80 >> counter));
|
||||
bgFIFO.lowByte ^= ((-((spriteFIFO.lowByte & 0x80) >> 7) ^ bgFIFO.lowByte) & (0x80 >> counter));
|
||||
bgFIFO.sprite |= (0x80 >> counter);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bgFIFO.highByte ^= ((((spriteFIFO.highByte & 0x80) >> 7) ^ bgFIFO.highByte) & (0x80 >> counter));
|
||||
bgFIFO.lowByte ^= ((((spriteFIFO.lowByte & 0x80) >> 7) ^ bgFIFO.lowByte) & (0x80 >> counter));
|
||||
bgFIFO.sprite |= (0x80 >> counter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
spriteFIFO.full <<= 1;
|
||||
spriteFIFO.highByte <<= 1;
|
||||
spriteFIFO.lowByte <<= 1;
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (fetcher.cycle)
|
||||
{
|
||||
case 0: // Get Tile
|
||||
{
|
||||
// TODO: Can be different (Window)
|
||||
WORD baseAddr = (lcdc.w.bg_tilemap ? 0x1C00 : 0x1800);
|
||||
BYTE fetcherX = ((scx + fetcher.x) & 0xFF) / 8;
|
||||
BYTE fetcherY = ((ly + scy) & 0xFF) / 8;
|
||||
fetcher.tile = vram[baseAddr + (0x20 * fetcherY) + fetcherX];
|
||||
}
|
||||
break;
|
||||
|
||||
case 2: // Get Tile Data Low
|
||||
{
|
||||
WORD baseAddr = (lcdc.w.tiledata ? 0x0000 : 0x0800);
|
||||
fetcher.lo = vram[baseAddr + 2 * ((fetcher.y + scy) % 8) + (fetcher.tile * 16)];
|
||||
}
|
||||
break;
|
||||
|
||||
case 4: // Get Tile Data High
|
||||
{
|
||||
// TODO: Check LCDC.4
|
||||
WORD baseAddr = (lcdc.w.tiledata ? 0x0000 : 0x0800);
|
||||
fetcher.hi = vram[baseAddr + 2 * ((fetcher.y + scy) % 8) + (fetcher.tile * 16) + 1];
|
||||
}
|
||||
break;
|
||||
|
||||
case 8: // Push
|
||||
if (!(bgFIFO.full & 0x00FF))
|
||||
{
|
||||
bgFIFO.lowByte |= fetcher.lo;
|
||||
bgFIFO.highByte |= fetcher.hi;
|
||||
bgFIFO.sprite |= 0x00;
|
||||
bgFIFO.full |= 0xFF;
|
||||
fetcher.cycle = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
fetcher.cycle--;
|
||||
}
|
||||
|
||||
fetcher.x--;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
fetcher.cycle++;
|
||||
fetcher.x++;
|
||||
|
||||
// Draw pixels
|
||||
if (bgFIFO.full & 0x00FF) // If Data in FIFO
|
||||
{
|
||||
BYTE color = ((bgFIFO.highByte & 0x80) >> 6) | ((bgFIFO.lowByte & 0x80) >> 7);
|
||||
|
||||
display[ly * 160 + x] = colormap[color];
|
||||
x++;
|
||||
|
||||
bgFIFO.highByte <<= 1;
|
||||
bgFIFO.lowByte <<= 1;
|
||||
bgFIFO.full <<= 1;
|
||||
bgFIFO.sprite <<= 1;
|
||||
}
|
||||
|
||||
if (x == 160)
|
||||
{
|
||||
stat.w.mode = 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else if (ly == 144)
|
||||
{
|
||||
stat.w.mode = 1;
|
||||
fetcher.y = -1;
|
||||
}
|
||||
}
|
||||
|
||||
bool LCD::Read(WORD addr, BYTE& val)
|
||||
{
|
||||
if (0x8000 <= addr && addr < 0xA000) // VRAM
|
||||
{
|
||||
if (stat.w.mode != 3 || !lcdc.w.enable)
|
||||
val = vram[addr & 0x1FFF];
|
||||
else
|
||||
val = undefined;
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (0xFE00 <= addr && addr < 0xFEA0) // OAM
|
||||
{
|
||||
if (stat.w.mode == 0 || stat.w.mode == 1 || !lcdc.w.enable)
|
||||
val = oam[addr & 0x9F];
|
||||
else
|
||||
val = undefined;
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (0xFF00 <= addr && addr < 0xFF80) // I/O
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
case 0xFF40: val = lcdc.b; return true;
|
||||
case 0xFF41: val = stat.b; return true;
|
||||
case 0xFF42: val = scy; return true;
|
||||
case 0xFF43: val = scx; return true;
|
||||
case 0xFF44: val = ly; return true;
|
||||
case 0xFF45: val = lyc; return true;
|
||||
case 0xFF47: val = bgp; return true;
|
||||
case 0xFF48: val = obp0; return true;
|
||||
case 0xFF49: val = obp1; return true;
|
||||
case 0xFF4A: val = wy; return true;
|
||||
case 0xFF4B: val = wx; return true;
|
||||
|
||||
case 0xFF46: val = dma; return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LCD::Write(WORD addr, BYTE val)
|
||||
{
|
||||
if (0x8000 <= addr && addr < 0xA000) // VRAM
|
||||
{
|
||||
if (stat.w.mode != 3 || !lcdc.w.enable)
|
||||
vram[addr & 0x1FFF] = val;
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (0xFE00 <= addr && addr < 0xFEA0) // OAM
|
||||
{
|
||||
if (stat.w.mode == 0 || stat.w.mode == 1 || !lcdc.w.enable)
|
||||
oam[addr & 0x9F] = val;
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (0xFF00 <= addr && addr < 0xFF80) // I/O
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
case 0xFF40: lcdc.b = val; return true;
|
||||
case 0xFF41: stat.b = val; return true;
|
||||
case 0xFF42: scy = val; return true;
|
||||
case 0xFF43: scx = val; return true;
|
||||
case 0xFF44: ly = val; return true;
|
||||
case 0xFF45: lyc = val; return true;
|
||||
case 0xFF47: bgp = val; return true;
|
||||
case 0xFF48: obp0 = val; return true;
|
||||
case 0xFF49: obp1 = val; return true;
|
||||
case 0xFF4A: wy = val; return true;
|
||||
case 0xFF4B: wx = val; return true;
|
||||
|
||||
case 0xFF46: dma = val; dmaCycles = 160;
|
||||
}
|
||||
|
||||
while (dmaCycles != 0)
|
||||
{
|
||||
dmaCycles--;
|
||||
oam[dmaCycles] = bus->Read(((WORD)dma) << 8 | dmaCycles);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
388
src/main.cpp
Normal file
388
src/main.cpp
Normal file
|
@ -0,0 +1,388 @@
|
|||
#include "bus.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <SDL.h>
|
||||
#include <imgui.h>
|
||||
#include <imgui_sdl.h>
|
||||
|
||||
static BYTE colormap[4] = { 0b00000000, 0b00100101, 0b01001010, 0b10010011 };
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
// Calculate the size of the window? This is literally random lol
|
||||
int width = (512 + 384) * 2 + 10;
|
||||
int height = 256 * 4;
|
||||
|
||||
SDL_Init(SDL_INIT_VIDEO);
|
||||
|
||||
SDL_Window* window = SDL_CreateWindow("Gameboy Emulator", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, SDL_WINDOW_SHOWN);
|
||||
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
|
||||
SDL_Event e;
|
||||
|
||||
// Initialize ImGui
|
||||
// I use ImGui to display lots of useful info, mainly memory and registers
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
ImGuiSDL::Initialize(renderer, width, height);
|
||||
|
||||
ImGui::StyleColorsDark();
|
||||
|
||||
// To avoid some ImGui weirdness, we need to clear our screen using a texture
|
||||
SDL_Texture* clearScreen = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, 1, 1);
|
||||
{
|
||||
SDL_SetRenderTarget(renderer, clearScreen);
|
||||
SDL_SetRenderDrawColor(renderer, 100, 0, 100, 255);
|
||||
SDL_RenderClear(renderer);
|
||||
SDL_SetRenderTarget(renderer, NULL);
|
||||
}
|
||||
|
||||
// All the textures to old the info we'll be displaying
|
||||
SDL_Texture* ramScreen = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGB332, SDL_TEXTUREACCESS_STREAMING, 128, 64);
|
||||
SDL_Texture* vramScreen = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGB332, SDL_TEXTUREACCESS_STREAMING, 128, 64);
|
||||
SDL_Texture* tilemapRaw1 = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGB332, SDL_TEXTUREACCESS_STREAMING, 32, 32);
|
||||
SDL_Texture* tilemapRaw2 = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGB332, SDL_TEXTUREACCESS_STREAMING, 32, 32);
|
||||
SDL_Texture* tilemap1 = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGB332, SDL_TEXTUREACCESS_STREAMING, 256, 256);
|
||||
SDL_Texture* tilemap2 = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGB332, SDL_TEXTUREACCESS_STREAMING, 256, 256);
|
||||
SDL_Texture* hramScreen = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGB332, SDL_TEXTUREACCESS_STREAMING, 16, 8);
|
||||
SDL_Texture* gameboyScreen = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGB332, SDL_TEXTUREACCESS_STREAMING, 160, 144);
|
||||
|
||||
// aspect ratios for the non-square windows
|
||||
float ramAR = 128.f / 64.f;
|
||||
float vramAR = 128.f / 64.f;
|
||||
float hramAR = 16.f / 8.f;
|
||||
float gbAR = 160.f / 144.f;
|
||||
|
||||
// Initialize the gameboy
|
||||
Bus bus;
|
||||
CPU cpu;
|
||||
LCD lcd;
|
||||
|
||||
bus.AttachCPU(cpu);
|
||||
bus.AttachLCD(lcd);
|
||||
|
||||
// Load the rom
|
||||
if (argc != 2)
|
||||
{
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Failed to load ROM", "Usage: gbemu <ROM>\nOr drag and drop a ROM onto the executable.", window);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
FILE* f = fopen(argv[1], "rb");
|
||||
ROM rom(f);
|
||||
fclose(f);
|
||||
|
||||
bus.InsertROM(rom);
|
||||
|
||||
cpu.Powerup();
|
||||
|
||||
// Placeholder vars for pixel arrays used to calcualte the rendered tilemaps
|
||||
BYTE* tilemappixels1;
|
||||
int tilemappitch1;
|
||||
BYTE* tilemappixels2;
|
||||
int tilemappitch2;
|
||||
|
||||
// This is literally irrelevant since I'm using a texture to clear anyways? lol
|
||||
SDL_SetRenderDrawColor(renderer, 100, 0, 100, 255);
|
||||
|
||||
// The main program loop
|
||||
bool done = false;
|
||||
while (!done)
|
||||
{
|
||||
// If the emulator hasn't shit itself yet we can run the Gameboy for one frame
|
||||
if (!bus.invalid)
|
||||
bus.Frame();
|
||||
|
||||
// Also give ImGui all the inputs that happened
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
int wheel = 0;
|
||||
|
||||
// Poll for events
|
||||
while (SDL_PollEvent(&e))
|
||||
{
|
||||
// Boring ImGui stuff
|
||||
if (e.type == SDL_QUIT) done = true;
|
||||
else if (e.type == SDL_WINDOWEVENT)
|
||||
{
|
||||
if (e.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
|
||||
{
|
||||
io.DisplaySize.x = static_cast<float>(e.window.data1);
|
||||
io.DisplaySize.y = static_cast<float>(e.window.data2);
|
||||
}
|
||||
else if(e.window.event == SDL_WINDOWEVENT_CLOSE)
|
||||
{
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
else if (e.type == SDL_MOUSEWHEEL)
|
||||
{
|
||||
wheel = e.wheel.y;
|
||||
}
|
||||
|
||||
// Input for the Joypad. Look at how ugly it is
|
||||
else if (e.type == SDL_KEYUP)
|
||||
{
|
||||
switch (e.key.keysym.sym)
|
||||
{
|
||||
case SDLK_s: bus.joypad.a = true; break;
|
||||
case SDLK_a: bus.joypad.b = true; break;
|
||||
case SDLK_UP: bus.joypad.up = true; break;
|
||||
case SDLK_DOWN: bus.joypad.down = true; break;
|
||||
case SDLK_LEFT: bus.joypad.left = true; break;
|
||||
case SDLK_RIGHT: bus.joypad.right = true; break;
|
||||
case SDLK_LSHIFT: bus.joypad.select = true; break;
|
||||
case SDLK_RETURN: bus.joypad.start = true; break;
|
||||
}
|
||||
}
|
||||
|
||||
else if (e.type == SDL_KEYDOWN)
|
||||
{
|
||||
switch (e.key.keysym.sym)
|
||||
{
|
||||
case SDLK_s: bus.joypad.a = false; cpu.interruptFlag.flags.joypad = 1; break;
|
||||
case SDLK_a: bus.joypad.b = false; cpu.interruptFlag.flags.joypad = 1; break;
|
||||
case SDLK_UP: bus.joypad.up = false; cpu.interruptFlag.flags.joypad = 1; break;
|
||||
case SDLK_DOWN: bus.joypad.down = false; cpu.interruptFlag.flags.joypad = 1; break;
|
||||
case SDLK_LEFT: bus.joypad.left = false; cpu.interruptFlag.flags.joypad = 1; break;
|
||||
case SDLK_RIGHT: bus.joypad.right = false; cpu.interruptFlag.flags.joypad = 1; break;
|
||||
case SDLK_LSHIFT: bus.joypad.select = false; cpu.interruptFlag.flags.joypad = 1; break;
|
||||
case SDLK_RETURN: bus.joypad.start = false; cpu.interruptFlag.flags.joypad = 1; break;
|
||||
}
|
||||
bus.cpu->stopped = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Send all the things that changed to ImGui
|
||||
int mouseX, mouseY;
|
||||
const int buttons = SDL_GetMouseState(&mouseX, &mouseY);
|
||||
|
||||
io.DeltaTime = 1.0f / 60.0f;
|
||||
io.MousePos = ImVec2(static_cast<float>(mouseX), static_cast<float>(mouseY));
|
||||
io.MouseDown[0] = buttons & SDL_BUTTON(SDL_BUTTON_LEFT);
|
||||
io.MouseDown[1] = buttons & SDL_BUTTON(SDL_BUTTON_RIGHT);
|
||||
io.MouseWheel = static_cast<float>(wheel);
|
||||
|
||||
ImGui::NewFrame();
|
||||
|
||||
// Update the memory maps by literally just copying the raw data of the arrays to the texture
|
||||
SDL_UpdateTexture(ramScreen, NULL, bus.wram.data(), 128);
|
||||
SDL_UpdateTexture(vramScreen, NULL, lcd.vram.data(), 128);
|
||||
|
||||
SDL_UpdateTexture(hramScreen, NULL, bus.hram.data(), 16);
|
||||
|
||||
SDL_UpdateTexture(tilemapRaw1, NULL, lcd.vram.data() + 0x1800, 32);
|
||||
SDL_UpdateTexture(tilemapRaw2, NULL, lcd.vram.data() + 0x1C00, 32);
|
||||
|
||||
SDL_UpdateTexture(gameboyScreen, NULL, lcd.display.data(), 160);
|
||||
|
||||
// Just for the rendered tilemap we need to be a bit more elaborate
|
||||
SDL_LockTexture(tilemap1, NULL, (void**)&tilemappixels1, &tilemappitch1);
|
||||
SDL_LockTexture(tilemap2, NULL, (void**)&tilemappixels2, &tilemappitch2);
|
||||
|
||||
// basically this is a quick and dirty PPU background renderer
|
||||
// read about it in the wiki if you wanna know more, i cba to
|
||||
// explain it in a comment here
|
||||
for (int tileY = 0; tileY < 32; tileY++)
|
||||
{
|
||||
for (int tileX = 0; tileX < 32; tileX++)
|
||||
{
|
||||
BYTE tile1ID = lcd.vram[0x1800 + (tileY * 32) + tileX];
|
||||
BYTE tile2ID = lcd.vram[0x1C00 + (tileY * 32) + tileX];
|
||||
|
||||
WORD baseAddr = (lcd.lcdc.w.tiledata ? 0x0000 : 0x0800);
|
||||
WORD addr1 = baseAddr + (tile1ID * 16);
|
||||
WORD addr2 = baseAddr + (tile2ID * 16);
|
||||
|
||||
for (int y = 0; y < 8; y++)
|
||||
{
|
||||
BYTE lo1 = lcd.vram[addr1 + 2 * y];
|
||||
BYTE hi1 = lcd.vram[addr1 + 2 * y + 1];
|
||||
|
||||
BYTE lo2 = lcd.vram[addr2 + 2 * y];
|
||||
BYTE hi2 = lcd.vram[addr2 + 2 * y + 1];
|
||||
for (int x = 0; x < 8; x++)
|
||||
{
|
||||
BYTE loVal1 = (lo1 & (0x80 >> x)) >> (7 - x);
|
||||
BYTE hiVal1 = (hi1 & (0x80 >> x)) >> (7 - x);
|
||||
|
||||
BYTE loVal2 = (lo2 & (0x80 >> x)) >> (7 - x);
|
||||
BYTE hiVal2 = (hi2 & (0x80 >> x)) >> (7 - x);
|
||||
|
||||
tilemappixels1[(tileX * 8 + x) + (32 * 8) * (tileY * 8 + y)] = colormap[loVal1 + hiVal1];
|
||||
tilemappixels2[(tileX * 8 + x) + (32 * 8) * (tileY * 8 + y)] = colormap[loVal2 + hiVal2];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SDL_UnlockTexture(tilemap2);
|
||||
SDL_UnlockTexture(tilemap1);
|
||||
|
||||
|
||||
// Put all the textures in their appropriate ImGui menu
|
||||
ImGui::Begin("WRAM");
|
||||
ImGui::Image(ramScreen, ImVec2(ImGui::GetWindowContentRegionWidth(), ImGui::GetWindowContentRegionWidth() / ramAR));
|
||||
ImGui::End();
|
||||
|
||||
ImGui::Begin("VRAM");
|
||||
ImGui::Text("Raw");
|
||||
ImGui::Image(vramScreen, ImVec2(ImGui::GetWindowContentRegionWidth(), ImGui::GetWindowContentRegionWidth() / vramAR));
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Raw Tilemaps");
|
||||
if (ImGui::BeginTable("tilemaps", 2))
|
||||
{
|
||||
ImGui::TableNextColumn(); ImGui::Image(tilemapRaw1, ImVec2(ImGui::GetWindowContentRegionWidth() / 2, ImGui::GetWindowContentRegionWidth() / 2));
|
||||
ImGui::TableNextColumn(); ImGui::Image(tilemapRaw2, ImVec2(ImGui::GetWindowContentRegionWidth() / 2, ImGui::GetWindowContentRegionWidth() / 2));
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Rendered Tilemaps");
|
||||
if (ImGui::BeginTable("rawtilemaps", 2))
|
||||
{
|
||||
ImGui::TableNextColumn(); ImGui::Image(tilemap1, ImVec2(ImGui::GetWindowContentRegionWidth() / 2, ImGui::GetWindowContentRegionWidth() / 2));
|
||||
ImGui::TableNextColumn(); ImGui::Image(tilemap2, ImVec2(ImGui::GetWindowContentRegionWidth() / 2, ImGui::GetWindowContentRegionWidth() / 2));
|
||||
ImGui::EndTable();
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
ImGui::Begin("HRAM");
|
||||
ImGui::Image(hramScreen, ImVec2(ImGui::GetWindowContentRegionWidth(), ImGui::GetWindowContentRegionWidth() / hramAR));
|
||||
ImGui::End();
|
||||
|
||||
ImGui::Begin("CPU");
|
||||
ImGui::Text("-- Registers --");
|
||||
if (ImGui::BeginTable("Registers", 6))
|
||||
{
|
||||
ImGui::TableNextColumn(); ImGui::Text("AF");
|
||||
ImGui::TableNextColumn(); ImGui::Text("BC");
|
||||
ImGui::TableNextColumn(); ImGui::Text("DE");
|
||||
ImGui::TableNextColumn(); ImGui::Text("HL");
|
||||
ImGui::TableNextColumn(); ImGui::Text("SP");
|
||||
ImGui::TableNextColumn(); ImGui::Text("PC");
|
||||
|
||||
ImGui::TableNextColumn(); ImGui::Text("%04x", cpu.AF.w);
|
||||
ImGui::TableNextColumn(); ImGui::Text("%04x", cpu.BC.w);
|
||||
ImGui::TableNextColumn(); ImGui::Text("%04x", cpu.DE.w);
|
||||
ImGui::TableNextColumn(); ImGui::Text("%04x", cpu.HL.w);
|
||||
ImGui::TableNextColumn(); ImGui::Text("%04x", cpu.SP.w);
|
||||
ImGui::TableNextColumn(); ImGui::Text("%04x", cpu.PC.w);
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
ImGui::Separator();
|
||||
ImGui::Text("-- Status Flags --");
|
||||
if (ImGui::BeginTable("Flags", 4))
|
||||
{
|
||||
ImGui::TableNextColumn(); ImGui::Text("Z");
|
||||
ImGui::TableNextColumn(); ImGui::Text("N");
|
||||
ImGui::TableNextColumn(); ImGui::Text("H");
|
||||
ImGui::TableNextColumn(); ImGui::Text("C");
|
||||
|
||||
ImGui::TableNextColumn(); ImGui::Text("%u", cpu.flag->f.zero);
|
||||
ImGui::TableNextColumn(); ImGui::Text("%u", cpu.flag->f.negative);
|
||||
ImGui::TableNextColumn(); ImGui::Text("%u", cpu.flag->f.halfCarry);
|
||||
ImGui::TableNextColumn(); ImGui::Text("%u", cpu.flag->f.carry);
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
ImGui::Separator();
|
||||
ImGui::Text("-- Interrupts --");
|
||||
ImGui::Text("Interrupts: %s", (cpu.ime ? "ON" : "OFF"));
|
||||
if (ImGui::BeginTable("Interrupts", 6))
|
||||
{
|
||||
ImGui::TableNextColumn(); ImGui::Text("");
|
||||
ImGui::TableNextColumn(); ImGui::Text("V-Blank");
|
||||
ImGui::TableNextColumn(); ImGui::Text("LCD STAT");
|
||||
ImGui::TableNextColumn(); ImGui::Text("Timer");
|
||||
ImGui::TableNextColumn(); ImGui::Text("Serial");
|
||||
ImGui::TableNextColumn(); ImGui::Text("Joypad");
|
||||
|
||||
ImGui::TableNextColumn(); ImGui::Text("IE");
|
||||
ImGui::TableNextColumn(); ImGui::Text("%u", cpu.interruptEnable.flags.vblank);
|
||||
ImGui::TableNextColumn(); ImGui::Text("%u", cpu.interruptEnable.flags.lcd_stat);
|
||||
ImGui::TableNextColumn(); ImGui::Text("%u", cpu.interruptEnable.flags.timer);
|
||||
ImGui::TableNextColumn(); ImGui::Text("%u", cpu.interruptEnable.flags.serial);
|
||||
ImGui::TableNextColumn(); ImGui::Text("%u", cpu.interruptEnable.flags.joypad);
|
||||
|
||||
ImGui::TableNextColumn(); ImGui::Text("IF");
|
||||
ImGui::TableNextColumn(); ImGui::Text("%u", cpu.interruptFlag.flags.vblank);
|
||||
ImGui::TableNextColumn(); ImGui::Text("%u", cpu.interruptFlag.flags.lcd_stat);
|
||||
ImGui::TableNextColumn(); ImGui::Text("%u", cpu.interruptFlag.flags.timer);
|
||||
ImGui::TableNextColumn(); ImGui::Text("%u", cpu.interruptFlag.flags.serial);
|
||||
ImGui::TableNextColumn(); ImGui::Text("%u", cpu.interruptFlag.flags.joypad);
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
if (bus.cpu->stopped)
|
||||
{
|
||||
ImGui::Separator();
|
||||
ImGui::TextColored(ImVec4(255, 0, 0, 255), "STOPPED");
|
||||
}
|
||||
|
||||
if (bus.cpu->halted)
|
||||
{
|
||||
ImGui::Separator();
|
||||
ImGui::TextColored(ImVec4(255, 0, 0, 255), "HALTED");
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
ImGui::Begin("OAM");
|
||||
if (ImGui::BeginTable("OAM", 5))
|
||||
{
|
||||
ImGui::TableNextColumn(); ImGui::Text("$");
|
||||
ImGui::TableNextColumn(); ImGui::Text("Y");
|
||||
ImGui::TableNextColumn(); ImGui::Text("X");
|
||||
ImGui::TableNextColumn(); ImGui::Text("#");
|
||||
ImGui::TableNextColumn(); ImGui::Text("F");
|
||||
|
||||
for (int i = 0; i < 40; i++)
|
||||
{
|
||||
WORD addr = i * 4;
|
||||
ImGui::TableNextColumn(); ImGui::Text("%02x", 0xFE00 + addr);
|
||||
ImGui::TableNextColumn(); ImGui::Text("%03u", lcd.oam[addr + 0x0]);
|
||||
ImGui::TableNextColumn(); ImGui::Text("%03u", lcd.oam[addr + 0x1]);
|
||||
ImGui::TableNextColumn(); ImGui::Text("%02x", lcd.oam[addr + 0x2]);
|
||||
ImGui::TableNextColumn(); ImGui::Text("%02x", lcd.oam[addr + 0x3]);
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
ImGui::Begin("Gameboy");
|
||||
ImGui::Image(gameboyScreen, ImVec2(ImGui::GetWindowContentRegionWidth(), ImGui::GetWindowContentRegionWidth() / gbAR));
|
||||
ImGui::End();
|
||||
|
||||
// Clear screen and render ImGui
|
||||
SDL_RenderClear(renderer);
|
||||
SDL_RenderCopy(renderer, clearScreen, NULL, NULL);
|
||||
|
||||
ImGui::Render();
|
||||
ImGuiSDL::Render(ImGui::GetDrawData());
|
||||
|
||||
SDL_RenderPresent(renderer);
|
||||
}
|
||||
|
||||
// Free memory & cleanup
|
||||
ImGuiSDL::Deinitialize();
|
||||
ImGui::DestroyContext();
|
||||
|
||||
SDL_DestroyTexture(gameboyScreen);
|
||||
SDL_DestroyTexture(hramScreen);
|
||||
SDL_DestroyTexture(tilemap2);
|
||||
SDL_DestroyTexture(tilemap1);
|
||||
SDL_DestroyTexture(tilemapRaw2);
|
||||
SDL_DestroyTexture(tilemapRaw1);
|
||||
SDL_DestroyTexture(vramScreen);
|
||||
SDL_DestroyTexture(ramScreen);
|
||||
|
||||
SDL_DestroyRenderer(renderer);
|
||||
SDL_DestroyWindow(window);
|
||||
|
||||
SDL_Quit();
|
||||
|
||||
return 0;
|
||||
}
|
104
src/rom.cpp
Normal file
104
src/rom.cpp
Normal file
|
@ -0,0 +1,104 @@
|
|||
#include "rom.hpp"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "bus.hpp"
|
||||
#include "mbcs/mbc0.hpp"
|
||||
#include "mbcs/mbc1.hpp"
|
||||
|
||||
static BYTE bios[0x100] = {
|
||||
0x31, 0xFE, 0xFF, 0xAF, 0x21, 0xFF, 0x9F, 0x32, 0xCB, 0x7C, 0x20, 0xFB, 0x21, 0x26, 0xFF, 0x0E, 0x11, 0x3E, 0x80, 0x32, 0xE2, 0x0C, 0x3E, 0xF3, 0xE2, 0x32, 0x3E, 0x77, 0x77, 0x3E, 0xFC, 0xE0, 0x47, 0x11, 0x04, 0x01, 0x21, 0x10, 0x80, 0x1A, 0xCD, 0x95, 0x00, 0xCD, 0x96, 0x00, 0x13, 0x7B, 0xFE, 0x34, 0x20, 0xF3, 0x11, 0xD8, 0x00, 0x06, 0x08, 0x1A, 0x13, 0x22, 0x23, 0x05, 0x20, 0xF9, 0x3E, 0x19, 0xEA, 0x10, 0x99, 0x21, 0x2F, 0x99, 0x0E, 0x0C, 0x3D, 0x28, 0x08, 0x32, 0x0D, 0x20, 0xF9, 0x2E, 0x0F, 0x18, 0xF3, 0x67, 0x3E, 0x64, 0x57, 0xE0, 0x42, 0x3E, 0x91, 0xE0, 0x40, 0x04, 0x1E, 0x02, 0x0E, 0x0C, 0xF0, 0x44, 0xFE, 0x90, 0x20, 0xFA, 0x0D, 0x20, 0xF7, 0x1D, 0x20, 0xF2, 0x0E, 0x13, 0x24, 0x7C, 0x1E, 0x83, 0xFE, 0x62, 0x28, 0x06, 0x1E, 0xC1, 0xFE, 0x64, 0x20, 0x06, 0x7B, 0xE2, 0x0C, 0x3E, 0x87, 0xE2, 0xF0, 0x42, 0x90, 0xE0, 0x42, 0x15, 0x20, 0xD2, 0x05, 0x20, 0x4F, 0x16, 0x20, 0x18, 0xCB, 0x4F, 0x06, 0x04, 0xC5, 0xCB, 0x11, 0x17, 0xC1, 0xCB, 0x11, 0x17, 0x05, 0x20, 0xF5, 0x22, 0x23, 0x22, 0x23, 0xC9, 0xCE, 0xED, 0x66, 0x66, 0xCC, 0x0D, 0x00, 0x0B, 0x03, 0x73, 0x00, 0x83, 0x00, 0x0C, 0x00, 0x0D, 0x00, 0x08, 0x11, 0x1F, 0x88, 0x89, 0x00, 0x0E, 0xDC, 0xCC, 0x6E, 0xE6, 0xDD, 0xDD, 0xD9, 0x99, 0xBB, 0xBB, 0x67, 0x63, 0x6E, 0x0E, 0xEC, 0xCC, 0xDD, 0xDC, 0x99, 0x9F, 0xBB, 0xB9, 0x33, 0x3E, 0x3C, 0x42, 0xB9, 0xA5, 0xB9, 0xA5, 0x42, 0x3C, 0x21, 0x04, 0x01, 0x11, 0xA8, 0x00, 0x1A, 0x13, 0xBE, 0x20, 0xFE, 0x23, 0x7D, 0xFE, 0x34, 0x20, 0xF5, 0x06, 0x19, 0x78, 0x86, 0x23, 0x05, 0x20, 0xFB, 0x86, 0x20, 0xFE, 0x3E, 0x01, 0xE0, 0x50
|
||||
};
|
||||
|
||||
ROM::ROM(FILE* f)
|
||||
{
|
||||
fseek(f, 0, SEEK_END);
|
||||
long fsize = ftell(f);
|
||||
rewind(f);
|
||||
|
||||
data = std::vector<BYTE>(fsize);
|
||||
fread(data.data(), 1, fsize, f);
|
||||
|
||||
switch (data[0x149])
|
||||
{
|
||||
case 0x01: ram = std::vector<BYTE>(0x800); break;
|
||||
case 0x02: ram = std::vector<BYTE>(0x2000); break;
|
||||
case 0x03: ram = std::vector<BYTE>(0x8000); break;
|
||||
case 0x04: ram = std::vector<BYTE>(0x20000); break;
|
||||
case 0x05: ram = std::vector<BYTE>(0x10000); break;
|
||||
}
|
||||
|
||||
WORD RomBanks = (WORD)0x2 << data[0x0148];
|
||||
if (data[0x0148] == 0x52)
|
||||
RomBanks = 72;
|
||||
else if (data[0x0148] == 0x53)
|
||||
RomBanks = 80;
|
||||
else if (data[0x0148] == 0x54)
|
||||
RomBanks = 96;
|
||||
|
||||
WORD RamBanks = 0x00;
|
||||
switch (data[0x0149])
|
||||
{
|
||||
case 0x03: RamBanks = 4; break;
|
||||
case 0x04: RamBanks = 16; break;
|
||||
case 0x05: RamBanks = 8; break;
|
||||
}
|
||||
|
||||
// Select MBC
|
||||
switch (data[0x0147])
|
||||
{
|
||||
case 0x00:
|
||||
mbc = std::make_unique<MBC0>(0);
|
||||
break;
|
||||
|
||||
case 0x01:
|
||||
case 0x02:
|
||||
case 0x03:
|
||||
mbc = std::make_unique<MBC1>(RomBanks, RamBanks, 8);
|
||||
break;
|
||||
|
||||
case 0x08:
|
||||
case 0x09:
|
||||
mbc = std::make_unique<MBC0>(1);
|
||||
break;
|
||||
|
||||
default:
|
||||
EXIT_MSG("This ROM uses an unsupported memory bank controller: %02x\n", data[0x0147]);
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
BYTE ROM::Read(WORD addr)
|
||||
{
|
||||
DWORD mappedAddr = 0x00;
|
||||
if (!mbc->GetMappedRead(addr, mappedAddr))
|
||||
return 0xFF;
|
||||
|
||||
switch (bus->Read(0xFF50) + (addr >= 0x100))
|
||||
{
|
||||
case 0:
|
||||
// Read BIOS
|
||||
return bios[addr];
|
||||
|
||||
default:
|
||||
// Read ROM
|
||||
if (addr < 0x8000)
|
||||
return data[mappedAddr];
|
||||
else
|
||||
return ram[mappedAddr];
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
void ROM::Write(WORD addr, BYTE val)
|
||||
{
|
||||
DWORD mappedAddr = 0x00;
|
||||
if (!mbc->GetMappedWrite(addr, val, mappedAddr))
|
||||
return;
|
||||
|
||||
ram[mappedAddr] = val;
|
||||
}
|
2852
vendor/imgui/include/imgui.h
vendored
Normal file
2852
vendor/imgui/include/imgui.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
87
vendor/imgui/include/imgui_impl_opengl3.h
vendored
Normal file
87
vendor/imgui/include/imgui_impl_opengl3.h
vendored
Normal file
|
@ -0,0 +1,87 @@
|
|||
// dear imgui: Renderer Backend for modern OpenGL with shaders / programmatic pipeline
|
||||
// - Desktop GL: 2.x 3.x 4.x
|
||||
// - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0)
|
||||
// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID!
|
||||
// [x] Renderer: Desktop GL only: Support for large meshes (64k+ vertices) with 16-bit indices.
|
||||
|
||||
// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
|
||||
// Read online: https://github.com/ocornut/imgui/tree/master/docs
|
||||
|
||||
// About Desktop OpenGL function loaders:
|
||||
// Modern Desktop OpenGL doesn't have a standard portable header file to load OpenGL function pointers.
|
||||
// Helper libraries are often used for this purpose! Here we are supporting a few common ones (gl3w, glew, glad).
|
||||
// You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own.
|
||||
|
||||
// About GLSL version:
|
||||
// The 'glsl_version' initialization parameter should be NULL (default) or a "#version XXX" string.
|
||||
// On computer platform the GLSL version default to "#version 130". On OpenGL ES 3 platform it defaults to "#version 300 es"
|
||||
// Only override if your GL version doesn't handle this GLSL version. See GLSL version table at the top of imgui_impl_opengl3.cpp.
|
||||
|
||||
#pragma once
|
||||
#include "imgui.h" // IMGUI_IMPL_API
|
||||
|
||||
// Backend API
|
||||
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = NULL);
|
||||
IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown();
|
||||
IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame();
|
||||
IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data);
|
||||
|
||||
// (Optional) Called by Init/NewFrame/Shutdown
|
||||
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture();
|
||||
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyFontsTexture();
|
||||
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateDeviceObjects();
|
||||
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects();
|
||||
|
||||
// Specific OpenGL ES versions
|
||||
//#define IMGUI_IMPL_OPENGL_ES2 // Auto-detected on Emscripten
|
||||
//#define IMGUI_IMPL_OPENGL_ES3 // Auto-detected on iOS/Android
|
||||
|
||||
// Attempt to auto-detect the default Desktop GL loader based on available header files.
|
||||
// If auto-detection fails or doesn't select the same GL loader file as used by your application,
|
||||
// you are likely to get a crash in ImGui_ImplOpenGL3_Init().
|
||||
// You can explicitly select a loader by using one of the '#define IMGUI_IMPL_OPENGL_LOADER_XXX' in imconfig.h or compiler command-line.
|
||||
#if !defined(IMGUI_IMPL_OPENGL_ES2) \
|
||||
&& !defined(IMGUI_IMPL_OPENGL_ES3) \
|
||||
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GL3W) \
|
||||
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GLEW) \
|
||||
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GLAD) \
|
||||
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GLAD2) \
|
||||
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING2) \
|
||||
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING3) \
|
||||
&& !defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM)
|
||||
|
||||
// Try to detect GLES on matching platforms
|
||||
#if defined(__APPLE__)
|
||||
#include "TargetConditionals.h"
|
||||
#endif
|
||||
#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV)) || (defined(__ANDROID__))
|
||||
#define IMGUI_IMPL_OPENGL_ES3 // iOS, Android -> GL ES 3, "#version 300 es"
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
#define IMGUI_IMPL_OPENGL_ES2 // Emscripten -> GL ES 2, "#version 100"
|
||||
|
||||
// Otherwise try to detect supported Desktop OpenGL loaders..
|
||||
#elif defined(__has_include)
|
||||
#if __has_include(<GL/glew.h>)
|
||||
#define IMGUI_IMPL_OPENGL_LOADER_GLEW
|
||||
#elif __has_include(<glad/glad.h>)
|
||||
#define IMGUI_IMPL_OPENGL_LOADER_GLAD
|
||||
#elif __has_include(<glad/gl.h>)
|
||||
#define IMGUI_IMPL_OPENGL_LOADER_GLAD2
|
||||
#elif __has_include(<GL/gl3w.h>)
|
||||
#define IMGUI_IMPL_OPENGL_LOADER_GL3W
|
||||
#elif __has_include(<glbinding/glbinding.h>)
|
||||
#define IMGUI_IMPL_OPENGL_LOADER_GLBINDING3
|
||||
#elif __has_include(<glbinding/Binding.h>)
|
||||
#define IMGUI_IMPL_OPENGL_LOADER_GLBINDING2
|
||||
#else
|
||||
#error "Cannot detect OpenGL loader!"
|
||||
#endif
|
||||
#else
|
||||
#define IMGUI_IMPL_OPENGL_LOADER_GL3W // Default to GL3W embedded in our repository
|
||||
#endif
|
||||
|
||||
#endif
|
29
vendor/imgui/include/imgui_impl_sdl.h
vendored
Normal file
29
vendor/imgui/include/imgui_impl_sdl.h
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
// dear imgui: Platform Backend for SDL2
|
||||
// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
|
||||
// (Info: SDL2 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
|
||||
// [X] Platform: Clipboard support.
|
||||
// [X] Platform: Keyboard arrays indexed using SDL_SCANCODE_* codes, e.g. ImGui::IsKeyPressed(SDL_SCANCODE_SPACE).
|
||||
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
|
||||
// Missing features:
|
||||
// [ ] Platform: SDL2 handling of IME under Windows appears to be broken and it explicitly disable the regular Windows IME. You can restore Windows IME by compiling SDL with SDL_DISABLE_WINDOWS_IME.
|
||||
|
||||
// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
|
||||
// Read online: https://github.com/ocornut/imgui/tree/master/docs
|
||||
|
||||
#pragma once
|
||||
#include "imgui.h" // IMGUI_IMPL_API
|
||||
|
||||
struct SDL_Window;
|
||||
typedef union SDL_Event SDL_Event;
|
||||
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForOpenGL(SDL_Window* window, void* sdl_gl_context);
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForVulkan(SDL_Window* window);
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForD3D(SDL_Window* window);
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForMetal(SDL_Window* window);
|
||||
IMGUI_IMPL_API void ImGui_ImplSDL2_Shutdown();
|
||||
IMGUI_IMPL_API void ImGui_ImplSDL2_NewFrame(SDL_Window* window);
|
||||
IMGUI_IMPL_API bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event);
|
2688
vendor/imgui/include/imgui_internal.h
vendored
Normal file
2688
vendor/imgui/include/imgui_internal.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
17
vendor/imgui/include/imgui_sdl.h
vendored
Normal file
17
vendor/imgui/include/imgui_sdl.h
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
struct ImDrawData;
|
||||
struct SDL_Renderer;
|
||||
|
||||
namespace ImGuiSDL
|
||||
{
|
||||
// Call this to initialize the SDL renderer device that is internally used by the renderer.
|
||||
void Initialize(SDL_Renderer* renderer, int windowWidth, int windowHeight);
|
||||
// Call this before destroying your SDL renderer or ImGui to ensure that proper cleanup is done. This doesn't do anything critically important though,
|
||||
// so if you're fine with small memory leaks at the end of your application, you can even omit this.
|
||||
void Deinitialize();
|
||||
|
||||
// Call this every frame after ImGui::Render with ImGui::GetDrawData(). This will use the SDL_Renderer provided to the interfrace with Initialize
|
||||
// to draw the contents of the draw data to the screen.
|
||||
void Render(ImDrawData* drawData);
|
||||
}
|
4903
vendor/imgui/include/imstb_truetype.h
vendored
Normal file
4903
vendor/imgui/include/imstb_truetype.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
11589
vendor/imgui/src/imgui.cpp
vendored
Normal file
11589
vendor/imgui/src/imgui.cpp
vendored
Normal file
File diff suppressed because it is too large
Load diff
7725
vendor/imgui/src/imgui_demo.cpp
vendored
Normal file
7725
vendor/imgui/src/imgui_demo.cpp
vendored
Normal file
File diff suppressed because it is too large
Load diff
4152
vendor/imgui/src/imgui_draw.cpp
vendored
Normal file
4152
vendor/imgui/src/imgui_draw.cpp
vendored
Normal file
File diff suppressed because it is too large
Load diff
725
vendor/imgui/src/imgui_impl_opengl3.cpp
vendored
Normal file
725
vendor/imgui/src/imgui_impl_opengl3.cpp
vendored
Normal file
|
@ -0,0 +1,725 @@
|
|||
// dear imgui: Renderer Backend for modern OpenGL with shaders / programmatic pipeline
|
||||
// - Desktop GL: 2.x 3.x 4.x
|
||||
// - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0)
|
||||
// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID!
|
||||
// [x] Renderer: Desktop GL only: Support for large meshes (64k+ vertices) with 16-bit indices.
|
||||
|
||||
// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
|
||||
// Read online: https://github.com/ocornut/imgui/tree/master/docs
|
||||
|
||||
// CHANGELOG
|
||||
// (minor and older changes stripped away, please see git history for details)
|
||||
// 2021-05-19: OpenGL: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement)
|
||||
// 2021-04-06: OpenGL: Don't try to read GL_CLIP_ORIGIN unless we're OpenGL 4.5 or greater.
|
||||
// 2021-02-18: OpenGL: Change blending equation to preserve alpha in output buffer.
|
||||
// 2021-01-03: OpenGL: Backup, setup and restore GL_STENCIL_TEST state.
|
||||
// 2020-10-23: OpenGL: Backup, setup and restore GL_PRIMITIVE_RESTART state.
|
||||
// 2020-10-15: OpenGL: Use glGetString(GL_VERSION) instead of glGetIntegerv(GL_MAJOR_VERSION, ...) when the later returns zero (e.g. Desktop GL 2.x)
|
||||
// 2020-09-17: OpenGL: Fix to avoid compiling/calling glBindSampler() on ES or pre 3.3 context which have the defines set by a loader.
|
||||
// 2020-07-10: OpenGL: Added support for glad2 OpenGL loader.
|
||||
// 2020-05-08: OpenGL: Made default GLSL version 150 (instead of 130) on OSX.
|
||||
// 2020-04-21: OpenGL: Fixed handling of glClipControl(GL_UPPER_LEFT) by inverting projection matrix.
|
||||
// 2020-04-12: OpenGL: Fixed context version check mistakenly testing for 4.0+ instead of 3.2+ to enable ImGuiBackendFlags_RendererHasVtxOffset.
|
||||
// 2020-03-24: OpenGL: Added support for glbinding 2.x OpenGL loader.
|
||||
// 2020-01-07: OpenGL: Added support for glbinding 3.x OpenGL loader.
|
||||
// 2019-10-25: OpenGL: Using a combination of GL define and runtime GL version to decide whether to use glDrawElementsBaseVertex(). Fix building with pre-3.2 GL loaders.
|
||||
// 2019-09-22: OpenGL: Detect default GL loader using __has_include compiler facility.
|
||||
// 2019-09-16: OpenGL: Tweak initialization code to allow application calling ImGui_ImplOpenGL3_CreateFontsTexture() before the first NewFrame() call.
|
||||
// 2019-05-29: OpenGL: Desktop GL only: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
|
||||
// 2019-04-30: OpenGL: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
|
||||
// 2019-03-29: OpenGL: Not calling glBindBuffer more than necessary in the render loop.
|
||||
// 2019-03-15: OpenGL: Added a GL call + comments in ImGui_ImplOpenGL3_Init() to detect uninitialized GL function loaders early.
|
||||
// 2019-03-03: OpenGL: Fix support for ES 2.0 (WebGL 1.0).
|
||||
// 2019-02-20: OpenGL: Fix for OSX not supporting OpenGL 4.5, we don't try to read GL_CLIP_ORIGIN even if defined by the headers/loader.
|
||||
// 2019-02-11: OpenGL: Projecting clipping rectangles correctly using draw_data->FramebufferScale to allow multi-viewports for retina display.
|
||||
// 2019-02-01: OpenGL: Using GLSL 410 shaders for any version over 410 (e.g. 430, 450).
|
||||
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
|
||||
// 2018-11-13: OpenGL: Support for GL 4.5's glClipControl(GL_UPPER_LEFT) / GL_CLIP_ORIGIN.
|
||||
// 2018-08-29: OpenGL: Added support for more OpenGL loaders: glew and glad, with comments indicative that any loader can be used.
|
||||
// 2018-08-09: OpenGL: Default to OpenGL ES 3 on iOS and Android. GLSL version default to "#version 300 ES".
|
||||
// 2018-07-30: OpenGL: Support for GLSL 300 ES and 410 core. Fixes for Emscripten compilation.
|
||||
// 2018-07-10: OpenGL: Support for more GLSL versions (based on the GLSL version string). Added error output when shaders fail to compile/link.
|
||||
// 2018-06-08: Misc: Extracted imgui_impl_opengl3.cpp/.h away from the old combined GLFW/SDL+OpenGL3 examples.
|
||||
// 2018-06-08: OpenGL: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.
|
||||
// 2018-05-25: OpenGL: Removed unnecessary backup/restore of GL_ELEMENT_ARRAY_BUFFER_BINDING since this is part of the VAO state.
|
||||
// 2018-05-14: OpenGL: Making the call to glBindSampler() optional so 3.2 context won't fail if the function is a NULL pointer.
|
||||
// 2018-03-06: OpenGL: Added const char* glsl_version parameter to ImGui_ImplOpenGL3_Init() so user can override the GLSL version e.g. "#version 150".
|
||||
// 2018-02-23: OpenGL: Create the VAO in the render function so the setup can more easily be used with multiple shared GL context.
|
||||
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplSdlGL3_RenderDrawData() in the .h file so you can call it yourself.
|
||||
// 2018-01-07: OpenGL: Changed GLSL shader version from 330 to 150.
|
||||
// 2017-09-01: OpenGL: Save and restore current bound sampler. Save and restore current polygon mode.
|
||||
// 2017-05-01: OpenGL: Fixed save and restore of current blend func state.
|
||||
// 2017-05-01: OpenGL: Fixed save and restore of current GL_ACTIVE_TEXTURE.
|
||||
// 2016-09-05: OpenGL: Fixed save and restore of current scissor rectangle.
|
||||
// 2016-07-29: OpenGL: Explicitly setting GL_UNPACK_ROW_LENGTH to reduce issues because SDL changes it. (#752)
|
||||
|
||||
//----------------------------------------
|
||||
// OpenGL GLSL GLSL
|
||||
// version version string
|
||||
//----------------------------------------
|
||||
// 2.0 110 "#version 110"
|
||||
// 2.1 120 "#version 120"
|
||||
// 3.0 130 "#version 130"
|
||||
// 3.1 140 "#version 140"
|
||||
// 3.2 150 "#version 150"
|
||||
// 3.3 330 "#version 330 core"
|
||||
// 4.0 400 "#version 400 core"
|
||||
// 4.1 410 "#version 410 core"
|
||||
// 4.2 420 "#version 410 core"
|
||||
// 4.3 430 "#version 430 core"
|
||||
// ES 2.0 100 "#version 100" = WebGL 1.0
|
||||
// ES 3.0 300 "#version 300 es" = WebGL 2.0
|
||||
//----------------------------------------
|
||||
|
||||
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_opengl3.h"
|
||||
#include <stdio.h>
|
||||
#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
|
||||
#include <stddef.h> // intptr_t
|
||||
#else
|
||||
#include <stdint.h> // intptr_t
|
||||
#endif
|
||||
|
||||
// GL includes
|
||||
#if defined(IMGUI_IMPL_OPENGL_ES2)
|
||||
#include <GLES2/gl2.h>
|
||||
#elif defined(IMGUI_IMPL_OPENGL_ES3)
|
||||
#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV))
|
||||
#include <OpenGLES/ES3/gl.h> // Use GL ES 3
|
||||
#else
|
||||
#include <GLES3/gl3.h> // Use GL ES 3
|
||||
#endif
|
||||
#else
|
||||
// About Desktop OpenGL function loaders:
|
||||
// Modern desktop OpenGL doesn't have a standard portable header file to load OpenGL function pointers.
|
||||
// Helper libraries are often used for this purpose! Here we are supporting a few common ones (gl3w, glew, glad).
|
||||
// You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own.
|
||||
#if defined(IMGUI_IMPL_OPENGL_LOADER_GL3W)
|
||||
#include <GL/gl3w.h> // Needs to be initialized with gl3wInit() in user's code
|
||||
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLEW)
|
||||
#include <GL/glew.h> // Needs to be initialized with glewInit() in user's code.
|
||||
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD)
|
||||
#include <glad/glad.h> // Needs to be initialized with gladLoadGL() in user's code.
|
||||
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD2)
|
||||
#include <glad/gl.h> // Needs to be initialized with gladLoadGL(...) or gladLoaderLoadGL() in user's code.
|
||||
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING2)
|
||||
#ifndef GLFW_INCLUDE_NONE
|
||||
#define GLFW_INCLUDE_NONE // GLFW including OpenGL headers causes ambiguity or multiple definition errors.
|
||||
#endif
|
||||
#include <glbinding/Binding.h> // Needs to be initialized with glbinding::Binding::initialize() in user's code.
|
||||
#include <glbinding/gl/gl.h>
|
||||
using namespace gl;
|
||||
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING3)
|
||||
#ifndef GLFW_INCLUDE_NONE
|
||||
#define GLFW_INCLUDE_NONE // GLFW including OpenGL headers causes ambiguity or multiple definition errors.
|
||||
#endif
|
||||
#include <glbinding/glbinding.h>// Needs to be initialized with glbinding::initialize() in user's code.
|
||||
#include <glbinding/gl/gl.h>
|
||||
using namespace gl;
|
||||
#else
|
||||
#include IMGUI_IMPL_OPENGL_LOADER_CUSTOM
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Desktop GL 3.2+ has glDrawElementsBaseVertex() which GL ES and WebGL don't have.
|
||||
#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) && defined(GL_VERSION_3_2)
|
||||
#define IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET
|
||||
#endif
|
||||
|
||||
// Desktop GL 3.3+ has glBindSampler()
|
||||
#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) && defined(GL_VERSION_3_3)
|
||||
#define IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER
|
||||
#endif
|
||||
|
||||
// Desktop GL 3.1+ has GL_PRIMITIVE_RESTART state
|
||||
#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) && defined(GL_VERSION_3_1)
|
||||
#define IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART
|
||||
#endif
|
||||
|
||||
// OpenGL Data
|
||||
static GLuint g_GlVersion = 0; // Extracted at runtime using GL_MAJOR_VERSION, GL_MINOR_VERSION queries (e.g. 320 for GL 3.2)
|
||||
static char g_GlslVersionString[32] = ""; // Specified by user or detected based on compile time GL settings.
|
||||
static GLuint g_FontTexture = 0;
|
||||
static GLuint g_ShaderHandle = 0, g_VertHandle = 0, g_FragHandle = 0;
|
||||
static GLint g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0; // Uniforms location
|
||||
static GLuint g_AttribLocationVtxPos = 0, g_AttribLocationVtxUV = 0, g_AttribLocationVtxColor = 0; // Vertex attributes location
|
||||
static unsigned int g_VboHandle = 0, g_ElementsHandle = 0;
|
||||
|
||||
// Functions
|
||||
bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
|
||||
{
|
||||
// Query for GL version (e.g. 320 for GL 3.2)
|
||||
#if !defined(IMGUI_IMPL_OPENGL_ES2)
|
||||
GLint major = 0;
|
||||
GLint minor = 0;
|
||||
glGetIntegerv(GL_MAJOR_VERSION, &major);
|
||||
glGetIntegerv(GL_MINOR_VERSION, &minor);
|
||||
if (major == 0 && minor == 0)
|
||||
{
|
||||
// Query GL_VERSION in desktop GL 2.x, the string will start with "<major>.<minor>"
|
||||
const char* gl_version = (const char*)glGetString(GL_VERSION);
|
||||
sscanf(gl_version, "%d.%d", &major, &minor);
|
||||
}
|
||||
g_GlVersion = (GLuint)(major * 100 + minor * 10);
|
||||
#else
|
||||
g_GlVersion = 200; // GLES 2
|
||||
#endif
|
||||
|
||||
// Setup backend capabilities flags
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.BackendRendererName = "imgui_impl_opengl3";
|
||||
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET
|
||||
if (g_GlVersion >= 320)
|
||||
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
|
||||
#endif
|
||||
|
||||
// Store GLSL version string so we can refer to it later in case we recreate shaders.
|
||||
// Note: GLSL version is NOT the same as GL version. Leave this to NULL if unsure.
|
||||
#if defined(IMGUI_IMPL_OPENGL_ES2)
|
||||
if (glsl_version == NULL)
|
||||
glsl_version = "#version 100";
|
||||
#elif defined(IMGUI_IMPL_OPENGL_ES3)
|
||||
if (glsl_version == NULL)
|
||||
glsl_version = "#version 300 es";
|
||||
#elif defined(__APPLE__)
|
||||
if (glsl_version == NULL)
|
||||
glsl_version = "#version 150";
|
||||
#else
|
||||
if (glsl_version == NULL)
|
||||
glsl_version = "#version 130";
|
||||
#endif
|
||||
IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(g_GlslVersionString));
|
||||
strcpy(g_GlslVersionString, glsl_version);
|
||||
strcat(g_GlslVersionString, "\n");
|
||||
|
||||
// Debugging construct to make it easily visible in the IDE and debugger which GL loader has been selected.
|
||||
// The code actually never uses the 'gl_loader' variable! It is only here so you can read it!
|
||||
// If auto-detection fails or doesn't select the same GL loader file as used by your application,
|
||||
// you are likely to get a crash below.
|
||||
// You can explicitly select a loader by using '#define IMGUI_IMPL_OPENGL_LOADER_XXX' in imconfig.h or compiler command-line.
|
||||
const char* gl_loader = "Unknown";
|
||||
IM_UNUSED(gl_loader);
|
||||
#if defined(IMGUI_IMPL_OPENGL_LOADER_GL3W)
|
||||
gl_loader = "GL3W";
|
||||
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLEW)
|
||||
gl_loader = "GLEW";
|
||||
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD)
|
||||
gl_loader = "GLAD";
|
||||
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD2)
|
||||
gl_loader = "GLAD2";
|
||||
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING2)
|
||||
gl_loader = "glbinding2";
|
||||
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING3)
|
||||
gl_loader = "glbinding3";
|
||||
#elif defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM)
|
||||
gl_loader = "custom";
|
||||
#else
|
||||
gl_loader = "none";
|
||||
#endif
|
||||
|
||||
// Make an arbitrary GL call (we don't actually need the result)
|
||||
// IF YOU GET A CRASH HERE: it probably means that you haven't initialized the OpenGL function loader used by this code.
|
||||
// Desktop OpenGL 3/4 need a function loader. See the IMGUI_IMPL_OPENGL_LOADER_xxx explanation above.
|
||||
GLint current_texture;
|
||||
glGetIntegerv(GL_TEXTURE_BINDING_2D, ¤t_texture);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGui_ImplOpenGL3_Shutdown()
|
||||
{
|
||||
ImGui_ImplOpenGL3_DestroyDeviceObjects();
|
||||
}
|
||||
|
||||
void ImGui_ImplOpenGL3_NewFrame()
|
||||
{
|
||||
if (!g_ShaderHandle)
|
||||
ImGui_ImplOpenGL3_CreateDeviceObjects();
|
||||
}
|
||||
|
||||
static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height, GLuint vertex_array_object)
|
||||
{
|
||||
// Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill
|
||||
glEnable(GL_BLEND);
|
||||
glBlendEquation(GL_FUNC_ADD);
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART
|
||||
if (g_GlVersion >= 310)
|
||||
glDisable(GL_PRIMITIVE_RESTART);
|
||||
#endif
|
||||
#ifdef GL_POLYGON_MODE
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||
#endif
|
||||
|
||||
// Support for GL 4.5 rarely used glClipControl(GL_UPPER_LEFT)
|
||||
#if defined(GL_CLIP_ORIGIN)
|
||||
bool clip_origin_lower_left = true;
|
||||
if (g_GlVersion >= 450)
|
||||
{
|
||||
GLenum current_clip_origin = 0; glGetIntegerv(GL_CLIP_ORIGIN, (GLint*)¤t_clip_origin);
|
||||
if (current_clip_origin == GL_UPPER_LEFT)
|
||||
clip_origin_lower_left = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Setup viewport, orthographic projection matrix
|
||||
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
|
||||
glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height);
|
||||
float L = draw_data->DisplayPos.x;
|
||||
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
|
||||
float T = draw_data->DisplayPos.y;
|
||||
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
|
||||
#if defined(GL_CLIP_ORIGIN)
|
||||
if (!clip_origin_lower_left) { float tmp = T; T = B; B = tmp; } // Swap top and bottom if origin is upper left
|
||||
#endif
|
||||
const float ortho_projection[4][4] =
|
||||
{
|
||||
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
|
||||
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
|
||||
{ 0.0f, 0.0f, -1.0f, 0.0f },
|
||||
{ (R+L)/(L-R), (T+B)/(B-T), 0.0f, 1.0f },
|
||||
};
|
||||
glUseProgram(g_ShaderHandle);
|
||||
glUniform1i(g_AttribLocationTex, 0);
|
||||
glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]);
|
||||
|
||||
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER
|
||||
if (g_GlVersion >= 330)
|
||||
glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 may set that otherwise.
|
||||
#endif
|
||||
|
||||
(void)vertex_array_object;
|
||||
#ifndef IMGUI_IMPL_OPENGL_ES2
|
||||
glBindVertexArray(vertex_array_object);
|
||||
#endif
|
||||
|
||||
// Bind vertex/index buffers and setup attributes for ImDrawVert
|
||||
glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ElementsHandle);
|
||||
glEnableVertexAttribArray(g_AttribLocationVtxPos);
|
||||
glEnableVertexAttribArray(g_AttribLocationVtxUV);
|
||||
glEnableVertexAttribArray(g_AttribLocationVtxColor);
|
||||
glVertexAttribPointer(g_AttribLocationVtxPos, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, pos));
|
||||
glVertexAttribPointer(g_AttribLocationVtxUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, uv));
|
||||
glVertexAttribPointer(g_AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col));
|
||||
}
|
||||
|
||||
// OpenGL3 Render function.
|
||||
// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly.
|
||||
// This is in order to be able to run within an OpenGL engine that doesn't do so.
|
||||
void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
|
||||
{
|
||||
// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
|
||||
int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x);
|
||||
int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y);
|
||||
if (fb_width <= 0 || fb_height <= 0)
|
||||
return;
|
||||
|
||||
// Backup GL state
|
||||
GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint*)&last_active_texture);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
GLuint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&last_program);
|
||||
GLuint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint*)&last_texture);
|
||||
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER
|
||||
GLuint last_sampler; if (g_GlVersion >= 330) { glGetIntegerv(GL_SAMPLER_BINDING, (GLint*)&last_sampler); } else { last_sampler = 0; }
|
||||
#endif
|
||||
GLuint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, (GLint*)&last_array_buffer);
|
||||
#ifndef IMGUI_IMPL_OPENGL_ES2
|
||||
GLuint last_vertex_array_object; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, (GLint*)&last_vertex_array_object);
|
||||
#endif
|
||||
#ifdef GL_POLYGON_MODE
|
||||
GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode);
|
||||
#endif
|
||||
GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport);
|
||||
GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box);
|
||||
GLenum last_blend_src_rgb; glGetIntegerv(GL_BLEND_SRC_RGB, (GLint*)&last_blend_src_rgb);
|
||||
GLenum last_blend_dst_rgb; glGetIntegerv(GL_BLEND_DST_RGB, (GLint*)&last_blend_dst_rgb);
|
||||
GLenum last_blend_src_alpha; glGetIntegerv(GL_BLEND_SRC_ALPHA, (GLint*)&last_blend_src_alpha);
|
||||
GLenum last_blend_dst_alpha; glGetIntegerv(GL_BLEND_DST_ALPHA, (GLint*)&last_blend_dst_alpha);
|
||||
GLenum last_blend_equation_rgb; glGetIntegerv(GL_BLEND_EQUATION_RGB, (GLint*)&last_blend_equation_rgb);
|
||||
GLenum last_blend_equation_alpha; glGetIntegerv(GL_BLEND_EQUATION_ALPHA, (GLint*)&last_blend_equation_alpha);
|
||||
GLboolean last_enable_blend = glIsEnabled(GL_BLEND);
|
||||
GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE);
|
||||
GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST);
|
||||
GLboolean last_enable_stencil_test = glIsEnabled(GL_STENCIL_TEST);
|
||||
GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST);
|
||||
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART
|
||||
GLboolean last_enable_primitive_restart = (g_GlVersion >= 310) ? glIsEnabled(GL_PRIMITIVE_RESTART) : GL_FALSE;
|
||||
#endif
|
||||
|
||||
// Setup desired GL state
|
||||
// Recreate the VAO every time (this is to easily allow multiple GL contexts to be rendered to. VAO are not shared among GL contexts)
|
||||
// The renderer would actually work without any VAO bound, but then our VertexAttrib calls would overwrite the default one currently bound.
|
||||
GLuint vertex_array_object = 0;
|
||||
#ifndef IMGUI_IMPL_OPENGL_ES2
|
||||
glGenVertexArrays(1, &vertex_array_object);
|
||||
#endif
|
||||
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object);
|
||||
|
||||
// Will project scissor/clipping rectangles into framebuffer space
|
||||
ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports
|
||||
ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2)
|
||||
|
||||
// Render command lists
|
||||
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||
{
|
||||
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||
|
||||
// Upload vertex/index buffers
|
||||
glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)cmd_list->VtxBuffer.Size * (int)sizeof(ImDrawVert), (const GLvoid*)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)cmd_list->IdxBuffer.Size * (int)sizeof(ImDrawIdx), (const GLvoid*)cmd_list->IdxBuffer.Data, GL_STREAM_DRAW);
|
||||
|
||||
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
|
||||
{
|
||||
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
|
||||
if (pcmd->UserCallback != NULL)
|
||||
{
|
||||
// User callback, registered via ImDrawList::AddCallback()
|
||||
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
|
||||
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
|
||||
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object);
|
||||
else
|
||||
pcmd->UserCallback(cmd_list, pcmd);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Project scissor/clipping rectangles into framebuffer space
|
||||
ImVec4 clip_rect;
|
||||
clip_rect.x = (pcmd->ClipRect.x - clip_off.x) * clip_scale.x;
|
||||
clip_rect.y = (pcmd->ClipRect.y - clip_off.y) * clip_scale.y;
|
||||
clip_rect.z = (pcmd->ClipRect.z - clip_off.x) * clip_scale.x;
|
||||
clip_rect.w = (pcmd->ClipRect.w - clip_off.y) * clip_scale.y;
|
||||
|
||||
if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f)
|
||||
{
|
||||
// Apply scissor/clipping rectangle
|
||||
glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y));
|
||||
|
||||
// Bind texture, Draw
|
||||
glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->GetTexID());
|
||||
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET
|
||||
if (g_GlVersion >= 320)
|
||||
glDrawElementsBaseVertex(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)), (GLint)pcmd->VtxOffset);
|
||||
else
|
||||
#endif
|
||||
glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Destroy the temporary VAO
|
||||
#ifndef IMGUI_IMPL_OPENGL_ES2
|
||||
glDeleteVertexArrays(1, &vertex_array_object);
|
||||
#endif
|
||||
|
||||
// Restore modified GL state
|
||||
glUseProgram(last_program);
|
||||
glBindTexture(GL_TEXTURE_2D, last_texture);
|
||||
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER
|
||||
if (g_GlVersion >= 330)
|
||||
glBindSampler(0, last_sampler);
|
||||
#endif
|
||||
glActiveTexture(last_active_texture);
|
||||
#ifndef IMGUI_IMPL_OPENGL_ES2
|
||||
glBindVertexArray(last_vertex_array_object);
|
||||
#endif
|
||||
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
|
||||
glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha);
|
||||
glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha);
|
||||
if (last_enable_blend) glEnable(GL_BLEND); else glDisable(GL_BLEND);
|
||||
if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE);
|
||||
if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST);
|
||||
if (last_enable_stencil_test) glEnable(GL_STENCIL_TEST); else glDisable(GL_STENCIL_TEST);
|
||||
if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST);
|
||||
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART
|
||||
if (g_GlVersion >= 310) { if (last_enable_primitive_restart) glEnable(GL_PRIMITIVE_RESTART); else glDisable(GL_PRIMITIVE_RESTART); }
|
||||
#endif
|
||||
|
||||
#ifdef GL_POLYGON_MODE
|
||||
glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]);
|
||||
#endif
|
||||
glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]);
|
||||
glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]);
|
||||
}
|
||||
|
||||
bool ImGui_ImplOpenGL3_CreateFontsTexture()
|
||||
{
|
||||
// Build texture atlas
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
unsigned char* pixels;
|
||||
int width, height;
|
||||
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory.
|
||||
|
||||
// Upload texture to graphics system
|
||||
GLint last_texture;
|
||||
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
|
||||
glGenTextures(1, &g_FontTexture);
|
||||
glBindTexture(GL_TEXTURE_2D, g_FontTexture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
#ifdef GL_UNPACK_ROW_LENGTH
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
#endif
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
||||
|
||||
// Store our identifier
|
||||
io.Fonts->SetTexID((ImTextureID)(intptr_t)g_FontTexture);
|
||||
|
||||
// Restore state
|
||||
glBindTexture(GL_TEXTURE_2D, last_texture);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGui_ImplOpenGL3_DestroyFontsTexture()
|
||||
{
|
||||
if (g_FontTexture)
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
glDeleteTextures(1, &g_FontTexture);
|
||||
io.Fonts->SetTexID(0);
|
||||
g_FontTexture = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// If you get an error please report on github. You may try different GL context version or GLSL version. See GL<>GLSL version table at the top of this file.
|
||||
static bool CheckShader(GLuint handle, const char* desc)
|
||||
{
|
||||
GLint status = 0, log_length = 0;
|
||||
glGetShaderiv(handle, GL_COMPILE_STATUS, &status);
|
||||
glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &log_length);
|
||||
if ((GLboolean)status == GL_FALSE)
|
||||
fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to compile %s!\n", desc);
|
||||
if (log_length > 1)
|
||||
{
|
||||
ImVector<char> buf;
|
||||
buf.resize((int)(log_length + 1));
|
||||
glGetShaderInfoLog(handle, log_length, NULL, (GLchar*)buf.begin());
|
||||
fprintf(stderr, "%s\n", buf.begin());
|
||||
}
|
||||
return (GLboolean)status == GL_TRUE;
|
||||
}
|
||||
|
||||
// If you get an error please report on GitHub. You may try different GL context version or GLSL version.
|
||||
static bool CheckProgram(GLuint handle, const char* desc)
|
||||
{
|
||||
GLint status = 0, log_length = 0;
|
||||
glGetProgramiv(handle, GL_LINK_STATUS, &status);
|
||||
glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &log_length);
|
||||
if ((GLboolean)status == GL_FALSE)
|
||||
fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to link %s! (with GLSL '%s')\n", desc, g_GlslVersionString);
|
||||
if (log_length > 1)
|
||||
{
|
||||
ImVector<char> buf;
|
||||
buf.resize((int)(log_length + 1));
|
||||
glGetProgramInfoLog(handle, log_length, NULL, (GLchar*)buf.begin());
|
||||
fprintf(stderr, "%s\n", buf.begin());
|
||||
}
|
||||
return (GLboolean)status == GL_TRUE;
|
||||
}
|
||||
|
||||
bool ImGui_ImplOpenGL3_CreateDeviceObjects()
|
||||
{
|
||||
// Backup GL state
|
||||
GLint last_texture, last_array_buffer;
|
||||
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
|
||||
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
|
||||
#ifndef IMGUI_IMPL_OPENGL_ES2
|
||||
GLint last_vertex_array;
|
||||
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array);
|
||||
#endif
|
||||
|
||||
// Parse GLSL version string
|
||||
int glsl_version = 130;
|
||||
sscanf(g_GlslVersionString, "#version %d", &glsl_version);
|
||||
|
||||
const GLchar* vertex_shader_glsl_120 =
|
||||
"uniform mat4 ProjMtx;\n"
|
||||
"attribute vec2 Position;\n"
|
||||
"attribute vec2 UV;\n"
|
||||
"attribute vec4 Color;\n"
|
||||
"varying vec2 Frag_UV;\n"
|
||||
"varying vec4 Frag_Color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" Frag_UV = UV;\n"
|
||||
" Frag_Color = Color;\n"
|
||||
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
|
||||
"}\n";
|
||||
|
||||
const GLchar* vertex_shader_glsl_130 =
|
||||
"uniform mat4 ProjMtx;\n"
|
||||
"in vec2 Position;\n"
|
||||
"in vec2 UV;\n"
|
||||
"in vec4 Color;\n"
|
||||
"out vec2 Frag_UV;\n"
|
||||
"out vec4 Frag_Color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" Frag_UV = UV;\n"
|
||||
" Frag_Color = Color;\n"
|
||||
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
|
||||
"}\n";
|
||||
|
||||
const GLchar* vertex_shader_glsl_300_es =
|
||||
"precision mediump float;\n"
|
||||
"layout (location = 0) in vec2 Position;\n"
|
||||
"layout (location = 1) in vec2 UV;\n"
|
||||
"layout (location = 2) in vec4 Color;\n"
|
||||
"uniform mat4 ProjMtx;\n"
|
||||
"out vec2 Frag_UV;\n"
|
||||
"out vec4 Frag_Color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" Frag_UV = UV;\n"
|
||||
" Frag_Color = Color;\n"
|
||||
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
|
||||
"}\n";
|
||||
|
||||
const GLchar* vertex_shader_glsl_410_core =
|
||||
"layout (location = 0) in vec2 Position;\n"
|
||||
"layout (location = 1) in vec2 UV;\n"
|
||||
"layout (location = 2) in vec4 Color;\n"
|
||||
"uniform mat4 ProjMtx;\n"
|
||||
"out vec2 Frag_UV;\n"
|
||||
"out vec4 Frag_Color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" Frag_UV = UV;\n"
|
||||
" Frag_Color = Color;\n"
|
||||
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
|
||||
"}\n";
|
||||
|
||||
const GLchar* fragment_shader_glsl_120 =
|
||||
"#ifdef GL_ES\n"
|
||||
" precision mediump float;\n"
|
||||
"#endif\n"
|
||||
"uniform sampler2D Texture;\n"
|
||||
"varying vec2 Frag_UV;\n"
|
||||
"varying vec4 Frag_Color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV.st);\n"
|
||||
"}\n";
|
||||
|
||||
const GLchar* fragment_shader_glsl_130 =
|
||||
"uniform sampler2D Texture;\n"
|
||||
"in vec2 Frag_UV;\n"
|
||||
"in vec4 Frag_Color;\n"
|
||||
"out vec4 Out_Color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
|
||||
"}\n";
|
||||
|
||||
const GLchar* fragment_shader_glsl_300_es =
|
||||
"precision mediump float;\n"
|
||||
"uniform sampler2D Texture;\n"
|
||||
"in vec2 Frag_UV;\n"
|
||||
"in vec4 Frag_Color;\n"
|
||||
"layout (location = 0) out vec4 Out_Color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
|
||||
"}\n";
|
||||
|
||||
const GLchar* fragment_shader_glsl_410_core =
|
||||
"in vec2 Frag_UV;\n"
|
||||
"in vec4 Frag_Color;\n"
|
||||
"uniform sampler2D Texture;\n"
|
||||
"layout (location = 0) out vec4 Out_Color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
|
||||
"}\n";
|
||||
|
||||
// Select shaders matching our GLSL versions
|
||||
const GLchar* vertex_shader = NULL;
|
||||
const GLchar* fragment_shader = NULL;
|
||||
if (glsl_version < 130)
|
||||
{
|
||||
vertex_shader = vertex_shader_glsl_120;
|
||||
fragment_shader = fragment_shader_glsl_120;
|
||||
}
|
||||
else if (glsl_version >= 410)
|
||||
{
|
||||
vertex_shader = vertex_shader_glsl_410_core;
|
||||
fragment_shader = fragment_shader_glsl_410_core;
|
||||
}
|
||||
else if (glsl_version == 300)
|
||||
{
|
||||
vertex_shader = vertex_shader_glsl_300_es;
|
||||
fragment_shader = fragment_shader_glsl_300_es;
|
||||
}
|
||||
else
|
||||
{
|
||||
vertex_shader = vertex_shader_glsl_130;
|
||||
fragment_shader = fragment_shader_glsl_130;
|
||||
}
|
||||
|
||||
// Create shaders
|
||||
const GLchar* vertex_shader_with_version[2] = { g_GlslVersionString, vertex_shader };
|
||||
g_VertHandle = glCreateShader(GL_VERTEX_SHADER);
|
||||
glShaderSource(g_VertHandle, 2, vertex_shader_with_version, NULL);
|
||||
glCompileShader(g_VertHandle);
|
||||
CheckShader(g_VertHandle, "vertex shader");
|
||||
|
||||
const GLchar* fragment_shader_with_version[2] = { g_GlslVersionString, fragment_shader };
|
||||
g_FragHandle = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
glShaderSource(g_FragHandle, 2, fragment_shader_with_version, NULL);
|
||||
glCompileShader(g_FragHandle);
|
||||
CheckShader(g_FragHandle, "fragment shader");
|
||||
|
||||
g_ShaderHandle = glCreateProgram();
|
||||
glAttachShader(g_ShaderHandle, g_VertHandle);
|
||||
glAttachShader(g_ShaderHandle, g_FragHandle);
|
||||
glLinkProgram(g_ShaderHandle);
|
||||
CheckProgram(g_ShaderHandle, "shader program");
|
||||
|
||||
g_AttribLocationTex = glGetUniformLocation(g_ShaderHandle, "Texture");
|
||||
g_AttribLocationProjMtx = glGetUniformLocation(g_ShaderHandle, "ProjMtx");
|
||||
g_AttribLocationVtxPos = (GLuint)glGetAttribLocation(g_ShaderHandle, "Position");
|
||||
g_AttribLocationVtxUV = (GLuint)glGetAttribLocation(g_ShaderHandle, "UV");
|
||||
g_AttribLocationVtxColor = (GLuint)glGetAttribLocation(g_ShaderHandle, "Color");
|
||||
|
||||
// Create buffers
|
||||
glGenBuffers(1, &g_VboHandle);
|
||||
glGenBuffers(1, &g_ElementsHandle);
|
||||
|
||||
ImGui_ImplOpenGL3_CreateFontsTexture();
|
||||
|
||||
// Restore modified GL state
|
||||
glBindTexture(GL_TEXTURE_2D, last_texture);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
|
||||
#ifndef IMGUI_IMPL_OPENGL_ES2
|
||||
glBindVertexArray(last_vertex_array);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGui_ImplOpenGL3_DestroyDeviceObjects()
|
||||
{
|
||||
if (g_VboHandle) { glDeleteBuffers(1, &g_VboHandle); g_VboHandle = 0; }
|
||||
if (g_ElementsHandle) { glDeleteBuffers(1, &g_ElementsHandle); g_ElementsHandle = 0; }
|
||||
if (g_ShaderHandle && g_VertHandle) { glDetachShader(g_ShaderHandle, g_VertHandle); }
|
||||
if (g_ShaderHandle && g_FragHandle) { glDetachShader(g_ShaderHandle, g_FragHandle); }
|
||||
if (g_VertHandle) { glDeleteShader(g_VertHandle); g_VertHandle = 0; }
|
||||
if (g_FragHandle) { glDeleteShader(g_FragHandle); g_FragHandle = 0; }
|
||||
if (g_ShaderHandle) { glDeleteProgram(g_ShaderHandle); g_ShaderHandle = 0; }
|
||||
|
||||
ImGui_ImplOpenGL3_DestroyFontsTexture();
|
||||
}
|
377
vendor/imgui/src/imgui_impl_sdl.cpp
vendored
Normal file
377
vendor/imgui/src/imgui_impl_sdl.cpp
vendored
Normal file
|
@ -0,0 +1,377 @@
|
|||
// dear imgui: Platform Backend for SDL2
|
||||
// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
|
||||
// (Info: SDL2 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.)
|
||||
// (Requires: SDL 2.0. Prefer SDL 2.0.4+ for full feature support.)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
|
||||
// [X] Platform: Clipboard support.
|
||||
// [X] Platform: Keyboard arrays indexed using SDL_SCANCODE_* codes, e.g. ImGui::IsKeyPressed(SDL_SCANCODE_SPACE).
|
||||
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
|
||||
// Missing features:
|
||||
// [ ] Platform: SDL2 handling of IME under Windows appears to be broken and it explicitly disable the regular Windows IME. You can restore Windows IME by compiling SDL with SDL_DISABLE_WINDOWS_IME.
|
||||
|
||||
// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
|
||||
// Read online: https://github.com/ocornut/imgui/tree/master/docs
|
||||
|
||||
// CHANGELOG
|
||||
// (minor and older changes stripped away, please see git history for details)
|
||||
// 2021-03-22: Rework global mouse pos availability check listing supported platforms explicitly, effectively fixing mouse access on Raspberry Pi. (#2837, #3950)
|
||||
// 2020-05-25: Misc: Report a zero display-size when window is minimized, to be consistent with other backends.
|
||||
// 2020-02-20: Inputs: Fixed mapping for ImGuiKey_KeyPadEnter (using SDL_SCANCODE_KP_ENTER instead of SDL_SCANCODE_RETURN2).
|
||||
// 2019-12-17: Inputs: On Wayland, use SDL_GetMouseState (because there is no global mouse state).
|
||||
// 2019-12-05: Inputs: Added support for ImGuiMouseCursor_NotAllowed mouse cursor.
|
||||
// 2019-07-21: Inputs: Added mapping for ImGuiKey_KeyPadEnter.
|
||||
// 2019-04-23: Inputs: Added support for SDL_GameController (if ImGuiConfigFlags_NavEnableGamepad is set by user application).
|
||||
// 2019-03-12: Misc: Preserve DisplayFramebufferScale when main window is minimized.
|
||||
// 2018-12-21: Inputs: Workaround for Android/iOS which don't seem to handle focus related calls.
|
||||
// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window.
|
||||
// 2018-11-14: Changed the signature of ImGui_ImplSDL2_ProcessEvent() to take a 'const SDL_Event*'.
|
||||
// 2018-08-01: Inputs: Workaround for Emscripten which doesn't seem to handle focus related calls.
|
||||
// 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor.
|
||||
// 2018-06-08: Misc: Extracted imgui_impl_sdl.cpp/.h away from the old combined SDL2+OpenGL/Vulkan examples.
|
||||
// 2018-06-08: Misc: ImGui_ImplSDL2_InitForOpenGL() now takes a SDL_GLContext parameter.
|
||||
// 2018-05-09: Misc: Fixed clipboard paste memory leak (we didn't call SDL_FreeMemory on the data returned by SDL_GetClipboardText).
|
||||
// 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors flag + honor ImGuiConfigFlags_NoMouseCursorChange flag.
|
||||
// 2018-02-16: Inputs: Added support for mouse cursors, honoring ImGui::GetMouseCursor() value.
|
||||
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
|
||||
// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space.
|
||||
// 2018-02-05: Misc: Using SDL_GetPerformanceCounter() instead of SDL_GetTicks() to be able to handle very high framerate (1000+ FPS).
|
||||
// 2018-02-05: Inputs: Keyboard mapping is using scancodes everywhere instead of a confusing mixture of keycodes and scancodes.
|
||||
// 2018-01-20: Inputs: Added Horizontal Mouse Wheel support.
|
||||
// 2018-01-19: Inputs: When available (SDL 2.0.4+) using SDL_CaptureMouse() to retrieve coordinates outside of client area when dragging. Otherwise (SDL 2.0.3 and before) testing for SDL_WINDOW_INPUT_FOCUS instead of SDL_WINDOW_MOUSE_FOCUS.
|
||||
// 2018-01-18: Inputs: Added mapping for ImGuiKey_Insert.
|
||||
// 2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1).
|
||||
// 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers.
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_sdl.h"
|
||||
|
||||
// SDL
|
||||
#include <SDL.h>
|
||||
#include <SDL_syswm.h>
|
||||
#if defined(__APPLE__)
|
||||
#include "TargetConditionals.h"
|
||||
#endif
|
||||
|
||||
#define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE SDL_VERSION_ATLEAST(2,0,4)
|
||||
#define SDL_HAS_VULKAN SDL_VERSION_ATLEAST(2,0,6)
|
||||
|
||||
// Data
|
||||
static SDL_Window* g_Window = NULL;
|
||||
static Uint64 g_Time = 0;
|
||||
static bool g_MousePressed[3] = { false, false, false };
|
||||
static SDL_Cursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = {};
|
||||
static char* g_ClipboardTextData = NULL;
|
||||
static bool g_MouseCanUseGlobalState = true;
|
||||
|
||||
static const char* ImGui_ImplSDL2_GetClipboardText(void*)
|
||||
{
|
||||
if (g_ClipboardTextData)
|
||||
SDL_free(g_ClipboardTextData);
|
||||
g_ClipboardTextData = SDL_GetClipboardText();
|
||||
return g_ClipboardTextData;
|
||||
}
|
||||
|
||||
static void ImGui_ImplSDL2_SetClipboardText(void*, const char* text)
|
||||
{
|
||||
SDL_SetClipboardText(text);
|
||||
}
|
||||
|
||||
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
|
||||
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application.
|
||||
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application.
|
||||
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
|
||||
// If you have multiple SDL events and some of them are not meant to be used by dear imgui, you may need to filter events based on their windowID field.
|
||||
bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event)
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
switch (event->type)
|
||||
{
|
||||
case SDL_MOUSEWHEEL:
|
||||
{
|
||||
if (event->wheel.x > 0) io.MouseWheelH += 1;
|
||||
if (event->wheel.x < 0) io.MouseWheelH -= 1;
|
||||
if (event->wheel.y > 0) io.MouseWheel += 1;
|
||||
if (event->wheel.y < 0) io.MouseWheel -= 1;
|
||||
return true;
|
||||
}
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
{
|
||||
if (event->button.button == SDL_BUTTON_LEFT) g_MousePressed[0] = true;
|
||||
if (event->button.button == SDL_BUTTON_RIGHT) g_MousePressed[1] = true;
|
||||
if (event->button.button == SDL_BUTTON_MIDDLE) g_MousePressed[2] = true;
|
||||
return true;
|
||||
}
|
||||
case SDL_TEXTINPUT:
|
||||
{
|
||||
io.AddInputCharactersUTF8(event->text.text);
|
||||
return true;
|
||||
}
|
||||
case SDL_KEYDOWN:
|
||||
case SDL_KEYUP:
|
||||
{
|
||||
int key = event->key.keysym.scancode;
|
||||
IM_ASSERT(key >= 0 && key < IM_ARRAYSIZE(io.KeysDown));
|
||||
io.KeysDown[key] = (event->type == SDL_KEYDOWN);
|
||||
io.KeyShift = ((SDL_GetModState() & KMOD_SHIFT) != 0);
|
||||
io.KeyCtrl = ((SDL_GetModState() & KMOD_CTRL) != 0);
|
||||
io.KeyAlt = ((SDL_GetModState() & KMOD_ALT) != 0);
|
||||
#ifdef _WIN32
|
||||
io.KeySuper = false;
|
||||
#else
|
||||
io.KeySuper = ((SDL_GetModState() & KMOD_GUI) != 0);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool ImGui_ImplSDL2_Init(SDL_Window* window)
|
||||
{
|
||||
g_Window = window;
|
||||
|
||||
// Setup backend capabilities flags
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
|
||||
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
|
||||
io.BackendPlatformName = "imgui_impl_sdl";
|
||||
|
||||
// Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeysDown[] array.
|
||||
io.KeyMap[ImGuiKey_Tab] = SDL_SCANCODE_TAB;
|
||||
io.KeyMap[ImGuiKey_LeftArrow] = SDL_SCANCODE_LEFT;
|
||||
io.KeyMap[ImGuiKey_RightArrow] = SDL_SCANCODE_RIGHT;
|
||||
io.KeyMap[ImGuiKey_UpArrow] = SDL_SCANCODE_UP;
|
||||
io.KeyMap[ImGuiKey_DownArrow] = SDL_SCANCODE_DOWN;
|
||||
io.KeyMap[ImGuiKey_PageUp] = SDL_SCANCODE_PAGEUP;
|
||||
io.KeyMap[ImGuiKey_PageDown] = SDL_SCANCODE_PAGEDOWN;
|
||||
io.KeyMap[ImGuiKey_Home] = SDL_SCANCODE_HOME;
|
||||
io.KeyMap[ImGuiKey_End] = SDL_SCANCODE_END;
|
||||
io.KeyMap[ImGuiKey_Insert] = SDL_SCANCODE_INSERT;
|
||||
io.KeyMap[ImGuiKey_Delete] = SDL_SCANCODE_DELETE;
|
||||
io.KeyMap[ImGuiKey_Backspace] = SDL_SCANCODE_BACKSPACE;
|
||||
io.KeyMap[ImGuiKey_Space] = SDL_SCANCODE_SPACE;
|
||||
io.KeyMap[ImGuiKey_Enter] = SDL_SCANCODE_RETURN;
|
||||
io.KeyMap[ImGuiKey_Escape] = SDL_SCANCODE_ESCAPE;
|
||||
io.KeyMap[ImGuiKey_KeyPadEnter] = SDL_SCANCODE_KP_ENTER;
|
||||
io.KeyMap[ImGuiKey_A] = SDL_SCANCODE_A;
|
||||
io.KeyMap[ImGuiKey_C] = SDL_SCANCODE_C;
|
||||
io.KeyMap[ImGuiKey_V] = SDL_SCANCODE_V;
|
||||
io.KeyMap[ImGuiKey_X] = SDL_SCANCODE_X;
|
||||
io.KeyMap[ImGuiKey_Y] = SDL_SCANCODE_Y;
|
||||
io.KeyMap[ImGuiKey_Z] = SDL_SCANCODE_Z;
|
||||
|
||||
io.SetClipboardTextFn = ImGui_ImplSDL2_SetClipboardText;
|
||||
io.GetClipboardTextFn = ImGui_ImplSDL2_GetClipboardText;
|
||||
io.ClipboardUserData = NULL;
|
||||
|
||||
// Load mouse cursors
|
||||
g_MouseCursors[ImGuiMouseCursor_Arrow] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW);
|
||||
g_MouseCursors[ImGuiMouseCursor_TextInput] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM);
|
||||
g_MouseCursors[ImGuiMouseCursor_ResizeAll] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL);
|
||||
g_MouseCursors[ImGuiMouseCursor_ResizeNS] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS);
|
||||
g_MouseCursors[ImGuiMouseCursor_ResizeEW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE);
|
||||
g_MouseCursors[ImGuiMouseCursor_ResizeNESW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW);
|
||||
g_MouseCursors[ImGuiMouseCursor_ResizeNWSE] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE);
|
||||
g_MouseCursors[ImGuiMouseCursor_Hand] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND);
|
||||
g_MouseCursors[ImGuiMouseCursor_NotAllowed] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NO);
|
||||
|
||||
// Check and store if we are on a SDL backend that supports global mouse position
|
||||
// ("wayland" and "rpi" don't support it, but we chose to use a white-list instead of a black-list)
|
||||
const char* sdl_backend = SDL_GetCurrentVideoDriver();
|
||||
const char* global_mouse_whitelist[] = { "windows", "cocoa", "x11", "DIVE", "VMAN" };
|
||||
g_MouseCanUseGlobalState = false;
|
||||
for (int n = 0; n < IM_ARRAYSIZE(global_mouse_whitelist); n++)
|
||||
if (strncmp(sdl_backend, global_mouse_whitelist[n], strlen(global_mouse_whitelist[n])) == 0)
|
||||
g_MouseCanUseGlobalState = true;
|
||||
|
||||
#ifdef _WIN32
|
||||
SDL_SysWMinfo wmInfo;
|
||||
SDL_VERSION(&wmInfo.version);
|
||||
SDL_GetWindowWMInfo(window, &wmInfo);
|
||||
io.ImeWindowHandle = wmInfo.info.win.window;
|
||||
#else
|
||||
(void)window;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ImGui_ImplSDL2_InitForOpenGL(SDL_Window* window, void* sdl_gl_context)
|
||||
{
|
||||
(void)sdl_gl_context; // Viewport branch will need this.
|
||||
return ImGui_ImplSDL2_Init(window);
|
||||
}
|
||||
|
||||
bool ImGui_ImplSDL2_InitForVulkan(SDL_Window* window)
|
||||
{
|
||||
#if !SDL_HAS_VULKAN
|
||||
IM_ASSERT(0 && "Unsupported");
|
||||
#endif
|
||||
return ImGui_ImplSDL2_Init(window);
|
||||
}
|
||||
|
||||
bool ImGui_ImplSDL2_InitForD3D(SDL_Window* window)
|
||||
{
|
||||
#if !defined(_WIN32)
|
||||
IM_ASSERT(0 && "Unsupported");
|
||||
#endif
|
||||
return ImGui_ImplSDL2_Init(window);
|
||||
}
|
||||
|
||||
bool ImGui_ImplSDL2_InitForMetal(SDL_Window* window)
|
||||
{
|
||||
return ImGui_ImplSDL2_Init(window);
|
||||
}
|
||||
|
||||
void ImGui_ImplSDL2_Shutdown()
|
||||
{
|
||||
g_Window = NULL;
|
||||
|
||||
// Destroy last known clipboard data
|
||||
if (g_ClipboardTextData)
|
||||
SDL_free(g_ClipboardTextData);
|
||||
g_ClipboardTextData = NULL;
|
||||
|
||||
// Destroy SDL mouse cursors
|
||||
for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++)
|
||||
SDL_FreeCursor(g_MouseCursors[cursor_n]);
|
||||
memset(g_MouseCursors, 0, sizeof(g_MouseCursors));
|
||||
}
|
||||
|
||||
static void ImGui_ImplSDL2_UpdateMousePosAndButtons()
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
// Set OS mouse position if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
|
||||
if (io.WantSetMousePos)
|
||||
SDL_WarpMouseInWindow(g_Window, (int)io.MousePos.x, (int)io.MousePos.y);
|
||||
else
|
||||
io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
|
||||
|
||||
int mx, my;
|
||||
Uint32 mouse_buttons = SDL_GetMouseState(&mx, &my);
|
||||
io.MouseDown[0] = g_MousePressed[0] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0; // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame.
|
||||
io.MouseDown[1] = g_MousePressed[1] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0;
|
||||
io.MouseDown[2] = g_MousePressed[2] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_MIDDLE)) != 0;
|
||||
g_MousePressed[0] = g_MousePressed[1] = g_MousePressed[2] = false;
|
||||
|
||||
#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE && !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) && !(defined(__APPLE__) && TARGET_OS_IOS)
|
||||
SDL_Window* focused_window = SDL_GetKeyboardFocus();
|
||||
if (g_Window == focused_window)
|
||||
{
|
||||
if (g_MouseCanUseGlobalState)
|
||||
{
|
||||
// SDL_GetMouseState() gives mouse position seemingly based on the last window entered/focused(?)
|
||||
// The creation of a new windows at runtime and SDL_CaptureMouse both seems to severely mess up with that, so we retrieve that position globally.
|
||||
// Won't use this workaround on SDL backends that have no global mouse position, like Wayland or RPI
|
||||
int wx, wy;
|
||||
SDL_GetWindowPosition(focused_window, &wx, &wy);
|
||||
SDL_GetGlobalMouseState(&mx, &my);
|
||||
mx -= wx;
|
||||
my -= wy;
|
||||
}
|
||||
io.MousePos = ImVec2((float)mx, (float)my);
|
||||
}
|
||||
|
||||
// SDL_CaptureMouse() let the OS know e.g. that our imgui drag outside the SDL window boundaries shouldn't e.g. trigger the OS window resize cursor.
|
||||
// The function is only supported from SDL 2.0.4 (released Jan 2016)
|
||||
bool any_mouse_button_down = ImGui::IsAnyMouseDown();
|
||||
SDL_CaptureMouse(any_mouse_button_down ? SDL_TRUE : SDL_FALSE);
|
||||
#else
|
||||
if (SDL_GetWindowFlags(g_Window) & SDL_WINDOW_INPUT_FOCUS)
|
||||
io.MousePos = ImVec2((float)mx, (float)my);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void ImGui_ImplSDL2_UpdateMouseCursor()
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)
|
||||
return;
|
||||
|
||||
ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
|
||||
if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None)
|
||||
{
|
||||
// Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
|
||||
SDL_ShowCursor(SDL_FALSE);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Show OS mouse cursor
|
||||
SDL_SetCursor(g_MouseCursors[imgui_cursor] ? g_MouseCursors[imgui_cursor] : g_MouseCursors[ImGuiMouseCursor_Arrow]);
|
||||
SDL_ShowCursor(SDL_TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
static void ImGui_ImplSDL2_UpdateGamepads()
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
memset(io.NavInputs, 0, sizeof(io.NavInputs));
|
||||
if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0)
|
||||
return;
|
||||
|
||||
// Get gamepad
|
||||
SDL_GameController* game_controller = SDL_GameControllerOpen(0);
|
||||
if (!game_controller)
|
||||
{
|
||||
io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
|
||||
return;
|
||||
}
|
||||
|
||||
// Update gamepad inputs
|
||||
#define MAP_BUTTON(NAV_NO, BUTTON_NO) { io.NavInputs[NAV_NO] = (SDL_GameControllerGetButton(game_controller, BUTTON_NO) != 0) ? 1.0f : 0.0f; }
|
||||
#define MAP_ANALOG(NAV_NO, AXIS_NO, V0, V1) { float vn = (float)(SDL_GameControllerGetAxis(game_controller, AXIS_NO) - V0) / (float)(V1 - V0); if (vn > 1.0f) vn = 1.0f; if (vn > 0.0f && io.NavInputs[NAV_NO] < vn) io.NavInputs[NAV_NO] = vn; }
|
||||
const int thumb_dead_zone = 8000; // SDL_gamecontroller.h suggests using this value.
|
||||
MAP_BUTTON(ImGuiNavInput_Activate, SDL_CONTROLLER_BUTTON_A); // Cross / A
|
||||
MAP_BUTTON(ImGuiNavInput_Cancel, SDL_CONTROLLER_BUTTON_B); // Circle / B
|
||||
MAP_BUTTON(ImGuiNavInput_Menu, SDL_CONTROLLER_BUTTON_X); // Square / X
|
||||
MAP_BUTTON(ImGuiNavInput_Input, SDL_CONTROLLER_BUTTON_Y); // Triangle / Y
|
||||
MAP_BUTTON(ImGuiNavInput_DpadLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT); // D-Pad Left
|
||||
MAP_BUTTON(ImGuiNavInput_DpadRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT); // D-Pad Right
|
||||
MAP_BUTTON(ImGuiNavInput_DpadUp, SDL_CONTROLLER_BUTTON_DPAD_UP); // D-Pad Up
|
||||
MAP_BUTTON(ImGuiNavInput_DpadDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN); // D-Pad Down
|
||||
MAP_BUTTON(ImGuiNavInput_FocusPrev, SDL_CONTROLLER_BUTTON_LEFTSHOULDER); // L1 / LB
|
||||
MAP_BUTTON(ImGuiNavInput_FocusNext, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER); // R1 / RB
|
||||
MAP_BUTTON(ImGuiNavInput_TweakSlow, SDL_CONTROLLER_BUTTON_LEFTSHOULDER); // L1 / LB
|
||||
MAP_BUTTON(ImGuiNavInput_TweakFast, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER); // R1 / RB
|
||||
MAP_ANALOG(ImGuiNavInput_LStickLeft, SDL_CONTROLLER_AXIS_LEFTX, -thumb_dead_zone, -32768);
|
||||
MAP_ANALOG(ImGuiNavInput_LStickRight, SDL_CONTROLLER_AXIS_LEFTX, +thumb_dead_zone, +32767);
|
||||
MAP_ANALOG(ImGuiNavInput_LStickUp, SDL_CONTROLLER_AXIS_LEFTY, -thumb_dead_zone, -32767);
|
||||
MAP_ANALOG(ImGuiNavInput_LStickDown, SDL_CONTROLLER_AXIS_LEFTY, +thumb_dead_zone, +32767);
|
||||
|
||||
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
|
||||
#undef MAP_BUTTON
|
||||
#undef MAP_ANALOG
|
||||
}
|
||||
|
||||
void ImGui_ImplSDL2_NewFrame(SDL_Window* window)
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
IM_ASSERT(io.Fonts->IsBuilt() && "Font atlas not built! It is generally built by the renderer backend. Missing call to renderer _NewFrame() function? e.g. ImGui_ImplOpenGL3_NewFrame().");
|
||||
|
||||
// Setup display size (every frame to accommodate for window resizing)
|
||||
int w, h;
|
||||
int display_w, display_h;
|
||||
SDL_GetWindowSize(window, &w, &h);
|
||||
if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED)
|
||||
w = h = 0;
|
||||
SDL_GL_GetDrawableSize(window, &display_w, &display_h);
|
||||
io.DisplaySize = ImVec2((float)w, (float)h);
|
||||
if (w > 0 && h > 0)
|
||||
io.DisplayFramebufferScale = ImVec2((float)display_w / w, (float)display_h / h);
|
||||
|
||||
// Setup time step (we don't use SDL_GetTicks() because it is using millisecond resolution)
|
||||
static Uint64 frequency = SDL_GetPerformanceFrequency();
|
||||
Uint64 current_time = SDL_GetPerformanceCounter();
|
||||
io.DeltaTime = g_Time > 0 ? (float)((double)(current_time - g_Time) / frequency) : (float)(1.0f / 60.0f);
|
||||
g_Time = current_time;
|
||||
|
||||
ImGui_ImplSDL2_UpdateMousePosAndButtons();
|
||||
ImGui_ImplSDL2_UpdateMouseCursor();
|
||||
|
||||
// Update game controllers (if enabled and available)
|
||||
ImGui_ImplSDL2_UpdateGamepads();
|
||||
}
|
677
vendor/imgui/src/imgui_sdl.cpp
vendored
Normal file
677
vendor/imgui/src/imgui_sdl.cpp
vendored
Normal file
|
@ -0,0 +1,677 @@
|
|||
#include "imgui_sdl.h"
|
||||
|
||||
#include "SDL.h"
|
||||
|
||||
#include "imgui.h"
|
||||
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <cmath>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace
|
||||
{
|
||||
struct Device* CurrentDevice = nullptr;
|
||||
|
||||
namespace TupleHash
|
||||
{
|
||||
template <typename T> struct Hash
|
||||
{
|
||||
std::size_t operator()(const T& value) const
|
||||
{
|
||||
return std::hash<T>()(value);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> void CombineHash(std::size_t& seed, const T& value)
|
||||
{
|
||||
seed ^= TupleHash::Hash<T>()(value) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||
}
|
||||
|
||||
template <typename Tuple, std::size_t Index = std::tuple_size<Tuple>::value - 1> struct Hasher
|
||||
{
|
||||
static void Hash(std::size_t& seed, const Tuple& tuple)
|
||||
{
|
||||
Hasher<Tuple, Index - 1>::Hash(seed, tuple);
|
||||
CombineHash(seed, std::get<Index>(tuple));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Tuple> struct Hasher<Tuple, 0>
|
||||
{
|
||||
static void Hash(std::size_t& seed, const Tuple& tuple)
|
||||
{
|
||||
CombineHash(seed, std::get<0>(tuple));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename... T> struct Hash<std::tuple<T...>>
|
||||
{
|
||||
std::size_t operator()(const std::tuple<T...>& value) const
|
||||
{
|
||||
std::size_t seed = 0;
|
||||
Hasher<std::tuple<T...>>::Hash(seed, value);
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template <typename Key, typename Value, std::size_t Size> class LRUCache
|
||||
{
|
||||
public:
|
||||
bool Contains(const Key& key) const
|
||||
{
|
||||
return Container.find(key) != Container.end();
|
||||
}
|
||||
|
||||
const Value& At(const Key& key)
|
||||
{
|
||||
assert(Contains(key));
|
||||
|
||||
const auto location = Container.find(key);
|
||||
Order.splice(Order.begin(), Order, location->second);
|
||||
return location->second->second;
|
||||
}
|
||||
|
||||
void Insert(const Key& key, Value value)
|
||||
{
|
||||
const auto existingLocation = Container.find(key);
|
||||
if (existingLocation != Container.end())
|
||||
{
|
||||
Order.erase(existingLocation->second);
|
||||
Container.erase(existingLocation);
|
||||
}
|
||||
|
||||
Order.push_front(std::make_pair(key, std::move(value)));
|
||||
Container.insert(std::make_pair(key, Order.begin()));
|
||||
|
||||
Clean();
|
||||
}
|
||||
private:
|
||||
void Clean()
|
||||
{
|
||||
while (Container.size() > Size)
|
||||
{
|
||||
auto last = Order.end();
|
||||
last--;
|
||||
Container.erase(last->first);
|
||||
Order.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
std::list<std::pair<Key, Value>> Order;
|
||||
std::unordered_map<Key, decltype(Order.begin()), TupleHash::Hash<Key>> Container;
|
||||
};
|
||||
|
||||
struct Color
|
||||
{
|
||||
const float R, G, B, A;
|
||||
|
||||
explicit Color(uint32_t color)
|
||||
: R(((color >> 0) & 0xff) / 255.0f), G(((color >> 8) & 0xff) / 255.0f), B(((color >> 16) & 0xff) / 255.0f), A(((color >> 24) & 0xff) / 255.0f) { }
|
||||
Color(float r, float g, float b, float a) : R(r), G(g), B(b), A(a) { }
|
||||
|
||||
Color operator*(const Color& c) const { return Color(R * c.R, G * c.G, B * c.B, A * c.A); }
|
||||
Color operator*(float v) const { return Color(R * v, G * v, B * v, A * v); }
|
||||
Color operator+(const Color& c) const { return Color(R + c.R, G + c.G, B + c.B, A + c.A); }
|
||||
|
||||
uint32_t ToInt() const
|
||||
{
|
||||
return ((static_cast<int>(R * 255) & 0xff) << 0)
|
||||
| ((static_cast<int>(G * 255) & 0xff) << 8)
|
||||
| ((static_cast<int>(B * 255) & 0xff) << 16)
|
||||
| ((static_cast<int>(A * 255) & 0xff) << 24);
|
||||
}
|
||||
|
||||
void UseAsDrawColor(SDL_Renderer* renderer) const
|
||||
{
|
||||
SDL_SetRenderDrawColor(renderer,
|
||||
static_cast<uint8_t>(R * 255),
|
||||
static_cast<uint8_t>(G * 255),
|
||||
static_cast<uint8_t>(B * 255),
|
||||
static_cast<uint8_t>(A * 255));
|
||||
}
|
||||
};
|
||||
|
||||
struct Device
|
||||
{
|
||||
SDL_Renderer* Renderer;
|
||||
|
||||
struct ClipRect
|
||||
{
|
||||
int X, Y, Width, Height;
|
||||
} Clip;
|
||||
|
||||
struct TriangleCacheItem
|
||||
{
|
||||
SDL_Texture* Texture = nullptr;
|
||||
int Width = 0, Height = 0;
|
||||
|
||||
~TriangleCacheItem() { if (Texture) SDL_DestroyTexture(Texture); }
|
||||
};
|
||||
|
||||
// You can tweak these to values that you find that work the best.
|
||||
static constexpr std::size_t UniformColorTriangleCacheSize = 512;
|
||||
static constexpr std::size_t GenericTriangleCacheSize = 64;
|
||||
|
||||
// Uniform color is identified by its color and the coordinates of the edges.
|
||||
using UniformColorTriangleKey = std::tuple<uint32_t, int, int, int, int, int, int>;
|
||||
// The generic triangle cache unfortunately has to be basically a full representation of the triangle.
|
||||
// This includes the (offset) vertex positions, texture coordinates and vertex colors.
|
||||
using GenericTriangleVertexKey = std::tuple<int, int, double, double, uint32_t>;
|
||||
using GenericTriangleKey = std::tuple<GenericTriangleVertexKey, GenericTriangleVertexKey, GenericTriangleVertexKey>;
|
||||
|
||||
LRUCache<UniformColorTriangleKey, std::unique_ptr<TriangleCacheItem>, UniformColorTriangleCacheSize> UniformColorTriangleCache;
|
||||
LRUCache<GenericTriangleKey, std::unique_ptr<TriangleCacheItem>, GenericTriangleCacheSize> GenericTriangleCache;
|
||||
|
||||
Device(SDL_Renderer* renderer) : Renderer(renderer) { }
|
||||
|
||||
void SetClipRect(const ClipRect& rect)
|
||||
{
|
||||
Clip = rect;
|
||||
const SDL_Rect clip = { rect.X, rect.Y, rect.Width, rect.Height };
|
||||
SDL_RenderSetClipRect(Renderer, &clip);
|
||||
}
|
||||
|
||||
void EnableClip() { SetClipRect(Clip); }
|
||||
void DisableClip() { SDL_RenderSetClipRect(Renderer, nullptr); }
|
||||
|
||||
void SetAt(int x, int y, const Color& color)
|
||||
{
|
||||
color.UseAsDrawColor(Renderer);
|
||||
SDL_RenderDrawPoint(Renderer, x, y);
|
||||
}
|
||||
|
||||
SDL_Texture* MakeTexture(int width, int height)
|
||||
{
|
||||
SDL_Texture* texture = SDL_CreateTexture(Renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_TARGET, width, height);
|
||||
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
|
||||
return texture;
|
||||
}
|
||||
|
||||
void UseAsRenderTarget(SDL_Texture* texture)
|
||||
{
|
||||
SDL_SetRenderTarget(Renderer, texture);
|
||||
if (texture)
|
||||
{
|
||||
SDL_SetRenderDrawColor(Renderer, 0, 0, 0, 0);
|
||||
SDL_RenderClear(Renderer);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct Texture
|
||||
{
|
||||
SDL_Surface* Surface;
|
||||
SDL_Texture* Source;
|
||||
|
||||
~Texture()
|
||||
{
|
||||
SDL_FreeSurface(Surface);
|
||||
SDL_DestroyTexture(Source);
|
||||
}
|
||||
|
||||
Color Sample(float u, float v) const
|
||||
{
|
||||
const int x = static_cast<int>(std::round(u * (Surface->w - 1) + 0.5f));
|
||||
const int y = static_cast<int>(std::round(v * (Surface->h - 1) + 0.5f));
|
||||
|
||||
const int location = y * Surface->w + x;
|
||||
assert(location < Surface->w* Surface->h);
|
||||
|
||||
return Color(static_cast<uint32_t*>(Surface->pixels)[location]);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> class InterpolatedFactorEquation
|
||||
{
|
||||
public:
|
||||
InterpolatedFactorEquation(const T& value0, const T& value1, const T& value2, const ImVec2& v0, const ImVec2& v1, const ImVec2& v2)
|
||||
: Value0(value0), Value1(value1), Value2(value2), V0(v0), V1(v1), V2(v2),
|
||||
Divisor((V1.y - V2.y)* (V0.x - V2.x) + (V2.x - V1.x) * (V0.y - V2.y)) { }
|
||||
|
||||
T Evaluate(float x, float y) const
|
||||
{
|
||||
const float w1 = ((V1.y - V2.y) * (x - V2.x) + (V2.x - V1.x) * (y - V2.y)) / Divisor;
|
||||
const float w2 = ((V2.y - V0.y) * (x - V2.x) + (V0.x - V2.x) * (y - V2.y)) / Divisor;
|
||||
const float w3 = 1.0f - w1 - w2;
|
||||
|
||||
return static_cast<T>((Value0 * w1) + (Value1 * w2) + (Value2 * w3));
|
||||
}
|
||||
private:
|
||||
const T Value0;
|
||||
const T Value1;
|
||||
const T Value2;
|
||||
|
||||
const ImVec2& V0;
|
||||
const ImVec2& V1;
|
||||
const ImVec2& V2;
|
||||
|
||||
const float Divisor;
|
||||
};
|
||||
|
||||
struct Rect
|
||||
{
|
||||
float MinX, MinY, MaxX, MaxY;
|
||||
float MinU, MinV, MaxU, MaxV;
|
||||
|
||||
bool IsOnExtreme(const ImVec2& point) const
|
||||
{
|
||||
return (point.x == MinX || point.x == MaxX) && (point.y == MinY || point.y == MaxY);
|
||||
}
|
||||
|
||||
bool UsesOnlyColor() const
|
||||
{
|
||||
const ImVec2& whitePixel = ImGui::GetIO().Fonts->TexUvWhitePixel;
|
||||
|
||||
return MinU == MaxU && MinU == whitePixel.x && MinV == MaxV && MaxV == whitePixel.y;
|
||||
}
|
||||
|
||||
static Rect CalculateBoundingBox(const ImDrawVert& v0, const ImDrawVert& v1, const ImDrawVert& v2)
|
||||
{
|
||||
return Rect{
|
||||
std::min({ v0.pos.x, v1.pos.x, v2.pos.x }),
|
||||
std::min({ v0.pos.y, v1.pos.y, v2.pos.y }),
|
||||
std::max({ v0.pos.x, v1.pos.x, v2.pos.x }),
|
||||
std::max({ v0.pos.y, v1.pos.y, v2.pos.y }),
|
||||
std::min({ v0.uv.x, v1.uv.x, v2.uv.x }),
|
||||
std::min({ v0.uv.y, v1.uv.y, v2.uv.y }),
|
||||
std::max({ v0.uv.x, v1.uv.x, v2.uv.x }),
|
||||
std::max({ v0.uv.y, v1.uv.y, v2.uv.y })
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
struct FixedPointTriangleRenderInfo
|
||||
{
|
||||
int X1, X2, X3, Y1, Y2, Y3;
|
||||
int MinX, MaxX, MinY, MaxY;
|
||||
|
||||
static FixedPointTriangleRenderInfo CalculateFixedPointTriangleInfo(const ImVec2& v1, const ImVec2& v2, const ImVec2& v3)
|
||||
{
|
||||
static constexpr float scale = 16.0f;
|
||||
|
||||
const int x1 = static_cast<int>(std::round(v1.x * scale));
|
||||
const int x2 = static_cast<int>(std::round(v2.x * scale));
|
||||
const int x3 = static_cast<int>(std::round(v3.x * scale));
|
||||
|
||||
const int y1 = static_cast<int>(std::round(v1.y * scale));
|
||||
const int y2 = static_cast<int>(std::round(v2.y * scale));
|
||||
const int y3 = static_cast<int>(std::round(v3.y * scale));
|
||||
|
||||
int minX = (std::min({ x1, x2, x3 }) + 0xF) >> 4;
|
||||
int maxX = (std::max({ x1, x2, x3 }) + 0xF) >> 4;
|
||||
int minY = (std::min({ y1, y2, y3 }) + 0xF) >> 4;
|
||||
int maxY = (std::max({ y1, y2, y3 }) + 0xF) >> 4;
|
||||
|
||||
return FixedPointTriangleRenderInfo{ x1, x2, x3, y1, y2, y3, minX, maxX, minY, maxY };
|
||||
}
|
||||
};
|
||||
|
||||
void DrawTriangleWithColorFunction(const FixedPointTriangleRenderInfo& renderInfo, const std::function<Color(float x, float y)>& colorFunction, Device::TriangleCacheItem* cacheItem)
|
||||
{
|
||||
// Implementation source: https://web.archive.org/web/20171128164608/http://forum.devmaster.net/t/advanced-rasterization/6145.
|
||||
// This is a fixed point implementation that rounds to top-left.
|
||||
|
||||
const int deltaX12 = renderInfo.X1 - renderInfo.X2;
|
||||
const int deltaX23 = renderInfo.X2 - renderInfo.X3;
|
||||
const int deltaX31 = renderInfo.X3 - renderInfo.X1;
|
||||
|
||||
const int deltaY12 = renderInfo.Y1 - renderInfo.Y2;
|
||||
const int deltaY23 = renderInfo.Y2 - renderInfo.Y3;
|
||||
const int deltaY31 = renderInfo.Y3 - renderInfo.Y1;
|
||||
|
||||
const int fixedDeltaX12 = deltaX12 << 4;
|
||||
const int fixedDeltaX23 = deltaX23 << 4;
|
||||
const int fixedDeltaX31 = deltaX31 << 4;
|
||||
|
||||
const int fixedDeltaY12 = deltaY12 << 4;
|
||||
const int fixedDeltaY23 = deltaY23 << 4;
|
||||
const int fixedDeltaY31 = deltaY31 << 4;
|
||||
|
||||
const int width = renderInfo.MaxX - renderInfo.MinX;
|
||||
const int height = renderInfo.MaxY - renderInfo.MinY;
|
||||
if (width == 0 || height == 0) return;
|
||||
|
||||
int c1 = deltaY12 * renderInfo.X1 - deltaX12 * renderInfo.Y1;
|
||||
int c2 = deltaY23 * renderInfo.X2 - deltaX23 * renderInfo.Y2;
|
||||
int c3 = deltaY31 * renderInfo.X3 - deltaX31 * renderInfo.Y3;
|
||||
|
||||
if (deltaY12 < 0 || (deltaY12 == 0 && deltaX12 > 0)) c1++;
|
||||
if (deltaY23 < 0 || (deltaY23 == 0 && deltaX23 > 0)) c2++;
|
||||
if (deltaY31 < 0 || (deltaY31 == 0 && deltaX31 > 0)) c3++;
|
||||
|
||||
int edgeStart1 = c1 + deltaX12 * (renderInfo.MinY << 4) - deltaY12 * (renderInfo.MinX << 4);
|
||||
int edgeStart2 = c2 + deltaX23 * (renderInfo.MinY << 4) - deltaY23 * (renderInfo.MinX << 4);
|
||||
int edgeStart3 = c3 + deltaX31 * (renderInfo.MinY << 4) - deltaY31 * (renderInfo.MinX << 4);
|
||||
|
||||
SDL_Texture* cache = CurrentDevice->MakeTexture(width, height);
|
||||
CurrentDevice->DisableClip();
|
||||
CurrentDevice->UseAsRenderTarget(cache);
|
||||
|
||||
for (int y = renderInfo.MinY; y < renderInfo.MaxY; y++)
|
||||
{
|
||||
int edge1 = edgeStart1;
|
||||
int edge2 = edgeStart2;
|
||||
int edge3 = edgeStart3;
|
||||
|
||||
for (int x = renderInfo.MinX; x < renderInfo.MaxX; x++)
|
||||
{
|
||||
if (edge1 > 0 && edge2 > 0 && edge3 > 0)
|
||||
{
|
||||
CurrentDevice->SetAt(x - renderInfo.MinX, y - renderInfo.MinY, colorFunction(x + 0.5f, y + 0.5f));
|
||||
}
|
||||
|
||||
edge1 -= fixedDeltaY12;
|
||||
edge2 -= fixedDeltaY23;
|
||||
edge3 -= fixedDeltaY31;
|
||||
}
|
||||
|
||||
edgeStart1 += fixedDeltaX12;
|
||||
edgeStart2 += fixedDeltaX23;
|
||||
edgeStart3 += fixedDeltaX31;
|
||||
}
|
||||
|
||||
CurrentDevice->UseAsRenderTarget(nullptr);
|
||||
CurrentDevice->EnableClip();
|
||||
|
||||
cacheItem->Texture = cache;
|
||||
cacheItem->Width = width;
|
||||
cacheItem->Height = height;
|
||||
}
|
||||
|
||||
void DrawCachedTriangle(const Device::TriangleCacheItem& triangle, const FixedPointTriangleRenderInfo& renderInfo)
|
||||
{
|
||||
const SDL_Rect destination = { renderInfo.MinX, renderInfo.MinY, triangle.Width, triangle.Height };
|
||||
SDL_RenderCopy(CurrentDevice->Renderer, triangle.Texture, nullptr, &destination);
|
||||
}
|
||||
|
||||
void DrawTriangle(const ImDrawVert& v1, const ImDrawVert& v2, const ImDrawVert& v3, const Texture* texture)
|
||||
{
|
||||
// The naming inconsistency in the parameters is intentional. The fixed point algorithm wants the vertices in a counter clockwise order.
|
||||
const auto& renderInfo = FixedPointTriangleRenderInfo::CalculateFixedPointTriangleInfo(v3.pos, v2.pos, v1.pos);
|
||||
|
||||
// First we check if there is a cached version of this triangle already waiting for us. If so, we can just do a super fast texture copy.
|
||||
|
||||
const auto key = std::make_tuple(
|
||||
std::make_tuple(static_cast<int>(std::round(v1.pos.x)) - renderInfo.MinX, static_cast<int>(std::round(v1.pos.y)) - renderInfo.MinY, v1.uv.x, v1.uv.y, v1.col),
|
||||
std::make_tuple(static_cast<int>(std::round(v2.pos.x)) - renderInfo.MinX, static_cast<int>(std::round(v2.pos.y)) - renderInfo.MinY, v2.uv.x, v2.uv.y, v2.col),
|
||||
std::make_tuple(static_cast<int>(std::round(v3.pos.x)) - renderInfo.MinX, static_cast<int>(std::round(v3.pos.y)) - renderInfo.MinY, v3.uv.x, v3.uv.y, v3.col));
|
||||
|
||||
if (CurrentDevice->GenericTriangleCache.Contains(key))
|
||||
{
|
||||
const auto& cached = CurrentDevice->GenericTriangleCache.At(key);
|
||||
DrawCachedTriangle(*cached, renderInfo);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const InterpolatedFactorEquation<float> textureU(v1.uv.x, v2.uv.x, v3.uv.x, v1.pos, v2.pos, v3.pos);
|
||||
const InterpolatedFactorEquation<float> textureV(v1.uv.y, v2.uv.y, v3.uv.y, v1.pos, v2.pos, v3.pos);
|
||||
|
||||
const InterpolatedFactorEquation<Color> shadeColor(Color(v1.col), Color(v2.col), Color(v3.col), v1.pos, v2.pos, v3.pos);
|
||||
|
||||
auto cached = std::make_unique<Device::TriangleCacheItem>();
|
||||
DrawTriangleWithColorFunction(renderInfo, [&](float x, float y) {
|
||||
const float u = textureU.Evaluate(x, y);
|
||||
const float v = textureV.Evaluate(x, y);
|
||||
const Color sampled = texture->Sample(u, v);
|
||||
const Color shade = shadeColor.Evaluate(x, y);
|
||||
|
||||
return sampled * shade;
|
||||
}, cached.get());
|
||||
|
||||
if (!cached->Texture) return;
|
||||
|
||||
const SDL_Rect destination = { renderInfo.MinX, renderInfo.MinY, cached->Width, cached->Height };
|
||||
SDL_RenderCopy(CurrentDevice->Renderer, cached->Texture, nullptr, &destination);
|
||||
|
||||
CurrentDevice->GenericTriangleCache.Insert(key, std::move(cached));
|
||||
}
|
||||
|
||||
void DrawUniformColorTriangle(const ImDrawVert& v1, const ImDrawVert& v2, const ImDrawVert& v3)
|
||||
{
|
||||
const Color color(v1.col);
|
||||
|
||||
// The naming inconsistency in the parameters is intentional. The fixed point algorithm wants the vertices in a counter clockwise order.
|
||||
const auto& renderInfo = FixedPointTriangleRenderInfo::CalculateFixedPointTriangleInfo(v3.pos, v2.pos, v1.pos);
|
||||
|
||||
const auto key = std::make_tuple(v1.col,
|
||||
static_cast<int>(std::round(v1.pos.x)) - renderInfo.MinX, static_cast<int>(std::round(v1.pos.y)) - renderInfo.MinY,
|
||||
static_cast<int>(std::round(v2.pos.x)) - renderInfo.MinX, static_cast<int>(std::round(v2.pos.y)) - renderInfo.MinY,
|
||||
static_cast<int>(std::round(v3.pos.x)) - renderInfo.MinX, static_cast<int>(std::round(v3.pos.y)) - renderInfo.MinY);
|
||||
if (CurrentDevice->UniformColorTriangleCache.Contains(key))
|
||||
{
|
||||
const auto& cached = CurrentDevice->UniformColorTriangleCache.At(key);
|
||||
DrawCachedTriangle(*cached, renderInfo);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto cached = std::make_unique<Device::TriangleCacheItem>();
|
||||
DrawTriangleWithColorFunction(renderInfo, [&color](float, float) { return color; }, cached.get());
|
||||
|
||||
if (!cached->Texture) return;
|
||||
|
||||
const SDL_Rect destination = { renderInfo.MinX, renderInfo.MinY, cached->Width, cached->Height };
|
||||
SDL_RenderCopy(CurrentDevice->Renderer, cached->Texture, nullptr, &destination);
|
||||
|
||||
CurrentDevice->UniformColorTriangleCache.Insert(key, std::move(cached));
|
||||
}
|
||||
|
||||
void DrawRectangle(const Rect& bounding, SDL_Texture* texture, int textureWidth, int textureHeight, const Color& color, bool doHorizontalFlip, bool doVerticalFlip)
|
||||
{
|
||||
// We are safe to assume uniform color here, because the caller checks it and and uses the triangle renderer to render those.
|
||||
|
||||
const SDL_Rect destination = {
|
||||
static_cast<int>(bounding.MinX),
|
||||
static_cast<int>(bounding.MinY),
|
||||
static_cast<int>(bounding.MaxX - bounding.MinX),
|
||||
static_cast<int>(bounding.MaxY - bounding.MinY)
|
||||
};
|
||||
|
||||
// If the area isn't textured, we can just draw a rectangle with the correct color.
|
||||
if (bounding.UsesOnlyColor())
|
||||
{
|
||||
color.UseAsDrawColor(CurrentDevice->Renderer);
|
||||
SDL_RenderFillRect(CurrentDevice->Renderer, &destination);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We can now just calculate the correct source rectangle and draw it.
|
||||
|
||||
const SDL_Rect source = {
|
||||
static_cast<int>(bounding.MinU * textureWidth),
|
||||
static_cast<int>(bounding.MinV * textureHeight),
|
||||
static_cast<int>((bounding.MaxU - bounding.MinU) * textureWidth),
|
||||
static_cast<int>((bounding.MaxV - bounding.MinV) * textureHeight)
|
||||
};
|
||||
|
||||
const SDL_RendererFlip flip = static_cast<SDL_RendererFlip>((doHorizontalFlip ? SDL_FLIP_HORIZONTAL : 0) | (doVerticalFlip ? SDL_FLIP_VERTICAL : 0));
|
||||
|
||||
SDL_SetTextureColorMod(texture, static_cast<uint8_t>(color.R * 255), static_cast<uint8_t>(color.G * 255), static_cast<uint8_t>(color.B * 255));
|
||||
SDL_RenderCopyEx(CurrentDevice->Renderer, texture, &source, &destination, 0.0, nullptr, flip);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawRectangle(const Rect& bounding, const Texture* texture, const Color& color, bool doHorizontalFlip, bool doVerticalFlip)
|
||||
{
|
||||
DrawRectangle(bounding, texture->Source, texture->Surface->w, texture->Surface->h, color, doHorizontalFlip, doVerticalFlip);
|
||||
}
|
||||
|
||||
void DrawRectangle(const Rect& bounding, SDL_Texture* texture, const Color& color, bool doHorizontalFlip, bool doVerticalFlip)
|
||||
{
|
||||
int width, height;
|
||||
SDL_QueryTexture(texture, nullptr, nullptr, &width, &height);
|
||||
DrawRectangle(bounding, texture, width, height, color, doHorizontalFlip, doVerticalFlip);
|
||||
}
|
||||
}
|
||||
|
||||
namespace ImGuiSDL
|
||||
{
|
||||
void Initialize(SDL_Renderer* renderer, int windowWidth, int windowHeight)
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.DisplaySize.x = static_cast<float>(windowWidth);
|
||||
io.DisplaySize.y = static_cast<float>(windowHeight);
|
||||
|
||||
ImGui::GetStyle().WindowRounding = 0.0f;
|
||||
ImGui::GetStyle().AntiAliasedFill = false;
|
||||
ImGui::GetStyle().AntiAliasedLines = false;
|
||||
|
||||
// Loads the font texture.
|
||||
unsigned char* pixels;
|
||||
int width, height;
|
||||
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
|
||||
static constexpr uint32_t rmask = 0x000000ff, gmask = 0x0000ff00, bmask = 0x00ff0000, amask = 0xff000000;
|
||||
SDL_Surface* surface = SDL_CreateRGBSurfaceFrom(pixels, width, height, 32, 4 * width, rmask, gmask, bmask, amask);
|
||||
|
||||
Texture* texture = new Texture();
|
||||
texture->Surface = surface;
|
||||
texture->Source = SDL_CreateTextureFromSurface(renderer, surface);
|
||||
io.Fonts->TexID = (void*)texture;
|
||||
|
||||
CurrentDevice = new Device(renderer);
|
||||
}
|
||||
|
||||
void Deinitialize()
|
||||
{
|
||||
// Frees up the memory of the font texture.
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
Texture* texture = static_cast<Texture*>(io.Fonts->TexID);
|
||||
delete texture;
|
||||
|
||||
delete CurrentDevice;
|
||||
}
|
||||
|
||||
void Render(ImDrawData* drawData)
|
||||
{
|
||||
SDL_BlendMode blendMode;
|
||||
SDL_GetRenderDrawBlendMode(CurrentDevice->Renderer, &blendMode);
|
||||
SDL_SetRenderDrawBlendMode(CurrentDevice->Renderer, SDL_BLENDMODE_BLEND);
|
||||
|
||||
Uint8 initialR, initialG, initialB, initialA;
|
||||
SDL_GetRenderDrawColor(CurrentDevice->Renderer, &initialR, &initialG, &initialB, &initialA);
|
||||
|
||||
SDL_bool initialClipEnabled = SDL_RenderIsClipEnabled(CurrentDevice->Renderer);
|
||||
SDL_Rect initialClipRect;
|
||||
SDL_RenderGetClipRect(CurrentDevice->Renderer, &initialClipRect);
|
||||
|
||||
SDL_Texture* initialRenderTarget = SDL_GetRenderTarget(CurrentDevice->Renderer);
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
for (int n = 0; n < drawData->CmdListsCount; n++)
|
||||
{
|
||||
auto commandList = drawData->CmdLists[n];
|
||||
auto vertexBuffer = commandList->VtxBuffer;
|
||||
auto indexBuffer = commandList->IdxBuffer.Data;
|
||||
|
||||
for (int cmd_i = 0; cmd_i < commandList->CmdBuffer.Size; cmd_i++)
|
||||
{
|
||||
const ImDrawCmd* drawCommand = &commandList->CmdBuffer[cmd_i];
|
||||
|
||||
const Device::ClipRect clipRect = {
|
||||
static_cast<int>(drawCommand->ClipRect.x),
|
||||
static_cast<int>(drawCommand->ClipRect.y),
|
||||
static_cast<int>(drawCommand->ClipRect.z - drawCommand->ClipRect.x),
|
||||
static_cast<int>(drawCommand->ClipRect.w - drawCommand->ClipRect.y)
|
||||
};
|
||||
|
||||
CurrentDevice->SetClipRect(clipRect);
|
||||
|
||||
if (drawCommand->UserCallback)
|
||||
{
|
||||
drawCommand->UserCallback(commandList, drawCommand);
|
||||
}
|
||||
else
|
||||
{
|
||||
const bool isWrappedTexture = drawCommand->TextureId == io.Fonts->TexID;
|
||||
|
||||
// Loops over triangles.
|
||||
for (unsigned int i = 0; i + 3 <= drawCommand->ElemCount; i += 3)
|
||||
{
|
||||
const ImDrawVert& v0 = vertexBuffer[indexBuffer[i + 0]];
|
||||
const ImDrawVert& v1 = vertexBuffer[indexBuffer[i + 1]];
|
||||
const ImDrawVert& v2 = vertexBuffer[indexBuffer[i + 2]];
|
||||
|
||||
const Rect& bounding = Rect::CalculateBoundingBox(v0, v1, v2);
|
||||
|
||||
const bool isTriangleUniformColor = v0.col == v1.col && v1.col == v2.col;
|
||||
const bool doesTriangleUseOnlyColor = bounding.UsesOnlyColor();
|
||||
|
||||
// Actually, since we render a whole bunch of rectangles, we try to first detect those, and render them more efficiently.
|
||||
// How are rectangles detected? It's actually pretty simple: If all 6 vertices lie on the extremes of the bounding box,
|
||||
// it's a rectangle.
|
||||
if (i + 6 <= drawCommand->ElemCount)
|
||||
{
|
||||
const ImDrawVert& v3 = vertexBuffer[indexBuffer[i + 3]];
|
||||
const ImDrawVert& v4 = vertexBuffer[indexBuffer[i + 4]];
|
||||
const ImDrawVert& v5 = vertexBuffer[indexBuffer[i + 5]];
|
||||
|
||||
const bool isUniformColor = isTriangleUniformColor && v2.col == v3.col && v3.col == v4.col && v4.col == v5.col;
|
||||
|
||||
if (isUniformColor
|
||||
&& bounding.IsOnExtreme(v0.pos)
|
||||
&& bounding.IsOnExtreme(v1.pos)
|
||||
&& bounding.IsOnExtreme(v2.pos)
|
||||
&& bounding.IsOnExtreme(v3.pos)
|
||||
&& bounding.IsOnExtreme(v4.pos)
|
||||
&& bounding.IsOnExtreme(v5.pos))
|
||||
{
|
||||
// ImGui gives the triangles in a nice order: the first vertex happens to be the topleft corner of our rectangle.
|
||||
// We need to check for the orientation of the texture, as I believe in theory ImGui could feed us a flipped texture,
|
||||
// so that the larger texture coordinates are at topleft instead of bottomright.
|
||||
// We don't consider equal texture coordinates to require a flip, as then the rectangle is mostlikely simply a colored rectangle.
|
||||
const bool doHorizontalFlip = v2.uv.x < v0.uv.x;
|
||||
const bool doVerticalFlip = v2.uv.x < v0.uv.x;
|
||||
|
||||
if (isWrappedTexture)
|
||||
{
|
||||
DrawRectangle(bounding, static_cast<const Texture*>(drawCommand->TextureId), Color(v0.col), doHorizontalFlip, doVerticalFlip);
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawRectangle(bounding, static_cast<SDL_Texture*>(drawCommand->TextureId), Color(v0.col), doHorizontalFlip, doVerticalFlip);
|
||||
}
|
||||
|
||||
i += 3; // Additional increment to account for the extra 3 vertices we consumed.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (isTriangleUniformColor && doesTriangleUseOnlyColor)
|
||||
{
|
||||
DrawUniformColorTriangle(v0, v1, v2);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Currently we assume that any non rectangular texture samples the font texture. Dunno if that's what actually happens, but it seems to work.
|
||||
assert(isWrappedTexture);
|
||||
DrawTriangle(v0, v1, v2, static_cast<const Texture*>(drawCommand->TextureId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
indexBuffer += drawCommand->ElemCount;
|
||||
}
|
||||
}
|
||||
|
||||
CurrentDevice->DisableClip();
|
||||
|
||||
SDL_SetRenderTarget(CurrentDevice->Renderer, initialRenderTarget);
|
||||
|
||||
SDL_RenderSetClipRect(CurrentDevice->Renderer, initialClipEnabled ? &initialClipRect : nullptr);
|
||||
|
||||
SDL_SetRenderDrawColor(CurrentDevice->Renderer,
|
||||
initialR, initialG, initialB, initialA);
|
||||
|
||||
SDL_SetRenderDrawBlendMode(CurrentDevice->Renderer, blendMode);
|
||||
}
|
||||
}
|
4028
vendor/imgui/src/imgui_tables.cpp
vendored
Normal file
4028
vendor/imgui/src/imgui_tables.cpp
vendored
Normal file
File diff suppressed because it is too large
Load diff
8056
vendor/imgui/src/imgui_widgets.cpp
vendored
Normal file
8056
vendor/imgui/src/imgui_widgets.cpp
vendored
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue