1
0
mirror of https://github.com/XProger/OpenLara.git synced 2025-08-05 04:37:50 +02:00

GBA initial commit

This commit is contained in:
Timur Gagiev
2020-08-12 08:15:02 +03:00
parent 37d6410f37
commit e7ffb928bf
10 changed files with 2349 additions and 0 deletions

167
src/platform/gba/Makefile Normal file
View File

@@ -0,0 +1,167 @@
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
endif
include $(DEVKITARM)/gba_rules
#---------------------------------------------------------------------------------
# TARGET is the name of the output
# BUILD is the directory where object files & intermediate files will be placed
# SOURCES is a list of directories containing source code
# INCLUDES is a list of directories containing extra header files
# DATA is a list of directories containing binary data
# GRAPHICS is a list of directories containing files to be processed by grit
#
# All directories are specified relative to the project directory where
# the makefile is found
#
#---------------------------------------------------------------------------------
TARGET := OpenLara
BUILD := build
SOURCES := .
INCLUDES := include
DATA := data
MUSIC :=
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -mthumb -mthumb-interwork
CFLAGS := -g -Wall -O3\
-mcpu=arm7tdmi -mtune=arm7tdmi\
-fomit-frame-pointer\
-ffast-math\
$(ARCH)
CFLAGS += $(INCLUDE)
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
ASFLAGS := -g $(ARCH)
LDFLAGS = -g $(ARCH) -Wl,-Map,$(notdir $*.map)
#---------------------------------------------------------------------------------
# any extra libraries we wish to link with the project
#---------------------------------------------------------------------------------
LIBS := -lmm -lgba
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS := $(LIBGBA)
#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#---------------------------------------------------------------------------------
ifneq ($(BUILD),$(notdir $(CURDIR)))
#---------------------------------------------------------------------------------
export OUTPUT := $(CURDIR)/$(TARGET)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir)) \
$(foreach dir,$(GRAPHICS),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
ifneq ($(strip $(MUSIC)),)
export AUDIOFILES := $(foreach dir,$(notdir $(wildcard $(MUSIC)/*.*)),$(CURDIR)/$(MUSIC)/$(dir))
BINFILES += soundbank.bin
endif
#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
#---------------------------------------------------------------------------------
export LD := $(CC)
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
export LD := $(CXX)
#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
export OFILES_SOURCES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SOURCES)
export HFILES := $(addsuffix .h,$(subst .,_,$(BINFILES)))
export INCLUDE := $(foreach dir,$(INCLUDES),-iquote $(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
-I$(CURDIR)/$(BUILD)
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
.PHONY: $(BUILD) clean
#---------------------------------------------------------------------------------
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) $(TARGET).elf $(TARGET).gba
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
$(OUTPUT).gba : $(OUTPUT).elf
$(OUTPUT).elf : $(OFILES)
$(OFILES_SOURCES) : $(HFILES)
#---------------------------------------------------------------------------------
# The bin2o rule should be copied and modified
# for each extension used in the data directories
#---------------------------------------------------------------------------------
#---------------------------------------------------------------------------------
# rule to build soundbank from music files
#---------------------------------------------------------------------------------
soundbank.bin soundbank.h : $(AUDIOFILES)
#---------------------------------------------------------------------------------
@mmutil $^ -osoundbank.bin -hsoundbank.h
#---------------------------------------------------------------------------------
# This rule links in binary data with the .bin extension
#---------------------------------------------------------------------------------
%.PHD.o %_PHD.h : %.PHD
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPSDIR)/*.d
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

View File

@@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.28307.1022
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OpenLara", "OpenLara.vcxproj", "{990C6F40-6226-4011-B52C-FF042EBB7F15}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{990C6F40-6226-4011-B52C-FF042EBB7F15}.Debug|x64.ActiveCfg = Debug|x64
{990C6F40-6226-4011-B52C-FF042EBB7F15}.Debug|x64.Build.0 = Debug|x64
{990C6F40-6226-4011-B52C-FF042EBB7F15}.Debug|x86.ActiveCfg = Debug|Win32
{990C6F40-6226-4011-B52C-FF042EBB7F15}.Debug|x86.Build.0 = Debug|Win32
{990C6F40-6226-4011-B52C-FF042EBB7F15}.Release|x64.ActiveCfg = Release|x64
{990C6F40-6226-4011-B52C-FF042EBB7F15}.Release|x64.Build.0 = Release|x64
{990C6F40-6226-4011-B52C-FF042EBB7F15}.Release|x86.ActiveCfg = Release|Win32
{990C6F40-6226-4011-B52C-FF042EBB7F15}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {8D133CA9-7AA0-4806-9B61-82E8A3044A15}
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,163 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClCompile Include="common.iwram.cpp" />
<ClCompile Include="main.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="common.h" />
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{990C6F40-6226-4011-B52C-FF042EBB7F15}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>OpenLara</RootNamespace>
<WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level2</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level2</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

269
src/platform/gba/common.h Normal file
View File

@@ -0,0 +1,269 @@
#ifndef H_COMMON
#define H_COMMON
#ifdef _WIN32
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#else
#include <gba_console.h>
#include <gba_video.h>
#include <gba_timers.h>
#include <gba_interrupt.h>
#include <gba_systemcalls.h>
#include <gba_input.h>
#include <gba_dma.h>
#include <gba_affine.h>
#include <fade.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <assert.h>
//#define USE_MODE_5
#ifdef USE_MODE_5
#define WIDTH 160
#define HEIGHT 128
#define FRAME_WIDTH 160
#define FRAME_HEIGHT 128
#define PIXEL_SIZE 1
#else // MODE_4
#define WIDTH 240
#define HEIGHT 160
#define FRAME_WIDTH 240
#define FRAME_HEIGHT 160
#define PIXEL_SIZE 2
#endif
#define WND_SCALE 4
typedef signed char int8;
typedef signed short int16;
typedef signed int int32;
typedef signed long long int64;
typedef unsigned char uint8;
typedef unsigned short uint16;
typedef unsigned int uint32;
typedef int16 Index;
#define PI 3.14159265358979323846f
#define PIH (PI * 0.5f)
#define PI2 (PI * 2.0f)
#define DEG2RAD (PI / 180.0f)
#define RAD2DEG (180.0f / PI)
#ifdef _WIN32
extern uint8* LEVEL1_PHD;
extern uint32 VRAM[WIDTH * HEIGHT];
#ifdef USE_MODE_5
extern uint16 fb[WIDTH * HEIGHT];
#else
extern uint8 fb[WIDTH * HEIGHT];
#endif
#define IWRAM_CODE
#define EWRAM_DATA
#define dmaCopy(src,dst,size) memcpy(dst,src,size)
#define ALIGN4
struct ObjAffineSource {
int16 sX;
int16 sY;
uint16 theta;
};
struct ObjAffineDest {
int16 pa;
int16 pb;
int16 pc;
int16 pd;
};
static void ObjAffineSet(ObjAffineSource *source, ObjAffineDest *dest, int32 num, int32 offset) {
float ang = (source->theta >> 8) * PI / 128.0f;
int32 c = int32(cosf(ang) * 16384.0f);
int32 s = int32(sinf(ang) * 16384.0f);
dest->pa = ( source->sX * c) >> 14;
dest->pb = (-source->sX * s) >> 14;
dest->pc = ( source->sY * s) >> 14;
dest->pd = ( source->sY * c) >> 14;
}
#else
#define ALIGN4 __attribute__ ((aligned (4)))
extern uint32 fb;
#endif
enum InputKey {
IK_UP,
IK_RIGHT,
IK_DOWN,
IK_LEFT,
IK_A,
IK_B,
IK_L,
IK_R,
IK_MAX
};
extern bool keys[IK_MAX];
struct Quad {
Index indices[4];
uint16 flags;
};
struct Triangle {
Index indices[3];
uint16 flags;
};
struct Room {
struct Info {
int32 x, z;
int32 yBottom, yTop;
};
struct Vertex {
int16 x, y, z;
uint16 lighting;
};
struct Sprite {
Index index;
uint16 texture;
};
struct Portal {
struct Vertex {
int16 x, y, z;
};
uint16 roomIndex;
Vertex n;
Vertex v[4];
};
struct Sector {
uint16 floorIndex;
uint16 boxIndex;
uint8 roomBelow;
int8 floor;
uint8 roomAbove;
int8 ceiling;
};
struct Light {
// int32 x, y, z;
// uint16 intensity;
// uint32 radius;
uint8 dummy[18];
};
struct Mesh {
// int32 x, y, z;
// uint16 rotation;
// uint16 intensity;
// uint16 meshID;
uint8 dummy[18];
};
Info info;
uint32 dataSize;
/*
uint16 vCount;
Vertex* vertices;
uint16 qCount;
Quad* quads;
uint16 tCount;
Triangle* triangles;
uint16 sCount;
Sprite* sprites;
*/
};
struct Node {
uint32 flags;
int32 x, y, z;
};
struct Model {
uint16 type;
uint16 index;
uint16 mCount;
uint16 mStart;
uint32 node;
uint32 frame;
uint16 animation;
};
struct Texture {
uint16 attribute;
uint16 tile:14, :2;
uint8 xh0, x0, yh0, y0;
uint8 xh1, x1, yh1, y1;
uint8 xh2, x2, yh2, y2;
uint8 xh3, x3, yh3, y3;
};
struct Sprite {
uint16 tile;
uint8 u, v;
uint16 w, h;
int16 l, t, r, b;
};
struct SpriteSequence {
uint16 type;
uint16 unused;
int16 sCount;
int16 sStart;
};
struct Rect {
int32 x0;
int32 y0;
int32 x1;
int32 y1;
};
struct Vertex {
int16 x, y;
int16 z;
uint8 u, v, g, clip;
};
struct Face {
uint16 flags;
int16 depth;
int16 start;
int8 indices[4];
};
#define MAX_VERTICES 1024
#define MAX_FACES 384
#define MAX_DIST (16 * 1024)
#define FACE_TRIANGLE 0x8000
#define FACE_COLORED 0x4000
#define FACE_TEXTURE 0x3FFF
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#define MAX(a,b) ((a) > (b) ? (a) : (b))
void drawGlyph(int32 index, int32 x, int32 y);
void clear();
#endif

View File

@@ -0,0 +1,993 @@
#include "common.h"
#define DIV_TABLE_SIZE 512
uint16 divTable[DIV_TABLE_SIZE];
#ifdef _WIN32
uint8* LEVEL1_PHD;
uint32 VRAM[WIDTH * HEIGHT];
#ifdef USE_MODE_5
uint16 fb[WIDTH * HEIGHT];
#else
uint8 fb[WIDTH * HEIGHT];
#endif
#define MyDiv(Number, Divisor) ((Number) / (Divisor))
#else
uint32 fb = VRAM;
#define MyDiv(Number, Divisor) ((Number) / (Divisor))
//#define MyDiv(Number, Divisor) Div(Number, Divisor)
#endif
#define FixedInvS(x) ((x < 0) ? -divTable[abs(x)] : divTable[x])
#define FixedInvU(x) divTable[x]
bool keys[IK_MAX] = {};
#if defined(USE_MODE_5) || defined(_WIN32)
uint16 palette[256];
#endif
uint8 lightmap[256 * 32];
Vertex gVertices[MAX_VERTICES];
uint32 gVerticesCount = 0;
EWRAM_DATA Face gFaces[MAX_FACES];
int32 gFacesCount = 0;
Rect clip;
const uint8* tiles[15];
const uint8* curTile;
uint16 mipMask;
int32 fps;
int32 camSinY;
int32 camCosY;
int32 camX = 75162;
int32 camY = 3072 - 1024;
int32 camZ = 5000;
Vertex* Ledges[4];
Vertex* Redges[4];
int32 Lindex, Rindex;
uint16 roomsCount;
const Room* rooms;
uint32 texturesCount;
const Texture* textures;
const Sprite* sprites;
uint32 spriteSequencesCount;
const SpriteSequence* spriteSequences;
int32 seqGlyphs;
const uint8* meshData;
const uint32* meshOffsets;
const int32* nodes;
const Model* models;
#ifdef WIN32
#define INLINE inline
#else
#define INLINE __attribute__((always_inline)) inline
#endif
int32 clamp(int32 x, int32 a, int32 b) {
return x < a ? a : (x > b ? b : x);
}
template <class T>
inline void swap(T &a, T &b) {
T tmp = a;
a = b;
b = tmp;
}
bool checkBackface(const Vertex *a, const Vertex *b, const Vertex *c) {
return (b->x - a->x) * (c->y - a->y) -
(c->x - a->x) * (b->y - a->y) <= 0;
}
INLINE void sortVertices(Vertex *&t, Vertex *&m, Vertex *&b) {
if (t->y > m->y) swap(t, m);
if (t->y > b->y) swap(t, b);
if (m->y > b->y) swap(m, b);
}
INLINE void sortVertices(Vertex *&t, Vertex *&m, Vertex *&o, Vertex *&b) {
if (t->y > m->y) swap(t, m);
if (o->y > b->y) swap(o, b);
if (t->y > o->y) swap(t, o);
if (m->y > b->y) swap(m, b);
if (m->y > o->y) swap(m, o);
}
int32 classify(const Vertex* v) {
return (v->x < clip.x0 ? 1 : 0) |
(v->x > clip.x1 ? 2 : 0) |
(v->y < clip.y0 ? 4 : 0) |
(v->y > clip.y1 ? 8 : 0);
}
void transform(int32 vx, int32 vy, int32 vz, int32 vg, int32 x, int32 y, int32 z) {
#ifdef _WIN32
if (gVerticesCount >= MAX_VERTICES) {
DebugBreak();
return;
}
#endif
Vertex &res = gVertices[gVerticesCount++];
int32 px = vx + x;
int32 pz = vz + z;
int32 cz = px * camSinY + pz * camCosY;
cz >>= 16;
// znear / zfar clip
if (cz < 32 || cz > MAX_DIST) {
res.z = -1;
return;
}
int32 py = vy + y;
#if 0
int32 cx = (px * camCosY - pz * camSinY) >> 16;
int32 cy = py;
uint32 czInv = MyDiv(1 << 16, uint32(cz));
cx = cx * czInv;
cy = cy * czInv;
#else
int32 cx = px * camCosY - pz * camSinY;
int32 cy = py << 16;
cx = MyDiv(cx, cz);
cy = MyDiv(cy, cz);
#endif
cy = cy * FRAME_WIDTH / FRAME_HEIGHT;
cx = clamp(cx, -2 << 16, 2 << 16);
cy = clamp(cy, -2 << 16, 2 << 16);
res.x = ( ( (1 << 16) + cx ) * (FRAME_WIDTH / 2) ) >> 16;
res.y = ( ( (1 << 16) + cy ) * (FRAME_HEIGHT / 2) ) >> 16;
res.z = cz;
res.clip = classify(&res);
int32 fog = vg - ((cz * cz) >> 15);
if (fog < 0) {
fog = 0;
}
res.g = uint32(255 - (fog >> 5)) >> 3;
}
#define FETCH_T() curTile[(t & 0xFF00) | (t >> 24)]
#define FETCH_T_MIP() curTile[(t & 0xFF00) | (t >> 24) & mipMask]
#define FETCH_GT() lightmap[(g & 0x1F00) | FETCH_T()]
#define FETCH_G(palIndex) lightmap[(g & 0x1F00) | palIndex]
#define FETCH_GT_PAL() palette[FETCH_GT()]
#define FETCH_G_PAL(palIndex) palette[FETCH_G(palIndex)]
struct Edge {
int32 h;
int32 x;
int32 g;
uint32 t;
int32 dx;
int32 dg;
uint32 dt;
int32 index;
Vertex* vert[8];
Edge() : h(0), dx(0), dg(0), dt(0) {}
INLINE void stepG() {
x += dx;
g += dg;
}
INLINE void stepGT() {
x += dx;
g += dg;
t += dt;
}
INLINE bool nextG() {
if (index == 0) {
return false;
}
Vertex* v1 = vert[index--];
Vertex* v2 = vert[index];
h = v2->y - v1->y;
x = v1->x << 16;
g = v1->g << 16;
if (h > 1) {
uint32 d = FixedInvU(h);
dx = d * (v2->x - v1->x);
dg = d * (v2->g - v1->g);
}
return true;
}
INLINE bool nextGT() {
if (index == 0) {
return false;
}
Vertex* v1 = vert[index--];
Vertex* v2 = vert[index];
h = v2->y - v1->y;
x = v1->x << 16;
g = v1->g << 16;
t = (v1->u << 24) | (v1->v << 8);
if (h > 1) {
uint32 d = FixedInvU(h);
dx = d * (v2->x - v1->x);
dg = d * (v2->g - v1->g);
int32 du = d * (v2->u - v1->u);
int32 dv = d * (v2->v - v1->v);
dt = ((du << 8) & 0xFFFF0000) | int16(dv >> 8);
}
return true;
}
};
INLINE void scanlineG(uint16* buffer, int32 x1, int32 x2, uint8 palIndex, uint32 g, int32 dgdx) {
#ifdef USE_MODE_5
uint16* pixel = buffer + x1;
int32 width = (x2 - x1);
while (width--)
{
*pixel++ = FETCH_G_PAL(palIndex);
g += dgdx;
}
#else
if (x1 & 1)
{
uint16 &p = *(uint16*)((uint8*)buffer + x1 - 1);
p = (p & 0x00FF) | (FETCH_G(palIndex) << 8);
g += dgdx;
x1++;
}
int32 width = (x2 - x1) >> 1;
uint16* pixel = (uint16*)((uint8*)buffer + x1);
dgdx <<= 1;
if (width && (x1 & 3))
{
uint16 p = FETCH_G(palIndex);
*pixel++ = p | (FETCH_G(palIndex) << 8);
g += dgdx;
width--;
}
while (width-- > 0)
{
uint32 p = FETCH_G(palIndex);
p |= (FETCH_G(palIndex) << 8);
g += dgdx;
if (width-- > 0)
{
p |= (FETCH_G(palIndex) << 16);
p |= (FETCH_G(palIndex) << 24);
g += dgdx;
*(uint32*)pixel = p;
pixel += 2;
} else {
*(uint16*)pixel = p;
pixel += 1;
}
}
if (x2 & 1)
{
*pixel = (*pixel & 0xFF00) | FETCH_G(palIndex);
}
#endif
}
INLINE void scanlineGT(uint16* buffer, int32 x1, int32 x2, uint32 g, uint32 t, int32 dgdx, uint32 dtdx) {
#ifdef USE_MODE_5
uint16* pixel = buffer + x1;
int32 width = (x2 - x1);
while (width--)
{
*pixel++ = FETCH_GT_PAL();
t += dtdx;
g += dgdx;
}
#else
if (x1 & 1)
{
uint16 &p = *(uint16*)((uint8*)buffer + x1 - 1);
p = (p & 0x00FF) | (FETCH_GT() << 8);
t += dtdx;
g += dgdx;
x1++;
}
int32 width = (x2 - x1) >> 1;
uint16* pixel = (uint16*)((uint8*)buffer + x1);
dgdx <<= 1;
if (width && (x1 & 3))
{
uint16 p = FETCH_GT();
t += dtdx;
*pixel++ = p | (FETCH_GT() << 8);
t += dtdx;
g += dgdx;
width--;
}
while (width-- > 0)
{
uint32 p = FETCH_GT();
t += dtdx;
p |= (FETCH_GT() << 8);
t += dtdx;
g += dgdx;
if (width-- > 0)
{
p |= (FETCH_GT() << 16);
t += dtdx;
p |= (FETCH_GT() << 24);
t += dtdx;
g += dgdx;
*(uint32*)pixel = p;
pixel += 2;
} else {
*(uint16*)pixel = p;
pixel += 1;
}
}
if (x2 & 1)
{
*pixel = (*pixel & 0xFF00) | FETCH_GT();
}
#endif
}
void rasterizeG(uint16* buffer, int32 palIndex, Edge &L, Edge &R)
{
while (1)
{
while (L.h <= 0)
{
if (!L.nextG())
{
return;
}
}
while (R.h <= 0)
{
if (!R.nextG())
{
return;
}
}
int32 h = MIN(L.h, R.h);
L.h -= h;
R.h -= h;
while (h--) {
int32 x1 = L.x >> 16;
int32 x2 = R.x >> 16;
if (x2 > x1)
{
int32 d = FixedInvU(x2 - x1);
int32 dgdx = d * ((R.g - L.g) >> 8) >> 16;
scanlineG(buffer, x1, x2, palIndex, L.g >> 8, dgdx);
}
buffer += WIDTH / PIXEL_SIZE;
L.stepG();
R.stepG();
}
}
}
void rasterizeGT(uint16* buffer, Edge &L, Edge &R)
{
while (1)
{
while (L.h <= 0)
{
if (!L.nextGT())
{
return;
}
}
while (R.h <= 0)
{
if (!R.nextGT())
{
return;
}
}
int32 h = MIN(L.h, R.h);
L.h -= h;
R.h -= h;
while (h--) {
int32 x1 = L.x >> 16;
int32 x2 = R.x >> 16;
if (x2 > x1)
{
uint32 d = FixedInvU(x2 - x1);
int32 dgdx = d * ((R.g - L.g) >> 8) >> 16;
uint32 u = d * ((R.t >> 16) - (L.t >> 16));
uint32 v = d * ((R.t & 0xFFFF) - (L.t & 0xFFFF));
uint32 dtdx = (u & 0xFFFF0000) | (v >> 16);
scanlineGT(buffer, x1, x2, L.g >> 8, L.t, dgdx, dtdx);
}
buffer += WIDTH / PIXEL_SIZE;
L.stepGT();
R.stepGT();
}
}
}
void drawTriangle(uint16 flags, int32 start, const int8* indices)
{
Vertex *v1, *v2, *v3;
bool clipped = indices[0] == indices[1];
if (clipped) {
v1 = gVertices + start;
v2 = v1 + 1;
v3 = v1 + 2;
} else {
v1 = gVertices + start;
v2 = v1 + indices[1];
v3 = v1 + indices[2];
}
uint16 palIndex = flags & FACE_TEXTURE;
if (!(flags & FACE_COLORED)) {
const Texture &tex = textures[palIndex];
palIndex = 0xFFFF;
curTile = tiles[tex.tile];
if (!clipped) {
v1->u = tex.x0;
v1->v = tex.y0;
v2->u = tex.x1;
v2->v = tex.y1;
v3->u = tex.x2;
v3->v = tex.y2;
}
}
sortVertices(v1, v2, v3);
int32 temp = (v2->y - v1->y) * FixedInvU(v3->y - v1->y);
int32 longest = ((temp * (v3->x - v1->x)) >> 16) + (v1->x - v2->x);
if (longest == 0)
{
return;
}
Edge L, R;
if (longest < 0)
{
R.vert[0] = v3;
R.vert[1] = v2;
R.vert[2] = v1;
R.index = 2;
L.vert[0] = v3;
L.vert[1] = v1;
L.index = 1;
}
else
{
L.vert[0] = v3;
L.vert[1] = v2;
L.vert[2] = v1;
L.index = 2;
R.vert[0] = v3;
R.vert[1] = v1;
R.index = 1;
}
if (palIndex != 0xFFFF) {
rasterizeG((uint16*)fb + v1->y * (WIDTH / PIXEL_SIZE), palIndex, L, R);
} else {
rasterizeGT((uint16*)fb + v1->y * (WIDTH / PIXEL_SIZE), L, R);
}
}
void drawQuad(uint16 flags, int32 start, const int8* indices) {
Vertex *v1, *v2, *v3, *v4;
bool clipped = indices[0] == indices[1];
if (clipped) {
v1 = gVertices + start;
v2 = v1 + 1;
v3 = v1 + 2;
v4 = v1 + 3;
} else {
v1 = gVertices + start;
v2 = v1 + indices[1];
v3 = v1 + indices[2];
v4 = v1 + indices[3];
}
uint16 palIndex = flags & FACE_TEXTURE;
if (!(flags & FACE_COLORED)) {
const Texture &tex = textures[palIndex];
palIndex = 0xFFFF;
curTile = tiles[tex.tile];
if (!clipped) {
v1->u = int32(tex.x0);
v1->v = int32(tex.y0);
v2->u = int32(tex.x1);
v2->v = int32(tex.y1);
v3->u = int32(tex.x2);
v3->v = int32(tex.y2);
v4->u = int32(tex.x3);
v4->v = int32(tex.y3);
}
}
sortVertices(v1, v2, v3, v4);
int32 temp = (v2->y - v1->y) * FixedInvU(v4->y - v1->y);
int32 longest = ((temp * (v4->x - v1->x)) >> 16) + (v1->x - v2->x);
if (longest == 0)
{
return;
}
Edge L, R;
if (checkBackface(v1, v4, v2) == checkBackface(v1, v4, v3))
{
if (longest < 0)
{
L.vert[0] = v4;
L.vert[1] = v1;
L.index = 1;
R.vert[0] = v4;
R.vert[1] = v3;
R.vert[2] = v2;
R.vert[3] = v1;
R.index = 3;
}
else
{
R.vert[0] = v4;
R.vert[1] = v1;
R.index = 1;
L.vert[0] = v4;
L.vert[1] = v3;
L.vert[2] = v2;
L.vert[3] = v1;
L.index = 3;
}
}
else
{
R.vert[0] = v4;
R.vert[1] = v3;
R.vert[2] = v1;
R.index = 2;
L.vert[0] = v4;
L.vert[1] = v2;
L.vert[2] = v1;
L.index = 2;
if (longest < 0)
{
swap(L.vert[1], R.vert[1]);
}
}
if (palIndex != 0xFFFF) {
rasterizeG((uint16*)fb + v1->y * (WIDTH / PIXEL_SIZE), palIndex, L, R);
} else {
rasterizeGT((uint16*)fb + v1->y * (WIDTH / PIXEL_SIZE), L, R);
}
}
void drawPoly(uint16 flags, int32 start, int32 count) {
uint16 palIndex = flags & FACE_TEXTURE;
if (!(flags & FACE_COLORED)) {
const Texture &tex = textures[palIndex];
palIndex = 0xFFFF;
curTile = tiles[tex.tile];
}
int32 minY = 0x7FFF;
int32 maxY = -0x7FFF;
int32 t = start, b = start;
for (int i = 0; i < count; i++) {
Vertex *v = gVertices + start + i;
if (v->y < minY) {
minY = v->y;
t = i;
}
if (v->y > maxY) {
maxY = v->y;
b = i;
}
}
Edge L, R;
L.vert[L.index = 0] = gVertices + start + b;
R.vert[R.index = 0] = gVertices + start + b;
for (int i = 0; i < count; i++) {
int32 idx = (b + count + i) % count;
L.vert[++L.index] = gVertices + start + idx;
if (idx == t) {
break;
}
}
for (int i = 0; i < count; i++) {
int32 idx = (b + count - i) % count;
R.vert[++R.index] = gVertices + start + idx;
if (idx == t) {
break;
}
}
Vertex *v1 = gVertices + start + t;
if (palIndex != 0xFFFF) {
rasterizeG((uint16*)fb + v1->y * (WIDTH / PIXEL_SIZE), palIndex, L, R);
} else {
rasterizeGT((uint16*)fb + v1->y * (WIDTH / PIXEL_SIZE), L, R);
}
}
void drawGlyph(int32 index, int32 x, int32 y) {
const Sprite* sprite = sprites + spriteSequences[seqGlyphs].sStart + index;
int32 w = sprite->r - sprite->l;
int32 h = sprite->b - sprite->t;
w = (w >> 1) << 1; // make it even
int32 ix = x + sprite->l;
int32 iy = y + sprite->t;
uint16* ptr = (uint16*)fb + iy * (WIDTH / PIXEL_SIZE) + (ix >> 1);
const uint8* glyphData = tiles[sprite->tile] + 256 * sprite->v + sprite->u;
while (h--)
{
const uint8* p = glyphData;
for (int i = 0; i < (w / 2); i++) {
if (p[0] || p[1]) {
uint16 d = ptr[i];
if (p[0]) d = (d & 0xFF00) | p[0];
if (p[1]) d = (d & 0x00FF) | (p[1] << 8);
ptr[i] = d;
}
p += 2;
}
ptr += WIDTH / PIXEL_SIZE;
glyphData += 256;
}
}
void faceAddPolyClip(uint16 flags, Vertex** poly, int32 pCount)
{
#define LERP(a,b,t) (b + ((a - b) * t >> 16))
#define CLIP_AXIS(x, y, edge, output) {\
uint32 t = MyDiv((edge - b->x) << 16, a->x - b->x);\
Vertex* v = output + count++;\
v->x = edge;\
v->y = LERP(a->y, b->y, t);\
v->z = LERP(a->z, b->z, t);\
v->u = LERP(a->u, b->u, t);\
v->v = LERP(a->v, b->v, t);\
v->g = LERP(a->g, b->g, t);\
}
#define CLIP_VERTEX(x, y, x0, x1, input, output) {\
const Vertex *a, *b = input[pCount - 1];\
for (int32 i = 0; i < pCount; i++) {\
a = b;\
b = input[i];\
if (a->x < x0) {\
if (b->x < x0) continue;\
CLIP_AXIS(x, y, x0, output);\
} else if (a->x > x1) {\
if (b->x > x1) continue;\
CLIP_AXIS(x, y, x1, output);\
}\
if (b->x < x0) {\
CLIP_AXIS(x, y, x0, output);\
} else if (b->x > x1) {\
CLIP_AXIS(x, y, x1, output);\
} else {\
output[count++] = *b;\
}\
}\
if (count < 3) return;\
}
if (!(flags & FACE_COLORED)) {
const Texture &tex = textures[flags & FACE_TEXTURE];
curTile = tiles[tex.tile];
poly[0]->u = int32(tex.x0);
poly[0]->v = int32(tex.y0);
poly[1]->u = int32(tex.x1);
poly[1]->v = int32(tex.y1);
poly[2]->u = int32(tex.x2);
poly[2]->v = int32(tex.y2);
if (pCount == 4) {
poly[3]->u = int32(tex.x3);
poly[3]->v = int32(tex.y3);
}
}
Vertex tmp[8];
int32 count = 0;
// clip x
int32 x0 = clip.x0;
int32 x1 = clip.x1;
CLIP_VERTEX(x, y, x0, x1, poly, tmp);
pCount = count;
count = 0;
Vertex* output = gVertices + gVerticesCount;
// clip y
int32 y0 = clip.y0;
int32 y1 = clip.y1;
CLIP_VERTEX(y, x, y0, y1, &tmp, output);
Face &f = gFaces[gFacesCount++];
f.flags = flags;
f.start = gVerticesCount;
f.indices[0] = count;
f.indices[1] = count;
/*
if (count == 3) {
f.flags |= FACE_TRIANGLE;
f.depth = (output[0].z + output[1].z + output[2].z) / 3;
} else if (count == 4) {
f.depth = (output[0].z + output[1].z + output[2].z + output[3].z) >> 2;
} else*/ {
int32 depth = output[0].z;
for (int32 i = 1; i < count; i++) {
depth = (depth + output[i].z) >> 1;
}
f.depth = depth;
}
gVerticesCount += count;
}
void faceAddQuad(uint16 flags, const Index* indices, int32 startVertex) {
#ifdef _WIN32
if (gFacesCount >= MAX_FACES) {
DebugBreak();
}
#endif
Vertex* v1 = gVertices + startVertex + indices[0];
Vertex* v2 = gVertices + startVertex + indices[1];
Vertex* v3 = gVertices + startVertex + indices[2];
Vertex* v4 = gVertices + startVertex + indices[3];
if (v1->z < 0 || v2->z < 0 || v3->z < 0 || v4->z < 0)
return;
if (checkBackface(v1, v2, v3))
return;
if (v1->clip & v2->clip & v3->clip & v4->clip)
return;
if (v1->clip | v2->clip | v3->clip | v4->clip) {
Vertex* poly[4] = { v1, v2, v3, v4 };
faceAddPolyClip(flags, poly, 4);
} else {
Face &f = gFaces[gFacesCount++];
f.flags = flags;
f.depth = (v1->z + v2->z + v3->z + v4->z) >> 2;
f.start = startVertex + indices[0];
f.indices[0] = 0;
f.indices[1] = indices[1] - indices[0];
f.indices[2] = indices[2] - indices[0];
f.indices[3] = indices[3] - indices[0];
}
}
void faceAddTriangle(uint16 flags, const Index* indices, int32 startVertex) {
#ifdef _WIN32
if (gFacesCount >= MAX_FACES) {
DebugBreak();
}
#endif
Vertex* v1 = gVertices + startVertex + indices[0];
Vertex* v2 = gVertices + startVertex + indices[1];
Vertex* v3 = gVertices + startVertex + indices[2];
if (v1->z < 0 || v2->z < 0 || v3->z < 0)
return;
if (checkBackface(v1, v2, v3))
return;
if (v1->clip & v2->clip & v3->clip)
return;
if (v1->clip | v2->clip | v3->clip) {
Vertex* poly[3] = { v1, v2, v3 };
faceAddPolyClip(flags, poly, 3);
} else {
Face &f = gFaces[gFacesCount++];
f.flags = flags | FACE_TRIANGLE;
f.depth = (v1->z + v2->z + v3->z) / 3;
f.start = startVertex + indices[0];
f.indices[0] = 0;
f.indices[1] = indices[1] - indices[0];
f.indices[2] = indices[2] - indices[0];
}
}
int faceCmp(const void *a, const void *b) {
return ((Face*)b)->depth - ((Face*)a)->depth;
}
//int32 gFacesCountMax, gVerticesCountMax;
void flush() {
if (gFacesCount) {
qsort(gFaces, gFacesCount, sizeof(Face), faceCmp);
//const uint16 mips[] = { 0xFFFF, 0xFEFE, 0xFCFC, 0xF8F8 };
for (int32 i = 0; i < gFacesCount; i++) {
const Face &f = gFaces[i];
// TODO
//mipMask = mips[MIN(3, f.depth / 2048)];
if (f.flags & FACE_TRIANGLE) {
drawTriangle(f.flags, f.start, f.indices);
} else {
if (f.indices[0] == f.indices[1] /* && f.indices[0] > 4 */) {
drawPoly(f.flags, f.start, f.indices[0]);
} else {
drawQuad(f.flags, f.start, f.indices);
}
}
}
}
//if (gFacesCount > gFacesCountMax) gFacesCountMax = gFacesCount;
//if (gVerticesCount > gVerticesCountMax) gVerticesCountMax = gVerticesCount;
//printf("%d %d\n", gFacesCountMax, gVerticesCountMax);
gVerticesCount = 0;
gFacesCount = 0;
}
void initRender() {
divTable[0] = 0;
for (uint32 i = 1; i < DIV_TABLE_SIZE; i++) {
divTable[i] = MyDiv(1 << 16, i);
}
}
void clear() {
uint32* dst = (uint32*)fb;
#ifdef USE_MODE_5
uint32* end = dst + (WIDTH * HEIGHT >> 1);
#else
uint32* end = dst + (WIDTH * HEIGHT >> 2);
#endif
while (dst < end) {
*dst++ = 0;
*dst++ = 0;
*dst++ = 0;
*dst++ = 0;
*dst++ = 0;
*dst++ = 0;
*dst++ = 0;
*dst++ = 0;
*dst++ = 0;
*dst++ = 0;
*dst++ = 0;
*dst++ = 0;
*dst++ = 0;
*dst++ = 0;
*dst++ = 0;
*dst++ = 0;
}
}

BIN
src/platform/gba/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 546 B

595
src/platform/gba/main.cpp Normal file
View File

@@ -0,0 +1,595 @@
#ifndef _WIN32
#include <gba_console.h>
#include <gba_video.h>
#include <gba_timers.h>
#include <gba_interrupt.h>
#include <gba_systemcalls.h>
#include <gba_input.h>
#include <gba_dma.h>
#include <gba_affine.h>
#include <fade.h>
#include "LEVEL1_PHD.h"
#endif
#include "common.h"
//#define PROFILE
#if defined(USE_MODE_5) || defined(_WIN32)
extern uint16 palette[256];
#endif
extern uint8 lightmap[256 * 32];
extern Vertex gVertices[MAX_VERTICES];
extern uint32 gVerticesCount;
extern Face gFaces[MAX_FACES];
extern int32 gFacesCount;
extern Rect clip;
extern const uint8* tiles[15];
extern const uint8* curTile;
uint32 tilesCount;
extern int32 fps;
uint16 camRotY = 16 << 8;
extern int32 camSinY;
extern int32 camCosY;
extern int32 camX;
extern int32 camY;
extern int32 camZ;
extern uint16 roomsCount;
extern const Room* rooms;
extern uint32 texturesCount;
extern const Texture* textures;
extern const Sprite* sprites;
extern uint32 spriteSequencesCount;
extern const SpriteSequence* spriteSequences;
extern int32 seqGlyphs;
extern const uint8* meshData;
extern const uint32* meshOffsets;
extern const int32* nodes;
extern const Model* models;
extern void transform(int32 vx, int32 vy, int32 vz, int32 vg, int32 x, int32 y, int32 z);
extern void faceAddTriangle(uint16 flags, const Index* indices, int32 startVertex);
extern void faceAddQuad(uint16 flags, const Index* indices, int32 startVertex);
extern void flush();
extern void initRender();
void drawRoom(int16 roomIndex) {
const Room *room = rooms;
//Room::Portal *portals;
uint16 portalsCount;
//Room::Sector* sectors;
uint16 zSectors, xSectors;
//uint16 ambient;
//Room::Light* lights;
uint16 lightsCount;
//Room::Mesh* meshes;
uint16 meshesCount;
uint8 *ptr = (uint8*)room;
while (roomIndex--) {
uint32 dataSize;
memcpy(&dataSize, &room->dataSize, sizeof(dataSize));
ptr += sizeof(Room) + dataSize * 2;
portalsCount = *((uint16*)ptr);
ptr += 2;
//portals = (Room::Portal*)ptr;
ptr += sizeof(Room::Portal) * portalsCount;
zSectors = *((uint16*)ptr);
ptr += 2;
xSectors = *((uint16*)ptr);
ptr += 2;
//sectors = (Room::Sector*)sectors;
ptr += sizeof(Room::Sector) * zSectors * xSectors;
//ambient = *((uint16*)ptr);
ptr += 2;
lightsCount = *((uint16*)ptr);
ptr += 2;
//lights = (Room::Light*)ptr;
ptr += sizeof(Room::Light) * lightsCount;
meshesCount = *((uint16*)ptr);
ptr += 2;
//meshes = (Room::Mesh*)ptr;
ptr += sizeof(Room::Mesh) * meshesCount;
ptr += 2 + 2; // skip alternateRoom and flags
room = (Room*)ptr;
}
ptr += sizeof(Room);
uint16 vCount = *((uint16*)ptr);
ptr += 2;
Room::Vertex* vertices = (Room::Vertex*)ptr;
ptr += sizeof(Room::Vertex) * vCount;
// non-aligned data
int32 roomX;
int32 roomZ;
memcpy(&roomX, &room->info.x, sizeof(roomX));
memcpy(&roomZ, &room->info.z, sizeof(roomZ));
int32 dx = -camX + roomX;
int32 dy = -camY;
int32 dz = -camZ + roomZ;
int32 startVertex = gVerticesCount;
for (uint16 i = 0; i < vCount; i++) {
const Room::Vertex &v = vertices[i];
transform(v.x, v.y, v.z, v.lighting, dx, dy, dz);
}
uint16 qCount = *((uint16*)ptr);
ptr += 2;
Quad* quads = (Quad*)ptr;
ptr += sizeof(Quad) * qCount;
for (uint16 i = 0; i < qCount; i++) {
faceAddQuad(quads[i].flags, quads[i].indices, startVertex);
}
uint16 tCount = *((uint16*)ptr);
ptr += 2;
Triangle* triangles = (Triangle*)ptr;
ptr += sizeof(Triangle) * tCount;
for (uint16 i = 0; i < tCount; i++) {
faceAddTriangle(triangles[i].flags, triangles[i].indices, startVertex);
}
}
void drawMesh(int16 meshIndex, int32 x, int32 y, int32 z) {
uint32 offset = meshOffsets[meshIndex];
const uint8* ptr = meshData + offset;
//int16 cx = *(int16*)ptr; ptr += 2;
//int16 cy = *(int16*)ptr; ptr += 2;
//int16 cz = *(int16*)ptr; ptr += 2;
//int16 r = *(int16*)ptr; ptr += 2;
//ptr += 2; // skip flags
ptr += 2 * 5;
int16 vCount = *(int16*)ptr; ptr += 2;
const int16* vertices = (int16*)ptr;
ptr += vCount * 3 * sizeof(int16);
int16 nCount = *(int16*)ptr; ptr += 2;
//const int16* normals = (int16*)ptr;
if (nCount > 0) { // normals
ptr += nCount * 3 * sizeof(int16);
} else { // intensity
ptr += vCount * sizeof(int16);
}
int16 rCount = *(int16*)ptr; ptr += 2;
Quad* rFaces = (Quad*)ptr; ptr += rCount * sizeof(Quad);
int16 tCount = *(int16*)ptr; ptr += 2;
Triangle* tFaces = (Triangle*)ptr; ptr += tCount * sizeof(Triangle);
int16 crCount = *(int16*)ptr; ptr += 2;
Quad* crFaces = (Quad*)ptr; ptr += crCount * sizeof(Quad);
int16 ctCount = *(int16*)ptr; ptr += 2;
Triangle* ctFaces = (Triangle*)ptr; ptr += ctCount * sizeof(Triangle);
int32 startVertex = gVerticesCount;
int32 dx = x - camX;
int32 dy = y - camY;
int32 dz = z - camZ;
const int16* v = vertices;
for (uint16 i = 0; i < vCount; i++) {
transform(v[0], v[1], v[2], 4096, dx, dy, dz);
v += 3;
}
for (int i = 0; i < rCount; i++) {
faceAddQuad(rFaces[i].flags, rFaces[i].indices, startVertex);
}
for (int i = 0; i < crCount; i++) {
faceAddQuad(crFaces[i].flags | FACE_COLORED, crFaces[i].indices, startVertex);
}
for (int i = 0; i < tCount; i++) {
faceAddTriangle(tFaces[i].flags, tFaces[i].indices, startVertex);
}
for (int i = 0; i < ctCount; i++) {
faceAddTriangle(ctFaces[i].flags | FACE_COLORED, ctFaces[i].indices, startVertex);
}
}
void drawModel(int32 modelIndex, int32 x, int32 y, int32 z) {
const Model* model = models + modelIndex;
// non-aligned access
uint32 node, frame;
memcpy(&node, &model->node, sizeof(node));
memcpy(&frame, &model->frame, sizeof(frame));
Node bones[32];
memcpy(bones, nodes + node, (model->mCount - 1) * sizeof(Node));
const Node* n = bones;
struct StackItem {
int32 x, y, z;
} stack[4];
StackItem *s = stack;
drawMesh(model->mStart, x, y, z);
for (int i = 1; i < model->mCount; i++) {
if (n->flags & 1) {
s--;
x = s->x;
y = s->y;
z = s->z;
}
if (n->flags & 2) {
s->x = x;
s->y = y;
s->z = z;
s++;
}
x += n->x;
y += n->y;
z += n->z;
n++;
drawMesh(model->mStart + i, x, y, z);
}
}
void readLevel(const uint8 *data) {
tilesCount = *((uint32*)(data + 4));
for (uint32 i = 0; i < tilesCount; i++) {
tiles[i] = data + 8 + 256 * 256 * i;
}
roomsCount = *((uint16*)(data + 720908));
rooms = (Room*)(data + 720908 + 2);
texturesCount = *((uint32*)(data + 1271686));
textures = (Texture*)(data + 1271686 + 4);
sprites = (Sprite*)(data + 1289634);
spriteSequencesCount = *((uint32*)(data + 1292130));
spriteSequences = (SpriteSequence*)(data + 1292130 + 4);
for (uint32 i = 0; i < spriteSequencesCount; i++) {
if (spriteSequences[i].type == 190) {
seqGlyphs = i;
break;
}
}
meshData = data + 908172 + 4;
meshOffsets = (uint32*)(data + 975724 + 4);
nodes = (int32*)(data + 990318);
models = (Model*)(data + 1270670);
const uint8* f_lightmap = data + 1320576;
memcpy(lightmap, f_lightmap, sizeof(lightmap));
#if !(defined(USE_MODE_5) || defined(_WIN32))
uint16 palette[256];
#endif
const uint8* f_palette = data + 1328768;
const uint8* p = f_palette;
for (int i = 0; i < 256; i++) {
palette[i] = (p[0] >> 1) | ((p[1] >> 1) << 5) | ((p[2] >> 1) << 10);
p += 3;
}
#ifndef _WIN32
palette[1] = RGB8(0, 255, 0);
#ifndef USE_MODE_5
SetPalette(palette);
#endif
#endif
}
#ifdef _WIN32
#define CAM_SPEED (1 << 3)
#define CAM_ROT_SPEED (1 << 3)
#else
#define CAM_SPEED (1 << 6)
#define CAM_ROT_SPEED (1 << 8)
#endif
void updateCamera() {
if (keys[IK_LEFT]) camRotY -= CAM_ROT_SPEED;
if (keys[IK_RIGHT]) camRotY += CAM_ROT_SPEED;
{
ALIGN4 ObjAffineSource src;
ALIGN4 ObjAffineDest dst;
src.sX = 0x0100;
src.sY = 0x0100;
src.theta = camRotY;
ObjAffineSet(&src, &dst, 1, 2);
camCosY = dst.pd << 8;
camSinY = dst.pc << 8;
}
int32 dx = camSinY;
int32 dz = camCosY;
dx *= CAM_SPEED;
dz *= CAM_SPEED;
dx >>= 16;
dz >>= 16;
if (keys[IK_UP]) {
camX += int32(dx);
camZ += int32(dz);
}
if (keys[IK_DOWN]) {
camX -= int32(dx);
camZ -= int32(dz);
}
if (keys[IK_L]) {
camX -= int32(dz);
camZ += int32(dx);
}
if (keys[IK_R]) {
camX += int32(dz);
camZ -= int32(dx);
}
if (keys[IK_A]) camY -= CAM_SPEED;
if (keys[IK_B]) camY += CAM_SPEED;
clip = { 0, 0, FRAME_WIDTH, FRAME_HEIGHT };
}
void drawNumber(int32 number, int32 x, int32 y) {
const int32 widths[] = { 12, 8, 10, 10, 10, 10, 10, 10, 10, 10 };
while (number > 0) {
x -= widths[number % 10];
drawGlyph(52 + (number % 10), x, y);
number /= 10;
}
}
void update(int32 frames) {
for (int32 i = 0; i < frames; i++) {
updateCamera();
}
}
void render() {
clear();
drawRoom(6);
flush();
drawRoom(0);
drawModel(0, 75162, 3072 - 512, 5000 + 1024);
flush();
drawNumber(fps, WIDTH, 16);
}
#ifdef _WIN32
HDC hDC;
void VBlankIntrWait() {
#ifdef USE_MODE_5
for (int i = 0; i < WIDTH * HEIGHT; i++) {
uint16 c = fb[i];
VRAM[i] = (((c << 3) & 0xFF) << 16) | ((((c >> 5) << 3) & 0xFF) << 8) | ((c >> 10 << 3) & 0xFF) | 0xFF000000;
}
#else
for (int i = 0; i < WIDTH * HEIGHT; i++) {
uint16 c = palette[fb[i]];
VRAM[i] = (((c << 3) & 0xFF) << 16) | ((((c >> 5) << 3) & 0xFF) << 8) | ((c >> 10 << 3) & 0xFF) | 0xFF000000;
}
#endif
const BITMAPINFO bmi = { sizeof(BITMAPINFOHEADER), WIDTH, -HEIGHT, 1, 32, BI_RGB, 0, 0, 0, 0, 0 };
StretchDIBits(hDC, 0, 0, 240 * WND_SCALE, 160 * WND_SCALE, 0, 0, WIDTH, HEIGHT, VRAM, &bmi, DIB_RGB_COLORS, SRCCOPY);
}
LRESULT CALLBACK wndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_DESTROY :
PostQuitMessage(0);
break;
case WM_KEYDOWN :
case WM_KEYUP : {
InputKey key = IK_MAX;
switch (wParam) {
case VK_UP : key = IK_UP; break;
case VK_RIGHT : key = IK_RIGHT; break;
case VK_DOWN : key = IK_DOWN; break;
case VK_LEFT : key = IK_LEFT; break;
case 'Z' : key = IK_A; break;
case 'X' : key = IK_B; break;
case 'A' : key = IK_L; break;
case 'S' : key = IK_R; break;
}
if (key != IK_MAX) {
keys[key] = msg != WM_KEYUP;
}
break;
}
default :
return DefWindowProc(hWnd, msg, wParam, lParam);
}
return 0;
}
#endif
int32 frameIndex = 0;
int32 fpsCounter = 0;
void vblank() {
frameIndex++;
}
int main(void) {
#ifdef _WIN32
{
FILE *f = fopen("C:/Projects/TR/TR1_ANDROID/LEVEL1.PHD", "rb");
fseek(f, 0, SEEK_END);
int32 size = ftell(f);
fseek(f, 0, SEEK_SET);
LEVEL1_PHD = new uint8[size];
fread(LEVEL1_PHD, 1, size, f);
fclose(f);
}
#else
// set low latency mode via WAITCNT register (thanks to GValiente)
#define BIT_SET(y, flag) (y |= (flag))
#define REG_WAITCNT_NV *(u16*)(REG_BASE + 0x0204)
BIT_SET(REG_WAITCNT_NV, 0x0008 | 0x0010 | 0x4000);
#endif
initRender();
readLevel(LEVEL1_PHD);
#ifdef _WIN32
RECT r = { 0, 0, 240 * WND_SCALE, 160 * WND_SCALE };
AdjustWindowRect(&r, WS_OVERLAPPEDWINDOW, false);
int wx = (GetSystemMetrics(SM_CXSCREEN) - (r.right - r.left)) / 2;
int wy = (GetSystemMetrics(SM_CYSCREEN) - (r.bottom - r.top)) / 2;
HWND hWnd = CreateWindow("static", "OpenLara GBA", WS_OVERLAPPEDWINDOW, wx + r.left, wy + r.top, r.right - r.left, r.bottom - r.top, 0, 0, 0, 0);
hDC = GetDC(hWnd);
SetWindowLong(hWnd, GWL_WNDPROC, (LONG)&wndProc);
ShowWindow(hWnd, SW_SHOWDEFAULT);
MSG msg;
do {
if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
} else {
update(1);
render();
VBlankIntrWait();
}
} while (msg.message != WM_QUIT);
#else
irqInit();
irqSet(IRQ_VBLANK, vblank);
irqEnable(IRQ_VBLANK);
uint16 mode = BG2_ON | BACKBUFFER;
#ifdef USE_MODE_5
mode |= MODE_5;
REG_BG2PA = 256 - 64 - 16 - 4 - 1;
REG_BG2PD = 256 - 48 - 2;
#else
mode |= MODE_4;
#endif
int32 lastFrameIndex = -1;
#ifdef PROFILE
int counter = 0;
#endif
while (1) {
//VBlankIntrWait();
#ifdef PROFILE
if (counter++ >= 10) return 0;
#endif
SetMode(mode ^= BACKBUFFER);
fb ^= 0xA000;
scanKeys();
uint16 key = keysDown() | keysHeld();
keys[IK_UP] = (key & KEY_UP);
keys[IK_RIGHT] = (key & KEY_RIGHT);
keys[IK_DOWN] = (key & KEY_DOWN);
keys[IK_LEFT] = (key & KEY_LEFT);
keys[IK_A] = (key & KEY_A);
keys[IK_B] = (key & KEY_B);
keys[IK_L] = (key & KEY_L);
keys[IK_R] = (key & KEY_R);
int32 frame = frameIndex;
update(frame - lastFrameIndex);
lastFrameIndex = frame;
render();
fpsCounter++;
if (frameIndex >= 60) {
frameIndex -= 60;
lastFrameIndex -= 60;
fps = fpsCounter;
fpsCounter = 0;
}
}
#endif
}

View File

@@ -0,0 +1 @@
NO$GBA C:\Projects\OpenLara\src\platform\gba\OpenLara.elf

51
src/platform/gba/sdiv32.s Normal file
View File

@@ -0,0 +1,51 @@
@--------------------------------------------------------------------------------
@ udiv.s
@--------------------------------------------------------------------------------
@ Provides an implementation of signed division
@--------------------------------------------------------------------------------
@ refer to the unsigned division
.extern __aeabi_uidivmod
.extern __aeabi_uidiv
@ r0: the numerator / r1: the denominator
@ after it, r0 has the quotient and r1 has the modulo
.section .iwram, "ax", %progbits
.align 2
.arm
.global __aeabi_idivmod
.type __aeabi_idivmod STT_FUNC
__aeabi_idivmod:
.section .iwram, "ax", %progbits
.align 2
.arm
.global __aeabi_idiv
.type __aeabi_idiv STT_FUNC
__aeabi_idiv:
@ Move the lr to r12 and make the numbers positive
mov r12, lr
cmp r0, #0
rsblt r0, #0
orrlt r12, #1 << 30
cmp r1, #0
rsblt r1, #0
orrlt r12, #1 << 31
@ Call the unsigned division
.extern udiv32pastzero
bl udiv32pastzero
@ Test the old sign bits
tst r12, #1 << 30
rsbne r0, r0, #0
rsbne r1, r0, #0
tst r12, #1 << 31
rsbne r0, r0, #0
@ Erase the sign bits from the return address, and return
bic r12, #3 << 30
bx r12

79
src/platform/gba/udiv32.s Normal file
View File

@@ -0,0 +1,79 @@
@--------------------------------------------------------------------------------
@ udiv.s
@--------------------------------------------------------------------------------
@ Provides an implementation of unsigned division
@--------------------------------------------------------------------------------
@ Source code taken from https://www.chiark.greenend.org.uk/~theom/riscos/docs/ultimate/a252div.txt
@ r0: the numerator / r1: the denominator
@ after it, r0 has the quotient and r1 has the modulo
.section .iwram, "ax", %progbits
.align 2
.arm
.global __aeabi_uidivmod
.type __aeabi_uidivmod STT_FUNC
__aeabi_uidivmod:
.section .iwram, "ax", %progbits
.align 2
.arm
.global __aeabi_uidiv
.type __aeabi_uidiv STT_FUNC
__aeabi_uidiv:
@ Check for division by zero
cmp r1, #0
bxeq lr
.global udiv32pastzero
udiv32pastzero:
@ If n < d, just bail out as well
cmp r0, r1 @ n, d
movlo r1, r0 @ mod = n
movlo r0, #0 @ quot = 0
bxlo lr
@ Move the denominator to r2 and start to build a counter that
@ counts the difference on the number of bits on each numerator
@ and denominator
@ From now on: r0 = quot/num, r1 = mod, r2 = denom, r3 = counter
mov r2, r1
mov r3, #28 @ first guess on difference
mov r1, r0, lsr #4 @ r1 = num >> 4
@ Iterate three times to get the counter up to 4-bit precision
cmp r2, r1, lsr #12
suble r3, r3, #16
movle r1, r1, lsr #16
cmp r2, r1, lsr #4
suble r3, r3, #8
movle r1, r1, lsr #8
cmp r2, r1
suble r3, r3, #4
movle r1, r1, lsr #4
@ shift the numerator by the counter and flip the sign of the denom
mov r0, r0, lsl r3
adds r0, r0, r0
rsb r2, r2, #0
@ dynamically jump to the exact copy of the iteration
add r3, r3, r3, lsl #1 @ counter *= 3
add pc, pc, r3, lsl #2 @ jump
mov r0, r0 @ pipelining issues
@ here, r0 = num << (r3 + 1), r1 = num >> (32-r3), r2 = -denom
@ now, the real iteration part
.global divIteration
divIteration:
.rept 32
adcs r1, r2, r1, lsl #1
sublo r1, r1, r2
adcs r0, r0, r0
.endr
@ and then finally quit
@ r0 = quotient, r1 = remainder
bx lr