├── icon.ico ├── gl_template.dsp ├── gl_template.dsw ├── .gitignore ├── mytest.cpp ├── mdl3.h ├── main.h ├── mac.h ├── TODO ├── addons ├── export3ds.h ├── exportTexture.h ├── btp.h ├── bck.h ├── btp.cpp ├── exportTexture.cpp ├── bck.cpp └── export3ds.cpp ├── simple_gl_common.h ├── parameters.h ├── mdl3.cpp ├── drw1.h ├── evp1.h ├── ui.h ├── resource.h ├── transformtools.h ├── drawbmd.h ├── camera.h ├── bmdread.h ├── inf1.h ├── openfile.h ├── Makefile ├── oglblock.h ├── bmd23ds.cpp ├── vtx1.h ├── clock.h ├── style.css ├── jnt1.h ├── BMDView2.cbp ├── shp1.h ├── resource.rc ├── simple_gl.h ├── parameters.cpp ├── bmdinfo.cpp ├── Matrix44.h ├── common.h ├── Vector3.h ├── tex1.h ├── camera.cpp ├── drw1.cpp ├── README ├── simple_gl_common.cpp ├── common.cpp ├── bmdread.cpp ├── evp1.cpp ├── transformtools.cpp ├── inf1.cpp ├── mat3.h ├── openfile.cpp ├── common └── gccommon.h ├── Vector3.inc ├── ui.cpp ├── main.cpp ├── jnt1.cpp ├── Matrix44.cpp ├── vtx1.cpp ├── Matrix44.inc ├── BMDView2.depend ├── drawBmd.cpp ├── simple_gl.cpp ├── shp1.cpp └── tev.html /icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magcius/bmdview/HEAD/icon.ico -------------------------------------------------------------------------------- /gl_template.dsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magcius/bmdview/HEAD/gl_template.dsp -------------------------------------------------------------------------------- /gl_template.dsw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magcius/bmdview/HEAD/gl_template.dsw -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.dll 3 | *.exe 4 | *.tga 5 | *.bmd 6 | *.bdl 7 | *.3ds 8 | lib3ds* 9 | GL/ 10 | glew.c 11 | -------------------------------------------------------------------------------- /mytest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #if defined BIG_ENDIAN 4 | #error yes 5 | #else 6 | #error no 7 | #endif 8 | -------------------------------------------------------------------------------- /mdl3.h: -------------------------------------------------------------------------------- 1 | #ifndef BMD_MDL3_H 2 | #define BMD_MDL3_H BMD_MDL3_H 3 | 4 | #include 5 | 6 | void writeMdl3Info(FILE* f, std::ostream& out); 7 | 8 | #endif //BMD_MDL3_H 9 | -------------------------------------------------------------------------------- /main.h: -------------------------------------------------------------------------------- 1 | #ifndef BMD_MAIN_H 2 | #define BMD_MAIN_H BMD_MAIN_H 3 | 4 | //TODO: clean up all this globals mess 5 | 6 | #include "drawbmd.h" 7 | 8 | extern std::vector g_models; //in main.cpp 9 | 10 | #endif //BMD_MAIN_H 11 | 12 | -------------------------------------------------------------------------------- /mac.h: -------------------------------------------------------------------------------- 1 | #ifndef BMD_MAC_H 2 | #define BMD_MAC_H BMD_MAC_H 3 | 4 | typedef void* MacStuff; 5 | 6 | //Inits mac-specific stuff when compiled on os x 7 | MacStuff* initMac(); 8 | 9 | void exitMac(MacStuff* s); 10 | 11 | #endif //BMD_MAC_H 12 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | 20130210 - DA 2 | 3 | * Fix issue with Mario's eyes not showing up except when 'G' is hit 4 | DONE * When displaying bones, show it in front of rendered object 5 | DONE * Help text (when F1 pressed) not showing up 6 | DONE * Mouse input for dragging viewpoint 7 | -------------------------------------------------------------------------------- /addons/export3ds.h: -------------------------------------------------------------------------------- 1 | #ifndef EXPORT_3DS_H 2 | #define EXPORT_3DS_H EXPORT_3DS_H 3 | 4 | #include 5 | #include "../bmdread.h" 6 | 7 | //exports a BModel to a .3ds file 8 | void exportAs3ds(const BModel& bmd, const std::string& filename); 9 | 10 | #endif //EXPORT_3DS_H 11 | -------------------------------------------------------------------------------- /simple_gl_common.h: -------------------------------------------------------------------------------- 1 | #ifndef SIMPLE_GL_COMMON_H 2 | #define SIMPLE_GL_COMMON_H SIMPLE_GL_COMMON_H 3 | 4 | void updateProjectionMatrix(int w, int h); 5 | void handleCamera(); 6 | 7 | void setLastFrameSeconds(float s); 8 | float getLastFrameSeconds(); 9 | float getAverageSecondsPerFrame(); 10 | 11 | #endif //SIMPLE_GL_COMMON_H 12 | -------------------------------------------------------------------------------- /parameters.h: -------------------------------------------------------------------------------- 1 | #ifndef BMD_PARAMETERS_H 2 | #define BMD_PARAMETERS_H BMD_PARAMETERS_H 3 | 4 | #include 5 | #include 6 | 7 | int getParameterCount(); 8 | std::string getParameter(int num); 9 | void parseParameters(const char* params); 10 | void parseParameters(int argc, char* argv[]); 11 | 12 | #endif //BMD_PARAMETERS_H 13 | -------------------------------------------------------------------------------- /mdl3.cpp: -------------------------------------------------------------------------------- 1 | #include "mdl3.h" 2 | 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | 8 | void writeMdl3Info(FILE* f, ostream& out) 9 | { 10 | out << string(50, '/') << endl 11 | << "//Mdl3 section" << endl 12 | << string(50, '/') << endl << endl; 13 | 14 | out << "(unimplemented)" << endl << endl << endl; 15 | } 16 | -------------------------------------------------------------------------------- /drw1.h: -------------------------------------------------------------------------------- 1 | #ifndef BMD_DRW1_H 2 | #define BMD_DRW1_H BMD_DRW1_H 3 | 4 | #include "common.h" 5 | #include 6 | #include 7 | 8 | struct Drw1 9 | { 10 | std::vector isWeighted; 11 | std::vector data; 12 | }; 13 | 14 | void dumpDrw1(FILE* f, Drw1& dst); 15 | void writeDrw1Info(FILE* f, std::ostream& out); 16 | 17 | #endif //BMD_DRW1_H 18 | -------------------------------------------------------------------------------- /addons/exportTexture.h: -------------------------------------------------------------------------------- 1 | #ifndef EXPORT_TEXTURE_H 2 | #define EXPORT_TEXTURE_H EXPORT_TEXTURE_H 3 | 4 | #include 5 | #include "../bmdread.h" 6 | 7 | enum IMAGETYPE 8 | { 9 | DDS, 10 | TGA 11 | }; 12 | 13 | void saveTexture(IMAGETYPE imgType, const Image& img, 14 | const std::string& filename); 15 | 16 | void exportTextures(IMAGETYPE imgType, const Tex1& tex, 17 | const std::string& basename); 18 | 19 | #endif //EXPORT_TEXTURE_H 20 | -------------------------------------------------------------------------------- /evp1.h: -------------------------------------------------------------------------------- 1 | #ifndef BMD_EVP1_H 2 | #define BMD_EVP1_H BMD_EVP1_H 3 | 4 | #include "common.h" 5 | 6 | #include 7 | 8 | #include "Matrix44.h" 9 | 10 | struct MultiMatrix 11 | { 12 | std::vector weights; 13 | std::vector indices; //indices into Evp1.matrices (?) 14 | }; 15 | 16 | struct Evp1 17 | { 18 | std::vector weightedIndices; 19 | std::vector matrices; 20 | }; 21 | 22 | void dumpEvp1(FILE* f, Evp1& dst); 23 | void writeEvp1Info(FILE* f, std::ostream& out); 24 | 25 | #endif //BMD_EVP1_H 26 | -------------------------------------------------------------------------------- /ui.h: -------------------------------------------------------------------------------- 1 | #ifndef BMD_UI_H 2 | #define BMD_UI_H BMD_UI_H 3 | 4 | #include 5 | 6 | void menuFileOpenModel(); 7 | void menuFileMergeModel(); 8 | void menuFileOpenAnimation(); 9 | void menuFileExportModel(); 10 | void menuFileExportModel(const std::string& filename); 11 | void menuFileExportTextures(); 12 | void menuFileExportTextures(const std::string& filename); 13 | void menuFileExportShaders(); 14 | void menuFileReimportShaders(); 15 | void menuFileRegenerateShaders(); 16 | 17 | void menuDebugSectioninfo(); 18 | 19 | #endif //BMD_UI_H 20 | -------------------------------------------------------------------------------- /addons/btp.h: -------------------------------------------------------------------------------- 1 | #ifndef BTP_H 2 | #define BTP_H BTP_H 3 | 4 | //btp files contain texture animations for bmd/bdl files 5 | 6 | #include "../common.h" 7 | 8 | #include "../mat3.h" 9 | 10 | struct TextureAnimation 11 | { 12 | std::string name; 13 | std::vector indices; 14 | int indexToMat3Table; 15 | }; 16 | 17 | struct Btp 18 | { 19 | //std::vector stringtable; 20 | std::vector anims; 21 | }; 22 | 23 | Btp* readBtp(FILE* f); 24 | 25 | void animate(Btp& btp, Mat3& mat3, float time); 26 | 27 | #endif //BTP_H 28 | -------------------------------------------------------------------------------- /resource.h: -------------------------------------------------------------------------------- 1 | #define IDI_ICON 100 2 | #define IDD_ABOUT 151 3 | #define IDC_ABOUT_DATE 152 4 | 5 | #define IDM_MENU 200 6 | #define MENU_FILE_OPEN_MODEL 307 7 | #define MENU_FILE_MERGE_MODEL 308 8 | #define MENU_FILE_OPEN_ANIMATION 309 9 | #define MENU_FILE_EXPORT_MODEL 301 10 | #define MENU_FILE_EXPORT_TEXTURES 302 11 | #define MENU_FILE_EXPORT_SHADERS 303 12 | #define MENU_FILE_REIMPORT_SHADERS 304 13 | #define MENU_FILE_REGENERATE_SHADERS 305 14 | #define MENU_FILE_EXIT 306 15 | 16 | #define MENU_HELP_ABOUT 401 17 | 18 | #define MENU_DEBUG_SECTIONINFO 501 19 | -------------------------------------------------------------------------------- /transformtools.h: -------------------------------------------------------------------------------- 1 | #ifndef BMD_TRANSFORMTOOLS_H 2 | #define BMD_TRANSFORMTOOLS_H 3 | 4 | #include 5 | 6 | #include "Matrix44.h" 7 | #include "Vector3.h" 8 | #include "bmdread.h" 9 | #include "shp1.h" 10 | #include "jnt1.h" 11 | 12 | Matrix44f frameMatrix(const Frame& f); 13 | void updateMatrixTable(const BModel& bmd, const Packet& currPacket, 14 | Matrix44f* matrixTable, 15 | std::vector* isMatrixWeighted = NULL); 16 | Vector3f operator*(const Matrix44f& m, const Vector3f& v); 17 | 18 | #endif //BMD_TRANSFORMTOOLS_H 19 | -------------------------------------------------------------------------------- /drawbmd.h: -------------------------------------------------------------------------------- 1 | #ifndef BMD_DRAWBMD_H 2 | #define BMD_DRAWBMD_H BMD_DRAWBMD_H 3 | 4 | struct BModel; 5 | struct OglBlock; 6 | struct Bck; 7 | struct Btp; 8 | 9 | #include 10 | #include "inf1.h" 11 | 12 | struct Model 13 | { 14 | Model(BModel* bmdP = NULL, OglBlock* b = NULL) 15 | : bmd(bmdP), oglBlock(b), bck(NULL), btp(NULL) 16 | { } 17 | 18 | BModel* bmd; 19 | OglBlock* oglBlock; 20 | Bck* bck; 21 | Btp* btp; 22 | SceneGraph sceneGraph; 23 | std::string bmdFileName; 24 | }; 25 | 26 | void drawBmd(Model& bmd, const SceneGraph& sg); 27 | 28 | 29 | #endif //BMD_DRAWBMD_H 30 | -------------------------------------------------------------------------------- /camera.h: -------------------------------------------------------------------------------- 1 | #ifndef BMD_CAMERA_H 2 | #define BMD_CAMERA_H BMD_CAMERA_H 3 | 4 | #include "Matrix44.h" 5 | 6 | Matrix44f getCameraMatrix(); 7 | void store_camera_matrix(void); 8 | void restore_camera_matrix(void); 9 | 10 | void walkLeft(float amount); 11 | void walkRight(float amount); 12 | void walkForward(float amount); 13 | void walkBack(float amount); 14 | void walkUp(float amount); 15 | void walkDown(float amount); 16 | void turnLeft(float amount); 17 | void turnRight(float amount); 18 | void turnUp(float amount); 19 | void turnDown(float amount); 20 | void turnCCW(float amount); 21 | void turnCW(float amount); 22 | 23 | void prepareCamera(); 24 | 25 | #endif //BMD_CAMERA_H 26 | -------------------------------------------------------------------------------- /bmdread.h: -------------------------------------------------------------------------------- 1 | #ifndef BMD_BMDREAD_H 2 | #define BMD_BMDREAD_H BMD_BMDREAD_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "Vector3.h" 8 | #include "common.h" 9 | 10 | 11 | #include "inf1.h" 12 | #include "vtx1.h" 13 | #include "evp1.h" 14 | #include "drw1.h" 15 | #include "jnt1.h" 16 | #include "shp1.h" 17 | #include "mat3.h" 18 | #include "tex1.h" 19 | 20 | //only in zelda files 21 | #include "mdl3.h" 22 | 23 | 24 | struct BModel 25 | { 26 | Inf1 inf1; 27 | Vtx1 vtx1; 28 | Drw1 drw1; 29 | Evp1 evp1; 30 | Jnt1 jnt1; 31 | Shp1 shp1; 32 | Mat3 mat3; 33 | Tex1 tex1; 34 | }; 35 | 36 | BModel* loadBmd(FILE* f); 37 | void writeBmdInfo(FILE* f, std::ostream& out); 38 | 39 | #endif //BMD_BMDREAD_H 40 | -------------------------------------------------------------------------------- /inf1.h: -------------------------------------------------------------------------------- 1 | #ifndef BMD_INF1_H 2 | #define BMD_INF1_H BMD_INF1_H 3 | 4 | #include "common.h" 5 | #include 6 | #include 7 | 8 | struct Node //same as Inf1Entry 9 | { 10 | u16 type, index; 11 | }; 12 | 13 | struct Inf1 14 | { 15 | int numVertices; //no idea what's this good for ;-) 16 | 17 | std::vector scenegraph; 18 | }; 19 | 20 | void dumpInf1(FILE* f, Inf1& dst); 21 | 22 | 23 | //the following is only convenience stuff 24 | 25 | struct SceneGraph 26 | { 27 | int type, index; 28 | std::vector children; 29 | }; 30 | 31 | int buildSceneGraph(/*in*/ const Inf1& inf1, /*out*/ SceneGraph& sg, int j = 0 /* used internally */); 32 | void writeInf1Info(FILE* f, std::ostream& out); 33 | 34 | #endif //BMD_INF1_H 35 | -------------------------------------------------------------------------------- /openfile.h: -------------------------------------------------------------------------------- 1 | #ifndef BMD_OPENFILE_H 2 | #define BMD_OPENFILE_H BMD_OPENFILE_H 3 | 4 | #include 5 | #include 6 | 7 | struct OpenedFile 8 | { 9 | //If the file was compressed, this is the 10 | //filename of the temp file with the uncompressed 11 | //data (needed by closeFile() to delete that file). 12 | //If the file was not compressed, this is "" 13 | std::string tempFileName; 14 | 15 | //this is your file descriptor 16 | FILE* f; 17 | }; 18 | 19 | //opens a file for binary reading, if the 20 | //file is yaz0-compressed an uncompressed 21 | //file is returned 22 | OpenedFile* openFile(const std::string& name); 23 | 24 | //closes a file, deletes created temporary files 25 | void closeFile(OpenedFile* f); 26 | 27 | #endif //BMD_OPENFILE_H 28 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS=-O2 -g -Wall -I . -DGLEW_STATIC 2 | CXXFLAGS=$(CFLAGS) 3 | LDFLAGS=-L. -static-libgcc -static-libstdc++ 4 | LDLIBS=-lopengl32 -lglu32 -lcomdlg32 -luser32 -lgdi32 5 | 6 | BASE_OBJS = glew.o bmdread.o openfile.o common.o drw1.o \ 7 | evp1.o inf1.o jnt1.o mat3.o mdl3.o shp1.o tex1.o \ 8 | vtx1.o transformtools.o addons/export3ds.o addons/exportTexture.o \ 9 | lib3ds.a 10 | 11 | BMDVIEW2_OBJS = drawBmd.o main.o camera.o parameters.o \ 12 | simple_gl_common.o oglblock.o ui.o simple_gl.o \ 13 | addons/bck.o addons/btp.o $(BASE_OBJS) 14 | 15 | all: bmdview2 bmd23ds 16 | 17 | bmdview2: $(BMDVIEW2_OBJS) 18 | $(CXX) $(LDFLAGS) -o $@ $^ $(LDLIBS) 19 | 20 | bmd23ds: $(BASE_OBJS) bmd23ds.o 21 | $(CXX) $(LDFLAGS) -o $@ $^ $(LDLIBS) 22 | -------------------------------------------------------------------------------- /oglblock.h: -------------------------------------------------------------------------------- 1 | #ifndef NICO_OGLBLOCK_H 2 | #define NICO_OGLBLOCK_H NICO_OGLBLOCK_H 3 | 4 | #include "common.h" 5 | #include "GL/glew.h" 6 | 7 | #include 8 | #include 9 | 10 | #include "bmdread.h" 11 | 12 | struct OglMaterial 13 | { 14 | std::string vertexShaderString; 15 | std::string fragmentShaderString; 16 | 17 | bool needsColor[2]; 18 | bool needsTexCoord[8]; 19 | //etc 20 | 21 | 22 | GLhandleARB vertexShader; 23 | GLhandleARB fragmentShader; 24 | GLhandleARB glslProgram; 25 | }; 26 | 27 | struct OglBlock 28 | { 29 | std::vector materials; 30 | }; 31 | 32 | OglBlock* createOglBlock(const Mat3& mat, const std::string& baseName = ""); 33 | void freeOglBlock(OglBlock*& oglBlock); 34 | void setMaterial(int index, const OglBlock& block, const BModel& bmd); 35 | 36 | #endif //NICO_OGLBLOCK_H 37 | -------------------------------------------------------------------------------- /bmd23ds.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | #include "bmdread.h" 7 | #include "openfile.h" 8 | 9 | #include "addons/exportTexture.h" 10 | #include "addons/export3ds.h" 11 | 12 | using namespace std; 13 | 14 | void setStartupText(const std::string& s) 15 | { 16 | cout << s << endl; 17 | } 18 | 19 | void warn(const char* msg, ...) 20 | { 21 | va_list argList; 22 | va_start(argList, msg); 23 | char buff[161]; 24 | vsnprintf(buff, 161, msg, argList); 25 | va_end(argList); 26 | cout << buff << endl; 27 | } 28 | 29 | int main(int argc, char **argv) { 30 | if(argc < 2) 31 | return -1; 32 | 33 | OpenedFile* f = openFile(argv[1]); 34 | if(f == 0) 35 | return -2; 36 | 37 | BModel* mod = loadBmd(f->f); 38 | 39 | exportAs3ds(*mod, string(argv[1]) + string(".3ds")); 40 | delete mod; 41 | } 42 | -------------------------------------------------------------------------------- /vtx1.h: -------------------------------------------------------------------------------- 1 | #ifndef BMD_VTX1_H 2 | #define BMD_VTX1_H BMD_VTX1_H 3 | 4 | #include "common.h" 5 | #include 6 | #include 7 | #include "Vector3.h" 8 | 9 | struct Color 10 | { 11 | unsigned char r, g, b, a; 12 | 13 | void setRGBA(float ri, float gi, float bi, float ai) 14 | { 15 | r = (unsigned char)(ri + .5f); 16 | g = (unsigned char)(gi + .5f); 17 | b = (unsigned char)(bi + .5f); 18 | a = (unsigned char)(ai + .5f); 19 | } 20 | }; 21 | 22 | struct TexCoord 23 | { 24 | float s, t; 25 | 26 | void setST(float si, float ti) 27 | { 28 | s = si; 29 | t = ti; 30 | } 31 | }; 32 | 33 | struct Vtx1 34 | { 35 | std::vector positions; 36 | std::vector normals; 37 | std::vector colors[2]; 38 | std::vector texCoords[8]; 39 | }; 40 | 41 | void dumpVtx1(FILE* f, Vtx1& dst); 42 | void writeVtx1Info(FILE* f, std::ostream& out); 43 | 44 | #endif //BMD_VTX1_H 45 | -------------------------------------------------------------------------------- /addons/bck.h: -------------------------------------------------------------------------------- 1 | #ifndef BCK_H 2 | #define BCK_H BCK_H 3 | 4 | //bck files contain joint animations for bmd/bdl files 5 | 6 | #include "../common.h" 7 | 8 | #include "../jnt1.h" 9 | 10 | struct Key 11 | { 12 | float time; 13 | float value; 14 | float tangent; //?? 15 | }; 16 | 17 | struct JointAnim 18 | { 19 | std::vector scalesX; 20 | std::vector scalesY; 21 | std::vector scalesZ; 22 | 23 | std::vector rotationsX; 24 | std::vector rotationsY; 25 | std::vector rotationsZ; 26 | 27 | std::vector translationsX; 28 | std::vector translationsY; 29 | std::vector translationsZ; 30 | }; 31 | 32 | struct Bck 33 | { 34 | std::vector anims; 35 | int animationLength; 36 | }; 37 | 38 | Bck* readBck(FILE* f); 39 | 40 | //the caller has to ensure that jnt1.frames and bck.anims contain 41 | //the same number of elements 42 | void animate(Bck& bck, Jnt1& jnt1, float time); 43 | 44 | #endif //BCK_H 45 | -------------------------------------------------------------------------------- /clock.h: -------------------------------------------------------------------------------- 1 | #ifndef BMD_CLOCK_H 2 | #define BMD_CLOCK_H BMD_CLOCK_H 3 | 4 | template 5 | class Clock 6 | { 7 | public: 8 | Clock() : m_numValues(0), m_sum(0.0f) { } 9 | 10 | void addValue(float val) 11 | { 12 | m_sum += val; 13 | if(m_numValues < T) 14 | { 15 | m_values[m_numValues] = val; 16 | ++m_numValues; 17 | } 18 | else 19 | { 20 | m_sum -= m_values[0]; 21 | for(int i = 0; i < T - 1; ++i) 22 | m_values[i] = m_values[i + 1]; 23 | m_values[T - 1] = val; 24 | } 25 | } 26 | 27 | float getAverage() 28 | { 29 | if(m_numValues == 0) 30 | return 0; 31 | 32 | float sum = 0.0f; 33 | for(int i = 0; i < m_numValues; ++i) 34 | sum += m_values[i]; 35 | 36 | return sum/m_numValues; 37 | } 38 | 39 | private: 40 | float m_values[T]; 41 | int m_numValues; 42 | float m_sum; 43 | }; 44 | 45 | 46 | 47 | #endif //BMD_CLOCK_H 48 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | /* somewhat inspired by TextMate's bright theme*/ 2 | 3 | body 4 | { 5 | background: #FFF; 6 | color: #000; 7 | font-family: sans-serif; 8 | } 9 | 10 | h1 11 | { 12 | text-shadow: #DDDDDD 3px 3px 5px; 13 | color: #333; 14 | border-style: none none dotted none; 15 | } 16 | 17 | h2 18 | { 19 | color: #222; 20 | text-shadow: #DDDDDD 3px 3px 5px; 21 | } 22 | 23 | h3 24 | { 25 | color: #333; 26 | text-shadow: #DDDDDD 3px 3px 5px; 27 | } 28 | 29 | h4 30 | { 31 | color: #666; 32 | } 33 | 34 | code, pre 35 | { 36 | font-size: 95%; 37 | font-family: "LuxiMono", "Bitstream Vera Sans Mono", "Monaco", "Courier New", monospace; 38 | word-wrap: break-word; 39 | white-space: pre; 40 | white-space: pre-wrap; 41 | white-space: -moz-pre-wrap; 42 | white-space: -o-pre-wrap; 43 | } 44 | 45 | code 46 | { 47 | color: #1C360C; 48 | } 49 | 50 | pre 51 | { 52 | background-color: #f0f0f0; 53 | border: 1px solid #cccbba; 54 | padding: 10px 10px 10px 20px; 55 | } 56 | 57 | /*p { margin-left: 20px }*/ 58 | img { border: 0 } 59 | 60 | -------------------------------------------------------------------------------- /jnt1.h: -------------------------------------------------------------------------------- 1 | #ifndef BMD_JNT1_H 2 | #define BMD_JNT1_H BMD_JNT1_H 3 | 4 | #include "common.h" 5 | 6 | #include "Vector3.h" 7 | #include 8 | #include 9 | #include 10 | #include "Matrix44.h" 11 | 12 | struct Frame 13 | { 14 | float sx, sy, sz; //scale 15 | float rx, ry, rz; //rotation (in degree) 16 | Vector3f t; //translation 17 | std::string name; 18 | 19 | //TODO: 20 | //bounding box, float unknown2 21 | 22 | //experimentally 23 | u16 unknown; 24 | u8 unknown3; 25 | Vector3f bbMin, bbMax; 26 | }; 27 | 28 | struct Jnt1 29 | { 30 | std::vector frames; 31 | 32 | //the Frames have to be converted to matrices 33 | //to be usable by gl. isMatrixValid stores 34 | //if a matrix represents a frame of if the 35 | //frame has changed since the matrix was 36 | //built (in animations for example) 37 | std::vector matrices; 38 | std::vector isMatrixValid; //TODO: use this 39 | 40 | //TODO: unknown array 41 | }; 42 | 43 | void dumpJnt1(FILE* f, Jnt1& dst); 44 | void writeJnt1Info(FILE* f, std::ostream& out); 45 | 46 | #endif //BMD_JNT1_H 47 | -------------------------------------------------------------------------------- /BMDView2.cbp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 39 | 40 | -------------------------------------------------------------------------------- /shp1.h: -------------------------------------------------------------------------------- 1 | #ifndef BMD_SHP1_H 2 | #define BMD_SHP1_H BMD_SHP1_H 3 | 4 | #include 5 | #include 6 | #include "common.h" 7 | #include "Vector3.h" 8 | 9 | struct Index 10 | { 11 | u16 matrixIndex; 12 | u16 posIndex; 13 | u16 normalIndex; 14 | u16 colorIndex[2]; 15 | u16 texCoordIndex[8]; 16 | }; 17 | 18 | struct Primitive 19 | { 20 | u8 type; 21 | std::vector points; 22 | }; 23 | 24 | enum 25 | { 26 | GX_TRIANGLE_STRIP = 0x98, 27 | GX_TRIANGLE_FAN = 0xa0 28 | }; 29 | 30 | struct Packet 31 | { 32 | std::vector primitives; 33 | 34 | std::vector matrixTable; //maps attribute matrix index to draw array index 35 | }; 36 | 37 | struct Attributes 38 | { 39 | bool hasMatrixIndices, hasPositions, hasNormals, hasColors[2], hasTexCoords[8]; 40 | }; 41 | 42 | struct Batch 43 | { 44 | Attributes attribs; 45 | std::vector packets; 46 | 47 | Vector3f bbMin, bbMax; //experimental 48 | u8 matrixType; //experimental 49 | }; 50 | 51 | struct Shp1 52 | { 53 | std::vector batches; 54 | 55 | //TODO: unknown data is missing, ... 56 | }; 57 | 58 | void dumpShp1(FILE* f, Shp1& dst); 59 | void writeShp1Info(FILE* f, std::ostream& out); 60 | 61 | #endif //BMD_SHP1_H 62 | -------------------------------------------------------------------------------- /resource.rc: -------------------------------------------------------------------------------- 1 | #include "resource.h" 2 | #include 3 | 4 | //icon 5 | IDI_ICON ICON DISCARDABLE "icon.ico" 6 | 7 | //menu 8 | IDM_MENU MENU 9 | BEGIN 10 | POPUP "&File" 11 | BEGIN 12 | MENUITEM "&Open model...", MENU_FILE_OPEN_MODEL 13 | MENUITEM "Me&rge model...", MENU_FILE_MERGE_MODEL 14 | MENUITEM "Open &animation...", MENU_FILE_OPEN_ANIMATION 15 | MENUITEM SEPARATOR 16 | MENUITEM "Export &model...", MENU_FILE_EXPORT_MODEL 17 | MENUITEM "Export &textures...", MENU_FILE_EXPORT_TEXTURES 18 | MENUITEM SEPARATOR 19 | MENUITEM "E&xit", MENU_FILE_EXIT 20 | END 21 | POPUP "&Help" 22 | BEGIN 23 | MENUITEM "&About...", MENU_HELP_ABOUT 24 | END 25 | POPUP "&Debug" 26 | BEGIN 27 | MENUITEM "&Section info...", MENU_DEBUG_SECTIONINFO 28 | END 29 | END 30 | 31 | //about dialog 32 | IDD_ABOUT DIALOG DISCARDABLE 80, 40, 175, 109 33 | STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU 34 | CAPTION "About bmdview2" 35 | FONT 8, "MS Sans Serif" 36 | BEGIN 37 | DEFPUSHBUTTON "OK",IDOK,59,79,50,14 38 | ICON IDI_ICON,0,15,14,21,20 39 | LTEXT "bmdview2\nby thakis\n\nthanks to yaz0r, _demo_, MasterPhW, lightning", 40 | 0,78,14,74,51 41 | LTEXT "Build 20050842",IDC_ABOUT_DATE,15,47,55,18 42 | END 43 | -------------------------------------------------------------------------------- /simple_gl.h: -------------------------------------------------------------------------------- 1 | #ifndef SIMPLE_GL_H 2 | #define SIMPLE_GL_H SIMPLE_GL_H 3 | 4 | #include 5 | #include "common.h" 6 | 7 | //Functions in this header have to be implemented by the platform-dependent 8 | //implementation 9 | 10 | int getWindowWidth(); 11 | int getWindowHeight(); 12 | 13 | bool isInputInProgress(); 14 | std::string getCommand(); 15 | 16 | 17 | 18 | void flush(); 19 | 20 | #ifdef _WIN32 21 | 22 | #include 23 | 24 | const int BKEY_LEFT = VK_LEFT; 25 | const int BKEY_RIGHT = VK_RIGHT; 26 | const int BKEY_UP = VK_UP; 27 | const int BKEY_DOWN = VK_DOWN; 28 | const int BKEY_SHIFT = VK_SHIFT; 29 | const int BKEY_CONTROL = VK_CONTROL; 30 | const int BKEY_TAB = VK_TAB; 31 | const int BKEY_PG_UP = VK_PRIOR; 32 | const int BKEY_PG_DOWN = VK_NEXT; 33 | const int BKEY_F1 = 0x70; 34 | const int BKEY_F2 = 0x71; 35 | const int BKEY_ALT = VK_MENU; 36 | #else 37 | const int BKEY_LEFT = 256; 38 | const int BKEY_RIGHT = 257; 39 | const int BKEY_UP = 258; 40 | const int BKEY_DOWN = 259; 41 | const int BKEY_SHIFT = 260; 42 | const int BKEY_CONTROL = 261; 43 | const int BKEY_TAB = '\t'; 44 | const int BKEY_PG_UP = 263; 45 | const int BKEY_PG_DOWN = 264; 46 | const int BKEY_F1 = 265; 47 | const int BKEY_F2 = 266; 48 | const int BKEY_ALT = 267; 49 | const int BKEY_ESCAPE = 0x1b; 50 | #endif 51 | 52 | bool isKeyPressed(int key); 53 | 54 | 55 | #endif //SIMPLE_GL_H 56 | -------------------------------------------------------------------------------- /parameters.cpp: -------------------------------------------------------------------------------- 1 | #include "parameters.h" 2 | 3 | using namespace std; 4 | 5 | static vector g_parameters; 6 | 7 | 8 | int getParameterCount() 9 | { 10 | return g_parameters.size(); 11 | } 12 | 13 | string getParameter(int num) 14 | { 15 | return g_parameters[num]; 16 | } 17 | 18 | void parseParameters(const char* params, vector& dst) 19 | { 20 | while(*params != '\0') 21 | { 22 | while(isspace(*params)) 23 | ++params; 24 | 25 | if(*params == '\"') 26 | { 27 | ++params; 28 | const char* start = params; 29 | while(*params != '\"' && *params != '\0') 30 | ++params; 31 | dst.push_back(string(start, params - start)); 32 | 33 | if(*params == '\0') 34 | break; 35 | ++params; 36 | } 37 | else 38 | { 39 | const char* start = params; 40 | while(!isspace(*params) && *params != '\0') 41 | ++params; 42 | dst.push_back(string(start, params - start)); 43 | 44 | if(*params == '\0') 45 | break; 46 | ++params; 47 | } 48 | } 49 | } 50 | 51 | void parseParameters(const char* params) 52 | { 53 | parseParameters(params, g_parameters); 54 | } 55 | 56 | void parseParameters(int argc, char* argv[], vector& dst) 57 | { 58 | // skip executable name 59 | for(int i = 1; i < argc; ++i) 60 | dst.push_back(argv[i]); 61 | } 62 | 63 | void parseParameters(int argc, char* argv[]) 64 | { 65 | parseParameters(argc, argv, g_parameters); 66 | } 67 | -------------------------------------------------------------------------------- /bmdinfo.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | #include "bmdread.h" 6 | #include "openfile.h" 7 | 8 | #include "addons/exportTexture.h" 9 | #include "addons/export3ds.h" 10 | 11 | //has to be implemented (TODO...) 12 | void setStartupText(const std::string& s) 13 | { 14 | cout << s << endl; 15 | } 16 | 17 | //dito (TODO...) 18 | void warn(const char* msg, ...) 19 | { 20 | va_list argList; 21 | va_start(argList, msg); 22 | char buff[161]; 23 | vsnprintf(buff, 161, msg, argList); 24 | va_end(argList); 25 | cout << buff << endl; 26 | } 27 | 28 | int main(int argc, char* argv[]) 29 | { 30 | if(argc < 2) 31 | return -1; 32 | 33 | OpenedFile* f = openFile(argv[1]); 34 | if(f == 0) 35 | return -2; 36 | 37 | #if 1 38 | writeBmdInfo(f->f, cout); 39 | #elif 0 40 | BModel* mod = loadBmd(f->f); 41 | exportTextures(TGA, mod->tex1, string("testdata/tex") + strchr(argv[1], '/')); 42 | delete mod; 43 | #elif 1 44 | BModel* mod = loadBmd(f->f); 45 | exportAs3ds(*mod, string("testdata/3ds") 46 | + strchr(argv[1], '/') + string(".3ds")); 47 | delete mod; 48 | #else 49 | BModel* mod = loadBmd(f->f); 50 | for(int i = 0; i < mod->tex1.images.size(); ++i) 51 | { 52 | Image& img = mod->tex1.images[i]; 53 | cout << img.originalFormat << ' ' << img.paletteFormat << ' ' 54 | << img.width << ' ' << img.height << endl; 55 | } 56 | delete mod; 57 | #endif 58 | 59 | closeFile(f); 60 | 61 | } 62 | -------------------------------------------------------------------------------- /Matrix44.h: -------------------------------------------------------------------------------- 1 | #ifndef NICO_MATRIX44_INC 2 | #define NICO_MATRIX44_INC NICO_MATRIX44_INC 3 | 4 | template 5 | class Matrix44 6 | { 7 | public: 8 | //standard-constructor keeps matrix uninitialized for efficiency 9 | Matrix44(); 10 | Matrix44(Num _00, Num _01, Num _02, Num _03, 11 | Num _10, Num _11, Num _12, Num _13, 12 | Num _20, Num _21, Num _22, Num _23, 13 | Num _30, Num _31, Num _32, Num _33); 14 | Matrix44(Num values[4][4]); 15 | 16 | void loadIdentity(); 17 | void flip(); 18 | Matrix44 transpose() const; 19 | 20 | //returns Matrix44::ZERO if no inverse exists 21 | Matrix44 inverse() const; 22 | 23 | void loadTranslateRM(Num tx, Num ty, Num tz); 24 | void loadTranslateLM(Num tx, Num ty, Num tz); 25 | 26 | void loadRotateXRM(Num rad); 27 | void loadRotateXLM(Num rad); 28 | 29 | void loadRotateYRM(Num rad); 30 | void loadRotateYLM(Num rad); 31 | 32 | void loadRotateZRM(Num rad); 33 | void loadRotateZLM(Num rad); 34 | 35 | void loadScale(Num sx, Num sy, Num sz); 36 | void loadScale(Num s); 37 | 38 | 39 | Matrix44 operator*(const Matrix44& b) const; 40 | 41 | //checks if two matrices are exactly equal 42 | bool operator==(const Matrix44& b) const; 43 | 44 | operator Num*(); 45 | operator const Num*() const; 46 | 47 | Num* operator[](int i); 48 | const Num* operator[](int i) const; 49 | 50 | static const Matrix44 IDENTITY; 51 | static const Matrix44 ZERO; 52 | 53 | private: 54 | static bool gaussJordan(Num matrix[4][4]); 55 | Num m[4][4]; 56 | }; 57 | 58 | #include "Matrix44.inc" 59 | 60 | typedef Matrix44 Matrix44f; 61 | typedef Matrix44 Matrix44d; 62 | 63 | 64 | #endif //NICO_MATRIX44_INC 65 | -------------------------------------------------------------------------------- /common.h: -------------------------------------------------------------------------------- 1 | #ifndef BMD_COMMON_H 2 | #define BMD_COMMON_H BMD_COMMON_H 3 | 4 | #include "common/gccommon.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #ifdef _MSC_VER 11 | namespace std 12 | { 13 | template 14 | T min(T a, T b) 15 | { 16 | return a < b?a:b; 17 | } 18 | } 19 | #endif 20 | 21 | const float PI = 3.14159265358979323f; 22 | 23 | void log(const char* msg, ...); 24 | void warn(const char* msg, ...); 25 | std::string getString(int pos, FILE* f); 26 | void readStringtable(int pos, FILE* f, std::vector& dest); 27 | 28 | //does more or less the same as readStringtable(), but writes 29 | //the read data to an ostream instead of writing it into a vector 30 | void writeStringtable(std::ostream& out, FILE* f, int offset); 31 | 32 | inline int max(int a, int b) 33 | { 34 | return a > b?a:b; 35 | } 36 | 37 | void setTextColor3f(float r, float g, float b); 38 | void drawText(const char* s, ...); 39 | void setStartupText(const std::string& text); 40 | 41 | /** 42 | Splits a filename into path and basename components. 43 | "c:\bla/blubb\bar.ext" is split into "c:\bla/blubb\" 44 | and "bar.ext" for example 45 | */ 46 | void splitPath(const std::string& filename, std::string& folder, 47 | std::string& basename); 48 | 49 | /** 50 | Splits a basename into name and extension. 51 | "bar.ext" is split into "bar" and "ext" for example. 52 | */ 53 | void splitName(const std::string& basename, std::string& name, 54 | std::string& extension); 55 | 56 | /** 57 | Returns true if file with name fileName exists. 58 | Note that the file fileName could be deleted before you 59 | open it even if this function returns true. 60 | */ 61 | bool doesFileExist(const std::string& fileName); 62 | 63 | 64 | #endif //BMD_COMMON_H 65 | -------------------------------------------------------------------------------- /Vector3.h: -------------------------------------------------------------------------------- 1 | #ifndef NICO_VECTOR3_INC 2 | #define NICO_VECTOR3_INC NICO_VECTOR3_INC 3 | 4 | template 5 | class Vector3 6 | { 7 | public: 8 | //standard constructor leaves vector uninitialized 9 | //for efficiency 10 | Vector3(); 11 | Vector3(const Num& x, const Num& y, const Num& z); 12 | Vector3(const Num v[]); 13 | 14 | Num& operator[](int i); 15 | const Num& operator[](int i) const; 16 | 17 | Num getLength() const; 18 | Num getLengthSquared() const; 19 | Vector3 normalized() const; 20 | void normalize(); 21 | 22 | Vector3 cross(const Vector3& b) const; 23 | Num dot(const Vector3& b) const; 24 | 25 | void setX(const Num& x); 26 | void setY(const Num& y); 27 | void setZ(const Num& z); 28 | void setXYZ(const Num& x, const Num& y, const Num& z); 29 | 30 | Num getX() const; 31 | Num getY() const; 32 | Num getZ() const; 33 | 34 | Num& x(); 35 | Num& y(); 36 | Num& z(); 37 | 38 | const Num& x() const; 39 | const Num& y() const; 40 | const Num& z() const; 41 | 42 | Vector3 operator+(const Vector3& b) const; 43 | Vector3 operator-(const Vector3& b) const; 44 | Vector3 operator-() const; 45 | Vector3 operator*(const Num& b) const; 46 | Vector3 operator/(const Num& b) const; 47 | 48 | template 49 | friend Vector3 operator*(const T& a, const Vector3& b); 50 | 51 | Vector3& operator +=(const Vector3& b); 52 | Vector3& operator -=(const Vector3& b); 53 | Vector3& operator *=(const Num& s); 54 | Vector3& operator /=(const Num& b); 55 | 56 | operator Num*(); 57 | operator const Num*() const; 58 | 59 | private: 60 | Num v[3]; 61 | }; 62 | 63 | #include "Vector3.inc" 64 | 65 | typedef Vector3 Vector3f; 66 | typedef Vector3 Vector3d; 67 | 68 | #endif //NICO_VECTOR3_INC 69 | -------------------------------------------------------------------------------- /tex1.h: -------------------------------------------------------------------------------- 1 | #ifndef BMD_TEX1_H 2 | #define BMD_TEX1_H BMD_TEX1_H 3 | 4 | #include "common.h" 5 | #include 6 | #include 7 | #include 8 | 9 | struct Image; 10 | struct ImageHeader 11 | { 12 | Image* data; 13 | std::string name; 14 | 15 | /* 16 | from gx.h: 17 | 0: clamp to edge 18 | 1: repeat 19 | 2: mirror 20 | */ 21 | u8 wrapS, wrapT; 22 | 23 | u8 minFilter, magFilter; 24 | 25 | //TODO: unknown fields 26 | }; 27 | 28 | const int I8 = 1; 29 | const int I8_A8 = 3; 30 | const int RGBA8 = 6; 31 | const int DXT1 = 14; 32 | 33 | struct Image 34 | { 35 | int format; 36 | int width, height; 37 | 38 | std::vector mipmaps; //points into imageData 39 | std::vector sizes; //image data size for each mipmap 40 | std::vector imageData; 41 | 42 | //NOTE: palettized images are converted 43 | //to non-palettized images during load time, 44 | //i4 is converted to i8, a4i4 and a8i8 is converted to i8a8. 45 | //r5g5b5a3 and r5g6b5 are converted to rgba8. 46 | //(that is, only formats 1 (i8), 3* (i8a8), 6 (rgba8) 47 | //and 14 (dxt1) are used after conversion) 48 | 49 | //TODO: gl image conversions (rgba -> abgr, ai -> ia 50 | //somewhere else?) 51 | 52 | //TODO: this is temporary and belongs somewhere else: 53 | unsigned int texId; 54 | 55 | int originalFormat, paletteFormat; 56 | }; 57 | 58 | struct Tex1 59 | { 60 | std::vector imageHeaders; 61 | 62 | //because several image headers might point to the 63 | //same image data, this data is stored 64 | //separately to save some memory 65 | //(this way only about 1/6 of the memory required 66 | //otherwise is used) 67 | std::vector images; 68 | }; 69 | 70 | void uploadImagesToGl(Tex1& tex1); 71 | 72 | void dumpTex1(FILE* f, Tex1& dst); 73 | void writeTex1Info(FILE* f, std::ostream& out); 74 | 75 | #endif //BMD_TEX1_H 76 | -------------------------------------------------------------------------------- /camera.cpp: -------------------------------------------------------------------------------- 1 | #include "camera.h" 2 | 3 | #include "GL/glew.h" 4 | 5 | static Matrix44f g_camera = Matrix44f::IDENTITY; 6 | static Matrix44f stored_camera; 7 | 8 | Matrix44f getCameraMatrix() 9 | { 10 | return g_camera.inverse(); 11 | } 12 | 13 | void store_camera_matrix(void) 14 | { 15 | stored_camera = g_camera; 16 | } 17 | 18 | void restore_camera_matrix(void) 19 | { 20 | g_camera = stored_camera; 21 | } 22 | 23 | void prepareCamera() 24 | { 25 | glLoadIdentity(); 26 | glLoadMatrixf(g_camera); 27 | } 28 | 29 | void walkLeft(float amount) 30 | { 31 | Matrix44f m; 32 | m.loadTranslateRM(amount, 0.0f, 0.0f); 33 | g_camera = g_camera*m; 34 | } 35 | 36 | void walkRight(float amount) 37 | { 38 | Matrix44f m; 39 | m.loadTranslateRM(-amount, 0.0f, 0.0f); 40 | g_camera = g_camera*m; 41 | } 42 | 43 | void walkForward(float amount) 44 | { 45 | Matrix44f m; 46 | m.loadTranslateRM(0.0f, 0.0f, amount); 47 | g_camera = g_camera*m; 48 | } 49 | 50 | void walkBack(float amount) 51 | { 52 | Matrix44f m; 53 | m.loadTranslateRM(0.0f, 0.0f, -amount); 54 | g_camera = g_camera*m; 55 | } 56 | 57 | void walkUp(float amount) 58 | { 59 | Matrix44f m; 60 | m.loadTranslateRM(0.0f, -amount, 0.0f); 61 | g_camera = g_camera*m; 62 | } 63 | 64 | void walkDown(float amount) 65 | { 66 | Matrix44f m; 67 | m.loadTranslateRM(0.0f, amount, 0.0f); 68 | g_camera = g_camera*m; 69 | } 70 | 71 | void turnLeft(float amount) 72 | { 73 | Matrix44f m; 74 | m.loadRotateYRM(-amount); 75 | g_camera = g_camera*m; 76 | } 77 | 78 | void turnRight(float amount) 79 | { 80 | Matrix44f m; 81 | m.loadRotateYRM(amount); 82 | g_camera = g_camera*m; 83 | } 84 | 85 | void turnUp(float amount) 86 | { 87 | Matrix44f m; 88 | m.loadRotateXRM(-amount); 89 | g_camera = g_camera*m; 90 | } 91 | 92 | void turnDown(float amount) 93 | { 94 | Matrix44f m; 95 | m.loadRotateXRM(amount); 96 | g_camera = g_camera*m; 97 | } 98 | 99 | void turnCCW(float amount) 100 | { 101 | Matrix44f m; 102 | m.loadRotateZRM(-amount); 103 | g_camera = g_camera*m; 104 | } 105 | 106 | void turnCW(float amount) 107 | { 108 | Matrix44f m; 109 | m.loadRotateZRM(amount); 110 | g_camera = g_camera*m; 111 | } 112 | -------------------------------------------------------------------------------- /drw1.cpp: -------------------------------------------------------------------------------- 1 | #include "drw1.h" 2 | 3 | #include 4 | 5 | using namespace std; 6 | 7 | namespace bmd 8 | { 9 | 10 | // DRW1 ///////////////////////////////////////////////////////////// 11 | 12 | struct Drw1Header 13 | { 14 | char tag[4]; 15 | u32 sizeOfSection; 16 | u16 count; 17 | u16 pad; 18 | 19 | //stores for each matrix if it's weighted (normal (0)/skinned (1) matrix types) 20 | u32 offsetToIsWeighted; 21 | 22 | //for normal (0) matrices, this is an index into the global matrix 23 | //table (which stores a matrix for every joint). for skinned 24 | //matrices (1), I'm not yet totally sure how this works (but it's 25 | //probably an offset into the Evp1-array) 26 | u32 offsetToData; 27 | }; 28 | 29 | }; 30 | 31 | void readDrw1Header(FILE* f, bmd::Drw1Header& h) 32 | { 33 | fread(h.tag, 1, 4, f); 34 | readDWORD(f, h.sizeOfSection); 35 | readWORD(f, h.count); 36 | readWORD(f, h.pad); 37 | readDWORD(f, h.offsetToIsWeighted); 38 | readDWORD(f, h.offsetToData); 39 | } 40 | 41 | void dumpDrw1(FILE* f, Drw1& dst) 42 | { 43 | int drw1Offset = ftell(f), i; 44 | 45 | //read header 46 | bmd::Drw1Header h; 47 | readDrw1Header(f, h); 48 | 49 | //read bool array 50 | dst.isWeighted.resize(h.count); 51 | fseek(f, drw1Offset + h.offsetToIsWeighted, SEEK_SET); 52 | for(i = 0; i < h.count; ++i) 53 | { 54 | u8 v; fread(&v, 1, 1, f); 55 | if(v == 0) 56 | dst.isWeighted[i] = false; 57 | else if(v == 1) 58 | dst.isWeighted[i] = true; 59 | else 60 | warn("drw1: unexpected value in isWeighted array: %d", v); 61 | } 62 | 63 | //read data array 64 | dst.data.resize(h.count); 65 | fseek(f, drw1Offset + h.offsetToData, SEEK_SET); 66 | for(i = 0; i < h.count; ++i) 67 | readWORD(f, dst.data[i]); 68 | } 69 | 70 | void writeDrw1Info(FILE* f, ostream& out) 71 | { 72 | out << string(50, '/') << endl 73 | << "//Drw1 section" << endl 74 | << string(50, '/') << endl << endl; 75 | 76 | int drw1Offset = ftell(f), i; 77 | 78 | bmd::Drw1Header h; 79 | readDrw1Header(f, h); 80 | 81 | out << h.count << " many" << endl << endl; 82 | 83 | out << "isWeighted:" << endl; 84 | fseek(f, drw1Offset + h.offsetToIsWeighted, SEEK_SET); 85 | for(i = 0; i < h.count; ++i) 86 | { 87 | u8 v; fread(&v, 1, 1, f); 88 | out << " " << (int)v; 89 | } 90 | out << endl; 91 | 92 | out << "Data:" << endl; 93 | fseek(f, drw1Offset + h.offsetToData, SEEK_SET); 94 | for(i = 0; i < h.count; ++i) 95 | { 96 | u16 v; readWORD(f, v); 97 | out << " " << v; 98 | } 99 | out << endl << endl; 100 | 101 | } 102 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This code lives at the google code server here: 2 | http://code.google.com/p/bmdview2/ 3 | 4 | 5 | Originally it came from this discussion thread: 6 | http://www.emutalk.net/threads/54262-BMDView2-Linux?p=447701 7 | 8 | And that had this download link: 9 | http://dl.dropbox.com/u/21757902/bmdview2.tar.gz 10 | 11 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 12 | Here's an older Readme.txt file that I found from a file BMDview2.rar 13 | 14 | latest version of BMD View 2 at 15 | 16 | http://emutalk.net/showthread.php?t=26919&page=18 17 | 18 | without the install. Later versions of the BMD View 2 might show up later 19 | in the thread. 20 | 21 | ==Controls== 22 | -Move controls- 23 | to move, use the arrow keys. Forward and backward to move forward and 24 | backward, and left and right to pan. Hold Shift to go faster while you 25 | move, and Ctrl to move slower. Also, you can use I, J, K, and L to look 26 | around (Holding left click and moving the mouse works also) Use U, and 27 | O to rotate the view. 28 | 29 | -control model properties- 30 | (press F1 to show controls) 31 | W - For Wireframe 32 | T - Scene Graph (use with shift to hide joints) 33 | B - Show Bones of the model 34 | c - Don't cull (shows back sides of polygons) 35 | A - Don't alphaset (renders transparent colors) 36 | Q - Don't Blend (renders translucent textures as white) 37 | E - Color weighted verticies* 38 | H - Hide model (use for seeing bones easier)* 39 | G - Disable Shaders* 40 | 1 - Show only vertex colors (disables textures)* 41 | 2 - Ignore Vertex color (renders with textures as their normal colors) 42 | 9 - Show batch Bounding Boxes 43 | 0 - Show joint bounding Boxes 44 | *Use to increase FPS. 45 | 46 | -How to view models and animations- 47 | Besides the obvious ways of file>open, and dragging the file on the 48 | executable, you can open a blank window and drag the model in the 49 | window. 50 | 51 | When the model is open, you can drag animation files in the window 52 | (.bck, and .btp) but the animation has to correspond with the model, 53 | usually the names of the files will have the same beginning. 54 | 55 | ==Reasons for bugs== 56 | First of all, make sure that you have the latest drivers for your 57 | graphics card, and make sure your graphics card also supports Pixel 58 | shaders. You might find graphics glitches if your graphics card 59 | doesnt support support pixel shaders. 60 | 61 | ==closing notes== 62 | This model viewer was written by Thakis, with thanks to yaz0r, 63 | _demo_, Master PhW, and Lightning. Readme written by BlueFalcon7. 64 | This viewer has no affiliation with Nintendo what so ever. 65 | 66 | The official emutalk thread for this program can be found at 67 | 68 | http://www.emutalk.net/showthread.php?t=26062 69 | (part 1), and at 70 | 71 | http://emutalk.net/showthread.php?t=26919 72 | (part 2). 73 | -------------------------------------------------------------------------------- /simple_gl_common.cpp: -------------------------------------------------------------------------------- 1 | #include "simple_gl_common.h" 2 | 3 | #include 4 | 5 | #include "GL/glew.h" 6 | 7 | #include "common.h" //PI 8 | #include "camera.h" 9 | #include "clock.h" 10 | 11 | #include "simple_gl.h" 12 | 13 | static float g_transSpeed = 5.0f; 14 | static float g_rotSpeed = PI/4.0f; 15 | static float g_lastFrameSeconds = 0.f; 16 | static Clock<20> g_clock; 17 | 18 | 19 | void nglPerspective(float fovy, float aspect, float z1, float z2) 20 | { 21 | fovy *= 0.5f; //use only half the angle because interval 22 | //goes from -u to u (2*u) 23 | fovy *= PI/180; //convert to radians 24 | 25 | float u = z1*(float)tan(fovy); 26 | float r = u*aspect; 27 | glFrustum(-r, r, -u, u, z1, z2); 28 | } 29 | 30 | void updateProjectionMatrix(int w, int h) 31 | { 32 | float aspect = float(w)/float(h); 33 | 34 | //initialize matrices and viewport 35 | glMatrixMode(GL_PROJECTION); 36 | glLoadIdentity(); 37 | float zNear = 0.1f*128, zFar = 2500.0f*128; 38 | if(w > h) 39 | nglPerspective(2*22.5f, aspect, zNear, zFar); 40 | else 41 | { 42 | //calculate fovy to keep fovx constant 43 | float fovy = (float)atan(tan(22.5f*PI/180)/aspect)*(180/PI); 44 | nglPerspective(2*fovy, aspect, zNear, zFar); 45 | } 46 | glMatrixMode(GL_MODELVIEW); 47 | glLoadIdentity(); 48 | glViewport(0, 0, w, h); 49 | } 50 | 51 | void handleCamera() 52 | { 53 | float walkSpeed = g_transSpeed*getLastFrameSeconds(); 54 | if(isKeyPressed(BKEY_SHIFT)) 55 | walkSpeed *= 10.0f; 56 | if(isKeyPressed(BKEY_CONTROL)) 57 | walkSpeed /= 100.0f; 58 | 59 | walkSpeed *= 128; 60 | 61 | if(isKeyPressed('A')) 62 | walkLeft(walkSpeed); 63 | if(isKeyPressed('D')) 64 | walkRight(walkSpeed); 65 | 66 | if(isKeyPressed('W')) { 67 | if (isKeyPressed(BKEY_ALT)) 68 | walkUp(walkSpeed); 69 | else 70 | walkForward(walkSpeed); 71 | } 72 | 73 | if(isKeyPressed('S')) { 74 | if (isKeyPressed(BKEY_ALT)) 75 | walkDown(walkSpeed); 76 | else 77 | walkBack(walkSpeed); 78 | } 79 | 80 | float rotSpeed = g_rotSpeed*getLastFrameSeconds(); 81 | 82 | if(isKeyPressed('J')) 83 | turnLeft(rotSpeed); 84 | if(isKeyPressed('L')) 85 | turnRight(rotSpeed); 86 | if(isKeyPressed('I')) 87 | turnUp(rotSpeed); 88 | if(isKeyPressed('K')) 89 | turnDown(rotSpeed); 90 | if(isKeyPressed('U')) 91 | turnCCW(rotSpeed); 92 | if(isKeyPressed('O')) 93 | turnCW(rotSpeed); 94 | } 95 | 96 | void setLastFrameSeconds(float s) 97 | { 98 | g_clock.addValue(s); 99 | g_lastFrameSeconds = s; 100 | } 101 | 102 | float getLastFrameSeconds() 103 | { 104 | return g_lastFrameSeconds; 105 | } 106 | 107 | float getAverageSecondsPerFrame() 108 | { 109 | return g_clock.getAverage(); 110 | } 111 | -------------------------------------------------------------------------------- /common.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | #include 4 | 5 | std::string getString(int pos, FILE* f) 6 | { 7 | int t = ftell(f); 8 | fseek(f, pos, SEEK_SET); 9 | 10 | std::string ret; 11 | char c; 12 | while((c = fgetc(f)) != '\0') 13 | ret.append(1, c); 14 | 15 | fseek(f, t, SEEK_SET); 16 | 17 | return ret; 18 | } 19 | 20 | void readStringtable(int pos, FILE* f, std::vector& dest) 21 | { 22 | long oldPos = ftell(f); 23 | 24 | fseek(f, pos, SEEK_SET); 25 | 26 | u16 count; fread(&count, 2, 1, f); toWORD(count); 27 | fseek(f, 2, SEEK_CUR); //skip pad bytes 28 | 29 | for(int i = 0; i < count; ++i) 30 | { 31 | u16 unknown, stringOffset; 32 | fread(&unknown, 2, 1, f); toWORD(unknown); 33 | fread(&stringOffset, 2, 1, f); toWORD(stringOffset); 34 | std::string s = getString(pos + stringOffset, f); 35 | dest.push_back(s); 36 | } 37 | 38 | fseek(f, oldPos, SEEK_SET); 39 | } 40 | 41 | void writeStringtable(std::ostream& out, FILE* f, int offset) 42 | { 43 | int p = ftell(f); 44 | 45 | fseek(f, offset, SEEK_SET); 46 | 47 | u16 count; fread(&count, 2, 1, f); toWORD(count); 48 | fseek(f, 2, SEEK_CUR); //skip pad bytes 49 | 50 | out << "String table (" << count << " entries)" << std::endl; 51 | for(int i = 0; i < count; ++i) 52 | { 53 | u16 unknown, stringOffset; 54 | fread(&unknown, 2, 1, f); toWORD(unknown); 55 | fread(&stringOffset, 2, 1, f); toWORD(stringOffset); 56 | std::string s = getString(offset + stringOffset, 57 | f); 58 | out << " 0x" << std::hex << unknown << " - " << s << std::endl; 59 | } 60 | 61 | fseek(f, p, SEEK_SET); 62 | } 63 | 64 | void splitPath(const std::string& filename, std::string& folder, 65 | std::string& basename) 66 | { 67 | std::string::size_type a = filename.rfind('\\'); 68 | std::string::size_type b = filename.rfind('/'); 69 | std::string::size_type c; 70 | if(a == std::string::npos) 71 | c = b; 72 | else if(b == std::string::npos) 73 | c = a; 74 | else 75 | c = std::min(a, b); 76 | if(c != std::string::npos) 77 | { 78 | folder = filename.substr(0, c + 1); 79 | basename = filename.substr(c + 1); 80 | } 81 | else 82 | { 83 | folder = ""; 84 | basename = filename; 85 | } 86 | } 87 | 88 | void splitName(const std::string& basename, std::string& name, 89 | std::string& extension) 90 | { 91 | std::string::size_type a = basename.rfind('.'); 92 | if(a != std::string::npos) 93 | { 94 | name = basename.substr(0, a); 95 | extension = basename.substr(a + 1); 96 | } 97 | else 98 | { 99 | name = basename; 100 | extension = ""; 101 | } 102 | } 103 | 104 | bool doesFileExist(const std::string& fileName) 105 | { 106 | FILE* f = fopen(fileName.c_str(), "rb"); 107 | bool ret = f != NULL; 108 | if(f != NULL) 109 | fclose(f); 110 | return ret; 111 | } 112 | -------------------------------------------------------------------------------- /bmdread.cpp: -------------------------------------------------------------------------------- 1 | #include "bmdread.h" 2 | #include // strncmp 3 | #include 4 | 5 | using namespace std; 6 | 7 | 8 | void readBmd(FILE* f, BModel* dst) 9 | { 10 | //skip file header 11 | fseek(f, 0x20, SEEK_SET); 12 | 13 | u32 size = 0; 14 | char tag[4]; 15 | int t; 16 | 17 | do 18 | { 19 | fseek(f, size, SEEK_CUR); 20 | t = ftell(f); 21 | 22 | fread(tag, 1, 4, f); 23 | fread(&size, 4, 1, f); 24 | toDWORD(size); 25 | if(size < 8) size = 8; //prevent endless loop on corrupt data 26 | 27 | if(feof(f)) 28 | break; 29 | 30 | fseek(f, t, SEEK_SET); 31 | 32 | if(strncmp(tag, "INF1", 4) == 0) 33 | dumpInf1(f, dst->inf1); 34 | else if(strncmp(tag, "VTX1", 4) == 0) 35 | dumpVtx1(f, dst->vtx1); 36 | else if(strncmp(tag, "EVP1", 4) == 0) 37 | dumpEvp1(f, dst->evp1); 38 | else if(strncmp(tag, "DRW1", 4) == 0) 39 | dumpDrw1(f, dst->drw1); 40 | else if(strncmp(tag, "JNT1", 4) == 0) 41 | dumpJnt1(f, dst->jnt1); 42 | else if(strncmp(tag, "SHP1", 4) == 0) 43 | dumpShp1(f, dst->shp1); 44 | //else if(strncmp(tag, "MAT3", 4) == 0) 45 | else if(strncmp(tag, "MAT", 3) == 0) //s_forest.bmd has a MAT2 section 46 | dumpMat3(f, dst->mat3); 47 | else if(strncmp(tag, "TEX1", 4) == 0) 48 | dumpTex1(f, dst->tex1); 49 | else 50 | warn("readBmd(): Unsupported section \'%c%c%c%c\'", 51 | tag[0], tag[1], tag[2], tag[3]); 52 | 53 | fseek(f, t, SEEK_SET); 54 | 55 | } while(!feof(f)); 56 | } 57 | 58 | BModel* loadBmd(FILE* f) 59 | { 60 | BModel* ret = new BModel; 61 | readBmd(f, ret); 62 | return ret; 63 | } 64 | 65 | void writeBmdInfo(FILE* f, std::ostream& out) 66 | { 67 | //skip file header 68 | fseek(f, 0x20, SEEK_SET); 69 | 70 | u32 size = 0; 71 | char tag[4]; 72 | int t; 73 | 74 | do 75 | { 76 | fseek(f, size, SEEK_CUR); 77 | t = ftell(f); 78 | 79 | fread(tag, 1, 4, f); 80 | fread(&size, 4, 1, f); 81 | toDWORD(size); 82 | if(size < 8) size = 8; //prevent endless loop on corrupt data 83 | 84 | if(feof(f)) 85 | break; 86 | 87 | fseek(f, t, SEEK_SET); 88 | 89 | if(strncmp(tag, "INF1", 4) == 0) 90 | writeInf1Info(f, out); 91 | else if(strncmp(tag, "VTX1", 4) == 0) 92 | writeVtx1Info(f, out); 93 | else if(strncmp(tag, "EVP1", 4) == 0) 94 | writeEvp1Info(f, out); 95 | else if(strncmp(tag, "DRW1", 4) == 0) 96 | writeDrw1Info(f, out); 97 | else if(strncmp(tag, "JNT1", 4) == 0) 98 | writeJnt1Info(f, out); 99 | else if(strncmp(tag, "SHP1", 4) == 0) 100 | writeShp1Info(f, out); 101 | //else if(strncmp(tag, "MAT3", 4) == 0) 102 | else if(strncmp(tag, "MAT", 3) == 0) //s_forest.bmd has a MAT2 section 103 | writeMat3Info(f, out); 104 | else if(strncmp(tag, "TEX1", 4) == 0) 105 | writeTex1Info(f, out); 106 | else if(strncmp(tag, "MDL3", 4) == 0) 107 | writeMdl3Info(f, out); 108 | 109 | fseek(f, t, SEEK_SET); 110 | } while(!feof(f)); 111 | } 112 | -------------------------------------------------------------------------------- /evp1.cpp: -------------------------------------------------------------------------------- 1 | #include "evp1.h" 2 | 3 | #include 4 | using namespace std; 5 | 6 | namespace bmd 7 | { 8 | 9 | struct Evp1Header 10 | { 11 | char tag[4]; //'EVP1' 12 | u32 sizeOfSection; 13 | u16 count; 14 | u16 pad; 15 | 16 | //0 - count many bytes, each byte describes how many bones belong to this index 17 | //1 - sum over all bytes in 0 many shorts (index into some joint stuff? into matrix table?) 18 | //2 - bone weights table (as many floats as shorts in 1) 19 | //3 - matrix table (matrix is 3x4 float array) 20 | u32 offsets[4]; 21 | }; 22 | 23 | }; 24 | 25 | void readEvp1Header(FILE* f, bmd::Evp1Header& h) 26 | { 27 | fread(h.tag, 1, 4, f); 28 | readDWORD(f, h.sizeOfSection); 29 | readWORD(f, h.count); 30 | readWORD(f, h.pad); 31 | for(int i = 0; i < 4; ++i) 32 | readDWORD(f, h.offsets[i]); 33 | } 34 | 35 | void readArray8(FILE* f, vector& arr) 36 | { 37 | for(size_t i = 0; i < arr.size(); ++i) 38 | { 39 | u8 v; fread(&v, 1, 1, f); 40 | arr[i] = v; 41 | } 42 | } 43 | 44 | void readMatrix(FILE* f, f32 m[3][4]) 45 | { 46 | fread(&m[0][0], 4, 3*4, f); 47 | for(int j = 0; j < 3; ++j) 48 | for(int k = 0; k < 4; ++k) 49 | toFLOAT(m[j][k]); 50 | } 51 | 52 | void dumpEvp1(FILE* f, Evp1& dst) 53 | { 54 | int evp1Offset = ftell(f), i; 55 | 56 | //read header 57 | bmd::Evp1Header h; 58 | readEvp1Header(f, h); 59 | 60 | //read counts array 61 | fseek(f, evp1Offset + h.offsets[0], SEEK_SET); 62 | vector counts(h.count); 63 | readArray8(f, counts); 64 | 65 | //read indices of weighted matrices 66 | dst.weightedIndices.resize(h.count); 67 | fseek(f, evp1Offset + h.offsets[1], SEEK_SET); 68 | int numMatrices = 0; 69 | for(i = 0; i < h.count; ++i) 70 | { 71 | dst.weightedIndices[i].indices.resize(counts[i]); 72 | for(int j = 0; j < counts[i]; ++j) 73 | { 74 | u16 d; fread(&d, 2, 1, f); toWORD(d); 75 | dst.weightedIndices[i].indices[j] = d; 76 | numMatrices = max(numMatrices, d + 1); 77 | } 78 | } 79 | 80 | //read weights of weighted matrices 81 | fseek(f, evp1Offset + h.offsets[2], SEEK_SET); 82 | for(i = 0; i < h.count; ++i) 83 | { 84 | dst.weightedIndices[i].weights.resize(counts[i]); 85 | for(int j = 0; j < counts[i]; ++j) 86 | { 87 | float fl; fread(&fl, 4, 1, f); toFLOAT(fl); 88 | dst.weightedIndices[i].weights[j] = fl; 89 | } 90 | } 91 | 92 | //read matrices 93 | dst.matrices.resize(numMatrices); 94 | fseek(f, evp1Offset + h.offsets[3], SEEK_SET); 95 | for(i = 0; i < numMatrices; ++i) 96 | { 97 | f32 m[3][4]; 98 | readMatrix(f, m); 99 | 100 | Matrix44f& dMat = dst.matrices[i]; 101 | for(int j = 0; j < 3; ++j) 102 | for(int k = 0; k < 4; ++k) 103 | dMat[j][k] = m[j][k]; 104 | dMat[3][0] = 0.f; dMat[3][1] = 0.f; dMat[3][2] = 0.f; dMat[3][3] = 1.f; 105 | } 106 | } 107 | 108 | void writeEvp1Info(FILE* f, ostream& out) 109 | { 110 | out << string(50, '/') << endl 111 | << "//Evp1 section (incomplete)" << endl 112 | << string(50, '/') << endl << endl; 113 | 114 | bmd::Evp1Header h; 115 | readEvp1Header(f, h); 116 | 117 | out << h.count << " many" << endl << endl; 118 | } 119 | -------------------------------------------------------------------------------- /transformtools.cpp: -------------------------------------------------------------------------------- 1 | #include "transformtools.h" 2 | 3 | #include 4 | 5 | using namespace std; 6 | 7 | Matrix44f frameMatrix(const Frame& f) 8 | { 9 | Matrix44f t, rx, ry, rz, s; 10 | t.loadTranslateLM(f.t.x(), f.t.y(), f.t.z()); 11 | rx.loadRotateXLM(f.rx/360.f*2*PI); 12 | ry.loadRotateYLM(f.ry/360.f*2*PI); 13 | rz.loadRotateZLM(f.rz/360.f*2*PI); 14 | s.loadScale(f.sx, f.sy, f.sz); 15 | 16 | //this is probably right this way: 17 | //return t*rz*ry*rx*s; //scales seem to be local only 18 | return t*rz*ry*rx; 19 | 20 | //experimental: 21 | if(f.unknown == 0) 22 | return t*rx*ry*rz; 23 | else if(f.unknown == 1) 24 | return t*ry*rz*rx; 25 | else if(f.unknown == 2) 26 | return t*rz*ry*rx; 27 | else 28 | assert(false); 29 | } 30 | 31 | Matrix44f& mad(Matrix44f& r, const Matrix44f& m, float f) 32 | { 33 | for(int j = 0; j < 3; ++j) 34 | for(int k = 0; k < 4; ++k) 35 | r[j][k] += f*m[j][k]; 36 | return r; 37 | } 38 | 39 | Matrix44f localMatrix(int i, const BModel& bm) 40 | { 41 | Matrix44f s; 42 | s.loadScale(bm.jnt1.frames[i].sx, bm.jnt1.frames[i].sy, bm.jnt1.frames[i].sz); 43 | 44 | //TODO: I don't know which of these two return values are the right ones 45 | //(if it's the first, then what is scale used for at all?) 46 | 47 | //looks wrong in certain circumstances... 48 | return bm.jnt1.matrices[i]; //this looks better with vf_064l.bdl (from zelda) 49 | return bm.jnt1.matrices[i]*s; //this looks a bit better with mario's bottle_in animation 50 | } 51 | 52 | 53 | void updateMatrixTable(const BModel& bmd, const Packet& currPacket, Matrix44f* matrixTable, 54 | vector* isMatrixWeighted) 55 | { 56 | for(size_t n = 0; n < currPacket.matrixTable.size(); ++n) 57 | { 58 | if(currPacket.matrixTable[n] != 0xffff) //this means keep old entry 59 | { 60 | u16 index = currPacket.matrixTable[n]; 61 | if(bmd.drw1.isWeighted[index]) 62 | { 63 | //TODO: the EVP1 data should probably be used here, 64 | //figure out how this works (most files look ok 65 | //without this, but models/ji.bdl is for example 66 | //broken this way) 67 | //matrixTable[n] = def; 68 | 69 | //the following _does_ the right thing...it looks 70 | //ok for all files, but i don't understand why :-P 71 | //(and this code is slow as hell, so TODO: fix this) 72 | 73 | //NO idea if this is right this way... 74 | Matrix44f m = Matrix44f::ZERO; 75 | const MultiMatrix& mm = bmd.evp1.weightedIndices[bmd.drw1.data[index]]; 76 | for(size_t r = 0; r < mm.weights.size(); ++r) 77 | { 78 | const Matrix44f& sm1 = bmd.evp1.matrices[mm.indices[r]]; 79 | const Matrix44f& sm2 = localMatrix(mm.indices[r], bmd); 80 | mad(m, sm2*sm1, mm.weights[r]); 81 | } 82 | m[3][3] = 1; 83 | 84 | matrixTable[n] = m; 85 | if(isMatrixWeighted != NULL) 86 | (*isMatrixWeighted)[n] = true; 87 | } 88 | else 89 | { 90 | matrixTable[n] = bmd.jnt1.matrices[bmd.drw1.data[index]]; 91 | if(isMatrixWeighted != NULL) 92 | (*isMatrixWeighted)[n] = false; 93 | } 94 | } 95 | } 96 | } 97 | 98 | Vector3f operator*(const Matrix44f& m, const Vector3f& v) 99 | { 100 | return Vector3f( 101 | m[0][0]*v[0] + m[0][1]*v[1] + m[0][2]*v[2] + m[0][3], 102 | m[1][0]*v[0] + m[1][1]*v[1] + m[1][2]*v[2] + m[1][3], 103 | m[2][0]*v[0] + m[2][1]*v[1] + m[2][2]*v[2] + m[2][3] 104 | ); 105 | } 106 | 107 | -------------------------------------------------------------------------------- /inf1.cpp: -------------------------------------------------------------------------------- 1 | #include "inf1.h" 2 | 3 | #include 4 | using namespace std; 5 | 6 | namespace bmd 7 | { 8 | 9 | struct Inf1Header 10 | { 11 | char tag[4]; //'INF1' 12 | u32 sizeOfSection; 13 | u16 unknown1; 14 | u16 pad; //0xffff 15 | u32 unknown2; 16 | u32 vertexCount; //number of coords in VTX1 section 17 | u32 offsetToEntries; //offset relative to Inf1Header start 18 | }; 19 | 20 | //This stores the scene graph of the file 21 | struct Inf1Entry 22 | { 23 | //0x10: Joint 24 | //0x11: Material 25 | //0x12: Shape (ie. Batch) 26 | //0x01: Hierarchy down (insert node), new child 27 | //0x02: Hierarchy up, close child 28 | //0x00: Terminator 29 | u16 type; 30 | 31 | //Index into Joint, Material or Shape table 32 | //always zero for types 0, 1 and 2 33 | u16 index; 34 | }; 35 | 36 | }; 37 | 38 | void readInf1Header(FILE* f, bmd::Inf1Header& h) 39 | { 40 | fread(h.tag, 1, 4, f); 41 | readDWORD(f, h.sizeOfSection); 42 | readWORD(f, h.unknown1); 43 | readWORD(f, h.pad); 44 | readDWORD(f, h.unknown2); 45 | readDWORD(f, h.vertexCount); 46 | readDWORD(f, h.offsetToEntries); 47 | } 48 | 49 | void readInf1Entry(FILE* f, bmd::Inf1Entry& e) 50 | { 51 | readWORD(f, e.type); 52 | readWORD(f, e.index); 53 | } 54 | 55 | void dumpInf1(FILE* f, Inf1& dst) 56 | { 57 | long inf1Offset = ftell(f); 58 | 59 | //read header 60 | bmd::Inf1Header h; 61 | readInf1Header(f, h); 62 | 63 | dst.numVertices = h.vertexCount; 64 | 65 | //read scene graph 66 | fseek(f, inf1Offset + h.offsetToEntries, SEEK_SET); 67 | 68 | bmd::Inf1Entry e; 69 | readInf1Entry(f, e); 70 | 71 | while(e.type != 0) 72 | { 73 | Node node = { e.type, e.index }; 74 | dst.scenegraph.push_back(node); 75 | 76 | readInf1Entry(f, e); 77 | } 78 | } 79 | 80 | //the following is only convenience stuff 81 | int buildSceneGraph(const Inf1& inf1, SceneGraph& sg, int j) 82 | { 83 | for(size_t i = j; i < inf1.scenegraph.size(); ++i) 84 | { 85 | const Node& n = inf1.scenegraph[i]; 86 | 87 | if(n.type == 1) 88 | i += buildSceneGraph(inf1, sg.children.back(), i + 1); 89 | else if(n.type == 2) 90 | return i - j + 1; 91 | else if(n.type == 0x10 || n.type == 0x11 || n.type == 0x12) 92 | { 93 | SceneGraph t; 94 | t.type = n.type; t.index = n.index; 95 | sg.children.push_back(t); 96 | } 97 | else 98 | warn("buildSceneGraph(): unexpected node type %d", n.type); 99 | 100 | } 101 | 102 | //remove dummy node at root 103 | if(sg.children.size() == 1) 104 | { 105 | SceneGraph t = sg.children[0]; 106 | sg = t; //doesn't work without the temporary (msvc bug?) 107 | } 108 | else 109 | { 110 | sg.type = sg.index = -1; 111 | warn("buildSceneGraph(): Unexpected size %d", sg.children.size()); 112 | } 113 | 114 | return 0; 115 | } 116 | 117 | void writeInf1Info(FILE* f, ostream& out) 118 | { 119 | out << string(50, '/') << endl 120 | << "//Inf1 section (TODO: dump decoded scenegraph?)" << endl 121 | << string(50, '/') << endl << endl; 122 | 123 | long inf1Offset = ftell(f); 124 | 125 | bmd::Inf1Header h; 126 | readInf1Header(f, h); 127 | 128 | 129 | out << h.vertexCount << " vertices in file." << endl 130 | << "Scenegraph:" << endl; 131 | 132 | 133 | fseek(f, inf1Offset + h.offsetToEntries, SEEK_SET); 134 | 135 | bmd::Inf1Entry e; 136 | readInf1Entry(f, e); 137 | 138 | while(e.type != 0) 139 | { 140 | out << " (" << e.type << ", " << e.index << ")"; 141 | readInf1Entry(f, e); 142 | } 143 | 144 | out << endl << endl; 145 | } 146 | -------------------------------------------------------------------------------- /mat3.h: -------------------------------------------------------------------------------- 1 | #ifndef BMD_MAT3_H 2 | #define BMD_MAT3_H BMD_MAT3_H 3 | 4 | #include "common.h" 5 | 6 | #include 7 | 8 | struct MColor 9 | { 10 | u8 r, g, b, a; 11 | }; 12 | 13 | struct Color16 14 | { 15 | s16 r, g, b, a; 16 | }; 17 | 18 | struct ColorChanInfo 19 | { 20 | //not sure if this is right 21 | u8 enable; 22 | u8 matColorSource; 23 | u8 litMask; 24 | u8 diffuseAttenuationFunc; 25 | u8 attenuationFracFunc; 26 | u8 ambColorSource; 27 | u8 pad[2]; 28 | }; 29 | 30 | struct TexGenInfo 31 | { 32 | u8 texGenType; 33 | u8 texGenSrc; 34 | u8 matrix; 35 | }; 36 | 37 | struct TexMtxInfo 38 | { 39 | float scaleCenterX, scaleCenterY; 40 | float scaleU, scaleV; 41 | }; 42 | 43 | struct TevOrderInfo 44 | { 45 | u8 texCoordId; 46 | u8 texMap; 47 | u8 chanId; 48 | }; 49 | 50 | struct TevSwapModeInfo 51 | { 52 | u8 rasSel; 53 | u8 texSel; 54 | }; 55 | 56 | struct TevSwapModeTable 57 | { 58 | u8 r; 59 | u8 g; 60 | u8 b; 61 | u8 a; 62 | }; 63 | 64 | struct AlphaCompare 65 | { 66 | u8 comp0, ref0; 67 | u8 alphaOp; 68 | u8 comp1, ref1; 69 | }; 70 | 71 | struct BlendInfo 72 | { 73 | u8 blendMode; 74 | u8 srcFactor, dstFactor; 75 | u8 logicOp; 76 | }; 77 | 78 | struct ZMode 79 | { 80 | bool enable; 81 | u8 zFunc; 82 | bool enableUpdate; 83 | }; 84 | 85 | 86 | struct TevStageInfo 87 | { 88 | //GX_SetTevColorIn() arguments 89 | u8 colorIn[4]; //GX_CC_* 90 | 91 | //GX_SetTevColorOp() arguments 92 | u8 colorOp; 93 | u8 colorBias; 94 | u8 colorScale; 95 | u8 colorClamp; 96 | u8 colorRegId; 97 | 98 | //GX_SetTevAlphaIn() arguments 99 | u8 alphaIn[4]; //GC_CA_* 100 | 101 | //GX_SetTevAlphaOp() arguments 102 | u8 alphaOp; 103 | u8 alphaBias; 104 | u8 alphaScale; 105 | u8 alphaClamp; 106 | u8 alphaRegId; 107 | }; 108 | 109 | struct Material 110 | { 111 | u8 flag; 112 | u8 cullIndex; 113 | u8 numChansIndex; 114 | u8 texGenCountIndex; 115 | u8 tevCountIndex; 116 | 117 | u8 zModeIndex; 118 | 119 | u16 color1[2]; 120 | u16 chanControls[4]; 121 | u16 color2[2]; 122 | 123 | u16 texGenInfos[8]; 124 | 125 | u16 texMtxInfos[8]; 126 | 127 | u16 texStages[8]; 128 | //constColor (GX_TEV_KCSEL_K0-3) 129 | u16 color3[4]; 130 | u8 constColorSel[16]; //0x0c most of the time (const color sel, GX_TEV_KCSEL_*) 131 | u8 constAlphaSel[16]; //0x1c most of the time (const alpha sel, GX_TEV_KASEL_*) 132 | u16 tevOrderInfo[16]; 133 | //this is to be loaded into 134 | //GX_CC_CPREV - GX_CC_A2?? 135 | u16 colorS10[4]; 136 | u16 tevStageInfo[16]; 137 | u16 tevSwapModeInfo[16]; 138 | u16 tevSwapModeTable[4]; 139 | 140 | u16 alphaCompIndex; 141 | u16 blendIndex; 142 | }; 143 | 144 | struct Mat3 145 | { 146 | std::vector color1; 147 | std::vector numChans; 148 | std::vector colorChanInfos; 149 | std::vector color2; 150 | 151 | std::vector materials; 152 | std::vector indexToMatIndex; 153 | std::vector stringtable; 154 | 155 | std::vector cullModes; 156 | 157 | std::vector texGenCounts; 158 | std::vector texGenInfos; 159 | std::vector texMtxInfos; 160 | 161 | std::vector texStageIndexToTextureIndex; 162 | std::vector tevOrderInfos; 163 | std::vector colorS10; 164 | std::vector color3; 165 | std::vector tevCounts; 166 | std::vector tevStageInfos; 167 | std::vector tevSwapModeInfos; 168 | std::vector tevSwapModeTables; 169 | std::vector alphaCompares; 170 | std::vector blendInfos; 171 | std::vector zModes; 172 | }; 173 | 174 | void dumpMat3(FILE* f, Mat3& dst); 175 | void writeMat3Info(FILE* f, std::ostream& out);; 176 | 177 | #endif //BMD_MAT3_H 178 | -------------------------------------------------------------------------------- /openfile.cpp: -------------------------------------------------------------------------------- 1 | #include "openfile.h" 2 | 3 | #include "common.h" 4 | 5 | #include 6 | #include 7 | #include 8 | using namespace std; 9 | 10 | #ifdef _WIN32 11 | #include //GetTempPath(), GetTempFileName() 12 | 13 | std::string getTempFileName() 14 | { 15 | char pathName[MAX_PATH], fileName[MAX_PATH]; 16 | GetTempPath(MAX_PATH, pathName); 17 | GetTempFileName(pathName, "yaz0", 0, fileName); 18 | return fileName; 19 | } 20 | 21 | #else 22 | 23 | std::string getTempFileName() 24 | { 25 | char fileName[L_tmpnam]; 26 | tmpnam(fileName); //TODO: better use mkstemp() 27 | return fileName; 28 | } 29 | 30 | #endif 31 | 32 | 33 | struct Ret 34 | { 35 | int srcPos, dstPos; 36 | }; 37 | 38 | 39 | Ret decodeYaz0(u8* src, int srcSize, u8* dst, int uncompressedSize) 40 | { 41 | Ret r = { 0, 0 }; //current read/write positions 42 | 43 | u32 validBitCount = 0; //number of valid bits left in "code" byte 44 | u8 currCodeByte; 45 | while(r.dstPos < uncompressedSize) 46 | { 47 | //read new "code" byte if the current one is used up 48 | if(validBitCount == 0) 49 | { 50 | if(r.srcPos >= srcSize) 51 | return r; 52 | currCodeByte = src[r.srcPos]; 53 | ++r.srcPos; 54 | validBitCount = 8; 55 | } 56 | 57 | if((currCodeByte & 0x80) != 0) 58 | { 59 | //straight copy 60 | if(r.srcPos >= srcSize) 61 | return r; 62 | dst[r.dstPos] = src[r.srcPos]; 63 | r.dstPos++; 64 | r.srcPos++; 65 | } 66 | else 67 | { 68 | //RLE part 69 | if(r.srcPos >= srcSize - 1) 70 | return r; 71 | u8 byte1 = src[r.srcPos]; 72 | u8 byte2 = src[r.srcPos + 1]; 73 | r.srcPos += 2; 74 | 75 | 76 | u32 dist = ((byte1 & 0xF) << 8) | byte2; 77 | u32 copySource = r.dstPos - (dist + 1); 78 | 79 | u32 numBytes = byte1 >> 4; 80 | if(numBytes == 0) 81 | { 82 | if(r.srcPos >= srcSize) 83 | return r; 84 | numBytes = src[r.srcPos] + 0x12; 85 | r.srcPos++; 86 | } 87 | else 88 | numBytes += 2; 89 | 90 | //copy run 91 | for(size_t i = 0; i < numBytes; ++i) 92 | { 93 | if(r.dstPos >= uncompressedSize) 94 | return r; 95 | dst[r.dstPos] = dst[copySource]; 96 | copySource++; 97 | r.dstPos++; 98 | } 99 | } 100 | 101 | //use next bit from "code" byte 102 | currCodeByte <<= 1; 103 | validBitCount-=1; 104 | } 105 | 106 | return r; 107 | } 108 | 109 | OpenedFile* openFile(const string& name) 110 | { 111 | FILE* f = fopen(name.c_str(), "rb"); 112 | if(f == NULL) 113 | { 114 | fprintf(stderr, "Failed to open \"%s\"\n", name.c_str()); 115 | return NULL; 116 | } 117 | 118 | OpenedFile* ret = new OpenedFile; 119 | 120 | char buff[4]; 121 | fread(buff, 1, 4, f); 122 | if(strncmp(buff, "Yaz0", 4) != 0) 123 | { 124 | fseek(f, 0, SEEK_SET); 125 | ret->f = f; 126 | return ret; //not compressed, return file directly 127 | } 128 | 129 | //yaz0-compressed file - uncompress to a temporary file, 130 | //return the uncompressed file 131 | 132 | u32 uncompressedSize; 133 | fread(&uncompressedSize, 4, 1, f); 134 | toDWORD(uncompressedSize); 135 | 136 | fseek(f, 0, SEEK_END); 137 | long compressedSize = ftell(f) - 16; //16 byte header 138 | fseek(f, 16, SEEK_SET); //seek to start of data 139 | 140 | vector srcData(compressedSize), dstData(uncompressedSize); 141 | fread(&srcData[0], 1, compressedSize, f); 142 | fclose(f); 143 | Ret r = decodeYaz0(&srcData[0], compressedSize, 144 | &dstData[0], uncompressedSize); 145 | 146 | 147 | //write decompressed data to a temporary file and 148 | //return handle to this file 149 | string tempFileName = getTempFileName(); 150 | 151 | f = fopen(tempFileName.c_str(), "wb+"); 152 | if(f == NULL) 153 | { 154 | delete ret; 155 | return NULL; 156 | } 157 | fwrite(&dstData[0], 1, r.dstPos, f); 158 | fseek(f, 0, SEEK_SET); 159 | 160 | ret->f = f; 161 | ret->tempFileName = tempFileName; 162 | return ret; 163 | } 164 | 165 | void closeFile(OpenedFile* f) 166 | { 167 | if(f == NULL) 168 | return; 169 | 170 | fclose(f->f); 171 | if(f->tempFileName != "") //delete temporary file 172 | remove(f->tempFileName.c_str()); 173 | delete f; 174 | } 175 | -------------------------------------------------------------------------------- /common/gccommon.h: -------------------------------------------------------------------------------- 1 | #ifndef GCCOMMON_H 2 | #define GCCOMMON_H GCCOMMON_H 3 | 4 | /* 5 | * On my Intel OS X, BIG_ENDIAN is defined for some reason when 6 | * is included (which it is by for example), so don't check for it. 7 | */ 8 | 9 | #if defined __BIG_ENDIAN__ || (defined __APPLE__ && defined __POWERPC__) 10 | #define GC_BIG_ENDIAN 11 | #endif 12 | 13 | //sanity check 14 | #if (defined __LITTLE_ENDIAN__ || defined LITTLE_ENDIAN) && defined GC_BIG_ENDIAN 15 | #error Unable to determine endianness 16 | #endif 17 | 18 | typedef unsigned char u8; 19 | typedef unsigned short u16; 20 | typedef unsigned int u32; 21 | #ifdef _MSC_VER 22 | typedef unsigned __int64 u64; 23 | #else 24 | typedef unsigned long long u64; 25 | #endif 26 | 27 | typedef signed char s8; 28 | typedef signed short s16; 29 | typedef signed int s32; 30 | #ifdef _MSC_VER 31 | typedef signed __int64 s64; 32 | #else 33 | typedef signed long long s64; 34 | #endif 35 | 36 | typedef float f32; 37 | 38 | inline void toWORD(u16& w) 39 | { 40 | #ifndef GC_BIG_ENDIAN 41 | u8 w1 = w & 0xFF; 42 | u8 w2 = w >> 8; 43 | w = (w1 << 8) | w2; 44 | #endif 45 | } 46 | 47 | inline void toSHORT(s16& w) 48 | { 49 | toWORD(*(u16*)&w); 50 | } 51 | 52 | inline void toDWORD(u32& d) 53 | { 54 | #ifndef GC_BIG_ENDIAN 55 | u8 w1 = d & 0xFF; 56 | u8 w2 = (d >> 8) & 0xFF; 57 | u8 w3 = (d >> 16) & 0xFF; 58 | u8 w4 = d >> 24; 59 | d = (w1 << 24) | (w2 << 16) | (w3 << 8) | w4; 60 | #endif 61 | } 62 | 63 | inline void toFLOAT(f32& f) 64 | { toDWORD(*(u32*)&f); } 65 | 66 | 67 | inline u16 aWORD(u16 w) 68 | { 69 | toWORD(w); return w; 70 | } 71 | 72 | inline s16 aSHORT(s16 s) 73 | { 74 | return aWORD(*(u16*)&s); 75 | } 76 | 77 | inline u32 aDWORD(u32 d) 78 | { 79 | toDWORD(d); return d; 80 | } 81 | 82 | inline f32 aFLOAT(f32 f) 83 | { 84 | toFLOAT(f); return f; 85 | } 86 | 87 | 88 | inline u16 memWORD(const u8* where) 89 | { 90 | return (where[0] << 8) | where[1]; 91 | } 92 | 93 | inline u32 memDWORD(const u8* where) 94 | { 95 | return (where[0] << 24) | (where[1] << 16) | (where[2] << 8) | where[3]; 96 | } 97 | 98 | 99 | #include 100 | 101 | inline void readWORD(FILE* f, u16& v) 102 | { 103 | int len; 104 | len = fread(&v, 2, 1, f); 105 | len=len; 106 | toWORD(v); 107 | } 108 | 109 | inline void readSHORT(FILE* f, s16& v) 110 | { 111 | int len; 112 | len = fread(&v, 2, 1, f); 113 | len=len; 114 | toSHORT(v); 115 | } 116 | 117 | inline void readDWORD(FILE* f, u32& v) 118 | { 119 | int len; 120 | len = fread(&v, 4, 1, f); 121 | len=len; 122 | toDWORD(v); 123 | } 124 | 125 | inline void readFLOAT(FILE* f, f32& v) 126 | { 127 | int len; 128 | len = fread(&v, 4, 1, f); 129 | len=len; 130 | toFLOAT(v); 131 | } 132 | 133 | 134 | 135 | inline void toWORD_le(u16& w) 136 | { 137 | #ifdef GC_BIG_ENDIAN 138 | u8 w1 = w & 0xFF; 139 | u8 w2 = w >> 8; 140 | w = (w1 << 8) | w2; 141 | #endif 142 | } 143 | 144 | inline void toSHORT_le(s16& w) 145 | { 146 | toWORD_le(*(u16*)&w); 147 | } 148 | 149 | inline void toDWORD_le(u32& d) 150 | { 151 | #ifdef GC_BIG_ENDIAN 152 | u8 w1 = d & 0xFF; 153 | u8 w2 = (d >> 8) & 0xFF; 154 | u8 w3 = (d >> 16) & 0xFF; 155 | u8 w4 = d >> 24; 156 | d = (w1 << 24) | (w2 << 16) | (w3 << 8) | w4; 157 | #endif 158 | } 159 | 160 | inline void toFLOAT_le(f32& f) 161 | { toDWORD_le(*(u32*)&f); } 162 | 163 | 164 | inline u16 aWORD_le(u16 w) 165 | { 166 | toWORD_le(w); return w; 167 | } 168 | 169 | inline s16 aSHORT_le(s16 s) 170 | { 171 | return aWORD_le(*(u16*)&s); 172 | } 173 | 174 | inline u32 aDWORD_le(u32 d) 175 | { 176 | toDWORD_le(d); return d; 177 | } 178 | 179 | inline f32 aFLOAT_le(f32 f) 180 | { 181 | toFLOAT_le(f); return f; 182 | } 183 | 184 | inline u16 memWORD_le(const u8* where) 185 | { 186 | return where[0] | (where[1] << 8); 187 | } 188 | 189 | inline u32 memDWORD_le(const u8* where) 190 | { 191 | return where[0] | (where[1] << 8) | (where[2] << 16) | (where[3] << 24); 192 | } 193 | 194 | 195 | #include 196 | 197 | inline void writeWORD_le(FILE* f, u16 v) 198 | { 199 | toWORD_le(v); 200 | fwrite(&v, 2, 1, f); 201 | } 202 | 203 | inline void writeSHORT_le(FILE* f, s16 v) 204 | { 205 | toSHORT_le(v); 206 | fwrite(&v, 2, 1, f); 207 | } 208 | 209 | inline void writeDWORD_le(FILE* f, u32 v) 210 | { 211 | toDWORD_le(v); 212 | fwrite(&v, 4, 1, f); 213 | } 214 | 215 | inline void writeFLOAT_le(FILE* f, f32 v) 216 | { 217 | toFLOAT_le(v); 218 | fwrite(&v, 4, 1, f); 219 | } 220 | 221 | 222 | #endif //GCCOMMON_H 223 | -------------------------------------------------------------------------------- /Vector3.inc: -------------------------------------------------------------------------------- 1 | #include "Vector3.h" 2 | 3 | #include 4 | #include 5 | 6 | template 7 | Vector3::Vector3() 8 | { 9 | } 10 | 11 | template 12 | Vector3::Vector3(const Num& x, const Num& y, const Num& z) 13 | { 14 | v[0] = x; 15 | v[1] = y; 16 | v[2] = z; 17 | } 18 | 19 | template 20 | Vector3::Vector3(const Num iv[]) 21 | { 22 | v[0] = iv[0]; 23 | v[1] = iv[1]; 24 | v[2] = iv[2]; 25 | } 26 | 27 | template 28 | Num& Vector3::operator[](int i) 29 | { 30 | return v[i]; 31 | } 32 | 33 | template 34 | const Num& Vector3::operator[](int i) const 35 | { 36 | return v[i]; 37 | } 38 | 39 | template 40 | Num Vector3::getLength() const 41 | { 42 | return (Num)sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]); 43 | } 44 | 45 | template 46 | Num Vector3::getLengthSquared() const 47 | { 48 | return v[0]*v[0] + v[1]*v[1] + v[2]*v[2]; 49 | } 50 | 51 | template 52 | Vector3 Vector3::normalized() const 53 | { 54 | Num invLen = ((Num)1)/getLength(); 55 | return Vector3(v[0]*invLen, v[1]*invLen, v[2]*invLen); 56 | } 57 | 58 | template 59 | void Vector3::normalize() 60 | { 61 | Num invLen = ((Num)1)/getLength(); 62 | v[0] *= invLen; 63 | v[1] *= invLen; 64 | v[2] *= invLen; 65 | } 66 | 67 | template 68 | Vector3 Vector3::cross(const Vector3& b) const 69 | { 70 | return Vector3(v[1]*b.v[2] - v[2]*b.v[1], v[2]*b.v[0] - v[0]*b.v[2], v[0]*b.v[1] - v[1]*b.v[0]); 71 | } 72 | 73 | template 74 | Num Vector3::dot(const Vector3& b) const 75 | { 76 | return v[0]*b.v[0] + v[1]*b.v[1] + v[2]*b.v[2]; 77 | } 78 | 79 | template 80 | void Vector3::setX(const Num& x) 81 | { 82 | v[0] = x; 83 | } 84 | 85 | template 86 | void Vector3::setY(const Num& y) 87 | { 88 | v[1] = y; 89 | } 90 | 91 | template 92 | void Vector3::setZ(const Num& z) 93 | { 94 | v[2] = z; 95 | } 96 | 97 | template 98 | void Vector3::setXYZ(const Num& x, const Num& y, const Num& z) 99 | { 100 | v[0] = x; 101 | v[1] = y; 102 | v[2] = z; 103 | } 104 | 105 | template 106 | Num Vector3::getX() const 107 | { 108 | return v[0]; 109 | } 110 | 111 | template 112 | Num Vector3::getY() const 113 | { 114 | return v[1]; 115 | } 116 | 117 | template 118 | Num Vector3::getZ() const 119 | { 120 | return v[2]; 121 | } 122 | 123 | template 124 | Num& Vector3::x() 125 | { 126 | return v[0]; 127 | } 128 | 129 | template 130 | Num& Vector3::y() 131 | { 132 | return v[1]; 133 | } 134 | 135 | template 136 | Num& Vector3::z() 137 | { 138 | return v[2]; 139 | } 140 | 141 | template 142 | const Num& Vector3::x() const 143 | { 144 | return v[0]; 145 | } 146 | 147 | template 148 | const Num& Vector3::y() const 149 | { 150 | return v[1]; 151 | } 152 | 153 | template 154 | const Num& Vector3::z() const 155 | { 156 | return v[2]; 157 | } 158 | 159 | template 160 | Vector3 Vector3::operator+(const Vector3& b) const 161 | { 162 | return Vector3(v[0] + b.v[0], v[1] + b.v[1], v[2] + b.v[2]); 163 | } 164 | 165 | template 166 | Vector3 Vector3::operator-(const Vector3& b) const 167 | { 168 | return Vector3(v[0] - b.v[0], v[1] - b.v[1], v[2] - b.v[2]); 169 | } 170 | 171 | template 172 | Vector3 Vector3::operator-() const 173 | { 174 | return Vector3(-v[0], -v[1], -v[2]); 175 | } 176 | 177 | template 178 | Vector3 Vector3::operator*(const Num& b) const 179 | { 180 | return Vector3(v[0]*b, v[1]*b, v[2]*b); 181 | } 182 | 183 | template 184 | Vector3 Vector3::operator/(const Num& b) const 185 | { 186 | Num i = ((Num)1)/b; 187 | return Vector3(v[0]*i, v[1]*i, v[2]*i); 188 | } 189 | 190 | template 191 | Vector3 operator*(const T& a, const Vector3& b) 192 | { 193 | return b*a; 194 | } 195 | 196 | template 197 | Vector3& Vector3::operator +=(const Vector3& b) 198 | { 199 | v[0] += b.v[0]; 200 | v[1] += b.v[1]; 201 | v[2] += b.v[2]; 202 | 203 | return *this; 204 | } 205 | 206 | template 207 | Vector3& Vector3::operator -=(const Vector3& b) 208 | { 209 | v[0] -= b.v[0]; 210 | v[1] -= b.v[1]; 211 | v[2] -= b.v[2]; 212 | 213 | return *this; 214 | } 215 | 216 | template 217 | Vector3& Vector3::operator *=(const Num& s) 218 | { 219 | v[0] *= s; 220 | v[1] *= s; 221 | v[2] *= s; 222 | 223 | return *this; 224 | } 225 | 226 | template 227 | Vector3& Vector3::operator /=(const Num& s) 228 | { 229 | Num i = ((Num)1)/s; 230 | v[0] *= i; 231 | v[1] *= i; 232 | v[2] *= i; 233 | 234 | return *this; 235 | } 236 | 237 | template 238 | Vector3::operator Num*() 239 | { 240 | return &v[0]; 241 | } 242 | 243 | template 244 | Vector3::operator const Num*() const 245 | { 246 | return &v[0]; 247 | } 248 | 249 | -------------------------------------------------------------------------------- /ui.cpp: -------------------------------------------------------------------------------- 1 | #include "ui.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "addons/export3ds.h" 7 | #include "addons/exportTexture.h" 8 | 9 | #include "drawbmd.h" 10 | #include "oglblock.h" 11 | 12 | #include 13 | 14 | #ifdef _WIN32 15 | 16 | #include 17 | #undef min 18 | #undef max 19 | 20 | 21 | //TODO: this is ugly, do this somehow else 22 | void loadFile(const std::string& name, bool merge = false); 23 | extern HWND getHWnd(); //in simple_gl.cpp 24 | 25 | #endif 26 | 27 | #include "main.h" 28 | 29 | #ifdef _WIN32 30 | 31 | void unimplemented(const std::string& str) 32 | { 33 | MessageBox(getHWnd(), str.c_str(), "Unimplemeted:", MB_OK); 34 | } 35 | 36 | std::string getOpenFilename(const std::string& title = "Open", 37 | const char* filter = "All Files\0*.*\0") 38 | { 39 | static bool s_isInitialized = false; 40 | static OPENFILENAME s_openFileName; 41 | static char buffer[MAX_PATH]; 42 | if(!s_isInitialized) 43 | { 44 | ZeroMemory(&s_openFileName, sizeof(s_openFileName)); 45 | s_openFileName.lStructSize = sizeof(s_openFileName); 46 | s_openFileName.hwndOwner = getHWnd(); 47 | s_openFileName.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_EXPLORER; 48 | s_openFileName.lpstrFile = buffer; 49 | s_openFileName.nMaxFile = MAX_PATH; 50 | s_openFileName.lpstrDefExt = "XXX"; //this way extensions are appended 51 | s_isInitialized = true; 52 | } 53 | 54 | s_openFileName.lpstrTitle = title.c_str(); 55 | s_openFileName.lpstrFilter = filter; 56 | 57 | if(GetOpenFileName(&s_openFileName)) 58 | { 59 | return s_openFileName.lpstrFile; 60 | } 61 | else 62 | return ""; 63 | } 64 | 65 | std::string getSaveFilename(const std::string& title = "Save", 66 | const char* filter = "All Files\0*.*\0") 67 | { 68 | static bool s_isInitialized = false; 69 | static OPENFILENAME s_saveFileName; 70 | static char buffer[MAX_PATH]; 71 | if(!s_isInitialized) 72 | { 73 | ZeroMemory(&s_saveFileName, sizeof(s_saveFileName)); 74 | s_saveFileName.lStructSize = sizeof(s_saveFileName); 75 | s_saveFileName.hwndOwner = getHWnd(); 76 | s_saveFileName.Flags = OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY | OFN_EXPLORER; 77 | s_saveFileName.lpstrFile = buffer; 78 | s_saveFileName.nMaxFile = MAX_PATH; 79 | s_saveFileName.lpstrDefExt = "XXX"; //this way extensions are appended 80 | s_isInitialized = true; 81 | } 82 | 83 | s_saveFileName.lpstrTitle = title.c_str(); 84 | s_saveFileName.lpstrFilter = filter; 85 | 86 | if(GetSaveFileName(&s_saveFileName)) 87 | { 88 | return s_saveFileName.lpstrFile; 89 | } 90 | else 91 | return ""; 92 | } 93 | 94 | void menuFileOpenModel() 95 | { 96 | std::string name = getOpenFilename("Open model", 97 | "BModel (*.bmd, *.bdl)\0*.bmd;*.bdl\0"); 98 | if(name != "") 99 | loadFile(name); 100 | } 101 | 102 | void menuFileMergeModel() 103 | { 104 | std::string name = getOpenFilename("Merge model", 105 | "BModel (*.bmd, *.bdl)\0*.bmd;*.bdl\0"); 106 | if(name != "") 107 | loadFile(name, true); 108 | } 109 | 110 | void menuFileOpenAnimation() 111 | { 112 | std::string name = getOpenFilename("Open animation", 113 | "All animations (*.bck, *.btp)\0*.bck;*.btp\0" 114 | "Bone animation (*.bck)\0*.bck\0" 115 | "Texture animation (*.btp)\0*.btp\0"); 116 | if(name != "") 117 | loadFile(name); 118 | } 119 | 120 | void menuFileExportModel() 121 | { 122 | std::string name = getSaveFilename("Export model", "3ds files\0*.3ds\0"); 123 | if(name != "") 124 | menuFileExportModel(name); 125 | } 126 | 127 | void menuFileExportTextures() 128 | { 129 | std::string filename = getSaveFilename("Enter textures basename", 130 | "dds files\0*.dds\0tga files\0*.tga\0"); 131 | if(filename != "") 132 | menuFileExportTextures(filename); 133 | } 134 | 135 | #endif 136 | 137 | void menuFileExportModel(const std::string& filename) 138 | { 139 | if(g_models.back().bmd != NULL) 140 | exportAs3ds(*g_models.back().bmd, filename); 141 | else 142 | warn("Failed to export %s, no model loaded", filename.c_str()); 143 | } 144 | 145 | void menuFileExportTextures(const std::string& filename) 146 | { 147 | if(g_models.back().bmd != NULL) 148 | { 149 | std::string name, extension; 150 | splitName(filename, name, extension); 151 | std::transform(extension.begin(), extension.end(), extension.begin(), 152 | tolower); 153 | 154 | if(extension == "dds") 155 | exportTextures(DDS, g_models.back().bmd->tex1, name); 156 | else if(extension == "tga") 157 | exportTextures(TGA, g_models.back().bmd->tex1, name); 158 | else 159 | warn("Failed to export textures, unknown extension \"%s\"", 160 | extension.c_str()); 161 | 162 | } 163 | else 164 | warn("Failed to export textures to \"%s\", no model loaded", 165 | filename.c_str()); 166 | } 167 | 168 | void menuDebugSectioninfo() 169 | { 170 | if(g_models.back().oglBlock != NULL) //model loaded? 171 | { 172 | FILE* f = fopen(g_models.back().bmdFileName.c_str(), "rb"); 173 | 174 | if(f != NULL) 175 | { 176 | std::ostringstream out; 177 | writeBmdInfo(f, out); 178 | fclose(f); 179 | 180 | std::cout << out.str(); 181 | } 182 | } 183 | } 184 | 185 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include "simple_gl.h" 2 | #include "simple_gl_common.h" 3 | 4 | #include "openfile.h" 5 | #include "bmdread.h" 6 | #include "drawbmd.h" 7 | #include "parameters.h" 8 | #include "camera.h" 9 | 10 | #include "oglblock.h" 11 | 12 | #include "addons/bck.h" 13 | #include "addons/btp.h" 14 | #include "addons/export3ds.h" 15 | #include "addons/exportTexture.h" 16 | 17 | #include //va_list 18 | 19 | using namespace std; 20 | 21 | extern GLuint g_fontBase; 22 | 23 | static char * 24 | _vasprintf (const char *format, va_list args) 25 | { 26 | int size = vsnprintf(NULL, 0, format, args); 27 | char *buf = (char *) malloc(size + 1); 28 | buf[size - 1] = 0; 29 | vsprintf(buf, format, args); 30 | return buf; 31 | } 32 | 33 | void log(const char* msg, ...) 34 | { 35 | va_list argList; 36 | va_start(argList, msg); 37 | char *str = _vasprintf(msg, argList); 38 | va_end(argList); 39 | fprintf(stderr, "log: %s\n", str); 40 | free(str); 41 | } 42 | 43 | void warn(const char* msg, ...) 44 | { 45 | va_list argList; 46 | va_start(argList, msg); 47 | char *str = _vasprintf(msg, argList); 48 | va_end(argList); 49 | fprintf(stderr, "warn: %s\n", str); 50 | free(str); 51 | } 52 | 53 | string getExtension(const string& name) 54 | { 55 | string::size_type pos = name.rfind("."); 56 | if(pos == string::npos) 57 | return ""; 58 | 59 | return name.substr(pos + 1); 60 | } 61 | 62 | std::vector g_models(1); 63 | 64 | void freeModel(Model& m) 65 | { 66 | freeOglBlock(m.oglBlock); 67 | 68 | if(m.btp != NULL) 69 | { 70 | delete m.btp; 71 | m.btp = NULL; 72 | } 73 | 74 | if(m.bck != NULL) 75 | { 76 | delete m.bck; 77 | m.bck = NULL; 78 | } 79 | 80 | if(m.bmd != NULL) 81 | { 82 | delete m.bmd; 83 | m.bmd = NULL; 84 | } 85 | 86 | m.sceneGraph.children.clear(); 87 | } 88 | 89 | void loadFile(const string& name, bool merge = false) 90 | { 91 | printf("loading %s", name.c_str()); 92 | 93 | OpenedFile* f = openFile(name); 94 | if(f == NULL) 95 | return; 96 | 97 | Model& m = g_models.back(); 98 | 99 | //for now, recognize file content by extension 100 | string extension = getExtension(name); 101 | if(extension == "bmd" || extension == "bdl") 102 | { 103 | if(!merge) 104 | { 105 | for(size_t i = 0; i < g_models.size(); ++i) 106 | freeModel(g_models[i]); 107 | g_models.resize(1); 108 | } 109 | else 110 | g_models.push_back(Model()); 111 | 112 | freeModel(g_models.back()); 113 | g_models.back().bmd = loadBmd(f->f); 114 | 115 | if(g_models.back().bmd != NULL) 116 | { 117 | g_models.back().bmdFileName = name; 118 | 119 | uploadImagesToGl(g_models.back().bmd->tex1); 120 | 121 | g_models.back().oglBlock = createOglBlock(g_models.back().bmd->mat3, name); 122 | g_models.back().sceneGraph.children.clear(); 123 | buildSceneGraph(g_models.back().bmd->inf1, g_models.back().sceneGraph); 124 | 125 | //hack: this computes the matrices for the first time. 126 | //without this call, the screen contains garbage for 127 | //a short time, because right now, the matrices from 128 | //the last frame are used to draw the next frame 129 | //(it's even uglier: a mixture of this frame's and 130 | //last frame's matrices are used right now -> TODO) 131 | drawBmd(g_models.back(), g_models.back().sceneGraph); 132 | } 133 | } 134 | else if(extension == "bck") 135 | { 136 | if(m.bck != NULL) delete m.bck; 137 | m.bck = readBck(f->f); 138 | } 139 | else if(extension == "btp") 140 | { 141 | if(m.btp != NULL) delete m.btp; 142 | m.btp = readBtp(f->f); 143 | } 144 | 145 | closeFile(f); 146 | } 147 | 148 | bool init() 149 | { 150 | glEnable(GL_LIGHT0); 151 | glEnable(GL_RESCALE_NORMAL); 152 | glEnable(GL_COLOR_MATERIAL); 153 | glEnable(GL_DEPTH_TEST); 154 | glEnable(GL_CULL_FACE); 155 | glFrontFace(GL_CW); 156 | glClearColor(77/255.f, 50/255.f, 153/255.f, 1.f); 157 | 158 | walkBack(10*128); 159 | 160 | for(int i = 0; i < getParameterCount(); ++i) 161 | loadFile(getParameter(i)); 162 | 163 | return true; 164 | } 165 | 166 | float g_time = 0.f; 167 | 168 | void draw() 169 | { 170 | prepareCamera(); 171 | 172 | glDepthMask(GL_TRUE); //if this was set to false, depth is not cleared... 173 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 174 | 175 | GLenum error; 176 | while((error = glGetError()) != GL_NO_ERROR) 177 | fprintf(stderr, "GL error: %s\n", gluErrorString(error)); 178 | 179 | for(size_t i = 0; i < g_models.size(); ++i) 180 | { 181 | Model& m = g_models[i]; 182 | 183 | //update animations: 184 | g_time += getLastFrameSeconds(); 185 | float animTime = 256*g_time; //TODO: find the right factor for this 186 | if(m.bmd != NULL) 187 | { 188 | if(m.bck != NULL) 189 | { 190 | if(m.bck->anims.size() != m.bmd->jnt1.frames.size()) 191 | warn("number of joints in anim (%d) doesn't match number of joints of model (%d)", 192 | m.bck->anims.size(), m.bmd->jnt1.frames.size()); 193 | else 194 | animate(*m.bck, m.bmd->jnt1, animTime); 195 | } 196 | 197 | if(m.btp != NULL) 198 | animate(*m.btp, m.bmd->mat3, animTime); 199 | } 200 | 201 | if(m.bmd != NULL) 202 | drawBmd(m, m.sceneGraph); 203 | } 204 | 205 | flush(); 206 | } 207 | 208 | void exit() 209 | { 210 | for(size_t i = 0; i < g_models.size(); ++i) 211 | freeModel(g_models[i]); 212 | g_models.clear(); 213 | } 214 | -------------------------------------------------------------------------------- /addons/btp.cpp: -------------------------------------------------------------------------------- 1 | #include "btp.h" 2 | #include //strncmp 3 | 4 | #include //copy 5 | 6 | using namespace std; 7 | 8 | namespace btp 9 | { 10 | 11 | struct TptHeader 12 | { 13 | char tag[4]; //'TPT1' 14 | u32 sizeOfSection; 15 | u8 unk; //loop type???? 16 | u8 pad; 17 | u16 unk2; //(shortsPerMaterialAnim - no...sometimes 1 less (?)) 18 | u16 numMaterialAnims; 19 | u16 numShorts; //(should be product of previous shorts) 20 | 21 | u32 offsetToMatAnims; //- for each materialAnim: u16 numSorts, u16 firstShort, u32 unk 22 | u32 offsetToShorts; // (shorts are texture indices) 23 | u32 offsetToIndexTable; //stores for every material to which mat3 index it belongs 24 | u32 offsetToStringTable; 25 | }; 26 | 27 | struct MatAnim 28 | { 29 | u16 count; 30 | u16 firstIndex; 31 | u32 unknown; 32 | }; 33 | 34 | }; 35 | 36 | void readTptHeader(FILE* f, btp::TptHeader& h) 37 | { 38 | fread(h.tag, 1, 4, f); 39 | readDWORD(f, h.sizeOfSection); 40 | fread(&h.unk, 1, 1, f); 41 | fread(&h.pad, 1, 1, f); 42 | readWORD(f, h.unk2); 43 | readWORD(f, h.numMaterialAnims); 44 | readWORD(f, h.numShorts); 45 | readDWORD(f, h.offsetToMatAnims); 46 | readDWORD(f, h.offsetToShorts); 47 | readDWORD(f, h.offsetToIndexTable); 48 | readDWORD(f, h.offsetToStringTable); 49 | } 50 | 51 | void readMatAnim(FILE* f, btp::MatAnim& anim) 52 | { 53 | readWORD(f, anim.count); 54 | readWORD(f, anim.firstIndex); 55 | readDWORD(f, anim.unknown); 56 | } 57 | 58 | void dumpTpt1(FILE* f, Btp& btp) 59 | { 60 | long tpt1Offset = ftell(f); 61 | size_t i; 62 | 63 | //read header 64 | btp::TptHeader h; 65 | readTptHeader(f, h); 66 | 67 | //read stringtable 68 | vector stringtable; 69 | readStringtable(tpt1Offset + h.offsetToStringTable, f, stringtable); 70 | if(stringtable.size() != h.numMaterialAnims) 71 | warn("dumpTpt1: number of strings (%d) doesn't match number of " 72 | "animated materials (%d)", stringtable.size(), h.numMaterialAnims); 73 | 74 | //read matAnimIndexToMat3Index table 75 | vector matAnimIndexToMat3Index(h.numMaterialAnims); 76 | fseek(f, tpt1Offset + h.offsetToIndexTable, SEEK_SET); 77 | for(i = 0; i < h.numMaterialAnims; ++i) 78 | readWORD(f, matAnimIndexToMat3Index[i]); 79 | 80 | //read shorts table 81 | vector shorts(h.numShorts); 82 | fseek(f, tpt1Offset + h.offsetToShorts, SEEK_SET); 83 | for(i = 0; i < h.numShorts; ++i) 84 | readWORD(f, shorts[i]); 85 | 86 | //read animations 87 | btp.anims.resize(h.numMaterialAnims); 88 | fseek(f, tpt1Offset + h.offsetToMatAnims, SEEK_SET); 89 | for(i = 0; i < h.numMaterialAnims; ++i) 90 | { 91 | btp::MatAnim anim; 92 | readMatAnim(f, anim); 93 | 94 | if(anim.unknown != 0x00ffffff) 95 | warn("btp: %d instead of 0x00ffffff for mat anim nr %d", anim.unknown, i); 96 | 97 | if(i < stringtable.size()) 98 | btp.anims[i].name = stringtable[i]; 99 | btp.anims[i].indexToMat3Table = matAnimIndexToMat3Index[i]; 100 | btp.anims[i].indices.resize(anim.count); 101 | copy(shorts.begin() + anim.firstIndex, shorts.begin() + anim.firstIndex + anim.count, 102 | btp.anims[i].indices.begin()); 103 | } 104 | } 105 | 106 | Btp* readBtp(FILE* f) 107 | { 108 | Btp* ret = new Btp; 109 | 110 | //skip file header 111 | fseek(f, 0x20, SEEK_SET); 112 | 113 | u32 size = 0; 114 | char tag[4]; 115 | int t; 116 | 117 | do 118 | { 119 | fseek(f, size, SEEK_CUR); 120 | t = ftell(f); 121 | 122 | fread(tag, 1, 4, f); 123 | fread(&size, 4, 1, f); 124 | toDWORD(size); 125 | if(size < 8) size = 8; //prevent endless loop on corrupt data 126 | 127 | if(feof(f)) 128 | break; 129 | fseek(f, t, SEEK_SET); 130 | 131 | if(strncmp(tag, "TPT1", 4) == 0) 132 | dumpTpt1(f, *ret); 133 | else 134 | warn("readBtp(): Unsupported section \'%c%c%c%c\'", 135 | tag[0], tag[1], tag[2], tag[3]); 136 | 137 | fseek(f, t, SEEK_SET); 138 | } while(!feof(f)); 139 | 140 | return ret; 141 | } 142 | 143 | ////////////////////////////////////////////////////////////////////// 144 | 145 | void animate(Btp& btp, Mat3& mat3, float ftime) 146 | { 147 | unsigned int time = (unsigned int)(ftime + .5f); 148 | for(size_t i = 0; i < btp.anims.size(); ++i) 149 | { 150 | TextureAnimation& curr = btp.anims[i]; 151 | 152 | unsigned int index = time % curr.indices.size(); 153 | 154 | //mat3.materials[curr.indexToMat3Table].texStages[0] = curr.indices[index]; 155 | //mat3.texStageIndexToTextureIndex[curr.indices[index]] = curr.indices[index]; 156 | //mat3.texStageIndexToTextureIndex[curr.indexToMat3Table] = curr.indices[index]; 157 | 158 | //works for link and mario, not for peach and yoshi (this line _is_ probably right, 159 | //but missing on more lookup) 160 | //mat3.texStageIndexToTextureIndex 161 | // [mat3.materials[mat3.indexToMatIndex[curr.indexToMat3Table]].texStages[0]] = curr.indices[index]; 162 | 163 | //this is hacky but works for all models - don't use index from file but 164 | //search an index which has the same name. TODO: do this correctly later, 165 | //it's good enough for now. 166 | size_t d; 167 | for(d = 0; d < mat3.indexToMatIndex.size(); ++d) 168 | if(mat3.stringtable[d] == curr.name) 169 | break; 170 | mat3.texStageIndexToTextureIndex 171 | [mat3.materials[mat3.indexToMatIndex[d]].texStages[0]] = 172 | curr.indices[index]; 173 | 174 | //drawText("found index %d, real/expected index %d: %s", curr.indexToMat3Table, d, curr.name.c_str()); 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /jnt1.cpp: -------------------------------------------------------------------------------- 1 | #include "jnt1.h" 2 | 3 | #include 4 | using namespace std; 5 | 6 | namespace bmd 7 | { 8 | 9 | struct Jnt1Header 10 | { 11 | char tag[4]; //'JNT1' 12 | u32 sizeOfSection; 13 | u16 count; //number of joints 14 | u16 pad; //padding u16 (?) 15 | 16 | u32 jntEntryOffset; //joints are stored at this place 17 | //offset relative to Jnt1Header start 18 | 19 | u32 unknownOffset; //there are count u16's stored at this point, 20 | //always the numbers 0 to count - 1 (in that order). 21 | //perhaps an index-to-stringtable-index map? 22 | //offset relative to Jnt1Header start 23 | u32 stringTableOffset; //names of joints 24 | }; 25 | 26 | 27 | struct JntEntry 28 | { 29 | u16 unknown; //no idea how this works...always 0, 1 or 2 30 | //"matrix type" according to yaz0r - whatever this means ;-) 31 | //if this is 1 or 2, unknown2 and bounding box is 0.f 32 | //seems to be always 0 if a joint has direct non-joint children 33 | //(it's even "iff" - only joints with non-joint children have 0?) 34 | 35 | u8 unknown3; //0 or 1 (0xff in one single file) (inherit parent scale???) 36 | 37 | u8 pad; //always 0xff 38 | 39 | f32 sx, sy, sz; //scale 40 | s16 rx, ry, rz; //-32768 = -180 deg, 32768 = 180 deg 41 | u16 pad2; //always 0xffff 42 | f32 tx, ty, tz; //translation 43 | 44 | f32 unknown2; 45 | f32 bbMin[3]; //bounding box (?) 46 | f32 bbMax[3]; //bounding box (?) 47 | }; 48 | 49 | }; 50 | 51 | void readJnt1Header(FILE* f, bmd::Jnt1Header& h) 52 | { 53 | fread(h.tag, 1, 4, f); 54 | readDWORD(f, h.sizeOfSection); 55 | readWORD(f, h.count); 56 | readWORD(f, h.pad); 57 | readDWORD(f, h.jntEntryOffset); 58 | readDWORD(f, h.unknownOffset); 59 | readDWORD(f, h.stringTableOffset); 60 | } 61 | 62 | void readJnt1Entry(FILE* f, bmd::JntEntry& e) 63 | { 64 | readWORD(f, e.unknown); 65 | fread(&e.unknown3, 1, 1, f); 66 | fread(&e.pad, 1, 1, f); 67 | 68 | readFLOAT(f, e.sx); readFLOAT(f, e.sy); readFLOAT(f, e.sz); 69 | readSHORT(f, e.rx); readSHORT(f, e.ry); readSHORT(f, e.rz); 70 | readWORD(f, e.pad2); 71 | readFLOAT(f, e.tx); readFLOAT(f, e.ty); readFLOAT(f, e.tz); 72 | readFLOAT(f, e.unknown2); 73 | 74 | int j; 75 | for(j = 0; j < 3; ++j) 76 | readFLOAT(f, e.bbMin[j]); 77 | for(j = 0; j < 3; ++j) 78 | readFLOAT(f, e.bbMax[j]); 79 | } 80 | 81 | void dumpJnt1(FILE* f, Jnt1& dst) 82 | { 83 | int jnt1Offset = ftell(f); 84 | 85 | //read header 86 | bmd::Jnt1Header h; 87 | readJnt1Header(f, h); 88 | 89 | //read stringtable 90 | vector stringtable; 91 | readStringtable(jnt1Offset + h.stringTableOffset, f, stringtable); 92 | 93 | if(stringtable.size() != h.count) 94 | warn("jnt1: number of strings doesn't match number of joints"); 95 | 96 | //read joints 97 | fseek(f, jnt1Offset + h.jntEntryOffset, SEEK_SET); 98 | 99 | dst.frames.resize(h.count); 100 | dst.matrices.resize(h.count); 101 | dst.isMatrixValid.resize(h.count); 102 | fill_n(dst.isMatrixValid.begin(), h.count, false); 103 | for(size_t i = 0; i < h.count; ++i) 104 | { 105 | bmd::JntEntry e; 106 | readJnt1Entry(f, e); 107 | 108 | Frame& f = dst.frames[i]; 109 | f.sx = e.sx; 110 | f.sy = e.sy; 111 | f.sz = e.sz; 112 | f.rx = e.rx/32768.f*180; 113 | f.ry = e.ry/32768.f*180; 114 | f.rz = e.rz/32768.f*180; 115 | f.t.setXYZ(e.tx, e.ty, e.tz); 116 | if(i < stringtable.size()) //should always be true 117 | f.name = stringtable[i]; 118 | 119 | f.unknown = e.unknown; 120 | f.unknown3 = e.unknown3; 121 | f.bbMin.setXYZ(e.bbMin[0], e.bbMin[1], e.bbMin[2]); 122 | f.bbMax.setXYZ(e.bbMax[0], e.bbMax[1], e.bbMax[2]); 123 | } 124 | } 125 | 126 | void writeJnt1Info(FILE* f, ostream& out) 127 | { 128 | out << string(50, '/') << endl 129 | << "//Jnt1 section" << endl 130 | << string(50, '/') << endl << endl; 131 | 132 | int jnt1Offset = ftell(f), i; 133 | 134 | //read jnt1 header 135 | bmd::Jnt1Header h; 136 | readJnt1Header(f, h); 137 | 138 | out << hex << "pad: " << h.pad << endl << endl; 139 | 140 | //dump stringtable 141 | writeStringtable(out, f, jnt1Offset + h.stringTableOffset); 142 | 143 | //read stringtable 144 | std::vector stringtable; 145 | readStringtable(jnt1Offset + h.stringTableOffset, f, stringtable); 146 | 147 | //joint entries 148 | out << endl << "Joint entries" << endl; 149 | fseek(f, jnt1Offset + h.jntEntryOffset, SEEK_SET); 150 | for(i = 0; i < h.count; ++i) 151 | { 152 | bmd::JntEntry e; 153 | readJnt1Entry(f, e); 154 | 155 | out << " \"" << stringtable[i] << "\":" 156 | << endl << " " 157 | << " unknown " << e.unknown 158 | << " unk3 " << (int)e.unknown3 159 | << " pad " << (int)e.pad 160 | << endl << " " 161 | << " sx " << e.sx 162 | << " sy " << e.sy 163 | << " sz " << e.sz 164 | << endl << " " 165 | << " rx " << e.rx/32768.f*180 166 | << " ry " << e.ry/32768.f*180 167 | << " rz " << e.rz/32768.f*180 168 | << " pad2 " << e.pad2 169 | << endl << " " 170 | << " tx " << e.tx 171 | << " ty " << e.ty 172 | << " tz " << e.tz 173 | << endl << " " 174 | << " unknown2 " << e.unknown2 175 | << endl << " " 176 | << " bbMin " << e.bbMin[0] << ", " << e.bbMin[1] << ", " << e.bbMin[2] 177 | << endl << " " 178 | << " bbMax " << e.bbMax[0] << ", " << e.bbMax[1] << ", " << e.bbMax[2] 179 | << endl << endl; 180 | } 181 | 182 | out << endl; 183 | } 184 | -------------------------------------------------------------------------------- /Matrix44.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include //swap 3 | 4 | template 5 | Matrix44::Matrix44() 6 | { 7 | } 8 | 9 | template 10 | Matrix44::Matrix44(Num _00, Num _01, Num _02, Num _03, 11 | Num _10, Num _11, Num _12, Num _13, 12 | Num _20, Num _21, Num _22, Num _23, 13 | Num _30, Num _31, Num _32, Num _33) 14 | { 15 | m[0][0] = _00; m[0][1] = _01; m[0][2] = _02; m[0][3] = _03; 16 | m[1][0] = _10; m[1][1] = _11; m[1][2] = _12; m[1][3] = _13; 17 | m[2][0] = _20; m[2][1] = _21; m[2][2] = _22; m[2][3] = _23; 18 | m[3][0] = _30; m[3][1] = _31; m[3][2] = _32; m[3][3] = _33; 19 | } 20 | 21 | template 22 | Matrix44::Matrix44(Num values[4][4]) 23 | { 24 | for(int i = 0; i < 4; ++i) 25 | for(int j = 0; j < 4; ++j) 26 | m[i][j] = values[i][j]; 27 | } 28 | 29 | template 30 | void Matrix44::loadIdentity() 31 | { 32 | m[0][0] = (Num)1; m[0][1] = (Num)0; m[0][2] = (Num)0; m[0][3] = (Num)0; 33 | m[1][0] = (Num)0; m[1][1] = (Num)1; m[1][2] = (Num)0; m[1][3] = (Num)0; 34 | m[2][0] = (Num)0; m[2][1] = (Num)0; m[2][2] = (Num)1; m[2][3] = (Num)0; 35 | m[3][0] = (Num)0; m[3][1] = (Num)0; m[3][2] = (Num)0; m[3][3] = (Num)1; 36 | } 37 | 38 | template 39 | void Matrix44::flip() 40 | { 41 | Num temp; 42 | 43 | temp = m[0][1]; m[0][1] = m[1][0]; m[1][0] = temp; 44 | temp = m[0][2]; m[0][2] = m[2][0]; m[2][0] = temp; 45 | temp = m[0][3]; m[0][3] = m[3][0]; m[3][0] = temp; 46 | 47 | temp = m[1][2]; m[1][2] = m[2][1]; m[2][1] = temp; 48 | temp = m[1][3]; m[1][3] = m[3][1]; m[3][1] = temp; 49 | 50 | temp = m[2][3]; m[2][3] = m[3][2]; m[3][2] = temp; 51 | } 52 | 53 | template 54 | Matrix44 Matrix44::transpose() const 55 | { 56 | Matrix44 ret = *this; 57 | ret.flip(); 58 | return ret; 59 | } 60 | 61 | template 62 | Matrix44 Matrix44::inverse() const 63 | { 64 | Matrix44 ret = *this; 65 | if(gaussJordan(ret.m)) 66 | return ret; 67 | /* else 68 | return ZERO;*/ 69 | } 70 | 71 | template 72 | void Matrix44::loadTranslateRM(Num tx, Num ty, Num tz) 73 | { 74 | loadIdentity(); 75 | m[3][0] = tx; 76 | m[3][1] = ty; 77 | m[3][2] = tz; 78 | } 79 | 80 | template 81 | void Matrix44::loadTranslateLM(Num tx, Num ty, Num tz) 82 | { 83 | loadIdentity(); 84 | m[0][3] = tx; 85 | m[1][3] = ty; 86 | m[2][3] = tz; 87 | } 88 | 89 | template 90 | void Matrix44::loadRotateXRM(Num rad) 91 | { 92 | loadIdentity(); 93 | m[1][1] = (Num)cos(rad); 94 | m[2][1] = -(Num)sin(rad); 95 | m[1][2] = (Num)sin(rad); 96 | m[2][2] = (Num)cos(rad); 97 | } 98 | 99 | template 100 | void Matrix44::loadRotateXLM(Num rad) 101 | { 102 | loadIdentity(); 103 | m[1][1] = (Num)cos(rad); 104 | m[1][2] = -(Num)sin(rad); 105 | m[2][1] = (Num)sin(rad); 106 | m[2][2] = (Num)cos(rad); 107 | } 108 | 109 | template 110 | void Matrix44::loadRotateYRM(Num rad) 111 | { 112 | loadIdentity(); 113 | m[0][0] = (Num)cos(rad); 114 | m[2][0] = (Num)sin(rad); 115 | m[0][2] = -(Num)sin(rad); 116 | m[2][2] = (Num)cos(rad); 117 | } 118 | 119 | template 120 | void Matrix44::loadRotateYLM(Num rad) 121 | { 122 | loadIdentity(); 123 | m[0][0] = (Num)cos(rad); 124 | m[0][2] = (Num)sin(rad); 125 | m[2][0] = -(Num)sin(rad); 126 | m[2][2] = (Num)cos(rad); 127 | } 128 | 129 | template 130 | void Matrix44::loadRotateZRM(Num rad) 131 | { 132 | loadIdentity(); 133 | m[0][0] = (Num)cos(rad); 134 | m[1][0] = -(Num)sin(rad); 135 | m[0][1] = (Num)sin(rad); 136 | m[1][1] = (Num)cos(rad); 137 | } 138 | 139 | template 140 | void Matrix44::loadRotateZLM(Num rad) 141 | { 142 | loadIdentity(); 143 | m[0][0] = (Num)cos(rad); 144 | m[0][1] = -(Num)sin(rad); 145 | m[1][0] = (Num)sin(rad); 146 | m[1][1] = (Num)cos(rad); 147 | } 148 | 149 | 150 | template 151 | Matrix44 Matrix44::operator*(const Matrix44& b) const 152 | { 153 | Matrix44 ret; 154 | for(int i = 0; i < 4; ++i) 155 | for(int j = 0; j < 4; ++j) 156 | ret.m[i][j] = m[i][0]*b.m[0][j] + m[i][1]*b.m[1][j] + m[i][2]*b.m[2][j] + m[i][3]*b.m[3][j]; 157 | 158 | return ret; 159 | } 160 | 161 | //checks if two matrices are exactly equal 162 | template 163 | bool Matrix44::operator==(const Matrix44& b) const 164 | { 165 | for(int i = 0; i < 4; ++i) 166 | for(int j = 0; j < 4; ++j) 167 | if(m[i][j] != b.m[i][j]) 168 | return false; 169 | 170 | return true; 171 | } 172 | 173 | 174 | template 175 | bool Matrix44::gaussJordan(Num matrix[4][4]) 176 | { 177 | const int rows = 4; 178 | int i; //loop counter 179 | int pivotRows[4]; 180 | int pivotColumns[4]; 181 | bool wasColumnUsed[4]; 182 | for(i = 0; i < rows; ++i) 183 | wasColumnUsed[i] = false; 184 | 185 | for(int rowCount = 0; rowCount < rows; ++rowCount) 186 | { 187 | Num maxVal = (Num)0; 188 | int currPivotRow, currPivotColumn; 189 | //search for biggest number in matrix, use it as pivot 190 | for(i = 0; i < rows; ++i) //loop thru rows 191 | { 192 | if(wasColumnUsed[i]) //use only one pivot from each row 193 | continue; 194 | 195 | for(int j = 0; j < rows; ++j) //loop thru columns 196 | { 197 | if(wasColumnUsed[j]) //use only one pivot from each column 198 | continue; 199 | 200 | Num curr = (Num)fabs(matrix[j][i]); 201 | if(curr > maxVal) 202 | { 203 | maxVal = curr; 204 | currPivotRow = j; 205 | currPivotColumn = i; 206 | } 207 | } 208 | } 209 | 210 | if(wasColumnUsed[currPivotColumn]) 211 | return false; 212 | wasColumnUsed[currPivotColumn] = true; 213 | 214 | //store which pivot was chosen in the rowCount'th run to reswap inverse matrix afterwards 215 | pivotRows[rowCount] = currPivotRow; 216 | pivotColumns[rowCount] = currPivotColumn; 217 | 218 | 219 | //swap rows to bring pivot on diagonal 220 | if(currPivotRow != currPivotColumn) 221 | { 222 | for(i = 0; i < rows; ++i) std::swap(matrix[currPivotRow][i], matrix[currPivotColumn][i]); 223 | currPivotRow = currPivotColumn; 224 | } 225 | 226 | if(matrix[currPivotRow][currPivotColumn] == (Num)0) 227 | return false; 228 | 229 | //start eliminating the pivot's column in each row 230 | Num inversePivot = ((Num)1)/matrix[currPivotRow][currPivotColumn]; 231 | matrix[currPivotRow][currPivotColumn] = (Num)1; 232 | for(i = 0; i < rows; ++i) matrix[currPivotRow][i] *= inversePivot; 233 | 234 | //reduce non-pivot rows 235 | for(i = 0; i < rows; ++i) 236 | { 237 | if(i == currPivotRow) //pivot row is already reduced 238 | continue; 239 | 240 | Num temp = matrix[i][currPivotColumn]; 241 | matrix[i][currPivotColumn] = (Num)0; 242 | for(int j = 0; j < rows; ++j) matrix[i][j] -= temp*matrix[currPivotRow][j]; 243 | } 244 | } 245 | 246 | //unscramble inverse matrix 247 | for(i = rows - 1; i >= 0; --i) 248 | { 249 | if(pivotRows[i] != pivotColumns[i]) 250 | { 251 | for(int j = 0; j < rows; ++j) 252 | std::swap(matrix[j][pivotRows[i]], matrix[j][pivotColumns[i]]); 253 | } 254 | } 255 | 256 | //done :-)!! 257 | 258 | return true; 259 | } 260 | /* 261 | template 262 | const Matrix44 Matrix44::IDENTITY((Num)1, (Num)0, (Num)0, (Num)0, 263 | (Num)0, (Num)1, (Num)0, (Num)0, 264 | (Num)0, (Num)0, (Num)1, (Num)0, 265 | (Num)0, (Num)0, (Num)0, (Num)1); 266 | */ 267 | /* 268 | const Matrix44 Matrix44::ZERO((Num)0, (Num)0, (Num)0, (Num)0, 269 | (Num)0, (Num)0, (Num)0, (Num)0, 270 | (Num)0, (Num)0, (Num)0, (Num)0, 271 | (Num)0, (Num)0, (Num)0, (Num)0); 272 | */ 273 | -------------------------------------------------------------------------------- /addons/exportTexture.cpp: -------------------------------------------------------------------------------- 1 | #include "exportTexture.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include // memset 7 | 8 | #include "../common.h" 9 | 10 | struct ColorCaps 11 | { 12 | u32 size; 13 | u32 flags; 14 | char fourCC[4]; 15 | u32 rgbBitCount; 16 | u32 rBitMask; 17 | u32 gBitMask; 18 | u32 bBitMask; 19 | u32 aBitMask; 20 | }; 21 | 22 | struct DdsHeader 23 | { 24 | char type[4]; 25 | u32 size; 26 | u32 flags; 27 | u32 height; 28 | u32 width; 29 | u32 linearSize; 30 | u32 depth; 31 | u32 numMips; 32 | u32 unused[11]; 33 | ColorCaps colorCaps; 34 | u32 caps; 35 | u32 unused2[4]; 36 | }; 37 | 38 | 39 | struct TgaHeader 40 | { 41 | u8 identsize; // size of ID field that follows 18 uint8 header (0 usually) 42 | u8 colourmaptype; // type of colour map 0=none, 1=has palette 43 | u8 imagetype; // type of image 0=none,1=indexed,2=rgb,3=grey,+8=rle packed 44 | 45 | s16 colourmapstart; // first colour map entry in palette 46 | s16 colourmaplength; // number of colours in palette 47 | u8 colourmapbits; // number of bits per palette entry 15,16,24,32 48 | 49 | s16 xstart; // image x origin 50 | s16 ystart; // image y origin 51 | s16 width; // image width in pixels 52 | s16 height; // image height in pixels 53 | u8 bits; // image bits per pixel 8,16,24,32 54 | u8 descriptor; // image descriptor bits (vh flip bits) 55 | }; 56 | 57 | 58 | void writeColorCaps(FILE* f, const ColorCaps& cc) 59 | { 60 | writeDWORD_le(f, cc.size); 61 | writeDWORD_le(f, cc.flags); 62 | fwrite(cc.fourCC, 1, 4, f); 63 | writeDWORD_le(f, cc.rgbBitCount); 64 | writeDWORD_le(f, cc.rBitMask); 65 | writeDWORD_le(f, cc.gBitMask); 66 | writeDWORD_le(f, cc.bBitMask); 67 | writeDWORD_le(f, cc.aBitMask); 68 | } 69 | 70 | void writeDdsHeader(FILE* f, const DdsHeader& h) 71 | { 72 | fwrite(h.type, 1, 4, f); 73 | writeDWORD_le(f, h.size); 74 | writeDWORD_le(f, h.flags); 75 | writeDWORD_le(f, h.height); 76 | writeDWORD_le(f, h.width); 77 | writeDWORD_le(f, h.linearSize); 78 | writeDWORD_le(f, h.depth); 79 | writeDWORD_le(f, h.numMips); 80 | for(int i = 0; i < 11; ++i) 81 | writeDWORD_le(f, h.unused[i]); 82 | writeColorCaps(f, h.colorCaps); 83 | writeDWORD_le(f, h.caps); 84 | for(int j = 0; j < 4; ++j) 85 | writeDWORD_le(f, h.unused2[j]); 86 | } 87 | 88 | void writeTgaHeader(FILE* f, const TgaHeader& h) 89 | { 90 | fwrite(&h.identsize, 1, 1, f); 91 | fwrite(&h.colourmaptype, 1, 1, f); 92 | fwrite(&h.imagetype, 1, 1, f); 93 | 94 | writeSHORT_le(f, h.colourmapstart); 95 | writeSHORT_le(f, h.colourmaplength); 96 | fwrite(&h.colourmapbits, 1, 1, f); 97 | 98 | writeSHORT_le(f, h.xstart); 99 | writeSHORT_le(f, h.ystart); 100 | writeSHORT_le(f, h.width); 101 | writeSHORT_le(f, h.height); 102 | fwrite(&h.bits, 1, 1, f); 103 | fwrite(&h.descriptor, 1, 1, f); 104 | } 105 | 106 | //in tex1.cpp (TODO: create imageutils header) 107 | void decompressDxt1(u8* dest, const u8* src, int w, int h); 108 | 109 | void i8a8ToRgba8(u8* dest, const u8* src, int w, int h) 110 | { 111 | for(int y = 0; y < h; ++y) 112 | { 113 | for(int x = 0; x < w; ++x) 114 | { 115 | u8 i8 = src[2*x + 0]; 116 | u8 a8 = src[2*x + 1]; 117 | dest[4*x + 0] = i8; 118 | dest[4*x + 1] = i8; 119 | dest[4*x + 2] = i8; 120 | dest[4*x + 3] = a8; 121 | } 122 | 123 | src += 2*w; 124 | dest += 4*w; 125 | } 126 | } 127 | 128 | void flipVertical(std::vector& vec, int w, int h, int bpp) 129 | { 130 | assert(w*h*bpp == (int)vec.size()); 131 | std::vector tmpLine(w*bpp); 132 | u8* up = &vec[0], * down = &vec[w*(h - 1)*bpp]; 133 | for(int y = 0; y < h/2; ++y) 134 | { 135 | std::copy(up, up + w*bpp, tmpLine.begin()); 136 | std::copy(down, down + w*bpp, up); 137 | std::copy(tmpLine.begin(), tmpLine.end(), down); 138 | 139 | up += w*bpp; 140 | down -= w*bpp; 141 | } 142 | } 143 | 144 | DdsHeader createDdsHeader(int w, int h, int numMips) 145 | { 146 | DdsHeader ret; 147 | memset(&ret, 0, sizeof(ret)); 148 | 149 | strncpy(ret.type, "DDS ", 4); 150 | ret.size = 124; 151 | ret.flags = 0x21007; //mipmapcount + pixelformat + width + height + caps 152 | ret.width = w; 153 | ret.height = h; 154 | ret.numMips = numMips; 155 | ret.colorCaps.size = 32; 156 | ret.caps = 0x401000; //mipmaps + texture 157 | return ret; 158 | } 159 | 160 | void saveTextureDds(const std::string& filename, const Image& img) 161 | { 162 | DdsHeader h = createDdsHeader(img.width, img.height, img.mipmaps.size()); 163 | switch(img.format) 164 | { 165 | case I8: 166 | h.colorCaps.flags = 0x20000; //luminance 167 | h.colorCaps.rgbBitCount = 8; 168 | h.colorCaps.rBitMask = 0xff; 169 | break; 170 | case I8_A8: 171 | h.colorCaps.flags = 0x20001; //luminance + alpha 172 | h.colorCaps.rgbBitCount = 16; 173 | h.colorCaps.rBitMask = 0xff; 174 | h.colorCaps.aBitMask = 0xff00; 175 | break; 176 | case RGBA8: 177 | h.colorCaps.flags = 0x41; //rgb + alpha 178 | h.colorCaps.rgbBitCount = 32; 179 | h.colorCaps.rBitMask = 0xff; 180 | h.colorCaps.gBitMask = 0xff00; 181 | h.colorCaps.bBitMask = 0xff0000; 182 | h.colorCaps.aBitMask = 0xff000000; 183 | break; 184 | case DXT1: 185 | h.colorCaps.flags = 0x4; //fourcc 186 | strncpy(h.colorCaps.fourCC, "DXT1", 4); 187 | break; 188 | } 189 | 190 | FILE* file = fopen(filename.c_str(), "wb"); 191 | if(file == NULL) 192 | return; 193 | 194 | writeDdsHeader(file, h); 195 | fwrite(&img.imageData[0], 1, img.imageData.size(), file); 196 | fclose(file); 197 | } 198 | 199 | void saveTextureTga(const std::string& filename, const Image& img) 200 | { 201 | TgaHeader h = { 0, 0, 2, 0, 0, 0, 0, 0, img.width, img.height, 32, 0 }; 202 | 203 | FILE* file = fopen(filename.c_str(), "wb"); 204 | if(file == NULL) 205 | return; 206 | 207 | if(img.format == I8) 208 | { 209 | //greyscale 210 | h.imagetype = 3; 211 | h.bits = 8; 212 | writeTgaHeader(file, h); 213 | 214 | //flip vertical 215 | std::vector data(h.width*h.height); 216 | std::copy(img.imageData.begin(), img.imageData.begin() + data.size(), 217 | data.begin()); 218 | flipVertical(data, h.width, h.height, 1); 219 | fwrite(&data[0], 1, data.size(), file); 220 | } 221 | else 222 | { 223 | //convert to rgba8, save 224 | writeTgaHeader(file, h); 225 | 226 | std::vector data(h.width*h.height*4); 227 | switch(img.format) 228 | { 229 | case I8_A8: //convert i8a8 to rgba8 230 | i8a8ToRgba8(&data[0], &img.imageData[0], h.width, h.height); 231 | break; 232 | case RGBA8: //rgba8 - write directly 233 | memcpy(&data[0], &img.imageData[0], data.size()); 234 | break; 235 | case DXT1: //convert dxt1 to rgba8 236 | decompressDxt1(&data[0], &img.imageData[0], h.width, h.height); 237 | break; 238 | } 239 | 240 | //data is rgba8, targa stores bgra8, convert: 241 | for(size_t i = 0; i < data.size(); i += 4) 242 | std::swap(data[i + 0], data[i + 2]); 243 | 244 | //data is top-down, targa stores bottom up 245 | flipVertical(data, h.width, h.height, 4); 246 | 247 | fwrite(&data[0], 1, data.size(), file); 248 | } 249 | 250 | fclose(file); 251 | } 252 | 253 | void saveTexture(IMAGETYPE imgType, const Image& img, const std::string& filename) 254 | { 255 | switch(imgType) 256 | { 257 | case DDS: 258 | saveTextureDds(filename + ".dds", img); 259 | break; 260 | case TGA: 261 | saveTextureTga(filename + ".tga", img); 262 | break; 263 | } 264 | } 265 | 266 | void exportTextures(IMAGETYPE imgType, const Tex1& tex, 267 | const std::string& basename) 268 | { 269 | //export every image only once 270 | std::set isAlreadyExported; 271 | 272 | for(size_t i = 0; i < tex.imageHeaders.size(); ++i) 273 | { 274 | const ImageHeader& h = tex.imageHeaders[i]; 275 | if(isAlreadyExported.find(h.data) 276 | == isAlreadyExported.end()) 277 | { 278 | isAlreadyExported.insert(h.data); 279 | 280 | char buff[1024]; sprintf(buff, "%02d%s_%d_%d", (int)i, h.name.c_str(), 281 | h.data->originalFormat, h.data->paletteFormat); 282 | saveTexture(imgType, *h.data, basename + buff); 283 | } 284 | } 285 | } 286 | -------------------------------------------------------------------------------- /vtx1.cpp: -------------------------------------------------------------------------------- 1 | #include "vtx1.h" 2 | 3 | #include 4 | 5 | using namespace std; 6 | 7 | namespace bmd 8 | { 9 | 10 | struct Vtx1Header 11 | { 12 | char tag[4]; //'VTX1' 13 | u32 sizeOfSection; 14 | u32 arrayFormatOffset; //for each offsets[i] != 0, an ArrayFormat 15 | //is stored for that offset 16 | //offset relative to Vtx1Header start 17 | 18 | /* 19 | content is described by ArrayFormat - for each offset != 0, 20 | an ArrayFormat struct is stored at Vtx1Header.arrayFormatOffset 21 | */ 22 | u32 offsets[13]; //offsets relative to Vtx1Header start 23 | }; 24 | 25 | struct ArrayFormat 26 | { 27 | //see ogc/gx.h for a more complete list of these values: 28 | u32 arrayType; //9: coords, a: normal, b: color, d: tex0 (gx.h: "Attribute") 29 | u32 componentCount; //meaning depends on dataType (gx.h: "CompCount") 30 | u32 dataType; //3: s16, 4: float, 5: rgba8 (gx.h: "CompType") 31 | 32 | //values i've seem for this: 7, e, 8, b, 0 33 | //-> number of mantissa bits for fixed point numbers! 34 | //(position of decimal point) 35 | u8 decimalPoint; 36 | u8 unknown3; //seems to be always 0xff 37 | u16 unknown4; //seems to be always 0xffff 38 | }; 39 | 40 | }; 41 | 42 | 43 | int getLength(const bmd::Vtx1Header& h, int k) 44 | { 45 | int startOffset = h.offsets[k]; 46 | for(int i = k + 1; i < 13; ++i) 47 | { 48 | if(h.offsets[i] != 0) 49 | return h.offsets[i] - startOffset; 50 | } 51 | return h.sizeOfSection - startOffset; 52 | } 53 | 54 | void readVertexArray(Vtx1& arrays, const bmd::ArrayFormat& af, int length, 55 | FILE* f, long offset) 56 | { 57 | fseek(f, offset, SEEK_SET); 58 | 59 | //convert array to float (so we have n + m cases, not n*m) 60 | vector data; 61 | switch(af.dataType) 62 | { 63 | case 3: //s16 fixed point 64 | { 65 | vector tmp(length/2); 66 | fread(&tmp[0], 2, length/2, f); 67 | data.resize(length/2); 68 | float scale = pow(.5f, af.decimalPoint); 69 | for(size_t j = 0; j < data.size(); ++j) 70 | { 71 | toSHORT(tmp[j]); 72 | data[j] = tmp[j]*scale; 73 | } 74 | }break; 75 | 76 | case 4: //f32 77 | { 78 | data.resize(length/4); 79 | fread(&data[0], 4, length/4, f); 80 | for(size_t j = 0; j < data.size(); ++j) 81 | toFLOAT(data[j]); 82 | }break; 83 | 84 | case 5: //rgb(a) 85 | { 86 | vector tmp(length); 87 | fread(&tmp[0], 1, length, f); 88 | data.resize(length); 89 | for(size_t j = 0; j < data.size(); ++j) 90 | data[j] = tmp[j]; 91 | }break; 92 | 93 | default: 94 | warn("vtx1: unknown array data type %d", af.dataType); 95 | return; 96 | } 97 | 98 | //stuff floats into appropriate vertex array 99 | switch(af.arrayType) 100 | { 101 | case 9: //positions 102 | { 103 | if(af.componentCount == 0) //xy 104 | { 105 | arrays.positions.resize(data.size()/2); 106 | for(size_t j = 0, k = 0; j < arrays.positions.size(); ++j, k += 2) 107 | arrays.positions[j].setXYZ(data[k], data[k + 1], 0); 108 | } 109 | else if(af.componentCount == 1) //xyz 110 | { 111 | arrays.positions.resize(data.size()/3); 112 | for(size_t j = 0, k = 0; j < arrays.positions.size(); ++j, k += 3) 113 | arrays.positions[j].setXYZ(data[k], data[k + 1], data[k + 2]); 114 | } 115 | else 116 | warn("vtx1: unsupported componentCount for positions array: %d", 117 | af.componentCount); 118 | }break; 119 | 120 | case 0xa: //normals 121 | { 122 | if(af.componentCount == 0) //xyz 123 | { 124 | arrays.normals.resize(data.size()/3); 125 | for(size_t j = 0, k = 0; j < arrays.normals.size(); ++j, k += 3) 126 | arrays.normals[j].setXYZ(data[k], data[k + 1], data[k + 2]); 127 | } 128 | else 129 | warn("vtx1: unsupported componentCount for normals array: %d", 130 | af.componentCount); 131 | }break; 132 | 133 | case 0xb: //color0 134 | case 0xc: //color1 135 | { 136 | int index = af.arrayType - 0xb; 137 | if(af.componentCount == 0) //rgb 138 | { 139 | arrays.colors[index].resize(data.size()/3); 140 | for(size_t j = 0, k = 0; j < arrays.colors[index].size(); ++j, k += 3) 141 | arrays.colors[index][j].setRGBA(data[k], data[k + 1], data[k + 2], 142 | 255.f); 143 | } 144 | else if(af.componentCount == 1) //rgba 145 | { 146 | arrays.colors[index].resize(data.size()/4); 147 | for(size_t j = 0, k = 0; j < arrays.colors[index].size(); ++j, k += 4) 148 | arrays.colors[index][j].setRGBA(data[k], data[k + 1], data[k + 2], 149 | data[k + 3]); 150 | } 151 | else 152 | warn("vtx1: unsupported componentCount for colors array %d: %d", 153 | index, af.componentCount); 154 | 155 | }break; 156 | 157 | //texcoords 0 - 7 158 | case 0xd: 159 | case 0xe: 160 | case 0xf: 161 | case 0x10: 162 | case 0x11: 163 | case 0x12: 164 | case 0x13: 165 | case 0x14: 166 | { 167 | int index = af.arrayType - 0xd; 168 | if(af.componentCount == 0) //s 169 | { 170 | arrays.texCoords[index].resize(data.size()); 171 | for(size_t j = 0; j < arrays.texCoords[index].size(); ++j) 172 | arrays.texCoords[index][j].setST(data[j], 0); 173 | } 174 | else if(af.componentCount == 1) //st 175 | { 176 | arrays.texCoords[index].resize(data.size()/2); 177 | for(size_t j = 0, k = 0; j < arrays.texCoords[index].size(); ++j, k += 2) 178 | arrays.texCoords[index][j].setST(data[k], data[k + 1]); 179 | } 180 | else 181 | warn("vtx1: unsupported componentCount for texcoords array %d: %d", 182 | index, af.componentCount); 183 | }break; 184 | 185 | 186 | default: 187 | warn("vtx1: unknown array type %d", af.arrayType); 188 | } 189 | } 190 | 191 | void readVtx1Header(FILE* f, bmd::Vtx1Header& h) 192 | { 193 | fread(h.tag, 1, 4, f); 194 | readDWORD(f, h.sizeOfSection); 195 | readDWORD(f, h.arrayFormatOffset); 196 | for(int i = 0; i < 13; ++i) 197 | readDWORD(f, h.offsets[i]); 198 | } 199 | 200 | void readArrayFormat(FILE* f, bmd::ArrayFormat& af) 201 | { 202 | readDWORD(f, af.arrayType); 203 | readDWORD(f, af.componentCount); 204 | readDWORD(f, af.dataType); 205 | fread(&af.decimalPoint, 1, 1, f); 206 | fread(&af.unknown3, 1, 1, f); 207 | readWORD(f, af.unknown4); 208 | } 209 | 210 | int countArrays(const bmd::Vtx1Header& h) 211 | { 212 | int numArrays = 0; 213 | for(int i = 0; i < 13; ++i) 214 | if(h.offsets[i] != 0) ++numArrays; 215 | return numArrays; 216 | } 217 | 218 | void dumpVtx1(FILE* f, Vtx1& dst) 219 | { 220 | int vtx1Offset = ftell(f), i; 221 | 222 | //read header 223 | bmd::Vtx1Header h; 224 | readVtx1Header(f, h); 225 | 226 | int numArrays = countArrays(h); 227 | 228 | //read vertex array format descriptions 229 | vector formats(numArrays); 230 | fseek(f, vtx1Offset + h.arrayFormatOffset, SEEK_SET); 231 | for(i = 0; i < numArrays; ++i) 232 | readArrayFormat(f, formats[i]); 233 | 234 | //read arrays 235 | int j = 0; 236 | for(i = 0; i < 13; ++i) 237 | { 238 | if(h.offsets[i] == 0) 239 | continue; 240 | 241 | bmd::ArrayFormat& currFormat = formats[j]; 242 | int len = getLength(h, i); 243 | readVertexArray(dst, currFormat, len, f, 244 | vtx1Offset + h.offsets[i]); 245 | 246 | ++j; 247 | } 248 | } 249 | 250 | 251 | void writeVtx1Info(FILE* f, ostream& out) 252 | { 253 | out << string(50, '/') << endl 254 | << "//Vtx1 section" << endl 255 | << string(50, '/') << endl << endl; 256 | 257 | int vtx1Offset = ftell(f); 258 | 259 | bmd::Vtx1Header h; 260 | readVtx1Header(f, h); 261 | 262 | int numArrays = countArrays(h); 263 | 264 | out << numArrays << " formats:" << endl; 265 | 266 | fseek(f, vtx1Offset + h.arrayFormatOffset, SEEK_SET); 267 | for(int i = 0; i < numArrays; ++i) 268 | { 269 | bmd::ArrayFormat af; 270 | readArrayFormat(f, af); 271 | 272 | out << af.arrayType << ", " << af.componentCount << ", " << af.dataType 273 | << " -- " << (int)af.decimalPoint; 274 | if(af.unknown3 != 0xff) out << " !" << (int)af.unknown3; 275 | if(af.unknown4 != 0xffff) out << " !!" << af.unknown3; 276 | out << endl; 277 | } 278 | out << endl; 279 | } 280 | -------------------------------------------------------------------------------- /addons/bck.cpp: -------------------------------------------------------------------------------- 1 | #include "bck.h" 2 | #include // strncmp 3 | 4 | using namespace std; 5 | 6 | namespace bck 7 | { 8 | 9 | struct Ank1Header 10 | { 11 | char tag[4]; //'ANK1' 12 | u32 sizeOfSection; 13 | 14 | /* 15 | 0 - play once 16 | 2 - loop 17 | */ 18 | u8 loopFlags; 19 | 20 | u8 angleMultiplier; //all angles have to multiplied by pow(2, angleMultiplyer) 21 | 22 | u16 animationLength; //in time units 23 | 24 | u16 numJoints; //that many animated joints at offsetToJoints 25 | u16 scaleCount; //that many floats at offsetToScales 26 | u16 rotCount; //that many s16s at offsetToRots 27 | u16 transCount; //that many floats at offsetToTrans 28 | 29 | u32 offsetToJoints; 30 | u32 offsetToScales; 31 | u32 offsetToRots; 32 | u32 offsetToTrans; 33 | }; 34 | 35 | 36 | //TODO: the following two structs have really silly names, rename them 37 | struct AnimIndex 38 | { 39 | u16 count; 40 | u16 index; 41 | u16 zero; //always 0?? -> no (biawatermill01.bck) TODO: find out what it means 42 | }; 43 | 44 | struct AnimComponent 45 | { 46 | AnimIndex s; //scale 47 | AnimIndex r; //rotation 48 | AnimIndex t; //translation 49 | }; 50 | 51 | struct AnimatedJoint 52 | { 53 | /* 54 | if count > 1, count*3 floats/shorts stored at index 55 | (time, value, unk [interpolation info, e.g. tangent??])? 56 | 57 | for shorts, time is a "real" short, no fixedpoint 58 | */ 59 | AnimComponent x; 60 | AnimComponent y; 61 | AnimComponent z; 62 | }; 63 | 64 | }; 65 | 66 | void readAnk1Header(FILE* f, bck::Ank1Header& h) 67 | { 68 | fread(h.tag, 1, 4, f); 69 | readDWORD(f, h.sizeOfSection); 70 | fread(&h.loopFlags, 1, 1, f); 71 | fread(&h.angleMultiplier, 1, 1, f); 72 | readWORD(f, h.animationLength); 73 | readWORD(f, h.numJoints); 74 | readWORD(f, h.scaleCount); 75 | readWORD(f, h.rotCount); 76 | readWORD(f, h.transCount); 77 | readDWORD(f, h.offsetToJoints); 78 | readDWORD(f, h.offsetToScales); 79 | readDWORD(f, h.offsetToRots); 80 | readDWORD(f, h.offsetToTrans); 81 | } 82 | 83 | void readAnimIndex(FILE* f, bck::AnimIndex& h) 84 | { 85 | readWORD(f, h.count); 86 | readWORD(f, h.index); 87 | readWORD(f, h.zero); 88 | } 89 | 90 | void readAnimComponent(FILE* f, bck::AnimComponent& h) 91 | { 92 | readAnimIndex(f, h.s); 93 | readAnimIndex(f, h.r); 94 | readAnimIndex(f, h.t); 95 | } 96 | 97 | void readAnimatedJoint(FILE* f, bck::AnimatedJoint& h) 98 | { 99 | readAnimComponent(f, h.x); 100 | readAnimComponent(f, h.y); 101 | readAnimComponent(f, h.z); 102 | } 103 | 104 | template 105 | void readComp(vector& dst, const vector& src, const bck::AnimIndex& index) 106 | { 107 | dst.resize(index.count); 108 | 109 | //violated by biawatermill01.bck 110 | if(index.zero != 0) 111 | warn("bck: zero field %d instead of zero", index.zero); 112 | //TODO: biawatermill01.bck doesn't work, so the "zero" 113 | //value is obviously something important 114 | 115 | if(index.count <= 0) 116 | { 117 | warn("bck1: readComp(): count is <= 0"); 118 | return; 119 | } 120 | else if(index.count == 1) 121 | { 122 | dst[0].time = 0; 123 | dst[0].value = src[index.index]; 124 | dst[0].tangent = 0; 125 | } 126 | else 127 | { 128 | for(int j = 0; j < index.count; ++j) 129 | { 130 | dst[j].time = src[index.index + 3*j]; 131 | dst[j].value = src[index.index + 3*j + 1]; 132 | dst[j].tangent = src[index.index + 3*j + 2]; 133 | } 134 | } 135 | } 136 | 137 | void convRotation(vector& rots, float scale) 138 | { 139 | for(size_t j = 0; j < rots.size(); ++j) 140 | { 141 | rots[j].value *= scale; 142 | rots[j].tangent *= scale; 143 | } 144 | } 145 | 146 | void dumpAnk1(FILE* f, Bck& bck) 147 | { 148 | int i; 149 | long ank1Offset = ftell(f); 150 | 151 | //read header 152 | bck::Ank1Header h; 153 | readAnk1Header(f, h); 154 | 155 | bck.animationLength = h.animationLength; 156 | 157 | //read scale floats: 158 | fseek(f, ank1Offset + h.offsetToScales, SEEK_SET); 159 | vector scales(h.scaleCount); 160 | fread(&scales[0], 4, h.scaleCount, f); 161 | for(i = 0; i < h.scaleCount; ++i) 162 | toFLOAT(scales[i]); 163 | 164 | //read rotation s16s: 165 | fseek(f, ank1Offset + h.offsetToRots, SEEK_SET); 166 | vector rotations(h.rotCount); 167 | fread(&rotations[0], 2, h.rotCount, f); 168 | for(i = 0; i < h.rotCount; ++i) 169 | toSHORT(rotations[i]); 170 | 171 | //read translation floats: 172 | fseek(f, ank1Offset + h.offsetToTrans, SEEK_SET); 173 | vector translations(h.transCount); 174 | fread(&translations[0], 4, h.transCount, f); 175 | for(i = 0; i < h.transCount; ++i) 176 | toFLOAT(translations[i]); 177 | 178 | //read joints 179 | float rotScale = pow(2.f, h.angleMultiplier)*180/32768.f; 180 | fseek(f, ank1Offset + h.offsetToJoints, SEEK_SET); 181 | bck.anims.resize(h.numJoints); 182 | for(i = 0; i < h.numJoints; ++i) 183 | { 184 | bck::AnimatedJoint joint; 185 | readAnimatedJoint(f, joint); 186 | 187 | readComp(bck.anims[i].scalesX, scales, joint.x.s); 188 | readComp(bck.anims[i].scalesY, scales, joint.y.s); 189 | readComp(bck.anims[i].scalesZ, scales, joint.z.s); 190 | 191 | readComp(bck.anims[i].rotationsX, rotations, joint.x.r); 192 | convRotation(bck.anims[i].rotationsX, rotScale); 193 | readComp(bck.anims[i].rotationsY, rotations, joint.y.r); 194 | convRotation(bck.anims[i].rotationsY, rotScale); 195 | readComp(bck.anims[i].rotationsZ, rotations, joint.z.r); 196 | convRotation(bck.anims[i].rotationsZ, rotScale); 197 | 198 | readComp(bck.anims[i].translationsX, translations, joint.x.t); 199 | readComp(bck.anims[i].translationsY, translations, joint.y.t); 200 | readComp(bck.anims[i].translationsZ, translations, joint.z.t); 201 | } 202 | 203 | //TODO: some zelda files store additional data after 204 | //the ANK1 block, for example 24_cl_cut07_dash_o.bck 205 | } 206 | 207 | Bck* readBck(FILE* f) 208 | { 209 | Bck* ret = new Bck; 210 | 211 | //skip file header 212 | fseek(f, 0x20, SEEK_SET); 213 | 214 | u32 size = 0; 215 | char tag[4]; 216 | int t; 217 | 218 | do 219 | { 220 | fseek(f, size, SEEK_CUR); 221 | t = ftell(f); 222 | 223 | fread(tag, 1, 4, f); 224 | fread(&size, 4, 1, f); 225 | toDWORD(size); 226 | if(size < 8) size = 8; //prevent endless loop on corrupt data 227 | 228 | if(feof(f)) 229 | break; 230 | fseek(f, t, SEEK_SET); 231 | 232 | if(strncmp(tag, "ANK1", 4) == 0) 233 | dumpAnk1(f, *ret); 234 | else 235 | warn("readBck(): Unsupported section \'%c%c%c%c\'", 236 | tag[0], tag[1], tag[2], tag[3]); 237 | 238 | fseek(f, t, SEEK_SET); 239 | } while(!feof(f)); 240 | 241 | return ret; 242 | } 243 | 244 | ////////////////////////////////////////////////////////////////////// 245 | 246 | 247 | template 248 | T interpolate(T v1, T d1, T v2, T d2, T t) //t in [0, 1] 249 | { 250 | //linear interpolation 251 | //return v1 + t*(v2 - v1); 252 | 253 | //cubic interpolation 254 | float a = 2*(v1 - v2) + d1 + d2; 255 | float b = -3*v1 + 3*v2 - 2*d1 - d2; 256 | float c = d1; 257 | float d = v1; 258 | //TODO: yoshi_walk.bck has strange-looking legs...not sure if 259 | //the following line is to blame, though 260 | return ((a*t + b)*t + c)*t + d; 261 | } 262 | 263 | float getAnimValue(const vector& keys, float t) 264 | { 265 | if(keys.size() == 0) 266 | return 0.f; 267 | 268 | if(keys.size() == 1) 269 | return keys[0].value; 270 | 271 | int i = 1; 272 | while(keys[i].time < t) 273 | ++i; 274 | 275 | float time = (t - keys[i - 1].time)/(keys[i].time - keys[i - 1].time); //scale to [0, 1] 276 | return interpolate(keys[i - 1].value, keys[i - 1].tangent, keys[i].value, keys[i].tangent, time); 277 | } 278 | 279 | //the caller has to ensure that jnt1.frames and bck.anims contain 280 | //the same number of elements 281 | void animate(Bck& bck, Jnt1& jnt1, float ftime) 282 | { 283 | ftime = fmod(ftime, bck.animationLength); 284 | 285 | //update joints 286 | for(size_t i = 0; i < jnt1.frames.size(); ++i) 287 | { 288 | jnt1.frames[i].sx = getAnimValue(bck.anims[i].scalesX, ftime); 289 | jnt1.frames[i].sy = getAnimValue(bck.anims[i].scalesY, ftime); 290 | jnt1.frames[i].sz = getAnimValue(bck.anims[i].scalesZ, ftime); 291 | 292 | //TODO: use quaternion interpolation for rotations? 293 | jnt1.frames[i].rx = getAnimValue(bck.anims[i].rotationsX, ftime); 294 | jnt1.frames[i].ry = getAnimValue(bck.anims[i].rotationsY, ftime); 295 | jnt1.frames[i].rz = getAnimValue(bck.anims[i].rotationsZ, ftime); 296 | 297 | jnt1.frames[i].t.x() = getAnimValue(bck.anims[i].translationsX, ftime); 298 | jnt1.frames[i].t.y() = getAnimValue(bck.anims[i].translationsY, ftime); 299 | jnt1.frames[i].t.z() = getAnimValue(bck.anims[i].translationsZ, ftime); 300 | } 301 | } 302 | -------------------------------------------------------------------------------- /Matrix44.inc: -------------------------------------------------------------------------------- 1 | #include 2 | #include //swap 3 | 4 | template 5 | Matrix44::Matrix44() 6 | { 7 | } 8 | 9 | template 10 | Matrix44::Matrix44(Num _00, Num _01, Num _02, Num _03, 11 | Num _10, Num _11, Num _12, Num _13, 12 | Num _20, Num _21, Num _22, Num _23, 13 | Num _30, Num _31, Num _32, Num _33) 14 | { 15 | m[0][0] = _00; m[0][1] = _01; m[0][2] = _02; m[0][3] = _03; 16 | m[1][0] = _10; m[1][1] = _11; m[1][2] = _12; m[1][3] = _13; 17 | m[2][0] = _20; m[2][1] = _21; m[2][2] = _22; m[2][3] = _23; 18 | m[3][0] = _30; m[3][1] = _31; m[3][2] = _32; m[3][3] = _33; 19 | } 20 | 21 | template 22 | Matrix44::Matrix44(Num values[4][4]) 23 | { 24 | for(int i = 0; i < 4; ++i) 25 | for(int j = 0; j < 4; ++j) 26 | m[i][j] = values[i][j]; 27 | } 28 | 29 | template 30 | void Matrix44::loadIdentity() 31 | { 32 | m[0][0] = (Num)1; m[0][1] = (Num)0; m[0][2] = (Num)0; m[0][3] = (Num)0; 33 | m[1][0] = (Num)0; m[1][1] = (Num)1; m[1][2] = (Num)0; m[1][3] = (Num)0; 34 | m[2][0] = (Num)0; m[2][1] = (Num)0; m[2][2] = (Num)1; m[2][3] = (Num)0; 35 | m[3][0] = (Num)0; m[3][1] = (Num)0; m[3][2] = (Num)0; m[3][3] = (Num)1; 36 | } 37 | 38 | template 39 | void Matrix44::flip() 40 | { 41 | Num temp; 42 | 43 | temp = m[0][1]; m[0][1] = m[1][0]; m[1][0] = temp; 44 | temp = m[0][2]; m[0][2] = m[2][0]; m[2][0] = temp; 45 | temp = m[0][3]; m[0][3] = m[3][0]; m[3][0] = temp; 46 | 47 | temp = m[1][2]; m[1][2] = m[2][1]; m[2][1] = temp; 48 | temp = m[1][3]; m[1][3] = m[3][1]; m[3][1] = temp; 49 | 50 | temp = m[2][3]; m[2][3] = m[3][2]; m[3][2] = temp; 51 | } 52 | 53 | template 54 | Matrix44 Matrix44::transpose() const 55 | { 56 | Matrix44 ret = *this; 57 | ret.flip(); 58 | return ret; 59 | } 60 | 61 | template 62 | Matrix44 Matrix44::inverse() const 63 | { 64 | Matrix44 ret = *this; 65 | if(gaussJordan(ret.m)) 66 | return ret; 67 | else 68 | return ZERO; 69 | } 70 | 71 | template 72 | void Matrix44::loadTranslateRM(Num tx, Num ty, Num tz) 73 | { 74 | loadIdentity(); 75 | m[3][0] = tx; 76 | m[3][1] = ty; 77 | m[3][2] = tz; 78 | } 79 | 80 | template 81 | void Matrix44::loadTranslateLM(Num tx, Num ty, Num tz) 82 | { 83 | loadIdentity(); 84 | m[0][3] = tx; 85 | m[1][3] = ty; 86 | m[2][3] = tz; 87 | } 88 | 89 | template 90 | void Matrix44::loadRotateXRM(Num rad) 91 | { 92 | loadIdentity(); 93 | m[1][1] = (Num)cos(rad); 94 | m[2][1] = -(Num)sin(rad); 95 | m[1][2] = (Num)sin(rad); 96 | m[2][2] = (Num)cos(rad); 97 | } 98 | 99 | template 100 | void Matrix44::loadRotateXLM(Num rad) 101 | { 102 | loadIdentity(); 103 | m[1][1] = (Num)cos(rad); 104 | m[1][2] = -(Num)sin(rad); 105 | m[2][1] = (Num)sin(rad); 106 | m[2][2] = (Num)cos(rad); 107 | } 108 | 109 | template 110 | void Matrix44::loadRotateYRM(Num rad) 111 | { 112 | loadIdentity(); 113 | m[0][0] = (Num)cos(rad); 114 | m[2][0] = (Num)sin(rad); 115 | m[0][2] = -(Num)sin(rad); 116 | m[2][2] = (Num)cos(rad); 117 | } 118 | 119 | template 120 | void Matrix44::loadRotateYLM(Num rad) 121 | { 122 | loadIdentity(); 123 | m[0][0] = (Num)cos(rad); 124 | m[0][2] = (Num)sin(rad); 125 | m[2][0] = -(Num)sin(rad); 126 | m[2][2] = (Num)cos(rad); 127 | } 128 | 129 | template 130 | void Matrix44::loadRotateZRM(Num rad) 131 | { 132 | loadIdentity(); 133 | m[0][0] = (Num)cos(rad); 134 | m[1][0] = -(Num)sin(rad); 135 | m[0][1] = (Num)sin(rad); 136 | m[1][1] = (Num)cos(rad); 137 | } 138 | 139 | template 140 | void Matrix44::loadRotateZLM(Num rad) 141 | { 142 | loadIdentity(); 143 | m[0][0] = (Num)cos(rad); 144 | m[0][1] = -(Num)sin(rad); 145 | m[1][0] = (Num)sin(rad); 146 | m[1][1] = (Num)cos(rad); 147 | } 148 | 149 | template 150 | void Matrix44::loadScale(Num sx, Num sy, Num sz) 151 | { 152 | loadIdentity(); 153 | m[0][0] = sx; 154 | m[1][1] = sy; 155 | m[2][2] = sz; 156 | } 157 | 158 | template 159 | void Matrix44::loadScale(Num s) 160 | { 161 | loadScale(s, s, s); 162 | } 163 | 164 | template 165 | Matrix44 Matrix44::operator*(const Matrix44& b) const 166 | { 167 | Matrix44 ret; 168 | for(int i = 0; i < 4; ++i) 169 | for(int j = 0; j < 4; ++j) 170 | ret.m[i][j] = m[i][0]*b.m[0][j] + m[i][1]*b.m[1][j] + m[i][2]*b.m[2][j] + m[i][3]*b.m[3][j]; 171 | 172 | return ret; 173 | } 174 | 175 | //checks if two matrices are exactly equal 176 | template 177 | bool Matrix44::operator==(const Matrix44& b) const 178 | { 179 | for(int i = 0; i < 4; ++i) 180 | for(int j = 0; j < 4; ++j) 181 | if(m[i][j] != b.m[i][j]) 182 | return false; 183 | 184 | return true; 185 | } 186 | 187 | template 188 | Matrix44::operator Num*() 189 | { 190 | return &m[0][0]; 191 | } 192 | 193 | template 194 | Matrix44::operator const Num*() const 195 | { 196 | return &m[0][0]; 197 | } 198 | 199 | template 200 | Num* Matrix44::operator[](int i) 201 | { 202 | return m[i]; 203 | } 204 | 205 | template 206 | const Num* Matrix44::operator[](int i) const 207 | { 208 | return m[i]; 209 | } 210 | 211 | template 212 | bool Matrix44::gaussJordan(Num matrix[4][4]) 213 | { 214 | const int rows = 4; 215 | int i; //loop counter 216 | int pivotRows[4]; 217 | int pivotColumns[4]; 218 | bool wasColumnUsed[4]; 219 | for(i = 0; i < rows; ++i) 220 | wasColumnUsed[i] = false; 221 | 222 | for(int rowCount = 0; rowCount < rows; ++rowCount) 223 | { 224 | Num maxVal = (Num)0; 225 | int currPivotRow, currPivotColumn; 226 | //search for biggest number in matrix, use it as pivot 227 | for(i = 0; i < rows; ++i) //loop thru rows 228 | { 229 | if(wasColumnUsed[i]) //use only one pivot from each row 230 | continue; 231 | 232 | for(int j = 0; j < rows; ++j) //loop thru columns 233 | { 234 | if(wasColumnUsed[j]) //use only one pivot from each column 235 | continue; 236 | 237 | Num curr = (Num)fabs(matrix[j][i]); 238 | if(curr > maxVal) 239 | { 240 | maxVal = curr; 241 | currPivotRow = j; 242 | currPivotColumn = i; 243 | } 244 | } 245 | } 246 | 247 | if(wasColumnUsed[currPivotColumn]) 248 | return false; 249 | wasColumnUsed[currPivotColumn] = true; 250 | 251 | //store which pivot was chosen in the rowCount'th run to reswap inverse matrix afterwards 252 | pivotRows[rowCount] = currPivotRow; 253 | pivotColumns[rowCount] = currPivotColumn; 254 | 255 | 256 | //swap rows to bring pivot on diagonal 257 | if(currPivotRow != currPivotColumn) 258 | { 259 | for(i = 0; i < rows; ++i) std::swap(matrix[currPivotRow][i], matrix[currPivotColumn][i]); 260 | currPivotRow = currPivotColumn; 261 | } 262 | 263 | if(matrix[currPivotRow][currPivotColumn] == (Num)0) 264 | return false; 265 | 266 | //start eliminating the pivot's column in each row 267 | Num inversePivot = ((Num)1)/matrix[currPivotRow][currPivotColumn]; 268 | matrix[currPivotRow][currPivotColumn] = (Num)1; 269 | for(i = 0; i < rows; ++i) matrix[currPivotRow][i] *= inversePivot; 270 | 271 | //reduce non-pivot rows 272 | for(i = 0; i < rows; ++i) 273 | { 274 | if(i == currPivotRow) //pivot row is already reduced 275 | continue; 276 | 277 | Num temp = matrix[i][currPivotColumn]; 278 | matrix[i][currPivotColumn] = (Num)0; 279 | for(int j = 0; j < rows; ++j) matrix[i][j] -= temp*matrix[currPivotRow][j]; 280 | } 281 | } 282 | 283 | //unscramble inverse matrix 284 | for(i = rows - 1; i >= 0; --i) 285 | { 286 | if(pivotRows[i] != pivotColumns[i]) 287 | { 288 | for(int j = 0; j < rows; ++j) 289 | std::swap(matrix[j][pivotRows[i]], matrix[j][pivotColumns[i]]); 290 | } 291 | } 292 | 293 | //done :-)!! 294 | 295 | return true; 296 | } 297 | 298 | template 299 | const Matrix44 Matrix44::IDENTITY = Matrix44 300 | ((Num)1, (Num)0, (Num)0, (Num)0, 301 | (Num)0, (Num)1, (Num)0, (Num)0, 302 | (Num)0, (Num)0, (Num)1, (Num)0, 303 | (Num)0, (Num)0, (Num)0, (Num)1); 304 | template 305 | const Matrix44 Matrix44::ZERO = Matrix44 306 | ((Num)0, (Num)0, (Num)0, (Num)0, 307 | (Num)0, (Num)0, (Num)0, (Num)0, 308 | (Num)0, (Num)0, (Num)0, (Num)0, 309 | (Num)0, (Num)0, (Num)0, (Num)0); 310 | -------------------------------------------------------------------------------- /BMDView2.depend: -------------------------------------------------------------------------------- 1 | # depslib dependency file v1.0 2 | 1355124155 source:/home/antidote/Desktop/bmdview/addons/bck.cpp 3 | "bck.h" 4 | 5 | 6 | 1166295581 /home/antidote/Desktop/bmdview/addons/bck.h 7 | "../common.h" 8 | "../jnt1.h" 9 | 10 | 1166814146 /home/antidote/Desktop/bmdview/common.h 11 | "common/gccommon.h" 12 | 13 | 14 | 15 | 16 | 17 | 1179206874 /home/antidote/Desktop/bmdview/common/gccommon.h 18 | 19 | 20 | 21 | 1166295584 /home/antidote/Desktop/bmdview/jnt1.h 22 | "common.h" 23 | "Vector3.h" 24 | 25 | 26 | 27 | "Matrix44.h" 28 | 29 | 1166948001 /home/antidote/Desktop/bmdview/Vector3.h 30 | "Vector3.inc" 31 | 32 | 1166948029 /home/antidote/Desktop/bmdview/Vector3.inc 33 | "Vector3.h" 34 | 35 | 36 | 37 | 1166295584 /home/antidote/Desktop/bmdview/Matrix44.h 38 | "Matrix44.inc" 39 | 40 | 1166295584 /home/antidote/Desktop/bmdview/Matrix44.inc 41 | 42 | 43 | 44 | 1355124173 source:/home/antidote/Desktop/bmdview/addons/btp.cpp 45 | "btp.h" 46 | 47 | 48 | 49 | 1167414564 /home/antidote/Desktop/bmdview/addons/btp.h 50 | "../common.h" 51 | "../mat3.h" 52 | 53 | 1166295584 /home/antidote/Desktop/bmdview/mat3.h 54 | "common.h" 55 | 56 | 57 | 1355124199 source:/home/antidote/Desktop/bmdview/addons/export3ds.cpp 58 | "export3ds.h" 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | "lib3ds/file.h" 67 | "lib3ds/mesh.h" 68 | "lib3ds/node.h" 69 | "lib3ds/material.h" 70 | "lib3ds/matrix.h" 71 | "lib3ds/vector.h" 72 | "../common.h" 73 | "exportTexture.h" 74 | "../transformtools.h" 75 | 76 | 1166295581 /home/antidote/Desktop/bmdview/addons/export3ds.h 77 | 78 | "../bmdread.h" 79 | 80 | 1166295584 /home/antidote/Desktop/bmdview/bmdread.h 81 | 82 | 83 | 84 | "Vector3.h" 85 | "common.h" 86 | "inf1.h" 87 | "vtx1.h" 88 | "evp1.h" 89 | "drw1.h" 90 | "jnt1.h" 91 | "shp1.h" 92 | "mat3.h" 93 | "tex1.h" 94 | "mdl3.h" 95 | 96 | 1166295584 /home/antidote/Desktop/bmdview/inf1.h 97 | "common.h" 98 | 99 | 100 | 101 | 1166295584 /home/antidote/Desktop/bmdview/vtx1.h 102 | "common.h" 103 | 104 | 105 | "Vector3.h" 106 | 107 | 1166295584 /home/antidote/Desktop/bmdview/evp1.h 108 | "common.h" 109 | 110 | "Matrix44.h" 111 | 112 | 1166295584 /home/antidote/Desktop/bmdview/drw1.h 113 | "common.h" 114 | 115 | 116 | 117 | 1166295584 /home/antidote/Desktop/bmdview/shp1.h 118 | 119 | 120 | "common.h" 121 | "Vector3.h" 122 | 123 | 1167265554 /home/antidote/Desktop/bmdview/tex1.h 124 | "common.h" 125 | 126 | 127 | 128 | 129 | 1166295584 /home/antidote/Desktop/bmdview/mdl3.h 130 | 131 | 132 | 1166295581 /home/antidote/Desktop/bmdview/addons/exportTexture.h 133 | 134 | "../bmdread.h" 135 | 136 | 1166814419 /home/antidote/Desktop/bmdview/transformtools.h 137 | 138 | "Matrix44.h" 139 | "Vector3.h" 140 | "bmdread.h" 141 | "shp1.h" 142 | "jnt1.h" 143 | 144 | 1355124216 source:/home/antidote/Desktop/bmdview/addons/exportTexture.cpp 145 | "exportTexture.h" 146 | 147 | 148 | 149 | 150 | "../common.h" 151 | 152 | 1167415583 source:/home/antidote/Desktop/bmdview/addons/exportb3d.cpp 153 | "exportb3d.h" 154 | 155 | 1167332404 /home/antidote/Desktop/bmdview/addons/exportb3d.h 156 | 157 | "../bmdread.h" 158 | "bck.h" 159 | 160 | 1167332569 source:/home/antidote/Desktop/bmdview/addons/exportx.cpp 161 | "exportx.h" 162 | 163 | "../transformtools.h" 164 | 165 | 1166295581 /home/antidote/Desktop/bmdview/addons/exportx.h 166 | 167 | "../bmdread.h" 168 | 169 | 1166826929 source:/home/antidote/Desktop/bmdview/bmdinfo.cpp 170 | 171 | 172 | "bmdread.h" 173 | "openfile.h" 174 | "addons/exportTexture.h" 175 | "addons/export3ds.h" 176 | 177 | 1166295584 /home/antidote/Desktop/bmdview/openfile.h 178 | 179 | 180 | 181 | 1355124231 source:/home/antidote/Desktop/bmdview/bmdread.cpp 182 | "bmdread.h" 183 | 184 | 185 | 186 | 1167416248 source:/home/antidote/Desktop/bmdview/camera.cpp 187 | "camera.h" 188 | "GL/glew.h" 189 | 190 | 1166901429 /home/antidote/Desktop/bmdview/camera.h 191 | "Matrix44.h" 192 | 193 | 1167248974 source:/home/antidote/Desktop/bmdview/common.cpp 194 | "common.h" 195 | 196 | 197 | 1167414933 source:/home/antidote/Desktop/bmdview/drawBmd.cpp 198 | "drawbmd.h" 199 | "bmdread.h" 200 | "simple_gl.h" 201 | 202 | 203 | "transformtools.h" 204 | "camera.h" 205 | "oglblock.h" 206 | 207 | 1166295584 /home/antidote/Desktop/bmdview/drawbmd.h 208 | 209 | "inf1.h" 210 | 211 | 1167137335 /home/antidote/Desktop/bmdview/simple_gl.h 212 | 213 | "common.h" 214 | 215 | 216 | 1166295584 /home/antidote/Desktop/bmdview/oglblock.h 217 | "common.h" 218 | "GL/glew.h" 219 | 220 | 221 | "bmdread.h" 222 | 223 | 1355124330 source:/home/antidote/Desktop/bmdview/drawtext.cpp 224 | "drawtext.h" 225 | "GL/glew.h" 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 1166951730 /home/antidote/Desktop/bmdview/drawtext.h 234 | "GL/glew.h" 235 | 236 | 237 | 238 | 1166514267 source:/home/antidote/Desktop/bmdview/drw1.cpp 239 | "drw1.h" 240 | 241 | 242 | 1167245624 source:/home/antidote/Desktop/bmdview/evp1.cpp 243 | "evp1.h" 244 | 245 | 246 | 1167245624 source:/home/antidote/Desktop/bmdview/inf1.cpp 247 | "inf1.h" 248 | 249 | 250 | 1167248974 source:/home/antidote/Desktop/bmdview/jnt1.cpp 251 | "jnt1.h" 252 | 253 | 254 | 1226280055 source:/home/antidote/Desktop/bmdview/main.cpp 255 | "simple_gl.h" 256 | "simple_gl_common.h" 257 | "openfile.h" 258 | "bmdread.h" 259 | "drawbmd.h" 260 | "drawtext.h" 261 | "parameters.h" 262 | "camera.h" 263 | "oglblock.h" 264 | "addons/bck.h" 265 | "addons/btp.h" 266 | "addons/export3ds.h" 267 | "addons/exportTexture.h" 268 | 269 | 270 | 1167131611 /home/antidote/Desktop/bmdview/simple_gl_common.h 271 | 272 | 1166951070 /home/antidote/Desktop/bmdview/parameters.h 273 | 274 | 275 | 276 | 1355124345 source:/home/antidote/Desktop/bmdview/mat3.cpp 277 | "mat3.h" 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 1166295584 source:/home/antidote/Desktop/bmdview/mdl3.cpp 286 | "mdl3.h" 287 | 288 | 289 | 290 | 1179209536 source:/home/antidote/Desktop/bmdview/oglblock.cpp 291 | "oglblock.h" 292 | "simple_gl.h" 293 | 294 | 295 | 296 | 1355124377 source:/home/antidote/Desktop/bmdview/openfile.cpp 297 | "openfile.h" 298 | "common.h" 299 | 300 | 301 | 302 | 303 | 304 | 1226279696 source:/home/antidote/Desktop/bmdview/parameters.cpp 305 | "parameters.h" 306 | 307 | 1167246490 source:/home/antidote/Desktop/bmdview/shp1.cpp 308 | "shp1.h" 309 | 310 | 311 | 312 | 1167131628 source:/home/antidote/Desktop/bmdview/simple_gl_common.cpp 313 | "simple_gl_common.h" 314 | 315 | "GL/glew.h" 316 | "common.h" 317 | "camera.h" 318 | "clock.h" 319 | "simple_gl.h" 320 | 321 | 1166903365 /home/antidote/Desktop/bmdview/clock.h 322 | 323 | 1355123773 source:/home/antidote/Desktop/bmdview/simple_gl_glut.cpp 324 | 325 | 326 | "GL/glew.h" 327 | 328 | "mac.h" 329 | 330 | "parameters.h" 331 | "simple_gl_common.h" 332 | "drawtext.h" 333 | "simple_gl.h" 334 | "ui.h" 335 | "resource.h" 336 | "main.h" 337 | <3DConnexionClient/ConnexionClientAPI.h> 338 | "camera.h" 339 | 340 | 1226282912 /home/antidote/Desktop/bmdview/mac.h 341 | 342 | 1179211397 /home/antidote/Desktop/bmdview/ui.h 343 | 344 | 345 | 1166295584 /home/antidote/Desktop/bmdview/resource.h 346 | 347 | 1179198203 /home/antidote/Desktop/bmdview/main.h 348 | "drawbmd.h" 349 | 350 | 1355124523 source:/home/antidote/Desktop/bmdview/tex1.cpp 351 | "tex1.h" 352 | 353 | 354 | 355 | 356 | "GL/glew.h" 357 | "simple_gl.h" 358 | 359 | 1167245624 source:/home/antidote/Desktop/bmdview/transformtools.cpp 360 | "transformtools.h" 361 | 362 | 363 | 1355125683 source:/home/antidote/Desktop/bmdview/ui.cpp 364 | "ui.h" 365 | 366 | 367 | "addons/export3ds.h" 368 | "addons/exportTexture.h" 369 | "drawbmd.h" 370 | "oglblock.h" 371 | 372 | 373 | "textbox.h" 374 | "main.h" 375 | 376 | 1166295584 /home/antidote/Desktop/bmdview/textbox.h 377 | 378 | 379 | 1167245624 source:/home/antidote/Desktop/bmdview/vtx1.cpp 380 | "vtx1.h" 381 | 382 | 383 | -------------------------------------------------------------------------------- /drawBmd.cpp: -------------------------------------------------------------------------------- 1 | #include "drawbmd.h" 2 | 3 | #include "bmdread.h" 4 | 5 | #include "simple_gl.h" 6 | #include 7 | #include 8 | 9 | #include "transformtools.h" 10 | #include "camera.h" 11 | 12 | #include "oglblock.h" 13 | 14 | using namespace std; 15 | 16 | Vector3f normTrans(const Matrix44f& m, const Vector3f& v) 17 | { 18 | //TODO: use inverse transpose 19 | return Vector3f( 20 | m[0][0]*v[0] + m[0][1]*v[1] + m[0][2]*v[2], 21 | m[1][0]*v[0] + m[1][1]*v[1] + m[1][2]*v[2], 22 | m[2][0]*v[0] + m[2][1]*v[1] + m[2][2]*v[2] 23 | ); 24 | } 25 | 26 | Matrix44f operator*(float f, const Matrix44f& m) 27 | { 28 | Matrix44f s; 29 | s.loadScale(f); 30 | return s*m; //doesn't change 4th row (intended) 31 | } 32 | 33 | Matrix44f updateMatrix(const Frame& f, Matrix44f effP) 34 | { 35 | return effP*frameMatrix(f); 36 | } 37 | 38 | 39 | static GLenum 40 | compareMode(u8 gxMode) 41 | { 42 | switch(gxMode) 43 | { 44 | case 0: //GX_NEVER 45 | return GL_NEVER; 46 | break; 47 | 48 | case 1: //GX_LESS 49 | return GL_LESS; 50 | break; 51 | 52 | case 2: //GX_EQUAL 53 | return GL_EQUAL; 54 | break; 55 | 56 | case 3: //GX_LEQUAL 57 | return GL_LEQUAL; 58 | break; 59 | 60 | case 4: //GX_GREATER 61 | return GL_GREATER; 62 | break; 63 | 64 | case 5: //GX_NEQUAL 65 | return GL_NOTEQUAL; 66 | break; 67 | 68 | case 6: //GX_GEQUAL 69 | return GL_GEQUAL; 70 | break; 71 | 72 | case 7: //GX_ALWAYS 73 | return GL_ALWAYS; 74 | break; 75 | 76 | default: 77 | fprintf (stderr, "unknown compare mode %d\n", gxMode); 78 | return GL_ALWAYS; 79 | } 80 | } 81 | 82 | static GLenum 83 | blendFunc(u8 blendMode) 84 | { 85 | switch(blendMode) 86 | { 87 | case 0: //GX_BL_ZERO 88 | return GL_ZERO; 89 | 90 | case 1: //GX_BL_ONE 91 | return GL_ONE; 92 | 93 | case 2: //GX_BL_SRCCLR / GX_BL_DSTCLR 94 | return GL_SRC_COLOR; 95 | 96 | case 3: //GX_BL_INVSRCCLOR / GX_BL_INVDSTCLR 97 | return GL_ONE_MINUS_SRC_COLOR; 98 | 99 | case 4: //GX_BL_SRCALPHA 100 | return GL_SRC_ALPHA; 101 | 102 | case 5: //GX_BL_INVSRCALPHA 103 | return GL_ONE_MINUS_SRC_ALPHA; 104 | 105 | case 6: //GX_DSTALPHA 106 | //return GL_DST_ALPHA; 107 | return GL_SRC_ALPHA; 108 | 109 | case 7: //GX_INVDSTALPHA 110 | //return GL_ONE_MINUS_DST_ALPHA; 111 | return GL_ONE_MINUS_SRC_ALPHA; 112 | 113 | default: 114 | warn("Unknown blendMode %d", (u32)blendMode); 115 | return GL_ONE; 116 | } 117 | } 118 | 119 | void applyMaterial(int index, Model& m, const OglBlock& oglBlock) 120 | { 121 | BModel& bmd = *m.bmd; 122 | 123 | //material (TODO: this is very hacky right now) 124 | 125 | Material& mat = bmd.mat3.materials[bmd.mat3.indexToMatIndex[index]]; 126 | string name = bmd.mat3.stringtable[index]; 127 | 128 | //blending 129 | const BlendInfo& bi = bmd.mat3.blendInfos[mat.blendIndex]; 130 | 131 | switch(bi.blendMode) 132 | { 133 | case 0: //TODO: this should mean "don't blend", but links eyes don't 134 | //work without this 135 | case 1: //blend 136 | //TODO: check for destination alpha etc 137 | if(bi.srcFactor == 1 && bi.dstFactor == 0) 138 | glDisable(GL_BLEND); 139 | else 140 | { 141 | glBlendFunc(blendFunc(bi.srcFactor), blendFunc(bi.dstFactor)); 142 | glEnable(GL_BLEND); 143 | } 144 | break; 145 | } 146 | 147 | //cull mode 148 | switch(bmd.mat3.cullModes[mat.cullIndex]) 149 | { 150 | case 0: //GX_CULL_NONE 151 | glDisable(GL_CULL_FACE); 152 | break; 153 | 154 | case 1: //GX_CULL_FRONT 155 | glEnable(GL_CULL_FACE); 156 | glCullFace(GL_FRONT); 157 | break; 158 | 159 | case 2: //GX_CULL_BACK 160 | glEnable(GL_CULL_FACE); 161 | glCullFace(GL_BACK); 162 | break; 163 | } 164 | 165 | if(bmd.mat3.zModes[mat.zModeIndex].enable) 166 | glEnable(GL_DEPTH_TEST); 167 | else 168 | glDisable(GL_DEPTH_TEST); 169 | 170 | glDepthFunc(compareMode(bmd.mat3.zModes[mat.zModeIndex].zFunc)); 171 | glDepthMask(bmd.mat3.zModes[mat.zModeIndex].enableUpdate); 172 | 173 | //texture 174 | if(m.oglBlock != NULL) 175 | setMaterial(index, *m.oglBlock, bmd); 176 | } 177 | 178 | static void 179 | adjustMatrix(Matrix44f& mat, u8 matrixType) 180 | { 181 | switch(matrixType) 182 | { 183 | case 1: //billboard 184 | { 185 | //get camera matrix to kill rotation from 186 | //the inverse camera transform in GL_MODELVIEW 187 | 188 | //slowww - each call computes a matrix inverse 189 | //TODO: why is transpose() needed? 190 | Matrix44f cam = getCameraMatrix().transpose(); 191 | 192 | cam[0][3] = 0.f; 193 | cam[1][3] = 0.f; 194 | cam[2][3] = 0.f; 195 | 196 | mat[0][0] = 1.f; 197 | mat[0][1] = 0.f; 198 | mat[0][2] = 0.f; 199 | 200 | mat[1][0] = 0.f; 201 | mat[1][1] = 1.f; 202 | mat[1][2] = 0.f; 203 | 204 | mat[2][0] = 0.f; 205 | mat[2][1] = 0.f; 206 | mat[2][2] = 1.f; 207 | 208 | mat = mat*cam; 209 | }break; 210 | 211 | case 2: //y billboard 212 | { 213 | Matrix44f cam = getCameraMatrix().transpose(); 214 | 215 | Vector3f camPos(cam[0][3], cam[1][3], cam[2][3]); 216 | 217 | cam[0][3] = 0.f; 218 | cam[1][3] = 0.f; 219 | cam[2][3] = 0.f; 220 | 221 | Vector3f up(0, 1, 0); 222 | 223 | Vector3f billPos(mat[3][0], mat[3][1], mat[3][2]); 224 | Vector3f view = camPos - billPos; 225 | 226 | Vector3f front = view - up*view.dot(up); 227 | front.normalize(); 228 | Vector3f right = -front.cross(up); 229 | 230 | mat[0][0] = right[0]; 231 | mat[1][0] = right[1]; 232 | mat[2][0] = right[2]; 233 | 234 | mat[0][1] = up[0]; 235 | mat[1][1] = up[1]; 236 | mat[2][1] = up[2]; 237 | 238 | mat[0][2] = front[0]; 239 | mat[1][2] = front[1]; 240 | mat[2][2] = front[2]; 241 | }break; 242 | } 243 | } 244 | 245 | void drawBatch(BModel& bmd, int index, const Matrix44f& def) 246 | { 247 | Batch& currBatch = bmd.shp1.batches[index]; 248 | 249 | if(!currBatch.attribs.hasPositions) 250 | { 251 | fprintf(stderr, "found batch without positions\n"); 252 | return; //not visible 253 | } 254 | 255 | if(currBatch.attribs.hasTexCoords[0]) 256 | glEnable(GL_TEXTURE_2D); 257 | else 258 | glDisable(GL_TEXTURE_2D); 259 | 260 | Matrix44f matrixTable[10]; //10 "normal" matrices - see gx.h 261 | 262 | vector isMatrixWeighted(10, false); //used for debug output 263 | 264 | size_t j; 265 | for(j = 0; j < currBatch.packets.size(); ++j) 266 | { 267 | Packet& currPacket = currBatch.packets[j]; 268 | 269 | //set up matrix table 270 | updateMatrixTable(bmd, currPacket, matrixTable, &isMatrixWeighted); 271 | 272 | //if no matrix index is given per vertex, 0 is the default. 273 | //otherwise, mat is overwritten later. 274 | Matrix44f mat = matrixTable[0]; 275 | 276 | adjustMatrix(mat, currBatch.matrixType); 277 | 278 | for(size_t k = 0; k < currPacket.primitives.size(); ++k) 279 | { 280 | Primitive& currPrimitive = currPacket.primitives[k]; 281 | 282 | switch(currPrimitive.type) 283 | { 284 | case 0x98: 285 | glBegin(GL_TRIANGLE_STRIP); break; 286 | 287 | case 0xa0: 288 | glBegin(GL_TRIANGLE_FAN); break; 289 | 290 | default: 291 | fprintf(stderr, "unknown primitive type %x\n", currPrimitive.type); 292 | continue; 293 | } 294 | 295 | glColor3f(1, 1, 1); 296 | for(size_t m = 0; m < currPrimitive.points.size(); ++m) 297 | { 298 | if(currBatch.attribs.hasMatrixIndices) 299 | { 300 | mat = matrixTable[currPrimitive.points[m].matrixIndex/3]; 301 | adjustMatrix(mat, currBatch.matrixType); 302 | } 303 | 304 | if(currBatch.attribs.hasNormals) 305 | glNormal3fv(normTrans(mat, bmd.vtx1.normals[currPrimitive.points[m].normalIndex])); 306 | 307 | if(currBatch.attribs.hasColors[0]) 308 | glColor4ubv((GLubyte*)&bmd.vtx1.colors[0][currPrimitive.points[m].colorIndex[0]]); 309 | 310 | for(int b = 0; b < 8; ++b) 311 | if(currBatch.attribs.hasTexCoords[b]) 312 | glMultiTexCoord2fv(GL_TEXTURE0 + b, 313 | (float*)&bmd.vtx1.texCoords[b][currPrimitive.points[m].texCoordIndex[b]]); 314 | 315 | glVertex3fv((mat)*bmd.vtx1.positions[currPrimitive.points[m].posIndex]); 316 | } 317 | 318 | glEnd(); 319 | } 320 | } 321 | 322 | } 323 | 324 | static void 325 | drawSceneGraph(Model& m, const SceneGraph& sg, 326 | const Matrix44f& p = Matrix44f::IDENTITY, 327 | int matIndex = 0) 328 | { 329 | BModel& bmd = *m.bmd; 330 | 331 | Matrix44f effP = p; 332 | 333 | switch(sg.type) 334 | { 335 | case 0x10: // joint 336 | { 337 | const Frame& f = bmd.jnt1.frames[sg.index]; 338 | bmd.jnt1.matrices[sg.index] = updateMatrix(f, effP); 339 | effP = bmd.jnt1.matrices[sg.index]; 340 | } 341 | break; 342 | case 0x11: // material 343 | matIndex = bmd.mat3.indexToMatIndex[sg.index]; 344 | break; 345 | case 0x12: // batch 346 | applyMaterial(matIndex, m, *m.oglBlock); 347 | drawBatch(bmd, sg.index, effP); 348 | break; 349 | } 350 | 351 | for(size_t i = 0; i < sg.children.size(); ++i) 352 | drawSceneGraph(m, sg.children[i], effP, matIndex); 353 | } 354 | 355 | void drawBmd(Model& m, const SceneGraph& sg) 356 | { 357 | drawSceneGraph(m, sg); 358 | } 359 | -------------------------------------------------------------------------------- /addons/export3ds.cpp: -------------------------------------------------------------------------------- 1 | #include "export3ds.h" 2 | 3 | #include 4 | #include // memcmp 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | #include "../common.h" 15 | #include "exportTexture.h" 16 | 17 | #include "../transformtools.h" 18 | 19 | //one bmd packet is one 3ds mesh 20 | 21 | //3ds supports 22 | //- vertex positions 23 | //- tex coords 24 | //- basic hierarchy 25 | //- textures (references to external files) 26 | //- animations (in a strange way) 27 | 28 | //does not support 29 | //- vertex colors 30 | //- vertex normals (but face normals...oh well) 31 | //- vertex weighting/skinning 32 | //- tristrips/trifans (converted to triangles) 33 | 34 | //currently working 35 | //- vertex positions 36 | //- tex coords 37 | //- textures 38 | 39 | //issues: levels are lit with vertex colors, which are not exported. 40 | //additionally, levels have no normals, so I can't decide which 41 | //objects need smoothend normals and which not. so level export 42 | //needs some manual tweaking 43 | 44 | static int 45 | countTriangles(const Primitive& curr) 46 | { 47 | switch(curr.type) 48 | { 49 | case GX_TRIANGLE_STRIP: 50 | case GX_TRIANGLE_FAN: 51 | return curr.points.size() - 2; 52 | break; 53 | 54 | default: //other primitives not supported atm 55 | warn("countTriangles(): unsupported primitive type %x", curr.type); 56 | return 0; 57 | } 58 | } 59 | 60 | static int 61 | countTriangles(const Packet& p) 62 | { 63 | int triCount = 0; 64 | for(size_t i = 0; i < p.primitives.size(); ++i) 65 | triCount += countTriangles(p.primitives[i]); 66 | return triCount; 67 | } 68 | 69 | static int 70 | countTriangles(const Batch& b) 71 | { 72 | int triCount = 0; 73 | for(size_t j = 0; j < b.packets.size(); ++j) 74 | triCount += countTriangles(b.packets[j]); 75 | return triCount; 76 | } 77 | 78 | struct Vert { 79 | Index point; 80 | int idx; 81 | }; 82 | 83 | static int 84 | internVert(std::map &usedVerts, 85 | Index point) 86 | { 87 | std::map::iterator it = usedVerts.find(point.posIndex); 88 | 89 | if (it == usedVerts.end()) 90 | { 91 | Vert v; 92 | v.point = point; 93 | v.idx = usedVerts.size(); 94 | usedVerts[point.posIndex] = v; 95 | } 96 | 97 | return usedVerts[point.posIndex].idx; 98 | } 99 | 100 | static Lib3dsMesh* 101 | batchToMesh(const Batch& b, const BModel& bmd, const std::string& name, 102 | int material) 103 | { 104 | const Vtx1& vtx = bmd.vtx1; 105 | const Attributes& attribs = b.attribs; 106 | 107 | if(!attribs.hasPositions) 108 | return NULL; 109 | 110 | Lib3dsMesh* mesh = lib3ds_mesh_new(name.c_str()); 111 | 112 | int triCount = countTriangles(b); 113 | 114 | std::map usedVerts; 115 | Matrix44f matrixTable[10]; 116 | 117 | //tris 118 | lib3ds_mesh_resize_faces(mesh, triCount); 119 | int triIndex = 0; 120 | size_t i, j, k; 121 | 122 | for(j = 0; j < b.packets.size(); ++j) 123 | { 124 | const Packet& p = b.packets[j]; 125 | 126 | for(i = 0; i < p.primitives.size(); ++i) 127 | { 128 | const Primitive& curr = p.primitives[i]; 129 | if(curr.points.size() < 2) continue; 130 | 131 | int a = 0; 132 | int b = 1; 133 | 134 | bool flip = true; 135 | for(k = 2; k < curr.points.size(); ++k) 136 | { 137 | int c = k; 138 | 139 | mesh->faces[triIndex].index[0] = internVert(usedVerts, curr.points[a]); 140 | mesh->faces[triIndex].index[1] = internVert(usedVerts, curr.points[b]); 141 | mesh->faces[triIndex].index[2] = internVert(usedVerts, curr.points[c]); 142 | 143 | if(attribs.hasTexCoords[0]) 144 | mesh->faces[triIndex].material = material; 145 | 146 | if(flip) 147 | std::swap(mesh->faces[triIndex].index[2], 148 | mesh->faces[triIndex].index[0]); 149 | 150 | ++triIndex; 151 | 152 | switch(curr.type) 153 | { 154 | case GX_TRIANGLE_STRIP: 155 | flip = !flip; 156 | a = b; 157 | b = c; 158 | break; 159 | case GX_TRIANGLE_FAN: 160 | b = c; 161 | break; 162 | } 163 | } 164 | } 165 | } 166 | 167 | lib3ds_mesh_resize_vertices(mesh, usedVerts.size(), 1, 0); 168 | 169 | for(j = 0; j < b.packets.size(); ++j) 170 | { 171 | const Packet& p = b.packets[j]; 172 | 173 | updateMatrixTable(bmd, p, matrixTable); 174 | 175 | for(i = 0; i < p.primitives.size(); ++i) 176 | { 177 | const Primitive& curr = p.primitives[i]; 178 | for(k = 0; k < curr.points.size(); ++k) 179 | { 180 | const Index &point = curr.points[k]; 181 | 182 | int idx = usedVerts[point.posIndex].idx; 183 | 184 | Vector3f v = vtx.positions[point.posIndex]; 185 | 186 | Matrix44f mat = matrixTable[0]; 187 | if(attribs.hasMatrixIndices) 188 | mat = matrixTable[point.matrixIndex/3]; 189 | else 190 | mat = Matrix44f::IDENTITY; 191 | 192 | v = mat * v; 193 | 194 | mesh->vertices[idx][0] = v.x(); 195 | mesh->vertices[idx][1] = -v.z(); 196 | mesh->vertices[idx][2] = v.y(); 197 | 198 | if(attribs.hasTexCoords[0]) 199 | { 200 | float u = vtx.texCoords[0][point.texCoordIndex[0]].s; 201 | float v = vtx.texCoords[0][point.texCoordIndex[0]].t; 202 | 203 | // store v flipped - 3ds max has v texcoord axis inverted 204 | mesh->texcos[idx][0] = u; 205 | mesh->texcos[idx][1] = 1.f - v; 206 | } 207 | } 208 | } 209 | } 210 | 211 | return mesh; 212 | } 213 | 214 | static Lib3dsMaterial* 215 | materialToMaterial(const BModel& bmd, const Material& mat, 216 | const std::vector& texNames, 217 | const std::string& name) 218 | { 219 | Lib3dsMaterial* mat3ds = lib3ds_material_new(name.c_str()); 220 | 221 | u16 stage = mat.texStages[0]; 222 | if(stage != 0xffff) 223 | { 224 | u16 v2 = bmd.mat3.texStageIndexToTextureIndex[stage]; 225 | std::string texName = texNames[v2]; 226 | strcpy(mat3ds->texture1_map.name, texName.c_str()); 227 | 228 | const ImageHeader& ih = bmd.tex1.imageHeaders[v2]; 229 | switch(ih.wrapS) 230 | { 231 | case 0: //clamp 232 | //16: clamp texture outside of [0, 1]x[0, 1] 233 | mat3ds->texture1_map.flags = LIB3DS_TEXTURE_DECALE | LIB3DS_TEXTURE_NO_TILE; 234 | break; 235 | case 1: //tile 236 | mat3ds->texture1_map.flags = 0; //tile textures 237 | break; 238 | case 2: //mirror 239 | mat3ds->texture1_map.flags = LIB3DS_TEXTURE_MIRROR; 240 | break; 241 | } 242 | 243 | mat3ds->opacity_map = mat3ds->texture1_map; 244 | if(ih.data->format != 1) 245 | mat3ds->opacity_map.flags |= LIB3DS_TEXTURE_ALPHA_SOURCE; 246 | } 247 | 248 | if(mat.cullIndex != 0xff) 249 | mat3ds->two_sided = bmd.mat3.cullModes[mat.cullIndex] == 0; 250 | 251 | return mat3ds; 252 | } 253 | 254 | static void 255 | traverseSceneGraph(Lib3dsFile* file, const BModel& bmd, const SceneGraph& sg, 256 | Lib3dsNode *&parent, int &material) 257 | { 258 | switch(sg.type) 259 | { 260 | case 0x10: // joint 261 | // XXX - joints 262 | break; 263 | case 0x11: // material 264 | material = bmd.mat3.indexToMatIndex[sg.index]; 265 | break; 266 | case 0x12: // batch 267 | const Batch& currBatch = bmd.shp1.batches[sg.index]; 268 | char buf[8] = { 0 }; 269 | snprintf(buf, sizeof(buf)-1, "n%d", sg.index); 270 | 271 | Lib3dsMesh* m = batchToMesh(currBatch, bmd, buf, material); 272 | 273 | if(m != NULL) 274 | { 275 | Lib3dsNode *node = (Lib3dsNode *) lib3ds_node_new_mesh_instance(m, buf, NULL, NULL, NULL); 276 | lib3ds_file_append_node(file, node, parent); 277 | lib3ds_file_insert_mesh(file, m, -1); 278 | parent = node; 279 | } 280 | 281 | break; 282 | } 283 | 284 | for(size_t i = 0; i < sg.children.size(); ++i) 285 | traverseSceneGraph(file, bmd, sg.children[i], parent, material); 286 | } 287 | 288 | static void 289 | traverseSceneGraph(Lib3dsFile* file, const BModel& bmd, const SceneGraph& sg) 290 | { 291 | int material = 0; 292 | Lib3dsNode *parent = NULL; 293 | traverseSceneGraph(file, bmd, sg, parent, material); 294 | } 295 | 296 | //exports a BModel to a .3ds file 297 | void exportAs3ds(const BModel& bmd, const std::string& filename) 298 | { 299 | std::string folder, basename; 300 | 301 | //strip folders 302 | splitPath(filename, folder, basename); 303 | 304 | size_t i; 305 | 306 | //export textures 307 | std::vector imageNames; 308 | 309 | for(i = 0; i < bmd.tex1.imageHeaders.size(); ++i) 310 | { 311 | char name[9] = { 0 }; // 8.3 format 312 | snprintf(name, sizeof(name), "%.*s%02d", 6, basename.c_str(), i); 313 | std::string texName = name; 314 | 315 | //replace spaces 316 | std::string::size_type pos = texName.find(' '); 317 | while(pos != std::string::npos) 318 | { 319 | texName[pos] = '_'; 320 | pos = texName.find(' ', pos + 1); 321 | } 322 | 323 | saveTexture(TGA, *bmd.tex1.imageHeaders[i].data, folder + texName); 324 | imageNames.push_back(texName + ".tga"); 325 | } 326 | 327 | 328 | Lib3dsFile* file = lib3ds_file_new(); 329 | 330 | //generate materials 331 | for(i = 0; i < bmd.mat3.materials.size(); ++i) 332 | { 333 | std::ostringstream str; 334 | str << "m" << i; 335 | Lib3dsMaterial* mat = materialToMaterial(bmd, bmd.mat3.materials[i], imageNames, str.str()); 336 | lib3ds_file_insert_material(file, mat, i); 337 | } 338 | 339 | //geometry 340 | SceneGraph sg; 341 | buildSceneGraph(bmd.inf1, sg); 342 | traverseSceneGraph(file, bmd, sg); 343 | 344 | lib3ds_file_save(file, filename.c_str()); 345 | lib3ds_file_free(file); 346 | } 347 | -------------------------------------------------------------------------------- /simple_gl.cpp: -------------------------------------------------------------------------------- 1 | #include "simple_gl.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #ifdef _WIN32 9 | #include 10 | #endif 11 | using namespace std; 12 | 13 | #include "GL/glew.h" 14 | 15 | #include "Matrix44.h" 16 | 17 | #include "resource.h" 18 | #include "ui.h" 19 | 20 | #include "parameters.h" 21 | #include "simple_gl_common.h" 22 | #include "camera.h" 23 | 24 | extern bool init(); 25 | extern void draw(); 26 | extern void exit(); 27 | 28 | //in main.cpp (TODO: put that in some header?) 29 | void loadFile(const string& name, bool merge = false); 30 | 31 | static bool g_done = false, g_isInited = false, g_initCalled = false; 32 | #ifdef _WIN32 33 | static HINSTANCE g_hInst = NULL; 34 | static HWND g_hWnd = NULL; 35 | static HDC g_hDc = NULL; 36 | static HGLRC g_hContext = NULL; 37 | static GLYPHMETRICS g_glyphMetrics[256]; 38 | static TEXTMETRIC g_fontMetrics; 39 | #endif 40 | static int g_width = 0, g_height = 0; 41 | GLuint g_fontBase; 42 | 43 | static bool g_isInputOn = false; 44 | static bool g_isInputComplete = true; 45 | static string g_input; 46 | 47 | static string g_startupText; 48 | 49 | int getWindowWidth() 50 | { 51 | return g_width; 52 | } 53 | 54 | int getWindowHeight() 55 | { 56 | return g_height; 57 | } 58 | 59 | bool isInputInProgress() 60 | { 61 | return g_isInputOn; 62 | } 63 | 64 | string getCommand() 65 | { 66 | if(g_isInputComplete) 67 | { 68 | string ret = g_input; 69 | g_input = ""; 70 | return ret; 71 | } 72 | else 73 | return ""; 74 | } 75 | 76 | void flush() 77 | { 78 | glFlush(); 79 | SwapBuffers(g_hDc); 80 | } 81 | 82 | HWND getHWnd() 83 | { 84 | return g_hWnd; 85 | } 86 | 87 | bool isKeyPressed(int key) 88 | { 89 | return (GetAsyncKeyState(key) & 0x8000) != 0; 90 | } 91 | 92 | bool init3D() 93 | { 94 | //get window dc 95 | g_hDc = GetDC(g_hWnd); 96 | if(g_hDc == NULL) 97 | return false; 98 | 99 | int bpp = GetDeviceCaps(g_hDc, BITSPIXEL); 100 | 101 | //set pixel format 102 | PIXELFORMATDESCRIPTOR pfd; 103 | memset(&pfd, 0, sizeof(pfd)); 104 | pfd.nSize = sizeof(pfd); 105 | pfd.nVersion = 1; 106 | pfd.cColorBits = (bpp >= 24)?24:16; 107 | pfd.cAlphaBits = (bpp == 32)?8:0; 108 | pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; 109 | pfd.iPixelType = PFD_TYPE_RGBA; 110 | pfd.cDepthBits = (bpp >= 24)?24:16; 111 | pfd.cStencilBits = (bpp == 32)?8:0; 112 | 113 | int formatIndex = ChoosePixelFormat(g_hDc, &pfd); 114 | 115 | //debug: 116 | DescribePixelFormat(g_hDc, formatIndex, sizeof(pfd), &pfd); 117 | 118 | SetPixelFormat(g_hDc, formatIndex, &pfd); 119 | 120 | //create rendering context 121 | g_hContext = wglCreateContext(g_hDc); 122 | if(g_hContext == NULL) 123 | return false; 124 | 125 | //activate context 126 | if(!wglMakeCurrent(g_hDc, g_hContext)) 127 | return false;; 128 | 129 | //load extensions 130 | glewInit(); 131 | 132 | //load font 133 | g_fontBase = glGenLists(256); 134 | //HFONT old = (HFONT)SelectObject(g_hDc, GetStockObject(DEFAULT_GUI_FONT)); 135 | HFONT old = (HFONT)SelectObject(g_hDc, GetStockObject(OEM_FIXED_FONT)); 136 | wglUseFontBitmaps(g_hDc, 0, 256, g_fontBase); 137 | for(int i = 0; i < 256; ++i) 138 | GetGlyphOutline(g_hDc, i, GGO_METRICS, &g_glyphMetrics[i], 0, NULL, NULL); 139 | GetTextMetrics(g_hDc, &g_fontMetrics); 140 | SelectObject(g_hDc, old); 141 | 142 | return true; 143 | } 144 | 145 | void exit3d() 146 | { 147 | glDeleteLists(g_fontBase, 256); 148 | 149 | wglMakeCurrent(NULL, NULL); 150 | if(g_hContext != NULL) 151 | { 152 | wglDeleteContext(g_hContext); 153 | g_hContext = NULL; 154 | } 155 | if(g_hDc != NULL) 156 | { 157 | ReleaseDC(g_hWnd, g_hDc); 158 | g_hDc = NULL; 159 | } 160 | } 161 | 162 | void centerWindowInWindow(HWND hDlg, HWND hParent) 163 | { 164 | RECT dlg, parent; 165 | GetWindowRect(hDlg, &dlg); 166 | dlg.right -= dlg.left; 167 | dlg.bottom -= dlg.top; 168 | GetWindowRect(hParent, &parent); 169 | parent.right -= parent.left; 170 | parent.bottom -= parent.top; 171 | MoveWindow(hDlg, 172 | parent.left + (parent.right - dlg.right)/2, 173 | parent.top + (parent.bottom - dlg.bottom)/2, 174 | dlg.right, dlg.bottom, FALSE); 175 | } 176 | 177 | BOOL CALLBACK dialogProcAbout(HWND hDlg, UINT msg, WPARAM wP, LPARAM lP) 178 | { 179 | switch(msg) 180 | { 181 | case WM_INITDIALOG: 182 | { 183 | centerWindowInWindow(hDlg, g_hWnd); 184 | 185 | SetWindowText(GetDlgItem(hDlg, IDC_ABOUT_DATE), 186 | (string("Build:\n") + string(__DATE__)).c_str()); 187 | 188 | return TRUE; 189 | } 190 | 191 | case WM_COMMAND: 192 | switch(LOWORD(wP)) 193 | { 194 | case IDOK: 195 | case IDCANCEL: 196 | EndDialog(hDlg, 0); 197 | return TRUE; 198 | } 199 | break; 200 | } 201 | return FALSE; 202 | } 203 | 204 | LRESULT CALLBACK eventListener(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 205 | { 206 | static int lastMouseX = 0, lastMouseY = 0; 207 | static bool isLDown = false; 208 | 209 | switch(message) 210 | { 211 | case WM_SIZE: 212 | { 213 | g_width = (int)LOWORD(lParam); 214 | g_height = (int)HIWORD(lParam); 215 | updateProjectionMatrix(g_width, g_height); 216 | return 0; 217 | }break; 218 | 219 | case WM_PAINT: 220 | { 221 | if(!g_isInited) 222 | { 223 | RECT r; 224 | PAINTSTRUCT ps; 225 | GetClientRect(hWnd, &r); 226 | HDC hDc = BeginPaint(hWnd, &ps); 227 | DrawText(hDc, g_startupText.data(), g_startupText.length(), 228 | &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE); 229 | EndPaint(hWnd, &ps); 230 | 231 | //this allows init() to redraw the window without 232 | //calling itself 233 | if(!g_initCalled) 234 | { 235 | g_initCalled = true; 236 | if(!init()) 237 | { 238 | MessageBox(hWnd, "Something went wrong!", 239 | "ARGH!!!", MB_OK); 240 | 241 | g_done = true; 242 | } 243 | else 244 | { 245 | g_isInited = true; 246 | draw(); 247 | } 248 | } 249 | } 250 | else 251 | { 252 | ValidateRect(hWnd, NULL); 253 | draw(); 254 | } 255 | return 0; 256 | }break; 257 | 258 | case WM_LBUTTONDOWN: 259 | { 260 | isLDown = true; 261 | POINTS p = MAKEPOINTS(lParam); 262 | lastMouseX = p.x; 263 | lastMouseY = p.y; 264 | return 0; 265 | }break; 266 | 267 | case WM_MOUSEMOVE: 268 | { 269 | if(!isLDown) 270 | return 0; 271 | 272 | RECT r; 273 | GetClientRect(hWnd, &r); 274 | POINTS pos = MAKEPOINTS(lParam); 275 | int dx = pos.x - lastMouseX; 276 | int dy = pos.y - lastMouseY; 277 | float radX = 2*PI*float(dx)/float(r.right); 278 | float radY = 2*PI*float(dy)/float(r.bottom); 279 | 280 | turnRight(radX); 281 | turnDown(radY); 282 | 283 | lastMouseX = pos.x; 284 | lastMouseY = pos.y; 285 | return 0; 286 | }break; 287 | 288 | case WM_LBUTTONUP: 289 | { 290 | isLDown = false; 291 | return 0; 292 | }break; 293 | 294 | case WM_CHAR: 295 | { 296 | switch(LOWORD(wParam)) 297 | { 298 | case '\r': 299 | { 300 | if(g_isInputOn) 301 | { 302 | g_isInputOn = false; 303 | g_isInputComplete = true; 304 | } 305 | else 306 | { 307 | g_isInputOn = true; 308 | g_isInputComplete = false; 309 | g_input = ""; 310 | } 311 | }break; 312 | 313 | case '\b': 314 | { 315 | if(g_isInputOn) 316 | g_input = g_input.substr(0, g_input.length() - 1); 317 | }break; 318 | 319 | case 0x1B: //escape 320 | { 321 | if(g_isInputOn) 322 | { 323 | g_isInputOn = false; 324 | g_isInputComplete = true; 325 | g_input = ""; 326 | } 327 | }break; 328 | 329 | default: 330 | { 331 | if(g_isInputOn) 332 | g_input += (char)LOWORD(wParam); 333 | }break; 334 | } 335 | return 0; 336 | }break; 337 | 338 | case WM_DROPFILES: 339 | { 340 | HDROP hDrop = (HDROP)wParam; 341 | char buff[1024]; 342 | DragQueryFile(hDrop, 0, buff, 1024); 343 | 344 | loadFile(buff); 345 | 346 | DragFinish(hDrop); 347 | return 0; 348 | }break; 349 | 350 | case WM_COMMAND: 351 | { 352 | UpdateWindow(hWnd); 353 | 354 | switch(LOWORD(wParam)) 355 | { 356 | case MENU_FILE_OPEN_MODEL: 357 | menuFileOpenModel(); 358 | break; 359 | 360 | case MENU_FILE_MERGE_MODEL: 361 | menuFileMergeModel(); 362 | break; 363 | 364 | case MENU_FILE_OPEN_ANIMATION: 365 | menuFileOpenAnimation(); 366 | break; 367 | 368 | case MENU_FILE_EXPORT_MODEL: 369 | menuFileExportModel(); 370 | break; 371 | 372 | case MENU_FILE_EXPORT_TEXTURES: 373 | menuFileExportTextures(); 374 | break; 375 | 376 | case MENU_FILE_EXIT: 377 | PostQuitMessage(0); 378 | break; 379 | 380 | case MENU_HELP_ABOUT: 381 | DialogBox(g_hInst, MAKEINTRESOURCE(IDD_ABOUT), 382 | g_hWnd, dialogProcAbout); 383 | break; 384 | 385 | case MENU_DEBUG_SECTIONINFO: 386 | menuDebugSectioninfo(); 387 | break; 388 | } 389 | return 0; 390 | }break; 391 | 392 | case WM_DESTROY: 393 | { 394 | PostQuitMessage(0); 395 | return 0; 396 | }break; 397 | 398 | default: 399 | return DefWindowProc(hWnd, message, wParam, lParam); 400 | } 401 | } 402 | 403 | int WINAPI WinMain(HINSTANCE hInst, HINSTANCE old, LPSTR params, int showCom) 404 | { 405 | parseParameters(params); 406 | 407 | WNDCLASSEX wc = { sizeof(wc), CS_HREDRAW | CS_VREDRAW, eventListener, 0, 0, hInst, 408 | LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON)), LoadCursor(NULL, IDC_ARROW), 409 | (HBRUSH)GetStockObject(WHITE_BRUSH), 410 | MAKEINTRESOURCE(IDM_MENU), "glclass", 411 | LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON)) 412 | }; 413 | 414 | if(!RegisterClassEx(&wc)) 415 | return -1; 416 | 417 | //RECT client = { 0, 0, 720, 576 }; //PAL resolution 418 | RECT client = { 0, 0, 1200, 650 }; 419 | AdjustWindowRect(&client, WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, TRUE); 420 | client.right -= client.left; 421 | client.bottom -= client.top; 422 | HWND hWnd = CreateWindowEx(0, "glclass", "bmdview2", WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 423 | 0, 0, client.right, client.bottom, 424 | NULL, NULL, hInst, NULL); 425 | 426 | if(hWnd == NULL) 427 | return -1; 428 | 429 | g_hInst = hInst; 430 | g_hWnd = hWnd; 431 | g_done = !init3D(); 432 | 433 | ShowWindow(hWnd, showCom); //SW_MAXIMIZE); 434 | 435 | if(g_done) 436 | MessageBox(hWnd, "OpenGL couldn't be set up", "ARGH!!!", MB_OK); 437 | 438 | DragAcceptFiles(hWnd, TRUE); 439 | 440 | MSG msg; 441 | DWORD startTime = GetTickCount(); 442 | while(!g_done) 443 | { 444 | if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 445 | { 446 | if(msg.message == WM_QUIT) 447 | g_done = true; 448 | TranslateMessage(&msg); 449 | DispatchMessage(&msg); 450 | } 451 | else 452 | { 453 | startTime = GetTickCount(); 454 | 455 | if(g_isInputComplete) 456 | handleCamera(); 457 | 458 | draw(); 459 | setLastFrameSeconds((GetTickCount() - startTime)/1000.0f); 460 | } 461 | } 462 | 463 | exit(); 464 | exit3d(); 465 | 466 | return msg.wParam; 467 | } 468 | -------------------------------------------------------------------------------- /shp1.cpp: -------------------------------------------------------------------------------- 1 | #include "shp1.h" 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | namespace bmd 8 | { 9 | 10 | struct Shp1Header 11 | { 12 | char tag[4]; 13 | u32 sizeOfSection; 14 | u16 batchCount; //number of batches 15 | u16 pad; //?? 16 | u32 offsetToBatches; //should be 0x2c (batch info starts here) 17 | 18 | u32 offsetUnknown; //?? 19 | u32 zero; //?? 20 | u32 offsetToBatchAttribs; //batch vertex attrib start 21 | 22 | //The matrixTable is an array of u16, which maps from the matrix data indices 23 | //to Drw1Data arrays indices. If a batch contains multiple packets, for the 24 | //2nd, 3rd, ... packet this array may contain 0xffff values, which means that 25 | //the corresponding index from the previous packet should be used. 26 | u32 offsetToMatrixTable; 27 | 28 | 29 | u32 offsetData; //start of the actual primitive data 30 | u32 offsetToMatrixData; 31 | u32 offsetToPacketLocations; //offset to packet start/length info 32 | 33 | //(all offsets relative to Shp1Header start) 34 | }; 35 | 36 | //Shp1Header.batchCount many of these structs are 37 | //stored at Shp1Header.offsetToBatches 38 | struct Batch 39 | { 40 | u8 matrixType; //0, 1, 2, 3 "matrix type" according to yazor 41 | //0 seems to be the default value 42 | 43 | //1 could mean y-billboard (mo_fire02.bmd, default.bmd) 44 | //or billboard (geostar.bmd) 45 | //RESOLVED: I just checked in sunshine, default.bmd always 46 | //faces the viewer, so this means billboard 47 | //(camera plane billboards, not camera centered billboards) 48 | 49 | //2 is probably y-billboard (see snowman1.bmd), looks like 50 | //that in mario kart in-game as well 51 | 52 | //3 is about as frequent as 1, but i have no idea what this 53 | //could mean (don't inherit parent transform?) (hum, yaz0r's 54 | //code suggests that this means multimatrix transform... 55 | //but that would conlict with my understanding of DRW1 and EVP1...) 56 | //-> probably means multimatrix 57 | 58 | u8 unknown2; //seems to be always 0xff 59 | u16 packetCount; //number of packets belonging to this batch 60 | 61 | //attribs used for the strips in this batch. relative to 62 | //Shp1Header.offsetToBatchAttribs 63 | //Read StripTypes until you encounter an 0x000000ff/0x00000000, 64 | //for all these types indices are included. If, for example, 65 | //a Batch has types (9, 3), (a, 3), (0xff, 0), then for this batch two shorts (= 3) 66 | //are stored per vertex: position index and normal index 67 | u16 offsetToAttribs; 68 | 69 | u16 firstMatrixData; //index to first matrix data (packetCount consecutive indices) 70 | u16 firstPacketLocation; //index to first packet location (packetCount consecutive indices) 71 | 72 | 73 | u16 unknown3; //0xffff 74 | 75 | f32 unknown4; 76 | 77 | //some kind of bounding box (in which coord frame?) 78 | f32 bbMin[3]; 79 | f32 bbMax[3]; 80 | }; 81 | 82 | struct BatchAttrib 83 | { 84 | u32 attrib; //cf. ArrayFormat.arrayType 85 | u32 dataType; //cf. ArrayFormat.dataType (always bytes or shorts...) 86 | }; 87 | 88 | //for every packet a PacketLocation struct is stored at 89 | //Shp1Header.offsetToPacketLocation + Batch.firstPacketLocation*sizeof(PacketLocation). 90 | //This struct stores where the primitive data for this packet is stored in the 91 | //data block. 92 | struct PacketLocation 93 | { 94 | u32 size; //size in bytes of packet 95 | u32 offset; //relative to Shp1Header.offsetData 96 | }; 97 | 98 | struct Primitive 99 | { 100 | u8 primitiveType; //see above 101 | u16 numVertices; //that many vertices included in this primitive - for 102 | //each vertex indices are stored according to batch type 103 | }; 104 | 105 | //for every packet a MatrixData struct is stored at 106 | //Shp1Header.offsetToMatrixData + Batch.firstMatrixData*sizeof(MatrixData). 107 | //This struct stores which part of the MatrixTable belongs to this packet 108 | //(the matrix table is stored at Shp1Header.offsetToMatrixTable) 109 | struct MatrixData //from yaz0r's source (animation stuff) 110 | { 111 | u16 unknown1; 112 | u16 count; //count many consecutive indices into matrixTable 113 | u32 firstIndex; //first index into matrix table 114 | }; 115 | 116 | 117 | typedef vector BatchAttribs; 118 | 119 | }; 120 | 121 | bmd::BatchAttribs getBatchAttribs(FILE* f, int off) 122 | { 123 | int old = ftell(f); 124 | 125 | fseek(f, off, SEEK_SET); 126 | 127 | bmd::BatchAttribs ret; 128 | 129 | bmd::BatchAttrib attrib; 130 | readDWORD(f, attrib.attrib); 131 | readDWORD(f, attrib.dataType); 132 | 133 | while(attrib.attrib != 0xff) 134 | { 135 | ret.push_back(attrib); 136 | 137 | readDWORD(f, attrib.attrib); 138 | readDWORD(f, attrib.dataType); 139 | } 140 | 141 | fseek(f, old, SEEK_SET); 142 | return ret; 143 | }; 144 | 145 | void dumpPacketPrimitives(const bmd::BatchAttribs& attribs, int dataSize, FILE* f, Packet& dst) 146 | { 147 | bool done = false; 148 | int readBytes = 0; 149 | 150 | while(!done) 151 | { 152 | u8 type; 153 | fread(&type, 1, 1, f); 154 | ++readBytes; 155 | 156 | if(type == 0 || readBytes >= dataSize) 157 | { 158 | done = true; 159 | continue; 160 | } 161 | 162 | dst.primitives.push_back(Primitive()); 163 | Primitive& currPrimitive = dst.primitives.back(); 164 | currPrimitive.type = type; 165 | 166 | u16 count; 167 | readWORD(f, count); 168 | readBytes += 2; 169 | 170 | currPrimitive.points.resize(count); 171 | 172 | for(int j = 0; j < count; ++j) 173 | { 174 | Index& currPoint = currPrimitive.points[j]; 175 | 176 | for(size_t k = 0; k < attribs.size(); ++k) 177 | { 178 | u16 val; 179 | 180 | //get value 181 | switch(attribs[k].dataType) 182 | { 183 | case 1: //s8 184 | { 185 | u8 tmp; 186 | fread(&tmp, 1, 1, f); 187 | val = tmp; 188 | readBytes += 1; 189 | }break; 190 | 191 | case 3: //s16 192 | { 193 | readWORD(f, val); 194 | readBytes += 2; 195 | }break; 196 | 197 | default: 198 | assert(false && "shp1: got invalid data type in packet. should never happen because " 199 | "dumpBatch() should check this before calling dumpPacket()"); 200 | } 201 | 202 | //set appropriate index 203 | switch(attribs[k].attrib) 204 | { 205 | case 0: 206 | currPoint.matrixIndex = val; 207 | break; 208 | 209 | case 9: 210 | currPoint.posIndex = val; 211 | break; 212 | 213 | case 0xa: 214 | currPoint.normalIndex = val; 215 | break; 216 | 217 | case 0xb: 218 | case 0xc: 219 | currPoint.colorIndex[attribs[k].attrib - 0xb] = val; 220 | break; 221 | 222 | case 0xd: 223 | case 0xe: 224 | case 0xf: 225 | case 0x10: 226 | case 0x11: 227 | case 0x12: 228 | case 0x13: 229 | case 0x14: 230 | currPoint.texCoordIndex[attribs[k].attrib - 0xd] = val; 231 | break; 232 | 233 | default: 234 | //assert(false && "shp1: got invalid attrib in packet. should never happen because " 235 | //"dumpBatch() should check this before calling dumpPacket()"); 236 | 237 | ; //ignore unknown types, it's enough to warn() in dumpBatch 238 | } 239 | } 240 | } 241 | } 242 | } 243 | 244 | void dumpBatch(const bmd::Batch& batch, const bmd::Shp1Header& h, FILE* f, long baseOffset, Batch& dst) 245 | { 246 | size_t i; 247 | 248 | dst.bbMin.setXYZ(batch.bbMin[0], batch.bbMin[1], batch.bbMin[2]); 249 | dst.bbMax.setXYZ(batch.bbMax[0], batch.bbMax[1], batch.bbMax[2]); 250 | dst.matrixType = batch.matrixType; 251 | 252 | //read and interpret batch vertex attribs 253 | bmd::BatchAttribs attribs = getBatchAttribs(f, 254 | baseOffset + h.offsetToBatchAttribs + batch.offsetToAttribs); 255 | 256 | dst.attribs.hasMatrixIndices = dst.attribs.hasPositions = dst.attribs.hasNormals = false; 257 | for(i = 0; i < 2; ++i) dst.attribs.hasColors[i] = false; 258 | for(i = 0; i < 8; ++i) dst.attribs.hasTexCoords[i] = false; 259 | for(i = 0; i < attribs.size(); ++i) 260 | { 261 | if(attribs[i].dataType != 1 && attribs[i].dataType != 3) 262 | { 263 | warn("shp1, dumpBatch(): unknown attrib data type %d, skipping batch", attribs[i].dataType); 264 | return; 265 | } 266 | 267 | switch(attribs[i].attrib) 268 | { 269 | case 0: 270 | dst.attribs.hasMatrixIndices = true; 271 | break; 272 | 273 | case 9: 274 | dst.attribs.hasPositions = true; 275 | break; 276 | 277 | case 0xa: 278 | dst.attribs.hasNormals = true; 279 | break; 280 | 281 | case 0xb: 282 | case 0xc: 283 | dst.attribs.hasColors[attribs[i].attrib - 0xb] = true; 284 | break; 285 | 286 | case 0xd: 287 | case 0xe: 288 | case 0xf: 289 | case 0x10: 290 | case 0x11: 291 | case 0x12: 292 | case 0x13: 293 | case 0x14: 294 | dst.attribs.hasTexCoords[attribs[i].attrib - 0xd] = true; 295 | break; 296 | 297 | default: 298 | warn("shp1, dumpBatch(): unknown attrib %d in batch, it might not display correctly", attribs[i].attrib); 299 | //return; //it's enough to warn 300 | } 301 | } 302 | 303 | //read packets 304 | dst.packets.resize(batch.packetCount); 305 | for(i = 0; i < batch.packetCount; ++i) 306 | { 307 | //read packet location for current packet 308 | bmd::PacketLocation packetLocation; 309 | fseek(f, baseOffset + h.offsetToPacketLocations 310 | + (batch.firstPacketLocation + i)*sizeof(packetLocation), SEEK_SET); 311 | readDWORD(f, packetLocation.size); 312 | readDWORD(f, packetLocation.offset); 313 | 314 | //read packet's primitives 315 | Packet& dstPacket = dst.packets[i]; 316 | fseek(f, baseOffset + h.offsetData + packetLocation.offset, SEEK_SET); 317 | dumpPacketPrimitives(attribs, packetLocation.size, f, dstPacket); 318 | 319 | //read matrix data for current packet 320 | bmd::MatrixData matrixData; 321 | fseek(f, baseOffset + h.offsetToMatrixData 322 | + (batch.firstMatrixData + i)*sizeof(matrixData), SEEK_SET); 323 | readWORD(f, matrixData.unknown1); //TODO: figure this out... 324 | readWORD(f, matrixData.count); 325 | readDWORD(f, matrixData.firstIndex); 326 | 327 | //read packet's matrix table 328 | dstPacket.matrixTable.resize(matrixData.count); 329 | fseek(f, baseOffset + h.offsetToMatrixTable 330 | + 2*matrixData.firstIndex, SEEK_SET); 331 | for(int j = 0; j < matrixData.count; ++j) 332 | readWORD(f, dstPacket.matrixTable[j]); 333 | } 334 | } 335 | 336 | void readShp1Header(FILE* f, bmd::Shp1Header& h) 337 | { 338 | fread(h.tag, 1, 4, f); 339 | readDWORD(f, h.sizeOfSection); 340 | readWORD(f, h.batchCount); 341 | readWORD(f, h.pad); 342 | readDWORD(f, h.offsetToBatches); 343 | readDWORD(f, h.offsetUnknown); 344 | readDWORD(f, h.zero); 345 | readDWORD(f, h.offsetToBatchAttribs); 346 | readDWORD(f, h.offsetToMatrixTable); 347 | 348 | readDWORD(f, h.offsetData); 349 | readDWORD(f, h.offsetToMatrixData); 350 | readDWORD(f, h.offsetToPacketLocations); 351 | } 352 | 353 | void readBatch(FILE* f, bmd::Batch& d) 354 | { 355 | fread(&d.matrixType, 1, 1, f); 356 | fread(&d.unknown2, 1, 1, f); 357 | readWORD(f, d.packetCount); 358 | readWORD(f, d.offsetToAttribs); 359 | readWORD(f, d.firstMatrixData); 360 | readWORD(f, d.firstPacketLocation); 361 | readWORD(f, d.unknown3); 362 | readFLOAT(f, d.unknown4); 363 | for(int i = 0; i < 3; ++i) 364 | readFLOAT(f, d.bbMin[i]); 365 | for(int j = 0; j < 3; ++j) 366 | readFLOAT(f, d.bbMax[j]); 367 | } 368 | 369 | void dumpShp1(FILE* f, Shp1& dst) 370 | { 371 | int shp1Offset = ftell(f), i; 372 | 373 | bmd::Shp1Header h; 374 | readShp1Header(f, h); 375 | 376 | //read batches 377 | fseek(f, h.offsetToBatches + shp1Offset, SEEK_SET); 378 | dst.batches.resize(h.batchCount); 379 | for(i = 0; i < h.batchCount; ++i) 380 | { 381 | bmd::Batch d; 382 | readBatch(f, d); 383 | 384 | Batch& dstBatch = dst.batches[i]; 385 | 386 | long filePos = ftell(f); 387 | dumpBatch(d, h, f, shp1Offset, dstBatch); 388 | fseek(f, filePos, SEEK_SET); 389 | } 390 | } 391 | 392 | void writeShp1Info(FILE* f, ostream& out) 393 | { 394 | out << string(50, '/') << endl 395 | << "//Shp1 section" << endl 396 | << string(50, '/') << endl << endl; 397 | 398 | 399 | int shp1Offset = ftell(f), i; 400 | 401 | bmd::Shp1Header h; 402 | readShp1Header(f, h); 403 | 404 | //read batches 405 | out << "Batches (VERY incomplete)" << endl; 406 | fseek(f, h.offsetToBatches + shp1Offset, SEEK_SET); 407 | for(i = 0; i < h.batchCount; ++i) 408 | { 409 | bmd::Batch d; 410 | readBatch(f, d); 411 | 412 | //very incomplete output ;-) 413 | out << " matrixType(?) " << hex << (int)d.matrixType 414 | << " unknown2 " << (int)d.unknown2 415 | << " numPackets " << d.packetCount 416 | << endl << " " 417 | << " unknown4 " << d.unknown4 418 | << endl << " " 419 | << " bbMin " << d.bbMin[0] << ", " << d.bbMin[1] << ", " << d.bbMin[2] 420 | << endl << " " 421 | << " bbMax " << d.bbMax[0] << ", " << d.bbMax[1] << ", " << d.bbMax[2] 422 | << endl << endl; 423 | } 424 | 425 | out << endl; 426 | } 427 | -------------------------------------------------------------------------------- /tev.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | Gamecube-Flipper semidocumentation 9 | 10 | 11 | 12 | 13 |

Flipper

14 | 15 |

Overview

16 | 17 |

This describes how some parts of Flipper (the graphics chip of the Gamecube). I’ll probably add more descriptions if you want. I don’t describe how indirect tev works, because I don’t know that. If you know, tell me :-) If some of the things discussed in this document are wrong, let me know as well.

18 | 19 |

This document would not have been possible without libogc, especially gx.h and gx.c.

20 | 21 |

Texgen

22 | 23 |

You don’t always need explicit texture coordinates if you want to use a texture. For example, you could compute texture coordinates from the vertex positions, or you could use one set of texture coordinates for different texture units. Texgen (‘texture coord generation’) is a means to accomplish that. This section discusses the texgen unit.

24 | 25 |

You call

26 | 27 |
void GX_SetNumTexGens(u32 nr);
 28 | 
29 | 30 |

to specify for how many texture units you want to enable texgen for.

31 | 32 |

To control how texture coordinates are computed, you call

33 | 34 |
void GX_SetTexCoordGen(u16 texcoord,u32 tgen_typ,u32 tgen_src,u32 mtxsrc);
 35 | 
36 | 37 |

texcoord can be one of

38 | 39 |
GX_TEXCOORD0
 40 | GX_TEXCOORD1
 41 | GX_TEXCOORD2
 42 | GX_TEXCOORD3
 43 | GX_TEXCOORD4
 44 | GX_TEXCOORD5
 45 | GX_TEXCOORD6
 46 | GX_TEXCOORD7
 47 | 
48 | 49 |

and specifies texgen for which texture coordinate you’re going to configure.

50 | 51 |

tgen_typ is used to specify how texture coordinates are generated from the inputs. I don’t really know how all these functions work, but I provide a few guesses. I have no idea at all how the BUMP stuff works.

52 | 53 |
GX_TG_MTX3x4
 54 |     output = 3x4matrix * input (u, v, w generated)
 55 | 
 56 | GX_TG_MTX2x4
 57 |     output = 2x4matrix * input (u, v generated)
 58 | 
 59 | GX_TG_BUMP0
 60 | GX_TG_BUMP1
 61 | GX_TG_BUMP2
 62 | GX_TG_BUMP3
 63 | GX_TG_BUMP4
 64 | GX_TG_BUMP5
 65 | GX_TG_BUMP6
 66 | GX_TG_BUMP7
 67 | GX_TG_SRTG
 68 |     I believe this is 'spherical reflection coord generation' like
 69 |     implemented in OpenGL
 70 | 
71 | 72 |

tgen_src is used to tell the texgen unit what is the input for the generation function. I don’t know what the TG_TEXn constants are for, the rest is self-explanatory.

73 | 74 |
GX_TG_POS
 75 | GX_TG_NRM
 76 | GX_TG_BINRM
 77 | GX_TG_TANGENT
 78 | GX_TG_TEX0
 79 | GX_TG_TEX1
 80 | GX_TG_TEX2
 81 | GX_TG_TEX3
 82 | GX_TG_TEX4
 83 | GX_TG_TEX5
 84 | GX_TG_TEX6
 85 | GX_TG_TEX7
 86 | GX_TG_TEXCOORD0
 87 | GX_TG_TEXCOORD1
 88 | GX_TG_TEXCOORD2
 89 | GX_TG_TEXCOORD3
 90 | GX_TG_TEXCOORD4
 91 | GX_TG_TEXCOORD5
 92 | GX_TG_TEXCOORD6
 93 | GX_TG_COLOR0
 94 | GX_TG_COLOR1
 95 | 
96 | 97 |

There is another function to control texgen that provides a few more options:

98 | 99 |
void GX_SetTexCoordGen2(u16 texcoord,u32 tgen_typ,u32 tgen_src,u32 mtxsrc,
100 |                         u32 normalize,u32 postmtx);
101 | 
102 | 103 |

This is like GX_SetTexCoordGen with a few additional parameters. I don’t know what this does (I guess it does the same as GX_SetTexCoordGen, but optionally normalizes the generated texture coordinate to unit length and multiplies it with a post processing matrix).

104 | 105 |

TEV

106 | 107 |

The Gamecube doesn’t really have a programmable graphics pipeline, only a set of 16 TEV stages that can combine colors in a fixed way. A single TEV stage has 4 inputs and 1 output. The output is calculated by selecting one of several fixed functions on the inputs. In only discuss the ‘color’ functions, the alpha functions are very similar. You should be able to figure them out yourself by looking at gx.h.

108 | 109 |

In the remainder, the 4 inputs of a tev a called a, b, c and d, the output is called out.

110 | 111 |

GX_SetNumTevStages

112 | 113 |

You configure how many of the 16 tev stages are used by calling

114 | 115 |
void GX_SetNumTevStages(u8 num);
116 | 
117 | 118 |

For color computation, the first num tev stages are executed. The color which ends up in the ‘previous color register’ at the end of these num stages is used as the color for the current pixel.

119 | 120 |

GX_SetTevColorOp

121 | 122 |

The central function to configure a tev unit is

123 | 124 |
void GX_SetTevColorOp(u8 tevstage,u8 tevop,u8 tevbias,u8 tevscale,u8 clamp,u8 tevregid);
125 | 
126 | 127 |

tevstage is one of GX_TEVSTAGE0 to GX_TEVSTAGE15 and identifies the tev unit you want to configure.

128 | 129 |

tevop is one of the following functions:

130 | 131 |
GX_TEV_ADD
132 |     out = a*(1 - c) + b*c + d
133 | 
134 | GX_TEV_SUB
135 |     out = -a*(1 - c) - b*c + d  (not 100% sure with this, please confirm)
136 | 
137 | GX_TEV_COMP_R8_GT
138 |     if(a.r > b.r) out = c; else out = d;
139 | 
140 | GX_TEV_COMP_R8_EQ
141 |     if(a.r == b.r) out = c; else out = d;
142 | 
143 | GX_TEV_COMP_GR16_GT
144 | GX_TEV_COMP_GR16_EQ
145 |     same as r8, but use gr as 16 bit value for comparing
146 | 
147 | GX_TEV_COMP_BGR24_GT
148 | GX_TEV_COMP_BGR24_EQ
149 |     same as r8, but use bgr as 24 bit value for comparing
150 | 
151 | GX_TEV_COMP_RGB8_GT
152 | GX_TEV_COMP_RGB8_EQ
153 |     Not sure with this, but I think this compares rgb independently. Can
154 |     you confirm this? (this is not done correctly in bmdview's source)
155 | 
156 | 157 |

and controls which operation the tev stage will perform (discussed in more detail below).

158 | 159 |

tevbias is one of

160 | 161 |
GX_TB_ZERO
162 | GX_TB_ADDHALF
163 | GX_TB_SUBHALF
164 | 
165 | 166 |

tevscale is one of

167 | 168 |
GX_CS_SCALE_1
169 | GX_CS_SCALE_2
170 | GX_CS_SCALE_4
171 | GX_CS_DIVIDE_2
172 | 
173 | 174 |

clamp is GX_TRUE or GX_FALSE.

175 | 176 |

After the tev operation has executed, the output is modified by bias, scale and clamp as follows (in this order):

177 | 178 |
out = out + bias; //for each component; bias is 0, 0.5 or -0.5
179 | out = out * scale; //scale is 1, 2, 4 or 0.5
180 | if(clamp)
181 |   clamp out to [0.0, 1.0] in each component
182 | 
183 | 184 |

If tev op is not GX_TEV_ADD or GX_TEV_SUB, bias, scale and clamp shall be ignored.

185 | 186 |

tevregid is one of

187 | 188 |
GX_TEVPREV
189 |     regPrev.rgb = out.rgb
190 | 
191 | GX_TEVREG0
192 |     reg0.rgb = out.rgb
193 | 
194 | GX_TEVREG1
195 |     reg1.rgb = out.rgb
196 | 
197 | GX_TEVREG2
198 |     reg2.rgb = out.rgb
199 | 
200 | 201 |

and specifies in which register out is written.

202 | 203 |

GX_SetTevColorIn

204 | 205 |

To control which are the 4 inputs of a tev stage, you call

206 | 207 |
void GX_SetTevColorIn(u8 tevstage,u8 a,u8 b,u8 c,u8 d);
208 | 
209 | 210 |

tevstage is again a tev stage identifier.

211 | 212 |

a, b, c and d specify which is loaded into a, b, c and d in the equations above:

213 | 214 |
GX_CC_CPREV
215 |     a.rgb = regPrev.rgb
216 | 
217 | GX_CC_APREV
218 |     a.rgb = regPrev.aaa
219 | 
220 | GX_CC_C0
221 |     a.rgb = reg0.rgb
222 | 
223 | GX_CC_A0
224 |     a.rgb = reg0.aaa
225 | 
226 | GX_CC_C1
227 |     a.rgb = reg1.rgb
228 | 
229 | GX_CC_A1
230 |     a.rgb = reg1.aaa
231 | 
232 | GX_CC_C2
233 |     a.rgb = reg2.rgb
234 | 
235 | GX_CC_A2
236 |     a.rgb = reg2.aaa
237 | 
238 | GX_CC_TEXC
239 |     a.rgb = textureBoundToTev.rgb (see GX_SetTevOrder below)
240 | 
241 | GX_CC_TEXA
242 |     a.rgb = textureBoundToTev.rgb (see GX_SetTevOrder below)
243 | 
244 | GX_CC_RASC
245 |     a.rgb = rasColor.rgb (see below)
246 | 
247 | GX_CC_RASA
248 |     a.rgb = rasColor.aaa (see below)
249 | 
250 | GX_CC_ONE
251 |     a.rgb = (1.0, 1.0, 1.0)
252 | 
253 | GX_CC_HALF
254 |     a.rgb = (0.5, 0.5, 0.5)
255 | 
256 | GX_CC_KONST
257 |     a.rgb = konst (see GX_SetTevKColorSel below)
258 | 
259 | GX_CC_ZERO
260 |     a.rgb = (0.0, 0.0, 0.0)
261 | 
262 | 263 |

In the list above, a is of course only used as an example — could be b, c or d as well :-P

264 | 265 |

rasColor is the color which is passed from the ‘vertex shader’, usually this is simply the vertex color. More information later if you need it (TODO: see GX_SetTevOrder?).

266 | 267 |

GX_SetTevOrder

268 | 269 |

If you use GX_CC_TEXC, GX_CC_TEXA, GX_CC_RASC or GX_CC_RASA in GX_SetTevColorIn, you use

270 | 271 |
void GX_SetTevOrder(u8 tevstage,u8 texcoord,u32 texmap,u8 color);
272 | 
273 | 274 |

to specify which texture is used for the texture input and which color for the ras color.

275 | 276 |

tevstage is the usual tev stage identifier.

277 | 278 |

texcoord is one of

279 | 280 |
GX_TEXCOORD0
281 | GX_TEXCOORD1
282 | GX_TEXCOORD2
283 | GX_TEXCOORD3
284 | GX_TEXCOORD4
285 | GX_TEXCOORD5
286 | GX_TEXCOORD6
287 | GX_TEXCOORD7
288 | GX_TEXCOORDNULL
289 | 
290 | 291 |

and tells the tev unit which texture coordinate channel is used to access the texture. Note: The texture coordinate after texgen is used (texgen is not described in this document yet — for now pretend that this makes no difference). Use GX_TEXCOORDNULL if you don’t need a texture input for the tev unit.

292 | 293 |

texmap is

294 | 295 |
GX_TEXMAP0
296 | GX_TEXMAP1
297 | GX_TEXMAP2
298 | GX_TEXMAP3
299 | GX_TEXMAP4
300 | GX_TEXMAP5
301 | GX_TEXMAP6
302 | GX_TEXMAP7
303 | GX_TEXMAP_NULL
304 | 
305 | 306 |

and specifies which texture image should be used as input for tevstage. Pass GX_TEXMAP_NULL if the texture input is not used in the current tev.

307 | 308 |

I’m pretty sure that color is one of

309 | 310 |
GX_COLOR0
311 | GX_COLOR1
312 | GX_ALPHA0
313 | GX_ALPHA1
314 | GX_COLOR0A0
315 | GX_COLOR1A1
316 | GX_COLORZERO
317 | GX_BUMP 
318 | GX_BUMPN
319 | GX_COLORNULL
320 | 
321 | 322 |

but I don’t really know what all these constants mean. The do specify what is used as ras color in the tev. I guess COLOR0 and COLOR1 are simply the primary and secondary vertex color, but I don’t know how the bump stuff works.

323 | 324 |

GX_SetTevKColorSel

325 | 326 |

If GX_CC_KONST is used as one of the 4 inputs to a tev stage in GX_SetTevColorIn, you specify which constant is used with

327 | 328 |
void GX_SetTevKColorSel(u8 tevstage,u8 sel);
329 | 
330 | 331 |

tevstage is the usual tev stage identifier.

332 | 333 |

sel is one of

334 | 335 |
GX_TEV_KCSEL_1
336 | GX_TEV_KCSEL_7_8
337 | GX_TEV_KCSEL_3_4
338 | GX_TEV_KCSEL_5_8
339 | GX_TEV_KCSEL_1_2
340 | GX_TEV_KCSEL_3_8
341 | GX_TEV_KCSEL_1_4
342 | GX_TEV_KCSEL_1_8
343 | GX_TEV_KCSEL_K0
344 | GX_TEV_KCSEL_K1
345 | GX_TEV_KCSEL_K2
346 | GX_TEV_KCSEL_K3
347 | GX_TEV_KCSEL_K0_R
348 | GX_TEV_KCSEL_K1_R
349 | GX_TEV_KCSEL_K2_R
350 | GX_TEV_KCSEL_K3_R
351 | GX_TEV_KCSEL_K0_G
352 | GX_TEV_KCSEL_K1_G
353 | GX_TEV_KCSEL_K2_G
354 | GX_TEV_KCSEL_K3_G
355 | GX_TEV_KCSEL_K0_B
356 | GX_TEV_KCSEL_K1_B
357 | GX_TEV_KCSEL_K2_B
358 | GX_TEV_KCSEL_K3_B
359 | GX_TEV_KCSEL_K0_A
360 | GX_TEV_KCSEL_K1_A
361 | GX_TEV_KCSEL_K2_A
362 | GX_TEV_KCSEL_K3_A
363 | 
364 | 365 |

The first few functions are simply the constants 1.0, 7.0/8.0, …, 1.0/8.0. The other constants select which of the 4 constant color registers are used. You load values in the constant color registers with XXX.

366 | 367 |

GX_SetTevColor

368 | 369 |

To load a color into a tev register, you call

370 | 371 |
void GX_SetTevColor(u8 tev_regid,GXColor color);
372 | 
373 | 374 |

tev_regid is a tev register identifier — these registers are shared by all tev stages. Valid values are

375 | 376 |
TODO (I guess values from 0 to 3 for the 4 constant color registers)
377 | 
378 | 379 |

color is the color to load into tev_regid. Who would have thought?

380 | 381 |

Swap Mode Tables

382 | 383 |

No bmd/bdl file I know of uses the stuff described in this section. It’s not that important to emulate it I guess ;-)

384 | 385 |

You can swizzle the ras color and the tex colors before they are given to the tev stages. For this, there are four different swap mode tables,

386 | 387 |
GX_TEV_SWAP0
388 | GX_TEV_SWAP1
389 | GX_TEV_SWAP2
390 | GX_TEV_SWAP3
391 | 
392 | 393 |

which are configures via GX_SetTevSwapModeTable (below). To use these tables, you call

394 | 395 |
void GX_SetTevSwapMode(u8 tevstage,u8 ras_sel,u8 tex_sel);
396 | 
397 | 398 |

tevstage is the usual tev stage id.

399 | 400 |

ras_sel is the swap mode table identifier to use for the ras color.

401 | 402 |

tex_sel is the swap mode table identifier to use for the tex color.

403 | 404 |

To configure what a swap mode table does, you call

405 | 406 |
void GX_SetTevSwapModeTable(u8 swapid,u8 r,u8 g,u8 b,u8 a);
407 | 
408 | 409 |

swapid is a swap mode table identifier.

410 | 411 |

r, g, b and a specify which channel of the original color should be passed to the tev stage as r, g, b or a. Valid values are

412 | 413 |
GX_CH_RED
414 | GX_CH_GREEN
415 | GX_CH_BLUE
416 | GX_CH_ALPHA
417 | 
418 | 419 |

Examples

420 | 421 |

TODO. For now, void GX_SetTevOp(u8 tevstage,u8 mode) from gx.c is a good example.

422 | 423 |

By thakis@users.sourceforge.net.

424 | 425 | 426 | 427 | --------------------------------------------------------------------------------