Include skelatool as part of the project to ensure the skelatool version matches the portal version

dockerize the project
This commit is contained in:
James Lambert 2022-05-17 20:45:40 -06:00
parent 62a34c0bda
commit de394ca30c
88 changed files with 9511 additions and 10 deletions

View file

@ -18,12 +18,12 @@ RUN dpkg --add-architecture i386
RUN apt install -y binutils-mips-n64 \
gcc-mips-n64 \
n64sdk \
libnustd \
makemask \
root-compatibility-environment \
build-essential \
libmpc-dev \
vtf2png \
skeletool64 \
libxi6 \
libxxf86vm-dev \
libxfixes3 \
@ -31,7 +31,30 @@ RUN apt install -y binutils-mips-n64 \
libgl1 \
python3 \
pip \
imagemagick
imagemagick \
libpng-dev \
libtiff-dev \
libassimp-dev \
git \
cmake \
build-essential \
wget \
unzip
COPY skelatool64/src skelatool64/src
COPY skelatool64/main.cpp skelatool64/main.cpp
COPY skelatool64/makefile skelatool64/makefile
RUN git clone https://github.com/jbeder/yaml-cpp.git skelatool64/yaml-cpp
RUN cmake -S skelatool64/yaml-cpp -B skelatool64/yaml-cpp
RUN make -C skelatool64/yaml-cpp
RUN wget http://cimg.eu/files/CImg_latest.zip
RUN unzip CImg_latest.zip
RUN mv CImg-3.1.3_pre051622 skelatool64/cimg
RUN make -C skelatool64
RUN pip install vpk
@ -41,6 +64,6 @@ COPY tools/generate_level_list.js tools/generate_level_list.js
COPY asm asm
COPY assets assets
COPY src src
copy portal.ld portal.ld
COPY portal.ld portal.ld
CMD make

View file

@ -8,9 +8,12 @@
# --------------------------------------------------------------------
include /usr/include/n64/make/PRdefs
SKELATOOL64:=skeletool64
SKELATOOL64:=skelatool64/skeletool64
VTF2PNG:=vtf2png
$(SKELATOOL64):
make -C skelatool64
OPTIMIZER := -O0
LCDEFS := -DDEBUG -g -Isrc/ -I/usr/include/n64/nustd -Werror -Wall
N64LIB := -lultra_rom -lnustd
@ -94,13 +97,14 @@ portal_pak_dir: vpk/portal_pak_dir.vpk
TEXTURE_SCRIPTS = $(shell find assets/ -type f -name '*.ims')
TEXTURE_IMAGES = $(TEXTURE_SCRIPTS:assets/%.ims=portal_pak_modified/%.png)
TEXTURE_VTF_SOURCES = $(TEXTURE_SCRIPTS:assets/%.ims=portal_pak_dir/%.vtf)
$(TEXTURE_IMAGES): portal_pak_dir
$(TEXTURE_VTF_SOURCES): portal_pak_dir
%.png: %.vtf
$(VTF2PNG) $< $@
portal_pak_modified/%.png: portal_pak_dir/%.png assets/%.ims
portal_pak_modified/%.png: portal_pak_dir/%.png assets/%.ims
@mkdir -p $(@D)
convert $< $(shell cat $(@:portal_pak_modified/%.png=assets/%.ims)) $@
@ -109,11 +113,11 @@ portal_pak_modified/%.png: portal_pak_dir/%.png assets/%.ims
## Materials
####################
build/assets/materials/static.h build/assets/materials/static_mat.c: assets/materials/static.skm.yaml $(TEXTURE_IMAGES)
build/assets/materials/static.h build/assets/materials/static_mat.c: assets/materials/static.skm.yaml $(TEXTURE_IMAGES) $(SKELATOOL64)
@mkdir -p $(@D)
$(SKELATOOL64) -n static -m $< -M build/assets/materials/static.h
build/assets/materials/hud.h build/assets/materials/hud_mat.c: assets/materials/hud.skm.yaml $(TEXTURE_IMAGES)
build/assets/materials/hud.h build/assets/materials/hud_mat.c: assets/materials/hud.skm.yaml $(TEXTURE_IMAGES) $(SKELATOOL64)
@mkdir -p $(@D)
$(SKELATOOL64) -n hud -m $< -M build/assets/materials/hud.h
@ -134,7 +138,7 @@ MODEL_LIST = assets/models/cube/cube.blend \
MODEL_HEADERS = $(MODEL_LIST:%.blend=build/%.h)
MODEL_OBJECTS = $(MODEL_LIST:%.blend=build/%_geo.o)
build/assets/models/%.h build/assets/models/%_geo.c: build/assets/models/%.fbx assets/materials/objects.skm.yaml
build/assets/models/%.h build/assets/models/%_geo.c: build/assets/models/%.fbx assets/materials/objects.skm.yaml $(SKELATOOL64)
$(SKELATOOL64) -s 2.56 -n $(<:build/assets/models/%.fbx=%) -m assets/materials/objects.skm.yaml -o $(<:%.fbx=%.h) $<
build/src/models/models.o: $(MODEL_HEADERS)
@ -152,7 +156,7 @@ build/%.fbx: %.blend
@mkdir -p $(@D)
$(BLENDER_2_9) $< --background --python tools/export_fbx.py -- $@
build/assets/test_chambers/%.h build/assets/test_chambers/%_geo.c: build/assets/test_chambers/%.fbx build/assets/materials/static.h
build/assets/test_chambers/%.h build/assets/test_chambers/%_geo.c: build/assets/test_chambers/%.fbx build/assets/materials/static.h $(SKELATOOL64) $(TEXTURE_IMAGES)
$(SKELATOOL64) -l -s 2.56 -c 0.01 -n $(<:build/assets/test_chambers/%.fbx=%) -m assets/materials/static.skm.yaml -o $(<:%.fbx=%.h) $<
build/assets/test_chambers/%.o: build/assets/test_chambers/%.c build/assets/materials/static.h

View file

@ -41,3 +41,16 @@ portal_pak_dir.vpk
Finally run `make` to build the project
## Build with Docker
Build the docker image
```
docker build . -t portal64
```
Then build
```
BLENDER_2_9=/blender/blender docker run -v /home/james/Blender/blender-2.93.1-linux-x64:/blender -e BLENDER_2_9 -v /home/james/portal/portal64/vpk:/usr/src/app/vpk -t -v /home/james/portal/portal64/docker-output:/usr/src/app/build portal64
```
where `/home/james/Blender/blender-2.93.1-linux-x64` is the folder where blender is located
`/home/james/portal/portal64/vpk` is the folder where the portal vpk files are located
`/home/james/portal/portal64/docker-output` is where you want the output of the build to located `portal.z64` will be put into this folder

13
skelatool64/.gitignore vendored Normal file
View file

@ -0,0 +1,13 @@
*.o
*.d
.vscode/
assimp/
cimg/
yaml-cpp/
output/
skeletool64
dna.txt
build/
images/
examples/*.h
examples/*.c

175
skelatool64/main.cpp Normal file
View file

@ -0,0 +1,175 @@
#include <assimp/mesh.h>
#include <iostream>
#include <fstream>
#include <execinfo.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include "src/SceneWriter.h"
#include "src/CommandLineParser.h"
#include "src/materials/MaterialParser.h"
#include "src/SceneLoader.h"
#include "src/FileUtils.h"
#include "src/definition_generator/MeshDefinitionGenerator.h"
#include "src/definition_generator/CollisionGenerator.h"
#include "src/definition_generator/MaterialGenerator.h"
#include "src/definition_generator/StaticGenerator.h"
#include "src/definition_generator/LevelGenerator.h"
#include "src/materials/MaterialState.h"
void handler(int sig) {
void *array[10];
size_t size;
// get void*'s for all entries on the stack
size = backtrace(array, 10);
// print out all the frames to stderr
fprintf(stderr, "Error: signal %d:\n", sig);
backtrace_symbols_fd(array, size, STDERR_FILENO);
exit(1);
}
bool parseMaterials(const std::string& filename, DisplayListSettings& output) {
std::fstream file(filename, std::ios::in);
struct ParseResult parseResult(DirectoryName(filename));
parseMaterialFile(file, parseResult);
output.mMaterials.insert(parseResult.mMaterialFile.mMaterials.begin(), parseResult.mMaterialFile.mMaterials.end());
for (auto err : parseResult.mErrors) {
std::cerr << "Error parsing file " << filename << std::endl;
std::cerr << err.mMessage << std::endl;
}
return parseResult.mErrors.size() == 0;
}
bool getVectorByName(const aiScene* scene, const std::string name, aiVector3D& result) {
int axis;
if (!scene->mMetaData->Get(name, axis)) {
return false;
}
switch (axis) {
case 0: result = aiVector3D(1.0f, 0.0f, 0.0f); break;
case 1: result = aiVector3D(0.0f, 1.0f, 0.0f); break;
case 2: result = aiVector3D(0.0f, 0.0f, 1.0f); break;
default: return false;
}
int upSign;
if (scene->mMetaData->Get(name + "Sign", upSign)) {
if (upSign < 0) {
result = -result;
}
}
return true;
}
float angleBetween(const aiVector3D& a, const aiVector3D& b) {
return acos(a * b / (a - b).SquareLength());
}
aiQuaternion getUpRotation(const aiVector3D& euler) {
return aiQuaternion(euler.y * M_PI / 180.0f, euler.z * M_PI / 180.0f, euler.x * M_PI / 180.0f);
}
/**
* F3DEX2 - 32 vertices in buffer
* F3D - 16 vetcies in buffer
*/
int main(int argc, char *argv[]) {
signal(SIGSEGV, handler);
CommandLineArguments args;
if (!parseCommandLineArguments(argc, argv, args)) {
return 1;
}
DisplayListSettings settings = DisplayListSettings();
settings.mGraphicsScale = args.mGraphicsScale;
settings.mCollisionScale = args.mCollisionScale;
settings.mRotateModel = getUpRotation(args.mEulerAngles);
settings.mPrefix = args.mPrefix;
settings.mExportAnimation = args.mExportAnimation;
settings.mExportGeometry = args.mExportGeometry;
bool hasError = false;
for (auto materialFile = args.mMaterialFiles.begin(); materialFile != args.mMaterialFiles.end(); ++materialFile) {
if (!parseMaterials(*materialFile, settings)) {
hasError = true;
}
}
if (hasError) {
return 1;
}
const aiScene* scene = NULL;
if (args.mInputFile.length()) {
std::cout << "Generating from mesh " << args.mInputFile << std::endl;
scene = loadScene(args.mInputFile, args.mIsLevel, settings.mVertexCacheSize);
if (!scene) {
return 1;
}
}
if (scene && args.mOutputFile.length()) {
std::cout << "Saving to " << args.mOutputFile << std::endl;
// generateMeshFromSceneToFile(scene, args.mOutputFile, settings);
MeshDefinitionGenerator meshGenerator(settings);
std::cout << "Generating mesh definitions" << std::endl;
meshGenerator.TraverseScene(scene);
CFileDefinition fileDef(settings.mPrefix, settings.mGraphicsScale, settings.mRotateModel);
meshGenerator.GenerateDefinitions(scene, fileDef);
if (args.mIsLevel) {
std::cout << "Generating collider definitions" << std::endl;
CollisionGenerator colliderGenerator(settings);
colliderGenerator.TraverseScene(scene);
colliderGenerator.GenerateDefinitions(scene, fileDef);
std::cout << "Generating static definitions" << std::endl;
StaticGenerator staticGenerator(settings);
staticGenerator.TraverseScene(scene);
staticGenerator.GenerateDefinitions(scene, fileDef);
std::cout << "Generating level definitions" << std::endl;
LevelGenerator levelGenerator(settings, staticGenerator.GetOutput(), colliderGenerator.GetOutput());
levelGenerator.GenerateDefinitions(scene, fileDef);
}
std::cout << "Writing output" << std::endl;
fileDef.GenerateAll(args.mOutputFile);
}
if (args.mMaterialOutput.length()) {
std::cout << "Saving materials to " << args.mMaterialOutput << std::endl;
MaterialGenerator materialGenerator(settings);
materialGenerator.TraverseScene(scene);
CFileDefinition fileDef(settings.mPrefix, settings.mGraphicsScale, settings.mRotateModel);
materialGenerator.GenerateDefinitions(scene, fileDef);
fileDef.GenerateAll(args.mMaterialOutput);
}
return 0;
}

39
skelatool64/makefile Normal file
View file

@ -0,0 +1,39 @@
GCC_FLAGS = -Wall -Werror -g -rdynamic -I./yaml-cpp/include
LINKER_FLAGS = -L./yaml-cpp -lassimp -lyaml-cpp -lpng -ltiff
SRC_FILES = main.cpp $(shell find src/ -type f -name '*.cpp')
OBJ_FILES = $(patsubst %.cpp, build/%.o, $(SRC_FILES))
DEPS = $(patsubst %.cpp, build/%.d, $(SRC_FILES))
.PHONY: default
default: skeletool64
-include $(DEPS)
build/%.o: %.cpp
@mkdir -p $(@D)
g++ $(GCC_FLAGS) -c $< -o $@
$(CC) $(GCC_FLAGS) -MM $^ -MF "$(@:.o=.d)" -MT"$@"
skeletool64: $(OBJ_FILES)
g++ -g -o skeletool64 $(OBJ_FILES) $(LINKER_FLAGS)
clean:
rm -r build/
init:
install: skeletool64
cp skeletool64 ~/.local/bin
build/skeletool.deb: skeletool64 control
mkdir build/skeletool/usr/local/bin -p
cp skeletool64 build/skeletool/usr/local/bin
mkdir build/skeletool/DEBIAN -p
cp control build/skeletool/DEBIAN
dpkg-deb --build build/skeletool

View file

@ -0,0 +1,695 @@
{
"$schema": "http://json-schema.org/draft-07/schema",
"title": "Schema for an N64 material",
"definitions": {
"renderBlendModes": {
"type": "array",
"items": [
{
"enum": [
"G_BL_CLR_IN",
"G_BL_CLR_MEM",
"G_BL_CLR_BL",
"G_BL_CLR_FOG"
]
},
{
"enum": [
"G_BL_A_IN",
"G_BL_A_FOG",
"G_BL_A_SHADE",
"G_BL_0"
]
},
{
"enum": [
"G_BL_CLR_IN",
"G_BL_CLR_MEM",
"G_BL_CLR_BL",
"G_BL_CLR_FOG"
]
},
{
"enum": [
"G_BL_1MA",
"G_BL_A_MEM",
"G_BL_1",
"G_BL_0"
]
}
]
},
"renderMode": {
"type": "object",
"properties": {
"flags": {
"type": "array",
"items": {
"enum": [
"AA_EN",
"Z_CMP",
"Z_UPD",
"IM_RD",
"CLR_ON_CVG",
"CVG_X_ALPHA",
"ALPHA_CVG_SEL",
"FORCE_BL",
"CVG_DST_CLAMP",
"CVG_DST_WRAP",
"CVG_DST_FULL",
"CVG_DST_SAVE",
"ZMODE_OPA",
"ZMODE_INTER",
"ZMODE_XLU",
"ZMODE_DEC"
]
},
"uniqueItems": true
},
"cycle1": {
"$ref": "#/definitions/renderBlendModes"
},
"cycle2": {
"$ref": "#/definitions/renderBlendModes"
}
}
},
"geometryModeArray": {
"type": "array",
"items": {
"enum": [
"G_ZBUFFER",
"G_SHADE",
"G_TEXTURE_ENABLE",
"G_SHADING_SMOOTH",
"G_CULL_FRONT",
"G_CULL_BACK",
"G_FOG",
"G_LIGHTING",
"G_TEXTURE_GEN",
"G_TEXTURE_GEN_LINEAR",
"G_LOD",
"G_CLIPPING"
]
},
"uniqueItems": true
},
"combineMode": {
"oneOf": [
{
"type": "object",
"properties": {
"color": {
"type": "array",
"items": [
{
"enum": [
"COMBINED",
"TEXEL0",
"TEXEL1",
"PRIMITIVE",
"SHADE",
"ENVIRONMENT",
"NOISE",
"1",
"0"
]
},
{
"enum": [
"COMBINED",
"TEXEL0",
"TEXEL1",
"PRIMITIVE",
"SHADE",
"ENVIRONMENT",
"CENTER",
"K4",
"0"
]
},
{
"enum": [
"COMBINED",
"TEXEL0",
"TEXEL1",
"PRIMITIVE",
"SHADE",
"ENVIRONMENT",
"SCALE",
"COMBINED_ALPHA",
"TEXEL0_ALPHA",
"TEXEL1_ALPHA",
"PRIMITIVE_ALPHA",
"SHADED_ALPHA",
"ENVIRONMENT_ALPHA",
"LOD_FRACTION",
"PRIM_LOD_FRAC",
"K5",
"0"
]
},
{
"enum": [
"COMBINED",
"TEXEL0",
"TEXEL1",
"PRIMITIVE",
"SHADE",
"ENVIRONMENT",
"1",
"0"
]
}
]
},
"alpha": {
"type": "array",
"items": [
{
"enum": [
"COMBINED",
"TEXEL0",
"TEXEL1",
"PRIMITIVE",
"SHADE",
"ENVIRONMENT",
"1",
"0"
]
},
{
"enum": [
"COMBINED",
"TEXEL0",
"TEXEL1",
"PRIMITIVE",
"SHADE",
"ENVIRONMENT",
"1",
"0"
]
},
{
"enum": [
"TEXEL0",
"TEXEL1",
"PRIMITIVE",
"SHADE",
"ENVIRONMENT",
"LOD_FRACTION",
"PRIM_LOD_FRAC",
"0"
]
},
{
"enum": [
"COMBINED",
"TEXEL0",
"TEXEL1",
"PRIMITIVE",
"SHADE",
"ENVIRONMENT",
"1",
"0"
]
}
]
}
}
},
{
"enum": [
"G_CC_PRIMITIVE",
"G_CC_SHADE",
"G_CC_MODULATEI",
"G_CC_MODULATEIA",
"G_CC_MODULATEIDECALA",
"G_CC_MODULATERGB",
"G_CC_MODULATERGBA",
"G_CC_MODULATERGBDECALA",
"G_CC_MODULATEI_PRIM",
"G_CC_MODULATEIA_PRIM",
"G_CC_MODULATEIDECALA_PRIM",
"G_CC_MODULATERGB_PRIM",
"G_CC_MODULATERGBA_PRIM",
"G_CC_MODULATERGBDECALA_PRIM",
"G_CC_DECALRGB",
"G_CC_DECALRGBA",
"G_CC_BLENDI",
"G_CC_BLENDIA",
"G_CC_BLENDIDECALA",
"G_CC_BLENDRGBA",
"G_CC_BLENDRGBDECALA",
"G_CC_ADDRGB",
"G_CC_ADDRGBDECALA",
"G_CC_REFLECTRGB",
"G_CC_REFLECTRGBDECALA",
"G_CC_HILITERGB",
"G_CC_HILITERGBA",
"G_CC_HILITERGBDECALA",
"G_CC_SHADEDECALA",
"G_CC_BLENDPE",
"G_CC_BLENDPEDECALA",
"_G_CC_BLENDPE",
"_G_CC_BLENDPEDECALA",
"_G_CC_TWOCOLORTEX",
"_G_CC_SPARSEST",
"G_CC_TEMPLERP",
"G_CC_TRILERP",
"G_CC_INTERFERENCE",
"G_CC_1CYUV2RGB",
"G_CC_YUV2RGB",
"G_CC_PASS2",
"G_CC_MODULATEI2",
"G_CC_MODULATEIA2",
"G_CC_MODULATERGB2",
"G_CC_MODULATERGBA2",
"G_CC_MODULATEI_PRIM2",
"G_CC_MODULATEIA_PRIM2",
"G_CC_MODULATERGB_PRIM2",
"G_CC_MODULATERGBA_PRIM2",
"G_CC_DECALRGB2",
"G_CC_BLENDI2",
"G_CC_BLENDIA2",
"G_CC_CHROMA_KEY2",
"G_CC_HILITERGB2",
"G_CC_HILITERGBA2",
"G_CC_HILITERGBDECALA2",
"G_CC_HILITERGBPASSA2"
]
}
]
},
"color": {
"type": "object",
"properties": {
"r": {
"type": "number",
"minimum": 0,
"maximum": 255
},
"g": {
"type": "number",
"minimum": 0,
"maximum": 255
},
"b": {
"type": "number",
"minimum": 0,
"maximum": 255
},
"a": {
"type": "number",
"minimum": 0,
"maximum": 255
}
},
"required": [
"r",
"g",
"b"
]
},
"textureCoordinateSettings": {
"type": "object",
"properties": {
"wrap": {
"type": "boolean"
},
"mirror": {
"type": "boolean"
},
"mask": {
"type": "integer"
},
"shift": {
"type": "integer"
},
"offset": {
"type": "integer"
},
"limit": {
"type": "integer"
}
}
},
"textureSettings": {
"type": "object",
"properties": {
"sc": {
"type": "number",
"minimum": 0,
"maximum": 65535
},
"tc": {
"type": "number",
"minimum": 0,
"maximum": 65535
},
"level": {
"type": "number",
"minimum": 0,
"maximum": 7
},
"tile": {
"type": "number",
"minimum": 0,
"maximum": 7
},
"s": {
"$ref": "#/definitions/textureCoordinateSettings"
},
"t": {
"$ref": "#/definitions/textureCoordinateSettings"
}
}
},
"tileSettings": {
"oneOf": [
{
"type": "string"
},
{
"type": "object",
"properties": {
"filename": {
"type": "string"
},
"fmt": {
"enum": [
"G_IM_FMT_RGBA",
"G_IM_FMT_YUV",
"G_IM_FMT_CI",
"G_IM_FMT_I",
"G_IM_FMT_IA"
]
},
"siz": {
"enum": [
"G_IM_SIZ_4b",
"G_IM_SIZ_8b",
"G_IM_SIZ_16b",
"G_IM_SIZ_32b"
]
},
"tmem": {
"type": "number",
"minimum": 0,
"maximum": 511
},
"pallete": {
"type": "number",
"minimum": 0,
"maximum": 15
},
"twoTone": {
"type": "boolean"
}
},
"required": [
"filename"
]
}
]
},
"material": {
"type": "object",
"properties": {
"gDPPipelineMode": {
"enum": [
"Unknown",
"G_PM_1PRIMITIVE",
"G_PM_NPRIMITIVE"
]
},
"gDPSetCycleType": {
"enum": [
"Unknown",
"G_CYC_1CYCLE",
"G_CYC_2CYCLE",
"G_CYC_COPY",
"G_CYC_FILL"
]
},
"gDPSetTexturePersp": {
"enum": [
"Unknown",
"G_TP_NONE",
"G_TP_PERSP"
]
},
"gDPSetTextureDetail": {
"enum": [
"Unknown",
"G_TD_CLAMP",
"G_TD_SHARPEN",
"G_TD_DETAIL"
]
},
"gDPSetTextureLOD": {
"enum": [
"Unknown",
"G_TL_TILE",
"G_TL_LOD"
]
},
"gDPSetTextureLUT": {
"enum": [
"Unknown",
"G_TT_NONE",
"G_TT_RGBA16",
"G_TT_IA16"
]
},
"gDPSetTextureFilter": {
"enum": [
"Unknown",
"G_TF_POINT",
"G_TF_AVERAGE",
"G_TF_BILERP"
]
},
"gDPSetTextureConvert": {
"enum": [
"Unknown",
"G_TC_CONV",
"G_TC_FILTCONV",
"G_TC_FILT"
]
},
"gDPSetCombineKey": {
"enum": [
"Unknown",
"G_CK_NONE",
"G_CK_KEY"
]
},
"gDPSetColorDither": {
"enum": [
"Unknown",
"G_CD_MAGICSQ",
"G_CD_BAYER",
"G_CD_NOISE",
"G_CD_DISABLE"
]
},
"gDPSetAlphaDither": {
"enum": [
"Unknown",
"G_AD_PATTERN",
"G_AD_NOTPATTERN",
"G_AD_NOISE",
"G_AD_DISABLE"
]
},
"gDPSetAlphaCompare": {
"enum": [
"Unknown",
"G_AC_NONE",
"G_AC_THRESHOLD",
"G_AC_DITHER"
]
},
"gDPSetDepthSource": {
"enum": [
"Unknown",
"G_ZS_PIXEL",
"G_ZS_PRIM"
]
},
"gDPSetRenderMode": {
"anyOf": [
{
"enum": [
"G_RM_AA_ZB_OPA_SURF",
"G_RM_RA_ZB_OPA_SURF",
"G_RM_AA_ZB_XLU_SURF",
"G_RM_AA_ZB_OPA_DECAL",
"G_RM_RA_ZB_OPA_DECAL",
"G_RM_AA_ZB_XLU_DECAL",
"G_RM_AA_ZB_OPA_INTER",
"G_RM_RA_ZB_OPA_INTER",
"G_RM_AA_ZB_XLU_INTER",
"G_RM_AA_ZB_XLU_LINE",
"G_RM_AA_ZB_DEC_LINE",
"G_RM_AA_ZB_TEX_EDGE",
"G_RM_AA_ZB_TEX_INTER",
"G_RM_AA_ZB_SUB_SURF",
"G_RM_AA_ZB_PCL_SURF",
"G_RM_AA_ZB_OPA_TERR",
"G_RM_AA_ZB_TEX_TERR",
"G_RM_AA_ZB_SUB_TERR",
"G_RM_AA_OPA_SURF",
"G_RM_RA_OPA_SURF",
"G_RM_AA_XLU_SURF",
"G_RM_AA_XLU_LINE",
"G_RM_AA_DEC_LINE",
"G_RM_AA_TEX_EDGE",
"G_RM_AA_SUB_SURF",
"G_RM_AA_PCL_SURF",
"G_RM_AA_OPA_TERR",
"G_RM_AA_TEX_TERR",
"G_RM_AA_SUB_TERR",
"G_RM_ZB_OPA_SURF",
"G_RM_ZB_XLU_SURF",
"G_RM_ZB_OPA_DECAL",
"G_RM_ZB_XLU_DECAL",
"G_RM_ZB_CLD_SURF",
"G_RM_ZB_OVL_SURF",
"G_RM_ZB_PCL_SURF",
"G_RM_OPA_SURF",
"G_RM_XLU_SURF",
"G_RM_TEX_EDGE",
"G_RM_CLD_SURF",
"G_RM_PCL_SURF",
"G_RM_ADD",
"G_RM_NOOP",
"G_RM_VISCVG",
"G_RM_OPA_CI",
"G_RM_FOG_SHADE_A",
"G_RM_FOG_PRIM_A",
"G_RM_PASS"
]
},
{
"$ref": "#/definitions/renderMode"
}
]
},
"gDPSetCombineMode": {
"anyOf": [
{
"type": "array",
"items": {
"$ref": "#/definitions/combineMode"
},
"maxLength": 2,
"minLength": 2
},
{
"$ref": "#/definitions/combineMode"
}
]
},
"gSPGeometryMode": {
"type": "object",
"properties": {
"clear": {
"$ref": "#/definitions/geometryModeArray"
},
"set": {
"$ref": "#/definitions/geometryModeArray"
}
}
},
"gSPTexture": {
"$ref": "#/definitions/textureSettings"
},
"gDPSetTile": {
"oneOf": [
{
"$ref": "#/definitions/tileSettings"
},
{
"type": "array",
"items": {
"oneOf": [
{
"type": "null"
},
{
"$ref": "#/definitions/tileSettings"
}
]
},
"maxLength": 8,
"minLength": 1
}
]
},
"gDPSetPrimColor": {
"type": "object",
"r": {
"type": "number",
"minimum": 0,
"maximum": 255
},
"g": {
"type": "number",
"minimum": 0,
"maximum": 255
},
"b": {
"type": "number",
"minimum": 0,
"maximum": 255
},
"a": {
"type": "number",
"minimum": 0,
"maximum": 255
},
"l": {
"type": "number",
"minimum": 0,
"maximum": 255
},
"m": {
"type": "number",
"minimum": 0,
"maximum": 255
}
},
"gDPSetEnvColor": {
"$ref": "#/definitions/color"
},
"gDPSetFogColor": {
"$ref": "#/definitions/color"
},
"gDPSetBlendColor": {
"$ref": "#/definitions/color"
},
"properties": {
"type": "object",
"additionalProperties": {
"oneOf": [{
"type": "number"
}, {
"type": "string"
}]
}
}
}
}
},
"type": "object",
"properties": {
"materials": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/material"
}
}
}
}

View file

@ -0,0 +1,12 @@
#!/usr/bin/bash
sudo apt install -y libpng-dev libtiff-dev libassimp-dev
git clone https://github.com/jbeder/yaml-cpp.git
cmake -S yaml-cpp -B yaml-cpp
make -C yaml-cpp
wget http://cimg.eu/files/CImg_latest.zip
unzip CImg_latest.zip
mv CImg-3.1.3_pre051622 cimg

View file

@ -0,0 +1,85 @@
#include "Animation.h"
#include <climits>
#include <algorithm>
#include <iomanip>
#define ALIGN_TO_16(input) (((input) + 15) & ~15)
#define NO_CHUNK 0
SKBoneKeyframe::SKBoneKeyframe():
boneIndex(0),
usedAttributes(0) {
}
SKAnimationKeyframe::SKAnimationKeyframe(): tick(0) {}
SKAnimationChunk::SKAnimationChunk(): nextChunkSize(0), nextChunkTick(0) {}
unsigned short calculateChunkSize(SKAnimationChunk& chunk) {
unsigned short result = 6;
for (auto keyframe = chunk.keyframes.begin(); keyframe != chunk.keyframes.end(); ++keyframe) {
result += 4;
for (auto bone = keyframe->bones.begin(); bone != keyframe->bones.end(); ++bone) {
result += 2 + 2 * bone->attributeData.size();
}
}
return result;
}
void generateAnimationChunk(SKAnimationChunk& chunk, std::vector<unsigned short>& output) {
output.push_back(chunk.nextChunkSize);
output.push_back(chunk.nextChunkTick);
output.push_back(chunk.keyframes.size());
for (auto keyframe = chunk.keyframes.begin(); keyframe != chunk.keyframes.end(); ++keyframe) {
output.push_back(keyframe->tick);
output.push_back(keyframe->bones.size());
for (auto bone = keyframe->bones.begin(); bone != keyframe->bones.end(); ++bone) {
output.push_back(((unsigned short)bone->boneIndex << 8) | bone->usedAttributes);
output.insert(output.end(), bone->attributeData.begin(), bone->attributeData.end());
}
}
}
unsigned short generateAnimationData(std::vector<SKAnimationChunk>& chunks, std::vector<unsigned short>& output) {
unsigned short prevChunkSize = NO_CHUNK;
for (auto it = chunks.rbegin(); it != chunks.rend(); ++it) {
it->nextChunkSize = prevChunkSize;
prevChunkSize = ALIGN_TO_16(calculateChunkSize(*it));
}
for (auto it = chunks.begin(); it != chunks.end(); ++it) {
generateAnimationChunk(*it, output);
// add padding
while (output.size() * 2 < ALIGN_TO_16(output.size() * 2)) {
output.push_back(0);
}
}
return prevChunkSize;
}
unsigned short formatAnimationChunks(const std::string& name, std::vector<SKAnimationChunk>& chunks, CFileDefinition& fileDef) {
std::vector<unsigned short> data;
unsigned short firstChunkSize = generateAnimationData(chunks, data);
std::unique_ptr<StructureDataChunk> animationDataChunk(new StructureDataChunk());
for (unsigned i = 0; i < data.size(); ++i) {
animationDataChunk->AddPrimitive(data[i]);
}
fileDef.AddDefinition(std::unique_ptr<FileDefinition>(new DataFileDefinition("u16", name, true, "_anim", std::move(animationDataChunk))));
return firstChunkSize;
}

View file

@ -0,0 +1,50 @@
#ifndef _SK_ANIMATION_H
#define _SK_ANIMATION_H
#include <vector>
#include <string>
#include <ostream>
#include "./CFileDefinition.h"
enum SKBoneAttrMask {
SKBoneAttrMaskPosition = (1 << 0),
SKBoneAttrMaskRotation = (1 << 1),
SKBoneAttrMaskScale = (1 << 2),
};
struct SKBoneKeyframe {
SKBoneKeyframe();
unsigned char boneIndex;
unsigned char usedAttributes;
std::vector<short> attributeData;
};
struct SKAnimationKeyframe {
SKAnimationKeyframe();
unsigned short tick;
std::vector<SKBoneKeyframe> bones;
};
struct SKAnimationChunk {
SKAnimationChunk();
unsigned short nextChunkSize;
unsigned short nextChunkTick;
std::vector<SKAnimationKeyframe> keyframes;
};
struct SKAnimationHeader {
unsigned short firstChunkSize;
unsigned short ticksPerSecond;
unsigned short maxTicks;
std::string animationName;
};
struct SKAnimation {
unsigned short ticksPerSecond;
unsigned short maxTicks;
std::vector<SKAnimationChunk> chunks;
};
unsigned short formatAnimationChunks(const std::string& name, std::vector<SKAnimationChunk>& chunks, CFileDefinition& fileDef);
#endif

View file

@ -0,0 +1,415 @@
#include "AnimationTranslator.h"
#include <map>
#include <vector>
#include <climits>
#include <algorithm>
#include <iostream>
#define KEYFRAME_REMOVE_TOLERNACE 8
struct SKBoneKeyframeChain {
SKBoneKeyframe keyframe;
unsigned short tick;
bool isNeeded;
int removalScore;
struct SKBoneKeyframeChain* next;
struct SKBoneKeyframeChain* prev;
};
bool chainIsLess(SKBoneKeyframeChain* a, SKBoneKeyframeChain* b) {
return a->removalScore > b->removalScore;
}
aiQuaternion getRotation(SKBoneKeyframe& keyframe) {
aiQuaternion result;
result.x = (float)keyframe.attributeData[0] / (float)std::numeric_limits<short>::max();
result.y = (float)keyframe.attributeData[1] / (float)std::numeric_limits<short>::max();
result.z = (float)keyframe.attributeData[2] / (float)std::numeric_limits<short>::max();
float wsqrd = 1.0f - (result.x * result.x + result.y * result.y + result.z + result.z);
if (wsqrd < 0.0f) {
result.w = 0.0f;
} else {
result.w = sqrtf(wsqrd);
}
return result;
}
void writeRotation(const aiQuaternion& quaternion, std::vector<short>& output) {
if (quaternion.w < 0.0f) {
output.push_back((short)(-quaternion.x * std::numeric_limits<short>::max()));
output.push_back((short)(-quaternion.y * std::numeric_limits<short>::max()));
output.push_back((short)(-quaternion.z * std::numeric_limits<short>::max()));
} else {
output.push_back((short)(quaternion.x * std::numeric_limits<short>::max()));
output.push_back((short)(quaternion.y * std::numeric_limits<short>::max()));
output.push_back((short)(quaternion.z * std::numeric_limits<short>::max()));
}
}
void guessInterpolatedValue(struct SKBoneKeyframeChain* prev, struct SKBoneKeyframeChain* next, unsigned short tick, bool isRotation, std::vector<short>& guessedValues) {
if (!next || !prev) {
return;
}
unsigned short timeOffset = next->tick - prev->tick;
unsigned short timeToCurr = tick - prev->tick;
if (isRotation) {
aiQuaternion prevRotation = getRotation(prev->keyframe);
aiQuaternion nextRotation = getRotation(next->keyframe);
aiQuaternion interpolatedQuat;
aiQuaternion::Interpolate(
interpolatedQuat,
prevRotation,
nextRotation,
(float)timeToCurr / (float)timeOffset
);
writeRotation(interpolatedQuat, guessedValues);
} else {
for (unsigned i = 0; i < prev->keyframe.attributeData.size(); ++i) {
short valueOffset = next->keyframe.attributeData[i] - prev->keyframe.attributeData[i];
guessedValues.push_back(prev->keyframe.attributeData[i] + valueOffset * timeToCurr / timeOffset);
}
}
}
bool isKeyframeNeeded(struct SKBoneKeyframeChain* prev, struct SKBoneKeyframeChain* curr, struct SKBoneKeyframeChain* next) {
if (!next || !prev) {
return true;
}
std::vector<short> guessedValues;
guessInterpolatedValue(prev, next, curr->tick, (curr->keyframe.usedAttributes & SKBoneAttrMaskRotation) != 0, guessedValues);
for (unsigned i = 0; i < curr->keyframe.attributeData.size(); ++i) {
if (abs(guessedValues[i] - curr->keyframe.attributeData[i]) > KEYFRAME_REMOVE_TOLERNACE) {
return true;
}
}
return false;
}
int keyframeRemovalError(struct SKBoneKeyframeChain* prev, struct SKBoneKeyframeChain* curr, struct SKBoneKeyframeChain* next) {
if (!next || !prev) {
return std::numeric_limits<int>::max();
}
int result = 0;
std::vector<short> guessedValues;
guessInterpolatedValue(prev, next, curr->tick, (curr->keyframe.usedAttributes & SKBoneAttrMaskRotation) != 0, guessedValues);
for (unsigned i = 0; i < curr->keyframe.attributeData.size(); ++i) {
result += abs(guessedValues[i] - curr->keyframe.attributeData[i]);
}
return result;
}
unsigned short keyForKeyframe(const SKBoneKeyframe& keyframe) {
return ((unsigned short)keyframe.boneIndex << 8) | (unsigned short)(keyframe.usedAttributes & 0x7);
}
bool keyframeSortFn(const SKBoneKeyframeChain& a, const SKBoneKeyframeChain& b) {
if (a.tick != b.tick) {
return a.tick < b.tick;
}
if (a.keyframe.boneIndex != b.keyframe.boneIndex) {
return a.keyframe.boneIndex < b.keyframe.boneIndex;
}
return (a.keyframe.usedAttributes & 0x7) < (b.keyframe.usedAttributes & 0x7);
}
void populateKeyframes(const aiAnimation& input, BoneHierarchy& bones, float modelScale, float timeScalar, std::vector<SKBoneKeyframeChain>& output, aiQuaternion rotation) {
for (unsigned i = 0; i < input.mNumChannels; ++i) {
aiNodeAnim* node = input.mChannels[i];
Bone* targetBone = bones.BoneForName(node->mNodeName.C_Str());
if (!targetBone) {
continue;
}
for (unsigned keyIndex = 0; keyIndex < node->mNumPositionKeys; ++keyIndex) {
aiVectorKey* vectorKey = &node->mPositionKeys[keyIndex];
SKBoneKeyframeChain keyframe;
keyframe.tick = (unsigned short)(vectorKey->mTime * timeScalar + 0.5f);
keyframe.next = nullptr;
keyframe.prev = nullptr;
keyframe.keyframe.usedAttributes = SKBoneAttrMaskPosition;
keyframe.keyframe.boneIndex = (unsigned char)targetBone->GetIndex();
aiVector3D origin = vectorKey->mValue;
if (!targetBone->GetParent()) {
origin = rotation.Rotate(origin);
}
keyframe.keyframe.attributeData.push_back((short)(origin.x * modelScale));
keyframe.keyframe.attributeData.push_back((short)(origin.y * modelScale));
keyframe.keyframe.attributeData.push_back((short)(origin.z * modelScale));
output.push_back(keyframe);
}
for (unsigned keyIndex = 0; keyIndex < node->mNumRotationKeys; ++keyIndex) {
aiQuatKey* quatKey = &node->mRotationKeys[keyIndex];
SKBoneKeyframeChain keyframe;
keyframe.tick = (unsigned short)(quatKey->mTime * timeScalar + 0.5f);
keyframe.next = nullptr;
keyframe.prev = nullptr;
keyframe.keyframe.usedAttributes = SKBoneAttrMaskRotation;
keyframe.keyframe.boneIndex = (unsigned char)targetBone->GetIndex();
if (targetBone->GetParent()) {
writeRotation(quatKey->mValue, keyframe.keyframe.attributeData);
} else {
writeRotation(rotation * quatKey->mValue, keyframe.keyframe.attributeData);
}
output.push_back(keyframe);
}
for (unsigned keyIndex = 0; keyIndex < node->mNumScalingKeys; ++keyIndex) {
aiVectorKey* vectorKey = &node->mScalingKeys[keyIndex];
SKBoneKeyframeChain keyframe;
keyframe.tick = (unsigned short)(vectorKey->mTime * timeScalar + 0.5f);
keyframe.next = nullptr;
keyframe.prev = nullptr;
keyframe.keyframe.usedAttributes = SKBoneAttrMaskScale;
keyframe.keyframe.boneIndex = (unsigned char)targetBone->GetIndex();
keyframe.keyframe.attributeData.push_back((short)(vectorKey->mValue.x * 0x100));
keyframe.keyframe.attributeData.push_back((short)(vectorKey->mValue.y * 0x100));
keyframe.keyframe.attributeData.push_back((short)(vectorKey->mValue.z * 0x100));
output.push_back(keyframe);
}
}
std::sort(output.begin(), output.end(), keyframeSortFn);
}
void connectKeyframeChain(std::vector<SKBoneKeyframeChain>& keyframes, std::map<unsigned short, SKBoneKeyframeChain*>& firstKeyframe) {
for (auto it = keyframes.rbegin(); it != keyframes.rend(); ++it) {
unsigned short key = keyForKeyframe(it->keyframe);
auto prev = firstKeyframe.find(key);
if (prev != firstKeyframe.end()) {
it->next = prev->second;
prev->second->prev = &*it;
} else {
it->next = nullptr;
}
firstKeyframe[key] = &(*it);
}
for (auto it = firstKeyframe.begin(); it != firstKeyframe.end(); ++it) {
it->second->prev = nullptr;
}
}
void removeRedundantKeyframes(std::vector<SKBoneKeyframeChain>& keyframes, std::map<unsigned short, SKBoneKeyframeChain*>& firstKeyframe) {
std::vector<SKBoneKeyframeChain*> byRemovalScore;
for (auto it = firstKeyframe.begin(); it != firstKeyframe.end(); ++it) {
SKBoneKeyframeChain* prev = nullptr;
SKBoneKeyframeChain* curr = it->second;
while (curr) {
curr->removalScore = keyframeRemovalError(prev, curr, curr->next);
byRemovalScore.push_back(curr);
std::push_heap(byRemovalScore.begin(), byRemovalScore.end(), chainIsLess);
prev = curr;
curr = curr->next;
}
}
while (byRemovalScore.size()) {
SKBoneKeyframeChain* curr = byRemovalScore.front();
curr->isNeeded = isKeyframeNeeded(curr->prev, curr, curr->next);
if (!curr->isNeeded) {
curr->next->prev = curr->prev;
curr->prev->next = curr->next;
curr->next = nullptr;
curr->prev = nullptr;
}
std::pop_heap(byRemovalScore.begin(), byRemovalScore.end(), chainIsLess);
byRemovalScore.pop_back();
}
auto currentRead = keyframes.begin();
auto currentWrite = keyframes.begin();
while (currentRead != keyframes.end()) {
if (currentRead->isNeeded) {
*currentWrite = *currentRead;
++currentWrite;
}
++currentRead;
}
keyframes.resize(currentWrite - keyframes.begin());
firstKeyframe.clear();
connectKeyframeChain(keyframes, firstKeyframe);
}
#define CONSTANT_INTERPOLATION_THESHOLD (7888 / 2)
void markConstantKeyframes(std::map<unsigned short, SKBoneKeyframeChain*>& firstKeyframe) {
std::set<int> jumpCutFrames;
double thresholdCheck = 0.0f;
for (auto it = firstKeyframe.begin(); it != firstKeyframe.end(); ++it) {
SKBoneKeyframeChain* curr = it->second;
while (curr && curr->next) {
if (curr->keyframe.attributeData == curr->next->keyframe.attributeData) {
curr->keyframe.usedAttributes |= (curr->keyframe.usedAttributes & 0x7) << 4;
} else if (curr->keyframe.usedAttributes & SKBoneAttrMaskPosition) {
int maxJump = 0;
for (size_t i = 0; i < curr->keyframe.attributeData.size() && i < curr->next->keyframe.attributeData.size(); ++i) {
maxJump = std::max(maxJump, curr->next->keyframe.attributeData[i] - curr->keyframe.attributeData[i]);
}
double maxJumpDouble = (double)maxJump / (double)(curr->next->tick - curr->tick);
if (maxJumpDouble > CONSTANT_INTERPOLATION_THESHOLD) {
jumpCutFrames.insert((curr->tick << 8) | curr->keyframe.boneIndex);
} else {
maxJump = std::max(thresholdCheck, maxJumpDouble);
}
}
curr = curr->next;
}
}
for (auto it = firstKeyframe.begin(); it != firstKeyframe.end(); ++it) {
SKBoneKeyframeChain* curr = it->second;
while (curr && curr->next) {
if (jumpCutFrames.find((curr->tick << 8) | curr->keyframe.boneIndex) != jumpCutFrames.end()) {
curr->keyframe.usedAttributes |= (curr->keyframe.usedAttributes & 0x7) << 4;
}
curr = curr->next;
}
}
std::cout << "maxJump " << thresholdCheck << std::endl;
}
void combineChunk(std::vector<SKBoneKeyframeChain>& chunkKeyframes, struct SKAnimationChunk& output) {
std::sort(chunkKeyframes.begin(), chunkKeyframes.end(), keyframeSortFn);
for (auto it = chunkKeyframes.begin(); it != chunkKeyframes.end(); ++it) {
if (!output.keyframes.size() || output.keyframes.rbegin()->tick != it->tick) {
SKAnimationKeyframe newKeyframe;
newKeyframe.tick = it->tick;
output.keyframes.push_back(newKeyframe);
}
auto targetKeyframe = output.keyframes.rbegin();
if (!targetKeyframe->bones.size() || targetKeyframe->bones.rbegin()->boneIndex != it->keyframe.boneIndex) {
SKBoneKeyframe newBone;
newBone.usedAttributes = 0;
newBone.boneIndex = it->keyframe.boneIndex;
targetKeyframe->bones.push_back(newBone);
}
auto targetBone = targetKeyframe->bones.rbegin();
targetBone->usedAttributes |= it->keyframe.usedAttributes;
targetBone->attributeData.insert(targetBone->attributeData.end(), it->keyframe.attributeData.begin(), it->keyframe.attributeData.end());
}
}
unsigned buildChunk(std::vector<SKBoneKeyframeChain>& keyframes, unsigned atIndex, unsigned short currentTick, struct SKAnimationChunk& output) {
std::vector<SKBoneKeyframeChain> nextKeyframes;
while (atIndex < keyframes.size() && keyframes[atIndex].tick == currentTick) {
if (keyframes[atIndex].next) {
nextKeyframes.push_back(*keyframes[atIndex].next);
}
++atIndex;
}
combineChunk(nextKeyframes, output);
return atIndex;
}
void buildInitialState(std::map<unsigned short, SKBoneKeyframeChain*>& firstKeyFrame, struct SKAnimationChunk& output) {
std::vector<SKBoneKeyframeChain> keyframes;
for (auto it = firstKeyFrame.begin(); it != firstKeyFrame.end(); ++it) {
SKBoneKeyframeChain modified = *(it->second);
modified.tick = 0;
keyframes.push_back(modified);
}
combineChunk(keyframes, output);
}
bool translateAnimationToSK(const aiAnimation& input, struct SKAnimation& output, BoneHierarchy& bones, float modelScale, unsigned short targetTicksPerSecond, aiQuaternion rotation) {
float timeScalar = (float)targetTicksPerSecond / (float)1000.0f;
std::vector<SKBoneKeyframeChain> keyframes;
populateKeyframes(input, bones, modelScale, timeScalar, keyframes, rotation);
if (keyframes.size() == 0) {
return false;
}
std::map<unsigned short, SKBoneKeyframeChain*> firstKeyFrame;
connectKeyframeChain(keyframes, firstKeyFrame);
removeRedundantKeyframes(keyframes, firstKeyFrame);
markConstantKeyframes(firstKeyFrame);
struct SKAnimationChunk currentChunk;
currentChunk.nextChunkSize = 0;
currentChunk.nextChunkTick = 0;
unsigned currentIndex = 0;
buildInitialState(firstKeyFrame, currentChunk);
output.ticksPerSecond = targetTicksPerSecond;
output.maxTicks = (unsigned short)(input.mDuration * timeScalar);
while (currentIndex < keyframes.size()) {
unsigned short tick = keyframes[currentIndex].tick;
currentIndex = buildChunk(keyframes, currentIndex, tick, currentChunk);
if (currentIndex < keyframes.size()) {
currentChunk.nextChunkTick = keyframes[currentIndex].tick;
} else {
currentChunk.nextChunkTick = std::numeric_limits<unsigned short>::max();
}
if (currentChunk.keyframes.size()) {
output.chunks.push_back(currentChunk);
}
currentChunk.nextChunkSize = 0;
currentChunk.nextChunkTick = 0;
currentChunk.keyframes.clear();
}
output.chunks.rbegin()->nextChunkTick = std::numeric_limits<unsigned short>::max();
return true;
}

View file

@ -0,0 +1,10 @@
#ifndef _SK_ANIMATION_TRANSLATOR_H
#define _SK_ANIMATION_TRANSLATOR_H
#include <assimp/anim.h>
#include "Animation.h"
#include "BoneHierarchy.h"
bool translateAnimationToSK(const aiAnimation& input, struct SKAnimation& output, BoneHierarchy& bones, float modelScale, unsigned short targetTicksPerSecond, aiQuaternion rotate);
#endif

View file

@ -0,0 +1,183 @@
#include "BoneHierarchy.h"
#include "CFileDefinition.h"
Bone::Bone(int index, std::string name, Bone* parent, const aiVector3D& restPosition, const aiQuaternion& restRotation, const aiVector3D& restScale):
mIndex(index),
mName(name),
mParent(parent),
mRestPosition(restPosition),
mRestRotation(restRotation),
mRestScale(restScale) {
if (mParent) {
mParent->mChildren.push_back(this);
}
}
int Bone::GetIndex() {
return mIndex;
}
const std::string& Bone::GetName() {
return mName;
}
Bone* Bone::GetParent() {
return mParent;
}
std::unique_ptr<DataChunk> Bone::GenerateRestPosiitonData(float scale, aiQuaternion rotation) {
aiVector3D restPosition = rotation.Rotate(mRestPosition);
aiQuaternion restRotation = rotation * mRestRotation;
std::unique_ptr<StructureDataChunk> result(new StructureDataChunk());
restPosition = restPosition * scale;
result->Add(std::unique_ptr<DataChunk>(new StructureDataChunk(restPosition)));
result->Add(std::unique_ptr<DataChunk>(new StructureDataChunk(restRotation)));
result->Add(std::unique_ptr<DataChunk>(new StructureDataChunk(mRestScale)));
return std::move(result);
}
Bone* Bone::FindCommonAncestor(Bone* a, Bone* b) {
std::set<Bone*> hierarchyDifference;
Bone* curr = a;
while (curr) {
hierarchyDifference.insert(curr);
curr = curr->mParent;
}
curr = b;
while (curr) {
hierarchyDifference.erase(curr);
curr = curr->mParent;
}
curr = a;
while (hierarchyDifference.find(curr) != hierarchyDifference.end()) {
curr = curr->mParent;
}
return curr;
}
Bone* Bone::StepDownTowards(Bone* ancestor, Bone* decendant) {
Bone* curr = decendant;
while (curr && curr->mParent != ancestor) {
curr = curr->mParent;
}
return curr;
}
bool Bone::CompareBones(Bone* a, Bone* b) {
if (a == nullptr && b == nullptr) {
return false;
} else if (a == nullptr) {
return true;
} else if (b == nullptr) {
return false;
} else {
return a->mIndex < b->mIndex;
}
}
int Bone::GetBoneIndex(Bone* a) {
if (a == nullptr) {
return -1;
} else {
return a->mIndex;
}
}
void BoneHierarchy::SearchForBones(aiNode* node, Bone* currentBoneParent, std::set<std::string>& knownBones, bool parentIsBone) {
if (knownBones.find(node->mName.C_Str()) != knownBones.end()) {
aiVector3D restPosition;
aiQuaternion restRotation;
aiVector3D restScale;
node->mTransformation.Decompose(restScale, restRotation, restPosition);
mBones.push_back(std::unique_ptr<Bone>(new Bone(
mBones.size(),
node->mName.C_Str(),
currentBoneParent,
restPosition,
restRotation,
restScale
)));
currentBoneParent = mBones[mBones.size() - 1].get();
mBoneByName.insert(std::pair<std::string, Bone*>(node->mName.C_Str(), currentBoneParent));
parentIsBone = true;
}
for (unsigned int i = 0; i < node->mNumChildren; ++i) {
SearchForBones(node->mChildren[i], currentBoneParent, knownBones, parentIsBone);
}
}
void BoneHierarchy::SearchForBonesInScene(const aiScene* scene) {
std::set<std::string> knownBones;
for (unsigned int meshIndex = 0; meshIndex < scene->mNumMeshes; ++meshIndex) {
aiMesh* mesh = scene->mMeshes[meshIndex];
for (unsigned int boneIndex = 0; boneIndex < mesh->mNumBones; ++boneIndex) {
knownBones.insert(mesh->mBones[boneIndex]->mName.C_Str());
}
}
SearchForBones(scene->mRootNode, nullptr, knownBones, false);
}
Bone* BoneHierarchy::BoneByIndex(unsigned index) {
return mBones[index].get();
}
Bone* BoneHierarchy::BoneForName(std::string name) {
auto result = mBoneByName.find(name);
if (result == mBoneByName.end()) {
return nullptr;
} else {
return result->second;
}
}
void BoneHierarchy::GenerateRestPosiitonData(CFileDefinition& fileDef, const std::string& variableName, float scale, aiQuaternion rotation) {
if (mBones.size() == 0) return;
std::unique_ptr<StructureDataChunk> transformData(new StructureDataChunk());
for (unsigned int boneIndex = 0; boneIndex < mBones.size(); ++boneIndex) {
if (mBones[boneIndex]->GetParent()) {
transformData->Add(std::move(mBones[boneIndex]->GenerateRestPosiitonData(scale, aiQuaternion())));
} else {
transformData->Add(std::move(mBones[boneIndex]->GenerateRestPosiitonData(scale, rotation)));
}
std::string boneName = fileDef.GetUniqueName(mBones[boneIndex]->GetName() + "_BONE");
std::transform(boneName.begin(), boneName.end(), boneName.begin(), ::toupper);
fileDef.AddMacro(boneName, std::to_string(boneIndex));
}
fileDef.AddDefinition(std::unique_ptr<FileDefinition>(new DataFileDefinition("struct Transform", variableName, true, "_anim", std::move(transformData))));
}
bool BoneHierarchy::HasData() const {
return mBones.size() > 0;
}
unsigned int BoneHierarchy::GetBoneCount() const {
return mBones.size();
}

View file

@ -0,0 +1,67 @@
#ifndef _BONE_HIERARCHY_H
#define _BONE_HIERARCHY_H
#include <assimp/vector3.h>
#include <assimp/quaternion.h>
#include <assimp/scene.h>
#include <vector>
#include <memory>
#include <string>
#include <set>
#include <map>
#include <ostream>
#include "ErrorCode.h"
#include "./definitions/DataChunk.h"
class CFileDefinition;
class Bone {
public:
Bone(int index, std::string name, Bone* parent, const aiVector3D& restPosition, const aiQuaternion& restRotation, const aiVector3D& restScale);
int GetIndex();
const std::string& GetName();
Bone* GetParent();
std::unique_ptr<DataChunk> GenerateRestPosiitonData(float scale, aiQuaternion rotation);
static Bone* FindCommonAncestor(Bone* a, Bone* b);
/**
* Assumes ancestor is an ancestor of decendant
* Returns the child bone of ancestor that is an ancestor
* of decendant or is decendant
*/
static Bone* StepDownTowards(Bone* ancestor, Bone* decendant);
static bool CompareBones(Bone* a, Bone* b);
// return -1 if a is null
static int GetBoneIndex(Bone* a);
private:
int mIndex;
std::string mName;
Bone* mParent;
aiVector3D mRestPosition;
aiQuaternion mRestRotation;
aiQuaternion mRestScale;
std::vector<Bone*> mChildren;
};
class BoneHierarchy {
public:
void SearchForBones(aiNode* node, Bone* currentBoneParent, std::set<std::string>& knownBones, bool parentIsBone);
void SearchForBonesInScene(const aiScene* scene);
Bone* BoneByIndex(unsigned index);
Bone* BoneForName(std::string name);
bool HasData() const;
unsigned int GetBoneCount() const;
void GenerateRestPosiitonData(CFileDefinition& fileDef, const std::string& variableName, float scale, aiQuaternion rotation);
private:
std::vector<std::unique_ptr<Bone>> mBones;
std::map<std::string, Bone*> mBoneByName;
};
#endif

View file

@ -0,0 +1,398 @@
#include "CFileDefinition.h"
#include <stdio.h>
#include <iostream>
#include "StringUtils.h"
#include "FileUtils.h"
#include <fstream>
VertexBufferDefinition::VertexBufferDefinition(std::shared_ptr<ExtendedMesh> targetMesh, std::string name, VertexType vertexType, int textureWidth, int textureHeight):
mTargetMesh(targetMesh),
mName(name),
mVertexType(vertexType),
mTextureWidth(textureWidth),
mTextureHeight(textureHeight) {
}
ErrorResult convertToShort(float value, short& output) {
int result = (int)floor(value + 0.5);
if (result < (int)std::numeric_limits<short>::min() || result > (int)std::numeric_limits<short>::max()) {
return ErrorResult("The value " + std::to_string(result) + " is too big to fit into a short");
}
output = (short)result;
return ErrorResult();
}
int convertNormalizedRange(float value) {
int result = (int)(value * 128.0f);
if (result < std::numeric_limits<char>::min()) {
return std::numeric_limits<char>::min();
} else if (result > std::numeric_limits<char>::max()) {
return std::numeric_limits<char>::max();
} else {
return (char)result;
}
}
unsigned convertByteRange(float value) {
int result = (int)(value * 256.0f);
if (result < std::numeric_limits<unsigned char>::min()) {
return std::numeric_limits<unsigned char>::min();
} else if (result > std::numeric_limits<unsigned char>::max()) {
return std::numeric_limits<unsigned char>::max();
} else {
return (unsigned char)result;
}
}
ErrorResult VertexBufferDefinition::Generate(float scale, aiQuaternion rotate, std::unique_ptr<FileDefinition>& output, const std::string& fileSuffix) {
std::unique_ptr<StructureDataChunk> dataChunk(new StructureDataChunk());
for (unsigned int i = 0; i < mTargetMesh->mMesh->mNumVertices; ++i) {
std::unique_ptr<StructureDataChunk> vertexWrapper(new StructureDataChunk());
std::unique_ptr<StructureDataChunk> vertex(new StructureDataChunk());
aiVector3D pos = mTargetMesh->mMesh->mVertices[i];
if (mTargetMesh->mPointInverseTransform[i]) {
pos = (*mTargetMesh->mPointInverseTransform[i]) * pos;
} else {
pos = rotate.Rotate(pos);
}
pos = pos * scale;
short converted;
std::unique_ptr<StructureDataChunk> posVertex(new StructureDataChunk());
ErrorResult code = convertToShort(pos.x, converted);
if (code.HasError()) return ErrorResult(code.GetMessage() + " for x coordinate");
posVertex->AddPrimitive(converted);
code = convertToShort(pos.y, converted);
if (code.HasError()) return ErrorResult(code.GetMessage() + " for y coordinate");
posVertex->AddPrimitive(converted);
code = convertToShort(pos.z, converted);
if (code.HasError()) return ErrorResult(code.GetMessage() + " for z coordinate");
posVertex->AddPrimitive(converted);
vertex->Add(std::move(posVertex));
vertex->AddPrimitive(0);
std::unique_ptr<StructureDataChunk> texCoords(new StructureDataChunk());
if (mTargetMesh->mMesh->mTextureCoords[0] == nullptr) {
texCoords->AddPrimitive(0);
texCoords->AddPrimitive(0);
} else {
aiVector3D uv = mTargetMesh->mMesh->mTextureCoords[0][i];
code = convertToShort(uv.x * mTextureWidth * (1 << 5), converted);
if (code.HasError()) return ErrorResult(code.GetMessage() + " for texture u coordinate");
texCoords->AddPrimitive(converted);
code = convertToShort((1.0f - uv.y) * mTextureHeight * (1 << 5), converted);
if (code.HasError()) return ErrorResult(code.GetMessage() + " for texture y coordinate");
texCoords->AddPrimitive(converted);
}
vertex->Add(std::move(texCoords));
std::unique_ptr<StructureDataChunk> vertexNormal(new StructureDataChunk());
switch (mVertexType) {
case VertexType::PosUVNormal:
if (mTargetMesh->mMesh->HasNormals()) {
aiVector3D normal = mTargetMesh->mMesh->mNormals[i];
if (mTargetMesh->mPointInverseTransform[i]) {
normal = (*mTargetMesh->mNormalInverseTransform[i]) * normal;
normal.Normalize();
} else {
normal = rotate.Rotate(normal);
}
vertexNormal->AddPrimitive(convertNormalizedRange(normal.x));
vertexNormal->AddPrimitive(convertNormalizedRange(normal.y));
vertexNormal->AddPrimitive(convertNormalizedRange(normal.z));
vertexNormal->AddPrimitive(255);
} else {
vertexNormal->AddPrimitive(0);
vertexNormal->AddPrimitive(0);
vertexNormal->AddPrimitive(0);
vertexNormal->AddPrimitive(255);
}
break;
case VertexType::PosUVColor:
if (mTargetMesh->mMesh->mColors[0] != nullptr) {
aiColor4D color = mTargetMesh->mMesh->mColors[0][i];
vertexNormal->AddPrimitive(color.r);
vertexNormal->AddPrimitive(color.g);
vertexNormal->AddPrimitive(color.b);
vertexNormal->AddPrimitive(color.a);
} else {
vertexNormal->AddPrimitive(0);
vertexNormal->AddPrimitive(0);
vertexNormal->AddPrimitive(0);
vertexNormal->AddPrimitive(255);
}
break;
}
vertex->Add(std::move(vertexNormal));
vertexWrapper->Add(std::move(vertex));
dataChunk->Add(std::move(vertexWrapper));
}
output = std::unique_ptr<FileDefinition>(new DataFileDefinition("Vtx", mName, true, fileSuffix, std::move(dataChunk)));
return ErrorResult();
}
CFileDefinition::CFileDefinition(std::string prefix, float modelScale, aiQuaternion modelRotate):
mPrefix(prefix),
mModelScale(modelScale),
mModelRotate(modelRotate) {
}
void CFileDefinition::AddDefinition(std::unique_ptr<FileDefinition> definition) {
if (definition->ForResource()) {
mResourceNames[definition->ForResource()] = definition->GetName();
}
mDefinitions.push_back(std::move(definition));
}
void CFileDefinition::AddMacro(const std::string& name, const std::string& value) {
mMacros.push_back(name + " " + value);
}
std::string CFileDefinition::GetVertexBuffer(std::shared_ptr<ExtendedMesh> mesh, VertexType vertexType, int textureWidth, int textureHeight, const std::string& modelSuffix) {
for (auto existing = mVertexBuffers.begin(); existing != mVertexBuffers.end(); ++existing) {
if (existing->second.mTargetMesh == mesh && existing->second.mVertexType == vertexType) {
return existing->first;
}
}
std::string requestedName;
if (mesh->mMesh->mName.length) {
requestedName = mesh->mMesh->mName.C_Str();
} else {
requestedName = "_mesh";
}
switch (vertexType) {
case VertexType::PosUVColor:
requestedName += "_color";
break;
case VertexType::PosUVNormal:
requestedName += "_normal";
break;
}
std::string name = GetUniqueName(requestedName);
mVertexBuffers.insert(std::pair<std::string, VertexBufferDefinition>(name, VertexBufferDefinition(
mesh,
name,
vertexType,
textureWidth,
textureHeight
)));
std::unique_ptr<FileDefinition> vtxDef;
ErrorResult result = mVertexBuffers.find(name)->second.Generate(mModelScale, mModelRotate, vtxDef, modelSuffix);
if (result.HasError()) {
std::cerr << "Error generating vertex buffer " << name << " error: " << result.GetMessage() << std::endl;
} else {
AddDefinition(std::move(vtxDef));
}
return name;
}
std::string CFileDefinition::GetCullingBuffer(const std::string& name, const aiVector3D& min, const aiVector3D& max, const std::string& modelSuffix) {
aiMesh* mesh = new aiMesh();
mesh->mName = name;
mesh->mNumVertices = 8;
mesh->mVertices = new aiVector3D[8];
mesh->mVertices[0] = aiVector3D(min.x, min.y, min.z);
mesh->mVertices[1] = aiVector3D(min.x, min.y, max.z);
mesh->mVertices[2] = aiVector3D(min.x, max.y, min.z);
mesh->mVertices[3] = aiVector3D(min.x, max.y, max.z);
mesh->mVertices[4] = aiVector3D(max.x, min.y, min.z);
mesh->mVertices[5] = aiVector3D(max.x, min.y, max.z);
mesh->mVertices[6] = aiVector3D(max.x, max.y, min.z);
mesh->mVertices[7] = aiVector3D(max.x, max.y, max.z);
BoneHierarchy boneHierarchy;
return GetVertexBuffer(std::shared_ptr<ExtendedMesh>(new ExtendedMesh(mesh, boneHierarchy)), VertexType::PosUVNormal, 0, 0, modelSuffix);
}
std::string CFileDefinition::GetUniqueName(std::string requestedName) {
std::string result = mPrefix + "_" + requestedName;
makeCCompatible(result);
int index = 1;
while (mUsedNames.find(result) != mUsedNames.end()) {
char strBuffer[8];
snprintf(strBuffer, 8, "_%d", index);
result = mPrefix + "_" + requestedName + strBuffer;
makeCCompatible(result);
++index;
}
mUsedNames.insert(result);
return result;
}
std::string CFileDefinition::GetMacroName(std::string requestedName) {
std::string result = GetUniqueName(requestedName);
std::transform(result.begin(), result.end(), result.begin(), ::toupper);
return result;
}
void CFileDefinition::GenerateAll(const std::string& headerFileLocation) {
std::set<std::string> keys;
for (auto fileDef = mDefinitions.begin(); fileDef != mDefinitions.end(); ++fileDef) {
keys.insert((*fileDef)->GetLocation());
}
std::string fileNoExtension = replaceExtension(headerFileLocation, "");
std::string fileNoPath = getBaseName(fileNoExtension);
for (auto key : keys) {
std::ofstream outputFile;
outputFile.open(fileNoExtension + key + ".c", std::ios_base::out | std::ios_base::trunc);
Generate(outputFile, key, fileNoPath + ".h");
outputFile.close();
}
std::ofstream outputHeader;
outputHeader.open(fileNoExtension + ".h", std::ios_base::out | std::ios_base::trunc);
GenerateHeader(outputHeader, fileNoPath);
outputHeader.close();
}
void CFileDefinition::Generate(std::ostream& output, const std::string& location, const std::string& headerFileName) {
output << "#include \"" << headerFileName << "\"" << std::endl;
for (auto it = mDefinitions.begin(); it != mDefinitions.end(); ++it) {
if ((*it)->GetLocation() == location) {
(*it)->Generate(output);
output << ";\n\n";
}
}
}
void CFileDefinition::GenerateHeader(std::ostream& output, const std::string& headerFileName) {
std::string infdef = std::string("__") + headerFileName + "_H__";
makeCCompatible(infdef);
std::transform(infdef.begin(), infdef.end(), infdef.begin(), ::toupper);
output << "#ifndef " << infdef << std::endl;
output << "#define " << infdef << std::endl;
output << std::endl;
std::set<std::string> includes;
for (auto it = mDefinitions.begin(); it != mDefinitions.end(); ++it) {
auto headers = (*it)->GetTypeHeaders();
for (auto header : headers) {
includes.insert(header);
}
}
std::vector<std::string> includesSorted(includes.size());
std::copy(includes.begin(), includes.end(), includesSorted.begin());
// std::sort(includes.begin(), includes.end());
for (auto include : includesSorted) {
output << "#include " << include << std::endl;
}
output << std::endl;
if (mMacros.size()) {
for (auto macro : mMacros) {
output << "#define " << macro << std::endl;
}
output << std::endl;
}
for (auto it = mDefinitions.begin(); it != mDefinitions.end(); ++it) {
(*it)->GenerateDeclaration(output);
output << ";\n";
};
output << std::endl;
output << "#endif" << std::endl;
}
bool CFileDefinition::HasDefinitions(const std::string& location) {
for (auto it = mDefinitions.begin(); it != mDefinitions.end(); ++it) {
if ((*it)->GetLocation() == location) {
return true;
}
}
return false;
}
bool CFileDefinition::GetResourceName(const void* resource, std::string& result) const {
auto it = mResourceNames.find(resource);
if (it != mResourceNames.end()) {
result = it->second;
return true;
}
return false;
}
std::shared_ptr<ExtendedMesh> CFileDefinition::GetExtendedMesh(aiMesh* mesh) {
auto it = mMeshes.find(mesh);
if (it != mMeshes.end()) {
return it->second;
}
std::shared_ptr<ExtendedMesh> result(new ExtendedMesh(mesh, mBoneHierarchy));
mMeshes[mesh] = result;
return result;
}

View file

@ -0,0 +1,68 @@
#ifndef _C_FILE_DEFINITION_H
#define _C_FILE_DEFINITION_H
#include <assimp/mesh.h>
#include <map>
#include <string>
#include <set>
#include <ostream>
#include <memory>
#include "./ErrorResult.h"
#include "./ExtendedMesh.h"
#include "./definitions/FileDefinition.h"
class VertexBufferDefinition {
public:
VertexBufferDefinition(std::shared_ptr<ExtendedMesh> targetMesh, std::string name, VertexType vertexType, int textureWidth, int textureHeight);
std::shared_ptr<ExtendedMesh> mTargetMesh;
std::string mName;
VertexType mVertexType;
int mTextureWidth;
int mTextureHeight;
ErrorResult Generate(float scale, aiQuaternion rotate, std::unique_ptr<FileDefinition>& output, const std::string& fileSuffix);
private:
};
class CFileDefinition {
public:
CFileDefinition(std::string prefix, float modelScale, aiQuaternion modelRotate);
void AddDefinition(std::unique_ptr<FileDefinition> definition);
void AddMacro(const std::string& name, const std::string& value);
std::string GetVertexBuffer(std::shared_ptr<ExtendedMesh> mesh, VertexType vertexType, int textureWidth, int textureHeight, const std::string& modelSuffix);
std::string GetCullingBuffer(const std::string& name, const aiVector3D& min, const aiVector3D& max, const std::string& modelSuffix);
std::string GetUniqueName(std::string requestedName);
std::string GetMacroName(std::string requestedName);
std::set<std::string> GetDefinitionTypes();
void GenerateAll(const std::string& headerFileLocation);
void Generate(std::ostream& output, const std::string& location, const std::string& headerFileName);
void GenerateHeader(std::ostream& output, const std::string& headerFileName);
bool HasDefinitions(const std::string& location);
bool GetResourceName(const void* resource, std::string& result) const;
std::shared_ptr<ExtendedMesh> GetExtendedMesh(aiMesh* mesh);
private:
std::string mPrefix;
float mModelScale;
aiQuaternion mModelRotate;
std::set<std::string> mUsedNames;
std::map<std::string, VertexBufferDefinition> mVertexBuffers;
std::vector<std::unique_ptr<FileDefinition>> mDefinitions;
std::vector<std::string> mMacros;
std::map<const void*, std::string> mResourceNames;
std::map<aiMesh*, std::shared_ptr<ExtendedMesh>> mMeshes;
BoneHierarchy mBoneHierarchy;
};
#endif

View file

@ -0,0 +1,132 @@
#include "CommandLineParser.h"
void parseEulerAngles(const std::string& input, aiVector3D& output) {
std::size_t firstComma = input.find(',');
std::size_t secondComma = input.find(',', firstComma + 1);
output.x = (float)atof(input.substr(0, firstComma).c_str());
output.y = (float)atof(input.substr(firstComma + 1, secondComma - (firstComma + 1)).c_str());
output.z = (float)atof(input.substr(secondComma + 1).c_str());
}
bool parseCommandLineArguments(int argc, char *argv[], struct CommandLineArguments& output) {
output.mInputFile = "";
output.mOutputFile = "";
output.mPrefix = "output";
output.mGraphicsScale = 256.0f;
output.mCollisionScale = 1.0f;
output.mExportAnimation = true;
output.mExportGeometry = true;
output.mIsLevel = false;
output.mIsLevelDef = false;
output.mEulerAngles = aiVector3D(0.0f, 0.0f, 0.0f);
char lastParameter = '\0';
bool hasError = false;
for (int i = 1; i < argc; ++i) {
char* curr = argv[i];
if (lastParameter != '\0') {
switch (lastParameter) {
case 'o':
if (output.mOutputFile != "") {
std::cerr << "You can only specify a single output file" << std::endl;
hasError = true;
}
output.mOutputFile = curr;
break;
case 'n':
output.mPrefix = curr;
break;
case 's':
output.mGraphicsScale = (float)atof(curr);
break;
case 'c':
output.mCollisionScale = (float)atof(curr);
break;
case 'm':
output.mMaterialFiles.push_back(curr);
break;
case 'M':
output.mMaterialOutput = curr;
break;
case 'r':
parseEulerAngles(curr, output.mEulerAngles);
break;
}
lastParameter = '\0';
} else if (
strcmp(curr, "-o") == 0 ||
strcmp(curr, "--output") == 0) {
lastParameter = 'o';
} else if (
strcmp(curr, "-n") == 0 ||
strcmp(curr, "--name") == 0) {
lastParameter = 'n';
} else if (
strcmp(curr, "-s") == 0 ||
strcmp(curr, "--scale") == 0) {
lastParameter = 's';
} else if (
strcmp(curr, "-c") == 0 ||
strcmp(curr, "--collision-scale") == 0) {
lastParameter = 'c';
} else if (
strcmp(curr, "-m") == 0 ||
strcmp(curr, "--materials") == 0) {
lastParameter = 'm';
} else if (
strcmp(curr, "-M") == 0 ||
strcmp(curr, "--material-output") == 0) {
lastParameter = 'M';
} else if (
strcmp(curr, "-r") == 0 ||
strcmp(curr, "--rotate") == 0) {
lastParameter = 'r';
} else if (
strcmp(curr, "-a") == 0 ||
strcmp(curr, "--animations-only") == 0) {
output.mExportGeometry = false;
} else if (
strcmp(curr, "-l") == 0 ||
strcmp(curr, "--level") == 0) {
output.mIsLevel = true;
output.mExportAnimation = false;
} else if (
strcmp(curr, "-d") == 0 ||
strcmp(curr, "--level-def") == 0) {
output.mIsLevelDef = true;
output.mExportAnimation = false;
} else {
if (curr[0] == '-') {
hasError = true;
std::cerr << "Unrecognized argument '" << curr << '"' << std::endl;
} else if (output.mInputFile == "") {
output.mInputFile = curr;
} else {
hasError = true;
std::cerr << "Only one input file allowed. " <<
"Already gave '" << output.mInputFile << "'" <<
". And then got '" << curr << "'" << std::endl;
}
}
}
if ((output.mInputFile == "" || (output.mOutputFile == "" && !output.mIsLevelDef)) && output.mMaterialOutput == "") {
std::cerr << "Input and output file are both required" << std::endl;
hasError = true;
}
if (hasError) {
std::cerr << "usage " << argv[0] << " [ARGS] -o [output-file] [input-file]" << std::endl;
}
if (output.mOutputFile.length() > 2 && output.mOutputFile.substr(output.mOutputFile.length() - 2) == ".h") {
output.mOutputFile = output.mOutputFile.substr(0, output.mOutputFile.length() - 2);
}
return !hasError;
}

View file

@ -0,0 +1,27 @@
#ifndef _COMMAND_LINE_PARSER_H
#define _COMMAND_LINE_PARSER_H
#include <assimp/mesh.h>
#include <string>
#include <vector>
#include <string.h>
#include <iostream>
struct CommandLineArguments {
std::string mInputFile;
std::string mOutputFile;
std::string mMaterialOutput;
std::string mPrefix;
std::vector<std::string> mMaterialFiles;
float mGraphicsScale;
float mCollisionScale;
bool mExportAnimation;
bool mExportGeometry;
bool mIsLevel;
bool mIsLevelDef;
aiVector3D mEulerAngles;
};
bool parseCommandLineArguments(int argc, char *argv[], struct CommandLineArguments& output);
#endif

View file

@ -0,0 +1,238 @@
#include "./DisplayList.h"
#include "./CFileDefinition.h"
#include <sstream>
DisplayListCommand::DisplayListCommand(DisplayListCommandType type): mType(type) {}
DisplayListCommand::~DisplayListCommand() {}
CommentCommand::CommentCommand(std::string comment):
DisplayListCommand(DisplayListCommandType::COMMENT),
mComment(comment) {
}
std::unique_ptr<DataChunk> CommentCommand::GenerateCommand() {
return std::unique_ptr<DataChunk>(new CommentDataChunk(mComment));
}
RawContentCommand::RawContentCommand(std::string content):
DisplayListCommand(DisplayListCommandType::RAW),
mContent(content) {
}
std::unique_ptr<DataChunk> RawContentCommand::GenerateCommand() {
return std::unique_ptr<DataChunk>(new PrimitiveDataChunk<std::string>(mContent));
}
VTXCommand::VTXCommand(
int numVerts,
int indexBufferStart,
std::string vertexBuffer,
int vertexBufferOffset
) :
DisplayListCommand(DisplayListCommandType::G_VTX),
mNumVerts(numVerts),
mIndexBufferStart(indexBufferStart),
mVertexBuffer(vertexBuffer),
mVertexBufferOffset(vertexBufferOffset) {
}
std::unique_ptr<DataChunk> VTXCommand::GenerateCommand() {
std::unique_ptr<MacroDataChunk> result(new MacroDataChunk("gsSPVertex"));
std::string vertexIndex = std::string("&") +
mVertexBuffer +
"[" + std::to_string(mVertexBufferOffset) + "]";
result->AddPrimitive(vertexIndex);
result->AddPrimitive(mNumVerts);
result->AddPrimitive(mIndexBufferStart);
return std::move(result);
}
TRI1Command::TRI1Command(int a, int b, int c) :
DisplayListCommand(DisplayListCommandType::G_TRI1),
mA(a),
mB(b),
mC(c) {
}
std::unique_ptr<DataChunk> TRI1Command::GenerateCommand() {
std::unique_ptr<MacroDataChunk> result(new MacroDataChunk("gsSP1Triangle"));
result->AddPrimitive(mA);
result->AddPrimitive(mB);
result->AddPrimitive(mC);
result->AddPrimitive(0);
return std::move(result);
}
TRI2Command::TRI2Command(int a0, int b0, int c0, int a1, int b1, int c1) :
DisplayListCommand(DisplayListCommandType::G_TRI2),
mA0(a0),
mB0(b0),
mC0(c0),
mA1(a1),
mB1(b1),
mC1(c1) {
}
std::unique_ptr<DataChunk> TRI2Command::GenerateCommand() {
std::unique_ptr<MacroDataChunk> result(new MacroDataChunk("gsSP2Triangles"));
result->AddPrimitive(mA0);
result->AddPrimitive(mB0);
result->AddPrimitive(mC0);
result->AddPrimitive(0);
result->AddPrimitive(mA1);
result->AddPrimitive(mB1);
result->AddPrimitive(mC1);
result->AddPrimitive(0);
return std::move(result);
}
CallDisplayListByNameCommand::CallDisplayListByNameCommand(const std::string& dlName):
DisplayListCommand(DisplayListCommandType::G_DL),
mDLName(dlName) {
}
std::unique_ptr<DataChunk> CallDisplayListByNameCommand::GenerateCommand() {
std::unique_ptr<MacroDataChunk> result(new MacroDataChunk("gsSPDisplayList"));
result->AddPrimitive(mDLName);
return std::move(result);
}
PushMatrixCommand::PushMatrixCommand(unsigned int matrixOffset, bool replace):
DisplayListCommand(DisplayListCommandType::G_MTX),
mMatrixOffset(matrixOffset),
mReplace(replace) {
}
std::unique_ptr<DataChunk> PushMatrixCommand::GenerateCommand() {
std::unique_ptr<MacroDataChunk> result(new MacroDataChunk("gsSPMatrix"));
std::string matrixSegment = std::string("(Mtx*)MATRIX_TRANSFORM_SEGMENT_ADDRESS + ") + std::to_string(mMatrixOffset);
result->AddPrimitive(matrixSegment);
std::string flags = "G_MTX_MODELVIEW | G_MTX_MUL | ";
if (mReplace) {
flags += "G_MTX_NOPUSH";
} else {
flags += "G_MTX_PUSH";
}
return std::move(result);
}
PopMatrixCommand::PopMatrixCommand(unsigned int popCount):
DisplayListCommand(DisplayListCommandType::G_POPMTX),
mPopCount(popCount) {
}
std::unique_ptr<DataChunk> PopMatrixCommand::GenerateCommand() {
if (mPopCount == 0) {
return std::unique_ptr<DataChunk>(new DataChunkNop());
}
if (mPopCount > 1) {
std::unique_ptr<MacroDataChunk> result(new MacroDataChunk("gsSPPopMatrixN"));
result->AddPrimitive<const char*>("G_MTX_MODELVIEW");
result->AddPrimitive(mPopCount);
return std::move(result);
}
std::unique_ptr<MacroDataChunk> result(new MacroDataChunk("gsSPPopMatrix"));
result->AddPrimitive<const char*>("G_MTX_MODELVIEW");
return std::move(result);
}
std::string generateGeometryMode(GeometryMode mode) {
std::string result = "";
return result;
}
ChangeGeometryMode::ChangeGeometryMode(GeometryMode clear, GeometryMode set):
DisplayListCommand(DisplayListCommandType::G_GEOMETRYMODE),
mClear(clear),
mSet(set) {
}
std::unique_ptr<DataChunk> ChangeGeometryMode::GenerateCommand() {
std::unique_ptr<MacroDataChunk> result(new MacroDataChunk("gsSPGeometryMode"));
result->AddPrimitive(generateGeometryMode(mClear));
result->AddPrimitive(generateGeometryMode(mSet));
return std::move(result);
}
CullDisplayList::CullDisplayList(unsigned int vertexCount):
DisplayListCommand(DisplayListCommandType::G_CULLDL),
mVertexCount(vertexCount) {
}
std::unique_ptr<DataChunk> CullDisplayList::GenerateCommand() {
std::unique_ptr<MacroDataChunk> result(new MacroDataChunk("gsSPCullDisplayList"));
result->AddPrimitive(mVertexCount - 1);
return std::move(result);
}
DisplayList::DisplayList(std::string name):
mName(name),
mDataChunk(new StructureDataChunk()) {
}
void DisplayList::AddCommand(std::unique_ptr<DisplayListCommand> command) {
mDataChunk->Add(std::move(command->GenerateCommand()));
}
StructureDataChunk& DisplayList::GetDataChunk() {
return *mDataChunk;
}
const std::string& DisplayList::GetName() {
return mName;
}
std::unique_ptr<FileDefinition> DisplayList::Generate(const std::string& fileSuffix) {
mDataChunk->Add(std::unique_ptr<DataChunk>(new MacroDataChunk("gsSPEndDisplayList")));
std::unique_ptr<FileDefinition> result(new DataFileDefinition(
std::string("Gfx"),
mName,
true,
fileSuffix,
std::move(mDataChunk)
));
result->AddTypeHeader("<ultra64.h>");
return result;
}

View file

@ -0,0 +1,153 @@
#ifndef _DISPLAY_LIST_H
#define _DISPLAY_LIST_H
#include <memory>
#include <vector>
#include <string>
#include <iostream>
#include "./definitions/FileDefinition.h"
#include "./materials/MaterialEnums.h"
enum class DisplayListCommandType {
COMMENT,
RAW,
G_VTX,
G_TRI1,
G_TRI2,
G_MTX,
G_POPMTX,
G_DL,
G_GEOMETRYMODE,
G_CULLDL,
};
class DisplayList;
class CFileDefinition;
struct DisplayListCommand {
DisplayListCommand(DisplayListCommandType type);
virtual ~DisplayListCommand();
DisplayListCommandType mType;
virtual std::unique_ptr<DataChunk> GenerateCommand() = 0;
};
struct CommentCommand : DisplayListCommand {
CommentCommand(std::string comment);
std::string mComment;
std::unique_ptr<DataChunk> GenerateCommand();
};
struct RawContentCommand : DisplayListCommand {
RawContentCommand(std::string content);
std::string mContent;
std::unique_ptr<DataChunk> GenerateCommand();
};
struct VTXCommand : DisplayListCommand {
VTXCommand(
int numVerts,
int indexBufferStart,
std::string vertexBuffer,
int vertexBufferOffset
);
int mNumVerts;
int mIndexBufferStart;
std::string mVertexBuffer;
int mVertexBufferOffset;
std::unique_ptr<DataChunk> GenerateCommand();
};
struct TRI1Command : DisplayListCommand {
TRI1Command(int a, int b, int c);
int mA;
int mB;
int mC;
std::unique_ptr<DataChunk> GenerateCommand();
};
struct TRI2Command : DisplayListCommand {
TRI2Command(int a0, int b0, int c0, int a1, int b1, int c1);
int mA0;
int mB0;
int mC0;
int mA1;
int mB1;
int mC1;
std::unique_ptr<DataChunk> GenerateCommand();
};
struct CallDisplayListCommand {
CallDisplayListCommand(int targetDL, int offset);
int mTargetDL;
int mOffset;
std::unique_ptr<DataChunk> GenerateCommand();
};
struct CallDisplayListByNameCommand : DisplayListCommand {
CallDisplayListByNameCommand(const std::string& dlName);
std::string mDLName;
std::unique_ptr<DataChunk> GenerateCommand();
};
struct PushMatrixCommand : DisplayListCommand {
PushMatrixCommand(unsigned int matrixOffset, bool replace);
std::unique_ptr<DataChunk> GenerateCommand();
unsigned int mMatrixOffset;
bool mReplace;
};
struct PopMatrixCommand : DisplayListCommand {
PopMatrixCommand(unsigned int popCount);
std::unique_ptr<DataChunk> GenerateCommand();
unsigned int mPopCount;
};
struct ChangeGeometryMode : DisplayListCommand {
ChangeGeometryMode(GeometryMode clear, GeometryMode set);
std::unique_ptr<DataChunk> GenerateCommand();
GeometryMode mClear;
GeometryMode mSet;
};
struct CullDisplayList : DisplayListCommand {
CullDisplayList(unsigned int vertexCount);
std::unique_ptr<DataChunk> GenerateCommand();
unsigned int mVertexCount;
};
class DisplayList {
public:
DisplayList(std::string name);
void AddCommand(std::unique_ptr<DisplayListCommand> command);
StructureDataChunk& GetDataChunk();
const std::string& GetName();
std::unique_ptr<FileDefinition> Generate(const std::string& fileSuffix);
private:
std::string mName;
std::unique_ptr<StructureDataChunk> mDataChunk;
};
#endif

View file

@ -0,0 +1,139 @@
#include <set>
#include <vector>
#include <algorithm>
#include <map>
#include "./DisplayListGenerator.h"
bool doesFaceFit(std::set<int>& indices, aiFace* face, unsigned int maxVertices) {
unsigned int misses = 0;
for (unsigned int i = 0; i < face->mNumIndices; ++i) {
if (indices.find(face->mIndices[i]) == indices.end()) {
++misses;
}
}
return indices.size() + misses <= maxVertices;
}
void flushVertices(RenderChunk& chunk, std::set<int>& currentVertices, std::vector<aiFace*>& currentFaces, RCPState& state, std::string vertexBuffer, DisplayList& output, bool hasTri2) {
std::vector<int> verticesAsVector(currentVertices.begin(), currentVertices.end());
std::sort(verticesAsVector.begin(), verticesAsVector.end(),
[=](int a, int b) -> bool {
Bone* boneA = chunk.mMesh->mVertexBones[a];
Bone* boneB = chunk.mMesh->mVertexBones[b];
if (boneA != boneB && (boneA == chunk.mBonePair.first || boneB == chunk.mBonePair.second)) {
return true;
}
return a < b;
});
VertexData vertexData[MAX_VERTEX_CACHE_SIZE];
for (unsigned int vertexIndex = 0; vertexIndex < verticesAsVector.size() && vertexIndex < MAX_VERTEX_CACHE_SIZE; ++vertexIndex) {
vertexData[vertexIndex] = VertexData(vertexBuffer, verticesAsVector[vertexIndex], -1);
}
unsigned int cacheLocation[MAX_VERTEX_CACHE_SIZE];
state.AssignSlots(vertexData, cacheLocation, verticesAsVector.size());
int lastVertexIndex = -1;
int lastCacheLocation = MAX_VERTEX_CACHE_SIZE;
int vertexCount = 0;
Bone* lastBone = nullptr;
std::map<int, int> vertexMapping;
for (unsigned int index = 0; index <= verticesAsVector.size(); ++index) {
int vertexIndex;
int cacheIndex;
Bone* bone = nullptr;
if (index < verticesAsVector.size()) {
vertexIndex = verticesAsVector[index];
cacheIndex = cacheLocation[index];
vertexMapping[vertexIndex] = cacheIndex;
bone = chunk.mMesh->mVertexBones[vertexIndex];
}
if (index == verticesAsVector.size() ||
(index != 0 && (
vertexIndex != lastVertexIndex + 1 ||
cacheIndex != lastCacheLocation + 1 || bone != lastBone
))) {
state.TraverseToBone(lastBone, output);
output.AddCommand(std::unique_ptr<DisplayListCommand>(new VTXCommand(
vertexCount,
lastCacheLocation + 1 - vertexCount,
vertexBuffer,
lastVertexIndex + 1 - vertexCount
)));
vertexCount = 1;
} else {
++vertexCount;
}
lastVertexIndex = vertexIndex;
lastCacheLocation = cacheIndex;
lastBone = bone;
}
for (unsigned int faceIndex = 0; faceIndex < currentFaces.size(); ++faceIndex) {
if (hasTri2 && faceIndex + 1 < currentFaces.size()) {
aiFace* currFace = currentFaces[faceIndex + 0];
aiFace* nextFace = currentFaces[faceIndex + 1];
output.AddCommand(std::unique_ptr<DisplayListCommand>(new TRI2Command(
vertexMapping.at(currFace->mIndices[0]), vertexMapping.at(currFace->mIndices[1]), vertexMapping.at(currFace->mIndices[2]),
vertexMapping.at(nextFace->mIndices[0]), vertexMapping.at(nextFace->mIndices[1]), vertexMapping.at(nextFace->mIndices[2])
)));
++faceIndex;
} else {
aiFace* currFace = currentFaces[faceIndex + 0];
output.AddCommand(std::unique_ptr<DisplayListCommand>(new TRI1Command(
vertexMapping.at(currFace->mIndices[0]), vertexMapping.at(currFace->mIndices[1]), vertexMapping.at(currFace->mIndices[2])
)));
}
}
}
void generateCulling(DisplayList& output, std::string vertexBuffer, bool renableLighting) {
output.AddCommand(std::unique_ptr<DisplayListCommand>(new ChangeGeometryMode(GeometryMode::G_LIGHTING, GeometryMode::None)));
output.AddCommand(std::unique_ptr<DisplayListCommand>(new VTXCommand(8, 0, vertexBuffer, 0)));
output.AddCommand(std::unique_ptr<DisplayListCommand>(new CullDisplayList(8)));
if (renableLighting) {
output.AddCommand(std::unique_ptr<DisplayListCommand>(new ChangeGeometryMode(GeometryMode::None, GeometryMode::G_LIGHTING)));
}
}
void generateGeometry(RenderChunk& chunk, RCPState& state, std::string vertexBuffer, DisplayList& output, bool hasTri2) {
std::set<int> currentVertices;
std::vector<aiFace*> currentFaces;
const std::vector<aiFace*>& faces = chunk.GetFaces();
for (unsigned int faceIndex = 0; faceIndex <= faces.size(); ++faceIndex) {
if (faceIndex == faces.size() || !doesFaceFit(currentVertices, faces[faceIndex], state.GetMaxVertices())) {
flushVertices(chunk, currentVertices, currentFaces, state, vertexBuffer, output, hasTri2);
currentVertices.clear();
currentFaces.clear();
if (faceIndex == faces.size()) {
break;
}
}
for (unsigned int vertexIndex = 0; vertexIndex < faces[faceIndex]->mNumIndices; ++vertexIndex) {
currentVertices.insert(faces[faceIndex]->mIndices[vertexIndex]);
}
currentFaces.push_back(faces[faceIndex]);
}
}

View file

@ -0,0 +1,13 @@
#ifndef _DISPLAY_LIST_GENERATOR_H
#define _DISPLAY_LIST_GENERATOR_H
#include <assimp/mesh.h>
#include "./RCPState.h"
#include "./DisplayList.h"
#include "./CFileDefinition.h"
#include "./RenderChunk.h"
void generateCulling(DisplayList& output, std::string vertexBuffer, bool renableLighting);
void generateGeometry(RenderChunk& mesh, RCPState& state, std::string vertexBuffer, DisplayList& output, bool hasTri2);
#endif

View file

@ -0,0 +1,25 @@
#include "DisplayListSettings.h"
#include "RCPState.h"
DisplayListSettings::DisplayListSettings():
mPrefix(""),
mVertexCacheSize(MAX_VERTEX_CACHE_SIZE),
mHasTri2(true),
mGraphicsScale(256.0f),
mCollisionScale(1.0f),
mMaxMatrixDepth(10),
mCanPopMultipleMatrices(true),
mTicksPerSecond(30),
mExportAnimation(true),
mExportGeometry(true),
mIncludeCulling(true) {
}
aiMatrix4x4 DisplayListSettings::CreateGlobalTransform() {
aiMatrix4x4 scale;
aiMatrix4x4::Scaling(aiVector3D(1, 1, 1) * mGraphicsScale, scale);
aiMatrix4x4 rotation(mRotateModel.GetMatrix());
return rotation * scale;
}

View file

@ -0,0 +1,31 @@
#ifndef _DISPLAY_LIST_SETTINGS_H
#define _DISPLAY_LIST_SETTINGS_H
#include <string>
#include <map>
#include <assimp/scene.h>
#include <memory>
#include "./materials/Material.h"
#include "./materials/MaterialState.h"
struct DisplayListSettings {
DisplayListSettings();
std::string mPrefix;
int mVertexCacheSize;
bool mHasTri2;
float mGraphicsScale;
float mCollisionScale;
int mMaxMatrixDepth;
bool mCanPopMultipleMatrices;
unsigned short mTicksPerSecond;
std::map<std::string, std::shared_ptr<Material>> mMaterials;
MaterialState mDefaultMaterialState;
aiQuaternion mRotateModel;
bool mExportAnimation;
bool mExportGeometry;
bool mIncludeCulling;
aiMatrix4x4 CreateGlobalTransform();
};
#endif

0
skelatool64/src/Enum.h Normal file
View file

View file

@ -0,0 +1,10 @@
#ifndef _ERROR_CODE_H
#define _ERROR_CODE_H
enum class ErrorCode {
None,
ModelTooLarge,
MatrixStackOverflow,
};
#endif

View file

@ -0,0 +1,13 @@
#include "ErrorResult.h"
ErrorResult::ErrorResult() : mMessage("") {}
ErrorResult::ErrorResult(const std::string& message) : mMessage(message) {}
bool ErrorResult::HasError() const {
return mMessage.length() > 0;
}
const std::string& ErrorResult::GetMessage() const {
return mMessage;
}

View file

@ -0,0 +1,17 @@
#ifndef __ERROR_RESULT_H__
#define __ERROR_RESULT_H__
#include <string>
class ErrorResult {
public:
ErrorResult();
ErrorResult(const std::string& message);
bool HasError() const;
const std::string& GetMessage() const;
private:
std::string mMessage;
};
#endif

View file

@ -0,0 +1,320 @@
#include "ExtendedMesh.h"
#include <algorithm>
#include "MathUtl.h"
aiMesh* copyMesh(aiMesh* mesh) {
aiMesh* result = new aiMesh();
result->mNumVertices = mesh->mNumVertices;
result->mVertices = new aiVector3D[result->mNumVertices];
std::copy(mesh->mVertices, mesh->mVertices + result->mNumVertices, result->mVertices);
if (mesh->mNormals) {
result->mNormals = new aiVector3D[result->mNumVertices];
std::copy(mesh->mNormals, mesh->mNormals + result->mNumVertices, result->mNormals);
}
result->mMaterialIndex = mesh->mMaterialIndex;
result->mNumFaces = mesh->mNumFaces;
result->mFaces = new aiFace[mesh->mNumFaces];
std::copy(mesh->mFaces, mesh->mFaces + result->mNumFaces, result->mFaces);
for (int i = 0; i < 8; ++i) {
if (mesh->mTextureCoords[i]) {
result->mTextureCoords[i] = new aiVector3D[result->mNumVertices];
result->mNumUVComponents[i] = mesh->mNumUVComponents[i];
std::copy(mesh->mTextureCoords[i], mesh->mTextureCoords[i] + result->mNumVertices, result->mTextureCoords[i]);
}
}
return result;
}
ExtendedMesh::ExtendedMesh(const ExtendedMesh& other):
mMesh(copyMesh(other.mMesh)),
mPointInverseTransform(other.mPointInverseTransform),
mNormalInverseTransform(other.mNormalInverseTransform),
mVertexBones(other.mVertexBones),
mFacesForBone(other.mFacesForBone),
bbMin(other.bbMin),
bbMax(other.bbMax) {
for (auto& it : mFacesForBone) {
std::vector<aiFace*> faces;
for (auto face : it.second) {
faces.push_back(mMesh->mFaces + (face - other.mMesh->mFaces));
}
mFacesForBone[it.first] = faces;
}
for (auto& it : mBoneSpanningFaces) {
std::vector<aiFace*> faces;
for (auto face : it.second) {
faces.push_back(mMesh->mFaces + (face - other.mMesh->mFaces));
}
mBoneSpanningFaces[it.first] = faces;
}
}
ExtendedMesh::ExtendedMesh(aiMesh* mesh, BoneHierarchy& boneHierarchy) :
mMesh(mesh) {
mVertexBones.resize(mMesh->mNumVertices);
mPointInverseTransform.resize(mMesh->mNumVertices);
mNormalInverseTransform.resize(mMesh->mNumVertices);
std::set<Bone*> bonesAsSet;
for (unsigned int boneIndex = 0; boneIndex < mMesh->mNumBones; ++boneIndex) {
aiBone* bone = mMesh->mBones[boneIndex];
Bone* hierarchyBone = boneHierarchy.BoneForName(bone->mName.C_Str());
bonesAsSet.insert(hierarchyBone);
aiMatrix3x3 normalTransform(bone->mOffsetMatrix);
normalTransform = normalTransform.Transpose().Inverse();
for (unsigned int vertexIndex = 0; vertexIndex < bone->mNumWeights; ++vertexIndex) {
unsigned int vertexId = bone->mWeights[vertexIndex].mVertexId;
mVertexBones[vertexId] = hierarchyBone;
mPointInverseTransform[vertexId] = &bone->mOffsetMatrix;
mNormalInverseTransform[vertexId] = new aiMatrix3x3(normalTransform);
}
}
PopulateFacesForBone();
RecalcBB();
}
ExtendedMesh::~ExtendedMesh() {
for (unsigned int i = 0; i < mNormalInverseTransform.size(); ++i) {
if (mNormalInverseTransform[i]) {
delete mNormalInverseTransform[i];
}
}
}
void ExtendedMesh::RecalcBB() {
bbMin = mMesh->mVertices[0];
bbMax = mMesh->mVertices[0];
for (unsigned i = 1; i < mMesh->mNumVertices; ++i) {
bbMin = min(bbMin, mMesh->mVertices[i]);
bbMax = max(bbMax, mMesh->mVertices[i]);
}
}
bool ExtendedMesh::isFaceOneBone(aiFace* face) {
Bone* bone = mVertexBones[face->mIndices[0]];
for (unsigned int i = 1; i < face->mNumIndices; ++i) {
if (mVertexBones[face->mIndices[i]] != bone) {
return false;
}
}
return true;
}
std::pair<Bone*, Bone*> ExtendedMesh::findTransitionPairForFace(aiFace* face) {
Bone* ancestor = mVertexBones[face->mIndices[0]];
for (unsigned int i = 1; i < face->mNumIndices; ++i) {
ancestor = Bone::FindCommonAncestor(ancestor, mVertexBones[face->mIndices[i]]);
}
Bone* second = ancestor;
for (unsigned int i = 0; i < face->mNumIndices; ++i) {
if (mVertexBones[face->mIndices[i]] != ancestor) {
second = Bone::StepDownTowards(ancestor, mVertexBones[face->mIndices[i]]);
break;
}
}
return std::make_pair(ancestor, second);
}
void ExtendedMesh::PopulateFacesForBone() {
for (unsigned int faceIndex = 0; faceIndex < mMesh->mNumFaces; ++faceIndex) {
aiFace* face = &mMesh->mFaces[faceIndex];
if (isFaceOneBone(face)) {
mFacesForBone[mVertexBones[face->mIndices[0]]].push_back(face);
} else {
mBoneSpanningFaces[findTransitionPairForFace(face)].push_back(face);
}
}
}
std::shared_ptr<ExtendedMesh> ExtendedMesh::Transform(const aiMatrix4x4& transform) const {
std::shared_ptr<ExtendedMesh> result(new ExtendedMesh(*this));
aiMatrix3x3 rotationOnly(transform);
for (unsigned i = 0; i < result->mMesh->mNumVertices; ++i) {
result->mMesh->mVertices[i] = transform * result->mMesh->mVertices[i];
if (result->mMesh->mNormals) {
result->mMesh->mNormals[i] = rotationOnly * result->mMesh->mNormals[i];
result->mMesh->mNormals[i].NormalizeSafe();
}
}
result->RecalcBB();
return result;
}
void ExtendedMesh::ReplaceColor(const aiColor4D& color) {
if (mMesh->mColors[0]) {
delete [] mMesh->mColors[0];
}
mMesh->mColors[0] = new aiColor4D[mMesh->mNumVertices];
for (unsigned i = 0; i < mMesh->mNumVertices; ++i) {
mMesh->mColors[0][i] = color;
}
}
void getMeshFaceGroups(aiMesh* mesh, std::vector<std::shared_ptr<std::set<aiFace*>>>& result) {
result.clear();
std::map<int, int> indexToGroup;
for (unsigned faceIndex = 0; faceIndex < mesh->mNumFaces; ++faceIndex) {
aiFace* face = &mesh->mFaces[faceIndex];
int currentGroup = -1;
for (unsigned indexIndex = 0; indexIndex < face->mNumIndices; ++indexIndex) {
unsigned index = face->mIndices[indexIndex];
auto indexToGroupFind = indexToGroup.find(index);
int indexGroup = indexToGroupFind == indexToGroup.end() ? -1 : indexToGroupFind->second;
if (indexGroup == -1) {
if (currentGroup == -1) {
indexGroup = result.size();
currentGroup = indexGroup;
result.push_back(std::shared_ptr<std::set<aiFace*>>(new std::set<aiFace*>()));
} else {
indexGroup = currentGroup;
}
indexToGroup[index] = indexGroup;
}
if (currentGroup == -1) {
currentGroup = indexGroup;
}
auto currentGroupSet = result[currentGroup];
auto indexGroupSet = result[indexGroup];
if (currentGroupSet != indexGroupSet) {
// merge both the groups
for (auto face : *indexGroupSet) {
currentGroupSet->insert(face);
}
// have both group numbers point to the same group
result[indexGroup] = currentGroupSet;
}
// add current face to group
currentGroupSet->insert(face);
}
}
std::sort(result.begin(), result.end());
result.erase(std::unique(result.begin(), result.end()), result.end());
}
void cubeProjectSingleFace(aiMesh* mesh, std::set<aiFace*>& faces, double sTile, double tTile) {
aiVector3D normal;
for (auto face : faces) {
for (unsigned i = 0; i < face->mNumIndices; ++i) {
normal += mesh->mNormals[face->mIndices[i]];
}
}
normal.NormalizeSafe();
aiVector3D left;
aiVector3D up;
float minLeft = 10000000000.0f;
float minUp = 10000000000.0f;
if (fabs(normal.y) > 0.7) {
up = aiVector3D(0.0f, 0.0f, 1.0f);
left = aiVector3D(1.0f, 0.0f, 0.0f);
} else if (fabs(normal.z) > 0.7) {
up = aiVector3D(0.0f, 1.0f, 0.0f);
left = aiVector3D(1.0f, 0.0f, 0.0f);
} else {
up = aiVector3D(0.0f, 1.0f, 0.0f);
left = aiVector3D(0.0f, 0.0f, 1.0f);
}
for (auto face : faces) {
for (unsigned i = 0; i < face->mNumIndices; ++i) {
aiVector3D vertex = mesh->mVertices[face->mIndices[i]];
minLeft = std::min(minLeft, vertex * left);
minUp = std::min(minUp, vertex * up);
}
}
for (auto face : faces) {
for (unsigned i = 0; i < face->mNumIndices; ++i) {
int index = face->mIndices[i];
aiVector3D vertex = mesh->mVertices[index];
float sCoord = vertex * left - minLeft;
float tCoord = vertex * up - minUp;
mesh->mTextureCoords[0][index] = aiVector3D(sCoord * sTile, tCoord * tTile, 0.0f);
}
}
}
void ExtendedMesh::CubeProjectTex(double sTile, double tTile) {
if (!mMesh->mTextureCoords[0]) {
mMesh->mNumUVComponents[0] = 2;
mMesh->mTextureCoords[0] = new aiVector3D[mMesh->mNumVertices];
}
std::vector<std::shared_ptr<std::set<aiFace*>>> faceGroups;
getMeshFaceGroups(mMesh, faceGroups);
for (auto group : faceGroups) {
cubeProjectSingleFace(mMesh, *group.get(), sTile, tTile);
}
}
void findAdjacentVertices(aiMesh* mesh, unsigned fromIndex, std::set<int>& result) {
for (unsigned faceIndex = 0; faceIndex < mesh->mNumFaces; ++faceIndex) {
aiFace* face = &mesh->mFaces[faceIndex];
for (unsigned index = 0; index < face->mNumIndices; ++index) {
if (face->mIndices[index] == fromIndex) {
result.insert(face->mIndices[(index + 1) % face->mNumIndices]);
result.insert(face->mIndices[(index + face->mNumIndices - 1) % face->mNumIndices]);
break;
}
}
}
}
std::string ExtendedMesh::GetMaterialName(aiMaterial* material) {
aiString name;
material->Get(AI_MATKEY_NAME, name);
return name.C_Str();
}

View file

@ -0,0 +1,48 @@
#ifndef _EXTENDED_MESH_H
#define _EXTENDED_MESH_H
#include <assimp/mesh.h>
#include "BoneHierarchy.h"
#include <vector>
#include <map>
#include <set>
enum class VertexType {
PosUVNormal,
PosUVColor,
};
class ExtendedMesh {
public:
ExtendedMesh(const ExtendedMesh& other);
ExtendedMesh(aiMesh* mesh, BoneHierarchy& boneHierarchy);
~ExtendedMesh();
aiMesh* mMesh;
std::vector<aiMatrix4x4*> mPointInverseTransform;
std::vector<aiMatrix3x3*> mNormalInverseTransform;
std::vector<Bone*> mVertexBones;
std::map<Bone*, std::vector<aiFace*>> mFacesForBone;
// first bone in pair is always the parent of the second
std::map<std::pair<Bone*, Bone*>, std::vector<aiFace*>> mBoneSpanningFaces;
aiVector3D bbMin;
aiVector3D bbMax;
void RecalcBB();
std::shared_ptr<ExtendedMesh> Transform(const aiMatrix4x4& transform) const;
void ReplaceColor(const aiColor4D& color);
void CubeProjectTex(double sTile, double tTile);
bool isFaceOneBone(aiFace* face);
std::pair<Bone*, Bone*> findTransitionPairForFace(aiFace* face);
static std::string GetMaterialName(aiMaterial* material);
private:
void PopulateFacesForBone();
};
aiMesh* copyMesh(aiMesh* mesh);
void findAdjacentVertices(aiMesh* mesh, unsigned fromIndex, std::set<int>& result);
#endif

View file

@ -0,0 +1,215 @@
#include "FileUtils.h"
#include <math.h>
#include <vector>
#include <sstream>
#include <iostream>
#include <unistd.h>
#include <fstream>
std::string gCwd;
#define MAX_PATH 256
const std::string& GetCwd() {
if (!gCwd.length()) {
char buffer[MAX_PATH];
if (getcwd(buffer, MAX_PATH)) {
gCwd = buffer;
}
}
return gCwd;
}
bool isPathCharacter(char chr) {
return chr == '\\' || chr == '/';
}
std::string replaceExtension(const std::string& input, const std::string& newExt) {
std::size_t extPos = input.rfind('.');
if (extPos == std::string::npos) {
return input + newExt;
} else {
return input.substr(0, extPos) + newExt;
}
}
std::string getBaseName(const std::string& input) {
std::size_t pathPos = input.rfind('/');
std::size_t wrongPathPos = input.rfind('\\');
if (wrongPathPos != std::string::npos && pathPos != std::string::npos) {
pathPos = std::max(pathPos, wrongPathPos);
} else if (wrongPathPos != std::string::npos) {
pathPos = wrongPathPos;
}
if (pathPos == std::string::npos) {
return input;
} else {
return input.substr(pathPos + 1);
}
}
std::string DirectoryName(const std::string& filename) {
std::size_t correctSlash = filename.rfind('/');
std::size_t wrongSlash = filename.rfind('\\');
if (correctSlash != std::string::npos && wrongSlash != std::string::npos) {
correctSlash = std::max(correctSlash, wrongSlash);
}
if (correctSlash == std::string::npos) {
return "";
}
return filename.substr(0, correctSlash);
}
std::size_t nextPathCharacter(const std::string& input, std::size_t curr) {
std::size_t correctPath = input.find('/', curr);
std::size_t wrongPath = input.find('\\', curr);
if (correctPath != std::string::npos && wrongPath != std::string::npos) {
return std::min(correctPath, wrongPath);
} else if (correctPath != std::string::npos) {
return correctPath;
} else {
return wrongPath;
}
}
void separatePath(const std::string& input, std::vector<std::string>& output) {
std::size_t curr = 0;
do {
std::size_t next = nextPathCharacter(input, curr);
if (next == std::string::npos) {
output.push_back(input.substr(curr));
curr = std::string::npos;
} else {
output.push_back(input.substr(curr, next - curr));
curr = next + 1;
}
} while (curr != std::string::npos);
}
std::string Join(const std::string& a, const std::string& b) {
if (b.length() == 0) {
return a;
}
if (isPathCharacter(b[0])) {
return b;
}
std::vector<std::string> parts;
separatePath(a, parts);
separatePath(b, parts);
std::vector<std::string> normalizedParts;
int skipCount = 0;
for (int i = parts.size() - 1; i >= 0; --i) {
if (parts[i] == "..") {
++skipCount;
} else if (parts[i] == ".") {
continue;
} else if (skipCount) {
--skipCount;
} else if (parts[i] != ".") {
normalizedParts.push_back(parts[i]);
}
}
std::ostringstream result;
for (int i = normalizedParts.size() - 1; i >= 0; --i) {
if (i != (int)normalizedParts.size() - 1) {
result << "/";
}
result << normalizedParts[i];
}
return result.str();
}
std::vector<std::string> SplitOnFirstPath(const std::string& path) {
std::size_t lastStart = 0;
std::vector<std::string> result;
bool hasMore = true;
while (hasMore) {
std::size_t pathPos = path.find('/', lastStart);
std::size_t wrongPathPos = path.find('\\', lastStart);
if (pathPos != std::string::npos && wrongPathPos != std::string::npos) {
pathPos = std::min(pathPos, wrongPathPos);
}
if (pathPos == std::string::npos) {
hasMore = false;
result.push_back(path.substr(lastStart));
} else {
if (pathPos != lastStart) {
result.push_back(path.substr(lastStart, pathPos - lastStart));
}
lastStart = pathPos + 1;
}
}
return result;
}
std::string Relative(const std::string& from, const std::string& to) {
std::vector<std::string> fromPathSplit = SplitOnFirstPath(DirectoryName(from));
std::vector<std::string> toPathSplit = SplitOnFirstPath(to);
unsigned commonStart = 0;
while (commonStart < fromPathSplit.size() && commonStart < toPathSplit.size() && fromPathSplit[commonStart] == toPathSplit[commonStart]) {
++commonStart;
}
std::ostringstream result;
for (unsigned i = commonStart; i < fromPathSplit.size(); ++i) {
result << "../";
}
for (unsigned i = commonStart; i < toPathSplit.size(); ++i) {
result << toPathSplit[i];
if (i+1 != toPathSplit.size()) {
result << '/';
}
}
return result.str();
}
std::string NormalizePath(const std::string& path) {
return Join(GetCwd(), path);
}
bool FileExists(const std::string& path) {
std::ifstream tmp;
tmp.open(path);
bool result = (bool)tmp;
if (result) {
tmp.close();
}
return result;
}

View file

@ -0,0 +1,19 @@
#ifndef _FILE_UTILS_H
#define _FILE_UTILS_H
#include <string>
bool isPathCharacter(char chr);
std::string replaceExtension(const std::string& input, const std::string& newExt);
std::string getBaseName(const std::string& input);
std::string DirectoryName(const std::string& filename);
std::string Join(const std::string& a, const std::string& b);
std::string Relative(const std::string& from, const std::string& to);
std::string NormalizePath(const std::string& path);
bool FileExists(const std::string& path);
#endif

View file

@ -0,0 +1,11 @@
#include "MathUtl.h"
#include <math.h>
aiVector3D min(const aiVector3D& a, const aiVector3D& b) {
return aiVector3D(std::min(a.x, b.x), std::min(a.y, b.y), std::min(a.z, b.z));
}
aiVector3D max(const aiVector3D& a, const aiVector3D& b) {
return aiVector3D(std::max(a.x, b.x), std::max(a.y, b.y), std::max(a.z, b.z));
}

View file

@ -0,0 +1,9 @@
#ifndef MATH_UTIL_H
#define MATH_UTIL_H
#include <assimp/mesh.h>
aiVector3D min(const aiVector3D& a, const aiVector3D& b);
aiVector3D max(const aiVector3D& a, const aiVector3D& b);
#endif

View file

@ -0,0 +1,110 @@
#include "MeshWriter.h"
#include <set>
#include <sstream>
#include "RCPState.h"
#include "DisplayListGenerator.h"
#include "StringUtils.h"
MaterialCollector::MaterialCollector(): mSceneCount(0) {}
void MaterialCollector::UseMaterial(const std::string& material, DisplayListSettings& settings) {
auto materialDL = settings.mMaterials.find(material);
if (materialDL == settings.mMaterials.end()) {
return;
}
auto prevCount = mMaterialUseCount.find(material);
if (prevCount == mMaterialUseCount.end()) {
mMaterialUseCount[material] = 1;
for (int i = 0; i < MAX_TILE_COUNT; ++i) {
auto tile = &materialDL->second->mState.tiles[i];
if (tile->isOn && tile->texture) {
mUsedTextures.insert(tile->texture);
}
}
} else {
mMaterialUseCount[material] = prevCount->second + 1;
}
}
void MaterialCollector::CollectMaterialResources(const aiScene* scene, std::vector<RenderChunk>& renderChunks, DisplayListSettings& settings) {
for (auto chunk = renderChunks.begin(); chunk != renderChunks.end(); ++chunk) {
UseMaterial(ExtendedMesh::GetMaterialName(scene->mMaterials[chunk->mMesh->mMesh->mMaterialIndex]), settings);
}
++mSceneCount;
}
void MaterialCollector::GenerateMaterials(DisplayListSettings& settings, CFileDefinition& fileDefinition, const std::string& fileSuffix) {
for (auto image : mUsedTextures) {
fileDefinition.AddDefinition(std::move(image->GenerateDefinition(fileDefinition.GetUniqueName(image->Name()), fileSuffix)));
}
for (auto useCount = mMaterialUseCount.begin(); useCount != mMaterialUseCount.end(); ++useCount) {
if (useCount->second > 1 || mSceneCount > 1) {
DisplayList materialDL(fileDefinition.GetUniqueName(useCount->first));
settings.mMaterials.find(useCount->first)->second->Write(fileDefinition, settings.mDefaultMaterialState, materialDL.GetDataChunk());
mMaterialNameMapping[useCount->first] = materialDL.GetName();
auto dl = materialDL.Generate(fileSuffix);
fileDefinition.AddDefinition(std::move(dl));
}
}
}
void generateMeshIntoDLWithMaterials(const aiScene* scene, CFileDefinition& fileDefinition, MaterialCollector* materials, std::vector<RenderChunk>& renderChunks, DisplayListSettings& settings, DisplayList &displayList, const std::string& modelSuffix) {
RCPState rcpState(settings.mDefaultMaterialState, settings.mVertexCacheSize, settings.mMaxMatrixDepth, settings.mCanPopMultipleMatrices);
for (auto chunk = renderChunks.begin(); chunk != renderChunks.end(); ++chunk) {
if (materials) {
std::string materialName = ExtendedMesh::GetMaterialName(scene->mMaterials[chunk->mMesh->mMesh->mMaterialIndex]);
displayList.AddCommand(std::unique_ptr<DisplayListCommand>(new CommentCommand("Material " + materialName)));
auto mappedMaterialName = materials->mMaterialNameMapping.find(materialName);
if (mappedMaterialName != materials->mMaterialNameMapping.end()) {
displayList.AddCommand(std::unique_ptr<DisplayListCommand>(new CallDisplayListByNameCommand(mappedMaterialName->second)));
} else {
auto material = settings.mMaterials.find(materialName);
if (material != settings.mMaterials.end()) {
material->second->Write(fileDefinition, rcpState.GetMaterialState(), displayList.GetDataChunk());
}
}
displayList.AddCommand(std::unique_ptr<DisplayListCommand>(new CommentCommand("End Material " + materialName)));
}
std::string vertexBuffer = fileDefinition.GetVertexBuffer(
chunk->mMesh,
Material::GetVertexType(chunk->mMaterial),
Material::TextureWidth(chunk->mMaterial),
Material::TextureHeight(chunk->mMaterial),
modelSuffix
);
generateGeometry(*chunk, rcpState, vertexBuffer, displayList, settings.mHasTri2);
}
rcpState.TraverseToBone(nullptr, displayList);
}
void generateMeshIntoDL(const aiScene* scene, CFileDefinition& fileDefinition, std::vector<RenderChunk>& renderChunks, DisplayListSettings& settings, DisplayList &displayList, const std::string& fileSuffix) {
MaterialCollector materials;
materials.CollectMaterialResources(scene, renderChunks, settings);
materials.GenerateMaterials(settings, fileDefinition, fileSuffix);
generateMeshIntoDLWithMaterials(scene, fileDefinition, &materials, renderChunks, settings, displayList, fileSuffix);
}
std::string generateMesh(const aiScene* scene, CFileDefinition& fileDefinition, std::vector<RenderChunk>& renderChunks, DisplayListSettings& settings, const std::string& fileSuffix) {
DisplayList displayList(fileDefinition.GetUniqueName("model_gfx"));
generateMeshIntoDL(scene, fileDefinition, renderChunks, settings, displayList, fileSuffix);
std::unique_ptr<FileDefinition> dlResult = displayList.Generate(fileSuffix);
fileDefinition.AddDefinition(std::move(dlResult));
return displayList.GetName();
}

View file

@ -0,0 +1,33 @@
#ifndef _MESH_WRITER_H
#define _MESH_WRITER_H
#include <set>
#include <map>
#include <ostream>
#include <string>
#include <assimp/scene.h>
#include "RenderChunk.h"
#include "DisplayListSettings.h"
#include "CFileDefinition.h"
#include "./materials/TextureDefinition.h"
class MaterialCollector {
public:
MaterialCollector();
void UseMaterial(const std::string& material, DisplayListSettings& settings);
void CollectMaterialResources(const aiScene* scene, std::vector<RenderChunk>& renderChunks, DisplayListSettings& settings);
void GenerateMaterials(DisplayListSettings& settings, CFileDefinition& fileDefinition, const std::string& fileSuffix);
unsigned mSceneCount;
std::set<std::shared_ptr<TextureDefinition>> mUsedTextures;
std::map<std::string, int> mMaterialUseCount;
std::map<std::string, std::string> mMaterialNameMapping;
std::map<std::string, std::string> mResourceNameMapping;
};
void generateMeshIntoDLWithMaterials(const aiScene* scene, CFileDefinition& fileDefinition, MaterialCollector* materials, std::vector<RenderChunk>& renderChunks, DisplayListSettings& settings, DisplayList &displayList, const std::string& modelSuffix);
void generateMeshIntoDL(const aiScene* scene, CFileDefinition& fileDefinition, std::vector<RenderChunk>& renderChunks, DisplayListSettings& settings, DisplayList &displayList, const std::string& fileSuffix);
std::string generateMesh(const aiScene* scene, CFileDefinition& fileDefinition, std::vector<RenderChunk>& renderChunks, DisplayListSettings& settings, const std::string& fileSuffix);
#endif

View file

@ -0,0 +1,119 @@
#include "./RCPState.h"
#include <set>
VertexData::VertexData() :
mVertexBuffer("-1"),
mVertexIndex(-1),
mMatrixIndex(-1) {
}
VertexData::VertexData(std::string vertexBuffer, int vertexIndex, int matrixIndex) :
mVertexBuffer(vertexBuffer),
mVertexIndex(vertexIndex),
mMatrixIndex(matrixIndex) {
}
const bool VertexData::operator==(const VertexData& other) {
return mVertexBuffer == other.mVertexBuffer &&
mVertexIndex == other.mVertexIndex &&
mMatrixIndex == other.mMatrixIndex;
}
RCPState::RCPState(const MaterialState& materialState, unsigned int maxVertexCount, unsigned int maxMatrixDepth, bool canPopMultiple) :
mMaterialState(materialState),
mMaxVertices(maxVertexCount),
mMaxMatrixDepth(maxMatrixDepth),
mCanPopMultiple(canPopMultiple) {
}
ErrorCode RCPState::TraverseToBone(Bone* bone, DisplayList& output) {
std::set<Bone*> bonesToPop(mBoneMatrixStack.begin(), mBoneMatrixStack.end());
Bone* curr = bone;
while (curr) {
bonesToPop.erase(curr);
curr = curr->GetParent();
}
if (mCanPopMultiple) {
if (bonesToPop.size() != 0) {
output.AddCommand(std::unique_ptr<DisplayListCommand>(new PopMatrixCommand(bonesToPop.size())));
}
} else {
for (unsigned int i = 0; i < bonesToPop.size(); ++i) {
output.AddCommand(std::unique_ptr<DisplayListCommand>(new PopMatrixCommand(1)));
}
}
mBoneMatrixStack.resize(mBoneMatrixStack.size() - bonesToPop.size());
std::vector<Bone*> bonesToAdd;
curr = bone;
while (curr != nullptr && (mBoneMatrixStack.size() == 0 || *mBoneMatrixStack.rbegin() != curr)) {
bonesToAdd.push_back(curr);
curr = curr->GetParent();
}
for (auto curr = bonesToAdd.rbegin(); curr != bonesToAdd.rend(); ++curr) {
if (mBoneMatrixStack.size() == mMaxMatrixDepth) {
return ErrorCode::MatrixStackOverflow;
}
mBoneMatrixStack.push_back(*curr);
output.AddCommand(std::unique_ptr<DisplayListCommand>(new CommentCommand((*curr)->GetName())));
output.AddCommand(std::unique_ptr<DisplayListCommand>(new PushMatrixCommand((*curr)->GetIndex(), false)));
}
return ErrorCode::None;
}
void RCPState::AssignSlots(VertexData* newVertices, unsigned int* slotIndex, unsigned int vertexCount) {
bool usedSlots[MAX_VERTEX_CACHE_SIZE];
bool assignedVertices[MAX_VERTEX_CACHE_SIZE];
for (unsigned int i = 0; i < MAX_VERTEX_CACHE_SIZE; ++i) {
usedSlots[i] = false;
assignedVertices[i] = false;
}
for (unsigned int currentVertex = 0; currentVertex < mMaxVertices; ++currentVertex) {
for (unsigned int newVertex = 0; newVertex < vertexCount; ++newVertex) {
if (mVertices[currentVertex] == newVertices[newVertex]) {
usedSlots[currentVertex] = true;
assignedVertices[newVertex] = true;
slotIndex[newVertex] = currentVertex;
}
}
}
unsigned int nextTarget = 0;
unsigned int nextSource = 0;
while (nextTarget < mMaxVertices && nextSource < vertexCount) {
if (usedSlots[nextTarget]) {
++nextTarget;
} else if (assignedVertices[nextSource]) {
++nextSource;
} else {
slotIndex[nextSource] = nextTarget;
++nextTarget;
++nextSource;
}
}
}
const unsigned int RCPState::GetMaxVertices() {
return mMaxVertices;
}
MaterialState& RCPState::GetMaterialState() {
return mMaterialState;
}

View file

@ -0,0 +1,40 @@
#ifndef _RCP_STATE_H
#define _RCP_STATE_H
#include <vector>
#include "BoneHierarchy.h"
#include "DisplayList.h"
#include "ErrorCode.h"
#include "materials/MaterialState.h"
struct VertexData {
VertexData();
VertexData(std::string vertexBuffer, int vertexIndex, int matrixIndex);
std::string mVertexBuffer;
int mVertexIndex;
int mMatrixIndex;
const bool operator==(const VertexData& other);
};
#define MAX_VERTEX_CACHE_SIZE 32
class RCPState {
public:
RCPState(const MaterialState& materialState, unsigned int maxVertexCount, unsigned int maxMatrixDepth, bool canPopMultiple);
ErrorCode TraverseToBone(Bone* bone, DisplayList& output);
void AssignSlots(VertexData* newVertices, unsigned int* slotIndex, unsigned int vertexCount);
const unsigned int GetMaxVertices();
MaterialState& GetMaterialState();
private:
MaterialState mMaterialState;
unsigned int mMaxVertices;
unsigned int mMaxMatrixDepth;
bool mCanPopMultiple;
VertexData mVertices[MAX_VERTEX_CACHE_SIZE];
std::vector<Bone*> mBoneMatrixStack;
};
#endif

View file

@ -0,0 +1,71 @@
#include "RenderChunk.h"
#include <algorithm>
RenderChunk::RenderChunk(std::pair<Bone*, Bone*> bonePair, std::shared_ptr<ExtendedMesh> mesh, Material* material):
mBonePair(bonePair),
mMesh(mesh),
mMaterial(material) {
}
VertexType RenderChunk::GetVertexType() {
return Material::GetVertexType(mMaterial);
}
int RenderChunk::GetTextureWidth() {
return Material::TextureWidth(mMaterial);
}
int RenderChunk::GetTextureHeight() {
return Material::TextureHeight(mMaterial);
}
const std::vector<aiFace*>& RenderChunk::GetFaces() {
if (mBonePair.first == mBonePair.second) {
auto result = mMesh->mFacesForBone.find(mBonePair.first);
return result->second;
} else {
auto result = mMesh->mBoneSpanningFaces.find(mBonePair);
return result->second;
}
}
void extractChunks(const aiScene* scene, std::vector<std::shared_ptr<ExtendedMesh>>& meshes, std::vector<RenderChunk>& result, std::map<std::string, std::shared_ptr<Material>>& materials) {
for (auto it = meshes.begin(); it != meshes.end(); ++it) {
Material* materialPtr = NULL;
auto material = materials.find(ExtendedMesh::GetMaterialName(scene->mMaterials[(*it)->mMesh->mMaterialIndex]));
if (material != materials.end()) {
materialPtr = material->second.get();
}
for (auto boneSegment = (*it)->mFacesForBone.begin(); boneSegment != (*it)->mFacesForBone.end(); ++boneSegment) {
result.push_back(RenderChunk(
std::make_pair(boneSegment->first, boneSegment->first),
*it,
materialPtr
));
}
for (auto pairSegment = (*it)->mBoneSpanningFaces.begin(); pairSegment != (*it)->mBoneSpanningFaces.end(); ++pairSegment) {
result.push_back(RenderChunk(pairSegment->first, *it, materialPtr));
}
}
}
void orderChunks(std::vector<RenderChunk>& result) {
// TODO solve the traveling salesman algorithm
std::sort(result.begin(), result.end(),
[](const RenderChunk& a, const RenderChunk& b) -> bool {
int aSecondScore = Bone::GetBoneIndex(a.mBonePair.second);
int bSecondScore = Bone::GetBoneIndex(b.mBonePair.second);
if (aSecondScore == bSecondScore) {
return Bone::GetBoneIndex(a.mBonePair.first) < Bone::GetBoneIndex(b.mBonePair.first);
}
return aSecondScore < bSecondScore;
});
}

View file

@ -0,0 +1,35 @@
#ifndef _RENDER_CHUNK_H
#define _RENDER_CHUNK_H
#include <assimp/scene.h>
#include "ExtendedMesh.h"
#include "BoneHierarchy.h"
#include "materials/Material.h"
#include <vector>
#include <memory>
#include <map>
class RenderChunk {
public:
RenderChunk(std::pair<Bone*, Bone*> bonePair, std::shared_ptr<ExtendedMesh> mesh, Material* material);
// if bones are the same, chunk cooresponds to a single bone
// the bones can be null
std::pair<Bone*, Bone*> mBonePair;
std::shared_ptr<ExtendedMesh> mMesh;
Material* mMaterial;
VertexType GetVertexType();
int GetTextureWidth();
int GetTextureHeight();
const std::vector<aiFace*>& GetFaces();
private:
};
void extractChunks(const aiScene* scene, std::vector<std::shared_ptr<ExtendedMesh>>& meshes, std::vector<RenderChunk>& result, std::map<std::string, std::shared_ptr<Material>>& mMaterials);
void orderChunks(std::vector<RenderChunk>& result);
#endif

View file

@ -0,0 +1,38 @@
#include "SceneLoader.h"
#include <assimp/Importer.hpp>
#include <assimp/postprocess.h>
#include "SceneModification.h"
#include <iostream>
aiScene* loadScene(const std::string& filename, bool isLevel, int vertexCacheSize) {
Assimp::Importer importer;
importer.SetPropertyInteger(AI_CONFIG_PP_LBW_MAX_WEIGHTS, 1);
unsigned int pFlags = aiProcess_JoinIdenticalVertices |
aiProcess_Triangulate |
aiProcess_LimitBoneWeights |
aiProcess_OptimizeMeshes;
if (!isLevel) {
importer.SetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_POINT | aiPrimitiveType_LINE);
pFlags |= aiProcess_OptimizeGraph | aiProcess_SortByPType;
}
const aiScene* scene = importer.ReadFile(filename, pFlags);
if (scene == nullptr) {
std::cerr << "Error loading input file: " << importer.GetErrorString() << std::endl;
return 0;
}
if (!isLevel) {
splitSceneByBones(const_cast<aiScene*>(scene));
}
importer.SetPropertyInteger(AI_CONFIG_PP_ICL_PTCACHE_SIZE, vertexCacheSize);
importer.ApplyPostProcessing(aiProcess_ImproveCacheLocality);
return importer.GetOrphanedScene();
}

View file

@ -0,0 +1,9 @@
#ifndef _SCENE_LOADER_H
#define _SCENE_LOADER_H
#include <assimp/scene.h>
#include <string>
aiScene* loadScene(const std::string& filename, bool isLevel, int vertexCacheSize);
#endif

View file

@ -0,0 +1,151 @@
#include "./SceneModification.h"
#include <map>
#include <set>
#include <memory>
#include "./BoneHierarchy.h"
#include "./ExtendedMesh.h"
void generateVertexMapping(aiMesh* mesh, std::vector<aiFace*> faces, std::map<unsigned int, unsigned int>& result) {
std::set<unsigned int> usedIndices;
for (auto faceIt = faces.begin(); faceIt != faces.end(); ++faceIt) {
aiFace* face = *faceIt;
for (unsigned int index = 0; index < face->mNumIndices; ++index) {
usedIndices.insert(face->mIndices[index]);
}
}
unsigned int mappedIndex = 0;
for (unsigned int vertexIndex = 0; vertexIndex < mesh->mNumVertices; ++vertexIndex) {
if (usedIndices.find(vertexIndex) != usedIndices.end()) {
result[vertexIndex] = mappedIndex;
++mappedIndex;
}
}
}
void filterOutBones(aiMesh* source, aiMesh* target, std::map<unsigned int, unsigned int>& vertexMapping) {
target->mNumBones = 0;
target->mBones = new aiBone*[source->mNumBones];
for (unsigned int sourceBoneIndex = 0; sourceBoneIndex < source->mNumBones; ++sourceBoneIndex) {
aiBone* newBone = new aiBone();
aiBone* sourceBone = source->mBones[sourceBoneIndex];
newBone->mNumWeights = 0;
newBone->mName = sourceBone->mName;
newBone->mOffsetMatrix = sourceBone->mOffsetMatrix;
newBone->mWeights = new aiVertexWeight[sourceBone->mNumWeights];
unsigned int numWeights = 0;
for (unsigned int boneVertIndex = 0; boneVertIndex < sourceBone->mNumWeights; ++boneVertIndex) {
auto newIndexIt = vertexMapping.find(sourceBone->mWeights[boneVertIndex].mVertexId);
if (newIndexIt != vertexMapping.end()) {
newBone->mWeights[numWeights].mVertexId = newIndexIt->second;
newBone->mWeights[numWeights].mWeight = sourceBone->mWeights[boneVertIndex].mWeight;
++numWeights;
}
}
if (numWeights) {
newBone->mNumWeights = numWeights;
target->mBones[target->mNumBones] = newBone;
++target->mNumBones;
} else {
delete newBone;
}
}
if (target->mNumBones == 0) {
delete [] target->mBones;
target->mBones = nullptr;
}
}
void filterOutFaces(aiMesh* source, aiMesh* target, std::map<unsigned int, unsigned int>& vertexMapping, std::vector<aiFace*> faces) {
target->mNumFaces = faces.size();
target->mFaces = new aiFace[faces.size()];
for (unsigned int currentFace = 0; currentFace < faces.size(); ++currentFace) {
aiFace newFace;
newFace.mNumIndices = faces[currentFace]->mNumIndices;
newFace.mIndices = new unsigned int[newFace.mNumIndices];
for (unsigned int index = 0; index < newFace.mNumIndices; ++index) {
newFace.mIndices[index] = vertexMapping[faces[currentFace]->mIndices[index]];
}
target->mFaces[currentFace] = newFace;
}
}
aiMesh* subMesh(aiMesh* mesh, std::vector<aiFace*> faces) {
aiMesh* result = new aiMesh();
std::map<unsigned int, unsigned int> vertexMapping;
generateVertexMapping(mesh, faces, vertexMapping);
result->mNumVertices = vertexMapping.size();
result->mVertices = new aiVector3D[result->mNumVertices];
result->mMaterialIndex = mesh->mMaterialIndex;
result->mMethod = mesh->mMethod;
result->mName = mesh->mName;
if (mesh->mNormals) result->mNormals = new aiVector3D[result->mNumVertices];
if (mesh->mTextureCoords[0]) result->mTextureCoords[0] = new aiVector3D[result->mNumVertices];
if (mesh->mColors[0]) result->mColors[0] = new aiColor4D[result->mNumVertices];
for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
auto newIndexIt = vertexMapping.find(i);
if (newIndexIt != vertexMapping.end()) {
unsigned int newIndex = newIndexIt->second;
result->mVertices[newIndex] = mesh->mVertices[i];
if (result->mNormals) result->mNormals[newIndex] = mesh->mNormals[i];
if (result->mTextureCoords[0]) result->mTextureCoords[0][newIndex] = mesh->mTextureCoords[0][i];
if (result->mColors[0]) result->mColors[0][newIndex] = mesh->mColors[0][i];
}
}
filterOutBones(mesh, result, vertexMapping);
filterOutFaces(mesh, result, vertexMapping, faces);
return result;
}
void splitSceneByBones(aiScene* targetScene) {
std::vector<aiMesh*> newMeshes;
BoneHierarchy bones;
bones.SearchForBonesInScene(targetScene);
for (unsigned int i = 0; i < targetScene->mNumMeshes; ++i) {
aiMesh* currMesh = targetScene->mMeshes[i];
std::unique_ptr<ExtendedMesh> extendedMesh(new ExtendedMesh(currMesh, bones));
for (auto newFaces = extendedMesh->mFacesForBone.begin(); newFaces != extendedMesh->mFacesForBone.end(); ++newFaces) {
newMeshes.push_back(subMesh(currMesh, newFaces->second));
}
for (auto newFaces = extendedMesh->mBoneSpanningFaces.begin(); newFaces != extendedMesh->mBoneSpanningFaces.end(); ++newFaces) {
newMeshes.push_back(subMesh(currMesh, newFaces->second));
}
delete currMesh;
}
delete [] targetScene->mMeshes;
targetScene->mNumMeshes = newMeshes.size();
targetScene->mMeshes = new aiMesh*[newMeshes.size()];
std::copy(newMeshes.begin(), newMeshes.end(), targetScene->mMeshes);
}

View file

@ -0,0 +1,13 @@
#ifndef _SCENE_MODIFICAITON_H
#define _SCENE_MODIFICAITON_H
#include <assimp/mesh.h>
#include <assimp/BaseImporter.h>
#include <vector>
// Caller is responsible for freeing memory
aiMesh* subMesh(aiMesh* mesh, std::vector<aiFace*> faces);
void splitSceneByBones(aiScene* targetScene);
#endif

View file

@ -0,0 +1,143 @@
#include "SceneWriter.h"
#include <fstream>
#include <sstream>
#include <filesystem>
#include <algorithm>
#include <vector>
#include <string>
#include "./DisplayList.h"
#include "./DisplayListGenerator.h"
#include "./BoneHierarchy.h"
#include "./ExtendedMesh.h"
#include "./RenderChunk.h"
#include "AnimationTranslator.h"
#include "MeshWriter.h"
#include "FileUtils.h"
std::vector<SKAnimationHeader> generateAnimationData(const aiScene* scene, BoneHierarchy& bones, CFileDefinition& fileDef, float modelScale, unsigned short targetTicksPerSecond, aiQuaternion rotate) {
std::vector<SKAnimationHeader> animations;
for (unsigned i = 0; i < scene->mNumAnimations; ++i) {
SKAnimation animation;
if (translateAnimationToSK(*scene->mAnimations[i], animation, bones, modelScale, targetTicksPerSecond, rotate)) {
std::string animationName = fileDef.GetUniqueName(scene->mAnimations[i]->mName.C_Str());
unsigned short firstChunkSize = formatAnimationChunks(animationName, animation.chunks, fileDef);
SKAnimationHeader header;
header.firstChunkSize = firstChunkSize;
header.ticksPerSecond = targetTicksPerSecond;
header.maxTicks = animation.maxTicks;
header.animationName = animationName;
animations.push_back(header);
}
}
return animations;
}
void generateMeshFromScene(const aiScene* scene, CFileDefinition& fileDefinition, DisplayListSettings& settings) {
BoneHierarchy bones;
bool shouldExportAnimations;
if (settings.mExportAnimation) {
bones.SearchForBonesInScene(scene);
shouldExportAnimations = bones.HasData();
} else {
shouldExportAnimations = false;
}
std::vector<std::shared_ptr<ExtendedMesh>> extendedMeshes;
for (unsigned int i = 0; i < scene->mNumMeshes; ++i) {
extendedMeshes.push_back(std::shared_ptr<ExtendedMesh>(new ExtendedMesh(scene->mMeshes[i], bones)));
}
std::vector<RenderChunk> renderChunks;
extractChunks(scene, extendedMeshes, renderChunks, settings.mMaterials);
orderChunks(renderChunks);
std::string renderDLName;
if (settings.mExportGeometry) {
renderDLName = generateMesh(scene, fileDefinition, renderChunks, settings, "");
}
if (shouldExportAnimations) {
std::string bonesName = fileDefinition.GetUniqueName("default_bones");
std::string boneParentName = fileDefinition.GetUniqueName("bone_parent");
bones.GenerateRestPosiitonData(fileDefinition, bonesName, settings.mGraphicsScale, settings.mRotateModel);
std::string boneCountName = bonesName + "_COUNT";
std::transform(boneCountName.begin(), boneCountName.end(), boneCountName.begin(), ::toupper);
fileDefinition.AddMacro(boneCountName, std::to_string(bones.GetBoneCount()));
std::string animationsName = fileDefinition.GetUniqueName("animations");
auto animations = generateAnimationData(scene, bones, fileDefinition, settings.mGraphicsScale, settings.mTicksPerSecond, settings.mRotateModel);
std::unique_ptr<StructureDataChunk> animationNameData(new StructureDataChunk());
int index = 0;
for (auto it = animations.begin(); it != animations.end(); ++it) {
std::unique_ptr<StructureDataChunk> animationChunk(new StructureDataChunk());
animationChunk->AddPrimitive(it->firstChunkSize);
animationChunk->AddPrimitive(it->ticksPerSecond);
animationChunk->AddPrimitive(it->maxTicks);
animationChunk->AddPrimitive(0);
animationChunk->AddPrimitive(std::string("(struct SKAnimationChunk*)") + it->animationName);
animationChunk->AddPrimitive(0);
animationNameData->Add(std::move(animationChunk));
std::string animationIndex = fileDefinition.GetUniqueName(it->animationName + "_INDEX");
std::transform(animationIndex.begin(), animationIndex.end(), animationIndex.begin(), ::toupper);
fileDefinition.AddMacro(animationIndex, std::to_string(index));
++index;
}
fileDefinition.AddDefinition(std::unique_ptr<FileDefinition>(new DataFileDefinition("struct SKAnimationHeader", animationsName, true, "_anim", std::move(animationNameData))));
std::unique_ptr<StructureDataChunk> boneParentDataChunk(new StructureDataChunk());
for (unsigned int boneIndex = 0; boneIndex < bones.GetBoneCount(); ++boneIndex) {
Bone* bone = bones.BoneByIndex(boneIndex);
if (bone->GetParent()) {
boneParentDataChunk->AddPrimitive(bone->GetParent()->GetIndex());
} else {
boneParentDataChunk->AddPrimitive(0xFFFF);
}
}
fileDefinition.AddDefinition(std::unique_ptr<FileDefinition>(new DataFileDefinition("unsigned short", boneParentName, true, "_anim", std::move(boneParentDataChunk))));
}
}
void generateMeshFromSceneToFile(const aiScene* scene, std::string filename, DisplayListSettings& settings) {
CFileDefinition fileDefinition(settings.mPrefix, settings.mGraphicsScale, settings.mRotateModel);
generateMeshFromScene(scene, fileDefinition, settings);
std::string filenameBase = replaceExtension(getBaseName(filename), "");
if (settings.mExportGeometry) {
std::ofstream outputFile;
outputFile.open(filename + "_geo.inc.h", std::ios_base::out | std::ios_base::trunc);
fileDefinition.Generate(outputFile, "", filenameBase + ".h");
outputFile.close();
}
std::ofstream outputHeader;
outputHeader.open(filename + ".h", std::ios_base::out | std::ios_base::trunc);
fileDefinition.GenerateHeader(outputHeader, filenameBase);
outputHeader.close();
if (fileDefinition.HasDefinitions("_anim")) {
std::ofstream animOutput;
animOutput.open(filename + "_anim.inc.h", std::ios_base::out | std::ios_base::trunc);
fileDefinition.Generate(animOutput, "_anim", filenameBase + ".h");
animOutput.close();
}
}

View file

@ -0,0 +1,16 @@
#ifndef _SCENE_WRITER_H
#define _SCENE_WRITER_H
#include <assimp/mesh.h>
#include <assimp/scene.h>
#include <sstream>
#include <string>
#include <map>
#include "./materials/Material.h"
#include "./DisplayListSettings.h"
#include "CFileDefinition.h"
void generateMeshFromScene(const aiScene* scene, CFileDefinition& fileDefinition, DisplayListSettings& settings);
void generateMeshFromSceneToFile(const aiScene* scene, std::string filename, DisplayListSettings& settings);
#endif

View file

@ -0,0 +1,16 @@
#ifndef _STRING_UTILS_H
#define _STRING_UTILS_H
#include <string>
std::string FindAndReplace(const std::string& source, const std::string& searchString, const std::string& replaceString, bool wholeWord = false);
std::string Indent(const std::string& input, const std::string& whitespace);
std::string Trim(const std::string& input);
void makeCCompatible(std::string& target);
bool StartsWith(const std::string& input, const std::string& prefix);
#endif

View file

@ -0,0 +1,120 @@
#include "StringUtils.h"
#include <sstream>
bool IsWordCharacter(char input) {
return isalnum(input) || input == '_';
}
std::string FindAndReplace(const std::string& source, const std::string& searchString, const std::string& replaceString, bool wholeWord) {
std::ostringstream result;
unsigned next = source.find(searchString);
unsigned last = 0;
while (next < source.length()) {
result << source.substr(last, next - last);
bool shouldReplace = true;
unsigned afterNext = next + searchString.length();
if (wholeWord) {
if (next > 0 && IsWordCharacter(source[next - 1])) {
shouldReplace = false;
}
if (afterNext < source.length() && IsWordCharacter(source[afterNext])) {
shouldReplace = false;
}
}
if (shouldReplace) {
result << replaceString;
} else {
result << searchString;
}
last = afterNext;
next = source.find(searchString, last);
}
result << source.substr(last, source.length() - last);
return result.str();
}
std::string Indent(const std::string& input, const std::string& whitespace) {
std::ostringstream result;
unsigned lineStart = 0;
bool lookingForLineStart = true;
for (unsigned curr = 0; curr <= input.length(); ++curr) {
char currentChar = curr < input.length() ? input[curr] : '\0';
if (lookingForLineStart) {
if (!isspace(currentChar)) {
lineStart = curr;
lookingForLineStart = false;
result << whitespace;
}
} else {
if (currentChar == '\n' || currentChar == '\r' || currentChar == '\0') {
result << input.substr(lineStart, curr - lineStart) << std::endl;
lookingForLineStart = true;
}
}
}
return result.str();
}
std::string Trim(const std::string& input) {
int start = 0;
while (start < (int)input.length() && isspace(input[start])) {
++start;
}
int end = input.length() - 1;
while (end >= 0 && isspace(input[end])) {
--end;
}
++end;
if (start >= end) {
return "";
}
return input.substr(start, end - start);
}
void makeCCompatible(std::string& target) {
for (unsigned int i = 0; i < target.length(); ++i) {
char curr = target[i];
if (!(curr >= 'a' && curr <= 'z') && !(curr >= 'A' && curr <= 'Z') && !(curr >= '0' && curr <= '9') && curr != '_') {
target[i] = '_';
}
}
if (target.length() > 0 && target[0] >= '0' && target[0] <= '9') {
target = '_' + target;
}
}
bool StartsWith(const std::string& input, const std::string& prefix) {
if (prefix.length() > input.length()) {
return false;
}
for (unsigned i = 0; i < prefix.length(); ++i) {
if (input[i] != prefix[i]) {
return false;
}
}
return true;
}

View file

@ -0,0 +1,255 @@
#include "CollisionGenerator.h"
#include "../StringUtils.h"
#include <algorithm>
#define SAME_TOLERANCE 0.00001f
bool bottomRightMost(const aiVector3D& a, const aiVector3D& b) {
if (fabs(a.x - b.x) > SAME_TOLERANCE) {
return a.x < b.x;
}
if (fabs(a.y - b.y) > SAME_TOLERANCE) {
return a.y < b.y;
}
return a.z - b.z;
}
const aiVector3D* findMostOppositeEdge(const aiVector3D& fromEdge, const std::vector<aiVector3D>& edges) {
return std::min_element(edges.begin(), edges.end(), [=](const aiVector3D& a, const aiVector3D& b) {
return (a * fromEdge) < (b * fromEdge);
}).base();
}
CollisionQuad::CollisionQuad(aiMesh* mesh, const aiMatrix4x4& transform) {
if (mesh->mVertices) {
std::vector<aiVector3D> transformedPoints;
for (unsigned index = 0; index < mesh->mNumVertices; ++index) {
transformedPoints.push_back(transform * mesh->mVertices[index]);
}
auto cornerPointer = std::min_element(transformedPoints.begin(), transformedPoints.end(), bottomRightMost);
unsigned cornerIndex = cornerPointer - transformedPoints.begin();
corner = *cornerPointer;
std::set<int> adjacentIndices;
findAdjacentVertices(mesh, cornerIndex, adjacentIndices);
std::vector<aiVector3D> edgesFromCorner;
for (auto index : adjacentIndices) {
edgesFromCorner.push_back(transformedPoints[index] - corner);
}
auto edgeAPoint = findMostOppositeEdge(edgesFromCorner[0], edgesFromCorner);
edgeA = *edgeAPoint;
edgeALength = edgeA.Length();
edgeA.Normalize();
auto edgeBPoint = findMostOppositeEdge(edgeA, edgesFromCorner);
edgeB = *edgeBPoint;
edgeBLength = edgeB.Length();
edgeB.Normalize();
aiMatrix3x3 rotation(transform);
for (unsigned i = 0; i < mesh->mNumVertices; ++i) {
normal += rotation * mesh->mNormals[i];
}
normal.Normalize();
if ((edgeA ^ edgeB) * normal < 0.0f) {
aiVector3D tmpEdge = edgeA;
float tmpLength = edgeALength;
edgeA = edgeB;
edgeALength = edgeBLength;
edgeB = tmpEdge;
edgeBLength = tmpLength;
}
corner.x = 0.001f * round(1000.0f * corner.x);
corner.y = 0.001f * round(1000.0f * corner.y);
corner.z = 0.001f * round(1000.0f * corner.z);
edgeA.x = 0.001f * round(1000.0f * edgeA.x);
edgeA.y = 0.001f * round(1000.0f * edgeA.y);
edgeA.z = 0.001f * round(1000.0f * edgeA.z);
edgeALength = 0.001f * round(1000.0f * edgeALength);
edgeB.x = 0.001f * round(1000.0f * edgeB.x);
edgeB.y = 0.001f * round(1000.0f * edgeB.y);
edgeB.z = 0.001f * round(1000.0f * edgeB.z);
edgeBLength = 0.001f * round(1000.0f * edgeBLength);
normal.x = 0.001f * round(1000.0f * normal.x);
normal.y = 0.001f * round(1000.0f * normal.y);
normal.z = 0.001f * round(1000.0f * normal.z);
} else {
corner = aiVector3D();
edgeA = aiVector3D();
edgeALength = 0.0f;
edgeB = aiVector3D();
edgeBLength = 0.0f;
normal = aiVector3D();
}
}
std::unique_ptr<DataChunk> CollisionQuad::Generate() {
std::unique_ptr<StructureDataChunk> result(new StructureDataChunk());
result->Add(std::unique_ptr<DataChunk>(new StructureDataChunk(corner)));
result->Add(std::unique_ptr<DataChunk>(new StructureDataChunk(edgeA)));
result->AddPrimitive(edgeALength);
result->Add(std::unique_ptr<DataChunk>(new StructureDataChunk(edgeB)));
result->AddPrimitive(edgeBLength);
std::unique_ptr<StructureDataChunk> plane(new StructureDataChunk());
plane->Add(std::unique_ptr<DataChunk>(new StructureDataChunk(normal)));
plane->AddPrimitive(-(corner * normal));
result->Add(std::move(plane));
result->AddPrimitive(0xF);
return result;
}
#define FIXED_POINT_PRECISION 8
#define FIXED_POINT_SCALAR (1 << FIXED_POINT_PRECISION)
void CollisionQuad::ToLocalCoords(const aiVector3D& input, short& outX, short& outY) {
aiVector3D relative = input - corner;
outX = (short)(relative * edgeA * FIXED_POINT_SCALAR + 0.5f);
outY = (short)(relative * edgeB * FIXED_POINT_SCALAR + 0.5f);
}
#define INSIDE_NORMAL_TOLERANCE 0.1f
bool CollisionQuad::IsCoplanar(ExtendedMesh& mesh, float relativeScale) const {
for (unsigned i = 0; i < mesh.mMesh->mNumVertices; ++i) {
aiVector3D offset = mesh.mMesh->mVertices[i] * relativeScale - corner;
float z = offset * normal;
if (fabs(z) >= INSIDE_NORMAL_TOLERANCE) {
return false;
}
float x = offset * edgeA;
if (x < -INSIDE_NORMAL_TOLERANCE || x > edgeALength + INSIDE_NORMAL_TOLERANCE) {
return false;
}
float y = offset * edgeB;
if (y < -INSIDE_NORMAL_TOLERANCE || y > edgeBLength + INSIDE_NORMAL_TOLERANCE) {
return false;
}
}
return true;
}
CollisionGenerator::CollisionGenerator(const DisplayListSettings& settings) :
DefinitionGenerator(),
mSettings(settings) {}
bool CollisionGenerator::ShouldIncludeNode(aiNode* node) {
return StartsWith(node->mName.C_Str(), "@collision");
}
void CollisionGenerator::GenerateDefinitions(const aiScene* scene, CFileDefinition& fileDefinition) {
std::unique_ptr<StructureDataChunk> collidersChunk(new StructureDataChunk());
std::unique_ptr<StructureDataChunk> colliderTypeChunk(new StructureDataChunk());
std::unique_ptr<StructureDataChunk> collisionObjectChunk(new StructureDataChunk());
aiMatrix4x4 scale;
aiMatrix4x4::Scaling(aiVector3D(1, 1, 1) * mSettings.mCollisionScale, scale);
aiMatrix4x4 rotation(mSettings.mRotateModel.GetMatrix());
aiMatrix4x4 globalTransform = rotation * scale;
int meshCount = 0;
std::string quadCollidersName = fileDefinition.GetUniqueName("quad_colliders");
std::string colliderTypesName = fileDefinition.GetUniqueName("collider_types");
std::string collisionObjectsName = fileDefinition.GetUniqueName("collision_objects");
for (auto node = mIncludedNodes.begin(); node != mIncludedNodes.end(); ++node) {
for (unsigned i = 0; i < (*node)->mNumMeshes; ++i) {
aiMesh* mesh = scene->mMeshes[(*node)->mMeshes[i]];
CollisionQuad collider(mesh, globalTransform * (*node)->mTransformation);
collidersChunk->Add(std::move(collider.Generate()));
std::unique_ptr<StructureDataChunk> colliderType(new StructureDataChunk());
colliderType->AddPrimitive<const char*>("CollisionShapeTypeQuad");
colliderType->AddPrimitive(std::string("&" + quadCollidersName + "[" + std::to_string(meshCount) + "]"));
colliderType->AddPrimitive(0.0f);
colliderType->AddPrimitive(1.0f);
colliderType->AddPrimitive<const char*>("NULL");
colliderTypeChunk->Add(std::move(colliderType));
std::unique_ptr<StructureDataChunk> collisionObject(new StructureDataChunk());
collisionObject->AddPrimitive(std::string("&" + colliderTypesName + "[" + std::to_string(meshCount) + "]"));
collisionObject->AddPrimitive<const char*>("NULL");
collisionObjectChunk->Add(std::move(collisionObject));
mOutput.quads.push_back(collider);
++meshCount;
}
}
std::unique_ptr<FileDefinition> collisionFileDef(new DataFileDefinition(
"struct CollisionQuad",
quadCollidersName,
true,
"_geo",
std::move(collidersChunk)
));
collisionFileDef->AddTypeHeader("\"physics/collision_quad.h\"");
collisionFileDef->AddTypeHeader("\"physics/collision.h\"");
collisionFileDef->AddTypeHeader("\"physics/collision_object.h\"");
fileDefinition.AddDefinition(std::move(collisionFileDef));
fileDefinition.AddDefinition(std::unique_ptr<FileDefinition>(new DataFileDefinition(
"struct ColliderTypeData",
colliderTypesName,
true,
"_geo",
std::move(colliderTypeChunk)
)));
fileDefinition.AddDefinition(std::unique_ptr<FileDefinition>(new DataFileDefinition(
"struct CollisionObject",
collisionObjectsName,
true,
"_geo",
std::move(collisionObjectChunk)
)));
fileDefinition.AddMacro(fileDefinition.GetMacroName("QUAD_COLLIDERS_COUNT"), std::to_string(meshCount));
mOutput.quadsName = collisionObjectsName;
}
const CollisionGeneratorOutput& CollisionGenerator::GetOutput() const {
return mOutput;
}

View file

@ -0,0 +1,43 @@
#ifndef __COLLISION_GENERATOR_H__
#define __COLLISION_GENERATOR_H__
#include "DefinitionGenerator.h"
#include "../DisplayListSettings.h"
struct CollisionQuad {
CollisionQuad(aiMesh* mesh, const aiMatrix4x4& transform);
aiVector3D corner;
aiVector3D edgeA;
float edgeALength;
aiVector3D edgeB;
float edgeBLength;
aiVector3D normal;
std::unique_ptr<DataChunk> Generate();
void ToLocalCoords(const aiVector3D& input, short& outX, short& outY);
bool IsCoplanar(ExtendedMesh& mesh, float relativeScale) const;
};
struct CollisionGeneratorOutput {
std::string quadsName;
std::vector<CollisionQuad> quads;
};
class CollisionGenerator : public DefinitionGenerator {
public:
CollisionGenerator(const DisplayListSettings& settings);
virtual bool ShouldIncludeNode(aiNode* node);
virtual void GenerateDefinitions(const aiScene* scene, CFileDefinition& fileDefinition);
const CollisionGeneratorOutput& GetOutput() const;
private:
DisplayListSettings mSettings;
CollisionGeneratorOutput mOutput;
};
#endif

View file

@ -0,0 +1,29 @@
#include "DefinitionGenerator.h"
DefinitionGenerator::DefinitionGenerator() {}
DefinitionGenerator::~DefinitionGenerator() {}
void DefinitionGenerator::TraverseScene(const aiScene* scene) {
if (!scene) {
return;
}
BeforeTraversal(scene);
TraverseNodes(scene->mRootNode);
}
void DefinitionGenerator::BeforeTraversal(const aiScene* scene) {}
void DefinitionGenerator::TraverseNodes(aiNode* node) {
if (!node) {
return;
}
if (ShouldIncludeNode(node)) {
mIncludedNodes.push_back(node);
}
for (unsigned int i = 0; i < node->mNumChildren; ++i) {
TraverseNodes(node->mChildren[i]);
}
}

View file

@ -0,0 +1,27 @@
#ifndef __DEFINITION_GENERATOR_H__
#define __DEFINITION_GENERATOR_H__
#include <assimp/scene.h>
#include <vector>
#include "../CFileDefinition.h"
class DefinitionGenerator {
public:
DefinitionGenerator();
virtual ~DefinitionGenerator();
void TraverseScene(const aiScene* scene);
virtual void BeforeTraversal(const aiScene* scene);
virtual bool ShouldIncludeNode(aiNode* node) = 0;
virtual void GenerateDefinitions(const aiScene* scene, CFileDefinition& fileDefinition) = 0;
protected:
std::vector<aiNode*> mIncludedNodes;
private:
void TraverseNodes(aiNode* node);
};
#endif

View file

@ -0,0 +1,198 @@
#include "LevelGenerator.h"
#include <set>
#include <string>
#include "../math/MES.h"
std::set<std::string> gPortalableSurfaces = {
"concrete_modular_wall001d",
"concrete_modular_ceiling001a",
"concrete_modular_floor001a",
};
LevelGenerator::LevelGenerator(
const DisplayListSettings& settings,
const StaticGeneratorOutput& staticOutput,
const CollisionGeneratorOutput& collisionOutput
) : mSettings(settings), mStaticOutput(staticOutput), mCollisionOutput(collisionOutput) {}
int levelEdgeKey(int a, int b) {
return (std::max(a, b) << 8) | std::min(a, b);
}
struct EdgeIndices {
uint8_t a;
uint8_t b;
};
std::unique_ptr<StructureDataChunk> LevelGenerator::CalculatePortalSingleSurface(CFileDefinition& fileDefinition, CollisionQuad& quad, ExtendedMesh& mesh, float scale) {
std::unique_ptr<StructureDataChunk> portalSurface(new StructureDataChunk());
std::unique_ptr<StructureDataChunk> vertices(new StructureDataChunk());
for (unsigned i = 0; i < mesh.mMesh->mNumVertices; ++i) {
short x, y;
quad.ToLocalCoords(mesh.mMesh->mVertices[i] * scale, x, y);
std::unique_ptr<StructureDataChunk> vertex(new StructureDataChunk());
vertex->AddPrimitive(x);
vertex->AddPrimitive(y);
vertices->Add(std::move(vertex));
}
std::string meshName(mesh.mMesh->mName.C_Str());
std::string verticesName = fileDefinition.GetUniqueName(meshName + "_portal_mesh");
fileDefinition.AddDefinition(std::unique_ptr<FileDefinition>(new DataFileDefinition("struct Vector2s16", verticesName, true, "_geo", std::move(vertices))));
std::map<int, int> edgeUseCount;
std::map<int, EdgeIndices> edgeDirection;
std::vector<int> edgeOrder;
for (unsigned faceIndex = 0; faceIndex < mesh.mMesh->mNumFaces; ++faceIndex) {
aiFace* face = &mesh.mMesh->mFaces[faceIndex];
for (unsigned index = 0; index < face->mNumIndices; ++index) {
unsigned currentIndex = face->mIndices[index];
unsigned nextIndex = face->mIndices[(index + 1) % face->mNumIndices];
int key = levelEdgeKey(currentIndex, nextIndex);
if (edgeUseCount.find(key) == edgeUseCount.end()) {
edgeUseCount.insert(std::make_pair(key, 1));
EdgeIndices indices = {(uint8_t)currentIndex, (uint8_t)nextIndex};
edgeDirection.insert(std::make_pair(key, indices));
edgeOrder.push_back(key);
} else {
edgeUseCount[key] = edgeUseCount[key] + 1;
}
}
}
// loops go first
std::sort(edgeOrder.begin(), edgeOrder.end(), [&](int a, int b) -> bool {
return edgeUseCount[a] < edgeUseCount[b];
});
int sideCount = 0;
std::unique_ptr<StructureDataChunk> edges(new StructureDataChunk());
for (auto key : edgeOrder) {
if (edgeUseCount[key] == 1) {
++sideCount;
}
std::unique_ptr<StructureDataChunk> edge(new StructureDataChunk());
EdgeIndices indices = edgeDirection[key];
edge->AddPrimitive((int)indices.a);
edge->AddPrimitive((int)indices.b);
edges->Add(std::move(edge));
}
std::string edgesName = fileDefinition.GetUniqueName(meshName + "_portal_edges");
fileDefinition.AddDefinition(std::unique_ptr<FileDefinition>(new DataFileDefinition("struct SurfaceEdge", edgesName, true, "_geo", std::move(edges))));
// vertices
portalSurface->AddPrimitive(verticesName);
// edges
portalSurface->AddPrimitive(edgesName);
// triangles
portalSurface->AddPrimitive<const char*>("NULL");
// sideCount
portalSurface->AddPrimitive(sideCount);
// edgesCount
portalSurface->AddPrimitive(edgeOrder.size());
// triangleCount
portalSurface->AddPrimitive(0);
portalSurface->Add(std::unique_ptr<DataChunk>(new StructureDataChunk(quad.edgeA)));
portalSurface->Add(std::unique_ptr<DataChunk>(new StructureDataChunk(quad.edgeB)));
portalSurface->Add(std::unique_ptr<DataChunk>(new StructureDataChunk(quad.corner)));
return portalSurface;
}
int LevelGenerator::CalculatePortalSurfaces(const aiScene* scene, CFileDefinition& fileDefinition, std::string& surfacesName, std::string& surfaceMappingName) {
int surfaceCount = 0;
std::unique_ptr<StructureDataChunk> portalSurfaceIndices(new StructureDataChunk());
std::unique_ptr<StructureDataChunk> portalSurfaces(new StructureDataChunk());
for (auto& collision : mCollisionOutput.quads) {
int startSurfaceCount = surfaceCount;
for (auto mesh : mStaticOutput.staticMeshes) {
aiMaterial* material = scene->mMaterials[mesh->mMesh->mMaterialIndex];
if (gPortalableSurfaces.find(ExtendedMesh::GetMaterialName(material)) == gPortalableSurfaces.end()) {
continue;;
}
if (collision.IsCoplanar(*mesh, mSettings.mCollisionScale)) {
portalSurfaces->Add(std::move(CalculatePortalSingleSurface(fileDefinition, collision, *mesh, mSettings.mCollisionScale)));
++surfaceCount;
}
}
std::unique_ptr<StructureDataChunk> indices(new StructureDataChunk());
indices->AddPrimitive(startSurfaceCount);
indices->AddPrimitive(surfaceCount);
portalSurfaceIndices->Add(std::move(indices));
}
surfacesName = fileDefinition.GetUniqueName("portal_surfaces");
std::unique_ptr<FileDefinition> portalSurfacesDef(new DataFileDefinition("struct PortalSurface", surfacesName, true, "_geo", std::move(portalSurfaces)));
portalSurfacesDef->AddTypeHeader("\"scene/portal_surface.h\"");
fileDefinition.AddDefinition(std::move(portalSurfacesDef));
surfaceMappingName = fileDefinition.GetUniqueName("collider_to_surface");
fileDefinition.AddDefinition(std::unique_ptr<FileDefinition>(new DataFileDefinition("struct PortalSurfaceMapping", surfaceMappingName, true, "_geo", std::move(portalSurfaceIndices))));
return surfaceCount;
}
void LevelGenerator::CalculateBoundingBoxes(const aiScene* scene, CFileDefinition& fileDefinition, std::string& boundingBoxesName) {
std::unique_ptr<StructureDataChunk> boundingBoxes(new StructureDataChunk());
for (auto& mesh : mStaticOutput.staticMeshes) {
std::unique_ptr<StructureDataChunk> sphere(new StructureDataChunk());
sphere->AddPrimitive((short)(mesh->bbMin.x * mSettings.mGraphicsScale + 0.5f));
sphere->AddPrimitive((short)(mesh->bbMin.y * mSettings.mGraphicsScale + 0.5f));
sphere->AddPrimitive((short)(mesh->bbMin.z * mSettings.mGraphicsScale + 0.5f));
sphere->AddPrimitive((short)(mesh->bbMax.x * mSettings.mGraphicsScale + 0.5f));
sphere->AddPrimitive((short)(mesh->bbMax.y * mSettings.mGraphicsScale + 0.5f));
sphere->AddPrimitive((short)(mesh->bbMax.z * mSettings.mGraphicsScale + 0.5f));
boundingBoxes->Add(std::move(sphere));
}
boundingBoxesName = fileDefinition.GetUniqueName("bounding_boxes");
std::unique_ptr<FileDefinition> boundingBoxDef(new DataFileDefinition("struct BoundingBoxs16", boundingBoxesName, true, "_geo", std::move(boundingBoxes)));
fileDefinition.AddDefinition(std::move(boundingBoxDef));
}
void LevelGenerator::GenerateDefinitions(const aiScene* scene, CFileDefinition& fileDefinition) {
std::string portalSurfaces;
std::string portalSurfaceMapping;
int portalSurfacesCount = CalculatePortalSurfaces(scene, fileDefinition, portalSurfaces, portalSurfaceMapping);
std::string boundingBoxes;
CalculateBoundingBoxes(scene, fileDefinition, boundingBoxes);
std::unique_ptr<StructureDataChunk> levelDef(new StructureDataChunk());
levelDef->AddPrimitive(mCollisionOutput.quadsName);
levelDef->AddPrimitive(mStaticOutput.staticContentName);
levelDef->AddPrimitive(boundingBoxes);
levelDef->AddPrimitive(portalSurfaces);
levelDef->AddPrimitive(portalSurfaceMapping);
levelDef->AddPrimitive(mCollisionOutput.quads.size());
levelDef->AddPrimitive(mStaticOutput.staticMeshes.size());
levelDef->AddPrimitive(portalSurfacesCount);
fileDefinition.AddDefinition(std::unique_ptr<FileDefinition>(new DataFileDefinition("struct LevelDefinition", fileDefinition.GetUniqueName("level"), false, "_geo", std::move(levelDef))));
}

View file

@ -0,0 +1,29 @@
#ifndef __LEVEL_GENERATOR_H__
#define __LEVEL_GENERATOR_H__
#include "DefinitionGenerator.h"
#include "StaticGenerator.h"
#include "CollisionGenerator.h"
#include "../DisplayListSettings.h"
class LevelGenerator {
public:
LevelGenerator(
const DisplayListSettings& settings,
const StaticGeneratorOutput& staticOutput,
const CollisionGeneratorOutput& collisionOutput
);
void GenerateDefinitions(const aiScene* scene, CFileDefinition& fileDefinition);
private:
DisplayListSettings mSettings;
StaticGeneratorOutput mStaticOutput;
CollisionGeneratorOutput mCollisionOutput;
std::unique_ptr<StructureDataChunk> CalculatePortalSingleSurface(CFileDefinition& fileDefinition, CollisionQuad& quad, ExtendedMesh& mesh, float scale);
int CalculatePortalSurfaces(const aiScene* scene, CFileDefinition& fileDefinition, std::string& surfacesName, std::string& surfaceMappingName);
void CalculateBoundingBoxes(const aiScene* scene, CFileDefinition& fileDefinition, std::string& boundingBoxesName);
};
#endif

View file

@ -0,0 +1,64 @@
#include "MaterialGenerator.h"
#include "../StringUtils.h"
MaterialGenerator::MaterialGenerator(const DisplayListSettings& settings): mSettings(settings) {}
bool MaterialGenerator::ShouldIncludeNode(aiNode* node) {
return false;
}
void MaterialGenerator::GenerateDefinitions(const aiScene* scene, CFileDefinition& fileDefinition) {
std::set<std::shared_ptr<TextureDefinition>> textures;
for (auto& entry : mSettings.mMaterials) {
for (int i = 0; i < 8; ++i) {
if (entry.second->mState.tiles[i].texture) {
textures.insert(entry.second->mState.tiles[i].texture);
}
}
}
for (auto& texture : textures) {
fileDefinition.AddDefinition(std::move(texture->GenerateDefinition(fileDefinition.GetUniqueName(texture->Name()), "_mat")));
}
int index = 0;
std::unique_ptr<StructureDataChunk> materialList(new StructureDataChunk());
std::unique_ptr<StructureDataChunk> revertList(new StructureDataChunk());
for (auto& entry : mSettings.mMaterials) {
std::string name = fileDefinition.GetUniqueName(entry.first);
DisplayList dl(name);
entry.second->Write(fileDefinition, mSettings.mDefaultMaterialState, dl.GetDataChunk());
std::unique_ptr<FileDefinition> material = dl.Generate("_mat");
materialList->AddPrimitive(material->GetName());
fileDefinition.AddDefinition(std::move(material));
std::string revertName = fileDefinition.GetUniqueName(entry.first + "_revert");
DisplayList revertDL(revertName);
generateMaterial(fileDefinition, entry.second->mState, mSettings.mDefaultMaterialState, revertDL.GetDataChunk());
std::unique_ptr<FileDefinition> materialRevert = revertDL.Generate("_mat");
revertList->AddPrimitive(materialRevert->GetName());
fileDefinition.AddDefinition(std::move(materialRevert));
fileDefinition.AddMacro(MaterialIndexMacroName(entry.second->mName), std::to_string(index));
++index;
}
fileDefinition.AddMacro(fileDefinition.GetMacroName("MATERIAL_COUNT"), std::to_string(index));
fileDefinition.AddDefinition(std::unique_ptr<FileDefinition>(new DataFileDefinition("Gfx*", fileDefinition.GetUniqueName("material_list"), true, "_mat", std::move(materialList))));
fileDefinition.AddDefinition(std::unique_ptr<FileDefinition>(new DataFileDefinition("Gfx*", fileDefinition.GetUniqueName("material_revert_list"), true, "_mat", std::move(revertList))));
}
std::string MaterialGenerator::MaterialIndexMacroName(const std::string& materialName) {
std::string result = materialName;
std::transform(materialName.begin(), materialName.end(), result.begin(), ::toupper);
makeCCompatible(result);
return result + "_INDEX";
}

View file

@ -0,0 +1,19 @@
#ifndef __MATERIAL_GENERATOR_H__
#define __MATERIAL_GENERATOR_H__
#include "DefinitionGenerator.h"
#include "../DisplayListSettings.h"
class MaterialGenerator : public DefinitionGenerator {
public:
MaterialGenerator(const DisplayListSettings& settings);
virtual bool ShouldIncludeNode(aiNode* node);
virtual void GenerateDefinitions(const aiScene* scene, CFileDefinition& fileDefinition);
static std::string MaterialIndexMacroName(const std::string& materialName);
private:
DisplayListSettings mSettings;
};
#endif

View file

@ -0,0 +1,75 @@
#include "MeshDefinitionGenerator.h"
#include "../RenderChunk.h"
#include "../MeshWriter.h"
bool extractMaterialAutoTileParameters(Material* material, double& sTile, double& tTile) {
if (!material) {
return false;
}
auto sTileEntry = material->mProperties.find("tileSizeS");
auto tTileEntry = material->mProperties.find("tileSizeT");
if (sTileEntry != material->mProperties.end()) {
sTile = std::atof(sTileEntry->second.c_str());
tTile = tTileEntry == material->mProperties.end() ? sTile : std::atof(tTileEntry->second.c_str());
return true;
}
return false;
}
MeshDefinitionGenerator::MeshDefinitionGenerator(const DisplayListSettings& settings) :
DefinitionGenerator(),
mSettings(settings) {
}
bool MeshDefinitionGenerator::ShouldIncludeNode(aiNode* node) {
return node->mName.C_Str()[0] != '@' && node->mNumMeshes > 0;
}
void MeshDefinitionGenerator::AppendRenderChunks(const aiScene* scene, aiNode* node, CFileDefinition& fileDefinition, DisplayListSettings& settings, std::vector<RenderChunk>& renderChunks) {
for (unsigned meshIndex = 0; meshIndex < node->mNumMeshes; ++meshIndex) {
std::shared_ptr<ExtendedMesh> mesh = fileDefinition.GetExtendedMesh(scene->mMeshes[node->mMeshes[meshIndex]]);
mesh = mesh->Transform(node->mTransformation);
std::string materialName = ExtendedMesh::GetMaterialName(scene->mMaterials[mesh->mMesh->mMaterialIndex]);
auto material = settings.mMaterials.find(materialName);
Material* materialPtr = NULL;
if (material != settings.mMaterials.end()) {
materialPtr = material->second.get();
}
double sTile;
double tTile;
if (extractMaterialAutoTileParameters(materialPtr, sTile, tTile)) {
mesh->CubeProjectTex(
settings.mCollisionScale / (double)sTile,
settings.mCollisionScale / (double)tTile
);
}
renderChunks.push_back(RenderChunk(
std::pair<Bone*, Bone*>(NULL, NULL),
mesh,
materialPtr
));
}
}
void MeshDefinitionGenerator::GenerateDefinitions(const aiScene* scene, CFileDefinition& fileDefinition) {
std::vector<RenderChunk> renderChunks;
for (auto node = mIncludedNodes.begin(); node != mIncludedNodes.end(); ++node) {
AppendRenderChunks(scene, *node, fileDefinition, mSettings, renderChunks);
}
generateMesh(scene, fileDefinition, renderChunks, mSettings, "_geo");
}

View file

@ -0,0 +1,20 @@
#ifndef __MESH_DEFINTION_GENERATOR_H__
#define __MESH_DEFINTION_GENERATOR_H__
#include "DefinitionGenerator.h"
#include "../DisplayListSettings.h"
#include "../RenderChunk.h"
class MeshDefinitionGenerator : public DefinitionGenerator {
public:
MeshDefinitionGenerator(const DisplayListSettings& settings);
virtual bool ShouldIncludeNode(aiNode* node);
virtual void GenerateDefinitions(const aiScene* scene, CFileDefinition& fileDefinition);
static void AppendRenderChunks(const aiScene* scene, aiNode* node, CFileDefinition& fileDefinition, DisplayListSettings& settings, std::vector<RenderChunk>& renderChunks);
private:
DisplayListSettings mSettings;
};
#endif

View file

@ -0,0 +1,65 @@
#include "StaticGenerator.h"
#include "../StringUtils.h"
#include "../MeshWriter.h"
#include "MeshDefinitionGenerator.h"
#include "MaterialGenerator.h"
#include "../RenderChunk.h"
StaticGenerator::StaticGenerator(const DisplayListSettings& settings) : DefinitionGenerator(), mSettings(settings) {
}
bool StaticGenerator::ShouldIncludeNode(aiNode* node) {
return StartsWith(node->mName.C_Str(), "@static") && node->mNumMeshes > 0;
}
void StaticGenerator::GenerateDefinitions(const aiScene* scene, CFileDefinition& fileDefinition) {
DisplayListSettings settings = mSettings;
settings.mMaterials.clear();
std::vector<StaticContentElement> elements;
for (auto node = mIncludedNodes.begin(); node != mIncludedNodes.end(); ++node) {
std::vector<RenderChunk> renderChunks;
MeshDefinitionGenerator::AppendRenderChunks(scene, *node, fileDefinition, mSettings, renderChunks);
if (renderChunks.size()) {
StaticContentElement element;
if (renderChunks[0].mMaterial) {
settings.mDefaultMaterialState = renderChunks[0].mMaterial->mState;
element.materialName = MaterialGenerator::MaterialIndexMacroName(renderChunks[0].mMaterial->mName);
} else {
element.materialName = "0";
}
element.meshName = generateMesh(scene, fileDefinition, renderChunks, settings, "_geo");
elements.push_back(element);
mOutput.staticMeshes.push_back(renderChunks[0].mMesh);
}
}
std::unique_ptr<StructureDataChunk> staticContentList(new StructureDataChunk());
for (auto& it : elements) {
std::unique_ptr<StructureDataChunk> element(new StructureDataChunk());
element->AddPrimitive(it.meshName);
element->AddPrimitive(it.materialName);
staticContentList->Add(std::move(element));
}
mOutput.staticContentName = fileDefinition.GetUniqueName("static");
std::unique_ptr<FileDefinition> fileDef(new DataFileDefinition("struct StaticContentElement", mOutput.staticContentName, true, "_geo", std::move(staticContentList)));
fileDef->AddTypeHeader("\"levels/level_def_gen.h\"");
fileDefinition.AddDefinition(std::move(fileDef));
}
const StaticGeneratorOutput& StaticGenerator::GetOutput() const {
return mOutput;
}

View file

@ -0,0 +1,31 @@
#ifndef __STATIC_GENERATOR_H__
#define __STATIC_GENERATOR_H__
#include "DefinitionGenerator.h"
#include "../DisplayListSettings.h"
struct StaticContentElement {
std::string meshName;
std::string materialName;
};
struct StaticGeneratorOutput {
std::string staticContentName;
std::vector<std::shared_ptr<ExtendedMesh>> staticMeshes;
};
class StaticGenerator : public DefinitionGenerator {
public:
StaticGenerator(const DisplayListSettings& settings);
virtual bool ShouldIncludeNode(aiNode* node);
virtual void GenerateDefinitions(const aiScene* scene, CFileDefinition& fileDefinition);
const StaticGeneratorOutput& GetOutput() const;
private:
DisplayListSettings mSettings;
StaticGeneratorOutput mOutput;
};
#endif

View file

@ -0,0 +1,203 @@
#include "DataChunk.h"
DataChunk::DataChunk(): mCachedLength(0) {}
DataChunk::~DataChunk() {}
int DataChunk::GetEstimatedLength() {
if (!mCachedLength) {
mCachedLength = CalculateEstimatedLength();
}
return mCachedLength;
}
DataChunkNop::DataChunkNop() : DataChunk() {}
bool DataChunkNop::Output(std::ostream& output, int indentLevel, int linePrefix) {
return false;
}
int DataChunkNop::CalculateEstimatedLength() {
return 0;
}
StringDataChunk::StringDataChunk(const std::string& value): PrimitiveDataChunk(EscapeAndWrapString(value)) {}
char StringDataChunk::EscapeCharacter(char input) {
switch (input) {
case '"': return '"';
case '\t': return 't';
case '\n': return 'n';
case '\r': return 'r';
}
return 0;
}
std::string StringDataChunk::EscapeAndWrapString(const std::string& string) {
std::ostringstream result;
result << '"';
for (auto currChar : string) {
char escapeChar = EscapeCharacter(currChar);
if (escapeChar) {
result << '\\' << escapeChar;
} else {
result << escapeChar;
}
}
result << '"';
return result.str();
}
StructureEntryDataChunk::StructureEntryDataChunk(const std::string& name, std::unique_ptr<DataChunk> entry) :
DataChunk(),
mName(name),
mEntry(std::move(entry)) {
}
bool StructureEntryDataChunk::Output(std::ostream& output, int indentLevel, int linePrefix) {
output << "." << mName << " = ";
mEntry->Output(output, indentLevel, linePrefix);
return true;
}
int StructureEntryDataChunk::CalculateEstimatedLength() {
return mName.length() + 4 + mEntry->GetEstimatedLength();
}
StructureDataChunk::StructureDataChunk(): DataChunk() {}
StructureDataChunk::StructureDataChunk(const aiVector3D& vector) : StructureDataChunk() {
AddPrimitive(vector.x);
AddPrimitive(vector.y);
AddPrimitive(vector.z);
}
StructureDataChunk::StructureDataChunk(const aiQuaternion& quat) : StructureDataChunk() {
AddPrimitive(quat.x);
AddPrimitive(quat.y);
AddPrimitive(quat.z);
AddPrimitive(quat.w);
}
void StructureDataChunk::Add(std::unique_ptr<DataChunk> entry) {
mChildren.push_back(std::move(entry));
}
void StructureDataChunk::Add(const std::string& name, std::unique_ptr<DataChunk> entry) {
mChildren.push_back(std::unique_ptr<DataChunk>(new StructureEntryDataChunk(
name,
std::move(entry)
)));
}
#define MAX_CHARS_PER_LINE 80
#define SPACES_PER_INDENT 4
bool StructureDataChunk::Output(std::ostream& output, int indentLevel, int linePrefix) {
output << '{';
OutputChildren(mChildren, output, indentLevel, linePrefix + GetEstimatedLength(), true);
output << '}';
return true;
}
int StructureDataChunk::CalculateEstimatedLength() {
int result = 2; // parenthesis
for (auto it = mChildren.begin(); it != mChildren.end(); ++it){
result += (*it)->GetEstimatedLength();
}
if (mChildren.size()) {
result += 2 * (mChildren.size() - 1);
}
return result;
}
void StructureDataChunk::OutputIndent(std::ostream& output, int indentLevel) {
for (int i = 0; i < indentLevel * SPACES_PER_INDENT; ++i) {
output << ' ';
}
}
void StructureDataChunk::OutputChildren(std::vector<std::unique_ptr<DataChunk>>& children, std::ostream& output, int indentLevel, int totalLength, bool trailingComma) {
bool needsComma = false;
if (totalLength < MAX_CHARS_PER_LINE) {
for (size_t i = 0; i < children.size(); ++i) {
if (needsComma) {
output << ", ";
}
needsComma = children[i]->Output(output, indentLevel, 0);
}
} else {
output << '\n';
++indentLevel;
for (size_t i = 0; i < children.size(); ++i) {
OutputIndent(output, indentLevel);
if (children[i]->Output(output, indentLevel, indentLevel * SPACES_PER_INDENT) && (i < children.size() - 1 || trailingComma)) {
output << ",\n";
} else {
output << "\n";
}
}
--indentLevel;
OutputIndent(output, indentLevel);
}
}
MacroDataChunk::MacroDataChunk(const std::string& macroName): DataChunk(), mMacroName(macroName), mSingleLine(true) {}
MacroDataChunk::MacroDataChunk(const std::string& macroName, bool singleLine) : DataChunk(), mMacroName(macroName), mSingleLine(singleLine) {}
void MacroDataChunk::Add(std::unique_ptr<DataChunk> entry) {
mParameters.push_back(std::move(entry));
}
bool MacroDataChunk::Output(std::ostream& output, int indentLevel, int linePrefix) {
output << mMacroName << '(';
StructureDataChunk::OutputChildren(mParameters, output, indentLevel, mSingleLine ? 0 : linePrefix + GetEstimatedLength(), false);
output << ')';
return true;
}
int MacroDataChunk::CalculateEstimatedLength() {
int result = mMacroName.length() + 2; // parenthesis
for (auto it = mParameters.begin(); it != mParameters.end(); ++it){
result += (*it)->GetEstimatedLength();
}
if (mParameters.size()) {
result += 2 * (mParameters.size() - 1);
}
return result;
}
CommentDataChunk::CommentDataChunk(const std::string& comment) : DataChunk(), mComment(comment) {}
bool CommentDataChunk::Output(std::ostream& output, int indentLevel, int linePrefix) {
output << "/* " << mComment << " */";
return false;
}
int CommentDataChunk::CalculateEstimatedLength() {
return 6 + mComment.length();
}

View file

@ -0,0 +1,140 @@
#ifndef __DATA_CHUNK_H__
#define __DATA_CHUNK_H__
#include <ostream>
#include <sstream>
#include <memory>
#include <vector>
#include <assimp/vector3.h>
#include <assimp/quaternion.h>
class DataChunk {
public:
DataChunk();
virtual ~DataChunk();
virtual bool Output(std::ostream& output, int indentLevel, int linePrefix) = 0;
int GetEstimatedLength();
protected:
virtual int CalculateEstimatedLength() = 0;
private:
int mCachedLength;
};
class DataChunkNop : public DataChunk {
public:
DataChunkNop();
virtual bool Output(std::ostream& output, int indentLevel, int linePrefix);
protected:
virtual int CalculateEstimatedLength();
};
template <typename T>
class PrimitiveDataChunk : public DataChunk {
public:
PrimitiveDataChunk(const T& value): DataChunk(), mValue(value) {}
virtual bool Output(std::ostream& output, int indentLevel, int linePrefix) {
output << mValue;
return true;
}
protected:
virtual int CalculateEstimatedLength() {
std::ostringstream tmp;
Output(tmp, 0, 0);
return tmp.tellp();
}
private:
T mValue;
};
class StringDataChunk : public PrimitiveDataChunk<std::string> {
public:
StringDataChunk(const std::string& value);
private:
static char EscapeCharacter(char input);
static std::string EscapeAndWrapString(const std::string& string);
};
class StructureEntryDataChunk : public DataChunk {
public:
StructureEntryDataChunk(const std::string& name, std::unique_ptr<DataChunk> entry);
virtual bool Output(std::ostream& output, int indentLevel, int linePrefix);
protected:
virtual int CalculateEstimatedLength();
private:
std::string mName;
std::unique_ptr<DataChunk> mEntry;
};
class StructureDataChunk : public DataChunk {
public:
StructureDataChunk();
StructureDataChunk(const aiVector3D& vector);
StructureDataChunk(const aiQuaternion& quat);
void Add(std::unique_ptr<DataChunk> entry);
template <typename T>
void AddPrimitive(const T& primitive) {
Add(std::unique_ptr<DataChunk>(new PrimitiveDataChunk<T>(primitive)));
}
void Add(const std::string& name, std::unique_ptr<DataChunk> entry);
template <typename T>
void AddPrimitive(const std::string& name, const T& primitive) {
Add(std::unique_ptr<DataChunk>(new StructureEntryDataChunk(
name,
std::unique_ptr<DataChunk>(new PrimitiveDataChunk<T>(primitive))
)));
}
virtual bool Output(std::ostream& output, int indentLevel, int linePrefix);
static void OutputIndent(std::ostream& output, int indentLevel);
static void OutputChildren(std::vector<std::unique_ptr<DataChunk>>& children, std::ostream& output, int indentLevel, int totalLength, bool trailingComma);
protected:
virtual int CalculateEstimatedLength();
private:
std::vector<std::unique_ptr<DataChunk>> mChildren;
};
class MacroDataChunk : public DataChunk {
public:
MacroDataChunk(const std::string& macroName);
MacroDataChunk(const std::string& macroName, bool singleLine);
void Add(std::unique_ptr<DataChunk> entry);
template <typename T>
void AddPrimitive(const T& primitive) {
Add(std::unique_ptr<DataChunk>(new PrimitiveDataChunk<T>(primitive)));
}
virtual bool Output(std::ostream& output, int indentLevel, int linePrefix);
protected:
virtual int CalculateEstimatedLength();
private:
std::string mMacroName;
std::vector<std::unique_ptr<DataChunk>> mParameters;
bool mSingleLine;
};
class CommentDataChunk : public DataChunk {
public:
CommentDataChunk(const std::string& comment);
virtual bool Output(std::ostream& output, int indentLevel, int linePrefix);
protected:
virtual int CalculateEstimatedLength();
private:
std::string mComment;
};
#endif

View file

@ -0,0 +1,92 @@
#include "FileDefinition.h"
#include "../StringUtils.h"
FileDefinition::FileDefinition(const std::string& type, const std::string& name, bool isArray, std::string location) :
mType(type),
mName(name),
mIsArray(isArray),
mLocation(location),
mForResource(NULL) {
}
FileDefinition::FileDefinition(const std::string& type, const std::string& name, bool isArray, std::string location, const void* forResource) :
mType(type),
mName(name),
mIsArray(isArray),
mLocation(location),
mForResource(forResource) {
}
FileDefinition::~FileDefinition() {
}
void FileDefinition::GenerateDeclaration(std::ostream& output) {
output << "extern " << mType << " " << mName;
if (mIsArray) {
output << "[]";
}
}
std::string FileDefinition::GetLocation() {
return mLocation;
}
void FileDefinition::AddTypeHeader(const std::string& typeHeader) {
mTypeHeaders.insert(typeHeader);
}
const std::set<std::string>& FileDefinition::GetTypeHeaders() {
return mTypeHeaders;
}
const void* FileDefinition::ForResource() const {
return mForResource;
}
const std::string& FileDefinition::GetName() const {
return mName;
}
DataFileDefinition::DataFileDefinition(const std::string& type, const std::string& name, bool isArray, std::string location, std::unique_ptr<DataChunk> data):
FileDefinition(type, name, isArray, location),
mData(std::move(data)) {
}
DataFileDefinition::DataFileDefinition(const std::string& type, const std::string& name, bool isArray, std::string location, std::unique_ptr<DataChunk> data, const void* forResource):
FileDefinition(type, name, isArray, location, forResource),
mData(std::move(data)) {
}
void DataFileDefinition::Generate(std::ostream& output) {
int start = (int)output.tellp();
output << mType << " " << mName;
if (mIsArray) {
output << "[]";
}
output << " = ";
mData->Output(output, 0, (int)output.tellp() - start);
}
RawFileDefinition::RawFileDefinition(const std::string& type, const std::string& name, bool isArray, std::string location, const std::string& content) : FileDefinition(type, name, isArray, location), mContent(content) {}
void RawFileDefinition::Generate(std::ostream& output) {
output << mType << " " << mName;
if (mIsArray) {
output << "[] = {" << std::endl;
output << Indent(mContent, " ");
output << "};";
} else {
output << " = " << mContent << ";";
}
}

View file

@ -0,0 +1,56 @@
#ifndef __FILE_DEFINITION_H__
#define __FILE_DEFINITION_H__
#include <string>
#include <ostream>
#include <set>
#include "DataChunk.h"
class FileDefinition {
public:
FileDefinition(const std::string& type, const std::string& name, bool isArray, std::string location);
FileDefinition(const std::string& type, const std::string& name, bool isArray, std::string location, const void* forResource);
virtual ~FileDefinition();
virtual void Generate(std::ostream& output) = 0;
void GenerateDeclaration(std::ostream& output);
std::string GetLocation();
void AddTypeHeader(const std::string& typeHeader);
const std::set<std::string>& GetTypeHeaders();
const void* ForResource() const;
const std::string& GetName() const;
protected:
std::string mType;
std::string mName;
bool mIsArray;
std::string mLocation;
const void* mForResource;
std::set<std::string> mTypeHeaders;
};
class DataFileDefinition : public FileDefinition {
public:
DataFileDefinition(const std::string& type, const std::string& name, bool isArray, std::string location, std::unique_ptr<DataChunk> data);
DataFileDefinition(const std::string& type, const std::string& name, bool isArray, std::string location, std::unique_ptr<DataChunk> data, const void* forResource);
virtual void Generate(std::ostream& output);
private:
std::unique_ptr<DataChunk> mData;
};
class RawFileDefinition : public FileDefinition {
public:
RawFileDefinition(const std::string& type, const std::string& name, bool isArray, std::string location, const std::string& content);
virtual void Generate(std::ostream& output);
private:
std::string mContent;
};
#endif

View file

@ -0,0 +1,97 @@
#include "CombineMode.h"
#include <map>
#define DEFINE_NAMED_COMBINE_MODE(combineMode) {#combineMode, DEFINE_COMBINE_MODE_LERP(combineMode)}
std::map<std::string, ColorCombineMode> gNamedCombineModes = {
DEFINE_NAMED_COMBINE_MODE(G_CC_PRIMITIVE),
DEFINE_NAMED_COMBINE_MODE(G_CC_SHADE),
DEFINE_NAMED_COMBINE_MODE(G_CC_MODULATEI),
DEFINE_NAMED_COMBINE_MODE(G_CC_MODULATEIA),
DEFINE_NAMED_COMBINE_MODE(G_CC_MODULATEIDECALA),
DEFINE_NAMED_COMBINE_MODE(G_CC_MODULATERGB),
DEFINE_NAMED_COMBINE_MODE(G_CC_MODULATERGBA),
DEFINE_NAMED_COMBINE_MODE(G_CC_MODULATERGBDECALA),
DEFINE_NAMED_COMBINE_MODE(G_CC_MODULATEI_PRIM),
DEFINE_NAMED_COMBINE_MODE(G_CC_MODULATEIA_PRIM),
DEFINE_NAMED_COMBINE_MODE(G_CC_MODULATEIDECALA_PRIM),
DEFINE_NAMED_COMBINE_MODE(G_CC_MODULATERGB_PRIM),
DEFINE_NAMED_COMBINE_MODE(G_CC_MODULATERGBA_PRIM),
DEFINE_NAMED_COMBINE_MODE(G_CC_MODULATERGBDECALA_PRIM),
DEFINE_NAMED_COMBINE_MODE(G_CC_DECALRGB),
DEFINE_NAMED_COMBINE_MODE(G_CC_DECALRGBA),
DEFINE_NAMED_COMBINE_MODE(G_CC_BLENDI),
DEFINE_NAMED_COMBINE_MODE(G_CC_BLENDIA),
DEFINE_NAMED_COMBINE_MODE(G_CC_BLENDIDECALA),
DEFINE_NAMED_COMBINE_MODE(G_CC_BLENDRGBA),
DEFINE_NAMED_COMBINE_MODE(G_CC_BLENDRGBDECALA),
DEFINE_NAMED_COMBINE_MODE(G_CC_ADDRGB),
DEFINE_NAMED_COMBINE_MODE(G_CC_ADDRGBDECALA),
DEFINE_NAMED_COMBINE_MODE(G_CC_REFLECTRGB),
DEFINE_NAMED_COMBINE_MODE(G_CC_REFLECTRGBDECALA),
DEFINE_NAMED_COMBINE_MODE(G_CC_HILITERGB),
DEFINE_NAMED_COMBINE_MODE(G_CC_HILITERGBA),
DEFINE_NAMED_COMBINE_MODE(G_CC_HILITERGBDECALA),
DEFINE_NAMED_COMBINE_MODE(G_CC_SHADEDECALA),
DEFINE_NAMED_COMBINE_MODE(G_CC_BLENDPE),
DEFINE_NAMED_COMBINE_MODE(G_CC_BLENDPEDECALA),
/* oddball modes */
DEFINE_NAMED_COMBINE_MODE(_G_CC_BLENDPE),
DEFINE_NAMED_COMBINE_MODE(_G_CC_BLENDPEDECALA),
DEFINE_NAMED_COMBINE_MODE(_G_CC_TWOCOLORTEX),
/* used for 1-cycle sparse mip-maps, primitive color has color of lowest LOD */
DEFINE_NAMED_COMBINE_MODE(_G_CC_SPARSEST),
DEFINE_NAMED_COMBINE_MODE(G_CC_TEMPLERP),
/* typical CC cycle 1 modes, usually followed by other cycle 2 modes */
DEFINE_NAMED_COMBINE_MODE(G_CC_TRILERP),
DEFINE_NAMED_COMBINE_MODE(G_CC_INTERFERENCE),
/*
* One-cycle color convert operation
*/
DEFINE_NAMED_COMBINE_MODE(G_CC_1CYUV2RGB),
/*
* NOTE: YUV2RGB expects TF step1 color conversion to occur in 2nd clock.
* Therefore, CC looks for step1 results in TEXEL1
*/
DEFINE_NAMED_COMBINE_MODE(G_CC_YUV2RGB),
/* typical CC cycle 2 modes */
DEFINE_NAMED_COMBINE_MODE(G_CC_PASS2),
DEFINE_NAMED_COMBINE_MODE(G_CC_MODULATEI2),
DEFINE_NAMED_COMBINE_MODE(G_CC_MODULATEIA2),
DEFINE_NAMED_COMBINE_MODE(G_CC_MODULATERGB2),
DEFINE_NAMED_COMBINE_MODE(G_CC_MODULATERGBA2),
DEFINE_NAMED_COMBINE_MODE(G_CC_MODULATEI_PRIM2),
DEFINE_NAMED_COMBINE_MODE(G_CC_MODULATEIA_PRIM2),
DEFINE_NAMED_COMBINE_MODE(G_CC_MODULATERGB_PRIM2),
DEFINE_NAMED_COMBINE_MODE(G_CC_MODULATERGBA_PRIM2),
DEFINE_NAMED_COMBINE_MODE(G_CC_DECALRGB2),
/*
* ?
DEFINE_NAMED_COMBINE_MODE(G_CC_DECALRGBA2),
*/
DEFINE_NAMED_COMBINE_MODE(G_CC_BLENDI2),
DEFINE_NAMED_COMBINE_MODE(G_CC_BLENDIA2),
DEFINE_NAMED_COMBINE_MODE(G_CC_CHROMA_KEY2),
DEFINE_NAMED_COMBINE_MODE(G_CC_HILITERGB2),
DEFINE_NAMED_COMBINE_MODE(G_CC_HILITERGBA2),
DEFINE_NAMED_COMBINE_MODE(G_CC_HILITERGBDECALA2),
DEFINE_NAMED_COMBINE_MODE(G_CC_HILITERGBPASSA2),
};
bool combineModeWithName(const std::string& name, ColorCombineMode& result) {
auto it = gNamedCombineModes.find(name);
if (it == gNamedCombineModes.end()) {
return false;
}
result = it->second;
return true;
}

View file

@ -0,0 +1,131 @@
#ifndef __COMBINE_MODE_H__
#define __COMBINE_MODE_H__
#include "MaterialState.h"
#include <string>
/*
* G_SETCOMBINE: color combine modes
*/
/* Color combiner constants: */
#define G_CCMUX_COMBINED ColorCombineSource::Combined
#define G_CCMUX_TEXEL0 ColorCombineSource::Texel0
#define G_CCMUX_TEXEL1 ColorCombineSource::Texel1
#define G_CCMUX_PRIMITIVE ColorCombineSource::PrimitiveColor
#define G_CCMUX_SHADE ColorCombineSource::ShadeColor
#define G_CCMUX_ENVIRONMENT ColorCombineSource::EnvironmentColor
#define G_CCMUX_CENTER ColorCombineSource::KeyCenter
#define G_CCMUX_SCALE ColorCombineSource::KeyScale
#define G_CCMUX_COMBINED_ALPHA ColorCombineSource::CombinedAlpha
#define G_CCMUX_TEXEL0_ALPHA ColorCombineSource::Texture0Alpha
#define G_CCMUX_TEXEL1_ALPHA ColorCombineSource::Texture1Alpha
#define G_CCMUX_PRIMITIVE_ALPHA ColorCombineSource::PrimitiveAlpha
#define G_CCMUX_SHADE_ALPHA ColorCombineSource::ShadedAlpha
#define G_CCMUX_ENV_ALPHA ColorCombineSource::EnvironmentAlpha
#define G_CCMUX_LOD_FRACTION ColorCombineSource::LODFraction
#define G_CCMUX_PRIM_LOD_FRAC ColorCombineSource::PrimitiveLODFraction
#define G_CCMUX_NOISE ColorCombineSource::Noise
#define G_CCMUX_K4 ColorCombineSource::ConvertK4
#define G_CCMUX_K5 ColorCombineSource::ConvertK5
#define G_CCMUX_1 ColorCombineSource::_1
#define G_CCMUX_0 ColorCombineSource::_0
/* Alpha combiner constants: */
#define G_ACMUX_COMBINED AlphaCombineSource::CombinedAlpha
#define G_ACMUX_TEXEL0 AlphaCombineSource::Texture0Alpha
#define G_ACMUX_TEXEL1 AlphaCombineSource::Texture1Alpha
#define G_ACMUX_PRIMITIVE AlphaCombineSource::PrimitiveAlpha
#define G_ACMUX_SHADE AlphaCombineSource::ShadedAlpha
#define G_ACMUX_ENVIRONMENT AlphaCombineSource::EnvironmentAlpha
#define G_ACMUX_LOD_FRACTION AlphaCombineSource::LODFraction
#define G_ACMUX_PRIM_LOD_FRAC AlphaCombineSource::PrimitiveLODFraction
#define G_ACMUX_1 AlphaCombineSource::_1
#define G_ACMUX_0 AlphaCombineSource::_0
/* typical CC cycle 1 modes */
#define G_CC_PRIMITIVE 0, 0, 0, PRIMITIVE, 0, 0, 0, PRIMITIVE
#define G_CC_SHADE 0, 0, 0, SHADE, 0, 0, 0, SHADE
#define G_CC_MODULATEI TEXEL0, 0, SHADE, 0, 0, 0, 0, SHADE
#define G_CC_MODULATEIA TEXEL0, 0, SHADE, 0, TEXEL0, 0, SHADE, 0
#define G_CC_MODULATEIDECALA TEXEL0, 0, SHADE, 0, 0, 0, 0, TEXEL0
#define G_CC_MODULATERGB G_CC_MODULATEI
#define G_CC_MODULATERGBA G_CC_MODULATEIA
#define G_CC_MODULATERGBDECALA G_CC_MODULATEIDECALA
#define G_CC_MODULATEI_PRIM TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE
#define G_CC_MODULATEIA_PRIM TEXEL0, 0, PRIMITIVE, 0, TEXEL0, 0, PRIMITIVE, 0
#define G_CC_MODULATEIDECALA_PRIM TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, TEXEL0
#define G_CC_MODULATERGB_PRIM G_CC_MODULATEI_PRIM
#define G_CC_MODULATERGBA_PRIM G_CC_MODULATEIA_PRIM
#define G_CC_MODULATERGBDECALA_PRIM G_CC_MODULATEIDECALA_PRIM
#define G_CC_DECALRGB 0, 0, 0, TEXEL0, 0, 0, 0, SHADE
#define G_CC_DECALRGBA 0, 0, 0, TEXEL0, 0, 0, 0, TEXEL0
#define G_CC_BLENDI ENVIRONMENT, SHADE, TEXEL0, SHADE, 0, 0, 0, SHADE
#define G_CC_BLENDIA ENVIRONMENT, SHADE, TEXEL0, SHADE, TEXEL0, 0, SHADE, 0
#define G_CC_BLENDIDECALA ENVIRONMENT, SHADE, TEXEL0, SHADE, 0, 0, 0, TEXEL0
#define G_CC_BLENDRGBA TEXEL0, SHADE, TEXEL0_ALPHA, SHADE, 0, 0, 0, SHADE
#define G_CC_BLENDRGBDECALA TEXEL0, SHADE, TEXEL0_ALPHA, SHADE, 0, 0, 0, TEXEL0
#define G_CC_ADDRGB 1, 0, TEXEL0, SHADE, 0, 0, 0, SHADE
#define G_CC_ADDRGBDECALA 1, 0, TEXEL0, SHADE, 0, 0, 0, TEXEL0
#define G_CC_REFLECTRGB ENVIRONMENT, 0, TEXEL0, SHADE, 0, 0, 0, SHADE
#define G_CC_REFLECTRGBDECALA ENVIRONMENT, 0, TEXEL0, SHADE, 0, 0, 0, TEXEL0
#define G_CC_HILITERGB PRIMITIVE, SHADE, TEXEL0, SHADE, 0, 0, 0, SHADE
#define G_CC_HILITERGBA PRIMITIVE, SHADE, TEXEL0, SHADE, PRIMITIVE, SHADE, TEXEL0, SHADE
#define G_CC_HILITERGBDECALA PRIMITIVE, SHADE, TEXEL0, SHADE, 0, 0, 0, TEXEL0
#define G_CC_SHADEDECALA 0, 0, 0, SHADE, 0, 0, 0, TEXEL0
#define G_CC_BLENDPE PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, SHADE, 0
#define G_CC_BLENDPEDECALA PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, 0, 0, 0, TEXEL0
/* oddball modes */
#define _G_CC_BLENDPE ENVIRONMENT, PRIMITIVE, TEXEL0, PRIMITIVE, TEXEL0, 0, SHADE, 0
#define _G_CC_BLENDPEDECALA ENVIRONMENT, PRIMITIVE, TEXEL0, PRIMITIVE, 0, 0, 0, TEXEL0
#define _G_CC_TWOCOLORTEX PRIMITIVE, SHADE, TEXEL0, SHADE, 0, 0, 0, SHADE
/* used for 1-cycle sparse mip-maps, primitive color has color of lowest LOD */
#define _G_CC_SPARSEST PRIMITIVE, TEXEL0, LOD_FRACTION, TEXEL0, PRIMITIVE, TEXEL0, LOD_FRACTION, TEXEL0
#define G_CC_TEMPLERP TEXEL1, TEXEL0, PRIM_LOD_FRAC, TEXEL0, TEXEL1, TEXEL0, PRIM_LOD_FRAC, TEXEL0
/* typical CC cycle 1 modes, usually followed by other cycle 2 modes */
#define G_CC_TRILERP TEXEL1, TEXEL0, LOD_FRACTION, TEXEL0, TEXEL1, TEXEL0, LOD_FRACTION, TEXEL0
#define G_CC_INTERFERENCE TEXEL0, 0, TEXEL1, 0, TEXEL0, 0, TEXEL1, 0
/*
* One-cycle color convert operation
*/
#define G_CC_1CYUV2RGB TEXEL0, K4, K5, TEXEL0, 0, 0, 0, SHADE
/*
* NOTE: YUV2RGB expects TF step1 color conversion to occur in 2nd clock.
* Therefore, CC looks for step1 results in TEXEL1
*/
#define G_CC_YUV2RGB TEXEL1, K4, K5, TEXEL1, 0, 0, 0, 0
/* typical CC cycle 2 modes */
#define G_CC_PASS2 0, 0, 0, COMBINED, 0, 0, 0, COMBINED
#define G_CC_MODULATEI2 COMBINED, 0, SHADE, 0, 0, 0, 0, SHADE
#define G_CC_MODULATEIA2 COMBINED, 0, SHADE, 0, COMBINED, 0, SHADE, 0
#define G_CC_MODULATERGB2 G_CC_MODULATEI2
#define G_CC_MODULATERGBA2 G_CC_MODULATEIA2
#define G_CC_MODULATEI_PRIM2 COMBINED, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE
#define G_CC_MODULATEIA_PRIM2 COMBINED, 0, PRIMITIVE, 0, COMBINED, 0, PRIMITIVE, 0
#define G_CC_MODULATERGB_PRIM2 G_CC_MODULATEI_PRIM2
#define G_CC_MODULATERGBA_PRIM2 G_CC_MODULATEIA_PRIM2
#define G_CC_DECALRGB2 0, 0, 0, COMBINED, 0, 0, 0, SHADE
/*
* ?
#define G_CC_DECALRGBA2 COMBINED, SHADE, COMBINED_ALPHA, SHADE, 0, 0, 0, SHADE
*/
#define G_CC_BLENDI2 ENVIRONMENT, SHADE, COMBINED, SHADE, 0, 0, 0, SHADE
#define G_CC_BLENDIA2 ENVIRONMENT, SHADE, COMBINED, SHADE, COMBINED, 0, SHADE, 0
#define G_CC_CHROMA_KEY2 TEXEL0, CENTER, SCALE, 0, 0, 0, 0, 0
#define G_CC_HILITERGB2 ENVIRONMENT, COMBINED, TEXEL0, COMBINED, 0, 0, 0, SHADE
#define G_CC_HILITERGBA2 ENVIRONMENT, COMBINED, TEXEL0, COMBINED, ENVIRONMENT, COMBINED, TEXEL0, COMBINED
#define G_CC_HILITERGBDECALA2 ENVIRONMENT, COMBINED, TEXEL0, COMBINED, 0, 0, 0, TEXEL0
#define G_CC_HILITERGBPASSA2 ENVIRONMENT, COMBINED, TEXEL0, COMBINED, 0, 0, 0, COMBINED
#define DEFINE_COMBINE_MODE_LERP(c0, c1, c2, c3, a0, a1, a2, a3) \
ColorCombineMode(G_CCMUX_##c0, G_CCMUX_##c1, G_CCMUX_##c2, G_CCMUX_##c3, G_ACMUX_##a0, G_ACMUX_##a1, G_ACMUX_##a2, G_ACMUX_##a3)
#define DEFINE_COMBINE_MODE(terms) DEFINE_COMBINE_MODE_LERP(terms)
bool combineModeWithName(const std::string& name, ColorCombineMode& result);
#endif

View file

@ -0,0 +1,51 @@
#include "Material.h"
#include "../StringUtils.h"
#include "../CFileDefinition.h"
Material::Material(const std::string& name): mName(name) {}
void Material::Write(CFileDefinition& fileDef, const MaterialState& from, StructureDataChunk& output) {
generateMaterial(fileDef, from, mState, output);
}
int Material::TextureWidth(Material* material) {
if (!material) {
return 0;
}
for (int i = 0; i < MAX_TILE_COUNT; ++i) {
if (material->mState.tiles[i].isOn && material->mState.tiles[i].texture) {
return material->mState.tiles[i].texture->Width();
}
}
return 0;
}
int Material::TextureHeight(Material* material) {
if (!material) {
return 0;
}
for (int i = 0; i < MAX_TILE_COUNT; ++i) {
if (material->mState.tiles[i].isOn && material->mState.tiles[i].texture) {
return material->mState.tiles[i].texture->Height();
}
}
return 0;
}
VertexType Material::GetVertexType(Material* material) {
if (!material) {
return VertexType::PosUVNormal;
}
if (material->mState.geometryModes.knownFlags & material->mState.geometryModes.flags & (int)GeometryMode::G_LIGHTING) {
return VertexType::PosUVNormal;
}
return VertexType::PosUVColor;
}

View file

@ -0,0 +1,34 @@
#ifndef _MATERIAL_H
#define _MATERIAL_H
#include <string>
#include <vector>
#include <memory>
#include <map>
#include <ostream>
#include "TextureDefinition.h"
#include "../DisplayList.h"
#include "../ExtendedMesh.h"
#include "MaterialState.h"
#include "../definitions/DataChunk.h"
#include "../CFileDefinition.h"
#include "MaterialEnums.h"
class Material {
public:
Material(const std::string& name);
std::string mName;
MaterialState mState;
std::map<std::string, std::string> mProperties;
void Write(CFileDefinition& fileDef, const MaterialState& from, StructureDataChunk& output);
static int TextureWidth(Material* material);
static int TextureHeight(Material* material);
static VertexType GetVertexType(Material* material);
};
#endif

View file

@ -0,0 +1,170 @@
#include "MaterialEnums.h"
const char* gGeometryModeNames[GEOMETRY_MODE_COUNT] = {
"G_ZBUFFER",
"G_SHADE",
"G_TEXTURE_ENABLE",
"G_SHADING_SMOOTH",
"G_CULL_FRONT",
"G_CULL_BACK",
"G_FOG",
"G_LIGHTING",
"G_TEXTURE_GEN",
"G_TEXTURE_GEN_LINEAR",
"G_LOD",
"G_CLIPPING",
};
const char* gCycleTypeNames[(int)CycleType::Count] = {
"Unknown",
"G_CYC_1CYCLE",
"G_CYC_2CYCLE",
"G_CYC_COPY",
"G_CYC_FILL",
};
const char* gColorCombineSourceNames[(int)ColorCombineSource::Count] = {
"COMBINED",
"TEXEL0",
"TEXEL1",
"PRIMITIVE",
"SHADE",
"ENVIRONMENT",
"CENTER",
"SCALE",
"COMBINED_ALPHA",
"TEXEL0_ALPHA",
"TEXEL1_ALPHA",
"PRIMITIVE_ALPHA",
"SHADED_ALPHA",
"ENVIRONMENT_ALPHA",
"LOD_FRACTION",
"PRIM_LOD_FRAC",
"NOISE",
"K4",
"K5",
"1",
"0",
};
bool gCanUseColorCombineSource[4][(int)ColorCombineSource::Count] = {
{true, true, true, true, true, true, false, false, false, false, false, false, false, false, false, false, true, false, false, true, true},
{true, true, true, true, true, true, true, false, false, false, false, false, false, false, false, false, false, true, false, false, true},
{true, true, true, true, true, true, false, true, true, true, true, true, true, true, true, true, false, false, true, false, true},
{true, true, true, true, true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, true, true},
};
bool canUseColorCombineSource(int offset, ColorCombineSource source) {
return gCanUseColorCombineSource[offset][(int)source];
}
const char* gAlphaCombineSourceNames[(int)AlphaCombineSource::Count] = {
"COMBINED",
"TEXEL0",
"TEXEL1",
"PRIMITIVE",
"SHADE",
"ENVIRONMENT",
"LOD_FRACTION",
"PRIM_LOD_FRAC",
"1",
"0",
};
bool gCanUseAlphaCombineSources[4][(int)AlphaCombineSource::Count] = {
{true, true, true, true, true, true, false, false, true, true},
{true, true, true, true, true, true, false, false, true, true},
{false, true, true, true, true, true, true, true, false, true},
{true, true, true, true, true, true, false, false, true, true},
};
bool canUseAlphaCombineSource(int offset, ColorCombineSource source) {
return gCanUseAlphaCombineSources[offset][(int)source];
}
const char* gPipelineModeNames[(int)PipelineMode::Count] = {
"Unknown",
"G_PM_1PRIMITIVE",
"G_PM_NPRIMITIVE",
};
const char* gPerspectiveModeNames[(int)PerspectiveMode::Count] = {
"Unknown",
"G_TP_NONE",
"G_TP_PERSP",
};
const char* gTextureDetailNames[(int)TextureDetail::Count] = {
"Unknown",
"G_TD_CLAMP",
"G_TD_SHARPEN",
"G_TD_DETAIL",
};
const char* gTextureLODNames[] = {
"Unknown",
"G_TL_TILE",
"G_TL_LOD",
};
const char* gTextureLUTNames[] = {
"Unknown",
"G_TT_NONE",
"G_TT_RGBA16",
"G_TT_IA16",
};
const char* gTextureFilterNames[] = {
"Unknown",
"G_TF_POINT",
"G_TF_AVERAGE",
"G_TF_BILERP",
};
const char* gTextureConvertNames[] = {
"Unknown",
"G_TC_CONV",
"G_TC_FILTCONV",
"G_TC_FILT",
};
const char* gCombineKeyNames[] = {
"Unknown",
"G_CK_NONE",
"G_CK_KEY",
};
const char* gCotherDitherNames[] = {
"Unknown",
"G_CD_MAGICSQ",
"G_CD_BAYER",
"G_CD_NOISE",
"G_CD_DISABLE",
};
const char* gAlphaDitherNames[] = {
"Unknown",
"G_AD_PATTERN",
"G_AD_NOTPATTERN",
"G_AD_NOISE",
"G_AD_DISABLE",
};
const char* gAlphaCompareNames[] = {
"Unknown",
"G_AC_NONE",
"G_AC_THRESHOLD",
"G_AC_DITHER",
};
const char* gDepthSourceNames[] = {
"Unknown",
"G_ZS_PIXEL",
"G_ZS_PRIM",
};
bool gCanUseAlphaBlendSource[2][7] = {
{false, false, true, true, true, false, true},
{true, true, false, false, false, true, true},
};

View file

@ -0,0 +1,199 @@
#ifndef __MATERIAL_ENUMS_H__
#define __MATERIAL_ENUMS_H__
enum class GeometryMode {
None = 0,
G_ZBUFFER = (1 << 0),
G_SHADE = (1 << 1),
G_TEXTURE_ENABLE = (1 << 2),
G_SHADING_SMOOTH = (1 << 3),
G_CULL_FRONT = (1 << 4),
G_CULL_BACK = (1 << 5),
G_FOG = (1 << 6),
G_LIGHTING = (1 << 7),
G_TEXTURE_GEN = (1 << 8),
G_TEXTURE_GEN_LINEAR = (1 << 9),
G_LOD = (1 << 10),
G_CLIPPING = (1 << 11),
};
extern const char* gGeometryModeNames[];
#define GEOMETRY_MODE_COUNT 12
enum class CycleType {
Unknown,
_1Cycle,
_2Cycle,
Copy,
Fill,
Count,
};
extern const char* gCycleTypeNames[];
enum class ColorCombineSource {
Combined,
Texel0,
Texel1,
PrimitiveColor,
ShadeColor,
EnvironmentColor,
KeyCenter,
KeyScale,
CombinedAlpha,
Texture0Alpha,
Texture1Alpha,
PrimitiveAlpha,
ShadedAlpha,
EnvironmentAlpha,
LODFraction,
PrimitiveLODFraction,
Noise,
ConvertK4,
ConvertK5,
_1,
_0,
Count,
};
extern const char* gColorCombineSourceNames[];
bool canUseColorCombineSource(int offset, ColorCombineSource source);
enum class AlphaCombineSource {
CombinedAlpha,
Texture0Alpha,
Texture1Alpha,
PrimitiveAlpha,
ShadedAlpha,
EnvironmentAlpha,
LODFraction,
PrimitiveLODFraction,
_1,
_0,
Count,
};
extern const char* gAlphaCombineSourceNames[];
bool canUseAlphaCombineSource(int offset, AlphaCombineSource source);
enum class PipelineMode {
Unknown,
_1Primitive,
_NPrimitive,
Count,
};
extern const char* gPipelineModeNames[];
enum class PerspectiveMode {
Unknown,
None,
Perspective,
Count,
};
extern const char* gPerspectiveModeNames[];
enum class TextureDetail {
Unknown,
Clamp,
Sharpen,
Detail,
Count,
};
extern const char* gTextureDetailNames[];
enum class TextureLOD {
Unknown,
Tile,
LOD,
Count,
};
extern const char* gTextureLODNames[];
enum class TextureLUT {
Unknown,
None,
RGBA16,
IA16,
Count,
};
extern const char* gTextureLUTNames[];
enum class TextureFilter {
Unknown,
Point,
Average,
Bilerp,
Count,
};
extern const char* gTextureFilterNames[];
enum class TextureConvert {
Unknown,
Conv,
FiltConv,
Filt,
Count,
};
extern const char* gTextureConvertNames[];
enum class CombineKey {
Unknown,
None,
Key,
Count,
};
extern const char* gCombineKeyNames[];
enum class ColorDither {
Unknown,
MagicSQ,
Bayer,
Noise,
Disable,
Count,
};
extern const char* gCotherDitherNames[];
enum class AlphaDither {
Unknown,
Pattern,
NotPattern,
Noise,
Disable,
Count,
};
extern const char* gAlphaDitherNames[];
enum class AlphaCompare {
Unknown,
None,
Threshold,
Dither,
Count,
};
extern const char* gAlphaCompareNames[];
enum class DepthSource {
Unknown,
Pixel,
Primitive,
Count,
};
extern const char* gDepthSourceNames[];
#endif

View file

@ -0,0 +1,740 @@
#include "MaterialParser.h"
#include "yaml-cpp/yaml.h"
#include <algorithm>
#include <string.h>
#include <sstream>
#include <string>
#include <stdexcept>
#include <map>
#include "./TextureCache.h"
#include "../FileUtils.h"
#include "./RenderMode.h"
#include "CombineMode.h"
TextureCache gTextureCache;
ParseError::ParseError(const std::string& message) :
mMessage(message) {
}
ParseResult::ParseResult(const std::string& insideFolder) : mInsideFolder(insideFolder) {}
std::string formatError(const std::string& message, const YAML::Mark& mark) {
std::stringstream output;
output << "error at line " << mark.line + 1 << ", column "
<< mark.column + 1 << ": " << message;
return output.str();
}
int parseInteger(const YAML::Node& node, ParseResult& output, int min, int max) {
if (!node.IsDefined() || !node.IsScalar()) {
output.mErrors.push_back(ParseError(formatError("Expected a number", node.Mark())));
return 0;
}
int result = 0;
try {
result = std::atoi(node.Scalar().c_str());
} catch (std::invalid_argument const& err) {
output.mErrors.push_back(ParseError(formatError("Expected a number", node.Mark())));
return 0;
}
if (result < min || result > max) {
std::stringstream errorMessage;
errorMessage << "Expected a number between " << min << " and " << max;
output.mErrors.push_back(ParseError(formatError(errorMessage.str(), node.Mark())));
return result;
}
return result;
}
int parseOptionalInteger(const YAML::Node& node, ParseResult& output, int min, int max, int defaultValue) {
if (!node.IsDefined()) {
return defaultValue;
}
return parseInteger(node, output, min, max);
}
std::string parseString(const YAML::Node& node, ParseResult& output) {
if (!node.IsDefined() || !node.IsScalar()) {
output.mErrors.push_back(ParseError(formatError("Expected a string", node.Mark())));
return "";
}
return node.as<std::string>();
}
template <typename T>
T parseEnumType(const YAML::Node& node, ParseResult& output, const char** names, T defaultValue, int count) {
if (!node.IsDefined()) {
return defaultValue;
}
if (!node.IsScalar()) {
output.mErrors.push_back(ParseError(formatError("Invalid type for enum", node.Mark())));
return defaultValue;
}
std::string asString = node.as<std::string>();
for (int i = 0; i < count; ++i) {
if (asString == names[i]) {
return (T)i;
}
}
output.mErrors.push_back(ParseError(formatError("Invalid type for enum", node.Mark())));
return defaultValue;
}
bool parseMaterialColor(const YAML::Node& node, PixelRGBAu8& color, ParseResult& output) {
if (!node.IsDefined()) {
return false;
}
if (!node.IsMap()) {
output.mErrors.push_back(ParseError(formatError("Color is expected to be map with r,g,b", node.Mark())));
return false;
}
color.r = parseInteger(node["r"], output, 0, 255);
color.g = parseInteger(node["g"], output, 0, 255);
color.b = parseInteger(node["b"], output, 0, 255);
color.a = parseOptionalInteger(node["a"], output, 0, 255, 255);
return true;
}
void parsePrimColor(const YAML::Node& node, MaterialState& state, ParseResult& output) {
bool result = parseMaterialColor(node, state.primitiveColor, output);
if (!result) {
return;
}
state.usePrimitiveColor = true;
YAML::Node m = node["m"];
if (m.IsDefined()) {
state.primitiveM = parseInteger(m, output, 0, 255);
}
YAML::Node l = node["l"];
if (l.IsDefined()) {
state.primitiveL = parseInteger(l, output, 0, 255);
}
}
VertexType parseMaterialVertexType(const YAML::Node& node) {
if (node.IsDefined() && node.IsScalar() && node.Scalar() == "Normal") {
return VertexType::PosUVNormal;
}
return VertexType::PosUVColor;
}
G_IM_FMT parseTextureFormat(const YAML::Node& node, ParseResult& output) {
std::string asString = parseString(node, output);
if (asString == "G_IM_FMT_RGBA") {
return G_IM_FMT::G_IM_FMT_RGBA;
}
if (asString == "G_IM_FMT_RGBA") {
return G_IM_FMT::G_IM_FMT_YUV;
}
if (asString == "G_IM_FMT_CI") {
return G_IM_FMT::G_IM_FMT_CI;
}
if (asString == "G_IM_FMT_I") {
return G_IM_FMT::G_IM_FMT_I;
}
if (asString == "G_IM_FMT_IA") {
return G_IM_FMT::G_IM_FMT_IA;
}
output.mErrors.push_back(ParseError(formatError("Texture format should be G_IM_FMT_RGBA, G_IM_FMT_YUV, G_IM_FMT_CI, G_IM_FMT_I, or G_IM_FMT_IA", node.Mark())));
return G_IM_FMT::G_IM_FMT_RGBA;
}
G_IM_SIZ parseTextureSize(const YAML::Node& node, ParseResult& output) {
std::string asString = parseString(node, output);
if (asString == "G_IM_SIZ_32b") {
return G_IM_SIZ::G_IM_SIZ_32b;
}
if (asString == "G_IM_SIZ_16b") {
return G_IM_SIZ::G_IM_SIZ_16b;
}
if (asString == "G_IM_SIZ_8b") {
return G_IM_SIZ::G_IM_SIZ_8b;
}
if (asString == "G_IM_SIZ_4b") {
return G_IM_SIZ::G_IM_SIZ_4b;
}
output.mErrors.push_back(ParseError(formatError("Texture size should be G_IM_SIZ_32b, G_IM_SIZ_16b, G_IM_SIZ_8b, or G_IM_SIZ_4b", node.Mark())));
return G_IM_SIZ::G_IM_SIZ_16b;
}
G_IM_SIZ gDefaultImageSize[] = {
// G_IM_FMT_RGBA
G_IM_SIZ::G_IM_SIZ_16b,
// G_IM_FMT_YUV
G_IM_SIZ::G_IM_SIZ_16b,
// G_IM_FMT_CI
G_IM_SIZ::G_IM_SIZ_8b,
// G_IM_FMT_I
G_IM_SIZ::G_IM_SIZ_8b,
// G_IM_FMT_IA
G_IM_SIZ::G_IM_SIZ_16b,
};
std::shared_ptr<TextureDefinition> parseTextureDefinition(const YAML::Node& node, ParseResult& output) {
if (!node.IsDefined()) {
return NULL;
}
std::string filename;
bool hasFormat = false;
G_IM_FMT requestedFormat;
bool hasSize = false;
G_IM_SIZ requestedSize;
TextureDefinitionEffect effects = (TextureDefinitionEffect)0;
if (node.IsScalar()) {
filename = parseString(node, output);
} else if (node.IsMap()) {
filename = parseString(node["filename"], output);
auto yamlFormat = node["fmt"];
if (yamlFormat.IsDefined()) {
requestedFormat = parseTextureFormat(yamlFormat, output);
hasFormat = true;
}
auto yamlSize = node["siz"];
if (yamlSize.IsDefined()) {
requestedSize = parseTextureSize(yamlSize, output);
hasSize = true;
}
auto twoTone = node["twoTone"];
if (twoTone.IsDefined() && twoTone.as<bool>()) {
effects = (TextureDefinitionEffect)((int)effects | (int)TextureDefinitionEffect::TwoToneGrayscale);
if (!yamlFormat.IsDefined()) {
requestedFormat = G_IM_FMT::G_IM_FMT_I;
hasFormat = true;
}
}
} else {
output.mErrors.push_back(ParseError(formatError(std::string("Tile should be a file name or object") + filename, node.Mark())));
return NULL;
}
filename = Join(output.mInsideFolder, filename);
if (!FileExists(filename)) {
output.mErrors.push_back(ParseError(formatError(std::string("Could not open file ") + filename, node.Mark())));
return NULL;
}
G_IM_FMT format;
G_IM_SIZ size;
if (hasFormat && hasSize) {
format = requestedFormat;
size = requestedSize;
} else {
TextureDefinition::DetermineIdealFormat(filename, format, size);
if (hasFormat) {
if (format != requestedFormat) {
size = gDefaultImageSize[(int)requestedFormat];
}
format = requestedFormat;
}
if (hasSize) {
size = requestedSize;
}
}
if (!isImageFormatSupported(format, size)) {
output.mErrors.push_back(ParseError(formatError("Unsupported image format ", node.Mark())));
return NULL;
}
return gTextureCache.GetTexture(filename, format, size, effects);
}
int parseRenderModeFlags(const YAML::Node& node, ParseResult& output) {
if (!node.IsDefined()) {
return 0;
}
if (!node.IsSequence()) {
output.mErrors.push_back(ParseError(formatError("Render mode flags should be an array of strings", node.Mark())));
return 0;
}
int result = 0;
for (auto it = node.begin(); it != node.end(); ++it) {
if (!it->second.IsScalar()) {
output.mErrors.push_back(ParseError(formatError("Flags should be a list of strings", it->second.Mark())));
continue;
}
std::string asString = it->second.as<std::string>();
int singleFlag = 0;
if (!renderModeGetFlagValue(asString, singleFlag)) {
output.mErrors.push_back(ParseError(formatError("Invalid flag", it->second.Mark())));
continue;
}
result |= singleFlag;
}
return result;
}
int parseBlendMode(const YAML::Node& node, ParseResult& output) {
if (!node.IsDefined()) {
return 0;
}
if (!node.IsSequence() || node.size() != 4) {
output.mErrors.push_back(ParseError(formatError("Render blend mode should be an array of 4 strings", node.Mark())));
return 0;
}
int params[4];
for (int i = 0; i < 4; ++i) {
const YAML::Node& element = node[i];
params[i] = 0;
if (!element.IsScalar()) {
output.mErrors.push_back(ParseError(formatError("Expected a string", node.Mark())));
continue;
}
std::string asString = element.as<std::string>();
if (!renderModeGetBlendModeValue(asString, i, params[i])) {
output.mErrors.push_back(ParseError(formatError("Invalid blend mode", node.Mark())));
}
}
return GBL_c1(params[0], params[1], params[2], params[3]);
}
void parseSingleRenderMode(const YAML::Node& node, RenderModeState& renderMode, ParseResult& output) {
if (node.IsScalar()) {
std::string asString = node.as<std::string>();
if (!findRenderModeByName(asString, renderMode)) {
output.mErrors.push_back(ParseError(formatError("Invalid render mode", node.Mark())));
return;
}
return;
}
if (node.IsMap()) {
renderMode.data =
parseRenderModeFlags(node["flags"], output) |
parseBlendMode(node["blend"], output);
return;
}
output.mErrors.push_back(ParseError(formatError("Invalid render mode", node.Mark())));
}
void parseRenderMode(const YAML::Node& node, MaterialState& state, ParseResult& output) {
if (!node.IsDefined()) {
state.hasRenderMode = false;
return;
}
state.hasRenderMode = true;
if (node.IsScalar() || node.IsMap()) {
parseSingleRenderMode(node, state.cycle1RenderMode, output);
state.cycle2RenderMode = state.cycle1RenderMode;
return;
}
if (node.IsSequence() && node.size() == 2) {
parseSingleRenderMode(node[0], state.cycle1RenderMode, output);
parseSingleRenderMode(node[1], state.cycle1RenderMode, output);
return;
}
output.mErrors.push_back(ParseError(formatError("Invalid render mode", node.Mark())));
}
void parseColorCombineMode(const YAML::Node& node, ColorCombineMode& combineMode, ParseResult& output) {
if (!node.IsDefined()) {
combineMode.color[0] = ColorCombineSource::_0;
combineMode.color[1] = ColorCombineSource::_0;
combineMode.color[2] = ColorCombineSource::_0;
combineMode.color[3] = ColorCombineSource::_0;
return;
}
if (!node.IsSequence() || node.size() != 4) {
output.mErrors.push_back(ParseError(formatError("Blend mode should be an array of strings", node.Mark())));
return;
}
for (int i = 0; i < 4; ++i) {
combineMode.color[i] = parseEnumType(node[i], output, gColorCombineSourceNames, ColorCombineSource::_0, (int)ColorCombineSource::Count);
}
}
void parseAlphaCombineMode(const YAML::Node& node, ColorCombineMode& combineMode, ParseResult& output) {
if (!node.IsDefined()) {
combineMode.alpha[0] = AlphaCombineSource::_0;
combineMode.alpha[1] = AlphaCombineSource::_0;
combineMode.alpha[2] = AlphaCombineSource::_0;
combineMode.alpha[3] = AlphaCombineSource::_1;
return;
}
if (!node.IsSequence() || node.size() != 4) {
output.mErrors.push_back(ParseError(formatError("Blend mode should be an array of strings", node.Mark())));
return;
}
for (int i = 0; i < 4; ++i) {
combineMode.alpha[i] = parseEnumType(node[i], output, gAlphaCombineSourceNames, AlphaCombineSource::_0, (int)AlphaCombineSource::Count);
}
}
void parseSingleCombineMode(const YAML::Node& node, ColorCombineMode& combineMode, ParseResult& output) {
if (!node.IsDefined()) {
output.mErrors.push_back(ParseError(formatError("Combine mode should be an object with a color and alpha", node.Mark())));
return;
}
if (node.IsMap()) {
parseColorCombineMode(node["color"], combineMode, output);
parseAlphaCombineMode(node["alpha"], combineMode, output);
return;
}
if (node.IsScalar()) {
std::string name = node.as<std::string>();
if (!combineModeWithName(name, combineMode)) {
output.mErrors.push_back(ParseError(formatError(name + " is not a valid name for a combine mode", node.Mark())));
}
return;
}
output.mErrors.push_back(ParseError(formatError("Combine mode should be an object with a color and alpha", node.Mark())));
}
void parseCombineMode(const YAML::Node& node, MaterialState& state, ParseResult& output) {
if (!node.IsDefined()) {
return;
}
state.hasCombineMode = true;
if (node.IsMap() || node.IsScalar()) {
parseSingleCombineMode(node, state.cycle1Combine, output);
state.cycle2Combine = state.cycle1Combine;
return;
}
if (node.IsSequence() && node.size() == 2) {
parseSingleCombineMode(node[0], state.cycle1Combine, output);
parseSingleCombineMode(node[1], state.cycle2Combine, output);
return;
}
output.mErrors.push_back(ParseError(formatError("Combine mode should be a map with a color and alpha", node.Mark())));
}
void parseGeometryModeSequence(const YAML::Node& node, FlagList& result, bool flagValue, ParseResult& output) {
if (!node.IsDefined()) {
return;
}
if (!node.IsSequence()) {
output.mErrors.push_back(ParseError(formatError("Should be a string array", node.Mark())));
return;
}
for (std::size_t i = 0; i < node.size(); ++i) {
const YAML::Node& element = node[i];
if (!element.IsScalar()) {
output.mErrors.push_back(ParseError(formatError("Expected a string", element.Mark())));
continue;
}
int mask = 1 << parseEnumType(element, output, gGeometryModeNames, 0, GEOMETRY_MODE_COUNT);
result.SetFlag(mask, flagValue);
}
}
FlagList parseGeometryMode(const YAML::Node& node, ParseResult& output) {
FlagList result;
if (!node.IsDefined()) {
return result;
}
parseGeometryModeSequence(node["clear"], result, false, output);
parseGeometryModeSequence(node["set"], result, true, output);
return result;
}
void parseTexture(const YAML::Node& node, TextureState& state, ParseResult& output) {
if (!node.IsDefined()) {
return;
}
if (!node.IsMap()) {
output.mErrors.push_back(ParseError(formatError("gSPTexture should be a map", node.Mark())));
}
state.sc = parseOptionalInteger(node["sc"], output, 0, 0xFFFF, 0xFFFF);
state.tc = parseOptionalInteger(node["tc"], output, 0, 0xFFFF, 0xFFFF);
state.level = parseOptionalInteger(node["level"], output, 0, 7, 0);
state.tile = parseOptionalInteger(node["tile"], output, 0, 7, 0);
state.isOn = true;
}
bool isEvenLog2(int size) {
return ((size - 1) & size) == 0;
}
int log2(int size) {
int result = 0;
--size;
while (size) {
++result;
size >>= 1;
}
return result;
}
void parseTextureCoordinate(const YAML::Node& node, TextureCoordinateState& state, ParseResult& output) {
if (!node.IsDefined()) {
return;
}
auto wrap = node["wrap"];
if (wrap.IsDefined()) {
state.wrap = wrap.as<bool>();
}
auto mirror = node["mirror"];
if (mirror.IsDefined()) {
state.mirror = mirror.as<bool>();
}
auto mask = node["mask"];
if (mask.IsDefined()) {
state.mask = mask.as<int>();
}
auto shift = node["shift"];
if (shift.IsDefined()) {
state.shift = shift.as<int>();
}
auto offset = node["offset"];
if (offset.IsDefined()) {
state.offset = offset.as<int>();
}
auto limit = node["limit"];
if (limit.IsDefined()) {
state.limit = limit.as<int>();
}
}
bool parseSingleTile(const YAML::Node& node, TileState& state, ParseResult& output) {
state.texture = parseTextureDefinition(node, output);
state.isOn = node.IsDefined();
if (state.texture) {
state.format = state.texture->Format();
state.size = state.texture->Size();
if (!state.texture->GetLine(state.line)) {
output.mErrors.push_back(ParseError(formatError("Texture line width should be a multiple of 64 bits", node.Mark())));
}
state.sCoord.mask = log2(state.texture->Width());
state.tCoord.mask = log2(state.texture->Height());
state.sCoord.limit = (state.texture->Width() - 1) * 4;
state.tCoord.limit = (state.texture->Height() - 1) * 4;
}
if (node.IsMap()) {
auto yamlFormat = node["fmt"];
if (!state.texture && yamlFormat.IsDefined()) {
state.format = parseTextureFormat(yamlFormat, output);
}
auto yamlSize = node["siz"];
if (!state.texture && yamlSize.IsDefined()) {
state.size = parseTextureSize(yamlSize, output);
}
state.tmem = parseOptionalInteger(node["tmem"], output, 0, 511, 0);
state.pallete = parseOptionalInteger(node["pallete"], output, 0, 15, 0);
}
parseTextureCoordinate(node["s"], state.sCoord, output);
parseTextureCoordinate(node["t"], state.tCoord, output);
return state.isOn;
}
void parseTiles(const YAML::Node& node, MaterialState& state, ParseResult& output) {
if (!node.IsDefined()) {
return;
}
if (node.IsMap() || node.IsScalar()) {
if (parseSingleTile(node, state.tiles[0], output) || state.textureState.isOn) {
state.textureState.isOn = true;
}
return;
}
if (node.IsSequence()) {
if (node.size() > 8) {
output.mErrors.push_back(ParseError(formatError("Only up to 8 tiles are supported", node.Mark())));
}
for (std::size_t i = 0; i < node.size() && i < 8; ++i) {
const YAML::Node& element = node[i];
if (!element.IsNull() && parseSingleTile(element, state.tiles[i], output)) {
state.textureState.isOn = true;
}
}
}
output.mErrors.push_back(ParseError(formatError("Expected a tile or array of tiles", node.Mark())));
}
ColorCombineMode gTwoToneCombineMode(
ColorCombineSource::PrimitiveColor,
ColorCombineSource::EnvironmentColor,
ColorCombineSource::Texel0,
ColorCombineSource::EnvironmentColor,
AlphaCombineSource::_0,
AlphaCombineSource::_0,
AlphaCombineSource::_0,
AlphaCombineSource::Texture0Alpha
);
std::shared_ptr<Material> parseMaterial(const std::string& name, const YAML::Node& node, ParseResult& output) {
std::shared_ptr<Material> material(new Material(name));
parseTexture(node["gSPTexture"], material->mState.textureState, output);
parseTiles(node["gDPSetTile"], material->mState, output);
for (int i = 0; i < MAX_TILE_COUNT; ++i) {
if (material->mState.tiles[i].texture && material->mState.tiles[i].texture->HasEffect(TextureDefinitionEffect::TwoToneGrayscale)) {
material->mState.envColor = material->mState.tiles[i].texture->GetTwoToneMin();
material->mState.useEnvColor = true;
material->mState.primitiveColor = material->mState.tiles[i].texture->GetTwoToneMax();
material->mState.usePrimitiveColor = true;
material->mState.cycle1Combine = gTwoToneCombineMode;
material->mState.cycle2Combine = gTwoToneCombineMode;
material->mState.hasCombineMode = true;
break;
}
}
parseRenderMode(node["gDPSetRenderMode"], material->mState, output);
parseCombineMode(node["gDPSetCombineMode"], material->mState, output);
material->mState.geometryModes = parseGeometryMode(node["gSPGeometryMode"], output);
material->mState.pipelineMode = parseEnumType(node["gDPPipelineMode"], output, gPipelineModeNames, PipelineMode::Unknown, (int)PipelineMode::Count);
material->mState.cycleType = parseEnumType(node["gDPSetCycleType"], output, gCycleTypeNames, CycleType::Unknown, (int)CycleType::Count);
material->mState.perspectiveMode = parseEnumType(node["gDPSetTexturePersp"], output, gPerspectiveModeNames, PerspectiveMode::Unknown, (int)PerspectiveMode::Count);
material->mState.textureDetail = parseEnumType(node["gDPSetTextureDetail"], output, gTextureDetailNames, TextureDetail::Unknown, (int)TextureDetail::Count);
material->mState.textureLOD = parseEnumType(node["gDPSetTextureLOD"], output, gTextureLODNames, TextureLOD::Unknown, (int)TextureLOD::Count);
material->mState.textureLUT = parseEnumType(node["gDPSetTextureLUT"], output, gTextureLUTNames, TextureLUT::Unknown, (int)TextureLUT::Count);
material->mState.textureFilter = parseEnumType(node["gDPSetTextureFilter"], output, gTextureFilterNames, TextureFilter::Unknown, (int)TextureFilter::Count);
material->mState.textureConvert = parseEnumType(node["gDPSetTextureConvert"], output, gTextureConvertNames, TextureConvert::Unknown, (int)TextureConvert::Count);
material->mState.combineKey = parseEnumType(node["gDPSetCombineKey"], output, gCombineKeyNames, CombineKey::Unknown, (int)CombineKey::Count);
material->mState.colorDither = parseEnumType(node["gDPSetColorDither"], output, gCotherDitherNames, ColorDither::Unknown, (int)ColorDither::Count);
material->mState.alphaDither = parseEnumType(node["gDPSetAlphaDither"], output, gAlphaDitherNames, AlphaDither::Unknown, (int)AlphaDither::Count);
material->mState.alphaCompare = parseEnumType(node["gDPSetAlphaCompare"], output, gAlphaCompareNames, AlphaCompare::Unknown, (int)AlphaCompare::Count);
material->mState.depthSource = parseEnumType(node["gDPSetDepthSource"], output, gDepthSourceNames, DepthSource::Unknown, (int)DepthSource::Count);
parsePrimColor(node["gDPSetPrimColor"], material->mState, output);
material->mState.useEnvColor = parseMaterialColor(node["gDPSetEnvColor"], material->mState.envColor, output) || material->mState.useEnvColor;
material->mState.useFogColor = parseMaterialColor(node["gDPSetFogColor"], material->mState.fogColor, output) || material->mState.useFogColor;
material->mState.useBlendColor = parseMaterialColor(node["gDPSetBlendColor"], material->mState.blendColor, output) || material->mState.useBlendColor;
auto properties = node["properties"];
if (properties.IsDefined() && properties.IsMap()) {
for (auto it = properties.begin(); it != properties.end(); ++it) {
material->mProperties[it->first.as<std::string>()] = it->second.as<std::string>();
}
}
return material;
}
void parseMaterialFile(std::istream& input, ParseResult& output) {
try {
YAML::Node doc = YAML::Load(input);
const YAML::Node& materials = doc["materials"];
for (auto it = materials.begin(); it != materials.end(); ++it) {
std::string name = it->first.as<std::string>();
output.mMaterialFile.mMaterials[name] = parseMaterial(name, it->second, output);
}
} catch (YAML::ParserException& e) {
output.mErrors.push_back(ParseError(e.what()));
}
}

View file

@ -0,0 +1,29 @@
#ifndef _MATERIAL_PARSER_H
#define _MATERIAL_PARSER_H
#include "Material.h"
#include <istream>
#include <map>
#include <string>
#include <vector>
struct MaterialFile {
public:
std::map<std::string, std::shared_ptr<Material>> mMaterials;
};
struct ParseError {
ParseError(const std::string& message);
std::string mMessage;
};
struct ParseResult {
ParseResult(const std::string& insideFolder);
std::string mInsideFolder;
MaterialFile mMaterialFile;
std::vector<ParseError> mErrors;
};
void parseMaterialFile(std::istream& input, ParseResult& output);
#endif

View file

@ -0,0 +1,567 @@
#include "MaterialState.h"
#include "MaterialEnums.h"
#include "RenderMode.h"
#include <iostream>
FlagList::FlagList() : flags(0), knownFlags(0) {}
void FlagList::SetFlag(int mask, bool value) {
knownFlags |= mask;
if (value) {
flags |= mask;
} else {
flags &= ~mask;
}
}
void FlagList::DeleteFlag(int mask) {
flags &= ~mask;
knownFlags &= ~mask;
}
struct FlagList FlagList::GetDeltaFrom(struct FlagList& other) {
struct FlagList result;
result.knownFlags =
// flags known by both with different values
(knownFlags & other.knownFlags & (flags ^ other.flags)) |
// flags known by this but not that
(knownFlags & ~other.knownFlags);
// mask by knownFlags so unknown data is set
// to 0 to avoid confusion
result.flags = flags & result.knownFlags;
return result;
}
TextureCoordinateState::TextureCoordinateState():
wrap(true),
mirror(false),
mask(0),
shift(0),
offset(0),
limit(0) {
}
TileState::TileState():
isOn(false),
line(0),
pallete(0) {
}
bool TileState::IsTileStateEqual(const TileState& other) const {
return format == other.format &&
size == other.size &&
line == other.line &&
tmem == other.tmem &&
pallete == other.pallete &&
sCoord.wrap == other.sCoord.wrap &&
sCoord.mirror == other.sCoord.mirror &&
sCoord.mask == other.sCoord.mask &&
sCoord.shift == other.sCoord.shift &&
tCoord.wrap == other.tCoord.wrap &&
tCoord.mirror == other.tCoord.mirror &&
tCoord.mask == other.tCoord.mask &&
tCoord.shift == other.tCoord.shift;
}
bool TileState::IsTileSizeEqual(const TileState& other) const {
return sCoord.offset == other.sCoord.offset &&
sCoord.limit == other.sCoord.limit &&
tCoord.offset == other.tCoord.offset &&
tCoord.limit == other.tCoord.limit;
}
TextureState::TextureState():
sc(0xFFFF),
tc(0xFFFF),
level(0),
tile(0),
isOn(false) {}
ColorCombineMode::ColorCombineMode() :
color{ColorCombineSource::_0, ColorCombineSource::_0, ColorCombineSource::_0, ColorCombineSource::_0},
alpha{AlphaCombineSource::_0, AlphaCombineSource::_0, AlphaCombineSource::_0, AlphaCombineSource::_0} {}
ColorCombineMode::ColorCombineMode(ColorCombineSource c0, ColorCombineSource c1, ColorCombineSource c2, ColorCombineSource c3, AlphaCombineSource a0, AlphaCombineSource a1, AlphaCombineSource a2, AlphaCombineSource a3) :
color{c0, c1, c2, c3},
alpha{a0, a1, a2, a3} {}
bool ColorCombineMode::operator==(const ColorCombineMode& other) const {
return color[0] == other.color[0] &&
color[1] == other.color[1] &&
color[2] == other.color[2] &&
color[3] == other.color[3] &&
alpha[0] == other.alpha[0] &&
alpha[1] == other.alpha[1] &&
alpha[2] == other.alpha[2] &&
alpha[3] == other.alpha[3];
}
RenderModeState::RenderModeState() : data(G_RM_OPA_SURF) {
}
RenderModeState::RenderModeState(int data) : data(data) {};
bool RenderModeState::operator==(const RenderModeState& other) const {
return data == other.data;
}
#define DEFINE_RENDER_MODE_ENTRY(name) std::make_pair(std::string(#name), RenderModeState(name))
std::pair<std::string, RenderModeState> gRenderModes[] = {
DEFINE_RENDER_MODE_ENTRY(G_RM_AA_ZB_OPA_SURF),
DEFINE_RENDER_MODE_ENTRY(G_RM_RA_ZB_OPA_SURF),
DEFINE_RENDER_MODE_ENTRY(G_RM_AA_ZB_XLU_SURF),
DEFINE_RENDER_MODE_ENTRY(G_RM_AA_ZB_OPA_DECAL),
DEFINE_RENDER_MODE_ENTRY(G_RM_RA_ZB_OPA_DECAL),
DEFINE_RENDER_MODE_ENTRY(G_RM_AA_ZB_XLU_DECAL),
DEFINE_RENDER_MODE_ENTRY(G_RM_AA_ZB_OPA_INTER),
DEFINE_RENDER_MODE_ENTRY(G_RM_RA_ZB_OPA_INTER),
DEFINE_RENDER_MODE_ENTRY(G_RM_AA_ZB_XLU_INTER),
DEFINE_RENDER_MODE_ENTRY(G_RM_AA_ZB_XLU_LINE),
DEFINE_RENDER_MODE_ENTRY(G_RM_AA_ZB_DEC_LINE),
DEFINE_RENDER_MODE_ENTRY(G_RM_AA_ZB_TEX_EDGE),
DEFINE_RENDER_MODE_ENTRY(G_RM_AA_ZB_TEX_INTER),
DEFINE_RENDER_MODE_ENTRY(G_RM_AA_ZB_SUB_SURF),
DEFINE_RENDER_MODE_ENTRY(G_RM_AA_ZB_PCL_SURF),
DEFINE_RENDER_MODE_ENTRY(G_RM_AA_ZB_OPA_TERR),
DEFINE_RENDER_MODE_ENTRY(G_RM_AA_ZB_TEX_TERR),
DEFINE_RENDER_MODE_ENTRY(G_RM_AA_ZB_SUB_TERR),
DEFINE_RENDER_MODE_ENTRY(G_RM_AA_OPA_SURF),
DEFINE_RENDER_MODE_ENTRY(G_RM_RA_OPA_SURF),
DEFINE_RENDER_MODE_ENTRY(G_RM_AA_XLU_SURF),
DEFINE_RENDER_MODE_ENTRY(G_RM_AA_XLU_LINE),
DEFINE_RENDER_MODE_ENTRY(G_RM_AA_DEC_LINE),
DEFINE_RENDER_MODE_ENTRY(G_RM_AA_TEX_EDGE),
DEFINE_RENDER_MODE_ENTRY(G_RM_AA_SUB_SURF),
DEFINE_RENDER_MODE_ENTRY(G_RM_AA_PCL_SURF),
DEFINE_RENDER_MODE_ENTRY(G_RM_AA_OPA_TERR),
DEFINE_RENDER_MODE_ENTRY(G_RM_AA_TEX_TERR),
DEFINE_RENDER_MODE_ENTRY(G_RM_AA_SUB_TERR),
DEFINE_RENDER_MODE_ENTRY(G_RM_ZB_OPA_SURF),
DEFINE_RENDER_MODE_ENTRY(G_RM_ZB_XLU_SURF),
DEFINE_RENDER_MODE_ENTRY(G_RM_ZB_OPA_DECAL),
DEFINE_RENDER_MODE_ENTRY(G_RM_ZB_XLU_DECAL),
DEFINE_RENDER_MODE_ENTRY(G_RM_ZB_CLD_SURF),
DEFINE_RENDER_MODE_ENTRY(G_RM_ZB_OVL_SURF),
DEFINE_RENDER_MODE_ENTRY(G_RM_ZB_PCL_SURF),
DEFINE_RENDER_MODE_ENTRY(G_RM_OPA_SURF),
DEFINE_RENDER_MODE_ENTRY(G_RM_XLU_SURF),
DEFINE_RENDER_MODE_ENTRY(G_RM_TEX_EDGE),
DEFINE_RENDER_MODE_ENTRY(G_RM_CLD_SURF),
DEFINE_RENDER_MODE_ENTRY(G_RM_PCL_SURF),
DEFINE_RENDER_MODE_ENTRY(G_RM_ADD),
DEFINE_RENDER_MODE_ENTRY(G_RM_NOOP),
DEFINE_RENDER_MODE_ENTRY(G_RM_VISCVG),
DEFINE_RENDER_MODE_ENTRY(G_RM_OPA_CI),
DEFINE_RENDER_MODE_ENTRY(G_RM_FOG_SHADE_A),
DEFINE_RENDER_MODE_ENTRY(G_RM_FOG_PRIM_A),
DEFINE_RENDER_MODE_ENTRY(G_RM_PASS),
};
bool findRenderModeByName(const std::string& name, RenderModeState& output) {
for (auto pair : gRenderModes) {
if (pair.first == name) {
output = pair.second;
return true;
}
}
return false;
}
MaterialState::MaterialState() :
pipelineMode(PipelineMode::Unknown),
cycleType(CycleType::Unknown),
perspectiveMode(PerspectiveMode::Unknown),
textureDetail(TextureDetail::Unknown),
textureLOD(TextureLOD::Unknown),
textureLUT(TextureLUT::Unknown),
textureFilter(TextureFilter::Unknown),
textureConvert(TextureConvert::Unknown),
combineKey(CombineKey::Unknown),
colorDither(ColorDither::Unknown),
alphaDither(AlphaDither::Unknown),
alphaCompare(AlphaCompare::Unknown),
depthSource(DepthSource::Unknown),
hasCombineMode(false),
hasRenderMode(false),
usePrimitiveColor(false),
primitiveM(255),
primitiveL(255),
useEnvColor(false),
useFillColor(false),
useFogColor(false),
useBlendColor(false)
{}
void appendToFlags(std::ostringstream& flags, const std::string& value) {
if (flags.tellp() != 0) {
flags << " | ";
}
flags << value;
}
std::unique_ptr<DataChunk> generateGeometryModes(const MaterialState& from, const MaterialState& to) {
std::ostringstream clearFlags;
std::ostringstream setFlags;
for (int i = 0; i < GEOMETRY_MODE_COUNT; ++i) {
int mask = 1 << i;
bool isKnownToTarget = (to.geometryModes.knownFlags & mask) != 0;
bool isKnownToSource = (from.geometryModes.knownFlags & mask) != 0;
bool targetMatchesSource = (mask & (to.geometryModes.flags ^ from.geometryModes.flags)) == 0;
if (isKnownToTarget && (!isKnownToSource || !targetMatchesSource)) {
if (to.geometryModes.flags & mask) {
appendToFlags(setFlags, gGeometryModeNames[i]);
} else {
appendToFlags(clearFlags, gGeometryModeNames[i]);
}
}
}
if (clearFlags.tellp() == 0 && setFlags.tellp() == 0) {
return NULL;
}
if (clearFlags.tellp() == 0) {
clearFlags << "0";
}
if (setFlags.tellp() == 0) {
setFlags << "0";
}
std::unique_ptr<MacroDataChunk> result(new MacroDataChunk("gsSPGeometryMode"));
result->AddPrimitive(clearFlags.str());
result->AddPrimitive(setFlags.str());
return result;
}
void generateEnumMacro(int from, int to, const char* macroName, const char** options, StructureDataChunk& output) {
if (from == to || to == 0) {
return;
}
std::unique_ptr<MacroDataChunk> result(new MacroDataChunk(macroName));
result->AddPrimitive(options[to]);
output.Add(std::move(result));
}
std::unique_ptr<DataChunk> generateCombineMode(const MaterialState& from, const MaterialState& to) {
if (!to.hasCombineMode ||
(from.hasCombineMode && from.cycle1Combine == to.cycle1Combine && from.cycle2Combine == to.cycle2Combine)) {
return NULL;
}
std::unique_ptr<MacroDataChunk> result(new MacroDataChunk("gsDPSetCombineLERP"));
for (int i = 0; i < 4; ++i) {
result->AddPrimitive(gColorCombineSourceNames[(int)to.cycle1Combine.color[i]]);
}
for (int i = 0; i < 4; ++i) {
result->AddPrimitive(gAlphaCombineSourceNames[(int)to.cycle1Combine.alpha[i]]);
}
for (int i = 0; i < 4; ++i) {
result->AddPrimitive(gColorCombineSourceNames[(int)to.cycle2Combine.color[i]]);
}
for (int i = 0; i < 4; ++i) {
result->AddPrimitive(gAlphaCombineSourceNames[(int)to.cycle2Combine.alpha[i]]);
}
return result;
}
std::string generateSingleRenderMode(int renderMode, int cycleNumber) {
std::ostringstream result;
std::vector<std::string> flags;
renderModeExtractFlags(renderMode, flags);
for (auto& flag : flags) {
if (result.tellp()) {
result << " | ";
}
result << flag;
}
result << "GBL_c" << cycleNumber << "(";
result << renderModeGetBlendModeName(renderMode, 0) << ", ";
result << renderModeGetBlendModeName(renderMode, 1) << ", ";
result << renderModeGetBlendModeName(renderMode, 2) << ", ";
result << renderModeGetBlendModeName(renderMode, 3) << ")";
return result.str();
}
std::unique_ptr<DataChunk> generateRenderMode(const MaterialState& from, const MaterialState& to) {
if (!to.hasRenderMode ||
(from.hasRenderMode && from.cycle1RenderMode == to.cycle1RenderMode && from.cycle2RenderMode == to.cycle2RenderMode)) {
return NULL;
}
std::unique_ptr<MacroDataChunk> result(new MacroDataChunk("gsDPSetRenderMode"));
std::string firstName;
std::string secondName;
for (auto pair : gRenderModes) {
if (pair.second == to.cycle1RenderMode) {
firstName = pair.first;
}
if (pair.second == to.cycle2RenderMode) {
secondName = pair.first;
}
}
if (firstName.length()) {
result->AddPrimitive(firstName);
} else {
result->AddPrimitive(generateSingleRenderMode(to.cycle1RenderMode.data, 1));
}
if (secondName.length()) {
result->AddPrimitive(secondName + "2");
} else {
result->AddPrimitive(generateSingleRenderMode(to.cycle1RenderMode.data, 2));
}
return result;
}
void generatePrimitiveColor(const MaterialState& from, const MaterialState& to, StructureDataChunk& output) {
if (!to.usePrimitiveColor ||
(from.usePrimitiveColor && from.primitiveColor == to.primitiveColor && from.primitiveL == to.primitiveL && from.primitiveM == to.primitiveM)) {
return;
}
std::unique_ptr<MacroDataChunk> result(new MacroDataChunk("gsDPSetPrimColor"));
result->AddPrimitive((int)to.primitiveM);
result->AddPrimitive((int)to.primitiveL);
result->AddPrimitive((int)to.primitiveColor.r);
result->AddPrimitive((int)to.primitiveColor.g);
result->AddPrimitive((int)to.primitiveColor.b);
result->AddPrimitive((int)to.primitiveColor.a);
output.Add(std::move(result));
}
void generateColor(const PixelRGBAu8& to, const char* macroName, StructureDataChunk& output) {
std::unique_ptr<MacroDataChunk> result(new MacroDataChunk(macroName));
result->AddPrimitive((int)to.r);
result->AddPrimitive((int)to.g);
result->AddPrimitive((int)to.b);
result->AddPrimitive((int)to.a);
output.Add(std::move(result));
}
std::string buildClampAndWrap(bool wrap, bool mirror) {
std::ostringstream result;
if (wrap) {
result << "G_TX_WRAP";
} else {
result << "G_TX_CLAMP";
}
result << " | ";
if (mirror) {
result << "G_TX_MIRROR";
} else {
result << "G_TX_NOMIRROR";
}
return result.str();
}
void generateTile(CFileDefinition& fileDef, const MaterialState& from, const TileState& to, int tileIndex, StructureDataChunk& output) {
if (!to.isOn) {
return;
}
bool needsToLoadImage = to.texture != nullptr;
for (int i = 0; i < MAX_TILE_COUNT && needsToLoadImage; ++i) {
if (from.tiles[i].texture == to.texture && from.tiles[i].tmem == to.tmem) {
needsToLoadImage = false;
}
}
if (needsToLoadImage) {
std::string imageName;
if (!fileDef.GetResourceName(to.texture.get(), imageName)) {
std::cerr << "Texture " << to.texture->Name() << " needs to be added to the file definition before being used in a material" << std::endl;
return;
}
output.Add(std::unique_ptr<MacroDataChunk>(new MacroDataChunk("gsDPTileSync")));
std::unique_ptr<MacroDataChunk> setTextureImage(new MacroDataChunk("gsDPSetTextureImage"));
setTextureImage->AddPrimitive(nameForImageFormat(to.format));
setTextureImage->AddPrimitive(std::string(nameForImageSize(to.size)) + "_LOAD_BLOCK");
setTextureImage->AddPrimitive(1);
setTextureImage->AddPrimitive(imageName);
output.Add(std::move(setTextureImage));
std::unique_ptr<MacroDataChunk> setTile(new MacroDataChunk("gsDPSetTile"));
setTile->AddPrimitive(nameForImageFormat(to.format));
setTile->AddPrimitive(std::string(nameForImageSize(to.size)) + "_LOAD_BLOCK");
setTile->AddPrimitive(0);
setTile->AddPrimitive(to.tmem);
setTile->AddPrimitive<const char*>("G_TX_LOADTILE");
setTile->AddPrimitive(to.pallete);
setTile->AddPrimitive(buildClampAndWrap(to.tCoord.wrap, to.tCoord.mirror));
setTile->AddPrimitive(to.tCoord.mask);
setTile->AddPrimitive(to.tCoord.shift);
setTile->AddPrimitive(buildClampAndWrap(to.sCoord.wrap, to.sCoord.mirror));
setTile->AddPrimitive(to.sCoord.mask);
setTile->AddPrimitive(to.sCoord.shift);
output.Add(std::move(setTile));
output.Add(std::unique_ptr<MacroDataChunk>(new MacroDataChunk("gsDPLoadSync")));
std::unique_ptr<MacroDataChunk> loadBlock(new MacroDataChunk("gsDPLoadBlock"));
loadBlock->AddPrimitive<const char*>("G_TX_LOADTILE");
loadBlock->AddPrimitive(0);
loadBlock->AddPrimitive(0);
loadBlock->AddPrimitive(to.texture->LoadBlockSize());
loadBlock->AddPrimitive(to.texture->DTX());
output.Add(std::move(loadBlock));
output.Add(std::unique_ptr<MacroDataChunk>(new MacroDataChunk("gsDPPipeSync")));
}
if (!from.tiles[tileIndex].IsTileStateEqual(to)) {
std::unique_ptr<MacroDataChunk> setTile(new MacroDataChunk("gsDPSetTile"));
setTile->AddPrimitive(nameForImageFormat(to.format));
setTile->AddPrimitive(nameForImageSize(to.size));
setTile->AddPrimitive(to.line);
setTile->AddPrimitive(to.tmem);
setTile->AddPrimitive(tileIndex);
setTile->AddPrimitive(to.pallete);
setTile->AddPrimitive(buildClampAndWrap(to.tCoord.wrap, to.tCoord.mirror));
setTile->AddPrimitive(to.tCoord.mask);
setTile->AddPrimitive(to.tCoord.shift);
setTile->AddPrimitive(buildClampAndWrap(to.sCoord.wrap, to.sCoord.mirror));
setTile->AddPrimitive(to.sCoord.mask);
setTile->AddPrimitive(to.sCoord.shift);
output.Add(std::move(setTile));
}
if (!from.tiles[tileIndex].IsTileSizeEqual(to)) {
std::unique_ptr<MacroDataChunk> setTileSize(new MacroDataChunk("gsDPSetTileSize"));
setTileSize->AddPrimitive(tileIndex);
setTileSize->AddPrimitive(to.sCoord.offset);
setTileSize->AddPrimitive(to.tCoord.offset);
setTileSize->AddPrimitive(to.sCoord.limit);
setTileSize->AddPrimitive(to.tCoord.limit);
output.Add(std::move(setTileSize));
}
}
void generateTexture(const TextureState& from, const TextureState& to, StructureDataChunk& output) {
if (!to.isOn) {
return;
}
if (from.isOn && from.sc == to.sc && from.tc == to.tc && from.level == to.level && from.tile == to.tile) {
return;
}
std::unique_ptr<MacroDataChunk> setTexture(new MacroDataChunk("gsSPTexture"));
setTexture->AddPrimitive(to.sc);
setTexture->AddPrimitive(to.tc);
setTexture->AddPrimitive(to.level);
setTexture->AddPrimitive(to.tile);
setTexture->AddPrimitive<const char*>("G_ON");
output.Add(std::move(setTexture));
}
void generateMaterial(CFileDefinition& fileDef, const MaterialState& from, const MaterialState& to, StructureDataChunk& output) {
output.Add(std::unique_ptr<DataChunk>(new MacroDataChunk("gsDPPipeSync")));
generateEnumMacro((int)from.pipelineMode, (int)to.pipelineMode, "gsDPPipelineMode", gPipelineModeNames, output);
generateEnumMacro((int)from.cycleType, (int)to.cycleType, "gsDPSetCycleType", gCycleTypeNames, output);
generateEnumMacro((int)from.perspectiveMode, (int)to.perspectiveMode, "gsDPSetTexturePersp", gPerspectiveModeNames, output);
generateEnumMacro((int)from.textureDetail, (int)to.textureDetail, "gsDPSetTextureDetail", gTextureDetailNames, output);
generateEnumMacro((int)from.textureLOD, (int)to.textureLOD, "gsDPSetTextureLOD", gTextureLODNames, output);
generateEnumMacro((int)from.textureLUT, (int)to.textureLUT, "gsDPSetTextureLUT", gTextureLUTNames, output);
generateEnumMacro((int)from.textureFilter, (int)to.textureFilter, "gsDPSetTextureFilter", gTextureFilterNames, output);
generateEnumMacro((int)from.textureConvert, (int)to.textureConvert, "gsDPSetTextureConvert", gTextureConvertNames, output);
generateEnumMacro((int)from.combineKey, (int)to.combineKey, "gsDPSetCombineKey", gCombineKeyNames, output);
generateEnumMacro((int)from.colorDither, (int)to.colorDither, "gsDPSetColorDither", gCotherDitherNames, output);
generateEnumMacro((int)from.alphaDither, (int)to.alphaDither, "gsDPSetAlphaDither", gAlphaDitherNames, output);
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));
if (geometryModes) {
output.Add(std::move(geometryModes));
}
std::unique_ptr<DataChunk> combineMode = std::move(generateCombineMode(from, to));
if (combineMode) {
output.Add(std::move(combineMode));
}
std::unique_ptr<DataChunk> renderMode = std::move(generateRenderMode(from, to));
if (renderMode) {
output.Add(std::move(renderMode));
}
generatePrimitiveColor(from, to, output);
if (to.useEnvColor && (!from.useEnvColor || !(to.envColor == from.envColor))) {
generateColor(to.envColor, "gsDPSetEnvColor", output);
}
if (to.useFogColor && (!from.useFogColor || !(to.fogColor == from.fogColor))) {
generateColor(to.fogColor, "gsDPSetFogColor", output);
}
if (to.useBlendColor && (!from.useBlendColor || !(to.blendColor == from.blendColor))) {
generateColor(to.blendColor, "gsDPSetBlendColor", output);
}
generateTexture(from.textureState, to.textureState, output);
for (int i = 0; i < MAX_TILE_COUNT; ++i) {
generateTile(fileDef, from, to.tiles[i], i, output);
}
// TODO fill color
}

View file

@ -0,0 +1,144 @@
#ifndef __MATERIAL_STATE_H__
#define __MATERIAL_STATE_H__
#include <inttypes.h>
#include "TextureDefinition.h"
#include "MaterialEnums.h"
#include "../CFileDefinition.h"
struct FlagList {
FlagList();
uint64_t flags;
uint64_t knownFlags;
void SetFlag(int mask, bool value);
void DeleteFlag(int mask);
struct FlagList GetDeltaFrom(struct FlagList& other);
};
struct TextureCoordinateState {
public:
TextureCoordinateState();
bool wrap;
bool mirror;
int mask;
int shift;
int offset;
int limit;
};
struct TileState {
public:
TileState();
bool IsTileStateEqual(const TileState& other) const;
bool IsTileSizeEqual(const TileState& other) const;
bool isOn;
std::shared_ptr<TextureDefinition> texture;
G_IM_FMT format;
G_IM_SIZ size;
// 1 line is a 64 bit offset in TMEM
int line;
int tmem;
int pallete;
struct TextureCoordinateState sCoord;
struct TextureCoordinateState tCoord;
};
struct TextureState {
public:
TextureState();
uint16_t sc;
uint16_t tc;
int level;
int tile;
bool isOn;
};
struct ColorCombineMode {
ColorCombineMode();
ColorCombineMode(ColorCombineSource c0, ColorCombineSource c1, ColorCombineSource c2, ColorCombineSource c3, AlphaCombineSource a0, AlphaCombineSource a1, AlphaCombineSource a2, AlphaCombineSource a3);
bool operator==(const ColorCombineMode& other) const;
ColorCombineSource color[4];
AlphaCombineSource alpha[4];
};
struct RenderModeState {
public:
RenderModeState();
RenderModeState(int data);
bool operator==(const RenderModeState& other) const;
int data;
};
bool findRenderModeByName(const std::string& name, RenderModeState& output);
#define MAX_TILE_COUNT 8
struct MaterialState {
public:
MaterialState();
// state to keep track of
// tiles
// RDP tile cache
struct TileState tiles[MAX_TILE_COUNT];
struct TextureState textureState;
// geometry modes
FlagList geometryModes;
// G_SETOTHERMODE_H
PipelineMode pipelineMode;
CycleType cycleType;
PerspectiveMode perspectiveMode;
TextureDetail textureDetail;
TextureLOD textureLOD;
TextureLUT textureLUT;
TextureFilter textureFilter;
TextureConvert textureConvert;
CombineKey combineKey;
ColorDither colorDither;
AlphaDither alphaDither;
// G_SETOTHERMODE_L
AlphaCompare alphaCompare;
DepthSource depthSource;
// combine mode
bool hasCombineMode;
ColorCombineMode cycle1Combine;
ColorCombineMode cycle2Combine;
bool hasRenderMode;
RenderModeState cycle1RenderMode;
RenderModeState cycle2RenderMode;
// RDP colors
bool usePrimitiveColor;
PixelRGBAu8 primitiveColor;
uint8_t primitiveM;
uint8_t primitiveL;
bool useEnvColor;
PixelRGBAu8 envColor;
bool useFillColor;
PixelRGBAu8 fillColor;
bool useFogColor;
PixelRGBAu8 fogColor;
bool useBlendColor;
PixelRGBAu8 blendColor;
};
void generateMaterial(CFileDefinition& fileDef, const MaterialState& from, const MaterialState& to, StructureDataChunk& output);
#endif

View file

@ -0,0 +1,114 @@
#include "RenderMode.h"
#include <map>
#define CREATE_RENDER_MODE_ENTRY(val) {std::string(#val), val}
std::map<std::string, int> gRenderModeFlags = {
CREATE_RENDER_MODE_ENTRY(AA_EN),
CREATE_RENDER_MODE_ENTRY(Z_CMP),
CREATE_RENDER_MODE_ENTRY(Z_UPD),
CREATE_RENDER_MODE_ENTRY(IM_RD),
CREATE_RENDER_MODE_ENTRY(CLR_ON_CVG),
CREATE_RENDER_MODE_ENTRY(CVG_X_ALPHA),
CREATE_RENDER_MODE_ENTRY(ALPHA_CVG_SEL),
CREATE_RENDER_MODE_ENTRY(FORCE_BL),
};
std::map<std::string, int> gCVG_DST_VALUES = {
CREATE_RENDER_MODE_ENTRY(CVG_DST_CLAMP),
CREATE_RENDER_MODE_ENTRY(CVG_DST_WRAP),
CREATE_RENDER_MODE_ENTRY(CVG_DST_FULL),
CREATE_RENDER_MODE_ENTRY(CVG_DST_SAVE),
};
std::map<std::string, int> gZMODE_VALUES = {
CREATE_RENDER_MODE_ENTRY(ZMODE_OPA),
CREATE_RENDER_MODE_ENTRY(ZMODE_INTER),
CREATE_RENDER_MODE_ENTRY(ZMODE_XLU),
CREATE_RENDER_MODE_ENTRY(ZMODE_DEC),
};
void renderModeExtractFlags(int flags, std::vector<std::string>& output) {
for (auto& pair : gRenderModeFlags) {
if (flags & pair.second) {
output.push_back(pair.first);
}
}
for (auto& pair : gCVG_DST_VALUES) {
if ((flags & CVG_DST_MASK) == pair.second) {
output.push_back(pair.first);
break;
}
}
for (auto& pair : gZMODE_VALUES) {
if ((flags & ZMODE_MASK) == pair.second) {
output.push_back(pair.first);
break;
}
}
}
bool renderModeGetFlagValue(const std::string& name, int& output) {
auto it = gRenderModeFlags.find(name);
if (it != gRenderModeFlags.end()) {
output = it->second;
return true;
}
it = gCVG_DST_VALUES.find(name);
if (it != gRenderModeFlags.end()) {
output = it->second;
return true;
}
it = gZMODE_VALUES.find(name);
if (it != gRenderModeFlags.end()) {
output = it->second;
return true;
}
return false;
}
std::string gBlendModeNames[4][4] = {
{std::string("G_BL_CLR_IN"), std::string("G_BL_CLR_MEM"), std::string("G_BL_CLR_BL"), std::string("G_BL_CLR_FOG")},
{std::string("G_BL_A_IN"), std::string("G_BL_A_FOG"), std::string("G_BL_A_SHADE"), std::string("G_BL_0")},
{std::string("G_BL_CLR_IN"), std::string("G_BL_CLR_MEM"), std::string("G_BL_CLR_BL"), std::string("G_BL_CLR_FOG")},
{std::string("G_BL_1MA"), std::string("G_BL_A_MEM"), std::string("G_BL_1"), std::string("G_BL_0")},
};
int gBlendModeShift[4] = {30, 26, 22, 18};
bool renderModeGetBlendModeValue(const std::string& name, int index, int& output) {
if (index < 0 || index >= 4) {
return false;
}
for (int i = 0; i < 4; ++i) {
if (gBlendModeNames[index][i] == name) {
output = i << gBlendModeShift[index];
return true;
}
}
return false;
}
const std::string& renderModeGetBlendModeName(int blendMode, int index) {
if (index < 0) {
index = 0;
}
if (index >= 4) {
index = 3;
}
return gBlendModeNames[index][(blendMode >> gBlendModeShift[index]) & 0x3];
}

View file

@ -0,0 +1,273 @@
#ifndef __RENDER_MODE_H__
#define __RENDER_MODE_H__
#include <vector>
#include <string>
#define AA_EN 0x8
#define Z_CMP 0x10
#define Z_UPD 0x20
#define IM_RD 0x40
#define CLR_ON_CVG 0x80
#define CVG_DST_CLAMP 0
#define CVG_DST_WRAP 0x100
#define CVG_DST_FULL 0x200
#define CVG_DST_SAVE 0x300
#define CVG_DST_MASK 0x300
#define ZMODE_OPA 0
#define ZMODE_INTER 0x400
#define ZMODE_XLU 0x800
#define ZMODE_DEC 0xc00
#define ZMODE_MASK 0xc00
#define CVG_X_ALPHA 0x1000
#define ALPHA_CVG_SEL 0x2000
#define FORCE_BL 0x4000
#define TEX_EDGE 0x0000 /* used to be 0x8000 */
#define G_BL_CLR_IN 0
#define G_BL_CLR_MEM 1
#define G_BL_CLR_BL 2
#define G_BL_CLR_FOG 3
#define G_BL_1MA 0
#define G_BL_A_MEM 1
#define G_BL_A_IN 0
#define G_BL_A_FOG 1
#define G_BL_A_SHADE 2
#define G_BL_1 2
#define G_BL_0 3
#define G_MDSFT_ALPHACOMPARE 0
#define G_AC_NONE (0 << G_MDSFT_ALPHACOMPARE)
#define G_AC_THRESHOLD (1 << G_MDSFT_ALPHACOMPARE)
#define G_AC_DITHER (3 << G_MDSFT_ALPHACOMPARE)
#define GBL_c1(m1a, m1b, m2a, m2b) \
(m1a) << 30 | (m1b) << 26 | (m2a) << 22 | (m2b) << 18
#define G_RM_AA_ZB_OPA_SURF \
AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | \
ZMODE_OPA | ALPHA_CVG_SEL | \
GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM)
#define G_RM_RA_ZB_OPA_SURF \
AA_EN | Z_CMP | Z_UPD | CVG_DST_CLAMP | \
ZMODE_OPA | ALPHA_CVG_SEL | \
GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM)
#define G_RM_AA_ZB_XLU_SURF \
AA_EN | Z_CMP | IM_RD | CVG_DST_WRAP | CLR_ON_CVG | \
FORCE_BL | ZMODE_XLU | \
GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)
#define G_RM_AA_ZB_OPA_DECAL \
AA_EN | Z_CMP | IM_RD | CVG_DST_WRAP | ALPHA_CVG_SEL | \
ZMODE_DEC | \
GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM)
#define G_RM_RA_ZB_OPA_DECAL \
AA_EN | Z_CMP | CVG_DST_WRAP | ALPHA_CVG_SEL | \
ZMODE_DEC | \
GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM)
#define G_RM_AA_ZB_XLU_DECAL \
AA_EN | Z_CMP | IM_RD | CVG_DST_WRAP | CLR_ON_CVG | \
FORCE_BL | ZMODE_DEC | \
GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)
#define G_RM_AA_ZB_OPA_INTER \
AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | \
ALPHA_CVG_SEL | ZMODE_INTER | \
GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM)
#define G_RM_RA_ZB_OPA_INTER \
AA_EN | Z_CMP | Z_UPD | CVG_DST_CLAMP | \
ALPHA_CVG_SEL | ZMODE_INTER | \
GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM)
#define G_RM_AA_ZB_XLU_INTER \
AA_EN | Z_CMP | IM_RD | CVG_DST_WRAP | CLR_ON_CVG | \
FORCE_BL | ZMODE_INTER | \
GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)
#define G_RM_AA_ZB_XLU_LINE \
AA_EN | Z_CMP | IM_RD | CVG_DST_CLAMP | CVG_X_ALPHA | \
ALPHA_CVG_SEL | FORCE_BL | ZMODE_XLU | \
GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)
#define G_RM_AA_ZB_DEC_LINE \
AA_EN | Z_CMP | IM_RD | CVG_DST_SAVE | CVG_X_ALPHA | \
ALPHA_CVG_SEL | FORCE_BL | ZMODE_DEC | \
GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)
#define G_RM_AA_ZB_TEX_EDGE \
AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | \
CVG_X_ALPHA | ALPHA_CVG_SEL | ZMODE_OPA | TEX_EDGE | \
GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM)
#define G_RM_AA_ZB_TEX_INTER \
AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | \
CVG_X_ALPHA | ALPHA_CVG_SEL | ZMODE_INTER | TEX_EDGE | \
GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM)
#define G_RM_AA_ZB_SUB_SURF \
AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_FULL | \
ZMODE_OPA | ALPHA_CVG_SEL | \
GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM)
#define G_RM_AA_ZB_PCL_SURF \
AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | \
ZMODE_OPA | G_AC_DITHER | \
GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)
#define G_RM_AA_ZB_OPA_TERR \
AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | \
ZMODE_OPA | ALPHA_CVG_SEL | \
GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)
#define G_RM_AA_ZB_TEX_TERR \
AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | \
CVG_X_ALPHA | ALPHA_CVG_SEL | ZMODE_OPA | TEX_EDGE | \
GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)
#define G_RM_AA_ZB_SUB_TERR \
AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_FULL | \
ZMODE_OPA | ALPHA_CVG_SEL | \
GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)
#define G_RM_AA_OPA_SURF \
AA_EN | IM_RD | CVG_DST_CLAMP | \
ZMODE_OPA | ALPHA_CVG_SEL | \
GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM)
#define G_RM_RA_OPA_SURF \
AA_EN | CVG_DST_CLAMP | \
ZMODE_OPA | ALPHA_CVG_SEL | \
GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM)
#define G_RM_AA_XLU_SURF \
AA_EN | IM_RD | CVG_DST_WRAP | CLR_ON_CVG | FORCE_BL | \
ZMODE_OPA | \
GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)
#define G_RM_AA_XLU_LINE \
AA_EN | IM_RD | CVG_DST_CLAMP | CVG_X_ALPHA | \
ALPHA_CVG_SEL | FORCE_BL | ZMODE_OPA | \
GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)
#define G_RM_AA_DEC_LINE \
AA_EN | IM_RD | CVG_DST_FULL | CVG_X_ALPHA | \
ALPHA_CVG_SEL | FORCE_BL | ZMODE_OPA | \
GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)
#define G_RM_AA_TEX_EDGE \
AA_EN | IM_RD | CVG_DST_CLAMP | \
CVG_X_ALPHA | ALPHA_CVG_SEL | ZMODE_OPA | TEX_EDGE | \
GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM)
#define G_RM_AA_SUB_SURF \
AA_EN | IM_RD | CVG_DST_FULL | \
ZMODE_OPA | ALPHA_CVG_SEL | \
GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM)
#define G_RM_AA_PCL_SURF \
AA_EN | IM_RD | CVG_DST_CLAMP | \
ZMODE_OPA | G_AC_DITHER | \
GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)
#define G_RM_AA_OPA_TERR \
AA_EN | IM_RD | CVG_DST_CLAMP | \
ZMODE_OPA | ALPHA_CVG_SEL | \
GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)
#define G_RM_AA_TEX_TERR \
AA_EN | IM_RD | CVG_DST_CLAMP | \
CVG_X_ALPHA | ALPHA_CVG_SEL | ZMODE_OPA | TEX_EDGE | \
GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)
#define G_RM_AA_SUB_TERR \
AA_EN | IM_RD | CVG_DST_FULL | \
ZMODE_OPA | ALPHA_CVG_SEL | \
GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)
#define G_RM_ZB_OPA_SURF \
Z_CMP | Z_UPD | CVG_DST_FULL | ALPHA_CVG_SEL | \
ZMODE_OPA | \
GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM)
#define G_RM_ZB_XLU_SURF \
Z_CMP | IM_RD | CVG_DST_FULL | FORCE_BL | ZMODE_XLU | \
GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)
#define G_RM_ZB_OPA_DECAL \
Z_CMP | CVG_DST_FULL | ALPHA_CVG_SEL | ZMODE_DEC | \
GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM)
#define G_RM_ZB_XLU_DECAL \
Z_CMP | IM_RD | CVG_DST_FULL | FORCE_BL | ZMODE_DEC | \
GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)
#define G_RM_ZB_CLD_SURF \
Z_CMP | IM_RD | CVG_DST_SAVE | FORCE_BL | ZMODE_XLU | \
GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)
#define G_RM_ZB_OVL_SURF \
Z_CMP | IM_RD | CVG_DST_SAVE | FORCE_BL | ZMODE_DEC | \
GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)
#define G_RM_ZB_PCL_SURF \
Z_CMP | Z_UPD | CVG_DST_FULL | ZMODE_OPA | \
G_AC_DITHER | \
GBL_c1(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1)
#define G_RM_OPA_SURF \
CVG_DST_CLAMP | FORCE_BL | ZMODE_OPA | \
GBL_c1(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1)
#define G_RM_XLU_SURF \
IM_RD | CVG_DST_FULL | FORCE_BL | ZMODE_OPA | \
GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)
#define G_RM_TEX_EDGE \
CVG_DST_CLAMP | CVG_X_ALPHA | ALPHA_CVG_SEL | FORCE_BL |\
ZMODE_OPA | TEX_EDGE | AA_EN | \
GBL_c1(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1)
#define G_RM_CLD_SURF \
IM_RD | CVG_DST_SAVE | FORCE_BL | ZMODE_OPA | \
GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)
#define G_RM_PCL_SURF \
CVG_DST_FULL | FORCE_BL | ZMODE_OPA | \
G_AC_DITHER | \
GBL_c1(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1)
#define G_RM_ADD \
IM_RD | CVG_DST_SAVE | FORCE_BL | ZMODE_OPA | \
GBL_c1(G_BL_CLR_IN, G_BL_A_FOG, G_BL_CLR_MEM, G_BL_1)
#define G_RM_NOOP \
GBL_c1(0, 0, 0, 0)
#define G_RM_VISCVG \
IM_RD | FORCE_BL | \
GBL_c1(G_BL_CLR_IN, G_BL_0, G_BL_CLR_BL, G_BL_A_MEM)
/* for rendering to an 8-bit framebuffer */
#define G_RM_OPA_CI \
CVG_DST_CLAMP | ZMODE_OPA | \
GBL_c1(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1)
#define G_RM_FOG_SHADE_A GBL_c1(G_BL_CLR_FOG, G_BL_A_SHADE, G_BL_CLR_IN, G_BL_1MA)
#define G_RM_FOG_PRIM_A GBL_c1(G_BL_CLR_FOG, G_BL_A_FOG, G_BL_CLR_IN, G_BL_1MA)
#define G_RM_PASS GBL_c1(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1)
void renderModeExtractFlags(int flags, std::vector<std::string>& output);
bool renderModeGetFlagValue(const std::string& name, int& output);
bool renderModeGetBlendModeValue(const std::string& name, int index, int& output);
const std::string& renderModeGetBlendModeName(int blendMode, int index);
#endif

View file

@ -0,0 +1,20 @@
#include "TextureCache.h"
#include "../FileUtils.h"
std::shared_ptr<TextureDefinition> TextureCache::GetTexture(const std::string& filename, G_IM_FMT format, G_IM_SIZ size, TextureDefinitionEffect effects) {
std::string normalizedPath = NormalizePath(filename) +
"#" + nameForImageFormat(format) +
":" + nameForImageSize(size) +
":" + std::to_string((int)effects);
auto check = mCache.find(normalizedPath);
if (check != mCache.end()) {
return check->second;
}
std::shared_ptr<TextureDefinition> result(new TextureDefinition(filename, format, size, effects));
mCache[normalizedPath] = result;
return result;
}

View file

@ -0,0 +1,16 @@
#ifndef __TEXTURE_CACHE_H__
#define __TEXTURE_CACHE_H__
#include "TextureDefinition.h"
#include <memory>
#include <map>
#include <string>
class TextureCache {
public:
std::shared_ptr<TextureDefinition> GetTexture(const std::string& filename, G_IM_FMT format, G_IM_SIZ size, TextureDefinitionEffect effects);
private:
std::map<std::string, std::shared_ptr<TextureDefinition>> mCache;
};
#endif

View file

@ -0,0 +1,495 @@
#define cimg_display 0
#define cimg_use_png
#define cimg_use_tiff
#include "../../cimg/CImg.h"
#include "TextureDefinition.h"
#include "../FileUtils.h"
#include "../math/LeastSquares.h"
#include <iomanip>
#include <algorithm>
#include <iomanip>
DataChunkStream::DataChunkStream() :
mCurrentBufferPos(0),
mCurrentBuffer(0) {}
void DataChunkStream::WriteBytes(const char* data, int byteCount) {
for (int i = 0; i < byteCount; ++i) {
WriteBits(data[i], 8);
}
}
void DataChunkStream::WriteBits(int from, int bitCount) {
if (!bitCount) {
return;
} else if (bitCount + mCurrentBufferPos > 64) {
int firstChunkSize = 64 - mCurrentBufferPos;
int secondChunkSize = bitCount - firstChunkSize;
WriteBits(from >> secondChunkSize, firstChunkSize);
FlushBuffer();
WriteBits(from, secondChunkSize);
} else {
uint64_t mask = ~(~(uint64_t)0 << bitCount);
mCurrentBuffer |= (from & mask) << (64 - mCurrentBufferPos - bitCount);
mCurrentBufferPos += bitCount;
}
}
const std::vector<uint64_t>& DataChunkStream::GetData() {
FlushBuffer();
return mData;
}
void DataChunkStream::FlushBuffer() {
if (mCurrentBufferPos == 0) {
return;
}
mData.push_back(mCurrentBuffer);
mCurrentBufferPos = 0;
mCurrentBuffer = 0;
}
PixelRGBAu8::PixelRGBAu8() : r(0), g(0), b(0), a(0) {}
PixelRGBAu8::PixelRGBAu8(uint8_t rVal, uint8_t gVal, uint8_t bVal, uint8_t aVal) :
r(rVal), g(gVal), b(bVal), a(aVal) {}
bool PixelRGBAu8::operator==(const PixelRGBAu8& other) const {
return r == other.r && g == other.g && b == other.b && a == other.a;
}
bool PixelRGBAu8::WriteToStream(DataChunkStream& output, G_IM_SIZ size) {
switch (size) {
case G_IM_SIZ::G_IM_SIZ_32b:
output.WriteBytes((const char*)this, sizeof(PixelRGBAu8));
return true;
case G_IM_SIZ::G_IM_SIZ_16b:
output.WriteBits(r >> 3, 5);
output.WriteBits(g >> 3, 5);
output.WriteBits(b >> 3, 5);
output.WriteBits(a >> 7, 1);
return true;
default:
return false;
}
}
PixelIu8::PixelIu8(uint8_t i) : i(i) {}
bool PixelIu8::WriteToStream(DataChunkStream& output, G_IM_SIZ size) {
switch (size) {
case G_IM_SIZ::G_IM_SIZ_8b:
output.WriteBits(i, 8);
return true;
case G_IM_SIZ::G_IM_SIZ_4b:
output.WriteBits(i >> 4, 4);
return true;
default:
return false;
}
}
PixelIAu8::PixelIAu8(uint8_t i, uint8_t a) : i(i), a(a) {}
bool PixelIAu8::WriteToStream(DataChunkStream& output, G_IM_SIZ size) {
switch (size) {
case G_IM_SIZ::G_IM_SIZ_16b:
output.WriteBits(i, 8);
output.WriteBits(a, 8);
return true;
case G_IM_SIZ::G_IM_SIZ_8b:
output.WriteBits(i >> 4, 4);
output.WriteBits(a >> 4, 4);
return true;
case G_IM_SIZ::G_IM_SIZ_4b:
output.WriteBits(i >> 5, 3);
output.WriteBits(a >> 7, 1);
return true;
default:
return false;
}
}
struct PixelRGBAu8 readRGBAPixel(cimg_library_suffixed::CImg<unsigned char>& input, int x, int y) {
struct PixelRGBAu8 result;
result.r = 0;
result.g = 0;
result.b = 1;
result.a = 0xFF;
switch (input.spectrum()) {
case 4:
result.a = input(x, y, 0, 3);
case 3:
result.r = input(x, y, 0, 0);
result.g = input(x, y, 0, 1);
result.b = input(x, y, 0, 2);
break;
case 2:
result.a = input(x, y, 0, 1);
case 1:
result.r = input(x, y, 0, 0);
result.g = input(x, y, 0, 0);
result.b = input(x, y, 0, 0);
break;
}
return result;
}
struct PixelIu8 readIPixel(cimg_library_suffixed::CImg<unsigned char>& input, int x, int y) {
switch (input.spectrum()) {
case 4:
case 3:
return PixelIu8((
input(x, y, 0, 0) * 85 +
input(x, y, 0, 1) * 86 +
input(x, y, 0, 2) * 85
) >> 8);
case 2:
case 1:
return PixelIu8(input(x, y, 0, 0));
}
return PixelIu8(0);
}
struct PixelIAu8 readIAPixel(cimg_library_suffixed::CImg<unsigned char>& input, int x, int y) {
uint8_t alpha = 0xFF;
switch (input.spectrum()) {
case 4:
alpha = input(x, y, 0, 3);
case 3:
return PixelIAu8((
input(x, y, 0, 0) * 85 +
input(x, y, 0, 1) * 86 +
input(x, y, 0, 2) * 85
) >> 8, alpha);
case 2:
alpha = input(x, y, 0, 1);
case 1:
return PixelIAu8(input(x, y, 0, 0), alpha);
}
return PixelIAu8(0, alpha);
}
void writeIAPixel(cimg_library_suffixed::CImg<unsigned char>& input, int x, int y, struct PixelIAu8 value) {
switch (input.spectrum()) {
case 4:
input(x, y, 0, 3) = value.a;
case 3:
input(x, y, 0, 0) = value.i;
input(x, y, 0, 1) = value.i;
input(x, y, 0, 2) = value.i;
break;
case 2:
input(x, y, 0, 1) = value.a;
case 1:
input(x, y, 0, 0) = value.i;
break;
}
}
bool convertPixel(cimg_library_suffixed::CImg<unsigned char>& input, int x, int y, DataChunkStream& output, G_IM_FMT fmt, G_IM_SIZ siz) {
switch (fmt) {
case G_IM_FMT::G_IM_FMT_RGBA: {
PixelRGBAu8 pixel = readRGBAPixel(input, x, y);
return pixel.WriteToStream(output, siz);
}
case G_IM_FMT::G_IM_FMT_I: {
PixelIu8 pixel = readIPixel(input, x, y);
return pixel.WriteToStream(output, siz);
}
case G_IM_FMT::G_IM_FMT_IA: {
PixelIAu8 pixel = readIAPixel(input, x, y);
return pixel.WriteToStream(output, siz);
}
default:
return false;
}
}
const char* gFormatShortName[] = {
"rgba",
"yuv",
"ci",
"i",
"ia",
};
const char* gSizeName[] = {
"4b",
"8b",
"16b",
"32b",
};
uint8_t interpolateGrayscale(int min, int max, uint8_t input) {
if (input <= min) {
return 0;
}
int result = 0x100 * (input - min + 1) / (max - min + 1) - 1;
if (result > 0xFF) {
return 0xFF;
}
return result;
}
uint8_t floatToByte(float input) {
int result = (int)(input + 0.5f);
if (result < 0) {
return 0;
}
if (result > 0xFF) {
return 0xFF;
}
return result;
}
void applyTwoToneEffect(cimg_library_suffixed::CImg<unsigned char>& input, PixelRGBAu8& maxColor, PixelRGBAu8& minColor) {
LinearLeastSquares r;
LinearLeastSquares g;
LinearLeastSquares b;
LinearLeastSquares a;
int minGray = 0xFF;
int maxGray = 0;
int minAlpha = 0xFF;
int maxAlpha = 0;
for (int y = 0; y < input.height(); ++y) {
for (int x = 0; x < input.width(); ++x) {
PixelRGBAu8 colorValue = readRGBAPixel(input, x, y);
PixelIAu8 grayScaleValue = readIAPixel(input, x, y);
r.AddDataPoint(grayScaleValue.i, colorValue.r);
g.AddDataPoint(grayScaleValue.i, colorValue.g);
b.AddDataPoint(grayScaleValue.i, colorValue.b);
a.AddDataPoint(grayScaleValue.a, colorValue.a);
minGray = std::min((int)grayScaleValue.i, minGray);
maxGray = std::max((int)grayScaleValue.i, maxGray);
minAlpha = std::min((int)grayScaleValue.i, minAlpha);
maxAlpha = std::max((int)grayScaleValue.i, maxAlpha);
}
}
for (int y = 0; y < input.height(); ++y) {
for (int x = 0; x < input.width(); ++x) {
PixelIAu8 grayScaleValue = readIAPixel(input, x, y);
grayScaleValue.i = interpolateGrayscale(minGray, maxGray, grayScaleValue.i);
grayScaleValue.a = interpolateGrayscale(minAlpha, maxAlpha, grayScaleValue.a);
writeIAPixel(input, x, y, grayScaleValue);
}
}
maxColor.r = floatToByte(r.PredictY(maxGray));
maxColor.g = floatToByte(g.PredictY(maxGray));
maxColor.b = floatToByte(b.PredictY(maxGray));
maxColor.a = floatToByte(a.PredictY(maxAlpha));
minColor.r = floatToByte(r.PredictY(minGray));
minColor.g = floatToByte(g.PredictY(minGray));
minColor.b = floatToByte(b.PredictY(minGray));
minColor.a = floatToByte(a.PredictY(minAlpha));
}
TextureDefinition::TextureDefinition(const std::string& filename, G_IM_FMT fmt, G_IM_SIZ siz, TextureDefinitionEffect effects) :
mName(getBaseName(replaceExtension(filename, "")) + "_" + gFormatShortName[(int)fmt] + "_" + gSizeName[(int)siz]),
mFmt(fmt),
mSiz(siz),
mEffects(effects) {
cimg_library_suffixed::CImg<unsigned char> imageData(filename.c_str());
if (HasEffect(TextureDefinitionEffect::TwoToneGrayscale)) {
applyTwoToneEffect(imageData, mTwoToneMax, mTwoToneMin);
}
mWidth = imageData.width();
mHeight = imageData.height();
DataChunkStream dataStream;
for (int y = 0; y < mHeight; ++y) {
for (int x = 0; x < mWidth; ++x) {
convertPixel(imageData, x, y, dataStream, fmt, siz);
}
}
auto data = dataStream.GetData();
mData.resize(data.size());
std::copy(data.begin(), data.end(), mData.begin());
}
bool isGrayscale(cimg_library_suffixed::CImg<unsigned char>& input, int x, int y) {
switch (input.spectrum()) {
case 1:
case 2:
return true;
case 3:
case 4:
return input(x, y, 0, 0) == input(x, y, 0, 1) && input(x, y, 0, 1) == input(x, y, 0, 2);
}
return false;
}
int colorHash(cimg_library_suffixed::CImg<unsigned char>& input, int x, int y) {
switch (input.spectrum()) {
case 1:
case 2:
return input(x, y, 0, 0);
case 3:
case 4:
return (input(x, y, 0, 0) << 24) | (input(x, y, 0, 1) << 16) | (input(x, y, 0, 2) << 8);
}
return 0;
}
void TextureDefinition::DetermineIdealFormat(const std::string& filename, G_IM_FMT& fmt, G_IM_SIZ& siz) {
cimg_library_suffixed::CImg<unsigned char> imageData(filename.c_str());
bool hasColor = false;
bool hasFullTransparency = false;
bool hasPartialTransparency = false;
std::set<int> colorCount;
for (int y = 0; y < imageData.height(); ++y) {
for (int x = 0; x < imageData.width(); ++x) {
colorCount.insert(colorHash(imageData, x, y));
bool isPixelGrayscale = isGrayscale(imageData, x, y);
hasColor = hasColor || !isPixelGrayscale;
unsigned char alpha = imageData.spectrum() == 4 ? imageData(x, y, 0, 3) : 0xFF;
hasPartialTransparency = hasPartialTransparency || (alpha != 0 && alpha != 0xFF);
hasFullTransparency = hasFullTransparency || alpha == 0;
}
}
if (hasColor) {
if (hasPartialTransparency) {
fmt = G_IM_FMT::G_IM_FMT_RGBA;
siz = G_IM_SIZ::G_IM_SIZ_32b;
} else {
fmt = G_IM_FMT::G_IM_FMT_RGBA;
siz = G_IM_SIZ::G_IM_SIZ_16b;
}
} else {
if (hasPartialTransparency || hasFullTransparency) {
fmt = G_IM_FMT::G_IM_FMT_IA;
siz = G_IM_SIZ::G_IM_SIZ_16b;
} else {
fmt = G_IM_FMT::G_IM_FMT_I;
siz = G_IM_SIZ::G_IM_SIZ_8b;
}
}
}
std::unique_ptr<FileDefinition> TextureDefinition::GenerateDefinition(const std::string& name, const std::string& location) const {
std::unique_ptr<StructureDataChunk> dataChunk(new StructureDataChunk());
int line;
int index = 0;
GetLine(line);
for (int y = 0; y < mHeight; ++y) {
std::ostringstream stream;
for (int lineIndex = 0; lineIndex < line; ++lineIndex) {
uint64_t data = mData[index];
if (lineIndex != 0) {
stream << ", ";
}
stream << "0x" << std::hex << std::setw(16) << std::setfill('0') << data;
++index;
}
dataChunk->AddPrimitive(stream.str());
}
return std::unique_ptr<FileDefinition>(new DataFileDefinition("u64", name, true, location, std::move(dataChunk), this));
}
int TextureDefinition::Width() const {
return mWidth;
}
int TextureDefinition::Height() const {
return mHeight;
}
G_IM_FMT TextureDefinition::Format() const {
return mFmt;
}
G_IM_SIZ TextureDefinition::Size() const {
return mSiz;
}
#define G_TX_DTX_FRAC 11
int gSizeInc[] = {3, 1, 0, 0};
int gSizeShift[] = {2, 1, 0, 0};
int TextureDefinition::LoadBlockSize() const {
return ((Height() * Width() + gSizeInc[(int)mSiz]) >> gSizeShift[(int)mSiz]) - 1;
}
int TextureDefinition::DTX() const {
int lineSize;
if (mSiz == G_IM_SIZ::G_IM_SIZ_4b) {
lineSize = Width() / 16;
} else {
GetLine(lineSize);
}
if (!lineSize) {
lineSize = 1;
}
return ((1 << G_TX_DTX_FRAC) + lineSize - 1) / lineSize;
}
bool TextureDefinition::GetLine(int& line) const {
int bitLine = bitSizeforSiz(mSiz) * mWidth;
line = bitLine / 64;
return bitLine % 64 == 0;
}
const std::string& TextureDefinition::Name() const {
return mName;
}
bool TextureDefinition::HasEffect(TextureDefinitionEffect effect) const {
return (int)mEffects & (int)effect;
}
PixelRGBAu8 TextureDefinition::GetTwoToneMin() const {
return mTwoToneMin;
}
PixelRGBAu8 TextureDefinition::GetTwoToneMax() const {
return mTwoToneMax;
}

View file

@ -0,0 +1,97 @@
#ifndef __TEXTURE_DEFINITION_H__
#define __TEXTURE_DEFINITION_H__
#include <vector>
#include <inttypes.h>
#include "TextureFormats.h"
#include "../definitions/DataChunk.h"
#include "../definitions/FileDefinition.h"
class DataChunkStream {
public:
DataChunkStream();
void WriteBytes(const char* data, int byteCount);
void WriteBits(int from, int bitCount);
const std::vector<uint64_t>& GetData();
private:
void FlushBuffer();
int mCurrentBufferPos;
uint64_t mCurrentBuffer;
std::vector<uint64_t> mData;
};
struct PixelRGBAu8 {
PixelRGBAu8();
PixelRGBAu8(uint8_t rVal, uint8_t gVal, uint8_t bVal, uint8_t aVal);
bool operator==(const PixelRGBAu8& other) const;
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t a;
bool WriteToStream(DataChunkStream& output, G_IM_SIZ size);
};
struct PixelIu8 {
PixelIu8(uint8_t i);
uint8_t i;
bool WriteToStream(DataChunkStream& output, G_IM_SIZ size);
};
struct PixelIAu8 {
PixelIAu8(uint8_t i, uint8_t a);
uint8_t i;
uint8_t a;
bool WriteToStream(DataChunkStream& output, G_IM_SIZ size);
};
enum class TextureDefinitionEffect {
TwoToneGrayscale = (1 << 0),
};
class TextureDefinition {
public:
TextureDefinition(const std::string& filename, G_IM_FMT fmt, G_IM_SIZ siz, TextureDefinitionEffect effects);
static void DetermineIdealFormat(const std::string& filename, G_IM_FMT& fmt, G_IM_SIZ& siz);
std::unique_ptr<FileDefinition> GenerateDefinition(const std::string& name, const std::string& location) const;
int Width() const;
int Height() const;
G_IM_FMT Format() const;
G_IM_SIZ Size() const;
bool GetLine(int& line) const;
int LoadBlockSize() const;
int DTX() const;
const std::string& Name() const;
bool HasEffect(TextureDefinitionEffect effect) const;
PixelRGBAu8 GetTwoToneMin() const;
PixelRGBAu8 GetTwoToneMax() const;
private:
std::string mName;
G_IM_FMT mFmt;
G_IM_SIZ mSiz;
int mWidth;
int mHeight;
std::vector<unsigned long long> mData;
TextureDefinitionEffect mEffects;
PixelRGBAu8 mTwoToneMin;
PixelRGBAu8 mTwoToneMax;
};
#endif

View file

@ -0,0 +1,47 @@
#include "TextureFormats.h"
const char* G_IM_FMT_NAMES[] = {
"G_IM_FMT_RGBA",
"G_IM_FMT_YUV",
"G_IM_FMT_CI",
"G_IM_FMT_I",
"G_IM_FMT_IA",
};
const char* nameForImageFormat(G_IM_FMT format) {
return G_IM_FMT_NAMES[(int)format];
}
const char* G_IM_SIZ_NAMES[] = {
"G_IM_SIZ_4b",
"G_IM_SIZ_8b",
"G_IM_SIZ_16b",
"G_IM_SIZ_32b",
};
const char* nameForImageSize(G_IM_SIZ size) {
return G_IM_SIZ_NAMES[(int)size];
}
bool G_IM_SUPPORTED[5][4] = {
{false, false, true, true},
{false, false, true, false},
{true, true, false, false},
{true, true, true, false},
{true, true, false, false},
};
bool isImageFormatSupported(G_IM_FMT format, G_IM_SIZ size) {
return G_IM_SUPPORTED[(int)format][(int)size];
}
const int G_IM_SIZ_SIZES[] = {
4,
8,
16,
32,
};
int bitSizeforSiz(G_IM_SIZ input) {
return G_IM_SIZ_SIZES[(int)input];
}

View file

@ -0,0 +1,27 @@
#ifndef __TEXTURE_FORMATS_H__
#define __TEXTURE_FORMATS_H__
enum class G_IM_FMT {
G_IM_FMT_RGBA,
G_IM_FMT_YUV,
G_IM_FMT_CI,
G_IM_FMT_I,
G_IM_FMT_IA,
};
const char* nameForImageFormat(G_IM_FMT format);
enum class G_IM_SIZ {
G_IM_SIZ_4b,
G_IM_SIZ_8b,
G_IM_SIZ_16b,
G_IM_SIZ_32b,
};
const char* nameForImageSize(G_IM_SIZ size);
bool isImageFormatSupported(G_IM_FMT format, G_IM_SIZ size);
int bitSizeforSiz(G_IM_SIZ input);
#endif

View file

@ -0,0 +1,47 @@
#include "LeastSquares.h"
#include <math.h>
LinearLeastSquares::LinearLeastSquares() :
mXSum(0.0),
mYSum(0.0),
mXYSum(0.0),
mXXSum(0.0),
mCount(0.0),
mIsDirty(false),
mSlope(0.0),
mYIntercept(0.0) {
}
void LinearLeastSquares::AddDataPoint(double x, double y) {
mXSum += x;
mYSum += y;
mXYSum += x * y;
mXXSum += x * x;
mCount += 1.0;
mIsDirty = true;
}
void LinearLeastSquares::Calculate() {
if (!mIsDirty) {
return;
}
mIsDirty = false;
double denom = mCount * mXXSum - mXSum * mXSum;
if (fabs(denom) < 0.00000001) {
mSlope = 0.0f;
} else {
mSlope = (mCount * mXYSum - mXSum * mYSum) / denom;
}
mYIntercept = (mYSum - mSlope * mXSum) / mCount;
}
double LinearLeastSquares::PredictY(double x) {
Calculate();
return mYIntercept + mSlope * x;
}

View file

@ -0,0 +1,22 @@
#ifndef __LEAST_SQUARES_H__
#define __LEAST_SQUARES_H__
class LinearLeastSquares {
public:
LinearLeastSquares();
void AddDataPoint(double x, double y);
void Calculate();
double PredictY(double x);
private:
double mXSum;
double mYSum;
double mXYSum;
double mXXSum;
double mCount;
bool mIsDirty;
double mSlope;
double mYIntercept;
};
#endif

View file

@ -0,0 +1,200 @@
#include "MES.h"
#include <random>
#include <math.h>
#include <iostream>
#include <assimp/scene.h>
#include "Vector4.h"
#include <set>
Sphere::Sphere() : radius(0.0f) {}
Sphere::Sphere(const aiVector3D& center, float radius): center(center), radius(radius) {}
bool Sphere::Contains(const aiVector3D& point) {
return (point - center).SquareLength() <= radius * radius * 1.001f;
}
struct Sphere miniumEnclosingSphereTwoPoints(const aiVector3D& a, const aiVector3D& b) {
return Sphere((a + b) * 0.5f, (a - b).Length() * 0.5);
}
bool miniumEnclosingSphereThreePoints(const aiVector3D& a, const aiVector3D& b, const aiVector3D& c, Sphere& output) {
aiMatrix4x4 coef;
aiVector3D edgeA = a - c;
aiVector3D edgeB = b - c;
aiVector3D normal = edgeA ^ edgeB;
if (normal.SquareLength() < 0.00001f) {
return false;
}
aiVector3D top = ((edgeB * edgeA.SquareLength()) - (edgeA * edgeB.SquareLength())) ^ normal;
float bottom = 0.5f / normal.SquareLength();
aiVector3D center = top * bottom;
output.center = c + center;
output.radius = center.Length();
return true;
}
bool miniumEnclosingSphereFourPoints(const aiVector3D& a, const aiVector3D& b, const aiVector3D& c,const aiVector3D& d, Sphere& output) {
aiVector3D edge1 = b - a;
aiVector3D edge2 = c - a;
aiVector3D edge3 = d - a;
float sqLength1 = edge1.SquareLength();
float sqLength2 = edge2.SquareLength();
float sqLength3 = edge3.SquareLength();
float determinant = edge1.x * (edge2.y * edge3.z - edge3.y * edge2.z)
- edge2.x * (edge1.y * edge3.z - edge3.y * edge1.z)
+ edge3.x * (edge1.y * edge2.z - edge2.y * edge1.z);
if (fabs(determinant) < 0.000001f) {
return false;
}
float scalar = 0.5f / determinant;
aiVector3D offset(
scalar * ((edge2.y * edge3.z - edge3.y * edge2.z) * sqLength1 - (edge1.y * edge3.z - edge3.y * edge1.z) * sqLength2 + (edge1.y * edge2.z - edge2.y * edge1.z) * sqLength3),
scalar * (-(edge2.x * edge3.z - edge3.x * edge2.z) * sqLength1 + (edge1.x * edge3.z - edge3.x * edge1.z) * sqLength2 - (edge1.x * edge2.z - edge2.x * edge1.z) * sqLength3),
scalar * ((edge2.x * edge3.y - edge3.x * edge2.y) * sqLength1 - (edge1.x * edge3.y - edge3.x * edge1.y) * sqLength2 + (edge1.x * edge2.y - edge2.x * edge1.y) * sqLength3)
);
output.center = a + offset;
output.radius = offset.Length();
return true;
}
struct Sphere miniumEnclosingSphereTrivial(const std::vector<aiVector3D>& points) {
if (points.size() > 4) {
// degenerate case
return Sphere(aiVector3D(0.0f, 0.0f, 0.0f), 0.0f);
}
if (points.size() == 0) {
return Sphere(aiVector3D(0.0f, 0.0f, 0.0f), 0.0f);
}
if (points.size() == 1) {
return Sphere(points[0], 0.0f);
}
if (points.size() == 2) {
return miniumEnclosingSphereTwoPoints(points[0], points[1]);
}
// see if two points is sufficient
for (unsigned i = 0; i < points.size(); ++i) {
for (unsigned j = i + 1; j < points.size(); ++j) {
Sphere test = miniumEnclosingSphereTwoPoints(points[i], points[j]);
unsigned otherIndex;
for (otherIndex = 0; otherIndex < points.size(); ++otherIndex) {
if (otherIndex == i || otherIndex == j) {
continue;
}
if (!test.Contains(points[otherIndex])) {
break;
}
}
if (otherIndex == points.size()) {
return test;
}
}
}
if (points.size() == 3) {
Sphere result;
if (miniumEnclosingSphereThreePoints(points[0], points[1], points[2], result)) {
return result;
} else {
return miniumEnclosingSphereTwoPoints(points[0], points[1]);
}
}
// see if 3 points are sufficent
for (unsigned i = 0; i < 4; ++i) {
Sphere result;
if (miniumEnclosingSphereThreePoints(points[i == 0 ? 1 : 0], points[i <= 1 ? 2 : 1], points[i <= 2 ? 3 : 2], result) && result.Contains(points[i])) {
return result;
}
}
Sphere result;
if (miniumEnclosingSphereFourPoints(points[0], points[1], points[2], points[3], result)) {
return result;
}
return Sphere(aiVector3D(0.0f, 0.0f, 0.0f), 0.0f);
}
struct Sphere minimumEnclosingSphereMutateInput(std::vector<aiVector3D>& input) {
std::shuffle(input.begin(), input.end(), std::default_random_engine(rand()));
std::set<unsigned> edgeIndices;
Sphere currentSphere;
unsigned pointIndex = 0;
while (pointIndex < input.size()) {
if (edgeIndices.find(pointIndex) != edgeIndices.end() || currentSphere.Contains(input[pointIndex])) {
++pointIndex;
} else {
std::set<unsigned> setCopy;
std::vector<aiVector3D> edgePoints;
for (auto current : edgeIndices) {
if (current > pointIndex) {
setCopy.insert(current);
edgePoints.push_back(input[current]);
}
}
edgeIndices = setCopy;
edgeIndices.insert(pointIndex);
edgePoints.push_back(input[pointIndex]);
currentSphere = miniumEnclosingSphereTrivial(edgePoints);
if (edgePoints.size() > 1 && currentSphere.radius == 0.0f) {
miniumEnclosingSphereTrivial(edgePoints);
std::cout << "something wrong" << std::endl;
}
if (edgeIndices.size() < 4) {
pointIndex = 0;
} else {
pointIndex = pointIndex + 1;
}
}
}
return currentSphere;
}
struct Sphere minimumEnclosingSphereForMeshes(const std::vector<aiMesh*>& input) {
std::vector<aiVector3D> allPoints;
for (auto mesh : input) {
allPoints.insert(allPoints.end(), mesh->mVertices, mesh->mVertices + mesh->mNumVertices);
}
return minimumEnclosingSphereMutateInput(allPoints);
}
struct Sphere minimumEnclosingSphere(const std::vector<aiVector3D>& input) {
std::vector<aiVector3D> copy = input;
return minimumEnclosingSphereMutateInput(copy);
}

View file

@ -0,0 +1,21 @@
#ifndef __MES_H__
#define __MES_H__
#include <assimp/vector3.h>
#include <assimp/mesh.h>
#include <vector>
struct Sphere {
Sphere();
Sphere(const aiVector3D& center, float radius);
aiVector3D center;
float radius;
bool Contains(const aiVector3D& point);
};
Sphere minimumEnclosingSphere(const std::vector<aiVector3D>& input);
Sphere minimumEnclosingSphereForMeshes(const std::vector<aiMesh*>& input);
#endif

View file

@ -0,0 +1,13 @@
#include "Vector4.h"
Vector4::Vector4(): x(0.0f), y(0.0f), z(0.0f), w(0.0f) {}
Vector4::Vector4(float x, float y, float z, float w): x(x), y(y), z(z), w(w) {}
Vector4 operator * (const aiMatrix4x4& matrix, const Vector4& vector) {
return Vector4(
matrix.a1 * vector.x + matrix.a2 * vector.y + matrix.a3 * vector.z + matrix.a4 * vector.w,
matrix.b1 * vector.x + matrix.b2 * vector.y + matrix.b3 * vector.z + matrix.b4 * vector.w,
matrix.c1 * vector.x + matrix.c2 * vector.y + matrix.c3 * vector.z + matrix.c4 * vector.w,
matrix.d1 * vector.x + matrix.d2 * vector.y + matrix.d3 * vector.z + matrix.d4 * vector.w
);
}

View file

@ -0,0 +1,17 @@
#ifndef __VECTOR4_H__
#define __VECTOR4_H__
#include <assimp/matrix4x4.h>
struct Vector4 {
Vector4();
Vector4(float x, float y, float z, float w);
float x;
float y;
float z;
float w;
};
Vector4 operator * (const aiMatrix4x4& matrix, const Vector4& vector);
#endif