mirror of
https://github.com/mwpenny/portal64-still-alive.git
synced 2024-11-23 04:19:50 +00:00
Include skelatool as part of the project to ensure the skelatool version matches the portal version
dockerize the project
This commit is contained in:
parent
62a34c0bda
commit
de394ca30c
29
Dockerfile
29
Dockerfile
@ -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
|
18
Makefile
18
Makefile
@ -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
|
||||
|
13
README.md
13
README.md
@ -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
13
skelatool64/.gitignore
vendored
Normal 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
175
skelatool64/main.cpp
Normal 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
39
skelatool64/makefile
Normal 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
|
695
skelatool64/schema/material-schema.json
Executable file
695
skelatool64/schema/material-schema.json
Executable 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
12
skelatool64/setup_dependencies.sh
Executable file
12
skelatool64/setup_dependencies.sh
Executable 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
|
85
skelatool64/src/Animation.cpp
Normal file
85
skelatool64/src/Animation.cpp
Normal 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;
|
||||
}
|
50
skelatool64/src/Animation.h
Normal file
50
skelatool64/src/Animation.h
Normal 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
|
415
skelatool64/src/AnimationTranslator.cpp
Normal file
415
skelatool64/src/AnimationTranslator.cpp
Normal 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;
|
||||
}
|
10
skelatool64/src/AnimationTranslator.h
Normal file
10
skelatool64/src/AnimationTranslator.h
Normal 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
|
183
skelatool64/src/BoneHierarchy.cpp
Normal file
183
skelatool64/src/BoneHierarchy.cpp
Normal 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();
|
||||
}
|
67
skelatool64/src/BoneHierarchy.h
Normal file
67
skelatool64/src/BoneHierarchy.h
Normal 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
|
398
skelatool64/src/CFileDefinition.cpp
Normal file
398
skelatool64/src/CFileDefinition.cpp
Normal 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;
|
||||
}
|
68
skelatool64/src/CFileDefinition.h
Normal file
68
skelatool64/src/CFileDefinition.h
Normal 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
|
132
skelatool64/src/CommandLineParser.cpp
Normal file
132
skelatool64/src/CommandLineParser.cpp
Normal 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;
|
||||
}
|
27
skelatool64/src/CommandLineParser.h
Normal file
27
skelatool64/src/CommandLineParser.h
Normal 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
|
238
skelatool64/src/DisplayList.cpp
Normal file
238
skelatool64/src/DisplayList.cpp
Normal 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;
|
||||
}
|
153
skelatool64/src/DisplayList.h
Normal file
153
skelatool64/src/DisplayList.h
Normal 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
|
139
skelatool64/src/DisplayListGenerator.cpp
Normal file
139
skelatool64/src/DisplayListGenerator.cpp
Normal 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]);
|
||||
}
|
||||
}
|
13
skelatool64/src/DisplayListGenerator.h
Normal file
13
skelatool64/src/DisplayListGenerator.h
Normal 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
|
25
skelatool64/src/DisplayListSettings.cpp
Normal file
25
skelatool64/src/DisplayListSettings.cpp
Normal 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;
|
||||
}
|
31
skelatool64/src/DisplayListSettings.h
Normal file
31
skelatool64/src/DisplayListSettings.h
Normal 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
0
skelatool64/src/Enum.h
Normal file
10
skelatool64/src/ErrorCode.h
Normal file
10
skelatool64/src/ErrorCode.h
Normal file
@ -0,0 +1,10 @@
|
||||
#ifndef _ERROR_CODE_H
|
||||
#define _ERROR_CODE_H
|
||||
|
||||
enum class ErrorCode {
|
||||
None,
|
||||
ModelTooLarge,
|
||||
MatrixStackOverflow,
|
||||
};
|
||||
|
||||
#endif
|
13
skelatool64/src/ErrorResult.cpp
Normal file
13
skelatool64/src/ErrorResult.cpp
Normal 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;
|
||||
}
|
17
skelatool64/src/ErrorResult.h
Normal file
17
skelatool64/src/ErrorResult.h
Normal 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
|
320
skelatool64/src/ExtendedMesh.cpp
Normal file
320
skelatool64/src/ExtendedMesh.cpp
Normal 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();
|
||||
}
|
48
skelatool64/src/ExtendedMesh.h
Normal file
48
skelatool64/src/ExtendedMesh.h
Normal 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
|
215
skelatool64/src/FileUtils.cpp
Normal file
215
skelatool64/src/FileUtils.cpp
Normal 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;
|
||||
}
|
19
skelatool64/src/FileUtils.h
Normal file
19
skelatool64/src/FileUtils.h
Normal 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
|
11
skelatool64/src/MathUtil.cpp
Normal file
11
skelatool64/src/MathUtil.cpp
Normal 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));
|
||||
}
|
9
skelatool64/src/MathUtl.h
Normal file
9
skelatool64/src/MathUtl.h
Normal 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
|
110
skelatool64/src/MeshWriter.cpp
Normal file
110
skelatool64/src/MeshWriter.cpp
Normal 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();
|
||||
}
|
33
skelatool64/src/MeshWriter.h
Normal file
33
skelatool64/src/MeshWriter.h
Normal 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
|
119
skelatool64/src/RCPState.cpp
Normal file
119
skelatool64/src/RCPState.cpp
Normal 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;
|
||||
}
|
40
skelatool64/src/RCPState.h
Normal file
40
skelatool64/src/RCPState.h
Normal 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
|
71
skelatool64/src/RenderChunk.cpp
Normal file
71
skelatool64/src/RenderChunk.cpp
Normal 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;
|
||||
});
|
||||
}
|
35
skelatool64/src/RenderChunk.h
Normal file
35
skelatool64/src/RenderChunk.h
Normal 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
|
38
skelatool64/src/SceneLoader.cpp
Normal file
38
skelatool64/src/SceneLoader.cpp
Normal 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();
|
||||
}
|
9
skelatool64/src/SceneLoader.h
Normal file
9
skelatool64/src/SceneLoader.h
Normal 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
|
151
skelatool64/src/SceneModification.cpp
Normal file
151
skelatool64/src/SceneModification.cpp
Normal 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);
|
||||
}
|
13
skelatool64/src/SceneModification.h
Normal file
13
skelatool64/src/SceneModification.h
Normal 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
|
143
skelatool64/src/SceneWriter.cpp
Normal file
143
skelatool64/src/SceneWriter.cpp
Normal 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();
|
||||
}
|
||||
}
|
16
skelatool64/src/SceneWriter.h
Normal file
16
skelatool64/src/SceneWriter.h
Normal 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
|
16
skelatool64/src/StringUtils.h
Normal file
16
skelatool64/src/StringUtils.h
Normal 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
|
120
skelatool64/src/StringUtls.cpp
Normal file
120
skelatool64/src/StringUtls.cpp
Normal 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;
|
||||
}
|
255
skelatool64/src/definition_generator/CollisionGenerator.cpp
Normal file
255
skelatool64/src/definition_generator/CollisionGenerator.cpp
Normal 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;
|
||||
}
|
43
skelatool64/src/definition_generator/CollisionGenerator.h
Normal file
43
skelatool64/src/definition_generator/CollisionGenerator.h
Normal 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
|
29
skelatool64/src/definition_generator/DefinitionGenerator.cpp
Normal file
29
skelatool64/src/definition_generator/DefinitionGenerator.cpp
Normal 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]);
|
||||
}
|
||||
}
|
27
skelatool64/src/definition_generator/DefinitionGenerator.h
Normal file
27
skelatool64/src/definition_generator/DefinitionGenerator.h
Normal 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
|
198
skelatool64/src/definition_generator/LevelGenerator.cpp
Normal file
198
skelatool64/src/definition_generator/LevelGenerator.cpp
Normal 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))));
|
||||
}
|
29
skelatool64/src/definition_generator/LevelGenerator.h
Normal file
29
skelatool64/src/definition_generator/LevelGenerator.h
Normal 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
|
64
skelatool64/src/definition_generator/MaterialGenerator.cpp
Normal file
64
skelatool64/src/definition_generator/MaterialGenerator.cpp
Normal 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";
|
||||
}
|
19
skelatool64/src/definition_generator/MaterialGenerator.h
Normal file
19
skelatool64/src/definition_generator/MaterialGenerator.h
Normal 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
|
@ -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");
|
||||
}
|
@ -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
|
65
skelatool64/src/definition_generator/StaticGenerator.cpp
Normal file
65
skelatool64/src/definition_generator/StaticGenerator.cpp
Normal 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;
|
||||
}
|
31
skelatool64/src/definition_generator/StaticGenerator.h
Normal file
31
skelatool64/src/definition_generator/StaticGenerator.h
Normal 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
|
203
skelatool64/src/definitions/DataChunk.cpp
Normal file
203
skelatool64/src/definitions/DataChunk.cpp
Normal 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();
|
||||
}
|
140
skelatool64/src/definitions/DataChunk.h
Normal file
140
skelatool64/src/definitions/DataChunk.h
Normal 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
|
92
skelatool64/src/definitions/FileDefinition.cpp
Normal file
92
skelatool64/src/definitions/FileDefinition.cpp
Normal 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 << ";";
|
||||
}
|
||||
}
|
56
skelatool64/src/definitions/FileDefinition.h
Normal file
56
skelatool64/src/definitions/FileDefinition.h
Normal 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
|
97
skelatool64/src/materials/CombineMode.cpp
Normal file
97
skelatool64/src/materials/CombineMode.cpp
Normal 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;
|
||||
}
|
131
skelatool64/src/materials/CombineMode.h
Normal file
131
skelatool64/src/materials/CombineMode.h
Normal 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
|
51
skelatool64/src/materials/Material.cpp
Normal file
51
skelatool64/src/materials/Material.cpp
Normal 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;
|
||||
}
|
34
skelatool64/src/materials/Material.h
Normal file
34
skelatool64/src/materials/Material.h
Normal 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
|
170
skelatool64/src/materials/MaterialEnums.cpp
Normal file
170
skelatool64/src/materials/MaterialEnums.cpp
Normal 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},
|
||||
};
|
199
skelatool64/src/materials/MaterialEnums.h
Normal file
199
skelatool64/src/materials/MaterialEnums.h
Normal 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
|
740
skelatool64/src/materials/MaterialParser.cpp
Normal file
740
skelatool64/src/materials/MaterialParser.cpp
Normal 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()));
|
||||
}
|
||||
}
|
29
skelatool64/src/materials/MaterialParser.h
Normal file
29
skelatool64/src/materials/MaterialParser.h
Normal 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
|
567
skelatool64/src/materials/MaterialState.cpp
Normal file
567
skelatool64/src/materials/MaterialState.cpp
Normal 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
|
||||
}
|
144
skelatool64/src/materials/MaterialState.h
Normal file
144
skelatool64/src/materials/MaterialState.h
Normal 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
|
114
skelatool64/src/materials/RenderMode.cpp
Normal file
114
skelatool64/src/materials/RenderMode.cpp
Normal 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];
|
||||
}
|
273
skelatool64/src/materials/RenderMode.h
Normal file
273
skelatool64/src/materials/RenderMode.h
Normal 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
|
20
skelatool64/src/materials/TextureCache.cpp
Normal file
20
skelatool64/src/materials/TextureCache.cpp
Normal 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;
|
||||
}
|
16
skelatool64/src/materials/TextureCache.h
Normal file
16
skelatool64/src/materials/TextureCache.h
Normal 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
|
495
skelatool64/src/materials/TextureDefinition.cpp
Normal file
495
skelatool64/src/materials/TextureDefinition.cpp
Normal 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;
|
||||
}
|
97
skelatool64/src/materials/TextureDefinition.h
Normal file
97
skelatool64/src/materials/TextureDefinition.h
Normal 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
|
47
skelatool64/src/materials/TextureFormats.cpp
Normal file
47
skelatool64/src/materials/TextureFormats.cpp
Normal 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];
|
||||
}
|
27
skelatool64/src/materials/TextureFormats.h
Normal file
27
skelatool64/src/materials/TextureFormats.h
Normal 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
|
47
skelatool64/src/math/LeastSquares.cpp
Normal file
47
skelatool64/src/math/LeastSquares.cpp
Normal 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;
|
||||
}
|
22
skelatool64/src/math/LeastSquares.h
Normal file
22
skelatool64/src/math/LeastSquares.h
Normal 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
|
200
skelatool64/src/math/MES.cpp
Normal file
200
skelatool64/src/math/MES.cpp
Normal 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);
|
||||
}
|
21
skelatool64/src/math/MES.h
Normal file
21
skelatool64/src/math/MES.h
Normal 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
|
13
skelatool64/src/math/Vector4.cpp
Normal file
13
skelatool64/src/math/Vector4.cpp
Normal 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
|
||||
);
|
||||
}
|
17
skelatool64/src/math/Vector4.h
Normal file
17
skelatool64/src/math/Vector4.h
Normal 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
|
Loading…
Reference in New Issue
Block a user