├── .gitignore ├── icons ├── cutter.png ├── icon_bg.png ├── file_open.png ├── file_save.png ├── item_add.png ├── scarlet2.png ├── view_orbit.png ├── auto_center.png ├── home_center.png ├── icon_expand.png ├── item_delete.png ├── replace_item.png ├── scarlet-logo.png ├── view_backcull.png ├── view_wiremesh.png ├── icon_rotate_left.png ├── icon_rotate_right.png ├── tool_paint_hitmap.png └── view_firstperson.png ├── .dep.inc ├── ContextClass.cpp ├── global.h ├── ColorPreview.h ├── about.h ├── ShaderClass.h ├── common ├── common.h ├── timloader.h ├── smx.h ├── timloader.cpp ├── common.cpp └── smx.cpp ├── ConfigClass.h ├── ContextClass.h ├── configform.h ├── TextureClass.h ├── .github └── workflows │ └── linux-build.yml ├── texturepreview.h ├── glwidget.h ├── nbproject ├── project.xml ├── Package-Debug.bash ├── Package-Release.bash ├── Package-Debug-linux.bash ├── Package-Release-linux.bash ├── Makefile-variables.mk ├── Makefile-impl.mk ├── Makefile-Release-linux.mk ├── Makefile-Debug-linux.mk ├── Makefile-Release.mk └── Makefile-Debug.mk ├── about.fl ├── ModelClass.h ├── configform.fl ├── TextureClass.cpp ├── README.md ├── mainform.h ├── configform.cxx ├── ConfigClass.cpp ├── Makefile ├── ShaderClass.cpp ├── ColorPreview.cpp ├── main.cpp ├── mainform.fl ├── texturepreview.cpp ├── glwidget.cpp └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | build 3 | scrap 4 | nbproject/private -------------------------------------------------------------------------------- /icons/cutter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lameguy64/smxtool/HEAD/icons/cutter.png -------------------------------------------------------------------------------- /icons/icon_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lameguy64/smxtool/HEAD/icons/icon_bg.png -------------------------------------------------------------------------------- /icons/file_open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lameguy64/smxtool/HEAD/icons/file_open.png -------------------------------------------------------------------------------- /icons/file_save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lameguy64/smxtool/HEAD/icons/file_save.png -------------------------------------------------------------------------------- /icons/item_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lameguy64/smxtool/HEAD/icons/item_add.png -------------------------------------------------------------------------------- /icons/scarlet2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lameguy64/smxtool/HEAD/icons/scarlet2.png -------------------------------------------------------------------------------- /icons/view_orbit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lameguy64/smxtool/HEAD/icons/view_orbit.png -------------------------------------------------------------------------------- /icons/auto_center.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lameguy64/smxtool/HEAD/icons/auto_center.png -------------------------------------------------------------------------------- /icons/home_center.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lameguy64/smxtool/HEAD/icons/home_center.png -------------------------------------------------------------------------------- /icons/icon_expand.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lameguy64/smxtool/HEAD/icons/icon_expand.png -------------------------------------------------------------------------------- /icons/item_delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lameguy64/smxtool/HEAD/icons/item_delete.png -------------------------------------------------------------------------------- /icons/replace_item.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lameguy64/smxtool/HEAD/icons/replace_item.png -------------------------------------------------------------------------------- /icons/scarlet-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lameguy64/smxtool/HEAD/icons/scarlet-logo.png -------------------------------------------------------------------------------- /icons/view_backcull.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lameguy64/smxtool/HEAD/icons/view_backcull.png -------------------------------------------------------------------------------- /icons/view_wiremesh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lameguy64/smxtool/HEAD/icons/view_wiremesh.png -------------------------------------------------------------------------------- /icons/icon_rotate_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lameguy64/smxtool/HEAD/icons/icon_rotate_left.png -------------------------------------------------------------------------------- /icons/icon_rotate_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lameguy64/smxtool/HEAD/icons/icon_rotate_right.png -------------------------------------------------------------------------------- /icons/tool_paint_hitmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lameguy64/smxtool/HEAD/icons/tool_paint_hitmap.png -------------------------------------------------------------------------------- /icons/view_firstperson.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lameguy64/smxtool/HEAD/icons/view_firstperson.png -------------------------------------------------------------------------------- /.dep.inc: -------------------------------------------------------------------------------- 1 | # This code depends on make tool being used 2 | DEPFILES=$(wildcard $(addsuffix .d, ${OBJECTFILES} ${TESTOBJECTFILES})) 3 | ifneq (${DEPFILES},) 4 | include ${DEPFILES} 5 | endif 6 | -------------------------------------------------------------------------------- /ContextClass.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | 7 | /* 8 | * File: context.cpp 9 | * Author: Lameguy64 10 | * 11 | * Created on March 13, 2018, 2:04 PM 12 | */ 13 | 14 | #include "ContextClass.h" 15 | 16 | ContextClass::ContextClass() { 17 | last_selected = -1; 18 | active_primitive = &temp; 19 | } 20 | 21 | ContextClass::~ContextClass() { 22 | } 23 | 24 | -------------------------------------------------------------------------------- /global.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File: global.h 3 | * Author: Lameguy64 4 | * 5 | * Created on March 13, 2018, 3:32 PM 6 | */ 7 | 8 | #ifndef GLOBAL_H 9 | #define GLOBAL_H 10 | 11 | #include "mainform.h" 12 | #include "ContextClass.h" 13 | #include "ConfigClass.h" 14 | 15 | #ifndef MAX_PATH 16 | #define MAX_PATH 256 17 | #endif 18 | 19 | #define VERSION "0.18b" 20 | 21 | typedef struct 22 | { 23 | unsigned char r; 24 | unsigned char g; 25 | unsigned char b; 26 | } PIXEL; 27 | 28 | extern ConfigClass config; 29 | extern ContextClass project; 30 | extern MainUI* ui; 31 | 32 | void setActiveFace(int face); 33 | void unsetFace(); 34 | 35 | #endif /* GLOBAL_H */ 36 | 37 | -------------------------------------------------------------------------------- /ColorPreview.h: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | 7 | /* 8 | * File: colorpreview.h 9 | * Author: Lameguy64 10 | * 11 | * Created on April 11, 2018, 10:25 AM 12 | */ 13 | 14 | #ifndef COLORPREVIEW_H 15 | #define COLORPREVIEW_H 16 | 17 | #include 18 | 19 | class ColorPreview : public Fl_Group { 20 | public: 21 | ColorPreview(int x, int y, int w, int h, const char* l = nullptr); 22 | virtual ~ColorPreview(); 23 | void draw(); 24 | private: 25 | 26 | }; 27 | 28 | #endif /* COLORPREVIEW_H */ 29 | 30 | -------------------------------------------------------------------------------- /about.h: -------------------------------------------------------------------------------- 1 | // generated by Fast Light User Interface Designer (fluid) version 1.0304 2 | 3 | #ifndef about_h 4 | #define about_h 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | class AboutUI : public Fl_Double_Window { 13 | void _AboutUI(); 14 | public: 15 | AboutUI(int X, int Y, int W, int H, const char *L = 0); 16 | AboutUI(int W, int H, const char *L = 0); 17 | AboutUI(); 18 | private: 19 | inline void cb_Dismiss_i(Fl_Return_Button*, void*); 20 | static void cb_Dismiss(Fl_Return_Button*, void*); 21 | public: 22 | Fl_Output *versionText; 23 | }; 24 | #endif 25 | -------------------------------------------------------------------------------- /ShaderClass.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File: ShaderClass.h 3 | * Author: lameguy64 4 | * 5 | * Created on November 15, 2017, 12:29 PM 6 | */ 7 | 8 | #ifndef SHADERCLASS_H 9 | #define SHADERCLASS_H 10 | 11 | class ShaderClass 12 | { 13 | 14 | GLuint vertex_shader; 15 | GLuint fragment_shader; 16 | GLuint shader_program; 17 | GLint uniform_texture_enable; 18 | bool is_initialized; 19 | 20 | int CompileShader(const char* shader_code, GLuint shader_type); 21 | 22 | public: 23 | 24 | ShaderClass(); 25 | virtual ~ShaderClass(); 26 | 27 | int Initialize(); 28 | 29 | void ActivateShader(); 30 | void DeactivateShader(); 31 | 32 | void TextureEnable(bool enable); 33 | 34 | bool Initialized() 35 | { 36 | return is_initialized; 37 | } 38 | }; 39 | 40 | #endif /* SHADERCLASS_H */ 41 | 42 | -------------------------------------------------------------------------------- /common/common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File: common.h 3 | * Author: Lameguy64 4 | * 5 | * Created on October 3, 2017, 3:42 PM 6 | */ 7 | 8 | #ifndef COMMON_H 9 | #define COMMON_H 10 | 11 | #include 12 | 13 | #ifndef MAX_PATH 14 | #define MAX_PATH 256 15 | #endif 16 | 17 | #define PI 3.14159263 18 | 19 | int FileExists( const char* fileName ); 20 | 21 | void EnsureFileExtension(std::string& path, const char* extension); 22 | void StripFileExtension(std::string& path); 23 | 24 | void MakeFilePathRelative(std::string& fileName, const char* basePath); 25 | int CalculateRelativePath(const char* path, std::string& output, 26 | const char* basePath); 27 | 28 | void StripPathname(std::string& path); 29 | void StripFilename(std::string& path); 30 | 31 | void Normalize(float* n, int count); 32 | 33 | #endif /* COMMON_H */ 34 | -------------------------------------------------------------------------------- /ConfigClass.h: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | 7 | /* 8 | * File: ConfigClass.h 9 | * Author: lameguy64 10 | * 11 | * Created on April 17, 2018, 10:41 AM 12 | */ 13 | 14 | #ifndef CONFIGCLASS_H 15 | #define CONFIGCLASS_H 16 | 17 | #include 18 | 19 | class ConfigClass { 20 | public: 21 | 22 | ConfigClass(); 23 | virtual ~ConfigClass(); 24 | 25 | std::string program_path; 26 | std::string config_file; 27 | 28 | int systemcolors_enable; 29 | int nsfw_enable; // :) 30 | int glsl_enable; 31 | int singlebuffer_enable; 32 | 33 | int LoadConfig(); 34 | int Saveconfig(); 35 | 36 | private: 37 | 38 | }; 39 | 40 | #endif /* CONFIGCLASS_H */ 41 | 42 | -------------------------------------------------------------------------------- /ContextClass.h: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | 7 | /* 8 | * File: context.h 9 | * Author: Lameguy64 10 | * 11 | * Created on March 13, 2018, 2:04 PM 12 | */ 13 | 14 | #ifndef CONTEXT_H 15 | #define CONTEXT_H 16 | 17 | #include "ModelClass.h" 18 | 19 | #define FILE_TYPE_SMX 0 20 | #define FILE_TYPE_RSD 1 21 | 22 | class ContextClass { 23 | public: 24 | ContextClass(); 25 | virtual ~ContextClass(); 26 | 27 | ModelClass model; 28 | ModelClass bg_model; 29 | 30 | int last_selected; 31 | 32 | std::string file_name; 33 | 34 | SmxClass::PRIMITIVE *active_primitive; 35 | SmxClass::PRIMITIVE temp; 36 | 37 | private: 38 | 39 | }; 40 | 41 | #endif /* CONTEXT_H */ 42 | 43 | -------------------------------------------------------------------------------- /configform.h: -------------------------------------------------------------------------------- 1 | // generated by Fast Light User Interface Designer (fluid) version 1.0304 2 | 3 | #ifndef configform_h 4 | #define configform_h 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | extern void configOkayButton_cb(Fl_Button*, void*); 12 | 13 | class ConfigUI : public Fl_Double_Window { 14 | void _ConfigUI(); 15 | public: 16 | ConfigUI(int X, int Y, int W, int H, const char *L = 0); 17 | ConfigUI(int W, int H, const char *L = 0); 18 | ConfigUI(); 19 | Fl_Tabs *configTabs; 20 | Fl_Group *editorGroup; 21 | Fl_Check_Button *singleBufferToggle; 22 | Fl_Check_Button *shadersToggle; 23 | Fl_Check_Button *systemColorsToggle; 24 | Fl_Check_Button *easterEggToggle; 25 | private: 26 | inline void cb_Cancel_i(Fl_Button*, void*); 27 | static void cb_Cancel(Fl_Button*, void*); 28 | }; 29 | #endif 30 | -------------------------------------------------------------------------------- /TextureClass.h: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | 7 | /* 8 | * File: TextureClass.h 9 | * Author: Lameguy64 10 | * 11 | * Created on March 13, 2018, 2:28 PM 12 | */ 13 | 14 | #ifndef TEXTURECLASS_H 15 | #define TEXTURECLASS_H 16 | 17 | #include 18 | #include 19 | #include "common/timloader.h" 20 | 21 | #define RED(c) (c&0xff) 22 | #define GREEN(c) ((c>>8)&0xff) 23 | #define BLUE(c) ((c>>16)&0xff) 24 | #define ALPHA(c) (c>>24) 25 | 26 | class TexImageClass { 27 | public: 28 | TexImageClass(); 29 | virtual ~TexImageClass(); 30 | 31 | int LoadTexture(const char* file_name); 32 | unsigned int ReadPixel(int x, int y); 33 | 34 | GLuint gl_texture; 35 | void* pixels; 36 | int w,h; 37 | 38 | int index; 39 | 40 | std::string file; 41 | 42 | private: 43 | }; 44 | 45 | #endif /* TEXTURECLASS_H */ 46 | 47 | -------------------------------------------------------------------------------- /common/timloader.h: -------------------------------------------------------------------------------- 1 | #ifndef _TIMLOADER_H 2 | #define _TIMLOADER_H 3 | 4 | #include 5 | #include 6 | 7 | typedef struct { 8 | unsigned int id; 9 | struct { 10 | unsigned int pmode:3; 11 | unsigned int cf:1; 12 | unsigned int reserved:28; 13 | } flag; 14 | } TIM_HEADER; 15 | 16 | typedef struct { 17 | unsigned int bnum; 18 | unsigned short x,y; 19 | unsigned short w,h; 20 | } TIM_BLOCK; 21 | 22 | typedef struct { 23 | unsigned short r:5; 24 | unsigned short g:5; 25 | unsigned short b:5; 26 | unsigned short i:1; 27 | } TIM_PIXEL; 28 | 29 | enum TIM_ERROR { 30 | TIM_ERROR_NONE = 0, 31 | TIM_ERROR_NOTFOUND, 32 | TIM_ERROR_INVALID 33 | }; 34 | 35 | class TimLoaderClass { 36 | public: 37 | 38 | TimLoaderClass(); 39 | virtual ~TimLoaderClass(); 40 | 41 | TIM_ERROR LoadFile(const char* filename); 42 | void ConvertToRGBA(void *output); 43 | 44 | int GetWidth(); 45 | int GetHeight(); 46 | 47 | TIM_HEADER header; 48 | TIM_BLOCK clutBlock; 49 | TIM_BLOCK pixelBlock; 50 | 51 | TIM_PIXEL *clut; 52 | void *pixels; 53 | 54 | }; 55 | 56 | #endif // _TIMLOADER_H 57 | -------------------------------------------------------------------------------- /.github/workflows/linux-build.yml: -------------------------------------------------------------------------------- 1 | name: Build and release 2 | 3 | on: 4 | push: 5 | branches: [ linux-build ] 6 | 7 | jobs: 8 | build: 9 | 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v2 14 | - name: Install dependencies 15 | run: | 16 | sudo apt-get update 17 | sudo apt-get install build-essential libtinyxml2-6 libtinyxml2-dev libfltk1.3-dev libglew-dev xorg-dev 18 | - name: make 19 | run: make -f Makefile CONF=Debug-linux 20 | - name: Create archive 21 | run: 7z a smxtool-0.18b-linux.zip ${{github.workspace}}/dist/Debug-linux/GNU-Linux 22 | - name: Upload sxmtool-linux 23 | uses: actions/upload-artifact@v2 24 | with: 25 | name: smxtool-0.18b-linux.zip 26 | path: ${{github.workspace}}/dist/Debug-linux/GNU-Linux 27 | - name: Create a Release 28 | uses: djnicholson/release-action@v2.11 29 | with: 30 | token: ${{ secrets.GITHUB_TOKEN }} 31 | release-name: 'Latest Linux build' 32 | tag-name: 'v0.18b' 33 | asset-name: 'smxtool-0.18b-linux.zip' 34 | file: 'smxtool-0.18b-linux.zip' 35 | -------------------------------------------------------------------------------- /texturepreview.h: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | 7 | /* 8 | * File: texturepreview.h 9 | * Author: lameguy64 10 | * 11 | * Created on April 17, 2018, 4:20 PM 12 | */ 13 | 14 | #ifndef TEXTUREPREVIEW_H 15 | #define TEXTUREPREVIEW_H 16 | 17 | #include 18 | #include 19 | 20 | typedef struct { 21 | int x,y; 22 | } VECTOR2D; 23 | 24 | class TexturePreview : public Fl_Group { 25 | public: 26 | 27 | TexturePreview(int x, int y, int w, int h, const char* l = nullptr); 28 | virtual ~TexturePreview(); 29 | void SetZoom(int scale); 30 | void draw(); 31 | int handle(int event); 32 | void resize(int x, int y, int w, int h); 33 | 34 | 35 | int zoom; 36 | 37 | Fl_Scrollbar _hscrollbar; 38 | Fl_Scrollbar _vscrollbar; 39 | 40 | private: 41 | int _dragx,_dragy; 42 | int _drag,_isdragging; 43 | int _dragpoints; 44 | VECTOR2D _draguv[4]; 45 | 46 | void calculateScrollbarPos(); 47 | void testScrollbars(); 48 | }; 49 | 50 | #endif /* TEXTUREPREVIEW_H */ 51 | 52 | -------------------------------------------------------------------------------- /glwidget.h: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | 7 | /* 8 | * File: glwidget.h 9 | * Author: Lameguy64 10 | * 11 | * Created on March 13, 2018, 1:24 PM 12 | */ 13 | 14 | #ifndef GLWIDGET_H 15 | #define GLWIDGET_H 16 | 17 | #include 18 | #include 19 | #include 20 | #include "common/smx.h" 21 | #include "ShaderClass.h" 22 | 23 | class glwidget : public Fl_Gl_Window { 24 | public: 25 | glwidget(int x, int y, int w, int h, const char* l = nullptr); 26 | virtual ~glwidget(); 27 | 28 | void CalculateClickRay(SmxClass::VECTOR3D* pos, SmxClass::VECTOR3D* ray); 29 | 30 | void draw(); 31 | int handle(int event); 32 | 33 | int persp_mode; 34 | 35 | float zoom; 36 | float position_x,position_y,position_z; 37 | float rotate_x,rotate_y; 38 | 39 | SmxClass::VECTOR3D pos,ray; 40 | ShaderClass shader; 41 | 42 | int backface_culling; 43 | int wireframe_overlay; 44 | 45 | private: 46 | 47 | GLdouble m_projection[16]; 48 | GLdouble m_modelview[16]; 49 | GLint m_view[4]; 50 | 51 | int clickx,clicky; 52 | float clickrx,clickry,clickrz; 53 | int view_move; 54 | 55 | int init_done; 56 | int shift_down; 57 | 58 | }; 59 | 60 | #endif /* GLWIDGET_H */ 61 | 62 | -------------------------------------------------------------------------------- /nbproject/project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | org.netbeans.modules.cnd.makeproject 4 | 5 | 6 | smxtool 7 | 8 | cpp,cxx 9 | h 10 | UTF-8 11 | 12 | 13 | 14 | 15 | Debug 16 | 1 17 | 18 | 19 | Release 20 | 1 21 | 22 | 23 | Debug-linux 24 | 1 25 | 26 | 27 | Release-linux 28 | 1 29 | 30 | 31 | 32 | false 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /about.fl: -------------------------------------------------------------------------------- 1 | # data file for the Fltk User Interface Designer (fluid) 2 | version 1.0304 3 | header_name {.h} 4 | code_name {.cxx} 5 | widget_class AboutUI { 6 | label {About SMXTool} open selected 7 | xywh {356 356 624 376} type Double 8 | class Fl_Double_Window modal visible 9 | } { 10 | Fl_Box {} { 11 | label SMXTOOL 12 | xywh {10 10 342 64} box THIN_UP_BOX labeltype ENGRAVED_LABEL labelfont 3 labelsize 38 13 | } 14 | Fl_Box {} { 15 | label {Artwork by HikkHyric on DeviantArt} 16 | image {icons/scarlet-logo.png} xywh {362 10 252 326} box THIN_DOWN_BOX color 55 labelfont 2 labelsize 12 align 578 17 | } 18 | Fl_Box {} { 19 | label {Part of Project Scarlet, the soon to be released 3D game engine for the Sony PlayStation} 20 | xywh {10 75 342 35} labelfont 9 align 128 21 | } 22 | Fl_Box {} { 23 | label {2017-2018 Meido-Tek Productions} 24 | xywh {10 306 342 29} box ENGRAVED_BOX labelsize 12 25 | } 26 | Fl_Group {} {open 27 | xywh {10 146 342 154} box ENGRAVED_BOX 28 | } { 29 | Fl_Box {} { 30 | label {Programmed by Lameguy64 of Meido-Tek Productions 31 | 32 | Built using FLTK and tinyxml2.} 33 | xywh {15 153 332 100} labelsize 12 align 149 34 | } 35 | Fl_Box {} { 36 | label {Project Scarlet is not officially licensed or endorsed by Sony Computer Entertainment.} 37 | xywh {15 258 330 32} labelsize 12 align 149 38 | } 39 | } 40 | Fl_Return_Button {} { 41 | label Dismiss 42 | callback {hide();} 43 | xywh {10 344 90 20} labelsize 12 44 | } 45 | Fl_Output versionText { 46 | label Version 47 | xywh {56 119 44 21} box FLAT_BOX color 49 labelsize 12 textsize 12 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /nbproject/Package-Debug.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | 3 | # 4 | # Generated - do not edit! 5 | # 6 | 7 | # Macros 8 | TOP=`pwd` 9 | CND_PLATFORM=GNU-Linux 10 | CND_CONF=Debug 11 | CND_DISTDIR=dist 12 | CND_BUILDDIR=build 13 | CND_DLIB_EXT=so 14 | NBTMPDIR=${CND_BUILDDIR}/${CND_CONF}/${CND_PLATFORM}/tmp-packaging 15 | TMPDIRNAME=tmp-packaging 16 | OUTPUT_PATH=${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/smxtool 17 | OUTPUT_BASENAME=smxtool 18 | PACKAGE_TOP_DIR=smxtool/ 19 | 20 | # Functions 21 | function checkReturnCode 22 | { 23 | rc=$? 24 | if [ $rc != 0 ] 25 | then 26 | exit $rc 27 | fi 28 | } 29 | function makeDirectory 30 | # $1 directory path 31 | # $2 permission (optional) 32 | { 33 | mkdir -p "$1" 34 | checkReturnCode 35 | if [ "$2" != "" ] 36 | then 37 | chmod $2 "$1" 38 | checkReturnCode 39 | fi 40 | } 41 | function copyFileToTmpDir 42 | # $1 from-file path 43 | # $2 to-file path 44 | # $3 permission 45 | { 46 | cp "$1" "$2" 47 | checkReturnCode 48 | if [ "$3" != "" ] 49 | then 50 | chmod $3 "$2" 51 | checkReturnCode 52 | fi 53 | } 54 | 55 | # Setup 56 | cd "${TOP}" 57 | mkdir -p ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package 58 | rm -rf ${NBTMPDIR} 59 | mkdir -p ${NBTMPDIR} 60 | 61 | # Copy files and create directories and links 62 | cd "${TOP}" 63 | makeDirectory "${NBTMPDIR}/smxtool/bin" 64 | copyFileToTmpDir "${OUTPUT_PATH}" "${NBTMPDIR}/${PACKAGE_TOP_DIR}bin/${OUTPUT_BASENAME}" 0755 65 | 66 | 67 | # Generate tar file 68 | cd "${TOP}" 69 | rm -f ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package/smxtool.tar 70 | cd ${NBTMPDIR} 71 | tar -vcf ../../../../${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package/smxtool.tar * 72 | checkReturnCode 73 | 74 | # Cleanup 75 | cd "${TOP}" 76 | rm -rf ${NBTMPDIR} 77 | -------------------------------------------------------------------------------- /nbproject/Package-Release.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | 3 | # 4 | # Generated - do not edit! 5 | # 6 | 7 | # Macros 8 | TOP=`pwd` 9 | CND_PLATFORM=GNU-Linux 10 | CND_CONF=Release 11 | CND_DISTDIR=dist 12 | CND_BUILDDIR=build 13 | CND_DLIB_EXT=so 14 | NBTMPDIR=${CND_BUILDDIR}/${CND_CONF}/${CND_PLATFORM}/tmp-packaging 15 | TMPDIRNAME=tmp-packaging 16 | OUTPUT_PATH=${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/smxtool 17 | OUTPUT_BASENAME=smxtool 18 | PACKAGE_TOP_DIR=smxtool/ 19 | 20 | # Functions 21 | function checkReturnCode 22 | { 23 | rc=$? 24 | if [ $rc != 0 ] 25 | then 26 | exit $rc 27 | fi 28 | } 29 | function makeDirectory 30 | # $1 directory path 31 | # $2 permission (optional) 32 | { 33 | mkdir -p "$1" 34 | checkReturnCode 35 | if [ "$2" != "" ] 36 | then 37 | chmod $2 "$1" 38 | checkReturnCode 39 | fi 40 | } 41 | function copyFileToTmpDir 42 | # $1 from-file path 43 | # $2 to-file path 44 | # $3 permission 45 | { 46 | cp "$1" "$2" 47 | checkReturnCode 48 | if [ "$3" != "" ] 49 | then 50 | chmod $3 "$2" 51 | checkReturnCode 52 | fi 53 | } 54 | 55 | # Setup 56 | cd "${TOP}" 57 | mkdir -p ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package 58 | rm -rf ${NBTMPDIR} 59 | mkdir -p ${NBTMPDIR} 60 | 61 | # Copy files and create directories and links 62 | cd "${TOP}" 63 | makeDirectory "${NBTMPDIR}/smxtool/bin" 64 | copyFileToTmpDir "${OUTPUT_PATH}" "${NBTMPDIR}/${PACKAGE_TOP_DIR}bin/${OUTPUT_BASENAME}" 0755 65 | 66 | 67 | # Generate tar file 68 | cd "${TOP}" 69 | rm -f ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package/smxtool.tar 70 | cd ${NBTMPDIR} 71 | tar -vcf ../../../../${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package/smxtool.tar * 72 | checkReturnCode 73 | 74 | # Cleanup 75 | cd "${TOP}" 76 | rm -rf ${NBTMPDIR} 77 | -------------------------------------------------------------------------------- /nbproject/Package-Debug-linux.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | 3 | # 4 | # Generated - do not edit! 5 | # 6 | 7 | # Macros 8 | TOP=`pwd` 9 | CND_PLATFORM=GNU-Linux 10 | CND_CONF=Debug-linux 11 | CND_DISTDIR=dist 12 | CND_BUILDDIR=build 13 | CND_DLIB_EXT=so 14 | NBTMPDIR=${CND_BUILDDIR}/${CND_CONF}/${CND_PLATFORM}/tmp-packaging 15 | TMPDIRNAME=tmp-packaging 16 | OUTPUT_PATH=${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/smxtool 17 | OUTPUT_BASENAME=smxtool 18 | PACKAGE_TOP_DIR=smxtool/ 19 | 20 | # Functions 21 | function checkReturnCode 22 | { 23 | rc=$? 24 | if [ $rc != 0 ] 25 | then 26 | exit $rc 27 | fi 28 | } 29 | function makeDirectory 30 | # $1 directory path 31 | # $2 permission (optional) 32 | { 33 | mkdir -p "$1" 34 | checkReturnCode 35 | if [ "$2" != "" ] 36 | then 37 | chmod $2 "$1" 38 | checkReturnCode 39 | fi 40 | } 41 | function copyFileToTmpDir 42 | # $1 from-file path 43 | # $2 to-file path 44 | # $3 permission 45 | { 46 | cp "$1" "$2" 47 | checkReturnCode 48 | if [ "$3" != "" ] 49 | then 50 | chmod $3 "$2" 51 | checkReturnCode 52 | fi 53 | } 54 | 55 | # Setup 56 | cd "${TOP}" 57 | mkdir -p ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package 58 | rm -rf ${NBTMPDIR} 59 | mkdir -p ${NBTMPDIR} 60 | 61 | # Copy files and create directories and links 62 | cd "${TOP}" 63 | makeDirectory "${NBTMPDIR}/smxtool/bin" 64 | copyFileToTmpDir "${OUTPUT_PATH}" "${NBTMPDIR}/${PACKAGE_TOP_DIR}bin/${OUTPUT_BASENAME}" 0755 65 | 66 | 67 | # Generate tar file 68 | cd "${TOP}" 69 | rm -f ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package/smxtool.tar 70 | cd ${NBTMPDIR} 71 | tar -vcf ../../../../${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package/smxtool.tar * 72 | checkReturnCode 73 | 74 | # Cleanup 75 | cd "${TOP}" 76 | rm -rf ${NBTMPDIR} 77 | -------------------------------------------------------------------------------- /ModelClass.h: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | 7 | /* 8 | * File: modelclass.h 9 | * Author: Lameguy64 10 | * 11 | * Created on March 13, 2018, 2:05 PM 12 | */ 13 | 14 | #ifndef MODELCLASS_H 15 | #define MODELCLASS_H 16 | 17 | #include 18 | #include "common/smx.h" 19 | #include "TextureClass.h" 20 | 21 | class ModelClass : public SmxClass { 22 | public: 23 | ModelClass(); 24 | virtual ~ModelClass(); 25 | 26 | enum FileType { 27 | FILETYPE_SMX = 0, 28 | FILETYPE_RSD 29 | }; 30 | 31 | int LoadFile(const char* file_name); 32 | int SaveFile(const char* file_name); 33 | 34 | int MergeSmxFile(const char* file_name); 35 | 36 | SmxClass::SMX_ERROR LoadRsdData(const char* file_name); 37 | SmxClass::SMX_ERROR SaveRsdData(const char* file_name); 38 | 39 | void LoadTextures( const char* src_file = nullptr ); 40 | 41 | void AddTexture(const char* file_name); 42 | int ReplaceTexture(int index, const char* file_name); 43 | void DeleteTexture(int index); 44 | 45 | void RenderSelected(); 46 | void RenderTextured(int culling); 47 | void RenderWireframe(); 48 | 49 | void SetShading(SmxClass::PRIMITIVE* prim, int shaded); 50 | void SetTexturing(SmxClass::PRIMITIVE* prim, int textured, int texnum); 51 | 52 | int ScanFace(SmxClass::VECTOR3D pos, SmxClass::VECTOR3D ray); 53 | 54 | void ClearSelection(); 55 | void ClearTextures(); 56 | 57 | std::vector texture_images; 58 | 59 | std::string rsd_ply_name; 60 | std::string rsd_mat_name; 61 | int file_type; 62 | 63 | private: 64 | 65 | int LoadTextureFile(const char* file_name, int index); 66 | }; 67 | 68 | #endif /* MODELCLASS_H */ 69 | 70 | -------------------------------------------------------------------------------- /nbproject/Package-Release-linux.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | 3 | # 4 | # Generated - do not edit! 5 | # 6 | 7 | # Macros 8 | TOP=`pwd` 9 | CND_PLATFORM=GNU-Linux 10 | CND_CONF=Release-linux 11 | CND_DISTDIR=dist 12 | CND_BUILDDIR=build 13 | CND_DLIB_EXT=so 14 | NBTMPDIR=${CND_BUILDDIR}/${CND_CONF}/${CND_PLATFORM}/tmp-packaging 15 | TMPDIRNAME=tmp-packaging 16 | OUTPUT_PATH=${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/smxtool 17 | OUTPUT_BASENAME=smxtool 18 | PACKAGE_TOP_DIR=smxtool/ 19 | 20 | # Functions 21 | function checkReturnCode 22 | { 23 | rc=$? 24 | if [ $rc != 0 ] 25 | then 26 | exit $rc 27 | fi 28 | } 29 | function makeDirectory 30 | # $1 directory path 31 | # $2 permission (optional) 32 | { 33 | mkdir -p "$1" 34 | checkReturnCode 35 | if [ "$2" != "" ] 36 | then 37 | chmod $2 "$1" 38 | checkReturnCode 39 | fi 40 | } 41 | function copyFileToTmpDir 42 | # $1 from-file path 43 | # $2 to-file path 44 | # $3 permission 45 | { 46 | cp "$1" "$2" 47 | checkReturnCode 48 | if [ "$3" != "" ] 49 | then 50 | chmod $3 "$2" 51 | checkReturnCode 52 | fi 53 | } 54 | 55 | # Setup 56 | cd "${TOP}" 57 | mkdir -p ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package 58 | rm -rf ${NBTMPDIR} 59 | mkdir -p ${NBTMPDIR} 60 | 61 | # Copy files and create directories and links 62 | cd "${TOP}" 63 | makeDirectory "${NBTMPDIR}/smxtool/bin" 64 | copyFileToTmpDir "${OUTPUT_PATH}" "${NBTMPDIR}/${PACKAGE_TOP_DIR}bin/${OUTPUT_BASENAME}" 0755 65 | 66 | 67 | # Generate tar file 68 | cd "${TOP}" 69 | rm -f ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package/smxtool.tar 70 | cd ${NBTMPDIR} 71 | tar -vcf ../../../../${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package/smxtool.tar * 72 | checkReturnCode 73 | 74 | # Cleanup 75 | cd "${TOP}" 76 | rm -rf ${NBTMPDIR} 77 | -------------------------------------------------------------------------------- /configform.fl: -------------------------------------------------------------------------------- 1 | # data file for the Fltk User Interface Designer (fluid) 2 | version 1.0304 3 | header_name {.h} 4 | code_name {.cxx} 5 | widget_class ConfigUI { 6 | label Configuration open 7 | xywh {464 363 404 308} type Double 8 | class Fl_Double_Window modal visible 9 | } { 10 | Fl_Tabs configTabs {open 11 | xywh {10 10 384 252} 12 | } { 13 | Fl_Group editorGroup { 14 | label Editor selected 15 | xywh {10 29 384 233} labelsize 12 16 | } { 17 | Fl_Group {} { 18 | label OpenGL open 19 | xywh {205 50 179 46} box ENGRAVED_BOX labelsize 12 align 5 20 | } { 21 | Fl_Check_Button singleBufferToggle { 22 | label {Single-buffer mode} 23 | tooltip {Forces OpenGL context to run in single buffer mode. Can fix Aero being disabled with newer NVIDIA cards on Windows 7. (restart required)} xywh {211 59 165 15} down_box DOWN_BOX labelsize 12 24 | } 25 | Fl_Check_Button shadersToggle { 26 | label {GLSL Shaders} 27 | tooltip {Enables use of GLSL shaders to accurately represent textured polygons if supported by your hardware. (restart required)} xywh {211 75 165 15} down_box DOWN_BOX labelsize 12 28 | } 29 | } 30 | Fl_Group {} { 31 | label Editor open 32 | xywh {20 50 180 46} box ENGRAVED_BOX labelsize 12 align 5 33 | } { 34 | Fl_Check_Button systemColorsToggle { 35 | label {Use System Colors} 36 | tooltip {Use the system's color scheme.} xywh {28 59 164 15} down_box DOWN_BOX labelsize 12 37 | } 38 | Fl_Check_Button easterEggToggle { 39 | label {NSFW Easter Egg :)} 40 | tooltip {The question is... Can you find it? :)} xywh {28 75 164 15} down_box DOWN_BOX labelsize 12 41 | } 42 | } 43 | } 44 | } 45 | Fl_Button {} { 46 | label Cancel 47 | callback {hide();} 48 | xywh {316 270 78 25} labelsize 12 49 | } 50 | Fl_Button {} { 51 | label Ok 52 | callback configOkayButton_cb 53 | xywh {232 270 78 25} labelsize 12 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /nbproject/Makefile-variables.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Generated - do not edit! 3 | # 4 | # NOCDDL 5 | # 6 | CND_BASEDIR=`pwd` 7 | CND_BUILDDIR=build 8 | CND_DISTDIR=dist 9 | # Debug configuration 10 | CND_PLATFORM_Debug=GNU-Linux 11 | CND_ARTIFACT_DIR_Debug=dist/Debug/GNU-Linux 12 | CND_ARTIFACT_NAME_Debug=smxtool 13 | CND_ARTIFACT_PATH_Debug=dist/Debug/GNU-Linux/smxtool 14 | CND_PACKAGE_DIR_Debug=dist/Debug/GNU-Linux/package 15 | CND_PACKAGE_NAME_Debug=smxtool.tar 16 | CND_PACKAGE_PATH_Debug=dist/Debug/GNU-Linux/package/smxtool.tar 17 | # Release configuration 18 | CND_PLATFORM_Release=GNU-Linux 19 | CND_ARTIFACT_DIR_Release=dist/Release/GNU-Linux 20 | CND_ARTIFACT_NAME_Release=smxtool 21 | CND_ARTIFACT_PATH_Release=dist/Release/GNU-Linux/smxtool 22 | CND_PACKAGE_DIR_Release=dist/Release/GNU-Linux/package 23 | CND_PACKAGE_NAME_Release=smxtool.tar 24 | CND_PACKAGE_PATH_Release=dist/Release/GNU-Linux/package/smxtool.tar 25 | # Debug-linux configuration 26 | CND_PLATFORM_Debug-linux=GNU-Linux 27 | CND_ARTIFACT_DIR_Debug-linux=dist/Debug-linux/GNU-Linux 28 | CND_ARTIFACT_NAME_Debug-linux=smxtool 29 | CND_ARTIFACT_PATH_Debug-linux=dist/Debug-linux/GNU-Linux/smxtool 30 | CND_PACKAGE_DIR_Debug-linux=dist/Debug-linux/GNU-Linux/package 31 | CND_PACKAGE_NAME_Debug-linux=smxtool.tar 32 | CND_PACKAGE_PATH_Debug-linux=dist/Debug-linux/GNU-Linux/package/smxtool.tar 33 | # Release-linux configuration 34 | CND_PLATFORM_Release-linux=GNU-Linux 35 | CND_ARTIFACT_DIR_Release-linux=dist/Release-linux/GNU-Linux 36 | CND_ARTIFACT_NAME_Release-linux=smxtool 37 | CND_ARTIFACT_PATH_Release-linux=dist/Release-linux/GNU-Linux/smxtool 38 | CND_PACKAGE_DIR_Release-linux=dist/Release-linux/GNU-Linux/package 39 | CND_PACKAGE_NAME_Release-linux=smxtool.tar 40 | CND_PACKAGE_PATH_Release-linux=dist/Release-linux/GNU-Linux/package/smxtool.tar 41 | # 42 | # include compiler specific variables 43 | # 44 | # dmake command 45 | ROOT:sh = test -f nbproject/private/Makefile-variables.mk || \ 46 | (mkdir -p nbproject/private && touch nbproject/private/Makefile-variables.mk) 47 | # 48 | # gmake command 49 | .PHONY: $(shell test -f nbproject/private/Makefile-variables.mk || (mkdir -p nbproject/private && touch nbproject/private/Makefile-variables.mk)) 50 | # 51 | include nbproject/private/Makefile-variables.mk 52 | -------------------------------------------------------------------------------- /TextureClass.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | 7 | /* 8 | * File: TextureClass.cpp 9 | * Author: Lameguy64 10 | * 11 | * Created on March 13, 2018, 2:28 PM 12 | */ 13 | 14 | #include 15 | #include "TextureClass.h" 16 | #include "common/smx.h" 17 | 18 | TexImageClass::TexImageClass() { 19 | gl_texture = 0; 20 | pixels = nullptr; 21 | } 22 | 23 | TexImageClass::~TexImageClass() { 24 | if ( gl_texture != 0 ) { 25 | glDeleteTextures( 1, &gl_texture ); 26 | } 27 | if ( pixels != nullptr ) { 28 | free( pixels ); 29 | } 30 | 31 | #ifdef DEBUG 32 | std::cout << "Freed texture " << file << std::endl; 33 | #endif 34 | } 35 | 36 | int TexImageClass::LoadTexture(const char* file_name) { 37 | TimLoaderClass tim; 38 | 39 | switch( tim.LoadFile( file_name ) ) 40 | { 41 | case TIM_ERROR_NOTFOUND: 42 | return -1; 43 | case TIM_ERROR_INVALID: 44 | return -1; 45 | default: 46 | break; 47 | }; 48 | 49 | file = file_name; 50 | 51 | //tim_header = tim.header; 52 | //tim_pixels = tim.pixelBlock; 53 | //tim_clut = tim.clutBlock; 54 | 55 | w = tim.GetWidth(); 56 | h = tim.GetHeight(); 57 | 58 | /*tyoffs = tim.pixelBlock.y%256; 59 | 60 | switch(tim.header.flag.pmode) { 61 | case 0: // 4-bit 62 | txoffs = (tim.pixelBlock.x*4)%256; 63 | break; 64 | case 1: // 8-bit 65 | txoffs = (tim.pixelBlock.x*2)%128; 66 | break; 67 | case 2: // 16-bit 68 | txoffs = tim.pixelBlock.x%64; 69 | break; 70 | }*/ 71 | 72 | pixels = malloc( 4*(w*h) ); 73 | tim.ConvertToRGBA( pixels ); 74 | 75 | glGenTextures( 1, &gl_texture ); 76 | glBindTexture( GL_TEXTURE_2D, gl_texture ); 77 | 78 | glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, 79 | GL_UNSIGNED_BYTE, pixels ); 80 | 81 | glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); 82 | glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); 83 | 84 | return 0; 85 | } 86 | 87 | unsigned int TexImageClass::ReadPixel(int x, int y) { 88 | 89 | if ( x < 0 ) 90 | return 0; 91 | if ( y < 0 ) 92 | return 0; 93 | if ( x >= w ) 94 | return 0; 95 | if ( y >= h ) 96 | return 0; 97 | 98 | return ((unsigned int*)pixels)[x+(w*y)]; 99 | 100 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SMXTOOL 2 | PlayStation RSD and Scarlet SMX model viewer and material editor. Intended to be part of Project Scarlet (not to be confused with the codename of the next Xbox) but due to development delays, this tool has been forked into its own thing for release to make up for the delays and that it can be used without the Scarlet engine as it can edit RSD model data. 3 | 4 | This tool is a bit early in development but should be useable for coloring and texturing RSD models. Suggestions for more features or quality of life improvements are welcome. 5 | 6 | ## Features 7 | * Uses GLSL shaders to emulate PSX style texture brightening (make sure to enable it in settings). 8 | * Supports blended polygons with all blend modes correctly represented. 9 | * Gouraud shaded polygons with unique colors on each vertex. 10 | * Double-sided (no culling) flag can be set on primitives. 11 | * Primitives can be sorted by hand. 12 | * Texture view can be zoomed (no more eyestrain to those who've used RSDTOOL!). 13 | * Can select and manipulate multiple primitives at once. 14 | * Can load and save RSD model files. 15 | 16 | ## RSD Format Support 17 | SMXTOOL supports Sony's RSD model format used by licensed PlayStation developers for creating 3D models to be rendered on the console using Sony's libraries after converting to the TMD file format. Not all RSD format features are supported but what is supported should be enough for most cases. 18 | 19 | The following features of the RSD format are not supported: 20 | * Line and sprite primitives. 21 | * Textured primitives with repeating textures. 22 | * GRP,MSH,PVT,COD and MOT files. 23 | 24 | ## Tips 25 | * You can double click a primitive to snap the axis point of the camera to the center of the primitive. Most useable in orbital camera view. 26 | 27 | ## Compiling 28 | 29 | You will need the following libraries: 30 | 31 | * tinyxml2 32 | * FLTK 33 | * GLEW 34 | * Xorg 35 | 36 | ### Linux 37 | 38 | You can install the needed libraries with : 39 | 40 | ```bash 41 | sudo apt-get install build-essential libtinyxml2-6 libtinyxml2-dev libfltk1.3-dev libglew-dev xorg-dev 42 | ``` 43 | 44 | then compile with : 45 | 46 | ```bash 47 | make -f Makefile CONF=Release-linux # or CONF=Debug-linux 48 | ``` 49 | 50 | The generated files will be in `smxtool/dist/Release-linux`. 51 | 52 | On Windows, it is recommended to compile using MinGW. MSVC compatibility is not guaranteed. 53 | 54 | Compile with 'mingw32-make CONF=Release' or open the project using Netbeans and compile through that. 55 | 56 | ## To-do 57 | * Line primitives. 58 | * Supporting STP mask bit on textures (not sure if possible with shaders). 59 | * Vertex paint tool. 60 | * Lighting..? -------------------------------------------------------------------------------- /common/smx.h: -------------------------------------------------------------------------------- 1 | #ifndef _SMX_H 2 | #define _SMX_H 3 | 4 | #include 5 | #include 6 | 7 | #ifndef nullptr 8 | #define nullptr 0 9 | #endif 10 | 11 | /* bit 0 - line/polygon 12 | * bit 1 - triangle/quad 13 | * bit 2 - flat/shaded 14 | * bit 3 - untextured/textured 15 | */ 16 | 17 | #define SMX_PRIM_LINE 0x0 18 | #define SMX_PRIM_POLY 0x1 19 | #define SMX_PRIM_TRI 0x0 20 | #define SMX_PRIM_QUAD 0x2 21 | #define SMX_PRIM_FLAT 0x0 22 | #define SMX_PRIM_GOURAUD 0x4 23 | #define SMX_PRIM_UNTEXTURED 0x0 24 | #define SMX_PRIM_TEXTURED 0x8 25 | 26 | class SmxClass 27 | { 28 | public: 29 | 30 | enum SMX_ERROR 31 | { 32 | ERR_NONE = 0, 33 | ERR_NOTFOUND, 34 | ERR_INVALID, 35 | ERR_UNKNOWN, 36 | ERR_NOEXT, 37 | ERR_NO_MODEL, 38 | ERR_NO_VERTS, 39 | ERR_NO_NORMS, 40 | ERR_NO_PRIMS, 41 | }; 42 | 43 | enum SMX_PRIMTYPE 44 | { 45 | PRIM_LINE_F = SMX_PRIM_LINE|SMX_PRIM_FLAT, 46 | PRIM_LINE_G = SMX_PRIM_LINE|SMX_PRIM_GOURAUD, 47 | 48 | PRIM_TRI_F = SMX_PRIM_POLY|SMX_PRIM_TRI|SMX_PRIM_FLAT, 49 | PRIM_TRI_FT = SMX_PRIM_POLY|SMX_PRIM_TRI|SMX_PRIM_FLAT|SMX_PRIM_TEXTURED, 50 | PRIM_TRI_G = SMX_PRIM_POLY|SMX_PRIM_TRI|SMX_PRIM_GOURAUD, 51 | PRIM_TRI_GT = SMX_PRIM_POLY|SMX_PRIM_TRI|SMX_PRIM_GOURAUD|SMX_PRIM_TEXTURED, 52 | 53 | PRIM_QUAD_F = SMX_PRIM_POLY|SMX_PRIM_QUAD|SMX_PRIM_FLAT, 54 | PRIM_QUAD_FT = SMX_PRIM_POLY|SMX_PRIM_QUAD|SMX_PRIM_FLAT|SMX_PRIM_TEXTURED, 55 | 56 | PRIM_QUAD_G = SMX_PRIM_POLY|SMX_PRIM_QUAD|SMX_PRIM_GOURAUD, 57 | PRIM_QUAD_GT = SMX_PRIM_POLY|SMX_PRIM_QUAD|SMX_PRIM_GOURAUD|SMX_PRIM_TEXTURED, 58 | }; 59 | 60 | enum SMX_SHADETYPE 61 | { 62 | SHADE_NONE, 63 | SHADE_FLAT, 64 | SHADE_SMOOTH, 65 | }; 66 | 67 | typedef struct { 68 | float x,y,z; 69 | } VECTOR3D; 70 | 71 | typedef struct { 72 | unsigned char u,v; 73 | } UV; 74 | 75 | typedef struct { 76 | unsigned char r,g,b; 77 | } RGB; 78 | 79 | typedef struct { 80 | int primtype; // Primitive type 81 | SMX_SHADETYPE shadetype; // Shading type 82 | int texnum; // Texture number of primitive (if textured) 83 | int blendmode; 84 | int doublesided; 85 | int selected; 86 | int v[4]; // Vertex indices 87 | int n[4]; // Normal indices 88 | RGB c[4]; // Vertex colors 89 | UV t[4]; // Texture coords 90 | } PRIMITIVE; 91 | 92 | class TextureClass 93 | { 94 | public: 95 | std::string filename; 96 | int userdata1; 97 | void* userdata2; 98 | }; 99 | 100 | SmxClass(); 101 | virtual ~SmxClass(); 102 | 103 | void Clear(); 104 | 105 | SMX_ERROR LoadFile(const char* filename); 106 | SMX_ERROR SaveFile(const char *filename); 107 | 108 | int FindVertexIndex(VECTOR3D *vertex); 109 | int FindNormalIndex(VECTOR3D *vertex); 110 | 111 | int IsTextured(PRIMITIVE* pri); 112 | 113 | std::vector vertices; 114 | std::vector normals; 115 | std::vector textures; 116 | std::vector primitives; 117 | 118 | }; 119 | 120 | #endif 121 | -------------------------------------------------------------------------------- /mainform.h: -------------------------------------------------------------------------------- 1 | // generated by Fast Light User Interface Designer (fluid) version 1.0304 2 | 3 | #ifndef mainform_h 4 | #define mainform_h 5 | #include 6 | #include 7 | extern void mainUI_cb(Fl_Double_Window*, void*); 8 | #include 9 | extern void loadModelFile_cb(Fl_Menu_*, void*); 10 | extern void saveModelFile_cb(Fl_Menu_*, void*); 11 | extern void saveModelFileAs_cb(Fl_Menu_*, void*); 12 | extern void mergeModelFile_cb(Fl_Menu_*, void*); 13 | extern void exitItem_cb(Fl_Menu_*, void*); 14 | extern void selBringFront_cb(Fl_Menu_*, void*); 15 | extern void selSendBack_cb(Fl_Menu_*, void*); 16 | extern void selectAllItem_cb(Fl_Menu_*, void*); 17 | extern void invertSelectionItem_cb(Fl_Menu_*, void*); 18 | extern void settingsItem_cb(Fl_Menu_*, long); 19 | extern void aboutItem_cb(Fl_Menu_*, void*); 20 | #include 21 | #include 22 | extern void loadModelButton_cb(Fl_Button*, void*); 23 | extern void saveModelButton_cb(Fl_Button*, void*); 24 | extern void loadBgModelFile_cb(Fl_Button*, void*); 25 | extern void paintModeToggle_cb(Fl_Button*, void*); 26 | extern void renderModeToggle_cb(Fl_Button*, long); 27 | extern void resetPosition_cb(Fl_Button*, void*); 28 | extern void homePosition_cb(Fl_Button*, void*); 29 | extern void perspModeChange_cb(Fl_Button*, long); 30 | extern void aboutButton_cb(Fl_Button*, void*); 31 | #include 32 | #include "glwidget.h" 33 | #include 34 | #include "ColorPreview.h" 35 | extern void setVcolor_cb(Fl_Button*, long); 36 | #include 37 | extern void colorSlider_cb(Fl_Value_Slider*, long); 38 | #include 39 | extern void blendModeSet_cb(Fl_Round_Button*, long); 40 | #include 41 | extern void planeSettingChange_cb(Fl_Check_Button*, long); 42 | extern void setPrimType_cb(Fl_Round_Button*, long); 43 | #include 44 | #include "texturepreview.h" 45 | #include 46 | extern void textureZoom_cb(Fl_Spinner*, void*); 47 | extern void rotateTexButton_cb(Fl_Button*, long); 48 | extern void expandTexButton_cb(Fl_Button*, void*); 49 | extern void setPrimTexture_cb(Fl_Check_Button*, long); 50 | #include 51 | extern void textureBrowser_cb(Fl_Browser*, void*); 52 | extern void addTextureButton_cb(Fl_Button*, void*); 53 | extern void deleteTextureButton_cb(Fl_Button*, void*); 54 | extern void replaceTextureButton_cb(Fl_Button*, void*); 55 | 56 | class MainUI : public Fl_Double_Window { 57 | void _MainUI(); 58 | public: 59 | MainUI(int X, int Y, int W, int H, const char *L = 0); 60 | MainUI(int W, int H, const char *L = 0); 61 | MainUI(); 62 | static Fl_Menu_Item menu_[]; 63 | static Fl_Menu_Item *viewDepthToggle; 64 | private: 65 | inline void cb_viewDepthToggle_i(Fl_Menu_*, void*); 66 | static void cb_viewDepthToggle(Fl_Menu_*, void*); 67 | public: 68 | Fl_Button *paintModeToggle; 69 | glwidget *glviewport; 70 | Fl_Group *planeSettings; 71 | ColorPreview *colorPreviewer; 72 | Fl_Button *setColor[4]; 73 | Fl_Value_Slider *colorSlider[3]; 74 | Fl_Round_Button *blendModeRadio[5]; 75 | Fl_Round_Button *lightModeRadio[3]; 76 | Fl_Check_Button *doubleSidedCheck; 77 | Fl_Round_Button *primTypeRadio[2]; 78 | Fl_Value_Output *primitiveIndexOutput; 79 | Fl_Group *textureSettings; 80 | TexturePreview *texturePreviewer; 81 | Fl_Spinner *textureZoom; 82 | Fl_Check_Button *primTexturedToggle; 83 | Fl_Browser *textureBrowser; 84 | }; 85 | #endif 86 | -------------------------------------------------------------------------------- /configform.cxx: -------------------------------------------------------------------------------- 1 | // generated by Fast Light User Interface Designer (fluid) version 1.0304 2 | 3 | #include "configform.h" 4 | 5 | void ConfigUI::cb_Cancel_i(Fl_Button*, void*) { 6 | hide(); 7 | } 8 | void ConfigUI::cb_Cancel(Fl_Button* o, void* v) { 9 | ((ConfigUI*)(o->parent()))->cb_Cancel_i(o,v); 10 | } 11 | ConfigUI::ConfigUI(int X, int Y, int W, int H, const char *L) 12 | : Fl_Double_Window(X, Y, W, H, L) { 13 | _ConfigUI(); 14 | } 15 | 16 | ConfigUI::ConfigUI(int W, int H, const char *L) 17 | : Fl_Double_Window(0, 0, W, H, L) { 18 | clear_flag(16); 19 | _ConfigUI(); 20 | } 21 | 22 | ConfigUI::ConfigUI() 23 | : Fl_Double_Window(0, 0, 404, 308, "Configuration") { 24 | clear_flag(16); 25 | _ConfigUI(); 26 | } 27 | 28 | void ConfigUI::_ConfigUI() { 29 | this->box(FL_FLAT_BOX); 30 | this->color(FL_BACKGROUND_COLOR); 31 | this->selection_color(FL_BACKGROUND_COLOR); 32 | this->labeltype(FL_NO_LABEL); 33 | this->labelfont(0); 34 | this->labelsize(14); 35 | this->labelcolor(FL_FOREGROUND_COLOR); 36 | this->align(Fl_Align(FL_ALIGN_TOP)); 37 | this->when(FL_WHEN_RELEASE); 38 | { configTabs = new Fl_Tabs(10, 10, 384, 252); 39 | { editorGroup = new Fl_Group(10, 29, 384, 233, "Editor"); 40 | editorGroup->labelsize(12); 41 | { Fl_Group* o = new Fl_Group(205, 50, 179, 46, "OpenGL"); 42 | o->box(FL_ENGRAVED_BOX); 43 | o->labelsize(12); 44 | o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); 45 | { singleBufferToggle = new Fl_Check_Button(211, 59, 165, 15, "Single-buffer mode"); 46 | singleBufferToggle->tooltip("Forces OpenGL context to run in single buffer mode. Can fix Aero being disabl\ 47 | ed with newer NVIDIA cards on Windows 7. (restart required)"); 48 | singleBufferToggle->down_box(FL_DOWN_BOX); 49 | singleBufferToggle->labelsize(12); 50 | } // Fl_Check_Button* singleBufferToggle 51 | { shadersToggle = new Fl_Check_Button(211, 75, 165, 15, "GLSL Shaders"); 52 | shadersToggle->tooltip("Enables use of GLSL shaders to accurately represent textured polygons if supp\ 53 | orted by your hardware. (restart required)"); 54 | shadersToggle->down_box(FL_DOWN_BOX); 55 | shadersToggle->labelsize(12); 56 | } // Fl_Check_Button* shadersToggle 57 | o->end(); 58 | } // Fl_Group* o 59 | { Fl_Group* o = new Fl_Group(20, 50, 180, 46, "Editor"); 60 | o->box(FL_ENGRAVED_BOX); 61 | o->labelsize(12); 62 | o->align(Fl_Align(FL_ALIGN_TOP_LEFT)); 63 | { systemColorsToggle = new Fl_Check_Button(28, 59, 164, 15, "Use System Colors"); 64 | systemColorsToggle->tooltip("Use the system\'s color scheme."); 65 | systemColorsToggle->down_box(FL_DOWN_BOX); 66 | systemColorsToggle->labelsize(12); 67 | } // Fl_Check_Button* systemColorsToggle 68 | { easterEggToggle = new Fl_Check_Button(28, 75, 164, 15, "NSFW Easter Egg :)"); 69 | easterEggToggle->tooltip("The question is... Can you find it? :)"); 70 | easterEggToggle->down_box(FL_DOWN_BOX); 71 | easterEggToggle->labelsize(12); 72 | } // Fl_Check_Button* easterEggToggle 73 | o->end(); 74 | } // Fl_Group* o 75 | editorGroup->end(); 76 | } // Fl_Group* editorGroup 77 | configTabs->end(); 78 | } // Fl_Tabs* configTabs 79 | { Fl_Button* o = new Fl_Button(316, 270, 78, 25, "Cancel"); 80 | o->labelsize(12); 81 | o->callback((Fl_Callback*)cb_Cancel); 82 | } // Fl_Button* o 83 | { Fl_Button* o = new Fl_Button(232, 270, 78, 25, "Ok"); 84 | o->labelsize(12); 85 | o->callback((Fl_Callback*)configOkayButton_cb); 86 | } // Fl_Button* o 87 | set_modal(); 88 | end(); 89 | } 90 | -------------------------------------------------------------------------------- /ConfigClass.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | 7 | /* 8 | * File: ConfigClass.cpp 9 | * Author: lameguy64 10 | * 11 | * Created on April 17, 2018, 10:41 AM 12 | */ 13 | 14 | #ifndef __WIN32__ 15 | #include 16 | #endif 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "ConfigClass.h" 29 | #include "configform.h" 30 | #include "global.h" 31 | #include "common/common.h" 32 | 33 | ConfigClass::ConfigClass() { 34 | 35 | systemcolors_enable = false; 36 | nsfw_enable = false; 37 | 38 | singlebuffer_enable = false; 39 | glsl_enable = false; 40 | 41 | } 42 | 43 | ConfigClass::~ConfigClass() { 44 | } 45 | 46 | int ConfigClass::LoadConfig() { 47 | 48 | tinyxml2::XMLDocument document; 49 | 50 | if ( document.LoadFile( config_file.c_str() ) != tinyxml2::XML_SUCCESS ) { 51 | return 0; 52 | } 53 | 54 | tinyxml2::XMLElement* base = document.FirstChildElement( "smxtool_config" ); 55 | 56 | if ( base ) { 57 | 58 | tinyxml2::XMLElement* o = base->FirstChildElement( "interface" ); 59 | if ( o ) { 60 | 61 | systemcolors_enable = o->IntAttribute( "use_system_colors" ); 62 | nsfw_enable = o->IntAttribute( "nsfw" ); 63 | 64 | } 65 | 66 | o = base->FirstChildElement( "opengl" ); 67 | if ( o ) { 68 | 69 | singlebuffer_enable = o->IntAttribute( "single_buffer" ); 70 | glsl_enable = o->IntAttribute( "glsl" ); 71 | 72 | } 73 | 74 | } 75 | 76 | return 1; 77 | } 78 | 79 | int ConfigClass::Saveconfig() { 80 | 81 | tinyxml2::XMLDocument document; 82 | 83 | tinyxml2::XMLElement* base = document.NewElement( "smxtool_config" ); 84 | 85 | tinyxml2::XMLElement* o = document.NewElement( "interface" ); 86 | o->SetAttribute( "use_system_colors", systemcolors_enable ); 87 | o->SetAttribute( "nsfw", nsfw_enable ); 88 | base->InsertEndChild( o ); 89 | 90 | o = document.NewElement( "opengl" ); 91 | o->SetAttribute( "single_buffer", singlebuffer_enable ); 92 | o->SetAttribute( "glsl", glsl_enable ); 93 | base->InsertEndChild( o ); 94 | 95 | o = document.NewElement( "externals" ); 96 | 97 | base->InsertEndChild( o ); 98 | 99 | document.InsertEndChild( base ); 100 | 101 | if ( document.SaveFile( config_file.c_str() ) != tinyxml2::XML_SUCCESS ) { 102 | return 0; 103 | } 104 | 105 | return 1; 106 | } 107 | 108 | 109 | static ConfigUI *config_ui; 110 | 111 | void settingsItem_cb(Fl_Menu_* widget, long userdata) { 112 | 113 | config_ui = new ConfigUI(); 114 | 115 | config_ui->position( (ui->x()+ui->w()/2)-config_ui->w()/2, 116 | (ui->y()+ui->h()/2)-config_ui->h()/2 ); 117 | 118 | config_ui->systemColorsToggle->value( config.systemcolors_enable ); 119 | config_ui->easterEggToggle->value( config.nsfw_enable ); 120 | config_ui->singleBufferToggle->value( config.singlebuffer_enable ); 121 | config_ui->shadersToggle->value( config.glsl_enable ); 122 | 123 | 124 | config_ui->show(); 125 | 126 | while( config_ui->shown() ) { 127 | Fl::wait(); 128 | } 129 | 130 | delete config_ui; 131 | 132 | } 133 | 134 | void configOkayButton_cb(Fl_Button* widget, void* userdata) { 135 | 136 | config.systemcolors_enable = config_ui->systemColorsToggle->value(); 137 | config.nsfw_enable = config_ui->easterEggToggle->value(); 138 | 139 | config.singlebuffer_enable = config_ui->singleBufferToggle->value(); 140 | config.glsl_enable = config_ui->shadersToggle->value(); 141 | 142 | if ( !config.Saveconfig() ) { 143 | fl_message_title( "Error Saving Config" ); 144 | fl_message( "Unable to write configuration file." ); 145 | return; 146 | } 147 | 148 | config_ui->hide(); 149 | } -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # There exist several targets which are by default empty and which can be 3 | # used for execution of your targets. These targets are usually executed 4 | # before and after some main targets. They are: 5 | # 6 | # .build-pre: called before 'build' target 7 | # .build-post: called after 'build' target 8 | # .clean-pre: called before 'clean' target 9 | # .clean-post: called after 'clean' target 10 | # .clobber-pre: called before 'clobber' target 11 | # .clobber-post: called after 'clobber' target 12 | # .all-pre: called before 'all' target 13 | # .all-post: called after 'all' target 14 | # .help-pre: called before 'help' target 15 | # .help-post: called after 'help' target 16 | # 17 | # Targets beginning with '.' are not intended to be called on their own. 18 | # 19 | # Main targets can be executed directly, and they are: 20 | # 21 | # build build a specific configuration 22 | # clean remove built files from a configuration 23 | # clobber remove all built files 24 | # all build all configurations 25 | # help print help mesage 26 | # 27 | # Targets .build-impl, .clean-impl, .clobber-impl, .all-impl, and 28 | # .help-impl are implemented in nbproject/makefile-impl.mk. 29 | # 30 | # Available make variables: 31 | # 32 | # CND_BASEDIR base directory for relative paths 33 | # CND_DISTDIR default top distribution directory (build artifacts) 34 | # CND_BUILDDIR default top build directory (object files, ...) 35 | # CONF name of current configuration 36 | # CND_PLATFORM_${CONF} platform name (current configuration) 37 | # CND_ARTIFACT_DIR_${CONF} directory of build artifact (current configuration) 38 | # CND_ARTIFACT_NAME_${CONF} name of build artifact (current configuration) 39 | # CND_ARTIFACT_PATH_${CONF} path to build artifact (current configuration) 40 | # CND_PACKAGE_DIR_${CONF} directory of package (current configuration) 41 | # CND_PACKAGE_NAME_${CONF} name of package (current configuration) 42 | # CND_PACKAGE_PATH_${CONF} path to package (current configuration) 43 | # 44 | # NOCDDL 45 | 46 | 47 | # Environment 48 | MKDIR=mkdir 49 | CP=cp 50 | CCADMIN=CCadmin 51 | 52 | 53 | # build 54 | build: .build-post 55 | 56 | .build-pre: 57 | # Add your pre 'build' code here... 58 | 59 | .build-post: .build-impl 60 | # Add your post 'build' code here... 61 | 62 | 63 | # clean 64 | clean: .clean-post 65 | 66 | .clean-pre: 67 | # Add your pre 'clean' code here... 68 | 69 | .clean-post: .clean-impl 70 | # Add your post 'clean' code here... 71 | 72 | 73 | # clobber 74 | clobber: .clobber-post 75 | 76 | .clobber-pre: 77 | # Add your pre 'clobber' code here... 78 | 79 | .clobber-post: .clobber-impl 80 | # Add your post 'clobber' code here... 81 | 82 | 83 | # all 84 | all: .all-post 85 | 86 | .all-pre: 87 | # Add your pre 'all' code here... 88 | 89 | .all-post: .all-impl 90 | # Add your post 'all' code here... 91 | 92 | 93 | # build tests 94 | build-tests: .build-tests-post 95 | 96 | .build-tests-pre: 97 | # Add your pre 'build-tests' code here... 98 | 99 | .build-tests-post: .build-tests-impl 100 | # Add your post 'build-tests' code here... 101 | 102 | 103 | # run tests 104 | test: .test-post 105 | 106 | .test-pre: build-tests 107 | # Add your pre 'test' code here... 108 | 109 | .test-post: .test-impl 110 | # Add your post 'test' code here... 111 | 112 | 113 | # help 114 | help: .help-post 115 | 116 | .help-pre: 117 | # Add your pre 'help' code here... 118 | 119 | .help-post: .help-impl 120 | # Add your post 'help' code here... 121 | 122 | 123 | 124 | # include project implementation makefile 125 | include nbproject/Makefile-impl.mk 126 | 127 | # include project make variables 128 | include nbproject/Makefile-variables.mk 129 | -------------------------------------------------------------------------------- /ShaderClass.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * File: ShaderClass.cpp 3 | * Author: lameguy64 4 | * 5 | * Created on November 15, 2017, 12:29 PM 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "ShaderClass.h" 13 | 14 | 15 | // Shader program code 16 | static const GLchar* gl_v_shader = 17 | { 18 | "uniform mat4 gl_ModelViewMatrix;\n" 19 | "uniform mat4 gl_ProjectionMatrix;\n" 20 | "attribute vec4 gl_Vertex;\n" 21 | "attribute vec4 gl_Color;\n" 22 | "varying vec4 vColor;\n" 23 | "void main() {\n" 24 | " gl_TexCoord[0]=gl_MultiTexCoord0;\n" 25 | " gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;\n" 26 | " vColor = gl_Color;\n" 27 | "}\n" 28 | }; 29 | 30 | static const GLchar* gl_f_shader = 31 | { 32 | "uniform sampler2D texture;\n" 33 | "uniform bool tex_enable;\n" 34 | "varying vec4 vColor;\n" 35 | "void main() {\n" 36 | " if (tex_enable) {\n" 37 | " vec4 fColor;\n" 38 | " float alpha;\n" 39 | " fColor = texture2D(texture, gl_TexCoord[0].st);\n" 40 | " alpha = fColor.w*vColor.w;\n" 41 | " fColor *= (vColor*2.0);\n" 42 | " fColor.w = alpha;\n" 43 | " gl_FragColor = fColor;\n" 44 | " } else {\n" 45 | " gl_FragColor = vColor;\n" 46 | " }\n" 47 | "}\n" 48 | }; 49 | 50 | 51 | int ShaderClass::CompileShader(const GLchar* shader_code, GLuint shader_type) 52 | { 53 | GLuint shader = glCreateShaderObjectARB( shader_type ); 54 | 55 | glShaderSourceARB( shader, 1, &shader_code, nullptr ); 56 | 57 | GLint compiled; 58 | 59 | glCompileShaderARB( shader ); 60 | glGetObjectParameterivARB( shader, GL_COMPILE_STATUS, &compiled ); 61 | 62 | GLint blen = 0; 63 | GLsizei slen = 0; 64 | glGetShaderiv( shader, GL_INFO_LOG_LENGTH , &blen); 65 | 66 | if (blen > 1) 67 | { 68 | GLchar* compiler_log = (GLchar*)malloc( blen ); 69 | glGetInfoLogARB( shader, blen, &slen, compiler_log ); 70 | 71 | std::cout << "ERROR: Failed to compile shader program." << std::endl; 72 | std::cout << compiler_log << std::endl; 73 | free(compiler_log); 74 | 75 | glDeleteObjectARB( shader ); 76 | 77 | return 0; 78 | } 79 | 80 | return shader; 81 | } 82 | 83 | ShaderClass::ShaderClass() 84 | { 85 | vertex_shader = 0; 86 | fragment_shader = 0; 87 | shader_program = 0; 88 | is_initialized = false; 89 | } 90 | 91 | ShaderClass::~ShaderClass() 92 | { 93 | if ( vertex_shader ) 94 | { 95 | glDeleteObjectARB( vertex_shader ); 96 | } 97 | if ( fragment_shader ) 98 | { 99 | glDeleteObjectARB( fragment_shader ); 100 | } 101 | if ( shader_program ) 102 | { 103 | glDeleteObjectARB( shader_program ); 104 | } 105 | } 106 | 107 | int ShaderClass::Initialize() 108 | { 109 | vertex_shader = CompileShader( gl_v_shader, GL_VERTEX_SHADER ); 110 | 111 | if ( !vertex_shader ) 112 | { 113 | return 1; 114 | } 115 | 116 | fragment_shader = CompileShader( gl_f_shader, GL_FRAGMENT_SHADER ); 117 | 118 | if ( !fragment_shader ) 119 | { 120 | return 1; 121 | } 122 | 123 | shader_program = glCreateProgram(); 124 | 125 | glAttachShader( shader_program, vertex_shader ); 126 | glAttachShader( shader_program, fragment_shader ); 127 | 128 | glLinkProgram( shader_program ); 129 | 130 | glUseProgramObjectARB( shader_program ); 131 | 132 | uniform_texture_enable = glGetUniformLocationARB( shader_program, 133 | "tex_enable" ); 134 | 135 | //std::cout << "tex_enable = " << uniform_texture_enable << std::endl; 136 | 137 | is_initialized = true; 138 | return 0; 139 | } 140 | 141 | void ShaderClass::TextureEnable(bool enable) 142 | { 143 | if ( !is_initialized ) 144 | { 145 | return; 146 | } 147 | glUniform1iARB( uniform_texture_enable, enable ); 148 | } 149 | 150 | void ShaderClass::ActivateShader() 151 | { 152 | if ( !is_initialized ) 153 | { 154 | return; 155 | } 156 | glUseProgramObjectARB( shader_program ); 157 | } 158 | 159 | void ShaderClass::DeactivateShader() 160 | { 161 | if ( !is_initialized ) 162 | { 163 | return; 164 | } 165 | glUseProgramObjectARB( 0 ); 166 | } -------------------------------------------------------------------------------- /nbproject/Makefile-impl.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Generated Makefile - do not edit! 3 | # 4 | # Edit the Makefile in the project folder instead (../Makefile). Each target 5 | # has a pre- and a post- target defined where you can add customization code. 6 | # 7 | # This makefile implements macros and targets common to all configurations. 8 | # 9 | # NOCDDL 10 | 11 | 12 | # Building and Cleaning subprojects are done by default, but can be controlled with the SUB 13 | # macro. If SUB=no, subprojects will not be built or cleaned. The following macro 14 | # statements set BUILD_SUB-CONF and CLEAN_SUB-CONF to .build-reqprojects-conf 15 | # and .clean-reqprojects-conf unless SUB has the value 'no' 16 | SUB_no=NO 17 | SUBPROJECTS=${SUB_${SUB}} 18 | BUILD_SUBPROJECTS_=.build-subprojects 19 | BUILD_SUBPROJECTS_NO= 20 | BUILD_SUBPROJECTS=${BUILD_SUBPROJECTS_${SUBPROJECTS}} 21 | CLEAN_SUBPROJECTS_=.clean-subprojects 22 | CLEAN_SUBPROJECTS_NO= 23 | CLEAN_SUBPROJECTS=${CLEAN_SUBPROJECTS_${SUBPROJECTS}} 24 | 25 | 26 | # Project Name 27 | PROJECTNAME=smxtool 28 | 29 | # Active Configuration 30 | DEFAULTCONF=Debug 31 | CONF=${DEFAULTCONF} 32 | 33 | # All Configurations 34 | ALLCONFS=Debug Release Debug-linux Release-linux 35 | 36 | 37 | # build 38 | .build-impl: .build-pre .validate-impl .depcheck-impl 39 | @#echo "=> Running $@... Configuration=$(CONF)" 40 | "${MAKE}" -f nbproject/Makefile-${CONF}.mk QMAKE=${QMAKE} SUBPROJECTS=${SUBPROJECTS} .build-conf 41 | 42 | 43 | # clean 44 | .clean-impl: .clean-pre .validate-impl .depcheck-impl 45 | @#echo "=> Running $@... Configuration=$(CONF)" 46 | "${MAKE}" -f nbproject/Makefile-${CONF}.mk QMAKE=${QMAKE} SUBPROJECTS=${SUBPROJECTS} .clean-conf 47 | 48 | 49 | # clobber 50 | .clobber-impl: .clobber-pre .depcheck-impl 51 | @#echo "=> Running $@..." 52 | for CONF in ${ALLCONFS}; \ 53 | do \ 54 | "${MAKE}" -f nbproject/Makefile-$${CONF}.mk QMAKE=${QMAKE} SUBPROJECTS=${SUBPROJECTS} .clean-conf; \ 55 | done 56 | 57 | # all 58 | .all-impl: .all-pre .depcheck-impl 59 | @#echo "=> Running $@..." 60 | for CONF in ${ALLCONFS}; \ 61 | do \ 62 | "${MAKE}" -f nbproject/Makefile-$${CONF}.mk QMAKE=${QMAKE} SUBPROJECTS=${SUBPROJECTS} .build-conf; \ 63 | done 64 | 65 | # build tests 66 | .build-tests-impl: .build-impl .build-tests-pre 67 | @#echo "=> Running $@... Configuration=$(CONF)" 68 | "${MAKE}" -f nbproject/Makefile-${CONF}.mk SUBPROJECTS=${SUBPROJECTS} .build-tests-conf 69 | 70 | # run tests 71 | .test-impl: .build-tests-impl .test-pre 72 | @#echo "=> Running $@... Configuration=$(CONF)" 73 | "${MAKE}" -f nbproject/Makefile-${CONF}.mk SUBPROJECTS=${SUBPROJECTS} .test-conf 74 | 75 | # dependency checking support 76 | .depcheck-impl: 77 | @echo "# This code depends on make tool being used" >.dep.inc 78 | @if [ -n "${MAKE_VERSION}" ]; then \ 79 | echo "DEPFILES=\$$(wildcard \$$(addsuffix .d, \$${OBJECTFILES} \$${TESTOBJECTFILES}))" >>.dep.inc; \ 80 | echo "ifneq (\$${DEPFILES},)" >>.dep.inc; \ 81 | echo "include \$${DEPFILES}" >>.dep.inc; \ 82 | echo "endif" >>.dep.inc; \ 83 | else \ 84 | echo ".KEEP_STATE:" >>.dep.inc; \ 85 | echo ".KEEP_STATE_FILE:.make.state.\$${CONF}" >>.dep.inc; \ 86 | fi 87 | 88 | # configuration validation 89 | .validate-impl: 90 | @if [ ! -f nbproject/Makefile-${CONF}.mk ]; \ 91 | then \ 92 | echo ""; \ 93 | echo "Error: can not find the makefile for configuration '${CONF}' in project ${PROJECTNAME}"; \ 94 | echo "See 'make help' for details."; \ 95 | echo "Current directory: " `pwd`; \ 96 | echo ""; \ 97 | fi 98 | @if [ ! -f nbproject/Makefile-${CONF}.mk ]; \ 99 | then \ 100 | exit 1; \ 101 | fi 102 | 103 | 104 | # help 105 | .help-impl: .help-pre 106 | @echo "This makefile supports the following configurations:" 107 | @echo " ${ALLCONFS}" 108 | @echo "" 109 | @echo "and the following targets:" 110 | @echo " build (default target)" 111 | @echo " clean" 112 | @echo " clobber" 113 | @echo " all" 114 | @echo " help" 115 | @echo "" 116 | @echo "Makefile Usage:" 117 | @echo " make [CONF=] [SUB=no] build" 118 | @echo " make [CONF=] [SUB=no] clean" 119 | @echo " make [SUB=no] clobber" 120 | @echo " make [SUB=no] all" 121 | @echo " make help" 122 | @echo "" 123 | @echo "Target 'build' will build a specific configuration and, unless 'SUB=no'," 124 | @echo " also build subprojects." 125 | @echo "Target 'clean' will clean a specific configuration and, unless 'SUB=no'," 126 | @echo " also clean subprojects." 127 | @echo "Target 'clobber' will remove all built files from all configurations and," 128 | @echo " unless 'SUB=no', also from subprojects." 129 | @echo "Target 'all' will will build all configurations and, unless 'SUB=no'," 130 | @echo " also build subprojects." 131 | @echo "Target 'help' prints this message." 132 | @echo "" 133 | 134 | -------------------------------------------------------------------------------- /nbproject/Makefile-Release-linux.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Generated Makefile - do not edit! 3 | # 4 | # Edit the Makefile in the project folder instead (../Makefile). Each target 5 | # has a -pre and a -post target defined where you can add customized code. 6 | # 7 | # This makefile implements configuration specific macros and targets. 8 | 9 | 10 | # Environment 11 | MKDIR=mkdir 12 | CP=cp 13 | GREP=grep 14 | NM=nm 15 | CCADMIN=CCadmin 16 | RANLIB=ranlib 17 | CC=gcc 18 | CCC=g++ 19 | CXX=g++ 20 | FC=gfortran 21 | AS=as 22 | 23 | # Macros 24 | CND_PLATFORM=GNU-Linux 25 | CND_DLIB_EXT=so 26 | CND_CONF=Release-linux 27 | CND_DISTDIR=dist 28 | CND_BUILDDIR=build 29 | 30 | # Include project Makefile 31 | include Makefile 32 | 33 | # Object Directory 34 | OBJECTDIR=${CND_BUILDDIR}/${CND_CONF}/${CND_PLATFORM} 35 | 36 | # Object Files 37 | OBJECTFILES= \ 38 | ${OBJECTDIR}/ColorPreview.o \ 39 | ${OBJECTDIR}/ConfigClass.o \ 40 | ${OBJECTDIR}/ContextClass.o \ 41 | ${OBJECTDIR}/ModelClass.o \ 42 | ${OBJECTDIR}/ShaderClass.o \ 43 | ${OBJECTDIR}/TextureClass.o \ 44 | ${OBJECTDIR}/about.o \ 45 | ${OBJECTDIR}/common/common.o \ 46 | ${OBJECTDIR}/common/smx.o \ 47 | ${OBJECTDIR}/common/timloader.o \ 48 | ${OBJECTDIR}/configform.o \ 49 | ${OBJECTDIR}/glwidget.o \ 50 | ${OBJECTDIR}/main.o \ 51 | ${OBJECTDIR}/mainform.o \ 52 | ${OBJECTDIR}/texturepreview.o 53 | 54 | 55 | # C Compiler Flags 56 | CFLAGS= 57 | 58 | # CC Compiler Flags 59 | CCFLAGS=-march=corei7 60 | CXXFLAGS=-march=corei7 61 | 62 | # Fortran Compiler Flags 63 | FFLAGS= 64 | 65 | # Assembler Flags 66 | ASFLAGS= 67 | 68 | # Link Libraries and Options 69 | LDLIBSOPTIONS=-lfltk_images -lfltk_gl -lfltk -ltinyxml2 -lGL -lGLU -lGLEW -ldl -lX11 -lXext -lXinerama -lXft -lXrender -lXdamage -lXfixes -lXcursor -lpthread -lfontconfig 70 | 71 | # Build Targets 72 | .build-conf: ${BUILD_SUBPROJECTS} 73 | "${MAKE}" -f nbproject/Makefile-${CND_CONF}.mk ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/smxtool 74 | 75 | ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/smxtool: ${OBJECTFILES} 76 | ${MKDIR} -p ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM} 77 | ${LINK.cc} -o ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/smxtool ${OBJECTFILES} ${LDLIBSOPTIONS} 78 | 79 | ${OBJECTDIR}/ColorPreview.o: ColorPreview.cpp 80 | ${MKDIR} -p ${OBJECTDIR} 81 | ${RM} "$@.d" 82 | $(COMPILE.cc) -O2 -I/../common -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/ColorPreview.o ColorPreview.cpp 83 | 84 | ${OBJECTDIR}/ConfigClass.o: ConfigClass.cpp 85 | ${MKDIR} -p ${OBJECTDIR} 86 | ${RM} "$@.d" 87 | $(COMPILE.cc) -O2 -I/../common -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/ConfigClass.o ConfigClass.cpp 88 | 89 | ${OBJECTDIR}/ContextClass.o: ContextClass.cpp 90 | ${MKDIR} -p ${OBJECTDIR} 91 | ${RM} "$@.d" 92 | $(COMPILE.cc) -O2 -I/../common -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/ContextClass.o ContextClass.cpp 93 | 94 | ${OBJECTDIR}/ModelClass.o: ModelClass.cpp 95 | ${MKDIR} -p ${OBJECTDIR} 96 | ${RM} "$@.d" 97 | $(COMPILE.cc) -O2 -I/../common -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/ModelClass.o ModelClass.cpp 98 | 99 | ${OBJECTDIR}/ShaderClass.o: ShaderClass.cpp 100 | ${MKDIR} -p ${OBJECTDIR} 101 | ${RM} "$@.d" 102 | $(COMPILE.cc) -O2 -I/../common -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/ShaderClass.o ShaderClass.cpp 103 | 104 | ${OBJECTDIR}/TextureClass.o: TextureClass.cpp 105 | ${MKDIR} -p ${OBJECTDIR} 106 | ${RM} "$@.d" 107 | $(COMPILE.cc) -O2 -I/../common -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/TextureClass.o TextureClass.cpp 108 | 109 | ${OBJECTDIR}/about.o: about.cxx 110 | ${MKDIR} -p ${OBJECTDIR} 111 | ${RM} "$@.d" 112 | $(COMPILE.cc) -O2 -I/../common -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/about.o about.cxx 113 | 114 | ${OBJECTDIR}/common/common.o: common/common.cpp 115 | ${MKDIR} -p ${OBJECTDIR}/common 116 | ${RM} "$@.d" 117 | $(COMPILE.cc) -O2 -I/../common -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/common/common.o common/common.cpp 118 | 119 | ${OBJECTDIR}/common/smx.o: common/smx.cpp 120 | ${MKDIR} -p ${OBJECTDIR}/common 121 | ${RM} "$@.d" 122 | $(COMPILE.cc) -O2 -I/../common -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/common/smx.o common/smx.cpp 123 | 124 | ${OBJECTDIR}/common/timloader.o: common/timloader.cpp 125 | ${MKDIR} -p ${OBJECTDIR}/common 126 | ${RM} "$@.d" 127 | $(COMPILE.cc) -O2 -I/../common -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/common/timloader.o common/timloader.cpp 128 | 129 | ${OBJECTDIR}/configform.o: configform.cxx 130 | ${MKDIR} -p ${OBJECTDIR} 131 | ${RM} "$@.d" 132 | $(COMPILE.cc) -O2 -I/../common -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/configform.o configform.cxx 133 | 134 | ${OBJECTDIR}/glwidget.o: glwidget.cpp 135 | ${MKDIR} -p ${OBJECTDIR} 136 | ${RM} "$@.d" 137 | $(COMPILE.cc) -O2 -I/../common -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/glwidget.o glwidget.cpp 138 | 139 | ${OBJECTDIR}/main.o: main.cpp 140 | ${MKDIR} -p ${OBJECTDIR} 141 | ${RM} "$@.d" 142 | $(COMPILE.cc) -O2 -I/../common -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/main.o main.cpp 143 | 144 | ${OBJECTDIR}/mainform.o: mainform.cxx 145 | ${MKDIR} -p ${OBJECTDIR} 146 | ${RM} "$@.d" 147 | $(COMPILE.cc) -O2 -I/../common -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/mainform.o mainform.cxx 148 | 149 | ${OBJECTDIR}/texturepreview.o: texturepreview.cpp 150 | ${MKDIR} -p ${OBJECTDIR} 151 | ${RM} "$@.d" 152 | $(COMPILE.cc) -O2 -I/../common -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/texturepreview.o texturepreview.cpp 153 | 154 | # Subprojects 155 | .build-subprojects: 156 | 157 | # Clean Targets 158 | .clean-conf: ${CLEAN_SUBPROJECTS} 159 | ${RM} -r ${CND_BUILDDIR}/${CND_CONF} 160 | 161 | # Subprojects 162 | .clean-subprojects: 163 | 164 | # Enable dependency checking 165 | .dep.inc: .depcheck-impl 166 | 167 | include .dep.inc 168 | -------------------------------------------------------------------------------- /common/timloader.cpp: -------------------------------------------------------------------------------- 1 | #include "timloader.h" 2 | 3 | 4 | TimLoaderClass::TimLoaderClass() { 5 | 6 | clut = NULL; 7 | pixels = NULL; 8 | 9 | } 10 | 11 | TimLoaderClass::~TimLoaderClass() { 12 | 13 | if (clut != NULL) 14 | free(clut); 15 | 16 | if (pixels != NULL) 17 | free(pixels); 18 | 19 | } 20 | 21 | TIM_ERROR TimLoaderClass::LoadFile(const char* filename) { 22 | 23 | FILE* fp = fopen(filename, "rb"); 24 | 25 | if (fp == 0) 26 | return TIM_ERROR_NOTFOUND; 27 | 28 | fread(&header, sizeof(TIM_HEADER), 1, fp); 29 | 30 | if (header.id != 0x00000010) { 31 | 32 | fclose(fp); 33 | return TIM_ERROR_INVALID; 34 | 35 | } 36 | 37 | if (clut != NULL) 38 | free(clut); 39 | 40 | if (pixels != NULL) 41 | free(pixels); 42 | 43 | // Is CLUT present? 44 | if (header.flag.cf) { 45 | 46 | fread(&clutBlock, sizeof(TIM_BLOCK), 1, fp); 47 | 48 | /* 49 | printf("CLUT Stats:\n"); 50 | printf("BNUM:%d\n", clutBlock.bnum); 51 | printf("X:%d Y:%d\n", clutBlock.x, clutBlock.y); 52 | printf("W:%d H:%d\n", clutBlock.w, clutBlock.h); 53 | */ 54 | 55 | clut = (TIM_PIXEL*)malloc(sizeof(TIM_PIXEL)*clutBlock.w); 56 | fread(clut, sizeof(TIM_PIXEL), clutBlock.w, fp); 57 | 58 | } 59 | 60 | fread(&pixelBlock, sizeof(TIM_BLOCK), 1, fp); 61 | 62 | /* 63 | printf("PIXEL Stats:\n"); 64 | printf("BNUM:%d\n", pixelBlock.bnum); 65 | printf("X:%d Y:%d\n", pixelBlock.x, pixelBlock.y); 66 | printf("W:%d H:%d\n", pixelBlock.w, pixelBlock.h); 67 | */ 68 | 69 | pixels = malloc((2*pixelBlock.w)*pixelBlock.h); 70 | fread(pixels, 2*pixelBlock.w, pixelBlock.h, fp); 71 | 72 | fclose(fp); 73 | 74 | return TIM_ERROR_NONE; 75 | 76 | } 77 | 78 | void TimLoaderClass::ConvertToRGBA(void *output) { 79 | 80 | typedef struct { 81 | unsigned char r; 82 | unsigned char g; 83 | unsigned char b; 84 | unsigned char a; 85 | } PIXEL_RGB; 86 | 87 | switch(header.flag.pmode) { 88 | case 0: // 4-bit 89 | { 90 | 91 | unsigned char *srcpix = (unsigned char*)pixels; 92 | PIXEL_RGB *dstpix = (PIXEL_RGB*)output; 93 | 94 | for(int py=0; py>4].r<<3; 113 | dstpix[1].g = clut[srcpix[0]>>4].g<<3; 114 | dstpix[1].b = clut[srcpix[0]>>4].b<<3; 115 | 116 | if ( clut[srcpix[0]>>4].i ) { 117 | dstpix[1].a = 255; 118 | } else { 119 | if ( ( dstpix[1].r + dstpix[1].g + dstpix[1].b ) == 0 ) { 120 | dstpix[1].a = 0; 121 | } else { 122 | dstpix[1].a = 255; 123 | } 124 | } 125 | 126 | dstpix[2].r = clut[srcpix[1]&0xf].r<<3; 127 | dstpix[2].g = clut[srcpix[1]&0xf].g<<3; 128 | dstpix[2].b = clut[srcpix[1]&0xf].b<<3; 129 | 130 | if ( clut[srcpix[1]&0xf].i ) { 131 | dstpix[2].a = 255; 132 | } else { 133 | if ( ( dstpix[2].r + dstpix[2].g + dstpix[2].b ) == 0 ) { 134 | dstpix[2].a = 0; 135 | } else { 136 | dstpix[2].a = 255; 137 | } 138 | } 139 | 140 | dstpix[3].r = clut[srcpix[1]>>4].r<<3; 141 | dstpix[3].g = clut[srcpix[1]>>4].g<<3; 142 | dstpix[3].b = clut[srcpix[1]>>4].b<<3; 143 | 144 | if ( clut[srcpix[1]>>4].i ) { 145 | dstpix[3].a = 255; 146 | } else { 147 | if ( ( dstpix[3].r + dstpix[3].g + dstpix[3].b ) == 0 ) { 148 | dstpix[3].a = 0; 149 | } else { 150 | dstpix[3].a = 255; 151 | } 152 | } 153 | 154 | srcpix += 2; 155 | dstpix += 4; 156 | 157 | } 158 | 159 | } 160 | 161 | } break; 162 | 163 | case 1: // 8-bit 164 | { 165 | 166 | unsigned char *srcpix = (unsigned char*)pixels; 167 | PIXEL_RGB *dstpix = (PIXEL_RGB*)output; 168 | 169 | for(int py=0; py>5)&0x1f)<<3; 203 | dstpix[0].b = ((*srcpix>>10)&0x1f)<<3; 204 | dstpix[0].a = 255; 205 | 206 | srcpix += 1; 207 | dstpix += 1; 208 | 209 | } 210 | 211 | } 212 | 213 | break; 214 | } 215 | 216 | } 217 | 218 | int TimLoaderClass::GetWidth() { 219 | 220 | switch(header.flag.pmode) { 221 | case 0: 222 | return pixelBlock.w*4; 223 | case 1: 224 | return pixelBlock.w*2; 225 | case 2: 226 | return pixelBlock.w; 227 | } 228 | 229 | return -1; 230 | 231 | } 232 | 233 | int TimLoaderClass::GetHeight() { 234 | 235 | return pixelBlock.h; 236 | 237 | } 238 | -------------------------------------------------------------------------------- /nbproject/Makefile-Debug-linux.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Generated Makefile - do not edit! 3 | # 4 | # Edit the Makefile in the project folder instead (../Makefile). Each target 5 | # has a -pre and a -post target defined where you can add customized code. 6 | # 7 | # This makefile implements configuration specific macros and targets. 8 | 9 | 10 | # Environment 11 | MKDIR=mkdir 12 | CP=cp 13 | GREP=grep 14 | NM=nm 15 | CCADMIN=CCadmin 16 | RANLIB=ranlib 17 | CC=gcc 18 | CCC=g++ 19 | CXX=g++ 20 | FC=gfortran 21 | AS=as 22 | 23 | # Macros 24 | CND_PLATFORM=GNU-Linux 25 | CND_DLIB_EXT=so 26 | CND_CONF=Debug-linux 27 | CND_DISTDIR=dist 28 | CND_BUILDDIR=build 29 | 30 | # Include project Makefile 31 | include Makefile 32 | 33 | # Object Directory 34 | OBJECTDIR=${CND_BUILDDIR}/${CND_CONF}/${CND_PLATFORM} 35 | 36 | # Object Files 37 | OBJECTFILES= \ 38 | ${OBJECTDIR}/ColorPreview.o \ 39 | ${OBJECTDIR}/ConfigClass.o \ 40 | ${OBJECTDIR}/ContextClass.o \ 41 | ${OBJECTDIR}/ModelClass.o \ 42 | ${OBJECTDIR}/ShaderClass.o \ 43 | ${OBJECTDIR}/TextureClass.o \ 44 | ${OBJECTDIR}/about.o \ 45 | ${OBJECTDIR}/common/common.o \ 46 | ${OBJECTDIR}/common/smx.o \ 47 | ${OBJECTDIR}/common/timloader.o \ 48 | ${OBJECTDIR}/configform.o \ 49 | ${OBJECTDIR}/glwidget.o \ 50 | ${OBJECTDIR}/main.o \ 51 | ${OBJECTDIR}/mainform.o \ 52 | ${OBJECTDIR}/texturepreview.o 53 | 54 | 55 | # C Compiler Flags 56 | CFLAGS= 57 | 58 | # CC Compiler Flags 59 | CCFLAGS= 60 | CXXFLAGS= 61 | 62 | # Fortran Compiler Flags 63 | FFLAGS= 64 | 65 | # Assembler Flags 66 | ASFLAGS= 67 | 68 | # Link Libraries and Options 69 | LDLIBSOPTIONS=-lfltk_images -lfltk_gl -lfltk -ltinyxml2 -lGL -lGLU -lGLEW -ldl -lX11 -lXext -lXinerama -lXft -lXrender -lXdamage -lXfixes -lXcursor -lpthread -lfontconfig 70 | 71 | # Build Targets 72 | .build-conf: ${BUILD_SUBPROJECTS} 73 | "${MAKE}" -f nbproject/Makefile-${CND_CONF}.mk ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/smxtool 74 | 75 | ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/smxtool: ${OBJECTFILES} 76 | ${MKDIR} -p ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM} 77 | ${LINK.cc} -o ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/smxtool ${OBJECTFILES} ${LDLIBSOPTIONS} 78 | 79 | ${OBJECTDIR}/ColorPreview.o: ColorPreview.cpp 80 | ${MKDIR} -p ${OBJECTDIR} 81 | ${RM} "$@.d" 82 | $(COMPILE.cc) -g -DDEBUG -I/../common -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/ColorPreview.o ColorPreview.cpp 83 | 84 | ${OBJECTDIR}/ConfigClass.o: ConfigClass.cpp 85 | ${MKDIR} -p ${OBJECTDIR} 86 | ${RM} "$@.d" 87 | $(COMPILE.cc) -g -DDEBUG -I/../common -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/ConfigClass.o ConfigClass.cpp 88 | 89 | ${OBJECTDIR}/ContextClass.o: ContextClass.cpp 90 | ${MKDIR} -p ${OBJECTDIR} 91 | ${RM} "$@.d" 92 | $(COMPILE.cc) -g -DDEBUG -I/../common -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/ContextClass.o ContextClass.cpp 93 | 94 | ${OBJECTDIR}/ModelClass.o: ModelClass.cpp 95 | ${MKDIR} -p ${OBJECTDIR} 96 | ${RM} "$@.d" 97 | $(COMPILE.cc) -g -DDEBUG -I/../common -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/ModelClass.o ModelClass.cpp 98 | 99 | ${OBJECTDIR}/ShaderClass.o: ShaderClass.cpp 100 | ${MKDIR} -p ${OBJECTDIR} 101 | ${RM} "$@.d" 102 | $(COMPILE.cc) -g -DDEBUG -I/../common -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/ShaderClass.o ShaderClass.cpp 103 | 104 | ${OBJECTDIR}/TextureClass.o: TextureClass.cpp 105 | ${MKDIR} -p ${OBJECTDIR} 106 | ${RM} "$@.d" 107 | $(COMPILE.cc) -g -DDEBUG -I/../common -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/TextureClass.o TextureClass.cpp 108 | 109 | ${OBJECTDIR}/about.o: about.cxx 110 | ${MKDIR} -p ${OBJECTDIR} 111 | ${RM} "$@.d" 112 | $(COMPILE.cc) -g -DDEBUG -I/../common -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/about.o about.cxx 113 | 114 | ${OBJECTDIR}/common/common.o: common/common.cpp 115 | ${MKDIR} -p ${OBJECTDIR}/common 116 | ${RM} "$@.d" 117 | $(COMPILE.cc) -g -DDEBUG -I/../common -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/common/common.o common/common.cpp 118 | 119 | ${OBJECTDIR}/common/smx.o: common/smx.cpp 120 | ${MKDIR} -p ${OBJECTDIR}/common 121 | ${RM} "$@.d" 122 | $(COMPILE.cc) -g -DDEBUG -I/../common -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/common/smx.o common/smx.cpp 123 | 124 | ${OBJECTDIR}/common/timloader.o: common/timloader.cpp 125 | ${MKDIR} -p ${OBJECTDIR}/common 126 | ${RM} "$@.d" 127 | $(COMPILE.cc) -g -DDEBUG -I/../common -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/common/timloader.o common/timloader.cpp 128 | 129 | ${OBJECTDIR}/configform.o: configform.cxx 130 | ${MKDIR} -p ${OBJECTDIR} 131 | ${RM} "$@.d" 132 | $(COMPILE.cc) -g -DDEBUG -I/../common -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/configform.o configform.cxx 133 | 134 | ${OBJECTDIR}/glwidget.o: glwidget.cpp 135 | ${MKDIR} -p ${OBJECTDIR} 136 | ${RM} "$@.d" 137 | $(COMPILE.cc) -g -DDEBUG -I/../common -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/glwidget.o glwidget.cpp 138 | 139 | ${OBJECTDIR}/main.o: main.cpp 140 | ${MKDIR} -p ${OBJECTDIR} 141 | ${RM} "$@.d" 142 | $(COMPILE.cc) -g -DDEBUG -I/../common -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/main.o main.cpp 143 | 144 | ${OBJECTDIR}/mainform.o: mainform.cxx 145 | ${MKDIR} -p ${OBJECTDIR} 146 | ${RM} "$@.d" 147 | $(COMPILE.cc) -g -DDEBUG -I/../common -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/mainform.o mainform.cxx 148 | 149 | ${OBJECTDIR}/texturepreview.o: texturepreview.cpp 150 | ${MKDIR} -p ${OBJECTDIR} 151 | ${RM} "$@.d" 152 | $(COMPILE.cc) -g -DDEBUG -I/../common -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/texturepreview.o texturepreview.cpp 153 | 154 | # Subprojects 155 | .build-subprojects: 156 | 157 | # Clean Targets 158 | .clean-conf: ${CLEAN_SUBPROJECTS} 159 | ${RM} -r ${CND_BUILDDIR}/${CND_CONF} 160 | 161 | # Subprojects 162 | .clean-subprojects: 163 | 164 | # Enable dependency checking 165 | .dep.inc: .depcheck-impl 166 | 167 | include .dep.inc 168 | -------------------------------------------------------------------------------- /common/common.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * File: common.cpp 3 | * Author: Lameguy64 4 | * 5 | * Created on October 3, 2017, 3:42 PM 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "common.h" 13 | 14 | int FileExists( const char* fileName ) 15 | { 16 | struct stat fd; 17 | 18 | if ( stat( fileName, &fd ) == 0 ) 19 | { 20 | return -1; 21 | } 22 | 23 | return 0; 24 | } 25 | 26 | void EnsureFileExtension( std::string& path, const char* extension ) 27 | { 28 | bool foundExtension = false; 29 | int i; 30 | 31 | for ( i=path.size()-1; i >= 0; i-- ) 32 | { 33 | if ( path[i] == '.' ) 34 | { 35 | foundExtension = true; 36 | break; 37 | } 38 | else if ( ( path[i] == '\\' ) || ( path[i] == '/' ) ) 39 | { 40 | break; 41 | } 42 | } 43 | 44 | if ( foundExtension ) 45 | { 46 | if ( path.compare( i+1, strlen( extension ), extension ) == 0 ) 47 | { 48 | return; 49 | } 50 | 51 | path.erase( i+1, (path.length()-i)-1 ); 52 | path.append( extension ); 53 | } 54 | else 55 | { 56 | path.append( "." ); 57 | path.append( extension ); 58 | } 59 | } 60 | 61 | void StripFileExtension( std::string& path ) 62 | { 63 | bool foundExtension = false; 64 | int i; 65 | 66 | for ( i=path.size()-1; i >= 0; i-- ) 67 | { 68 | if ( path[i] == '.' ) 69 | { 70 | foundExtension = true; 71 | break; 72 | } 73 | else if ( ( path[i] == '\\' ) || ( path[i] == '/' ) ) 74 | { 75 | break; 76 | } 77 | } 78 | 79 | if ( foundExtension ) 80 | { 81 | path.erase( i, path.length()-i ); 82 | } 83 | } 84 | 85 | void MakeFilePathRelative( std::string& fileName, const char* basePath ) 86 | { 87 | std::string tempA = fileName; 88 | std::string tempB = tempA; 89 | 90 | StripPathname( tempA ); 91 | StripFilename( tempB ); 92 | 93 | if ( CalculateRelativePath( tempB.c_str(), fileName, basePath ) == 0 ) 94 | { 95 | fileName = tempA + tempB; 96 | return; 97 | } 98 | 99 | #ifdef _WIN32 100 | fileName += "\\" + tempA; 101 | #else 102 | fileName += "/" + tempB; 103 | #endif 104 | } 105 | 106 | int CalculateRelativePath( const char* path, std::string& output, 107 | const char* basePath ) 108 | { 109 | int diffBegin = 0; 110 | int numDirs = 0; 111 | int inDir = false; 112 | 113 | char basePathed[MAX_PATH]; 114 | std::string pathfix; 115 | 116 | if ( basePath != NULL ) 117 | { 118 | strcpy( basePathed, basePath ); 119 | } 120 | else 121 | { 122 | getcwd( basePathed, MAX_PATH ); 123 | } 124 | 125 | if ( ( path[0] == '.' ) && ( ( path[1] == '\\' ) || ( path[1] == '/' ) ) ) 126 | { 127 | pathfix.append(basePathed); 128 | pathfix.append(path+1); 129 | path = pathfix.c_str(); 130 | } 131 | 132 | output.clear(); 133 | 134 | while ( ( path[diffBegin] != 0 ) && ( basePathed[diffBegin] != 0 ) ) 135 | { 136 | // Check for difference 137 | if ( tolower( basePathed[diffBegin] ) != tolower( path[diffBegin] ) ) 138 | { 139 | 140 | if ( diffBegin < 2 ) 141 | { 142 | return 0; 143 | } 144 | 145 | // Snap diffBegin to parent's slash character 146 | while ( ( basePathed[diffBegin-1] != '\\' ) && 147 | ( basePathed[diffBegin-1] != '/' ) ) 148 | { 149 | diffBegin--; 150 | } 151 | 152 | // Count number of directories from the destination path 153 | for ( int i=diffBegin; i strlen( path ) ) 193 | { 194 | // Count number of directories from the destination path 195 | for ( int i=diffBegin; i strlen( basePathed ) ) 226 | { 227 | if ( (path[diffBegin] == '\\') || (path[diffBegin] == '/') ) 228 | { 229 | diffBegin++; 230 | } 231 | output += &path[diffBegin]; 232 | } 233 | 234 | return 1; 235 | } 236 | 237 | void StripPathname( std::string &path ) 238 | { 239 | size_t pos = path.rfind( "/" ); 240 | 241 | if ( pos == std::string::npos ) 242 | { 243 | pos = path.rfind( "\\" ); 244 | if ( pos == std::string::npos ) 245 | { 246 | return; 247 | } 248 | } 249 | 250 | path.erase( 0, pos+1 ); 251 | } 252 | 253 | void StripFilename( std::string& path ) 254 | { 255 | size_t pos = path.rfind( "/" ); 256 | 257 | if ( pos == std::string::npos ) 258 | { 259 | pos = path.rfind( "\\" ); 260 | if ( pos == std::string::npos ) 261 | { 262 | return; 263 | } 264 | } 265 | 266 | path.erase( pos+1 ); 267 | } 268 | 269 | void Normalize(float *n, int count) { 270 | float length=0; 271 | 272 | for ( int i=0; i length ) { 274 | length = fabs( n[i] ); 275 | } 276 | } 277 | 278 | for ( int i=0; i 15 | #include 16 | #include 17 | #include 18 | #include "ColorPreview.h" 19 | #include "global.h" 20 | 21 | void planeSettingChange_cb(Fl_Check_Button* widget, long userdata ) { 22 | 23 | if ( project.active_primitive == nullptr ) { 24 | return; 25 | } 26 | 27 | if ( userdata == 0 ) { 28 | 29 | project.active_primitive->doublesided = widget->value(); 30 | 31 | for ( int i=0; ivalue(); 34 | } 35 | } 36 | 37 | if ( ui->paintModeToggle->value() ) { 38 | project.temp = project.model.primitives[project.last_selected]; 39 | } 40 | 41 | ui->glviewport->redraw(); 42 | 43 | } 44 | 45 | } 46 | 47 | void blendModeSet_cb(Fl_Round_Button* widget, long userdata) { 48 | 49 | if ( project.active_primitive == nullptr ) { 50 | return; 51 | } 52 | 53 | project.active_primitive->blendmode = userdata; 54 | 55 | for ( int i=0; iglviewport->redraw(); 63 | 64 | } 65 | 66 | void setPrimTexture_cb(Fl_Check_Button* widget, long userdata) { 67 | 68 | if ( ui->primTexturedToggle->value() ) { 69 | 70 | for( int i=0; itexturePreviewer->redraw(); 77 | ui->glviewport->redraw(); 78 | 79 | } else { 80 | 81 | for( int i=0; itexturePreviewer->redraw(); 88 | ui->glviewport->redraw(); 89 | 90 | } 91 | 92 | } 93 | 94 | void setPrimType_cb(Fl_Round_Button* widget, long userdata) { 95 | 96 | if ( userdata ) { 97 | 98 | for( int i=0; isetColor[1]->activate(); 105 | ui->setColor[2]->activate(); 106 | ui->setColor[3]->activate(); 107 | 108 | } else { 109 | 110 | for( int i=0; isetColor[1]->deactivate(); 117 | ui->setColor[2]->deactivate(); 118 | ui->setColor[3]->deactivate(); 119 | 120 | } 121 | 122 | 123 | if ( ui->paintModeToggle->value() == 0 ) { 124 | 125 | project.temp = project.model.primitives[project.last_selected]; 126 | 127 | } 128 | 129 | ui->glviewport->redraw(); 130 | ui->colorPreviewer->redraw(); 131 | } 132 | 133 | void colorSlider_cb(Fl_Value_Slider* widget, long userdata) { 134 | 135 | if ( project.active_primitive == nullptr ) { 136 | return; 137 | } 138 | 139 | int index=0,is_flat=false; 140 | 141 | if ( ui->setColor[1]->value() ) { 142 | index = 1; 143 | } else if ( ui->setColor[2]->value() ) { 144 | index = 2; 145 | } else if ( ui->setColor[3]->value() ) { 146 | index = 3; 147 | } 148 | 149 | project.active_primitive->c[index].r = ui->colorSlider[0]->value(); 150 | project.active_primitive->c[index].g = ui->colorSlider[1]->value(); 151 | project.active_primitive->c[index].b = ui->colorSlider[2]->value(); 152 | 153 | if ( index == 0 ) { 154 | 155 | switch( project.active_primitive->primtype ) { 156 | case SmxClass::PRIM_TRI_F: 157 | case SmxClass::PRIM_TRI_FT: 158 | case SmxClass::PRIM_QUAD_F: 159 | case SmxClass::PRIM_QUAD_FT: 160 | project.active_primitive->c[1] 161 | = project.active_primitive->c[index]; 162 | project.active_primitive->c[2] 163 | = project.active_primitive->c[index]; 164 | project.active_primitive->c[3] 165 | = project.active_primitive->c[index]; 166 | is_flat = true; 167 | break; 168 | } 169 | 170 | } 171 | 172 | for ( int i=0; iprimtype & SMX_PRIM_GOURAUD ) ) 181 | && ( ( project.model.primitives[i].primtype & SMX_PRIM_QUAD ) 182 | == ( project.active_primitive->primtype & SMX_PRIM_QUAD ) ) ) { 183 | 184 | project.model.primitives[i].c[index] 185 | = project.active_primitive->c[index]; 186 | 187 | if ( is_flat ) { 188 | project.model.primitives[i].c[1] 189 | = project.active_primitive->c[0]; 190 | project.model.primitives[i].c[2] 191 | = project.active_primitive->c[0]; 192 | project.model.primitives[i].c[3] 193 | = project.active_primitive->c[0]; 194 | } 195 | 196 | } 197 | } 198 | 199 | } 200 | 201 | if ( ui->paintModeToggle->value() == 0 ) { 202 | 203 | project.temp = project.model.primitives[project.last_selected]; 204 | 205 | } 206 | 207 | ui->colorPreviewer->redraw(); 208 | ui->glviewport->redraw(); 209 | 210 | } 211 | 212 | void setVcolor_cb(Fl_Button* widget, long userdata) { 213 | 214 | if ( ( userdata == 0 ) || ( userdata == 2 ) ) { 215 | ui->setColor[1]->value( 0 ); 216 | ui->setColor[3]->value( 0 ); 217 | } else if ( ( userdata == 1 ) || ( userdata == 3 ) ) { 218 | ui->setColor[0]->value( 0 ); 219 | ui->setColor[2]->value( 0 ); 220 | } 221 | 222 | ui->colorSlider[0]->value( project.active_primitive->c[userdata].r ); 223 | ui->colorSlider[1]->value( project.active_primitive->c[userdata].g ); 224 | ui->colorSlider[2]->value( project.active_primitive->c[userdata].b ); 225 | 226 | if ( ui->paintModeToggle->value() == 0 ) { 227 | project.temp = project.model.primitives[project.last_selected]; 228 | } 229 | } 230 | 231 | static void draw_shaded_triangle(void* userdata, int feed_X, int feed_Y, 232 | int feed_W, uchar* buf) { 233 | 234 | PIXEL* p = (PIXEL*)buf; 235 | ColorPreview* widget = (ColorPreview*)userdata; 236 | 237 | SmxClass::RGB rgb[3] = { 238 | project.active_primitive->c[0], 239 | project.active_primitive->c[1], 240 | project.active_primitive->c[2] 241 | }; 242 | 243 | int ww = widget->w()-8; 244 | int wh = widget->h()-8; 245 | 246 | int tw = floor( ww*(1.f-(feed_Y/(float)wh)) ); 247 | 248 | for ( int xx=feed_X; xxcolor())>>8)&0xff; 267 | p[xx-feed_X].g = (Fl::get_color(widget->color())>>16)&0xff; 268 | p[xx-feed_X].b = (Fl::get_color(widget->color())>>24)&0xff; 269 | 270 | } 271 | } 272 | 273 | } 274 | 275 | static void draw_shaded_quad(void* userdata, int feed_X, int feed_Y, 276 | int feed_W, uchar* buf) { 277 | 278 | PIXEL* p = (PIXEL*)buf; 279 | ColorPreview* widget = (ColorPreview*)userdata; 280 | 281 | SmxClass::RGB rgb[4] = { 282 | project.active_primitive->c[0], 283 | project.active_primitive->c[1], 284 | project.active_primitive->c[2], 285 | project.active_primitive->c[3] 286 | }; 287 | 288 | int ww = widget->w()-8; 289 | int wh = widget->h()-8; 290 | 291 | int tw = floor( ww*(1.f-(feed_Y/(float)wh)) ); 292 | 293 | for ( int xx=feed_X; xxprimtype ) 324 | { 325 | case SmxClass::PRIM_TRI_F: 326 | case SmxClass::PRIM_TRI_FT: 327 | fl_color( 328 | project.active_primitive->c[0].r, 329 | project.active_primitive->c[0].g, 330 | project.active_primitive->c[0].b ); 331 | fl_polygon( x()+4, y()+4, 332 | (x()+w())-8, y()+4, 333 | x()+4, (y()+h())-5, 334 | x()+4, (y()+h())-5 ); 335 | break; 336 | case SmxClass::PRIM_QUAD_F: 337 | case SmxClass::PRIM_QUAD_FT: 338 | fl_color( 339 | project.active_primitive->c[0].r, 340 | project.active_primitive->c[0].g, 341 | project.active_primitive->c[0].b ); 342 | fl_polygon( x()+4, y()+4, 343 | (x()+w())-5, y()+4, 344 | (x()+w())-5, (y()+h())-5, 345 | x()+4, (y()+h())-5 ); 346 | break; 347 | case SmxClass::PRIM_TRI_G: 348 | case SmxClass::PRIM_TRI_GT: 349 | fl_draw_image( draw_shaded_triangle, this, 350 | x()+4, y()+4, w()-8, h()-8, 3 ); 351 | break; 352 | case SmxClass::PRIM_QUAD_G: 353 | case SmxClass::PRIM_QUAD_GT: 354 | fl_draw_image( draw_shaded_quad, this, 355 | x()+4, y()+4, w()-8, h()-8, 3 ); 356 | break; 357 | } 358 | 359 | } 360 | 361 | } -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * File: main.cpp 3 | * Author: Lameguy64 4 | * 5 | * Created on March 13, 2018, 1:01 PM 6 | */ 7 | 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "common/common.h" 15 | #include "global.h" 16 | #include "about.h" 17 | 18 | ConfigClass config; 19 | ContextClass project; 20 | 21 | MainUI* ui; 22 | 23 | 24 | void ParseTextures() { 25 | 26 | ui->textureBrowser->clear(); 27 | 28 | for( int i=0; itextureBrowser->add( project.model.textures[i]->filename.c_str(), 31 | nullptr ); 32 | 33 | } 34 | 35 | ui->textureBrowser->redraw(); 36 | 37 | } 38 | 39 | int DeactivateAll() { 40 | 41 | if ( project.last_selected >= 0 ) { 42 | 43 | unsetFace(); 44 | 45 | return 1; 46 | } 47 | 48 | if ( ui->paintModeToggle->value() ) { 49 | 50 | ui->paintModeToggle->value( 0 ); 51 | ui->colorPreviewer->redraw(); 52 | ui->texturePreviewer->redraw(); 53 | return 1; 54 | 55 | } 56 | 57 | return 0; 58 | } 59 | 60 | void aboutItem_cb(Fl_Menu_* widget, void* userdata) { 61 | 62 | AboutUI *about = new AboutUI(); 63 | 64 | about->versionText->value( VERSION ); 65 | 66 | about->position( (ui->x()+(ui->w()/2))-(about->w()/2), 67 | (ui->y()+(ui->h()/2))-(about->h()/2) ); 68 | 69 | about->show(); 70 | 71 | while( about->shown() ) { 72 | Fl::wait(); 73 | } 74 | 75 | delete about; 76 | 77 | } 78 | 79 | void aboutButton_cb(Fl_Button* widget, void* userdata) { 80 | 81 | aboutItem_cb( (Fl_Menu_*)widget, userdata ); 82 | 83 | } 84 | 85 | void addTextureButton_cb(Fl_Button* widget, void* userdata) { 86 | 87 | DeactivateAll(); 88 | 89 | if ( project.file_name.empty() ) { 90 | fl_message_title( "No model loaded" ); 91 | fl_message( "You cannot add textures without a model loaded." ); 92 | return; 93 | } 94 | 95 | Fl_Native_File_Chooser file_chooser; 96 | 97 | file_chooser.title( "Add Texture File" ); 98 | file_chooser.filter( "Texture Image File\t*.tim\n" ); 99 | file_chooser.type( file_chooser.BROWSE_FILE ); 100 | 101 | switch( file_chooser.show() ) { 102 | case -1: 103 | case 1: 104 | break; 105 | default: 106 | 107 | #ifdef DEBUG 108 | std::cout << "Add texture file " 109 | << file_chooser.filename() << std::endl; 110 | #endif 111 | 112 | project.model.AddTexture( file_chooser.filename() ); 113 | ParseTextures(); 114 | 115 | break; 116 | } 117 | 118 | } 119 | 120 | void replaceTextureButton_cb(Fl_Button* widget, void* userdata) { 121 | 122 | DeactivateAll(); 123 | 124 | if ( project.file_name.empty() ) { 125 | fl_message_title( "No model loaded" ); 126 | fl_message( "You cannot add textures without a model loaded." ); 127 | return; 128 | } 129 | 130 | int replace_index = ui->textureBrowser->value()-1; 131 | 132 | if ( replace_index < 0 ) { 133 | fl_message_title( "No texture selected" ); 134 | fl_message( "Select a texture to replace." ); 135 | return; 136 | } 137 | 138 | Fl_Native_File_Chooser file_chooser; 139 | 140 | file_chooser.title( "Replace Texture File" ); 141 | file_chooser.filter( "Texture Image File\t*.tim\n" ); 142 | file_chooser.type( file_chooser.BROWSE_FILE ); 143 | 144 | switch( file_chooser.show() ) { 145 | case -1: 146 | case 1: 147 | break; 148 | default: 149 | 150 | #ifdef DEBUG 151 | std::cout << "Add texture file " 152 | << file_chooser.filename() << std::endl; 153 | #endif 154 | 155 | if ( project.model.ReplaceTexture( replace_index, file_chooser.filename() ) ) { 156 | ParseTextures(); 157 | ui->texturePreviewer->redraw(); 158 | ui->glviewport->redraw(); 159 | } 160 | 161 | break; 162 | } 163 | 164 | } 165 | 166 | void deleteTextureButton_cb(Fl_Button* widget, void* userdata) { 167 | 168 | if ( project.model.textures.size() == 0 ) { 169 | fl_message_title( "No textures" ); 170 | fl_message( "No textures to delete." ); 171 | return; 172 | } 173 | 174 | int index = ui->textureBrowser->value()-1; 175 | 176 | if ( index < 0 ) { 177 | fl_message_title( "No texture selected" ); 178 | fl_message( "Select a texture to delete." ); 179 | return; 180 | } 181 | 182 | fl_message_title( "Confirm texture deletion" ); 183 | if ( fl_choice( "Primitives with this texture assigned will become untextured.\n" 184 | "Are you sure?", "No", "Yes", 0 ) == 0 ) { 185 | return; 186 | } 187 | 188 | project.model.DeleteTexture( index ); 189 | ParseTextures(); 190 | ui->glviewport->redraw(); 191 | ui->texturePreviewer->redraw(); 192 | 193 | } 194 | 195 | void saveModelFile_cb(Fl_Menu_* widget, void* userdata) { 196 | 197 | if ( project.file_name.empty() ) { 198 | fl_message_title( "No Model Loaded" ); 199 | fl_message( "You cannot save an empty document." ); 200 | return; 201 | } 202 | 203 | if ( project.model.SaveFile( project.file_name.c_str() ) 204 | != SmxClass::ERR_NONE ) { 205 | fl_message_title( "File save error." ); 206 | fl_message( "Error saving selected file." ); 207 | return; 208 | } 209 | 210 | } 211 | 212 | void saveModelButton_cb(Fl_Button* widget, void* userdata) { 213 | 214 | saveModelFile_cb((Fl_Menu_*)widget, userdata); 215 | 216 | } 217 | 218 | void saveModelFileAs_cb(Fl_Menu_* widget, void* userdata) { 219 | 220 | DeactivateAll(); 221 | 222 | if ( project.file_name.empty() ) { 223 | 224 | fl_message_title( "No SMX Loaded" ); 225 | fl_message( "You cannot save an empty document." ); 226 | return; 227 | } 228 | 229 | std::string temp; 230 | Fl_Native_File_Chooser file_chooser; 231 | 232 | file_chooser.title( "Save Model File As" ); 233 | file_chooser.filter( "Scarlet Model XML\t*.smx\nRSD Model Data\t*.rsd\n" ); 234 | file_chooser.type( Fl_Native_File_Chooser::BROWSE_SAVE_FILE ); 235 | file_chooser.options( Fl_Native_File_Chooser::USE_FILTER_EXT ); 236 | 237 | switch ( file_chooser.show() ) 238 | { 239 | case -1: return; 240 | case 1: return; 241 | default: 242 | 243 | temp = file_chooser.filename(); 244 | 245 | if ( file_chooser.filter_value() == 0 ) { 246 | EnsureFileExtension( temp, "smx" ); 247 | } else if ( file_chooser.filter_value() == 1 ) { 248 | EnsureFileExtension( temp, "rsd" ); 249 | } 250 | 251 | #ifdef DEBUG 252 | std::cout << temp << std::endl; 253 | #endif 254 | 255 | if ( project.model.SaveFile( temp.c_str() ) != SmxClass::ERR_NONE ) { 256 | fl_message_title( "File save error" ); 257 | fl_message( "Error saving selected file." ); 258 | return; 259 | } 260 | 261 | project.file_name = temp; 262 | break; 263 | } 264 | } 265 | 266 | void loadModelFile_cb(Fl_Menu_* widget, void* userdata) { 267 | 268 | DeactivateAll(); 269 | 270 | Fl_Native_File_Chooser file_chooser; 271 | 272 | file_chooser.title( "Open Model File" ); 273 | 274 | #ifdef __WIN32__ 275 | file_chooser.filter( "All Supported Files\t*.smx;*.rsd\nScarlet Model XML\t*.smx\nRSD Model Data\t*.rsd\n" ); 276 | #else 277 | file_chooser.filter( "Scarlet Model XML\t*.smx\nRSD Model Data\t*.rsd\n" ); 278 | #endif 279 | 280 | switch ( file_chooser.show() ) 281 | { 282 | case -1: return; 283 | case 1: return; 284 | default: 285 | 286 | switch( project.model.LoadFile( file_chooser.filename() ) ) { 287 | case ModelClass::ERR_NOTFOUND: 288 | fl_message_title( "No file extension" ); 289 | fl_message( "Specified file was not found." ); 290 | return; 291 | case ModelClass::ERR_INVALID: 292 | fl_message_title( "Invalid file" ); 293 | fl_message( "Specified file is invalid." ); 294 | return; 295 | case ModelClass::ERR_NOEXT: 296 | fl_message_title( "No file extension" ); 297 | fl_message( "File extension not specified." ); 298 | return; 299 | } 300 | 301 | project.file_name = file_chooser.filename(); 302 | project.active_primitive = nullptr; 303 | project.last_selected = -1; 304 | ui->glviewport->redraw(); 305 | ParseTextures(); 306 | 307 | break; 308 | } 309 | } 310 | 311 | void loadModelButton_cb(Fl_Button* widget, void* userdata) { 312 | 313 | loadModelFile_cb((Fl_Menu_*)widget, userdata); 314 | 315 | } 316 | 317 | void mergeModelFile_cb(Fl_Menu_* widget, void* userdata) { 318 | 319 | DeactivateAll(); 320 | Fl_Native_File_Chooser file_chooser; 321 | 322 | file_chooser.title( "Merge Model File" ); 323 | file_chooser.filter( "Scarlet Model XML\t*.smx\n" ); 324 | 325 | switch ( file_chooser.show() ) 326 | { 327 | case -1: return; 328 | case 1: return; 329 | default: 330 | if ( project.model.MergeSmxFile( file_chooser.filename() ) 331 | != SmxClass::ERR_NONE ) { 332 | fl_message_title( "File load error." ); 333 | fl_message( "Error loading selected file." ); 334 | return; 335 | } 336 | ParseTextures(); 337 | break; 338 | } 339 | } 340 | 341 | void loadBgModelFile_cb(Fl_Button* widget, void* userdata) { 342 | 343 | DeactivateAll(); 344 | Fl_Native_File_Chooser file_chooser; 345 | 346 | file_chooser.title( "Load Model File as Background" ); 347 | #ifdef __WIN32__ 348 | file_chooser.filter( "All Supported Files\t*.smx;*.rsd\nScarlet Model XML\t*.smx\nRSD Model Data\t*.rsd\n" ); 349 | #else 350 | file_chooser.filter( "Scarlet Model XML\t*.smx\nRSD Model Data\t*.rsd\n" ); 351 | #endif 352 | 353 | switch ( file_chooser.show() ) 354 | { 355 | case -1: return; 356 | case 1: return; 357 | default: 358 | if ( project.bg_model.LoadFile( file_chooser.filename() ) 359 | != SmxClass::ERR_NONE ) { 360 | fl_message_title( "File load error." ); 361 | fl_message( "Error loading selected file." ); 362 | return; 363 | } 364 | ui->glviewport->redraw(); 365 | break; 366 | } 367 | 368 | } 369 | 370 | void selectAllItem_cb(Fl_Menu_* widget, void* userdata) { 371 | 372 | for( int i=0; iglviewport->redraw(); 379 | 380 | } 381 | 382 | void invertSelectionItem_cb(Fl_Menu_* widget, void* userdata) { 383 | 384 | for( int i=0; iglviewport->redraw(); 392 | 393 | } 394 | 395 | void selBringFront_cb(Fl_Menu_* widget, void* userdata) { 396 | 397 | ModelClass* m = &project.model; 398 | 399 | for ( int i=0; iprimitives.size(); i++ ) { 400 | for ( int j=0; jprimitives.size()-1; j++ ) { 401 | 402 | if ( m->primitives[j].selected < m->primitives[j+1].selected ) { 403 | std::swap( m->primitives[j], m->primitives[j+1] ); 404 | } 405 | 406 | } 407 | } 408 | 409 | setActiveFace( 0 ); 410 | 411 | } 412 | 413 | void selSendBack_cb(Fl_Menu_* widget, void* userdata) { 414 | 415 | ModelClass* m = &project.model; 416 | 417 | for ( int i=0; iprimitives.size(); i++ ) { 418 | for ( int j=0; jprimitives.size()-1; j++ ) { 419 | 420 | if ( m->primitives[j].selected > m->primitives[j+1].selected ) { 421 | std::swap( m->primitives[j], m->primitives[j+1] ); 422 | } 423 | 424 | } 425 | } 426 | 427 | setActiveFace( project.model.primitives.size()-1 ); 428 | 429 | } 430 | 431 | void mainUI_cb(Fl_Double_Window* widget, void* userdata) { 432 | 433 | if ( DeactivateAll() ) { 434 | return; 435 | } 436 | 437 | widget->hide(); 438 | 439 | } 440 | 441 | void exitItem_cb(Fl_Menu_* widget, void* userdata ) { 442 | 443 | ui->hide(); 444 | 445 | } 446 | 447 | void init() { 448 | 449 | fl_message_font( FL_HELVETICA, 12 ); 450 | 451 | char currentDir[MAX_PATH]; 452 | getcwd( currentDir, MAX_PATH ); 453 | 454 | config.program_path = currentDir; 455 | #ifdef _WIN32 456 | config.program_path += "\\"; 457 | #else 458 | config.program_path += "/"; 459 | #endif 460 | 461 | 462 | // Determine path where scarlet config files will be stored 463 | #ifdef _WIN32 464 | if ( getenv( "APPDATA" ) != NULL ) { 465 | 466 | config.config_file = getenv( "APPDATA" ); 467 | config.config_file += "\\scarlet"; 468 | 469 | } else { 470 | 471 | config.config_file = "scarlet"; 472 | 473 | } 474 | #else 475 | if ( getenv( "XDG_CONFIG_HOME" ) != NULL ) { 476 | 477 | config.config_file = getenv( "XDG_CONFIG_HOME" ); 478 | config.config_file += "/scarlet"; 479 | 480 | } else if ( getenv( "HOME" ) != NULL ) { 481 | 482 | config.config_file = getenv( "HOME" ); 483 | config.config_file += "/.config/scarlet"; 484 | 485 | } else { 486 | 487 | config.config_file = config.program_path; 488 | 489 | } 490 | #endif 491 | 492 | // Create directory to store scarlet SDK files 493 | if ( !FileExists( config.config_file.c_str() ) ) { 494 | 495 | // Syntax of mkdir is different between Windows and *nix 496 | #ifdef _WIN32 497 | mkdir( config.config_file.c_str() ); 498 | #else 499 | mkdir( config.config_file.c_str(), S_IREAD|S_IWRITE|S_IEXEC ); 500 | #endif 501 | 502 | } 503 | 504 | config.config_file += "/smxtool.cfg"; 505 | 506 | #ifdef DEBUG 507 | std::cout << "-- Configuration --" << std::endl; 508 | std::cout << "Program directory: " << config.program_path.c_str() << std::endl; 509 | std::cout << "Config file " << config.config_file << std::endl; 510 | std::cout << "-- End Configuration --" << std::endl; 511 | #endif 512 | 513 | // Load config 514 | config.LoadConfig(); 515 | 516 | if ( config.systemcolors_enable ) { 517 | Fl::get_system_colors(); 518 | } 519 | 520 | ui = new MainUI(); 521 | 522 | if ( config.singlebuffer_enable ) { 523 | ui->glviewport->mode( FL_RGB|FL_DEPTH ); 524 | } 525 | 526 | } 527 | 528 | int main(int argc, char** argv) { 529 | 530 | init(); 531 | 532 | ui->show(); 533 | Fl::wait(); 534 | 535 | aboutItem_cb( nullptr, nullptr ); 536 | 537 | int ret = Fl::run(); 538 | 539 | return ret; 540 | } 541 | 542 | -------------------------------------------------------------------------------- /mainform.fl: -------------------------------------------------------------------------------- 1 | # data file for the Fltk User Interface Designer (fluid) 2 | version 1.0304 3 | header_name {.h} 4 | code_name {.cxx} 5 | widget_class MainUI { 6 | label {SMX Tool} 7 | callback mainUI_cb open 8 | xywh {408 172 824 548} type Double hide resizable 9 | class Fl_Double_Window xclass {smxtool.smxtool} 10 | } { 11 | Fl_Menu_Bar {} {open selected 12 | xywh {0 0 824 20} labelsize 12 textsize 12 13 | } { 14 | Submenu {} { 15 | label {&File} open 16 | xywh {0 0 62 20} labelsize 12 17 | } { 18 | MenuItem {} { 19 | label {&Open} 20 | callback loadModelFile_cb 21 | xywh {0 0 30 20} shortcut 0x4006f labelsize 12 22 | } 23 | MenuItem {} { 24 | label {&Save} 25 | callback saveModelFile_cb 26 | xywh {0 0 30 20} shortcut 0x40073 labelsize 12 27 | } 28 | MenuItem {} { 29 | label {Save &As} 30 | callback saveModelFileAs_cb 31 | xywh {0 0 30 20} labelsize 12 divider 32 | } 33 | MenuItem {} { 34 | label {Merge...} 35 | callback mergeModelFile_cb 36 | xywh {0 0 30 20} labelsize 12 divider 37 | } 38 | MenuItem {} { 39 | label {E&xit} 40 | callback exitItem_cb 41 | xywh {5 5 30 20} shortcut 0x8ffc1 labelsize 12 42 | } 43 | } 44 | Submenu {} { 45 | label {&Edit} open 46 | xywh {5 5 62 20} labelsize 12 47 | } { 48 | MenuItem {} { 49 | label {Bring to &Front} 50 | callback selBringFront_cb 51 | xywh {0 0 30 20} shortcut 0xff50 labelsize 12 52 | } 53 | MenuItem {} { 54 | label {Send to &Back} 55 | callback selSendBack_cb 56 | xywh {0 0 30 20} shortcut 0xff57 labelsize 12 divider 57 | } 58 | MenuItem {} { 59 | label {Select &All} 60 | callback selectAllItem_cb 61 | xywh {0 0 30 20} shortcut 0x40061 labelsize 12 62 | } 63 | MenuItem {} { 64 | label {&Invert Selection} 65 | callback invertSelectionItem_cb 66 | xywh {0 0 30 20} shortcut 0x40069 labelsize 12 divider 67 | } 68 | MenuItem {} { 69 | label {Editor &Settings...} 70 | user_data 0 user_data_type long 71 | callback settingsItem_cb 72 | xywh {0 0 30 20} labelsize 12 73 | } 74 | } 75 | Submenu {} { 76 | label {&View} open 77 | xywh {5 5 62 20} labelsize 12 78 | } { 79 | MenuItem viewDepthToggle { 80 | label {&Depth Test} 81 | callback {glviewport->redraw();} 82 | tooltip {Toggles depth test useful when working with transparent textures} xywh {0 0 30 20} type Toggle value 1 labelsize 12 83 | } 84 | } 85 | MenuItem {} { 86 | label {&About} 87 | callback aboutItem_cb 88 | xywh {5 5 30 20} labelsize 12 89 | } 90 | } 91 | Fl_Group {} {open 92 | xywh {0 20 824 35} box UP_FRAME 93 | } { 94 | Fl_Button {} { 95 | callback loadModelButton_cb 96 | tooltip {Open File} image {icons/file_open.png} xywh {5 25 25 25} box THIN_UP_BOX labelsize 12 97 | } 98 | Fl_Button {} { 99 | callback saveModelButton_cb 100 | tooltip {Save File} image {icons/file_save.png} xywh {30 25 25 25} box THIN_UP_BOX labelsize 12 101 | } 102 | Fl_Button {} { 103 | callback loadBgModelFile_cb 104 | tooltip {Set Background Geometry} image {icons/icon_bg.png} xywh {59 25 25 25} box THIN_UP_BOX labelsize 12 105 | } 106 | Fl_Button paintModeToggle { 107 | callback paintModeToggle_cb 108 | tooltip {Paint Mode} image {icons/tool_paint_hitmap.png} xywh {88 25 25 25} type Toggle box THIN_UP_BOX labelsize 12 109 | } 110 | Fl_Button {} { 111 | user_data 0 user_data_type long 112 | callback renderModeToggle_cb 113 | tooltip {Wireframe Overlay} image {icons/view_wiremesh.png} xywh {117 25 25 25} type Toggle box THIN_UP_BOX labelsize 12 114 | } 115 | Fl_Button {} { 116 | user_data 1 user_data_type long 117 | callback renderModeToggle_cb 118 | tooltip {Backface Culling} image {icons/view_backcull.png} xywh {142 25 25 25} type Toggle box THIN_UP_BOX value 1 labelsize 12 119 | } 120 | Fl_Button {} { 121 | callback resetPosition_cb 122 | tooltip {Reset to Center of Model} image {icons/auto_center.png} xywh {172 25 25 25} box THIN_UP_BOX labelsize 12 123 | } 124 | Fl_Button {} { 125 | callback homePosition_cb 126 | tooltip {Reset to Center} image {icons/home_center.png} xywh {197 25 25 25} box THIN_UP_BOX labelsize 12 127 | } 128 | Fl_Group {} {open 129 | xywh {222 25 53 25} 130 | } { 131 | Fl_Button {} { 132 | user_data 0 user_data_type long 133 | callback perspModeChange_cb 134 | tooltip {Orbit Perspective} image {icons/view_orbit.png} xywh {222 25 25 25} type Radio box THIN_UP_BOX value 1 labelsize 12 135 | } 136 | Fl_Button {} { 137 | user_data 1 user_data_type long 138 | callback perspModeChange_cb 139 | tooltip {First-person Perspective} image {icons/view_firstperson.png} xywh {247 25 25 25} type Radio box THIN_UP_BOX labelsize 12 140 | } 141 | } 142 | Fl_Group {} {open 143 | xywh {275 25 513 25} resizable 144 | } {} 145 | Fl_Button {} { 146 | callback aboutButton_cb 147 | image {icons/scarlet2.png} xywh {792 25 25 25} box THIN_UP_BOX labelsize 12 148 | } 149 | } 150 | Fl_Tile {} {open 151 | xywh {-15 55 839 493} resizable 152 | } { 153 | Fl_Group {} {open 154 | xywh {240 55 584 493} box DOWN_BOX 155 | } { 156 | Fl_Group glviewport {open 157 | xywh {245 60 575 483} box FLAT_BOX color 4 resizable 158 | code0 {\#include "glwidget.h"} 159 | class glwidget 160 | } {} 161 | } 162 | Fl_Tabs {} {open 163 | xywh {0 55 240 493} labelsize 12 164 | } { 165 | Fl_Group {} { 166 | label {Vertex Color && Attributes} open 167 | xywh {0 75 240 473} box THIN_UP_BOX labelsize 12 resizable 168 | } { 169 | Fl_Group planeSettings {open 170 | xywh {8 96 222 441} deactivate resizable 171 | } { 172 | Fl_Group {} { 173 | label Color open 174 | xywh {9 139 220 192} box ENGRAVED_FRAME labelsize 12 align 5 resizable 175 | } { 176 | Fl_Group {} {open 177 | xywh {14 145 210 109} resizable 178 | } { 179 | Fl_Group colorPreviewer {open 180 | xywh {44 145 150 109} box THIN_DOWN_BOX labelsize 12 resizable 181 | code0 {\#include "ColorPreview.h"} 182 | class ColorPreview 183 | } {} 184 | Fl_Group {} {open 185 | xywh {15 145 25 109} 186 | } { 187 | Fl_Button {setColor[0]} { 188 | label V0 189 | user_data 0 user_data_type long 190 | callback setVcolor_cb 191 | xywh {15 145 25 20} type Radio labelsize 12 192 | } 193 | Fl_Button {setColor[2]} { 194 | label V2 195 | user_data 2 user_data_type long 196 | callback setVcolor_cb 197 | xywh {15 234 25 20} type Radio labelsize 12 198 | } 199 | Fl_Group {} {open 200 | xywh {15 170 25 59} resizable 201 | } {} 202 | } 203 | Fl_Group {} {open 204 | xywh {199 145 25 109} 205 | } { 206 | Fl_Button {setColor[1]} { 207 | label V1 208 | user_data 1 user_data_type long 209 | callback setVcolor_cb 210 | xywh {199 145 25 20} type Radio labelsize 12 211 | } 212 | Fl_Button {setColor[3]} { 213 | label V3 214 | user_data 3 user_data_type long 215 | callback setVcolor_cb 216 | xywh {199 234 25 20} type Radio labelsize 12 217 | } 218 | Fl_Group {} {open 219 | xywh {199 170 25 59} resizable 220 | } {} 221 | } 222 | } 223 | Fl_Group {} {open 224 | xywh {14 259 210 64} 225 | } { 226 | Fl_Value_Slider {colorSlider[0]} { 227 | label R 228 | user_data 0 user_data_type long 229 | callback colorSlider_cb 230 | xywh {30 259 194 18} type {Horz Fill} labelsize 12 align 4 maximum 255 step 1 textsize 12 resizable 231 | } 232 | Fl_Value_Slider {colorSlider[1]} { 233 | label G 234 | user_data 1 user_data_type long 235 | callback colorSlider_cb 236 | xywh {30 282 194 18} type {Horz Fill} labelsize 12 align 4 maximum 255 step 1 textsize 12 237 | } 238 | Fl_Value_Slider {colorSlider[2]} { 239 | label B 240 | user_data 2 user_data_type long 241 | callback colorSlider_cb 242 | xywh {30 305 194 18} type {Horz Fill} labelsize 12 align 4 maximum 255 step 1 textsize 12 243 | } 244 | } 245 | } 246 | Fl_Group {} { 247 | label Attributes open 248 | xywh {9 350 220 187} box ENGRAVED_FRAME labelsize 12 align 5 249 | } { 250 | Fl_Group {} { 251 | label {Blend Mode} open 252 | xywh {19 439 200 87} box ENGRAVED_FRAME labelsize 12 align 69 resizable 253 | } { 254 | Fl_Round_Button {blendModeRadio[0]} { 255 | label {No Blending} 256 | user_data 0 user_data_type long 257 | callback blendModeSet_cb 258 | xywh {27 446 184 15} type Radio down_box ROUND_DOWN_BOX value 1 labelsize 12 align 84 resizable 259 | } 260 | Fl_Round_Button {blendModeRadio[1]} { 261 | label {0: 50% + 50%} 262 | user_data 1 user_data_type long 263 | callback blendModeSet_cb 264 | xywh {27 461 184 15} type Radio down_box ROUND_DOWN_BOX labelsize 12 align 84 265 | } 266 | Fl_Round_Button {blendModeRadio[2]} { 267 | label {1: 100% + 100%} 268 | user_data 2 user_data_type long 269 | callback blendModeSet_cb 270 | xywh {27 476 184 15} type Radio down_box ROUND_DOWN_BOX labelsize 12 align 84 271 | } 272 | Fl_Round_Button {blendModeRadio[3]} { 273 | label {2: 100% + 50%} 274 | user_data 3 user_data_type long 275 | callback blendModeSet_cb 276 | xywh {27 491 184 15} type Radio down_box ROUND_DOWN_BOX labelsize 12 align 84 277 | } 278 | Fl_Round_Button {blendModeRadio[4]} { 279 | label {3: 100% - 100%} 280 | user_data 4 user_data_type long 281 | callback blendModeSet_cb 282 | xywh {27 506 184 15} type Radio down_box ROUND_DOWN_BOX labelsize 12 align 84 283 | } 284 | } 285 | Fl_Group {} { 286 | label {Lighting Mode} open 287 | xywh {19 393 201 27} box ENGRAVED_FRAME labelsize 12 align 5 deactivate 288 | } { 289 | Fl_Round_Button {lightModeRadio[0]} { 290 | label None 291 | xywh {24 400 53 15} type Radio down_box ROUND_DOWN_BOX value 1 labelsize 12 292 | } 293 | Fl_Round_Button {lightModeRadio[1]} { 294 | label Flat 295 | xywh {82 400 53 15} type Radio down_box ROUND_DOWN_BOX labelsize 12 296 | } 297 | Fl_Round_Button {lightModeRadio[2]} { 298 | label Smooth 299 | xywh {140 400 75 15} type Radio down_box ROUND_DOWN_BOX labelsize 12 resizable 300 | } 301 | } 302 | Fl_Check_Button doubleSidedCheck { 303 | label {Double sided (no culling)} 304 | user_data 0 user_data_type long 305 | callback planeSettingChange_cb 306 | xywh {19 359 201 15} down_box DOWN_BOX labelsize 12 align 84 307 | } 308 | } 309 | Fl_Group {} {open 310 | xywh {8 96 221 24} 311 | } { 312 | Fl_Group {} { 313 | label {Primitive Type} open 314 | xywh {8 96 137 24} box ENGRAVED_FRAME labelsize 12 align 5 315 | } { 316 | Fl_Round_Button {primTypeRadio[0]} { 317 | label Flat 318 | user_data 0 user_data_type long 319 | callback setPrimType_cb 320 | xywh {13 100 60 15} type Radio down_box ROUND_DOWN_BOX value 1 labelsize 12 align 84 321 | } 322 | Fl_Round_Button {primTypeRadio[1]} { 323 | label Gouraud 324 | user_data 1 user_data_type long 325 | callback setPrimType_cb 326 | xywh {68 100 66 15} type Radio down_box ROUND_DOWN_BOX labelsize 12 align 84 resizable 327 | } 328 | } 329 | Fl_Value_Output primitiveIndexOutput { 330 | label Index 331 | xywh {154 96 74 24} labelsize 12 align 5 textsize 12 resizable 332 | } 333 | } 334 | } 335 | } 336 | Fl_Group {} { 337 | label Texture open 338 | xywh {0 75 240 473} labelsize 12 hide 339 | } { 340 | Fl_Group textureSettings {open 341 | xywh {9 88 221 301} deactivate resizable 342 | } { 343 | Fl_Group {} { 344 | label Mapping open 345 | xywh {9 123 220 237} box ENGRAVED_BOX labelsize 12 align 5 resizable 346 | } { 347 | Fl_Group texturePreviewer {open 348 | xywh {15 128 210 194} box THIN_DOWN_BOX resizable 349 | code0 {\#include "texturepreview.h"} 350 | class TexturePreview 351 | } {} 352 | Fl_Group {} {open 353 | xywh {15 327 210 28} 354 | } { 355 | Fl_Spinner textureZoom { 356 | label Zoom 357 | callback textureZoom_cb 358 | xywh {156 332 69 23} labelsize 12 when 1 maximum 8 textsize 12 resizable 359 | } 360 | Fl_Button {} { 361 | user_data 0 user_data_type long 362 | callback rotateTexButton_cb 363 | tooltip {Rotate Coordinates Left} image {icons/icon_rotate_left.png} xywh {15 327 28 28} box THIN_UP_BOX labelsize 12 align 256 364 | } 365 | Fl_Button {} { 366 | user_data 1 user_data_type long 367 | callback rotateTexButton_cb 368 | tooltip {Rotate Coordinates Right} image {icons/icon_rotate_right.png} xywh {43 327 28 28} box THIN_UP_BOX labelsize 12 align 256 369 | } 370 | Fl_Button {} { 371 | callback expandTexButton_cb 372 | tooltip {Set Coordinates to Texture Borders} image {icons/icon_expand.png} xywh {71 327 28 28} box THIN_UP_BOX labelsize 12 373 | } 374 | } 375 | } 376 | Fl_Check_Button primTexturedToggle { 377 | label Textured 378 | user_data 2 user_data_type long 379 | callback setPrimTexture_cb 380 | tooltip {Make Primitive Textured} xywh {9 88 220 15} down_box DOWN_BOX labelsize 12 381 | } 382 | } 383 | Fl_Group {} { 384 | label {Texture File} open 385 | xywh {9 382 220 156} box ENGRAVED_FRAME labelsize 12 align 5 386 | } { 387 | Fl_Browser textureBrowser { 388 | callback textureBrowser_cb 389 | xywh {15 387 210 113} type Hold labelsize 12 when 3 textsize 12 resizable 390 | } 391 | Fl_Group {} {open 392 | xywh {15 505 210 28} 393 | } { 394 | Fl_Button {} { 395 | callback addTextureButton_cb 396 | tooltip {Add Texture} image {icons/item_add.png} xywh {15 505 28 28} box THIN_UP_BOX labelsize 12 397 | } 398 | Fl_Button {} { 399 | callback deleteTextureButton_cb 400 | tooltip {Delete Texture} image {icons/item_delete.png} xywh {71 505 28 28} box THIN_UP_BOX labelsize 12 401 | } 402 | Fl_Button {} { 403 | callback replaceTextureButton_cb 404 | tooltip {Replace Texture} image {icons/replace_item.png} xywh {43 505 28 28} box THIN_UP_BOX labelsize 12 405 | } 406 | Fl_Group {} {open 407 | xywh {102 505 123 28} resizable 408 | } {} 409 | } 410 | } 411 | } 412 | } 413 | } 414 | } 415 | -------------------------------------------------------------------------------- /texturepreview.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | 7 | /* 8 | * File: texturepreview.cpp 9 | * Author: lameguy64 10 | * 11 | * Created on April 17, 2018, 4:20 PM 12 | */ 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "texturepreview.h" 19 | #include "TextureClass.h" 20 | #include "global.h" 21 | 22 | int inbox(int x, int y, int bx, int by, int bw, int bh) { 23 | 24 | if ( ( x >= bx ) && ( x <= bx+(bw-1) ) ) { 25 | if ( ( y >= by ) && ( y <= by+(bh-1) ) ) { 26 | return 1; 27 | } 28 | } 29 | 30 | return 0; 31 | 32 | } 33 | 34 | int pointInPolygon(int x, int y, VECTOR2D* points, int num_points ) { 35 | 36 | int c = 0; 37 | int i,j; 38 | 39 | for( i = 0, j = num_points - 1; i < num_points; j = i++) { 40 | if ( ( (points[i].y >= y ) != (points[j].y >= y) ) 41 | && (x <= (points[j].x - points[i].x) 42 | * (y - points[i].y) / (points[j].y - points[i].y) + points[i].x) 43 | ) { 44 | c = !c; 45 | } 46 | } 47 | 48 | return c; 49 | } 50 | 51 | void expandTexButton_cb(Fl_Button* widget, void* userdata) { 52 | 53 | for( int i=0; iuserdata2; 63 | 64 | if ( tex != nullptr ) { 65 | 66 | project.model.primitives[i].t[0].u = 0; 67 | project.model.primitives[i].t[0].v = 0; 68 | 69 | project.model.primitives[i].t[1].u = tex->w-1; 70 | project.model.primitives[i].t[1].v = 0; 71 | 72 | project.model.primitives[i].t[2].u = 0; 73 | project.model.primitives[i].t[2].v = tex->h-1; 74 | 75 | if ( ( project.model.primitives[i].primtype 76 | == SmxClass::PRIM_QUAD_FT ) || 77 | ( project.model.primitives[i].primtype 78 | == SmxClass::PRIM_QUAD_GT ) ) { 79 | 80 | project.model.primitives[i].t[3].u = tex->w-1; 81 | project.model.primitives[i].t[3].v = tex->h-1; 82 | 83 | } 84 | 85 | } 86 | } 87 | 88 | } 89 | 90 | } 91 | 92 | ui->glviewport->redraw(); 93 | ui->texturePreviewer->redraw(); 94 | 95 | } 96 | 97 | void textureBrowser_cb(Fl_Browser* widget, void* userdata) { 98 | 99 | if ( ( project.last_selected < 0 ) || ( widget->value() == 0 ) ) { 100 | return; 101 | } 102 | 103 | if ( !project.model.IsTextured( &project.model.primitives[project.last_selected] ) ) { 104 | return; 105 | } 106 | 107 | int texnum = widget->value()-1; 108 | for( int i=0; itexturePreviewer->redraw(); 119 | ui->glviewport->redraw(); 120 | 121 | } 122 | 123 | void rotateTexButton_cb(Fl_Button* widget, long userdata) { 124 | 125 | if ( project.last_selected < 0 ) { 126 | return; 127 | } 128 | 129 | SmxClass::PRIMITIVE *p = &project.model.primitives[project.last_selected]; 130 | 131 | if ( userdata == 0 ) { 132 | 133 | if ( ( p->primtype == SmxClass::PRIM_TRI_FT ) || 134 | ( p->primtype == SmxClass::PRIM_TRI_GT ) ) { 135 | 136 | SmxClass::UV t; 137 | 138 | t = p->t[0]; 139 | p->t[0] = p->t[1]; 140 | p->t[1] = p->t[2]; 141 | p->t[2] = t; 142 | 143 | ui->glviewport->redraw(); 144 | 145 | } else if ( ( p->primtype == SmxClass::PRIM_QUAD_FT ) || 146 | ( p->primtype == SmxClass::PRIM_QUAD_GT ) ) { 147 | 148 | SmxClass::UV t; 149 | 150 | t = p->t[0]; 151 | p->t[0] = p->t[1]; 152 | p->t[1] = p->t[3]; 153 | p->t[3] = p->t[2]; 154 | p->t[2] = t; 155 | 156 | ui->glviewport->redraw(); 157 | 158 | } 159 | 160 | } else { 161 | 162 | if ( ( p->primtype == SmxClass::PRIM_TRI_FT ) || 163 | ( p->primtype == SmxClass::PRIM_TRI_GT ) ) { 164 | 165 | SmxClass::UV t; 166 | 167 | t = p->t[2]; 168 | p->t[2] = p->t[1]; 169 | p->t[1] = p->t[0]; 170 | p->t[0] = t; 171 | 172 | ui->glviewport->redraw(); 173 | 174 | } else if ( ( p->primtype == SmxClass::PRIM_QUAD_FT ) || 175 | ( p->primtype == SmxClass::PRIM_QUAD_GT ) ) { 176 | 177 | SmxClass::UV t; 178 | 179 | t = p->t[2]; 180 | p->t[2] = p->t[3]; 181 | p->t[3] = p->t[1]; 182 | p->t[1] = p->t[0]; 183 | p->t[0] = t; 184 | 185 | ui->glviewport->redraw(); 186 | 187 | } 188 | 189 | } 190 | 191 | project.temp = project.model.primitives[project.last_selected]; 192 | } 193 | 194 | void textureZoom_cb(Fl_Spinner* widget, void* userdata) { 195 | 196 | ui->texturePreviewer->SetZoom( widget->value() ); 197 | 198 | } 199 | 200 | static void scroll_cb(Fl_Widget* widget, void* userdata) { 201 | 202 | ui->texturePreviewer->redraw(); 203 | 204 | } 205 | 206 | static void draw_texture(void* userdata, int feed_X, int feed_Y, 207 | int feed_W, uchar* out_buf) 208 | { 209 | TexturePreview* widget = (TexturePreview*)userdata; 210 | SmxClass::RGB* p = (SmxClass::RGB*)out_buf; 211 | 212 | TexImageClass* tex 213 | = (TexImageClass*)project.model.textures 214 | [project.model.primitives[project.last_selected].texnum]->userdata2; 215 | 216 | if ( tex == nullptr ) { 217 | 218 | for( int i=feed_X; icolor())>>8)&0xff; 220 | p[i-feed_X].g = (Fl::get_color(widget->color())>>16)&0xff; 221 | p[i-feed_X].b = (Fl::get_color(widget->color())>>24); 222 | } 223 | 224 | return; 225 | } 226 | 227 | if ( feed_Y+widget->_vscrollbar.value() >= tex->h*widget->zoom ) { 228 | 229 | for( int i=feed_X; icolor())>>8)&0xff; 231 | p[i-feed_X].g = (Fl::get_color(widget->color())>>16)&0xff; 232 | p[i-feed_X].b = (Fl::get_color(widget->color())>>24); 233 | } 234 | 235 | return; 236 | } 237 | 238 | for( int i=feed_X; iReadPixel( 241 | (i+widget->_hscrollbar.value())/widget->zoom, 242 | (feed_Y+widget->_vscrollbar.value())/widget->zoom ); 243 | 244 | if ( ( i < tex->w*widget->zoom ) && ( ALPHA(pix) > 127 ) ) { 245 | 246 | p[i-feed_X].r = RED( pix ); 247 | p[i-feed_X].g = GREEN( pix ); 248 | p[i-feed_X].b = BLUE( pix ); 249 | 250 | } else { 251 | 252 | p[i-feed_X].r = (Fl::get_color(widget->color())>>8)&0xff; 253 | p[i-feed_X].g = (Fl::get_color(widget->color())>>16)&0xff; 254 | p[i-feed_X].b = (Fl::get_color(widget->color())>>24); 255 | 256 | } 257 | 258 | } 259 | 260 | } 261 | 262 | TexturePreview::TexturePreview(int x, int y, int w, int h, const char* l) 263 | : Fl_Group( x, y, w, h, l ), _hscrollbar( 0, 0, 0, 0 ), _vscrollbar( 0, 0, 0, 0 ) { 264 | 265 | zoom = 1; 266 | 267 | //_hscrollbar = new Fl_Scrollbar( 0, 0, 0, 0 ); 268 | //_hscrollbar->hide(); 269 | 270 | //_vscrollbar = new Fl_Scrollbar( 0, 0, 30, 30 ); 271 | //_vscrollbar->hide(); 272 | 273 | _hscrollbar.type( FL_HORIZONTAL ); 274 | _hscrollbar.hide(); 275 | _hscrollbar.callback( scroll_cb ); 276 | _vscrollbar.hide(); 277 | _vscrollbar.callback( scroll_cb ); 278 | 279 | calculateScrollbarPos(); 280 | 281 | _isdragging = false; 282 | 283 | } 284 | 285 | TexturePreview::~TexturePreview() { 286 | } 287 | 288 | void TexturePreview::SetZoom(int scale) { 289 | 290 | zoom = scale; 291 | 292 | testScrollbars(); 293 | redraw(); 294 | 295 | } 296 | 297 | int TexturePreview::handle(int event) { 298 | 299 | VECTOR2D temp[4]; 300 | int ret; 301 | 302 | if ( project.last_selected < 0 ) { 303 | return Fl_Group::handle( event ); 304 | } 305 | 306 | int points = 0; 307 | SmxClass::PRIMITIVE *p = &project.model.primitives[project.last_selected]; 308 | 309 | if ( ( p->primtype == SmxClass::PRIM_TRI_FT ) || 310 | ( p->primtype == SmxClass::PRIM_TRI_GT ) ) { 311 | 312 | points = 3; 313 | 314 | } else if ( ( p->primtype == SmxClass::PRIM_QUAD_FT ) || 315 | ( p->primtype == SmxClass::PRIM_QUAD_GT ) ) { 316 | 317 | points = 4; 318 | 319 | } 320 | 321 | if ( points == 0 ) { 322 | return Fl_Group::handle( event ); 323 | } 324 | 325 | ret = Fl_Group::handle( event ); 326 | 327 | if ( ret ) { 328 | return ret; 329 | } 330 | 331 | switch( event ) { 332 | case FL_PUSH: 333 | 334 | for( int i=0; it[i].u*zoom)+(zoom/2)-3)-_hscrollbar.value(), 338 | (1+y()+(p->t[i].v*zoom)+(zoom/2)-3)-_vscrollbar.value(), 339 | 8, 8 ) ) { 340 | 341 | _dragx = Fl::event_x(); 342 | _dragy = Fl::event_y(); 343 | _isdragging = 1; 344 | _drag = i; 345 | 346 | return 1; 347 | 348 | } 349 | 350 | } 351 | 352 | if ( points == 3 ) { 353 | 354 | temp[0].x = (x()+(p->t[0].u*zoom)+(zoom/2))-_hscrollbar.value(); 355 | temp[0].y = (y()+(p->t[0].v*zoom)+(zoom/2))-_vscrollbar.value(); 356 | temp[1].x = (x()+(p->t[1].u*zoom)+(zoom/2))-_hscrollbar.value(); 357 | temp[1].y = (y()+(p->t[1].v*zoom)+(zoom/2))-_vscrollbar.value(); 358 | temp[2].x = (x()+(p->t[2].u*zoom)+(zoom/2))-_hscrollbar.value(); 359 | temp[2].y = (y()+(p->t[2].v*zoom)+(zoom/2))-_vscrollbar.value(); 360 | 361 | } else if ( points == 4 ) { 362 | 363 | temp[0].x = (x()+(p->t[0].u*zoom)+(zoom/2))-_hscrollbar.value(); 364 | temp[0].y = (y()+(p->t[0].v*zoom)+(zoom/2))-_vscrollbar.value(); 365 | temp[1].x = (x()+(p->t[1].u*zoom)+(zoom/2))-_hscrollbar.value(); 366 | temp[1].y = (y()+(p->t[1].v*zoom)+(zoom/2))-_vscrollbar.value(); 367 | temp[2].x = (x()+(p->t[3].u*zoom)+(zoom/2))-_hscrollbar.value(); 368 | temp[2].y = (y()+(p->t[3].v*zoom)+(zoom/2))-_vscrollbar.value(); 369 | temp[3].x = (x()+(p->t[2].u*zoom)+(zoom/2))-_hscrollbar.value(); 370 | temp[3].y = (y()+(p->t[2].v*zoom)+(zoom/2))-_vscrollbar.value(); 371 | 372 | } 373 | 374 | if ( pointInPolygon( Fl::event_x(), Fl::event_y(), temp, points ) ) { 375 | 376 | for( int i=0; it[i].u; 378 | _draguv[i].y = p->t[i].v; 379 | } 380 | 381 | _dragx = Fl::event_x(); 382 | _dragy = Fl::event_y(); 383 | _dragpoints = points; 384 | 385 | _isdragging = 2; 386 | 387 | return 1; 388 | } 389 | 390 | break; 391 | case FL_DRAG: 392 | 393 | if ( _isdragging == 1 ) { 394 | 395 | temp[0].x = (((Fl::event_x()-(x()+1))+_hscrollbar.value())/zoom); 396 | temp[0].y = (((Fl::event_y()-(y()+1))+_vscrollbar.value())/zoom); 397 | 398 | if ( temp[0].x < 0 ) { 399 | temp[0].x = 0; 400 | } 401 | if ( temp[0].y < 0 ) { 402 | temp[0].y = 0; 403 | } 404 | 405 | if ( temp[0].x > 255 ) { 406 | temp[0].x = 255; 407 | } 408 | if ( temp[0].y > 255 ) { 409 | temp[0].y = 255; 410 | } 411 | 412 | p->t[_drag].u = temp[0].x; 413 | p->t[_drag].v = temp[0].y; 414 | 415 | for( int i=0; iprimtype ) ) { 419 | 420 | project.model.primitives[i].t[_drag] = p->t[_drag]; 421 | 422 | } 423 | 424 | } 425 | 426 | ui->glviewport->redraw(); 427 | redraw(); 428 | 429 | } else if ( _isdragging == 2 ) { 430 | 431 | int pu,pv; 432 | pu = (Fl::event_x()-_dragx)/zoom; 433 | pv = (Fl::event_y()-_dragy)/zoom; 434 | 435 | for( int i=0; i<_dragpoints; i++ ) { 436 | 437 | int tempx,tempy; 438 | 439 | tempx = _draguv[i].x+pu; 440 | tempy = _draguv[i].y+pv; 441 | 442 | if ( tempx < 0 ) { 443 | tempx = 0; 444 | } 445 | if ( tempy < 0 ) { 446 | tempy = 0; 447 | } 448 | if ( tempx > 255 ) { 449 | tempx = 255; 450 | } 451 | if ( tempy > 255 ) { 452 | tempy = 255; 453 | } 454 | 455 | p->t[i].u = tempx; 456 | p->t[i].v = tempy; 457 | } 458 | 459 | ui->glviewport->redraw(); 460 | redraw(); 461 | 462 | } 463 | 464 | break; 465 | case FL_RELEASE: 466 | 467 | if ( _isdragging ) { 468 | 469 | _isdragging = false; 470 | 471 | } 472 | 473 | break; 474 | } 475 | 476 | return Fl_Group::handle( event ); 477 | 478 | } 479 | 480 | void TexturePreview::draw() { 481 | 482 | testScrollbars(); 483 | calculateScrollbarPos(); 484 | 485 | int max_w = w()-2; 486 | int max_h = h()-2; 487 | 488 | if ( _vscrollbar.visible() ) { 489 | max_w -= Fl::scrollbar_size(); 490 | } 491 | 492 | if ( _hscrollbar.visible() ) { 493 | max_h -= Fl::scrollbar_size(); 494 | } 495 | 496 | draw_box( box(), x(), y(), w(), h(), color() ); 497 | 498 | fl_color( FL_BLACK ); 499 | 500 | if ( project.last_selected >= 0 ) { 501 | 502 | if ( project.model.IsTextured( &project.model.primitives[project.last_selected] ) ) { 503 | 504 | SmxClass::PRIMITIVE *p 505 | = &project.model.primitives[project.last_selected]; 506 | 507 | fl_push_clip( x()+1, y()+1, max_w, max_h ); 508 | 509 | fl_draw_image( draw_texture, this, x()+1, y()+1, 510 | max_w, max_h, 3 ); 511 | 512 | int c; 513 | 514 | if ( ( project.model.primitives[project.last_selected].primtype 515 | == SmxClass::PRIM_QUAD_FT ) || 516 | ( project.model.primitives[project.last_selected].primtype 517 | == SmxClass::PRIM_QUAD_GT ) ) { 518 | 519 | c = 4; 520 | 521 | fl_color( FL_MAGENTA ); 522 | 523 | fl_line( 524 | (1+x()+(p->t[0].u*zoom)+(zoom/2))-_hscrollbar.value(), 525 | (1+y()+(p->t[0].v*zoom)+(zoom/2))-_vscrollbar.value(), 526 | (1+x()+(p->t[1].u*zoom)+(zoom/2))-_hscrollbar.value(), 527 | (1+y()+(p->t[1].v*zoom)+(zoom/2))-_vscrollbar.value(), 528 | (1+x()+(p->t[3].u*zoom)+(zoom/2))-_hscrollbar.value(), 529 | (1+y()+(p->t[3].v*zoom)+(zoom/2))-_vscrollbar.value() ); 530 | 531 | fl_line( 532 | (1+x()+(p->t[3].u*zoom)+(zoom/2))-_hscrollbar.value(), 533 | (1+y()+(p->t[3].v*zoom)+(zoom/2))-_vscrollbar.value(), 534 | (1+x()+(p->t[2].u*zoom)+(zoom/2))-_hscrollbar.value(), 535 | (1+y()+(p->t[2].v*zoom)+(zoom/2))-_vscrollbar.value(), 536 | (1+x()+(p->t[0].u*zoom)+(zoom/2))-_hscrollbar.value(), 537 | (1+y()+(p->t[0].v*zoom)+(zoom/2))-_vscrollbar.value() ); 538 | 539 | } else { 540 | 541 | c = 3; 542 | 543 | fl_color( FL_MAGENTA ); 544 | 545 | fl_line( 546 | (1+x()+(p->t[0].u*zoom)+(zoom/2))-_hscrollbar.value(), 547 | (1+y()+(p->t[0].v*zoom)+(zoom/2))-_vscrollbar.value(), 548 | (1+x()+(p->t[1].u*zoom)+(zoom/2))-_hscrollbar.value(), 549 | (1+y()+(p->t[1].v*zoom)+(zoom/2))-_vscrollbar.value(), 550 | (1+x()+(p->t[2].u*zoom)+(zoom/2))-_hscrollbar.value(), 551 | (1+y()+(p->t[2].v*zoom)+(zoom/2))-_vscrollbar.value() ); 552 | 553 | fl_line( 554 | (1+x()+(p->t[2].u*zoom)+(zoom/2))-_hscrollbar.value(), 555 | (1+y()+(p->t[2].v*zoom)+(zoom/2))-_vscrollbar.value(), 556 | (1+x()+(p->t[0].u*zoom)+(zoom/2))-_hscrollbar.value(), 557 | (1+y()+(p->t[0].v*zoom)+(zoom/2))-_vscrollbar.value() ); 558 | 559 | } 560 | 561 | for( int i=0; it[i].u*zoom)+(zoom/2)-3)-_hscrollbar.value(), 565 | (1+y()+(p->t[i].v*zoom)+(zoom/2)-3)-_vscrollbar.value(), 566 | 8, 8, FL_MAGENTA ); 567 | 568 | } 569 | 570 | fl_pop_clip(); 571 | 572 | } 573 | 574 | } 575 | 576 | draw_child( _hscrollbar ); 577 | draw_child( _vscrollbar ); 578 | 579 | } 580 | 581 | void TexturePreview::resize(int x, int y, int w, int h) { 582 | 583 | Fl_Group::resize( x, y, w, h ); 584 | calculateScrollbarPos(); 585 | 586 | } 587 | 588 | void TexturePreview::calculateScrollbarPos() { 589 | 590 | _hscrollbar.resize( Fl_Group::x()+1, Fl_Group::y()+Fl_Group::h()-Fl::scrollbar_size()-1, 591 | Fl_Group::w()-2, Fl::scrollbar_size() ); 592 | 593 | if ( _hscrollbar.visible() ) { 594 | _vscrollbar.resize( Fl_Group::x()+Fl_Group::w()-Fl::scrollbar_size()-1, 595 | Fl_Group::y()+1, Fl::scrollbar_size(), (Fl_Group::h()-2)-Fl::scrollbar_size() ); 596 | } else { 597 | _vscrollbar.resize( Fl_Group::x()+Fl_Group::w()-Fl::scrollbar_size()-1, 598 | Fl_Group::y()+1, Fl::scrollbar_size(), Fl_Group::h()-2 ); 599 | } 600 | 601 | } 602 | 603 | void TexturePreview::testScrollbars() { 604 | 605 | int max_w = w()-2; 606 | int max_h = h()-2; 607 | 608 | if ( _vscrollbar.visible() ) { 609 | max_w -= Fl::scrollbar_size(); 610 | } 611 | 612 | if ( _hscrollbar.visible() ) { 613 | max_h -= Fl::scrollbar_size(); 614 | } 615 | 616 | if ( project.last_selected >= 0 ) { 617 | 618 | if ( project.model.IsTextured( 619 | &project.model.primitives[project.last_selected] ) ) { 620 | 621 | TexImageClass* tex 622 | = (TexImageClass*)project.model.textures 623 | [project.model.primitives[project.last_selected].texnum]->userdata2; 624 | 625 | if ( tex == nullptr ) { 626 | return; 627 | } 628 | 629 | if ( ( tex->w*zoom ) > max_w ) { 630 | 631 | _hscrollbar.minimum( 0 ); 632 | _hscrollbar.value( _hscrollbar.value(), max_w, 0, tex->w*zoom ); 633 | _hscrollbar.show(); 634 | 635 | } else { 636 | 637 | _hscrollbar.value( 0 ); 638 | _hscrollbar.hide(); 639 | 640 | } 641 | 642 | if ( ( tex->h*zoom ) > max_h ) { 643 | 644 | _vscrollbar.minimum( 0 ); 645 | _vscrollbar.value( _vscrollbar.value(), max_h, 0, tex->h*zoom ); 646 | _vscrollbar.show(); 647 | 648 | } else { 649 | 650 | _vscrollbar.value( 0 ); 651 | _vscrollbar.hide(); 652 | 653 | } 654 | } 655 | } 656 | 657 | } -------------------------------------------------------------------------------- /glwidget.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | 7 | /* 8 | * File: glwidget.cpp 9 | * Author: Lameguy64 10 | * 11 | * Created on March 13, 2018, 1:24 PM 12 | */ 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "glwidget.h" 21 | #include "global.h" 22 | 23 | #define PI 3.14159265358979f 24 | #define ROTPI PI/180.f 25 | 26 | void paintModeToggle_cb(Fl_Button* widget, void* userdata) { 27 | 28 | if ( project.last_selected < 0 ) { 29 | widget->value( 0 ); 30 | return; 31 | } 32 | 33 | if ( widget->value() ) { 34 | 35 | project.model.ClearSelection(); 36 | project.last_selected = -1; 37 | project.active_primitive = &project.temp; 38 | } 39 | 40 | ui->glviewport->redraw(); 41 | 42 | } 43 | 44 | void renderModeToggle_cb(Fl_Button* widget, long userdata) { 45 | 46 | if ( userdata == 0 ) { 47 | 48 | ui->glviewport->wireframe_overlay = widget->value(); 49 | 50 | } else if ( userdata == 1 ) { 51 | 52 | ui->glviewport->backface_culling = widget->value(); 53 | 54 | } 55 | 56 | ui->glviewport->redraw(); 57 | 58 | } 59 | 60 | void resetPosition_cb(Fl_Button* widget, void* userdata) { 61 | 62 | ui->glviewport->position_x = 0.f; 63 | ui->glviewport->position_y = 0.f; 64 | ui->glviewport->position_z = 0.f; 65 | 66 | if ( project.model.vertices.size() ) { 67 | 68 | for( int i=0; iglviewport->position_x += project.model.vertices[i].x; 71 | ui->glviewport->position_y += project.model.vertices[i].y; 72 | ui->glviewport->position_z += project.model.vertices[i].z; 73 | 74 | } 75 | 76 | } 77 | 78 | ui->glviewport->position_x /= (float)project.model.vertices.size(); 79 | ui->glviewport->position_y /= (float)project.model.vertices.size(); 80 | ui->glviewport->position_z /= (float)project.model.vertices.size(); 81 | 82 | ui->glviewport->redraw(); 83 | 84 | } 85 | 86 | void homePosition_cb(Fl_Button* widget, void* userdata) { 87 | 88 | ui->glviewport->position_x = 0.f; 89 | ui->glviewport->position_y = 0.f; 90 | ui->glviewport->position_z = 0.f; 91 | ui->glviewport->redraw(); 92 | 93 | } 94 | 95 | void perspModeChange_cb(Fl_Button* widget, long userdata) { 96 | 97 | if ( userdata == 0 ) { 98 | 99 | ui->glviewport->persp_mode = 0; 100 | 101 | } else { 102 | 103 | ui->glviewport->persp_mode = 1; 104 | 105 | } 106 | 107 | ui->glviewport->redraw(); 108 | 109 | } 110 | 111 | void setActiveFace(int face) { 112 | 113 | project.last_selected = face; 114 | 115 | if ( project.model.primitives[project.last_selected].primtype 116 | & SMX_PRIM_GOURAUD ) { 117 | 118 | ui->primTypeRadio[0]->value( 0 ); 119 | ui->primTypeRadio[1]->value( 1 ); 120 | ui->setColor[0]->activate(); 121 | ui->setColor[0]->value( 1 ); 122 | ui->setColor[1]->activate(); 123 | ui->setColor[1]->value( 0 ); 124 | ui->setColor[2]->activate(); 125 | ui->setColor[2]->value( 0 ); 126 | 127 | if ( project.model.primitives[project.last_selected].primtype 128 | & SMX_PRIM_QUAD ) { 129 | ui->setColor[3]->activate(); 130 | } else { 131 | ui->setColor[3]->deactivate(); 132 | } 133 | ui->setColor[3]->value( 0 ); 134 | 135 | } else { 136 | 137 | ui->primTypeRadio[0]->value( 1 ); 138 | ui->primTypeRadio[1]->value( 0 ); 139 | ui->setColor[0]->activate(); 140 | ui->setColor[0]->value( 1 ); 141 | ui->setColor[1]->deactivate(); 142 | ui->setColor[1]->value( 0 ); 143 | ui->setColor[2]->deactivate(); 144 | ui->setColor[2]->value( 0 ); 145 | ui->setColor[3]->deactivate(); 146 | ui->setColor[3]->value( 0 ); 147 | 148 | } 149 | 150 | ui->colorSlider[0]->value( project.model.primitives[project.last_selected].c[0].r ); 151 | ui->colorSlider[1]->value( project.model.primitives[project.last_selected].c[0].g ); 152 | ui->colorSlider[2]->value( project.model.primitives[project.last_selected].c[0].b ); 153 | 154 | 155 | if ( project.model.primitives[project.last_selected].primtype 156 | & SMX_PRIM_TEXTURED ) { 157 | ui->primTexturedToggle->value( 1 ); 158 | } else { 159 | ui->primTexturedToggle->value( 0 ); 160 | } 161 | 162 | 163 | ui->planeSettings->activate(); 164 | ui->textureSettings->activate(); 165 | 166 | ui->colorPreviewer->redraw(); 167 | ui->texturePreviewer->redraw(); 168 | ui->glviewport->redraw(); 169 | 170 | ui->primitiveIndexOutput->value( face ); 171 | 172 | ui->doubleSidedCheck->value( 173 | project.model.primitives[project.last_selected].doublesided ); 174 | 175 | ui->lightModeRadio[0]->value( 0 ); 176 | ui->lightModeRadio[1]->value( 0 ); 177 | ui->lightModeRadio[2]->value( 0 ); 178 | int i = project.model.primitives[project.last_selected].shadetype; 179 | ui->lightModeRadio[i]->value( 1 ); 180 | 181 | ui->blendModeRadio[0]->value( 0 ); 182 | ui->blendModeRadio[1]->value( 0 ); 183 | ui->blendModeRadio[2]->value( 0 ); 184 | ui->blendModeRadio[3]->value( 0 ); 185 | ui->blendModeRadio[4]->value( 0 ); 186 | i = project.model.primitives[project.last_selected].blendmode; 187 | ui->blendModeRadio[i]->value( 1 ); 188 | 189 | project.temp = project.model.primitives[project.last_selected]; 190 | project.active_primitive = &project.model.primitives[project.last_selected]; 191 | } 192 | 193 | void unsetFace() { 194 | 195 | project.model.ClearSelection(); 196 | project.last_selected = -1; 197 | project.active_primitive = &project.temp; 198 | 199 | ui->planeSettings->deactivate(); 200 | ui->textureSettings->deactivate(); 201 | ui->glviewport->redraw(); 202 | ui->colorPreviewer->redraw(); 203 | ui->texturePreviewer->redraw(); 204 | } 205 | 206 | glwidget::glwidget(int x, int y, int w, int h, const char* l) 207 | : Fl_Gl_Window( x, y, w, h, l ) { 208 | 209 | end(); 210 | 211 | init_done = false; 212 | position_x = 0.f; 213 | position_y = 0.f; 214 | position_z = 0.f; 215 | view_move = false; 216 | shift_down = false; 217 | persp_mode = 0; 218 | 219 | zoom = 4.f; 220 | rotate_x = 0.f; 221 | rotate_y = 0.f; 222 | 223 | backface_culling = true; 224 | wireframe_overlay = false; 225 | 226 | } 227 | 228 | glwidget::~glwidget() { 229 | } 230 | 231 | void glwidget::draw() { 232 | 233 | if ( !Fl_Gl_Window::valid() ) { 234 | 235 | if ( !init_done ) { 236 | 237 | if ( config.glsl_enable ) { 238 | 239 | int err = glewInit(); 240 | if ( err ) { 241 | fl_message_title( "GLEW Init Failed" ); 242 | switch( err ) { 243 | case GLEW_ERROR_NO_GL_VERSION: 244 | fl_message_title( "No OpenGL version available." ); 245 | fl_message( "Check if you have graphics drivers installed." ); 246 | break; 247 | case GLEW_ERROR_GL_VERSION_10_ONLY: 248 | fl_message_title( "Only OpenGL 1.0 is available.\n" ); 249 | fl_message( "Your graphics card is too old for GLSL." ); 250 | break; 251 | case GLEW_ERROR_GLX_VERSION_11_ONLY: 252 | fl_message_title( "Only OpenGL 1.1 is available." ); 253 | fl_message( "Your graphics card is too old for GLSL." ); 254 | break; 255 | } 256 | } else { 257 | #ifdef DEBUG 258 | std::cout << "GLEW initialized successfully." << std::endl; 259 | #endif 260 | if ( shader.Initialize() ) { 261 | 262 | fl_message_title( "Shader Init Error" ); 263 | fl_message( "Unable to initialize GLSL shader.\n" 264 | "Falling back to fixed-function rendering." ); 265 | 266 | config.glsl_enable = false; 267 | } 268 | } 269 | 270 | } 271 | 272 | init_done = true; 273 | } 274 | 275 | glViewport( 0, 0, w(), h() ); 276 | glEnable( GL_TEXTURE_2D ); 277 | 278 | glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); 279 | glEnable( GL_BLEND ); 280 | 281 | glFrontFace( GL_CW ); 282 | glCullFace( GL_BACK ); 283 | 284 | } 285 | 286 | glClearColor( 0.2f, 0.0f, 0.2f, 1.0f ); 287 | glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT ); 288 | 289 | // Setup projection 290 | glMatrixMode( GL_PROJECTION ); 291 | glLoadIdentity(); 292 | gluPerspective( tan(90.f*PI/360)*atan((float)h()/w())*360/PI, 293 | ((float)w()/h()), 1, 32768 ); 294 | 295 | // Setup modelview 296 | glMatrixMode( GL_MODELVIEW ); 297 | glLoadIdentity(); 298 | 299 | if ( project.bg_model.vertices.size() ) { 300 | glPushMatrix(); 301 | glRotatef( rotate_y, 1.f, 0.f, 0.f ); 302 | glRotatef( rotate_x, 0.f, 1.f, 0.f ); 303 | glDisable( GL_DEPTH_TEST ); 304 | project.bg_model.RenderTextured( 1 ); 305 | glPopMatrix(); 306 | } 307 | 308 | if ( persp_mode == 0 ) { 309 | glTranslatef( 0.f, 0.f, -zoom ); 310 | glRotatef( rotate_y, 1.f, 0.f, 0.f ); 311 | glRotatef( rotate_x, 0.f, 1.f, 0.f ); 312 | glTranslatef( -position_x, position_y, position_z ); 313 | } else { 314 | glRotatef( rotate_y, 1.f, 0.f, 0.f ); 315 | glRotatef( rotate_x, 0.f, 1.f, 0.f ); 316 | glTranslatef( -position_x, position_y, position_z ); 317 | } 318 | 319 | glGetDoublev( GL_PROJECTION_MATRIX, m_projection ); 320 | glGetDoublev( GL_MODELVIEW_MATRIX, m_modelview ); 321 | glGetIntegerv( GL_VIEWPORT, m_view ); 322 | 323 | 324 | glEnable( GL_DEPTH_TEST ); 325 | glDepthFunc( GL_LEQUAL ); 326 | 327 | glBindTexture( GL_TEXTURE_2D, 0 ); 328 | 329 | // Draw center 330 | glColor4f( 0.5f, 0.f, 0.f, 1.f ); 331 | glBegin( GL_LINES ); 332 | 333 | glVertex3f( -1.f, 0.f, 0.f ); 334 | glVertex3f( 1.f, 0.f, 0.f ); 335 | glVertex3f( 0.f, 0.f, -1.f ); 336 | glVertex3f( 0.f, 0.f, 1.f ); 337 | 338 | glEnd(); 339 | 340 | if ( ui->viewDepthToggle->value() ) { 341 | glEnable( GL_DEPTH_TEST ); 342 | } else { 343 | glDisable( GL_DEPTH_TEST ); 344 | } 345 | 346 | glPolygonOffset(1.0, 1.0); 347 | glEnable( GL_POLYGON_OFFSET_FILL ); 348 | 349 | 350 | /*pos.x = -( (zoom*sin(rotate_x*ROTPI))*cos(rotate_y*ROTPI) ); 351 | pos.y = -( zoom*sin(rotate_y*ROTPI) ); 352 | pos.z = -( (zoom*cos(rotate_x*ROTPI))*cos(rotate_y*ROTPI) ); 353 | 354 | pos.x += position_x; 355 | pos.y += position_y; 356 | pos.z += position_z; 357 | 358 | GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 }; 359 | GLfloat mat_shininess[] = { 100.0 }; 360 | GLfloat light_position[] = { 361 | pos.x, 362 | -pos.y, 363 | -pos.z, 0.0 }; 364 | glShadeModel (GL_SMOOTH); 365 | 366 | glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular); 367 | glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess); 368 | glLightfv(GL_LIGHT0, GL_POSITION, light_position); 369 | 370 | glEnable( GL_COLOR_MATERIAL ); 371 | glEnable( GL_LIGHTING ); 372 | glEnable( GL_LIGHT0 );*/ 373 | 374 | project.model.RenderTextured( backface_culling ); 375 | 376 | /*glDisable( GL_LIGHTING );*/ 377 | 378 | 379 | glDisable( GL_POLYGON_OFFSET_FILL ); 380 | 381 | if ( wireframe_overlay ) { 382 | glColor4f( 0.f, 0.f, 0.f, 1.f ); 383 | project.model.RenderWireframe(); 384 | } 385 | 386 | glDisable( GL_CULL_FACE ); 387 | 388 | project.model.RenderSelected(); 389 | 390 | } 391 | 392 | void glwidget::CalculateClickRay(SmxClass::VECTOR3D* pos, SmxClass::VECTOR3D* ray) { 393 | 394 | double ncx,ncy,ncz; 395 | double fcx,fcy,fcz; 396 | 397 | gluUnProject( Fl::event_x(), h()-Fl::event_y(), 0.f, 398 | m_modelview, m_projection, m_view, &ncx, &ncy, &ncz ); 399 | gluUnProject( Fl::event_x(), h()-Fl::event_y(), 1.f, 400 | m_modelview, m_projection, m_view, &fcx, &fcy, &fcz ); 401 | 402 | ray->x = ( fcx-ncx ); 403 | ray->y = -( fcy-ncy ); 404 | ray->z = -( fcz-ncz ); 405 | 406 | // Orbital view position calculation 407 | if ( persp_mode == 0 ) { 408 | 409 | pos->x = -( (zoom*sin(rotate_x*ROTPI))*cos(rotate_y*ROTPI) ); 410 | pos->y = -( zoom*sin(rotate_y*ROTPI) ); 411 | pos->z = -( (zoom*cos(rotate_x*ROTPI))*cos(rotate_y*ROTPI) ); 412 | 413 | pos->x += position_x; 414 | pos->y += position_y; 415 | pos->z += position_z; 416 | 417 | } else { 418 | 419 | pos->x = position_x; 420 | pos->y = position_y; 421 | pos->z = position_z; 422 | 423 | } 424 | 425 | } 426 | 427 | int glwidget::handle(int event) { 428 | 429 | SmxClass::PRIMITIVE *p = project.active_primitive; 430 | 431 | switch( event ) { 432 | case FL_PUSH: 433 | 434 | if ( Fl::event_clicks() ) { 435 | 436 | if ( ( p != nullptr ) && ( Fl::event_button1() ) ) { 437 | if ( ( p->primtype == SmxClass::PRIM_TRI_F ) || 438 | ( p->primtype == SmxClass::PRIM_TRI_FT ) || 439 | ( p->primtype == SmxClass::PRIM_TRI_G ) || 440 | ( p->primtype == SmxClass::PRIM_TRI_GT ) ) { 441 | 442 | position_x = (project.model.vertices[p->v[0]].x 443 | +project.model.vertices[p->v[1]].x 444 | +project.model.vertices[p->v[2]].x)/3; 445 | position_y = (project.model.vertices[p->v[0]].y 446 | +project.model.vertices[p->v[1]].y 447 | +project.model.vertices[p->v[2]].y)/3; 448 | position_z = (project.model.vertices[p->v[0]].z 449 | +project.model.vertices[p->v[1]].z 450 | +project.model.vertices[p->v[2]].z)/3; 451 | 452 | } else if ( ( p->primtype == SmxClass::PRIM_QUAD_F ) || 453 | ( p->primtype == SmxClass::PRIM_QUAD_FT ) || 454 | ( p->primtype == SmxClass::PRIM_QUAD_G ) || 455 | ( p->primtype == SmxClass::PRIM_QUAD_GT ) ) { 456 | 457 | position_x = (project.model.vertices[p->v[0]].x 458 | +project.model.vertices[p->v[1]].x 459 | +project.model.vertices[p->v[2]].x 460 | +project.model.vertices[p->v[3]].x)/4; 461 | position_y = (project.model.vertices[p->v[0]].y 462 | +project.model.vertices[p->v[1]].y 463 | +project.model.vertices[p->v[2]].y 464 | +project.model.vertices[p->v[3]].y)/4; 465 | position_z = (project.model.vertices[p->v[0]].z 466 | +project.model.vertices[p->v[1]].z 467 | +project.model.vertices[p->v[2]].z 468 | +project.model.vertices[p->v[3]].z)/4; 469 | 470 | } 471 | 472 | redraw(); 473 | } 474 | 475 | } else { 476 | 477 | if ( Fl::event_button() == FL_LEFT_MOUSE ) { 478 | CalculateClickRay( &pos, &ray ); 479 | 480 | int face = project.model.ScanFace( pos, ray ); 481 | 482 | if ( face >= 0 ) { 483 | 484 | if ( ui->paintModeToggle->value() == 0 ) { 485 | 486 | if ( !shift_down ) { 487 | project.model.ClearSelection(); 488 | } 489 | 490 | project.model.primitives[face].selected = true; 491 | setActiveFace( face ); 492 | 493 | } else { 494 | 495 | SmxClass::PRIMITIVE temp = project.model.primitives[face]; 496 | project.temp.selected = false; 497 | 498 | if ( ( project.temp.primtype & SMX_PRIM_QUAD ) 499 | == ( project.model.primitives[face].primtype & SMX_PRIM_QUAD ) ) { 500 | 501 | project.model.primitives[face] = project.temp; 502 | 503 | project.model.primitives[face].v[0] = temp.v[0]; 504 | project.model.primitives[face].v[1] = temp.v[1]; 505 | project.model.primitives[face].v[2] = temp.v[2]; 506 | project.model.primitives[face].v[3] = temp.v[3]; 507 | 508 | project.model.primitives[face].n[0] = temp.n[0]; 509 | project.model.primitives[face].n[1] = temp.n[1]; 510 | project.model.primitives[face].n[2] = temp.n[2]; 511 | project.model.primitives[face].n[3] = temp.n[3]; 512 | 513 | } 514 | 515 | redraw(); 516 | 517 | return 1; 518 | } 519 | 520 | } else { 521 | 522 | if ( ui->paintModeToggle->value() == 0 ) { 523 | unsetFace(); 524 | } 525 | 526 | } 527 | 528 | } else if ( Fl::event_button() == FL_RIGHT_MOUSE ) { 529 | 530 | clickx = Fl::event_x(); 531 | clicky = Fl::event_y(); 532 | 533 | if ( shift_down ) { 534 | clickrx = position_x; 535 | clickry = position_y; 536 | clickrz = position_z; 537 | view_move = 2; 538 | } else { 539 | clickrx = rotate_x; 540 | clickry = rotate_y; 541 | view_move = 1; 542 | } 543 | 544 | return 1; 545 | 546 | } 547 | 548 | } 549 | break; 550 | case FL_SHORTCUT: 551 | if ( ( Fl::event_key() == FL_Shift_L ) || 552 | ( Fl::event_key() == FL_Shift_R ) ) { 553 | 554 | shift_down = true; 555 | } 556 | break; 557 | case FL_KEYUP: 558 | if ( ( Fl::event_key() == FL_Shift_L ) || 559 | ( Fl::event_key() == FL_Shift_R ) ) { 560 | 561 | shift_down = false; 562 | } 563 | break; 564 | case FL_DRAG: 565 | 566 | if ( ( ui->paintModeToggle->value() ) && ( Fl::event_button1() ) ) { 567 | 568 | CalculateClickRay( &pos, &ray ); 569 | 570 | int face = project.model.ScanFace( pos, ray ); 571 | 572 | if ( ( face >= 0 ) && ( project.temp.primtype & SMX_PRIM_QUAD ) 573 | == ( project.model.primitives[face].primtype & SMX_PRIM_QUAD ) ) { 574 | 575 | SmxClass::PRIMITIVE temp = project.model.primitives[face]; 576 | project.temp.selected = false; 577 | temp.selected = false; 578 | 579 | project.model.primitives[face] = project.temp; 580 | 581 | project.model.primitives[face].v[0] = temp.v[0]; 582 | project.model.primitives[face].v[1] = temp.v[1]; 583 | project.model.primitives[face].v[2] = temp.v[2]; 584 | project.model.primitives[face].v[3] = temp.v[3]; 585 | 586 | project.model.primitives[face].n[0] = temp.n[0]; 587 | project.model.primitives[face].n[1] = temp.n[1]; 588 | project.model.primitives[face].n[2] = temp.n[2]; 589 | project.model.primitives[face].n[3] = temp.n[3]; 590 | 591 | redraw(); 592 | 593 | } 594 | 595 | } 596 | 597 | if ( view_move == 1 ) { 598 | rotate_x = clickrx+((Fl::event_x()-clickx)/4.f); 599 | rotate_y = clickry+((Fl::event_y()-clicky)/4.f); 600 | redraw(); 601 | } else if ( view_move == 2 ) { 602 | position_x = clickrx-((Fl::event_x()-clickx)/20.f)*cos(rotate_x*ROTPI) 603 | +(((Fl::event_y()-clicky)/20.f)*sin(rotate_y*ROTPI)*sin(rotate_x*ROTPI)); 604 | position_y = clickry-((Fl::event_y()-clicky)/20.f)*cos(rotate_y*ROTPI); 605 | position_z = clickrz+((Fl::event_x()-clickx)/20.f)*sin(rotate_x*ROTPI) 606 | +(((Fl::event_y()-clicky)/20.f)*sin(rotate_y*ROTPI)*cos(rotate_x*ROTPI)); 607 | redraw(); 608 | } 609 | break; 610 | case FL_MOUSEWHEEL: 611 | if ( persp_mode == 0 ) { 612 | zoom += Fl::event_dy(); 613 | } else { 614 | position_x -= ((Fl::event_dy()/2.f)*sin(rotate_x*ROTPI))*cos(rotate_y*ROTPI); 615 | position_y -= (Fl::event_dy()/2.f)*sin(rotate_y*ROTPI); 616 | position_z -= ((Fl::event_dy()/2.f)*cos(rotate_x*ROTPI))*cos(rotate_y*ROTPI); 617 | } 618 | redraw(); 619 | break; 620 | case FL_RELEASE: 621 | view_move = 0; 622 | break; 623 | } 624 | 625 | return Fl_Gl_Window::handle( event ); 626 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /common/smx.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "smx.h" 5 | 6 | static int casecmp( const char *a, const char *b ) 7 | { 8 | 9 | int ca,cb; 10 | 11 | do 12 | { 13 | 14 | ca = (unsigned char) *a++; 15 | cb = (unsigned char) *b++; 16 | ca = tolower(toupper(ca)); 17 | cb = tolower(toupper(cb)); 18 | 19 | } 20 | while (ca == cb && ca != '\0'); 21 | 22 | return ca-cb; 23 | 24 | } 25 | 26 | 27 | SmxClass::SmxClass() 28 | { 29 | } 30 | 31 | SmxClass::~SmxClass() 32 | { 33 | Clear(); 34 | 35 | for ( int i=0; iFirstChildElement( "vertices" ) != nullptr ) 89 | { 90 | 91 | tinyxml2::XMLElement* vElement = modelElement->FirstChildElement( 92 | "vertices" ); 93 | 94 | vElement = vElement->FirstChildElement("v"); 95 | while ( vElement != nullptr ) 96 | { 97 | 98 | SmxClass::VECTOR3D vertex; 99 | 100 | vertex.x = vElement->FloatAttribute( "x" ); 101 | vertex.y = vElement->FloatAttribute( "y" ); 102 | vertex.z = vElement->FloatAttribute( "z" ); 103 | 104 | SmxClass::vertices.push_back( vertex ); 105 | 106 | vElement = vElement->NextSiblingElement( "v" ); 107 | 108 | } 109 | 110 | } 111 | else 112 | { 113 | 114 | return SmxClass::ERR_NO_VERTS; 115 | 116 | } 117 | 118 | // Read normals 119 | if ( modelElement->FirstChildElement( "normals" ) != nullptr ) 120 | { 121 | 122 | tinyxml2::XMLElement* nElement = modelElement->FirstChildElement("normals"); 123 | 124 | nElement = nElement->FirstChildElement("v"); 125 | while ( nElement != nullptr ) 126 | { 127 | 128 | SmxClass::VECTOR3D normal; 129 | 130 | normal.x = nElement->FloatAttribute( "x" ); 131 | normal.y = nElement->FloatAttribute( "y" ); 132 | normal.z = nElement->FloatAttribute( "z" ); 133 | SmxClass::normals.push_back( normal ); 134 | 135 | nElement = nElement->NextSiblingElement( "v" ); 136 | 137 | } 138 | 139 | } 140 | /* 141 | else 142 | { 143 | 144 | return SmxClass::ERR_NO_NORMS; 145 | 146 | }*/ 147 | 148 | // Read texture files 149 | if (modelElement->FirstChildElement("textures") != nullptr) { 150 | 151 | tinyxml2::XMLElement* tElement = modelElement->FirstChildElement("textures"); 152 | 153 | tElement = tElement->FirstChildElement("texture"); 154 | 155 | while ( tElement != nullptr ) 156 | { 157 | 158 | if ( tElement->Attribute( "file" ) != NULL ) 159 | { 160 | SmxClass::TextureClass *texture = new TextureClass; 161 | 162 | texture->filename = tElement->Attribute( "file" ); 163 | texture->userdata1 = 0; 164 | texture->userdata2 = nullptr; 165 | 166 | textures.push_back( texture ); 167 | } 168 | 169 | tElement = tElement->NextSiblingElement( "texture" ); 170 | 171 | } 172 | 173 | } 174 | 175 | // Read primitives 176 | if ( modelElement->FirstChildElement( "primitives" ) != nullptr ) 177 | { 178 | 179 | tinyxml2::XMLElement* pElement = modelElement->FirstChildElement( 180 | "primitives" ); 181 | 182 | if ( pElement == nullptr ) 183 | { 184 | return SmxClass::ERR_NO_PRIMS; 185 | } 186 | 187 | pElement = pElement->FirstChildElement("poly"); 188 | 189 | while ( pElement != NULL ) 190 | { 191 | const char* primType = pElement->Attribute("type"); 192 | SMX_PRIMTYPE primCode = SmxClass::PRIM_TRI_F; 193 | 194 | SmxClass::PRIMITIVE prim; 195 | 196 | prim.texnum = -1; 197 | 198 | if ( pElement->Attribute( "shading" ) != nullptr ) 199 | { 200 | 201 | if ( casecmp( "F", pElement->Attribute("shading") ) == 0 ) 202 | { 203 | prim.shadetype = SmxClass::SHADE_FLAT; 204 | } 205 | else if ( casecmp( "S", pElement->Attribute("shading") ) == 0 ) 206 | { 207 | prim.shadetype = SmxClass::SHADE_SMOOTH; 208 | } 209 | else 210 | { 211 | prim.shadetype = SmxClass::SHADE_FLAT; 212 | } 213 | 214 | } else { 215 | 216 | prim.shadetype = SmxClass::SHADE_NONE; 217 | 218 | } 219 | 220 | if ( pElement->Attribute( "blend" ) != nullptr ) 221 | { 222 | 223 | prim.blendmode = pElement->IntAttribute( "blend" ); 224 | 225 | } else { 226 | 227 | prim.blendmode = 0; 228 | 229 | } 230 | 231 | if ( pElement->Attribute( "double" ) != nullptr ) 232 | { 233 | 234 | prim.doublesided = pElement->IntAttribute( "double" ); 235 | 236 | } else { 237 | 238 | prim.doublesided = 0; 239 | 240 | } 241 | 242 | if ( casecmp( "F3", primType ) == 0 ) 243 | { 244 | primCode = SmxClass::PRIM_TRI_F; 245 | } 246 | else if ( casecmp( "FT3", primType ) == 0 ) 247 | { 248 | primCode = SmxClass::PRIM_TRI_FT; 249 | } 250 | else if ( casecmp( "G3", primType ) == 0 ) 251 | { 252 | primCode = SmxClass::PRIM_TRI_G; 253 | } 254 | else if ( casecmp( "GT3", primType ) == 0) 255 | { 256 | primCode = SmxClass::PRIM_TRI_GT; 257 | } 258 | else if ( casecmp( "F4", primType ) == 0 ) 259 | { 260 | primCode = SmxClass::PRIM_QUAD_F; 261 | } 262 | else if ( casecmp( "FT4", primType ) == 0 ) 263 | { 264 | primCode = SmxClass::PRIM_QUAD_FT; 265 | } 266 | else if ( casecmp( "G4", primType ) == 0 ) 267 | { 268 | primCode = SmxClass::PRIM_QUAD_G; 269 | } 270 | else if ( casecmp( "GT4", primType ) == 0 ) 271 | { 272 | primCode = SmxClass::PRIM_QUAD_GT; 273 | } 274 | 275 | prim.primtype = primCode; 276 | 277 | if ( ( primCode == SmxClass::PRIM_TRI_F ) || 278 | ( primCode == SmxClass::PRIM_TRI_FT ) || 279 | ( primCode == SmxClass::PRIM_TRI_G ) || 280 | ( primCode == SmxClass::PRIM_TRI_GT ) ) 281 | { 282 | 283 | prim.v[0] = pElement->IntAttribute( "v0" ); 284 | prim.v[1] = pElement->IntAttribute( "v1" ); 285 | prim.v[2] = pElement->IntAttribute( "v2" ); 286 | 287 | if ( prim.shadetype == SmxClass::SHADE_FLAT ) 288 | { 289 | prim.n[0] = pElement->IntAttribute( "n0" ); 290 | prim.n[1] = pElement->IntAttribute( "n0" ); 291 | prim.n[2] = pElement->IntAttribute( "n0" ); 292 | } 293 | else 294 | { 295 | prim.n[0] = pElement->IntAttribute( "n0" ); 296 | prim.n[1] = pElement->IntAttribute( "n1" ); 297 | prim.n[2] = pElement->IntAttribute( "n2" ); 298 | } 299 | 300 | if ( ( primCode == SmxClass::PRIM_TRI_F ) || 301 | ( primCode == SmxClass::PRIM_TRI_FT ) || 302 | ( primCode == SmxClass::PRIM_TRI_G ) || 303 | ( primCode == SmxClass::PRIM_TRI_GT ) ) 304 | { 305 | 306 | prim.c[0].r = pElement->IntAttribute( "r0" ); 307 | prim.c[0].g = pElement->IntAttribute( "g0" ); 308 | prim.c[0].b = pElement->IntAttribute( "b0" ); 309 | 310 | prim.c[1] = prim.c[0]; 311 | prim.c[2] = prim.c[0]; 312 | 313 | if ( ( primCode == SmxClass::PRIM_TRI_G ) || 314 | ( primCode == SmxClass::PRIM_TRI_GT ) ) 315 | { 316 | 317 | prim.c[1].r = pElement->IntAttribute( "r1" ); 318 | prim.c[1].g = pElement->IntAttribute( "g1" ); 319 | prim.c[1].b = pElement->IntAttribute( "b1" ); 320 | 321 | prim.c[2].r = pElement->IntAttribute( "r2" ); 322 | prim.c[2].g = pElement->IntAttribute( "g2" ); 323 | prim.c[2].b = pElement->IntAttribute( "b2" ); 324 | 325 | } 326 | 327 | if ( ( primCode == SmxClass::PRIM_TRI_FT ) || 328 | ( primCode == SmxClass::PRIM_TRI_GT ) ) 329 | { 330 | 331 | prim.texnum = pElement->IntAttribute( "texture" ); 332 | 333 | prim.t[0].u = pElement->IntAttribute( "tu0" ); 334 | prim.t[0].v = pElement->IntAttribute( "tv0" ); 335 | 336 | prim.t[1].u = pElement->IntAttribute( "tu1" ); 337 | prim.t[1].v = pElement->IntAttribute( "tv1" ); 338 | 339 | prim.t[2].u = pElement->IntAttribute( "tu2" ); 340 | prim.t[2].v = pElement->IntAttribute( "tv2" ); 341 | 342 | } 343 | 344 | } 345 | 346 | } 347 | else if ( ( primCode == SmxClass::PRIM_QUAD_F ) || 348 | ( primCode == SmxClass::PRIM_QUAD_FT ) || 349 | ( primCode == SmxClass::PRIM_QUAD_G ) || 350 | ( primCode == SmxClass::PRIM_QUAD_GT ) ) 351 | { 352 | 353 | prim.v[0] = pElement->IntAttribute( "v0" ); 354 | prim.v[1] = pElement->IntAttribute( "v1" ); 355 | prim.v[2] = pElement->IntAttribute( "v2" ); 356 | prim.v[3] = pElement->IntAttribute( "v3" ); 357 | 358 | if ( prim.shadetype == SmxClass::SHADE_FLAT ) 359 | { 360 | 361 | prim.n[0] = pElement->IntAttribute( "n0" ); 362 | prim.n[1] = prim.n[0]; 363 | prim.n[2] = prim.n[0]; 364 | prim.n[3] = prim.n[0]; 365 | 366 | } else { 367 | 368 | prim.n[0] = pElement->IntAttribute( "n0" ); 369 | prim.n[1] = pElement->IntAttribute( "n1" ); 370 | prim.n[2] = pElement->IntAttribute( "n2" ); 371 | prim.n[3] = pElement->IntAttribute( "n3" ); 372 | 373 | } 374 | 375 | if ( ( primCode == SmxClass::PRIM_QUAD_F ) || 376 | ( primCode == SmxClass::PRIM_QUAD_FT ) || 377 | ( primCode == SmxClass::PRIM_QUAD_G ) || 378 | ( primCode == SmxClass::PRIM_QUAD_GT ) ) 379 | { 380 | 381 | prim.c[0].r = pElement->IntAttribute( "r0" ); 382 | prim.c[0].g = pElement->IntAttribute( "g0" ); 383 | prim.c[0].b = pElement->IntAttribute( "b0" ); 384 | 385 | prim.c[1] = prim.c[0]; 386 | prim.c[2] = prim.c[0]; 387 | prim.c[3] = prim.c[0]; 388 | 389 | if ( ( primCode == SmxClass::PRIM_QUAD_G ) || 390 | ( primCode == SmxClass::PRIM_QUAD_GT ) ) 391 | { 392 | 393 | prim.c[1].r = pElement->IntAttribute( "r1" ); 394 | prim.c[1].g = pElement->IntAttribute( "g1" ); 395 | prim.c[1].b = pElement->IntAttribute( "b1" ); 396 | 397 | prim.c[2].r = pElement->IntAttribute( "r2" ); 398 | prim.c[2].g = pElement->IntAttribute( "g2" ); 399 | prim.c[2].b = pElement->IntAttribute( "b2" ); 400 | 401 | prim.c[3].r = pElement->IntAttribute( "r3" ); 402 | prim.c[3].g = pElement->IntAttribute( "g3" ); 403 | prim.c[3].b = pElement->IntAttribute( "b3" ); 404 | 405 | } 406 | 407 | if ( ( primCode == SmxClass::PRIM_QUAD_FT ) || 408 | ( primCode == SmxClass::PRIM_QUAD_GT ) ) 409 | { 410 | 411 | prim.texnum = pElement->IntAttribute( "texture"); 412 | 413 | prim.t[0].u = pElement->IntAttribute( "tu0" ); 414 | prim.t[0].v = pElement->IntAttribute( "tv0" ); 415 | 416 | prim.t[1].u = pElement->IntAttribute( "tu1" ); 417 | prim.t[1].v = pElement->IntAttribute( "tv1" ); 418 | 419 | prim.t[2].u = pElement->IntAttribute( "tu2" ); 420 | prim.t[2].v = pElement->IntAttribute( "tv2" ); 421 | 422 | prim.t[3].u = pElement->IntAttribute( "tu3" ); 423 | prim.t[3].v = pElement->IntAttribute( "tv3" ); 424 | 425 | } 426 | 427 | } 428 | 429 | } 430 | 431 | prim.selected = 0; 432 | 433 | SmxClass::primitives.push_back( prim ); 434 | 435 | pElement = pElement->NextSiblingElement( "poly" ); 436 | 437 | } 438 | 439 | } 440 | 441 | #ifdef DEBUG 442 | printf( "SMX model stats:\n" ); 443 | printf( " VERTS = %d\n", SmxClass::vertices.size() ); 444 | printf( " NORMS = %d\n", SmxClass::normals.size() ); 445 | printf( " PRIMS = %d\n", SmxClass::primitives.size() ); 446 | #endif 447 | 448 | return SmxClass::ERR_NONE; 449 | 450 | } 451 | 452 | SmxClass::SMX_ERROR SmxClass::SaveFile( const char *filename ) { 453 | 454 | tinyxml2::XMLDocument document; 455 | 456 | tinyxml2::XMLElement* base = document.NewElement( "model" ); 457 | base->SetAttribute( "version", 1 ); 458 | 459 | // Write vertices 460 | tinyxml2::XMLElement* o = document.NewElement( "vertices" ); 461 | tinyxml2::XMLElement* oo; 462 | 463 | o->SetAttribute( "count", (int)vertices.size() ); 464 | 465 | for ( size_t i=0; iSetAttribute( "x", vertices[i].x ); 468 | oo->SetAttribute( "y", vertices[i].y ); 469 | oo->SetAttribute( "z", vertices[i].z ); 470 | o->InsertEndChild( oo ); 471 | } 472 | 473 | base->InsertEndChild( o ); 474 | 475 | // Write normals 476 | o = document.NewElement( "normals" ); 477 | o->SetAttribute( "count", (int)normals.size() ); 478 | 479 | for ( size_t i=0; iSetAttribute( "x", normals[i].x ); 482 | oo->SetAttribute( "y", normals[i].y ); 483 | oo->SetAttribute( "z", normals[i].z ); 484 | o->InsertEndChild( oo ); 485 | } 486 | 487 | base->InsertEndChild( o ); 488 | 489 | // Textures 490 | o = document.NewElement( "textures" ); 491 | o->SetAttribute( "count", (int)textures.size() ); 492 | 493 | for ( size_t i=0; iSetAttribute( "file", textures[i]->filename.c_str() ); 496 | o->InsertEndChild( oo ); 497 | } 498 | 499 | base->InsertEndChild( o ); 500 | 501 | // Primitives 502 | 503 | o = document.NewElement( "primitives" ); 504 | o->SetAttribute( "count", (int)primitives.size() ); 505 | 506 | for ( size_t i=0; iSetAttribute( "v0", p->v[0] ); 514 | oo->SetAttribute( "v1", p->v[1] ); 515 | oo->SetAttribute( "v2", p->v[2] ); 516 | if ( ( p->primtype == PRIM_QUAD_F ) || ( p->primtype == PRIM_QUAD_FT ) || 517 | ( p->primtype == PRIM_QUAD_G ) || ( p->primtype == PRIM_QUAD_GT ) ) { 518 | 519 | oo->SetAttribute( "v3", p->v[3] ); 520 | 521 | } 522 | 523 | // Set normal indices 524 | if ( p->shadetype == SHADE_SMOOTH ) { 525 | 526 | oo->SetAttribute( "n0", p->n[0] ); 527 | oo->SetAttribute( "n1", p->n[1] ); 528 | oo->SetAttribute( "n2", p->n[2] ); 529 | 530 | if ( ( p->primtype == PRIM_QUAD_F ) || ( p->primtype == PRIM_QUAD_FT ) || 531 | ( p->primtype == PRIM_QUAD_G ) || ( p->primtype == PRIM_QUAD_GT ) ) { 532 | oo->SetAttribute( "n3", p->n[3] ); 533 | } 534 | 535 | oo->SetAttribute( "shading", "S" ); 536 | 537 | } else if ( p->shadetype == SHADE_FLAT ) { 538 | 539 | oo->SetAttribute( "n0", p->n[0] ); 540 | oo->SetAttribute( "shading", "F" ); 541 | 542 | } 543 | 544 | if ( p->blendmode ) { 545 | oo->SetAttribute( "blend", p->blendmode ); 546 | } 547 | 548 | if ( p->doublesided ) { 549 | oo->SetAttribute( "double", p->doublesided ); 550 | } 551 | 552 | // Set color values 553 | switch( p->primtype ) { 554 | case PRIM_TRI_F: 555 | case PRIM_QUAD_F: 556 | case PRIM_TRI_FT: 557 | case PRIM_QUAD_FT: 558 | oo->SetAttribute( "r0", p->c[0].r ); 559 | oo->SetAttribute( "g0", p->c[0].g ); 560 | oo->SetAttribute( "b0", p->c[0].b ); 561 | break; 562 | case PRIM_TRI_G: 563 | case PRIM_TRI_GT: 564 | oo->SetAttribute( "r0", p->c[0].r ); 565 | oo->SetAttribute( "g0", p->c[0].g ); 566 | oo->SetAttribute( "b0", p->c[0].b ); 567 | 568 | oo->SetAttribute( "r1", p->c[1].r ); 569 | oo->SetAttribute( "g1", p->c[1].g ); 570 | oo->SetAttribute( "b1", p->c[1].b ); 571 | 572 | oo->SetAttribute( "r2", p->c[2].r ); 573 | oo->SetAttribute( "g2", p->c[2].g ); 574 | oo->SetAttribute( "b2", p->c[2].b ); 575 | break; 576 | case PRIM_QUAD_G: 577 | case PRIM_QUAD_GT: 578 | oo->SetAttribute( "r0", p->c[0].r ); 579 | oo->SetAttribute( "g0", p->c[0].g ); 580 | oo->SetAttribute( "b0", p->c[0].b ); 581 | 582 | oo->SetAttribute( "r1", p->c[1].r ); 583 | oo->SetAttribute( "g1", p->c[1].g ); 584 | oo->SetAttribute( "b1", p->c[1].b ); 585 | 586 | oo->SetAttribute( "r2", p->c[2].r ); 587 | oo->SetAttribute( "g2", p->c[2].g ); 588 | oo->SetAttribute( "b2", p->c[2].b ); 589 | 590 | oo->SetAttribute( "r3", p->c[3].r ); 591 | oo->SetAttribute( "g3", p->c[3].g ); 592 | oo->SetAttribute( "b3", p->c[3].b ); 593 | break; 594 | } 595 | 596 | // Set texture coordinates 597 | if ( ( p->primtype == PRIM_TRI_FT ) || ( p->primtype == PRIM_TRI_GT ) ) { 598 | oo->SetAttribute( "texture", p->texnum ); 599 | oo->SetAttribute( "tu0", p->t[0].u ); 600 | oo->SetAttribute( "tv0", p->t[0].v ); 601 | oo->SetAttribute( "tu1", p->t[1].u ); 602 | oo->SetAttribute( "tv1", p->t[1].v ); 603 | oo->SetAttribute( "tu2", p->t[2].u ); 604 | oo->SetAttribute( "tv2", p->t[2].v ); 605 | } else if ( ( p->primtype == PRIM_QUAD_FT ) || ( p->primtype == PRIM_QUAD_GT ) ) { 606 | oo->SetAttribute( "texture", p->texnum ); 607 | oo->SetAttribute( "tu0", p->t[0].u ); 608 | oo->SetAttribute( "tv0", p->t[0].v ); 609 | oo->SetAttribute( "tu1", p->t[1].u ); 610 | oo->SetAttribute( "tv1", p->t[1].v ); 611 | oo->SetAttribute( "tu2", p->t[2].u ); 612 | oo->SetAttribute( "tv2", p->t[2].v ); 613 | oo->SetAttribute( "tu3", p->t[3].u ); 614 | oo->SetAttribute( "tv3", p->t[3].v ); 615 | } 616 | 617 | switch ( p->primtype ) { 618 | case PRIM_TRI_F: 619 | oo->SetAttribute( "type", "F3" ); 620 | break; 621 | case PRIM_TRI_FT: 622 | oo->SetAttribute( "type", "FT3" ); 623 | break; 624 | case PRIM_TRI_G: 625 | oo->SetAttribute( "type", "G3" ); 626 | break; 627 | case PRIM_TRI_GT: 628 | oo->SetAttribute( "type", "GT3" ); 629 | break; 630 | case PRIM_QUAD_F: 631 | oo->SetAttribute( "type", "F4" ); 632 | break; 633 | case PRIM_QUAD_FT: 634 | oo->SetAttribute( "type", "FT4" ); 635 | break; 636 | case PRIM_QUAD_G: 637 | oo->SetAttribute( "type", "G4" ); 638 | break; 639 | case PRIM_QUAD_GT: 640 | oo->SetAttribute( "type", "GT4" ); 641 | break; 642 | } 643 | 644 | o->InsertEndChild( oo ); 645 | } 646 | 647 | base->InsertEndChild( o ); 648 | 649 | document.InsertEndChild( base ); 650 | 651 | if ( document.SaveFile( filename ) != tinyxml2::XML_SUCCESS ) 652 | { 653 | return ERR_INVALID; 654 | } 655 | 656 | return ERR_NONE; 657 | } 658 | 659 | int SmxClass::FindVertexIndex( SmxClass::VECTOR3D *vertex ) 660 | { 661 | 662 | for ( int i=0; iprimtype == SmxClass::PRIM_TRI_FT ) || 693 | ( pri->primtype == SmxClass::PRIM_TRI_GT ) || 694 | ( pri->primtype == SmxClass::PRIM_QUAD_FT ) || 695 | ( pri->primtype == SmxClass::PRIM_QUAD_GT ) ) { 696 | 697 | return 1; 698 | 699 | } 700 | 701 | return 0; 702 | } --------------------------------------------------------------------------------