Merge branch 'lambertjamesd:master' into docker-2
This commit is contained in:
commit
3ad1c815de
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -24,3 +24,6 @@ tools/vtf2png
|
|||
skelatool64/*.zip*
|
||||
|
||||
src/controls/controller-data.h
|
||||
|
||||
|
||||
log.txt
|
12
Dockerfile
12
Dockerfile
|
@ -7,14 +7,16 @@ ENV PATH /opt/crashsdk/bin:$PATH
|
|||
ENV PATH /root/.local/bin:$PATH
|
||||
ENV ROOT /etc/n64
|
||||
|
||||
RUN apt update -y && \
|
||||
DEBIAN_FRONTEND=noninteractive apt install -y \
|
||||
RUN apt-get update -y
|
||||
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y \
|
||||
apt-utils 2>/dev/null
|
||||
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y \
|
||||
ca-certificates
|
||||
RUN echo "deb [trusted=yes] https://lambertjamesd.github.io/apt/ ./" | tee /etc/apt/sources.list.d/lambertjamesd.list && \
|
||||
echo "deb [trusted=yes] https://crashoveride95.github.io/apt/ ./" | tee /etc/apt/sources.list.d/n64sdk.list
|
||||
RUN apt update -y && \
|
||||
RUN apt-get update -y && \
|
||||
dpkg --add-architecture i386 && \
|
||||
DEBIAN_FRONTEND=noninteractive apt install -y \
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y \
|
||||
binutils-mips-n64 \
|
||||
gcc-mips-n64 \
|
||||
n64sdk \
|
||||
|
@ -58,5 +60,5 @@ RUN rm /opt/blender/blender-3.6.1-linux-x64.tar.xz
|
|||
|
||||
ENV BLENDER_3_6 /opt/blender/blender-3.6.1-linux-x64/blender
|
||||
|
||||
RUN pipx ensurepath
|
||||
RUN pipx ensurepath --force
|
||||
RUN pipx install vpk
|
||||
|
|
8
Makefile
8
Makefile
|
@ -8,14 +8,13 @@
|
|||
# --------------------------------------------------------------------
|
||||
include /usr/include/n64/make/PRdefs
|
||||
|
||||
|
||||
SKELATOOL64:=skelatool64/skeletool64
|
||||
VTF2PNG:=vtf2png
|
||||
SFZ2N64:=sfz2n64
|
||||
|
||||
$(SKELATOOL64):
|
||||
skelatool64/setup_dependencies.sh
|
||||
make -C skelatool64
|
||||
@$(MAKE) -C skelatool64
|
||||
|
||||
OPTIMIZER := -Os
|
||||
LCDEFS := -DDEBUG -g -Isrc/ -I/usr/include/n64/nustd -Werror -Wall
|
||||
|
@ -67,6 +66,7 @@ LDFLAGS = -L/usr/lib/n64 $(N64LIB) -L$(N64_LIBGCCDIR) -lgcc
|
|||
default: english_audio
|
||||
|
||||
english_audio: build/src/audio/subtitles.h portal_pak_dir
|
||||
@$(MAKE) -C skelatool64
|
||||
@$(MAKE) buildgame
|
||||
|
||||
all_languages: build/src/audio/subtitles.h portal_pak_dir german_audio french_audio russian_audio spanish_audio
|
||||
|
@ -359,6 +359,7 @@ build/src/scene/security_camera.o: build/src/audio/clips.h build/assets/models/p
|
|||
build/src/scene/signage.o: $(MODEL_HEADERS)
|
||||
build/src/scene/switch.o: build/assets/models/props/switch001.h build/assets/materials/static.h build/assets/models/dynamic_animated_model_list.h
|
||||
build/src/util/dynamic_asset_data.o: build/assets/models/dynamic_model_list_data.h
|
||||
build/src/util/dynamic_animated_asset_data.o: build/assets/models/dynamic_animated_model_list_data.h
|
||||
build/src/util/dynamic_asset_loader.o: build/assets/models/dynamic_model_list.h build/assets/models/dynamic_animated_model_list.h
|
||||
build/src/menu/audio_options.o: build/src/audio/subtitles.h
|
||||
build/src/menu/video_options.o: build/src/audio/subtitles.h
|
||||
|
@ -572,7 +573,8 @@ clean:
|
|||
rm -rf build
|
||||
rm -rf portal_pak_dir
|
||||
rm -rf portal_pak_modified
|
||||
rm -rf assets/locales/
|
||||
rm -rf assets/locales
|
||||
@$(MAKE) -C skelatool64 clean
|
||||
|
||||
clean-src:
|
||||
rm -rf build/src
|
||||
|
|
|
@ -1,38 +1,35 @@
|
|||
all:
|
||||
docker run -v $$PWD:/usr/src/app -it portal64 make
|
||||
docker run --rm -v $$PWD:/usr/src/app -it portal64 make
|
||||
|
||||
debug:
|
||||
docker run -v $$PWD:/usr/src/app -it PORTAL64_WITH_GFX_VALIDATOR=1 portal64 make
|
||||
|
||||
convert_all_png:
|
||||
docker run -v $$PWD:/usr/src/app -it portal64 make convert_all_png
|
||||
docker run --rm -v $$PWD:/usr/src/app -it portal64 make convert_all_png
|
||||
|
||||
image:
|
||||
docker build -t portal64 .
|
||||
|
||||
bash:
|
||||
docker run -v $$PWD:/usr/src/app -it portal64 bash
|
||||
docker run --rm -v $$PWD:/usr/src/app -it portal64 bash
|
||||
|
||||
clean:
|
||||
sudo rm -rf build
|
||||
sudo rm -rf portal_pak_dir
|
||||
sudo rm -rf portal_pak_modified
|
||||
sudo rm -rf assets/locales/
|
||||
sudo make clean
|
||||
|
||||
english_audio:
|
||||
docker run -v $$PWD:/usr/src/app -it portal64 make english_audio
|
||||
docker run --rm -v $$PWD:/usr/src/app -it portal64 make english_audio
|
||||
|
||||
all_languages:
|
||||
docker run -v $$PWD:/usr/src/app -it portal64 make all_languages
|
||||
docker run --rm -v $$PWD:/usr/src/app -it portal64 make all_languages
|
||||
|
||||
german_audio:
|
||||
docker run -v $$PWD:/usr/src/app -it portal64 make german_audio
|
||||
docker run --rm -v $$PWD:/usr/src/app -it portal64 make german_audio
|
||||
|
||||
french_audio:
|
||||
docker run -v $$PWD:/usr/src/app -it portal64 make french_audio
|
||||
docker run --rm -v $$PWD:/usr/src/app -it portal64 make french_audio
|
||||
|
||||
russian_audio:
|
||||
docker run -v $$PWD:/usr/src/app -it portal64 make russian_audio
|
||||
docker run --rm -v $$PWD:/usr/src/app -it portal64 make russian_audio
|
||||
|
||||
spanish_audio:
|
||||
docker run -v $$PWD:/usr/src/app -it portal64 make spanish_audio
|
||||
docker run --rm -v $$PWD:/usr/src/app -it portal64 make spanish_audio
|
||||
|
|
14
README.md
14
README.md
|
@ -68,6 +68,13 @@ pipx install vpk
|
|||
|
||||
<br />
|
||||
|
||||
Clone the Portal64 repo or download the zip.
|
||||
|
||||
```sh
|
||||
sudo apt install git
|
||||
git clone https://github.com/lambertjamesd/portal64.git
|
||||
cd portal64
|
||||
```
|
||||
Setup and install dependencies for `skeletool64`
|
||||
|
||||
```sh
|
||||
|
@ -104,13 +111,6 @@ sudo apt install nodejs
|
|||
```
|
||||
|
||||
<br />
|
||||
Clone the Portal64 repo or download the zip.
|
||||
|
||||
```sh
|
||||
sudo apt install git
|
||||
git clone https://github.com/lambertjamesd/portal64.git
|
||||
cd portal64
|
||||
```
|
||||
|
||||
You then need to add the following files from where Portal is installed to the folder `vpk/` OR create a symbolic link to the `Portal` folder there (see [vpk/add_vpk_here.md](./vpk/add_vpk_here.md) for more details!).
|
||||
You can add multiple languages if desired.
|
||||
|
|
|
@ -14,10 +14,10 @@
|
|||
.word 0x00000000 /* Checksum 2 (OVERWRITTEN BY MAKEMASK)*/
|
||||
.word 0x00000000 /* Unknown */
|
||||
.word 0x00000000 /* Unknown */
|
||||
.ascii " " /* Internal ROM name (Max 20 characters) */
|
||||
.word 0x01000000 /* Advanced_Homebrew_ROM_Header (controller config) */
|
||||
/* Game ID (EXAMPLE: NSME) Begins here */
|
||||
.word 0x0000004E /* Cartridge Type (N)*/
|
||||
.ascii "ED" /* Cartridge ID (SM)*/
|
||||
.ascii " " /* Region (E)*/
|
||||
.byte 0x30 /* Version */
|
||||
.ascii "Portal 64 " /* Internal ROM name (Max 20 characters) */
|
||||
|
||||
.word 0x01000000 /* Unused officially / Advanced homebrew ROM header controller config */
|
||||
.word 0x0000004E /* Cartridge Type (N; cart)*/
|
||||
.ascii "ED" /* Cartridge ID (ED) / Advanced homebrew ROM header magic value */
|
||||
.byte 0x41 /* Region (A; All)*/
|
||||
.byte 0x32 /* Version / Advanced homebrew ROM header misc. (region-free + 256K SRAM) */
|
||||
|
|
|
@ -150,6 +150,10 @@ materials:
|
|||
filename: "../../portal_pak_modified/materials/gamepadui/portal_logo.png"
|
||||
siz: G_IM_SIZ_4b
|
||||
fmt: G_IM_FMT_I
|
||||
s:
|
||||
wrap: false
|
||||
t:
|
||||
wrap: false
|
||||
gDPSetCombineMode:
|
||||
color: ["0", "0", "0", ENVIRONMENT]
|
||||
alpha: [TEXEL0, "0", ENVIRONMENT, "0"]
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -32,7 +32,9 @@ skeletool64: $(OBJ_FILES) $(LUA_OBJ_FILES)
|
|||
g++ -g -o skeletool64 $(OBJ_FILES) $(LUA_OBJ_FILES) $(LINKER_FLAGS)
|
||||
|
||||
clean:
|
||||
rm -r build/
|
||||
rm -rf build/
|
||||
rm -f skeletool64
|
||||
rm -f CImg_*
|
||||
|
||||
init:
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ Bone* Bone::GetParent() {
|
|||
return mParent;
|
||||
}
|
||||
|
||||
std::unique_ptr<DataChunk> Bone::GenerateRestPosiitonData() {
|
||||
std::unique_ptr<DataChunk> Bone::GenerateRestPositionData() {
|
||||
std::unique_ptr<StructureDataChunk> result(new StructureDataChunk());
|
||||
|
||||
result->Add(std::unique_ptr<DataChunk>(new StructureDataChunk(mRestPosition)));
|
||||
|
@ -190,13 +190,14 @@ Bone* BoneHierarchy::BoneForName(std::string name) {
|
|||
}
|
||||
}
|
||||
|
||||
void BoneHierarchy::GenerateRestPosiitonData(CFileDefinition& fileDef, const std::string& variableName) {
|
||||
void BoneHierarchy::GenerateRestPositionData(CFileDefinition& fileDef, const std::string& variableName) {
|
||||
if (mBones.size() == 0) return;
|
||||
|
||||
std::unique_ptr<StructureDataChunk> transformData(new StructureDataChunk());
|
||||
|
||||
for (unsigned int boneIndex = 0; boneIndex < mBones.size(); ++boneIndex) {
|
||||
transformData->Add(std::move(mBones[boneIndex]->GenerateRestPosiitonData()));
|
||||
auto restPositionData = mBones[boneIndex]->GenerateRestPositionData();
|
||||
transformData->Add(std::move(restPositionData));
|
||||
|
||||
std::string boneName = fileDef.GetUniqueName(mBones[boneIndex]->GetName() + "_BONE");
|
||||
std::transform(boneName.begin(), boneName.end(), boneName.begin(), ::toupper);
|
||||
|
|
|
@ -36,7 +36,7 @@ public:
|
|||
const std::string& GetName();
|
||||
Bone* GetParent();
|
||||
|
||||
std::unique_ptr<DataChunk> GenerateRestPosiitonData();
|
||||
std::unique_ptr<DataChunk> GenerateRestPositionData();
|
||||
|
||||
static Bone* FindCommonAncestor(Bone* a, Bone* b);
|
||||
/**
|
||||
|
@ -72,7 +72,7 @@ public:
|
|||
bool HasData() const;
|
||||
unsigned int GetBoneCount() const;
|
||||
|
||||
void GenerateRestPosiitonData(CFileDefinition& fileDef, const std::string& variableName);
|
||||
void GenerateRestPositionData(CFileDefinition& fileDef, const std::string& variableName);
|
||||
private:
|
||||
std::vector<std::unique_ptr<Bone>> mBones;
|
||||
std::map<std::string, Bone*> mBoneByName;
|
||||
|
|
|
@ -212,7 +212,8 @@ DisplayList::DisplayList(std::string name):
|
|||
}
|
||||
|
||||
void DisplayList::AddCommand(std::unique_ptr<DisplayListCommand> command) {
|
||||
mDataChunk->Add(std::move(command->GenerateCommand()));
|
||||
auto generatedCommand = command->GenerateCommand();
|
||||
mDataChunk->Add(std::move(generatedCommand));
|
||||
}
|
||||
|
||||
StructureDataChunk& DisplayList::GetDataChunk() {
|
||||
|
|
|
@ -15,8 +15,8 @@ void useTexture(std::set<std::shared_ptr<TextureDefinition>>& usedTextures, std:
|
|||
}
|
||||
|
||||
usedTextures.insert(texture);
|
||||
|
||||
fileDefinition.AddDefinition(std::move(texture->GenerateDefinition(fileDefinition.GetUniqueName(texture->Name()), fileSuffix)));
|
||||
auto textureDefinition = texture->GenerateDefinition(fileDefinition.GetUniqueName(texture->Name()), fileSuffix);
|
||||
fileDefinition.AddDefinition(std::move(textureDefinition));
|
||||
|
||||
std::shared_ptr<PalleteDefinition> pallete = texture->GetPallete();
|
||||
if (!pallete || usedPalletes.find(pallete) != usedPalletes.end()) {
|
||||
|
@ -24,7 +24,8 @@ void useTexture(std::set<std::shared_ptr<TextureDefinition>>& usedTextures, std:
|
|||
}
|
||||
|
||||
usedPalletes.insert(pallete);
|
||||
fileDefinition.AddDefinition(std::move(pallete->GenerateDefinition(fileDefinition.GetUniqueName(pallete->Name()), fileSuffix)));
|
||||
auto palleteDefinition = pallete->GenerateDefinition(fileDefinition.GetUniqueName(pallete->Name()), fileSuffix);
|
||||
fileDefinition.AddDefinition(std::move(palleteDefinition));
|
||||
}
|
||||
|
||||
void MaterialCollector::UseMaterial(const std::string& material, DisplayListSettings& settings) {
|
||||
|
|
|
@ -266,7 +266,7 @@ AnimationResults generateAnimationForScene(const aiScene* scene, CFileDefinition
|
|||
|
||||
std::string bonesName = fileDefinition.GetUniqueName("default_bones");
|
||||
std::string boneParentName = fileDefinition.GetUniqueName("bone_parent");
|
||||
bones.GenerateRestPosiitonData(fileDefinition, bonesName);
|
||||
bones.GenerateRestPositionData(fileDefinition, bonesName);
|
||||
std::string boneCountName = bonesName + "_COUNT";
|
||||
std::transform(boneCountName.begin(), boneCountName.end(), boneCountName.begin(), ::toupper);
|
||||
fileDefinition.AddMacro(boneCountName, std::to_string(bones.GetBoneCount()));
|
||||
|
|
|
@ -84,7 +84,8 @@ std::shared_ptr<CollisionGeneratorOutput> generateCollision(const aiScene* scene
|
|||
fileDefinition.AddMacro(fileDefinition.GetMacroName(namedEntry + "_COLLISION_INDEX"), std::to_string(output->quads.size()));
|
||||
}
|
||||
|
||||
collidersChunk->Add(std::move(collider.Generate()));
|
||||
auto generatedCollider = collider.Generate();
|
||||
collidersChunk->Add(std::move(generatedCollider));
|
||||
|
||||
std::unique_ptr<StructureDataChunk> colliderType(new StructureDataChunk());
|
||||
colliderType->AddPrimitive<const char*>("CollisionShapeTypeQuad");
|
||||
|
|
|
@ -54,11 +54,13 @@ void MaterialGenerator::GenerateDefinitions(const aiScene* scene, CFileDefinitio
|
|||
}
|
||||
|
||||
for (auto& texture : textures) {
|
||||
fileDefinition.AddDefinition(std::move(texture->GenerateDefinition(fileDefinition.GetUniqueName(texture->Name()), "_mat")));
|
||||
auto textureDefinition = texture->GenerateDefinition(fileDefinition.GetUniqueName(texture->Name()), "_mat");
|
||||
fileDefinition.AddDefinition(std::move(textureDefinition));
|
||||
}
|
||||
|
||||
for (auto& pallete : palletes) {
|
||||
fileDefinition.AddDefinition(std::move(pallete->GenerateDefinition(fileDefinition.GetUniqueName(pallete->Name()), "_mat")));
|
||||
auto palleteDefinition = pallete->GenerateDefinition(fileDefinition.GetUniqueName(pallete->Name()), "_mat");
|
||||
fileDefinition.AddDefinition(std::move(palleteDefinition));
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
|
|
|
@ -20,7 +20,8 @@ std::unique_ptr<DataChunk> buildMacroChunk(lua_State* L) {
|
|||
|
||||
lua_pushnil(L); /* first key */
|
||||
while (lua_next(L, args) != 0) {
|
||||
result->Add(std::move(buildDataChunk(L)));
|
||||
auto dataChunk = buildDataChunk(L);
|
||||
result->Add(std::move(dataChunk));
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
@ -39,7 +40,8 @@ std::unique_ptr<DataChunk> buildStructureChunk(lua_State* L) {
|
|||
while (lua_next(L, topStart) != 0) {
|
||||
int keyType = lua_type(L, -2);
|
||||
if (keyType == LUA_TNUMBER) {
|
||||
result->Add(std::move(buildDataChunk(L)));
|
||||
auto dataChunk = buildDataChunk(L);
|
||||
result->Add(std::move(dataChunk));
|
||||
} else if (keyType == LUA_TSTRING) {
|
||||
namedEntries.push_back(std::pair<std::string, std::unique_ptr<DataChunk>>(
|
||||
lua_tostring(L, -2),
|
||||
|
|
|
@ -601,17 +601,20 @@ void generateMaterial(CFileDefinition& fileDef, const MaterialState& from, const
|
|||
generateEnumMacro((int)from.alphaCompare, (int)to.alphaCompare, "gsDPSetAlphaCompare", gAlphaCompareNames, output);
|
||||
generateEnumMacro((int)from.depthSource, (int)to.depthSource, "gsDPSetDepthSource", gDepthSourceNames, output);
|
||||
|
||||
std::unique_ptr<DataChunk> geometryModes = std::move(generateGeometryModes(from, to));
|
||||
auto generatedGeometryModes = generateGeometryModes(from, to);
|
||||
std::unique_ptr<DataChunk> geometryModes = std::move(generatedGeometryModes);
|
||||
if (geometryModes) {
|
||||
output.Add(std::move(geometryModes));
|
||||
}
|
||||
|
||||
std::unique_ptr<DataChunk> combineMode = std::move(generateCombineMode(from, to));
|
||||
auto generatedCombineMode = generateCombineMode(from, to);
|
||||
std::unique_ptr<DataChunk> combineMode = std::move(generatedCombineMode);
|
||||
if (combineMode) {
|
||||
output.Add(std::move(combineMode));
|
||||
}
|
||||
|
||||
std::unique_ptr<DataChunk> renderMode = std::move(generateRenderMode(from, to));
|
||||
auto generatedRenderMode = generateRenderMode(from, to);
|
||||
std::unique_ptr<DataChunk> renderMode = std::move(generatedRenderMode);
|
||||
if (renderMode) {
|
||||
output.Add(std::move(renderMode));
|
||||
}
|
||||
|
|
|
@ -122,7 +122,7 @@ struct DecorObjectDefinition gDecorObjectDefinitions[] = {
|
|||
NULL,
|
||||
},
|
||||
0.0f,
|
||||
1.0f,
|
||||
1.3f,
|
||||
PROPS_LIGHT_RAIL_ENDCAP_DYNAMIC_MODEL,
|
||||
.materialIndex = LIGHT_RAIL_ENDCAP_INDEX,
|
||||
.soundClipId = -1,
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#define CONTROLLER_PRIORITY 11
|
||||
#define AUDIO_PRIORITY 12
|
||||
#define SCHEDULER_PRIORITY 13
|
||||
#define RSP_PROFILE_PRIORITY 14
|
||||
#define NUM_FIELDS 1
|
||||
|
||||
#define LEVEL_SEGMENT 2
|
||||
|
|
101
src/font/font.c
101
src/font/font.c
|
@ -47,107 +47,6 @@ struct FontSymbol* fontFindSymbol(struct Font* font, short id) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
Gfx* fontRender(struct Font* font, char* message, int x, int y, Gfx* dl) {
|
||||
int startX = x;
|
||||
char prev = 0;
|
||||
|
||||
for (; *message; prev = *message, ++message) {
|
||||
char curr = *message;
|
||||
|
||||
if (curr == '\n') {
|
||||
y += font->charHeight;
|
||||
x = startX;
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO utf-8 decode
|
||||
struct FontSymbol* symbol = fontFindSymbol(font, (short)curr);
|
||||
|
||||
if (!symbol) {
|
||||
continue;
|
||||
}
|
||||
|
||||
x += fontDetermineKerning(font, prev, curr);
|
||||
|
||||
int finalX = x + symbol->xoffset;
|
||||
int finalY = y + symbol->yoffset;
|
||||
|
||||
gSPTextureRectangle(
|
||||
dl++,
|
||||
finalX << 2, finalY << 2,
|
||||
(finalX + symbol->width) << 2,
|
||||
(finalY + symbol->height) << 2,
|
||||
G_TX_RENDERTILE,
|
||||
symbol->x << 5, symbol->y << 5,
|
||||
0x400, 0x400
|
||||
);
|
||||
|
||||
x += symbol->xadvance;
|
||||
}
|
||||
|
||||
return dl;
|
||||
}
|
||||
|
||||
int fontCountGfx(struct Font* font, char* message) {
|
||||
int result = 0;
|
||||
|
||||
for (; *message; ++message) {
|
||||
char curr = *message;
|
||||
|
||||
if (curr == '\n') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO utf-8 decode
|
||||
struct FontSymbol* symbol = fontFindSymbol(font, (short)curr);
|
||||
|
||||
if (!symbol) {
|
||||
continue;
|
||||
}
|
||||
|
||||
result += 3;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
struct Vector2s16 fontMeasure(struct Font* font, char* message) {
|
||||
int startX = 0;
|
||||
char prev = 0;
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
|
||||
struct Vector2s16 result;
|
||||
|
||||
result.x = 0;
|
||||
|
||||
for (; *message; prev = *message, ++message) {
|
||||
char curr = *message;
|
||||
|
||||
if (curr == '\n') {
|
||||
y += font->charHeight;
|
||||
x = startX;
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO utf-8 decode
|
||||
struct FontSymbol* symbol = fontFindSymbol(font, (short)curr);
|
||||
|
||||
if (!symbol) {
|
||||
continue;
|
||||
}
|
||||
|
||||
x += fontDetermineKerning(font, prev, curr);
|
||||
x += symbol->xadvance;
|
||||
|
||||
result.x = MAX(result.x, x);
|
||||
}
|
||||
|
||||
result.y = y + font->charHeight;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
short fontNextUtf8Character(char** strPtr) {
|
||||
char* curr = *strPtr;
|
||||
|
||||
|
|
|
@ -35,11 +35,6 @@ struct Font {
|
|||
unsigned short kerningMaxCollisions;
|
||||
};
|
||||
|
||||
// legacy methods for a font that fits into a single page
|
||||
Gfx* fontRender(struct Font* font, char* message, int x, int y, Gfx* dl);
|
||||
int fontCountGfx(struct Font* font, char* message);
|
||||
struct Vector2s16 fontMeasure(struct Font* font, char* message);
|
||||
|
||||
struct SymbolLocation {
|
||||
short x;
|
||||
short y;
|
||||
|
|
143
src/graphics/profile_task.c
Normal file
143
src/graphics/profile_task.c
Normal file
|
@ -0,0 +1,143 @@
|
|||
#include "profile_task.h"
|
||||
|
||||
#include "../defs.h"
|
||||
|
||||
|
||||
#ifdef PORTAL64_WITH_DEBUGGER
|
||||
#include "../debugger/serial.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define VIDEO_MSG 666
|
||||
#define RSP_DONE_MSG 667
|
||||
#define RDP_DONE_MSG 668
|
||||
|
||||
#define SAMPLES_PER_STEP 10
|
||||
|
||||
void copyGfx(Gfx* from, Gfx* to, int count) {
|
||||
Gfx* max = from + count;
|
||||
|
||||
while (from < max) {
|
||||
*to++ = *from++;
|
||||
}
|
||||
}
|
||||
|
||||
void profileTask(OSSched* scheduler, OSThread* currentThread, OSTask* task) {
|
||||
// block scheduler thread
|
||||
osSetThreadPri(currentThread, RSP_PROFILE_PRIORITY);
|
||||
|
||||
// wait for DP to be available
|
||||
while (IO_READ(DPC_STATUS_REG) & (DPC_STATUS_DMA_BUSY | DPC_STATUS_END_VALID | DPC_STATUS_START_VALID));
|
||||
|
||||
OSMesgQueue messageQueue;
|
||||
OSMesg messages[4];
|
||||
|
||||
osCreateMesgQueue(&messageQueue, messages, 4);
|
||||
|
||||
// take over event queues
|
||||
osSetEventMesg(OS_EVENT_SP, &messageQueue, (OSMesg)RSP_DONE_MSG);
|
||||
osSetEventMesg(OS_EVENT_DP, &messageQueue, (OSMesg)RDP_DONE_MSG);
|
||||
osViSetEvent(&messageQueue, (OSMesg)VIDEO_MSG, 1);
|
||||
|
||||
Gfx* curr = (Gfx*)task->t.data_ptr;
|
||||
|
||||
Gfx* end = curr;
|
||||
|
||||
while (end[1].words.w0 != _SHIFTL(G_RDPFULLSYNC, 24, 8)) {
|
||||
++end;
|
||||
}
|
||||
|
||||
#ifdef PORTAL64_WITH_DEBUGGER
|
||||
int total = end - curr;
|
||||
#endif
|
||||
|
||||
Gfx tmp[3];
|
||||
|
||||
while (curr <= end) {
|
||||
for (int sample = 0; sample < SAMPLES_PER_STEP; ++sample) {
|
||||
// wait for DP to be available
|
||||
while (IO_READ(DPC_STATUS_REG) & (DPC_STATUS_DMA_BUSY | DPC_STATUS_END_VALID | DPC_STATUS_START_VALID));
|
||||
|
||||
copyGfx(curr, tmp, 3);
|
||||
|
||||
Gfx* dl = curr;
|
||||
gDPPipeSync(dl++);
|
||||
gDPFullSync(dl++);
|
||||
gSPEndDisplayList(dl++);
|
||||
|
||||
// not very precise, but it seems to work
|
||||
osWritebackDCacheAll();
|
||||
|
||||
#ifdef PORTAL64_WITH_DEBUGGER
|
||||
OSTime start = osGetTime();
|
||||
#endif
|
||||
osSpTaskStart(task);
|
||||
OSMesg recv;
|
||||
|
||||
do {
|
||||
(void)osRecvMesg(&messageQueue, &recv, OS_MESG_BLOCK);
|
||||
} while ((int)recv != RDP_DONE_MSG);
|
||||
|
||||
#ifdef PORTAL64_WITH_DEBUGGER
|
||||
OSTime result = osGetTime() - start;
|
||||
|
||||
u64 us = OS_CYCLES_TO_NSEC(result);
|
||||
#endif
|
||||
|
||||
// wait for DP to be available
|
||||
while (IO_READ(DPC_STATUS_REG) & (DPC_STATUS_DMA_BUSY | DPC_STATUS_END_VALID | DPC_STATUS_START_VALID));
|
||||
|
||||
copyGfx(tmp, curr, 3);
|
||||
|
||||
osWritebackDCacheAll();
|
||||
|
||||
#ifdef PORTAL64_WITH_DEBUGGER
|
||||
char message[64];
|
||||
int messageLen = sprintf(
|
||||
message,
|
||||
"%d/%d 0x%08x%08x ms %d.%d",
|
||||
curr - (Gfx*)task->t.data_ptr,
|
||||
total,
|
||||
curr->words.w0,
|
||||
curr->words.w1,
|
||||
(int)(us / 1000000),
|
||||
(int)(us % 1000000)
|
||||
);
|
||||
gdbSendMessage(GDBDataTypeText, message, messageLen);
|
||||
#endif
|
||||
}
|
||||
|
||||
++curr;
|
||||
}
|
||||
|
||||
|
||||
osSetEventMesg(OS_EVENT_SP, &scheduler->interruptQ, (OSMesg)RSP_DONE_MSG);
|
||||
osSetEventMesg(OS_EVENT_DP, &scheduler->interruptQ, (OSMesg)RDP_DONE_MSG);
|
||||
osViSetEvent(&scheduler->interruptQ, (OSMesg)VIDEO_MSG, 1);
|
||||
osSetThreadPri(currentThread, GAME_PRIORITY);
|
||||
}
|
||||
|
||||
void profileMapAddress(void* original, void* ramAddress) {
|
||||
#ifdef PORTAL64_WITH_DEBUGGER
|
||||
char message[64];
|
||||
int messageLen = sprintf(
|
||||
message,
|
||||
"addr 0x%08x -> 0x%08x",
|
||||
(int)original,
|
||||
(int)ramAddress
|
||||
);
|
||||
gdbSendMessage(GDBDataTypeText, message, messageLen);
|
||||
#endif
|
||||
}
|
||||
|
||||
void profileClearAddressMap() {
|
||||
#ifdef PORTAL64_WITH_DEBUGGER
|
||||
char message[64];
|
||||
int messageLen = sprintf(
|
||||
message,
|
||||
"addr clearall"
|
||||
);
|
||||
gdbSendMessage(GDBDataTypeText, message, messageLen);
|
||||
#endif
|
||||
}
|
11
src/graphics/profile_task.h
Normal file
11
src/graphics/profile_task.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
#ifndef __PROFILE_TASK_H__
|
||||
#define __PROFILE_TASK_H__
|
||||
|
||||
#include <ultra64.h>
|
||||
#include <sched.h>
|
||||
|
||||
void profileTask(OSSched* scheduler, OSThread* currentThread, OSTask* task);
|
||||
void profileMapAddress(void* original, void* ramAddress);
|
||||
void profileClearAddressMap();
|
||||
|
||||
#endif
|
|
@ -65,14 +65,16 @@ void staticRenderPopulateRooms(struct FrustrumCullingInformation* cullingInfo, M
|
|||
|
||||
struct RotatedBox rotatedBox;
|
||||
|
||||
rotatedBoxTransform(&staticTransforms[staticElement->transformIndex], animatedBox, &rotatedBox);
|
||||
struct Transform* transform = &staticTransforms[staticElement->transformIndex];
|
||||
|
||||
rotatedBoxTransform(transform, animatedBox, &rotatedBox);
|
||||
|
||||
if (isRotatedBoxOutsideFrustrum(cullingInfo, &rotatedBox)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
struct Vector3 center;
|
||||
vector3Scale(&rotatedBox.origin, ¢er, 1.0f / SCENE_SCALE);
|
||||
vector3AddScaled(&staticElement->center, &transform->position, 1.0f / SCENE_SCALE, ¢er);
|
||||
|
||||
renderSceneAdd(
|
||||
renderScene,
|
||||
|
|
12
src/main.c
12
src/main.c
|
@ -25,6 +25,7 @@
|
|||
#include "util/profile.h"
|
||||
#include "util/rom.h"
|
||||
#include "util/time.h"
|
||||
#include "graphics/profile_task.h"
|
||||
|
||||
#include "levels/levels.h"
|
||||
#include "savefile/checkpoint.h"
|
||||
|
@ -182,6 +183,7 @@ int updateSchedulerModeAndGetFPS(int interlacedMode) {
|
|||
schedulerMode = HIGH_RES ? (interlacedMode ? OS_VI_MPAL_HPF1 : OS_VI_MPAL_HPN1) : (interlacedMode ? OS_VI_MPAL_LPF1 : OS_VI_MPAL_LPN1);
|
||||
break;
|
||||
}
|
||||
|
||||
return fps;
|
||||
}
|
||||
|
||||
|
@ -253,6 +255,7 @@ static void gameProc(void* arg) {
|
|||
controllersInit();
|
||||
rumblePakClipInit();
|
||||
initAudio(fps);
|
||||
timeSetFrameRate(fps);
|
||||
soundPlayerInit();
|
||||
translationsLoad(gSaveData.controls.subtitleLanguage);
|
||||
skSetSegmentLocation(CHARACTER_ANIMATION_SEGMENT, (unsigned)_animation_segmentSegmentRomStart);
|
||||
|
@ -281,6 +284,7 @@ static void gameProc(void* arg) {
|
|||
portalSurfaceRevert(0);
|
||||
portalSurfaceCleanupQueueInit();
|
||||
heapInit(_heapStart, memoryEnd);
|
||||
profileClearAddressMap();
|
||||
translationsLoad(gSaveData.controls.subtitleLanguage);
|
||||
levelLoadWithCallbacks(levelGetQueued());
|
||||
rumblePakClipInit();
|
||||
|
@ -325,6 +329,14 @@ static void gameProc(void* arg) {
|
|||
profileEnd(updateStart, 0);
|
||||
drawingEnabled = 1;
|
||||
}
|
||||
|
||||
#if PORTAL64_WITH_RSP_PROFILER
|
||||
if (controllerGetButtonDown(2, START_BUTTON)) {
|
||||
struct GraphicsTask* task = &gGraphicsTasks[drawBufferIndex];
|
||||
zeroMemory(task->framebuffer, sizeof(u16) * SCREEN_WD * SCREEN_HT);
|
||||
profileTask(&scheduler, &gameThread, &task->task.list);
|
||||
}
|
||||
#endif
|
||||
timeUpdateDelta();
|
||||
soundPlayerUpdate();
|
||||
controllersSavePreviousState();
|
||||
|
|
|
@ -44,8 +44,9 @@ void mainMenuInit(struct GameMenu* gameMenu) {
|
|||
|
||||
mainMenuPlayAmbientSound();
|
||||
|
||||
for (int i = 0; i < gScene.signageCount; ++i) {
|
||||
signageActivate(&gScene.signage[i]);
|
||||
for (int i = 0; i < gScene.clockCount; ++i) {
|
||||
// this shows the pause time for the main menu
|
||||
gScene.clocks[i].timeLeft = -1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,6 +68,12 @@ void mainMenuUpdate(struct GameMenu* gameMenu) {
|
|||
|
||||
mainMenuPlayAmbientSound();
|
||||
|
||||
if (gScene.animator.animators[0].currentTime > 5.0f) {
|
||||
for (int i = 0; i < gScene.signageCount; ++i) {
|
||||
signageActivate(&gScene.signage[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < gScene.signageCount; ++i) {
|
||||
signageUpdate(&gScene.signage[i]);
|
||||
}
|
||||
|
|
|
@ -16,16 +16,6 @@ struct PrerenderedText* menuBuildPrerenderedText(struct Font* font, char* messag
|
|||
return result;
|
||||
}
|
||||
|
||||
Gfx* menuBuildText(struct Font* font, char* message, int x, int y) {
|
||||
Gfx* result = malloc(sizeof(Gfx) * (fontCountGfx(font, message) + 1));
|
||||
Gfx* dl = result;
|
||||
|
||||
dl = fontRender(font, message, x, y, dl);
|
||||
gSPEndDisplayList(dl++);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Gfx* menuBuildBorder(int x, int y, int width, int height) {
|
||||
Gfx* result = malloc(sizeof(Gfx) * 7 * 3 + 1);
|
||||
Gfx* dl = result;
|
||||
|
@ -211,7 +201,7 @@ void menuSetRenderColor(struct RenderState* renderState, int isSelected, struct
|
|||
}
|
||||
}
|
||||
|
||||
struct MenuCheckbox menuBuildCheckbox(struct Font* font, char* message, int x, int y, int shouldUsePrerendered) {
|
||||
struct MenuCheckbox menuBuildCheckbox(struct Font* font, char* message, int x, int y) {
|
||||
struct MenuCheckbox result;
|
||||
|
||||
result.x = x;
|
||||
|
@ -227,12 +217,7 @@ struct MenuCheckbox menuBuildCheckbox(struct Font* font, char* message, int x, i
|
|||
dl = menuRenderOutline(x, y, CHECKBOX_SIZE, CHECKBOX_SIZE, 1, dl);
|
||||
gSPEndDisplayList(dl++);
|
||||
|
||||
if (shouldUsePrerendered) {
|
||||
result.prerenderedText = menuBuildPrerenderedText(font, message, x + CHECKBOX_SIZE + 6, y, SCREEN_WD);
|
||||
} else {
|
||||
result.text = menuBuildText(font, message, x + CHECKBOX_SIZE + 6, y);
|
||||
}
|
||||
|
||||
result.checked = 0;
|
||||
|
||||
return result;
|
||||
|
|
|
@ -17,10 +17,7 @@ struct MenuButton {
|
|||
|
||||
struct MenuCheckbox {
|
||||
Gfx* outline;
|
||||
union {
|
||||
Gfx* text;
|
||||
struct PrerenderedText* prerenderedText;
|
||||
};
|
||||
Gfx* checkedIndicator;
|
||||
short x, y;
|
||||
short checked;
|
||||
|
@ -48,7 +45,6 @@ extern struct Coloru8 gSelectionGray;
|
|||
extern struct Coloru8 gBorderHighlight;
|
||||
extern struct Coloru8 gBorderDark;
|
||||
|
||||
Gfx* menuBuildText(struct Font* font, char* message, int x, int y);
|
||||
struct PrerenderedText* menuBuildPrerenderedText(struct Font* font, char* message, int x, int y, int maxWidth);
|
||||
|
||||
Gfx* menuBuildBorder(int x, int y, int width, int height);
|
||||
|
@ -61,7 +57,7 @@ struct MenuButton menuBuildButton(struct Font* font, char* message, int x, int y
|
|||
void menuSetRenderColor(struct RenderState* renderState, int isSelected, struct Coloru8* selected, struct Coloru8* defaultColor);
|
||||
void menuRebuildButtonText(struct MenuButton* button, struct Font* font, char* message, int rightAlign);
|
||||
|
||||
struct MenuCheckbox menuBuildCheckbox(struct Font* font, char* message, int x, int y, int shouldUsePrerendered);
|
||||
struct MenuCheckbox menuBuildCheckbox(struct Font* font, char* message, int x, int y);
|
||||
Gfx* menuCheckboxRender(struct MenuCheckbox* checkbox, Gfx* dl);
|
||||
|
||||
struct MenuSlider menuBuildSlider(int x, int y, int w, int tickCount);
|
||||
|
|
|
@ -44,8 +44,7 @@ void checkboxMenuItemInit(struct MenuBuilderElement* element) {
|
|||
element->params->params.checkbox.font,
|
||||
translationsGet(element->params->params.checkbox.messageId),
|
||||
element->params->x,
|
||||
element->params->y,
|
||||
1
|
||||
element->params->y
|
||||
);
|
||||
element->data = checkbox;
|
||||
}
|
||||
|
|
|
@ -41,8 +41,6 @@ struct Tab gOptionTabs[] = {
|
|||
void optionsMenuInit(struct OptionsMenu* options) {
|
||||
options->menuOutline = menuBuildBorder(MENU_LEFT, MENU_TOP, MENU_WIDTH, MENU_HEIGHT);
|
||||
|
||||
options->optionsText = menuBuildText(&gDejaVuSansFont, "OPTIONS", 48, 48);
|
||||
|
||||
tabsInit(
|
||||
&options->tabs,
|
||||
gOptionTabs,
|
||||
|
|
|
@ -22,7 +22,6 @@ enum OptionsMenuTabs {
|
|||
|
||||
struct OptionsMenu {
|
||||
Gfx* menuOutline;
|
||||
Gfx* optionsText;
|
||||
|
||||
struct Tabs tabs;
|
||||
|
||||
|
|
|
@ -570,7 +570,7 @@ int epaSolveSwept(struct Simplex* startingSimplex, void* objectA, MinkowsiSum ob
|
|||
vector3Scale(&raycastDir, &planePos, distance);
|
||||
float moveOffset = vector3DistSqrd(bStart, bEnd);
|
||||
|
||||
if (distance * distance > moveOffset + 0.1f) {
|
||||
if (distance * distance >= moveOffset + 0.01f) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
|
|
@ -525,5 +525,6 @@ void sceneDeserialize(struct Serializer* serializer, struct Scene* scene) {
|
|||
|
||||
if (scene->player.flags & (PlayerHasFirstPortalGun | PlayerHasSecondPortalGun)) {
|
||||
scene->portalGun.portalGunVisible = 1;
|
||||
scene->portalGun.rotation = scene->player.lookTransform.rotation;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,8 +19,10 @@
|
|||
|
||||
#define DIGIT_WIDTH 512
|
||||
|
||||
u8 gCurrentClockDigits[5];
|
||||
u8 gCurrentClockDigits[7];
|
||||
Vtx* gClockDigits[] = {
|
||||
signage_clock_digits_clock_digits_hour_01_color,
|
||||
signage_clock_digits_clock_digits_minute_10_color,
|
||||
signage_clock_digits_clock_digits_minute_01_color,
|
||||
signage_clock_digits_clock_digits_second_10_color,
|
||||
signage_clock_digits_clock_digits_second_01_color,
|
||||
|
@ -45,31 +47,34 @@ void clockSetDigit(int digitIndex, int currDigit) {
|
|||
}
|
||||
|
||||
void clockSetTime(float timeInSeconds) {
|
||||
clockSetDigit(0, 0);
|
||||
clockSetDigit(1, 0);
|
||||
|
||||
float minutes = floor(timeInSeconds * (1.0f / 60.0f));
|
||||
|
||||
clockSetDigit(0, (int)minutes);
|
||||
clockSetDigit(2, (int)minutes);
|
||||
|
||||
timeInSeconds -= minutes *= 60.0f;
|
||||
|
||||
float tenSeconds = floor(timeInSeconds * 0.1f);
|
||||
|
||||
clockSetDigit(1, (int)tenSeconds);
|
||||
clockSetDigit(3, (int)tenSeconds);
|
||||
|
||||
timeInSeconds -= tenSeconds * 10.0f;
|
||||
|
||||
float seconds = floor(timeInSeconds);
|
||||
|
||||
clockSetDigit(2, (int)seconds);
|
||||
clockSetDigit(4, (int)seconds);
|
||||
|
||||
timeInSeconds -= seconds;
|
||||
|
||||
float tenthsOfSecond = floor(timeInSeconds * 10.0f);
|
||||
|
||||
clockSetDigit(3, (int)tenthsOfSecond);
|
||||
clockSetDigit(5, (int)tenthsOfSecond);
|
||||
|
||||
timeInSeconds -= tenthsOfSecond * 0.1f;
|
||||
|
||||
clockSetDigit(4, (int)floor(timeInSeconds * 100.0f));
|
||||
clockSetDigit(6, (int)floor(timeInSeconds * 100.0f));
|
||||
}
|
||||
|
||||
void clockRenderRender(void* data, struct DynamicRenderDataList* renderList, struct RenderState* renderState) {
|
||||
|
@ -81,7 +86,18 @@ void clockRenderRender(void* data, struct DynamicRenderDataList* renderList, str
|
|||
return;
|
||||
}
|
||||
|
||||
// main menu clock time
|
||||
if (clock->timeLeft == -1.0f) {
|
||||
clockSetDigit(0, 1);
|
||||
clockSetDigit(1, 5);
|
||||
clockSetDigit(2, 9);
|
||||
clockSetDigit(3, 5);
|
||||
clockSetDigit(4, 0);
|
||||
clockSetDigit(5, 0);
|
||||
clockSetDigit(6, 0);
|
||||
} else {
|
||||
clockSetTime(clock->timeLeft);
|
||||
}
|
||||
|
||||
transformToMatrixL(&clock->transform, matrix, SCENE_SCALE);
|
||||
|
||||
|
|
|
@ -216,4 +216,6 @@ void fizzlerUpdate(struct Fizzler* fizzler) {
|
|||
fizzler->oldestParticleIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
osWritebackDCache(fizzler->modelVertices, sizeof(Vtx) * maxVertex);
|
||||
}
|
|
@ -49,6 +49,9 @@ void portalGunInit(struct PortalGun* portalGun, struct Transform* at, int isFres
|
|||
|
||||
if (isFreshStart) {
|
||||
skAnimatorRunClip(&portalGun->animator, &portal_gun_v_portalgun_Armature_draw_clip, 0.0f, 0);
|
||||
// the first time the scene renders, the animation clip hasn't started yet
|
||||
// this just hides the gun offscreen so it doesn't show up for a single frame
|
||||
portalGun->armature.pose[0].position.y = -1.0f;
|
||||
} else {
|
||||
skAnimatorRunClip(&portalGun->animator, &portal_gun_v_portalgun_Armature_idle_clip, 0.0f, 0);
|
||||
}
|
||||
|
|
|
@ -272,7 +272,8 @@ int portalSurfaceAdjustPosition(struct PortalSurface* surface, struct Transform*
|
|||
|
||||
for (iteration = 0; iteration < MAX_POS_ADJUST_ITERATIONS; ++iteration) {
|
||||
int minOverlap = PORTAL_SURFACE_OVERLAP;
|
||||
struct Vector2s16 minOverlapOffset;
|
||||
int minIsOutside = 0;
|
||||
struct Vector2s16 minOverlapOffset = {{{0, 0}}};
|
||||
|
||||
struct Vector2s16 portalMin;
|
||||
portalMin.x = output->x - halfSize.x;
|
||||
|
@ -310,40 +311,49 @@ int portalSurfaceAdjustPosition(struct PortalSurface* surface, struct Transform*
|
|||
vector2s16Sub(&b, &a, &edgeDir);
|
||||
|
||||
struct Vector2s16 offset;
|
||||
int distance = edgeDir.y >= 0 ? portalMax.x - edgeMin.x : PORTAL_SURFACE_OVERLAP;
|
||||
int distance = portalMax.x - edgeMin.x;
|
||||
int isOutside = edgeDir.y < 0;
|
||||
offset.x = -distance;
|
||||
offset.y = 0;
|
||||
|
||||
int distanceCheck = edgeMax.x - portalMin.x;
|
||||
|
||||
if (distanceCheck < distance && edgeDir.y <= 0) {
|
||||
if (distanceCheck < distance) {
|
||||
distance = distanceCheck;
|
||||
offset.x = distance;
|
||||
offset.y = 0;
|
||||
isOutside = edgeDir.y > 0;
|
||||
}
|
||||
|
||||
distanceCheck = portalMax.y - edgeMin.y;
|
||||
|
||||
if (distanceCheck < distance && edgeDir.x <= 0) {
|
||||
if (distanceCheck < distance) {
|
||||
distance = distanceCheck;
|
||||
offset.x = 0;
|
||||
offset.y = -distance;
|
||||
isOutside = edgeDir.x > 0;
|
||||
}
|
||||
|
||||
distanceCheck = edgeMax.y - portalMin.y;
|
||||
|
||||
if (distanceCheck < distance && edgeDir.x >= 0) {
|
||||
if (distanceCheck < distance) {
|
||||
distance = distanceCheck;
|
||||
offset.x = 0;
|
||||
offset.y = distance;
|
||||
isOutside = edgeDir.x < 0;
|
||||
}
|
||||
|
||||
if (distance < minOverlap) {
|
||||
minOverlap = distance;
|
||||
minIsOutside = isOutside;
|
||||
minOverlapOffset = offset;
|
||||
}
|
||||
}
|
||||
|
||||
if (minIsOutside) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (minOverlap == PORTAL_SURFACE_OVERLAP) {
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -326,12 +326,13 @@ void sceneRender(struct Scene* scene, struct RenderState* renderState, struct Gr
|
|||
gDPSetRenderMode(renderState->dl++, G_RM_OPA_SURF, G_RM_OPA_SURF2);
|
||||
gSPGeometryMode(renderState->dl++, G_ZBUFFER | G_LIGHTING | G_CULL_BOTH, G_SHADE);
|
||||
|
||||
if (gGameMenu.state == GameMenuStateResumeGame || scene->hud.fadeInTimer > 0.0f) {
|
||||
hudRender(&scene->hud, &scene->player, renderState);
|
||||
}
|
||||
|
||||
if (gGameMenu.state != GameMenuStateResumeGame) {
|
||||
gameMenuRender(&gGameMenu, renderState, task);
|
||||
}
|
||||
else{
|
||||
hudRender(&scene->hud, &scene->player, renderState);
|
||||
}
|
||||
|
||||
|
||||
// sceneRenderPerformanceMetrics(scene, renderState, task);
|
||||
|
|
|
@ -250,5 +250,7 @@ void signageUpdate(struct Signage* signage) {
|
|||
}
|
||||
|
||||
void signageActivate(struct Signage* signage) {
|
||||
if (signage->currentFrame == -1) {
|
||||
signage->currentFrame = 0;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
#include "dynamic_asset_loader.h"
|
||||
#include "memory.h"
|
||||
#include "rom.h"
|
||||
#include "../graphics/profile_task.h"
|
||||
|
||||
#include "../build/assets/models/dynamic_model_list.h"
|
||||
#include "../build/assets/models/dynamic_animated_model_list.h"
|
||||
|
@ -92,6 +93,8 @@ Gfx* dynamicAssetLoadModel(struct DynamicAssetModel* model, u32* pointerOffset)
|
|||
*pointerOffset = 0;
|
||||
}
|
||||
|
||||
profileMapAddress(model->model, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -114,6 +117,8 @@ void dynamicAssetLoadAnimatedModel(struct DynamicAnimatedAssetModel* model, stru
|
|||
}
|
||||
|
||||
result->clipCount = model->clipCount;
|
||||
|
||||
profileMapAddress(result->armature->displayList, result->armature->displayList);
|
||||
}
|
||||
|
||||
void dynamicAssetModelPreload(int index) {
|
||||
|
|
|
@ -3,9 +3,14 @@
|
|||
|
||||
float gTimePassed = 0.0f;
|
||||
int gCurrentFrame = 0;
|
||||
float gFixedDeltaTime = ((1.0f + FRAME_SKIP) / 60.0f);
|
||||
|
||||
void timeUpdateDelta() {
|
||||
OSTime currTime = osGetTime();
|
||||
gTimePassed = (float)OS_CYCLES_TO_USEC(currTime) / 1000000.0f;
|
||||
++gCurrentFrame;
|
||||
}
|
||||
|
||||
void timeSetFrameRate(int fps) {
|
||||
gFixedDeltaTime = ((1.0f + FRAME_SKIP) / (float)fps);
|
||||
}
|
|
@ -6,10 +6,12 @@
|
|||
extern float gTimePassed;
|
||||
extern OSTime gLastTime;
|
||||
extern int gCurrentFrame;
|
||||
extern float gFixedDeltaTime;
|
||||
|
||||
#define FRAME_SKIP 1
|
||||
#define FIXED_DELTA_TIME ((1.0f + FRAME_SKIP) / 60.0f)
|
||||
#define FIXED_DELTA_TIME gFixedDeltaTime
|
||||
|
||||
void timeUpdateDelta();
|
||||
void timeSetFrameRate(int fps);
|
||||
|
||||
#endif
|
171
tools/profile_parser.js
Normal file
171
tools/profile_parser.js
Normal file
|
@ -0,0 +1,171 @@
|
|||
const fs = require('fs');
|
||||
|
||||
const lines = fs.readFileSync(process.argv[2], 'utf-8').split('\n');
|
||||
|
||||
const symbolMapLines = fs.readFileSync(process.argv[3], 'utf-8').split('\n');
|
||||
|
||||
const lineParserRegexp = /(\d+)\/\d+ 0x([a-f0-9]{2})([a-f0-9]{6})([a-f0-9]{8}) ms (\d+\.\d+)/
|
||||
|
||||
const lineMappingRegexp = /addr 0x([a-f0-9]{8}) -> 0x([a-f0-9]{8})/
|
||||
|
||||
const symbolParserRegexp = /0x([a-f0-9]{16})\s+(\w+)/
|
||||
|
||||
function parseLine(line) {
|
||||
const match = lineParserRegexp.exec(line);
|
||||
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
index: parseInt(match[1]),
|
||||
command: match[2],
|
||||
w0: match[3],
|
||||
w1: match[4],
|
||||
startTime: parseFloat(match[5]),
|
||||
}
|
||||
}
|
||||
|
||||
function parseSymbolLine(line) {
|
||||
const match = symbolParserRegexp.exec(line);
|
||||
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
address: match[1].substring(8),
|
||||
name: match[2],
|
||||
};
|
||||
}
|
||||
|
||||
const memoryMapping = new Map();
|
||||
const profileBatches = [];
|
||||
let lastIndex = -1;
|
||||
|
||||
for (const line of lines) {
|
||||
const parsedLine = parseLine(line);
|
||||
|
||||
if (parsedLine) {
|
||||
if (lastIndex != 0 && parsedLine.index == 0) {
|
||||
profileBatches.push({
|
||||
memoryMapping: new Map(memoryMapping),
|
||||
lines: [],
|
||||
})
|
||||
}
|
||||
|
||||
const latestBatch = profileBatches[profileBatches.length - 1];
|
||||
latestBatch.lines.push(parsedLine);
|
||||
|
||||
lastIndex = parsedLine.index;
|
||||
continue
|
||||
}
|
||||
|
||||
if (line.trim() == 'addr clearall') {
|
||||
memoryMapping.clear();
|
||||
}
|
||||
|
||||
const addrMatch = lineMappingRegexp.exec(line);
|
||||
|
||||
if (addrMatch) {
|
||||
memoryMapping.set(addrMatch[2], addrMatch[1]);
|
||||
}
|
||||
}
|
||||
|
||||
const symbolAddressMapping = new Map();
|
||||
|
||||
for (const symbolLine of symbolMapLines) {
|
||||
const parsedLine = parseSymbolLine(symbolLine);
|
||||
|
||||
if (!parsedLine) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (symbolAddressMapping.has(parsedLine.address)) {
|
||||
symbolAddressMapping.set(parsedLine.address, symbolAddressMapping.get(parsedLine.address) + ',' + parsedLine.name);
|
||||
} else {
|
||||
symbolAddressMapping.set(parsedLine.address, parsedLine.name);
|
||||
}
|
||||
}
|
||||
|
||||
function calculateAverage(batch) {
|
||||
const combinedCommands = [];
|
||||
|
||||
for (const parsedLine of batch.lines) {
|
||||
const existing = combinedCommands[parsedLine.index];
|
||||
|
||||
if (existing) {
|
||||
existing.startTime += parsedLine.startTime;
|
||||
existing.total += 1;
|
||||
} else {
|
||||
combinedCommands[parsedLine.index] = {
|
||||
...parsedLine,
|
||||
total: 1,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < combinedCommands.length; ++i) {
|
||||
const current = combinedCommands[i];
|
||||
|
||||
if (current) {
|
||||
current.startTime /= current.total;
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i + 1 < combinedCommands.length; ++i) {
|
||||
const current = combinedCommands[i];
|
||||
const next = combinedCommands[i + 1];
|
||||
|
||||
current.elapsedTime = next.startTime - current.startTime;
|
||||
}
|
||||
|
||||
// the last command is always a pipe sync we dont care about
|
||||
combinedCommands.pop();
|
||||
|
||||
combinedCommands.sort((a, b) => b.elapsedTime - a.elapsedTime);
|
||||
|
||||
batch.combinedCommands = combinedCommands;
|
||||
}
|
||||
|
||||
profileBatches.forEach(calculateAverage);
|
||||
|
||||
function formatAddress(address, batch) {
|
||||
if (batch.memoryMapping.has(address)) {
|
||||
address = batch.memoryMapping.get(address);
|
||||
}
|
||||
|
||||
return symbolAddressMapping.get(address) || `0x${address}`;
|
||||
}
|
||||
|
||||
function formatCommandName(command, batch) {
|
||||
switch (command.command) {
|
||||
case 'db':
|
||||
{
|
||||
const segment = parseInt(command.w0.substring(4), 16) / 4;
|
||||
return `gsSPSegment(0x${segment}, 0x${command.w1})`;
|
||||
}
|
||||
case 'de':
|
||||
return `gsSPDisplayList(${formatAddress(command.w1, batch)})`;
|
||||
case 'f6':
|
||||
return `gsDPFillRectangle`;
|
||||
case 'd8':
|
||||
return `gsSPPopMatrix`;
|
||||
case 'da':
|
||||
return `gsSPMatrix`;
|
||||
default:
|
||||
return `unknown 0x${command.command} 0x${command.w0}${command.w1}`;
|
||||
}
|
||||
}
|
||||
|
||||
function formatCommand(command, batch) {
|
||||
return `${command.elapsedTime} ${formatCommandName(command, batch)}`;
|
||||
}
|
||||
|
||||
for (const batch of profileBatches) {
|
||||
console.log('start of batch');
|
||||
for (const command of batch.combinedCommands) {
|
||||
console.log(formatCommand(command, batch));
|
||||
}
|
||||
console.log('end of batch');
|
||||
}
|
Loading…
Reference in a new issue