├── Camera.cpp ├── Point3D.h ├── primitive.h ├── primitive.cpp ├── raytracer.cpp ├── images ├── open.png └── save.png ├── README ├── raytracer.qrc ├── main.cpp ├── .gitignore ├── twister.h ├── RayTracerCPU.pri ├── RayTracerCPU.pro ├── RayTracer.sln ├── Camera.h ├── RayTracerCPU.vcproj.user ├── common.h ├── MathDefs.inl ├── scene.h ├── mainwindow.h ├── twister.cpp ├── raytracer.h ├── Point3D.cpp ├── material.h ├── material.cpp ├── AccessObj.h ├── MathDefs.h ├── scene.cpp ├── mainwindow.cpp ├── RayTracerCPU.vcproj └── AccessObj.cpp /Camera.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxint/raytracer-cpu/HEAD/Camera.cpp -------------------------------------------------------------------------------- /Point3D.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxint/raytracer-cpu/HEAD/Point3D.h -------------------------------------------------------------------------------- /primitive.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxint/raytracer-cpu/HEAD/primitive.h -------------------------------------------------------------------------------- /primitive.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxint/raytracer-cpu/HEAD/primitive.cpp -------------------------------------------------------------------------------- /raytracer.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxint/raytracer-cpu/HEAD/raytracer.cpp -------------------------------------------------------------------------------- /images/open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxint/raytracer-cpu/HEAD/images/open.png -------------------------------------------------------------------------------- /images/save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxint/raytracer-cpu/HEAD/images/save.png -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Introduction 2 | ============ 3 | 4 | Ray tracing in CPU, as a project for Computer Graphics course 5 | -------------------------------------------------------------------------------- /raytracer.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | images/new.png 4 | images/save.png 5 | 6 | 7 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "mainwindow.h" 3 | 4 | int main(int argc, char* argv[]) 5 | { 6 | Q_INIT_RESOURCE(raytracer); 7 | QApplication app(argc, argv); 8 | 9 | MainWindow win; 10 | win.show(); 11 | 12 | return app.exec(); 13 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # git-ls-files --others --exclude-from=.git/info/exclude 2 | # Lines that start with '#' are comments. 3 | # For a project mostly in C, the following would be a good set of 4 | # exclude patterns (uncomment them if you want to use them): 5 | # *.[oa] 6 | # *~ 7 | 8 | *.[oa] 9 | *~ 10 | *.swp 11 | Makefile* 12 | 13 | ## directories 14 | [Dd]ebug 15 | [Rr]elease 16 | res 17 | textures 18 | objs 19 | 20 | ## vs temporary files 21 | *.ncb 22 | *.pdb 23 | *.idb 24 | *.suo 25 | -------------------------------------------------------------------------------- /twister.h: -------------------------------------------------------------------------------- 1 | #ifndef I_TWISTER_H 2 | #define I_TWISTER_H 3 | 4 | namespace RayTracer { 5 | 6 | #define mtRand_N 624 7 | 8 | class Twister 9 | { 10 | public: 11 | void Seed( unsigned long seed ); 12 | Twister( unsigned long seed ) 13 | { 14 | if (seed) { Seed(seed); } 15 | else { Seed( (unsigned long)0xf2710812 ); } 16 | } 17 | Twister() { Seed( (unsigned long)0xf2710812 ); } 18 | float Rand(); 19 | unsigned long RandL(); 20 | protected: 21 | unsigned long mt[mtRand_N]; 22 | int mti; 23 | }; 24 | 25 | }; // namespace RayTracer 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /RayTracerCPU.pri: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------------- 2 | # This file is generated by the Qt Visual Studio Add-in. 3 | # ------------------------------------------------------ 4 | 5 | HEADERS += ./AccessObj.h \ 6 | ./Camera.h \ 7 | ./common.h \ 8 | ./mainwindow.h \ 9 | ./material.h \ 10 | ./MathDefs.h \ 11 | ./Point3D.h \ 12 | ./primitive.h \ 13 | ./raytracer.h \ 14 | ./scene.h \ 15 | ./twister.h 16 | SOURCES += ./AccessObj.cpp \ 17 | ./Camera.cpp \ 18 | ./main.cpp \ 19 | ./mainwindow.cpp \ 20 | ./material.cpp \ 21 | ./Point3D.cpp \ 22 | ./primitive.cpp \ 23 | ./raytracer.cpp \ 24 | ./scene.cpp \ 25 | ./twister.cpp 26 | RESOURCES += raytracer.qrc 27 | -------------------------------------------------------------------------------- /RayTracerCPU.pro: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------------- 2 | # This file is generated by the Qt Visual Studio Add-in. 3 | # ------------------------------------------------------ 4 | 5 | # This is a reminder that you are using a generated .pro file. 6 | # Remove it when you are finished editing this file. 7 | message("You are running qmake on a generated .pro file. This may not work!") 8 | 9 | 10 | TEMPLATE = app 11 | TARGET = RayTracerCPU 12 | DESTDIR = ./release 13 | CONFIG += release 14 | DEFINES += _WINDOWS QT_LARGEFILE_SUPPORT _WINDOWS QT_LARGEFILE_SUPPORT QT_DLL QT_DLL 15 | INCLUDEPATH += . \ 16 | ./release \ 17 | $(QTDIR)/mkspecs/win32-msvc2008 \ 18 | ./release \ 19 | $(QTDIR)/mkspecs/win32-msvc2008 20 | DEPENDPATH += . 21 | MOC_DIR += release 22 | OBJECTS_DIR += release 23 | UI_DIR += ./GeneratedFiles 24 | RCC_DIR += ./release 25 | include(RayTracerCPU.pri) 26 | -------------------------------------------------------------------------------- /RayTracer.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 10.00 3 | # Visual Studio 2008 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RayTracerCPU", "RayTracerCPU.vcproj", "{53EA537F-F0D9-3620-A4F3-6A7F91A34DB3}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Win32 = Debug|Win32 9 | Release|Win32 = Release|Win32 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {53EA537F-F0D9-3620-A4F3-6A7F91A34DB3}.Debug|Win32.ActiveCfg = Debug|Win32 13 | {53EA537F-F0D9-3620-A4F3-6A7F91A34DB3}.Debug|Win32.Build.0 = Debug|Win32 14 | {53EA537F-F0D9-3620-A4F3-6A7F91A34DB3}.Release|Win32.ActiveCfg = Release|Win32 15 | {53EA537F-F0D9-3620-A4F3-6A7F91A34DB3}.Release|Win32.Build.0 = Release|Win32 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | EndGlobal 21 | -------------------------------------------------------------------------------- /Camera.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | namespace RayTracer 6 | { 7 | 8 | class CCamera 9 | { 10 | public: 11 | CCamera(void); 12 | ~CCamera(void); 13 | 14 | /// Return camera position 15 | const Vec3& pos() { return mCameraPos; } 16 | 17 | /// Camera related 18 | void lookAt(const Vec3& eye, const Vec3& at, const Vec3& up); 19 | 20 | void perspective(Real fovy, Real aspect, Real zNear); 21 | void frustum(Real left, Real right, Real bottom, Real top, Real zNear); 22 | 23 | /** Get screen position at screen position (x, y) 24 | \param 25 | x, y 0~1, relative position 26 | \return 27 | screen position in world coordinates. 28 | */ 29 | Vec3 getScreenPos(Real x, Real y); 30 | 31 | private: 32 | Matrix mInvViewMatrix; 33 | 34 | Vec3 mCameraPos; 35 | Vec3 mP1, mP2, mP3, mP4, mDx, mDy; 36 | 37 | bool mNeedUpdate; // is it needed to update @mFinalMatrix 38 | }; 39 | 40 | } // namespace RayTracer 41 | -------------------------------------------------------------------------------- /RayTracerCPU.vcproj.user: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 11 | 35 | 36 | 39 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /common.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | created: 2010/04/22 3 | file name: common.h 4 | author: maxint lnychina@gmail.com 5 | *********************************************************************/ 6 | 7 | #ifndef _RT_COMMON_H_ 8 | #define _RT_COMMON_H_ 9 | 10 | #include 11 | #include 12 | 13 | #define HIGH_PRECISION 14 | 15 | #include "MathDefs.h" 16 | 17 | // ------------------------------------------------------------------------------ 18 | // Useful Macros 19 | // ------------------------------------------------------------------------------ 20 | 21 | #ifndef RT_PI 22 | #define RT_PI 3.141592653589793239462f 23 | #endif 24 | 25 | #ifndef RT_CLAMP 26 | #define RT_CLAMP(x,x1,x2) std::max(Real(x1), std::min(Real(x2), Real(x))) 27 | #endif 28 | 29 | #define RT_TRACEDEPTH 6 30 | #define RT_GRIDSIZE 32 31 | #define RT_GRIDSHIFT 5 32 | 33 | #ifndef SAFE_DELETE 34 | #define SAFE_DELETE(p) if(p) { delete (p); (p)=0; } 35 | #endif 36 | 37 | #ifndef SAFE_DELETE_ARRAY 38 | #define SAFE_DELETE_ARRAY(p) if(p) { delete[] (p); (p)=0; } 39 | #endif 40 | 41 | namespace RayTracer { 42 | 43 | #ifdef HIGH_PRECISION 44 | typedef double Real; 45 | #else 46 | typedef float Real; 47 | #endif 48 | 49 | inline Real rtRand( Real a_Range ) { return ((Real)rand() / RAND_MAX) * a_Range; } 50 | 51 | /// Intersection method return values 52 | enum RTResult 53 | { 54 | INPRIM = -1, // Ray started inside primitive 55 | MISS = 0, // Ray missed primitive 56 | HIT = 1 // Ray hit primitive 57 | }; 58 | 59 | // ------------------------------------------------------------------------------ 60 | // Useful type definitions 61 | // ------------------------------------------------------------------------------ 62 | 63 | typedef Vector3 Color; 64 | typedef Vector3 Vec3; 65 | typedef Plane_ Plane; 66 | typedef AABB_ AABB; 67 | typedef Matrix_ Matrix; 68 | typedef std::string String; 69 | 70 | }; // namespace RayTracer 71 | 72 | #endif // _RT_COMMON_H_ -------------------------------------------------------------------------------- /MathDefs.inl: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | created: 2010/04/22 3 | file name: MathDefs.inl 4 | author: maxint lnychina@gmail.com 5 | *********************************************************************/ 6 | 7 | #ifndef _RT_MATHDEFS_INL_ 8 | #define _RT_MATHDEFS_INL_ 9 | 10 | namespace RayTracer { 11 | 12 | // ------------------------------------------------------------------------------ 13 | // Vector3 class implementation 14 | // ------------------------------------------------------------------------------ 15 | 16 | template const Vector3<_Tp> Vector3<_Tp>::ZERO = Vector3<_Tp>(0,0,0); 17 | template const Vector3<_Tp> Vector3<_Tp>::ONE = Vector3<_Tp>(1,1,1); 18 | template const Vector3<_Tp> Vector3<_Tp>::UNIT_X = Vector3<_Tp>(1,0,0); 19 | template const Vector3<_Tp> Vector3<_Tp>::UNIT_Y = Vector3<_Tp>(0,1,0); 20 | template const Vector3<_Tp> Vector3<_Tp>::UNIT_Z = Vector3<_Tp>(0,0,1); 21 | 22 | template 23 | const Vector3<_Tp>& Vector3<_Tp>::Min(const Vector3<_Tp>& b) 24 | { 25 | for (int i=0; i<3; ++i) 26 | cell[i] = std::min(cell[i], b.cell[i]); 27 | 28 | return *this; 29 | } 30 | 31 | template 32 | const Vector3<_Tp>& Vector3<_Tp>::Max(const Vector3<_Tp>& b) 33 | { 34 | for (int i=0; i<3; ++i) 35 | cell[i] = std::max(cell[i], b.cell[i]); 36 | 37 | return *this; 38 | } 39 | 40 | template 41 | const Vector3<_Tp>& Vector3<_Tp>::Abs() 42 | { 43 | for (int i=0; i<3; ++i) 44 | cell[i] = std::abs(cell[i]); 45 | 46 | return *this; 47 | } 48 | 49 | template 50 | bool AABB_<_Tp>::interset(const AABB_<_Tp>& aB2) const 51 | { 52 | return ( 53 | (mMin.x < aB2.mMax.x) && (aB2.mMin.x < mMax.x) && // x-axis overlap 54 | (mMin.y < aB2.mMax.y) && (aB2.mMin.y < mMax.y) && // y-axis overlap 55 | (mMin.z < aB2.mMax.z) && (aB2.mMin.z < mMax.z) // z-axis overlap 56 | ); 57 | } 58 | 59 | template 60 | bool AABB_<_Tp>::contains(const Vector3<_Tp>& aPos) const 61 | { 62 | return ( 63 | (aPos.x > mMin.x - RT_EPSILON) && (aPos.x < mMax.x + RT_EPSILON) && 64 | (aPos.y > mMin.y - RT_EPSILON) && (aPos.y < mMax.y + RT_EPSILON) && 65 | (aPos.z > mMin.z - RT_EPSILON) && (aPos.z < mMax.z + RT_EPSILON) 66 | ); 67 | } 68 | 69 | } 70 | 71 | #endif // _RT_MATHDEFS_INL_ -------------------------------------------------------------------------------- /scene.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | created: 2010/04/13 3 | file name: scene.h 4 | author: maxint lnychina@gmail.com 5 | *********************************************************************/ 6 | 7 | #ifndef _RT_SCENE_H_ 8 | #define _RT_SCENE_H_ 9 | 10 | #include "common.h" 11 | #include 12 | #include 13 | 14 | 15 | namespace trimeshVec{ 16 | class CAccessObj; 17 | } 18 | 19 | namespace RayTracer { 20 | 21 | class Primitive; 22 | class Vertex; 23 | class Light; 24 | 25 | // ------------------------------------------------------------------------------ 26 | // Scene class definition 27 | // ------------------------------------------------------------------------------ 28 | 29 | class Scene 30 | { 31 | public: 32 | Scene(); 33 | ~Scene(); 34 | 35 | /** Initialization 36 | Create some primitives 37 | */ 38 | void initScene(); 39 | 40 | int getNumOfPrimitives() const 41 | { 42 | return static_cast(mPrimitives.size()); 43 | } 44 | int getNumOfLights() const 45 | { 46 | return static_cast(mLights.size()); 47 | } 48 | const AABB& getExtends() const 49 | { 50 | return mExtends; 51 | } 52 | 53 | /** Load obj model file 54 | */ 55 | void loadObjModel(const trimeshVec::CAccessObj* accessObj); 56 | 57 | friend class Engine; 58 | 59 | private: 60 | void setupMaterials(); 61 | 62 | /** Destroy the primitives and release the resources. 63 | */ 64 | void destroy(); 65 | 66 | /** Setup lights 67 | */ 68 | void setupLights(); 69 | 70 | /** Destroy lights 71 | */ 72 | void destroyLights(); 73 | 74 | /** Find extends of the primitives 75 | */ 76 | void updateExtends(); 77 | 78 | /** Build the grid of the primitives 79 | */ 80 | void buildGrid(); 81 | 82 | void removeGrid(); 83 | 84 | private: 85 | typedef std::list PrimitiveList; 86 | typedef PrimitiveList::iterator PrimListItor; 87 | typedef std::list ObjectList; 88 | typedef ObjectList::iterator ObjectItor; 89 | typedef std::vector GridMap; 90 | typedef GridMap::iterator GridMapItor; 91 | typedef std::list VertexList; 92 | typedef VertexList::iterator VertexItor; 93 | typedef std::list LightList; 94 | typedef LightList::iterator LightItor; 95 | private: 96 | PrimitiveList mPrimitives; 97 | LightList mLights; 98 | GridMap mGird; 99 | AABB mExtends; 100 | 101 | /// obj model loader 102 | const trimeshVec::CAccessObj* mObjLoader; 103 | VertexList mVerticesPool; 104 | }; 105 | 106 | }; // namespace RayTracer 107 | 108 | #endif // _RT_SCENE_H_ -------------------------------------------------------------------------------- /mainwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_H 2 | #define MAINWINDOW_H 3 | 4 | #include 5 | 6 | class QMenu; 7 | class QLabel; 8 | class QAction; 9 | class QActionGroup; 10 | class QDoubleSpinBox; 11 | class QProgressBar; 12 | 13 | namespace trimeshVec { 14 | class CAccessObj; 15 | } 16 | 17 | namespace RayTracer { 18 | class Engine; 19 | } 20 | 21 | class MainWindow : public QMainWindow 22 | { 23 | Q_OBJECT 24 | 25 | public: 26 | MainWindow(); 27 | MainWindow(const QString &fileName); 28 | ~MainWindow(); 29 | 30 | protected: 31 | bool eventFilter(QObject *obj, QEvent *e); 32 | QSize sizeHint() const; 33 | 34 | private: 35 | void setupUi(); 36 | void createActions(); 37 | void createMenus(); 38 | void createToolBars(); 39 | void createStatusBar(); 40 | void init(); 41 | void initRenderSystem(); 42 | void openObjFile(const QString& fileName); 43 | void renderObj(); 44 | void saveAsImageFile(const QString& fileName); 45 | void setResolution(int width, int height); 46 | QString strippedName(const QString& fullFileName); 47 | void rotateBy(double xAngle, double yAngle, double zAngle); 48 | void updateInformationBar(); 49 | 50 | private slots: 51 | void open(); 52 | void saveAs(); 53 | void resolution(); 54 | void shadeModel(QAction* act); 55 | void toggleView(QAction* act); 56 | void newFrustumOrLight(); 57 | void about(); 58 | 59 | private: 60 | QWidget *mImgView; 61 | QImage mImage; 62 | QMenu *mFileMenu; 63 | QMenu *mViewMenu; 64 | QMenu *mEditMenu; 65 | QMenu *mHelpMenu; 66 | QToolBar *mFileToolBar; 67 | QToolBar *mEditToolBar; 68 | QToolBar *mCameraLightToolBar; 69 | QAction *mOpenAct; 70 | QAction *mQuitAct; 71 | QAction *mSaveAsImageAct; 72 | QAction *mResolutionAct; 73 | QActionGroup *mShadeActGroup; 74 | QAction *mRenderAct; 75 | QActionGroup *mViewActGroup; 76 | QAction *mViewToolBarAct; 77 | QAction *mInfoToolBarAct; 78 | QAction *mAboutAct; 79 | QAction *mAboutQtAct; 80 | 81 | QDoubleSpinBox *mSpinEyeX; 82 | QDoubleSpinBox *mSpinEyeY; 83 | QDoubleSpinBox *mSpinEyeZ; 84 | QDoubleSpinBox *mSpinLightX; 85 | QDoubleSpinBox *mSpinLightY; 86 | QDoubleSpinBox *mSpinLightZ; 87 | 88 | // info toolbar 89 | QToolBar *mInfoToolBar; 90 | QLabel *mInfoLabel; 91 | long mLastCostTime; 92 | QAction *mTraceDepthAct; 93 | QAction *mRegularSamplesAct; 94 | 95 | // status bar 96 | QLabel *mResLabel; 97 | QProgressBar *mProgressBar; 98 | 99 | trimeshVec::CAccessObj *mpAccessObj; 100 | RayTracer::Engine *mEngine; 101 | 102 | // mouse operations 103 | QPoint lastPos; 104 | int xRot; 105 | int yRot; 106 | int zRot; 107 | }; 108 | 109 | #endif // MAINWINDOW_H 110 | -------------------------------------------------------------------------------- /twister.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include"twister.h" 5 | 6 | namespace RayTracer { 7 | 8 | unsigned short mtRand_xsubi[3] = { 723, 32761, 44444 }; 9 | 10 | #define M 397 11 | #define MATRIX_A 0x9908b0df 12 | #define UPPER_MASK 0x80000000 13 | #define LOWER_MASK 0x7fffffff 14 | 15 | #define TEMPERING_MASK_B 0x9d2c5680 16 | #define TEMPERING_MASK_C 0xefc60000 17 | #define TEMPERING_SHIFT_U(y) (y >> 11) 18 | #define TEMPERING_SHIFT_S(y) (y << 7) 19 | #define TEMPERING_SHIFT_T(y) (y << 15) 20 | #define TEMPERING_SHIFT_L(y) (y >> 18) 21 | 22 | void Twister::Seed( unsigned long seed ) 23 | { 24 | mt[0]= seed & 0xffffffff; 25 | for ( mti = 1; mti < mtRand_N; mti++ ) mt[mti] = (69069 * mt[mti - 1]) & 0xffffffff; 26 | unsigned long s = 373737; 27 | for ( mti = 1; mti < mtRand_N; mti++ ) 28 | { 29 | mt[mti] ^= s; 30 | s = s * 5531 + 81547; 31 | s ^= (s >> 9) ^ (s << 19); 32 | } 33 | } 34 | 35 | float Twister::Rand() 36 | { 37 | unsigned long y; 38 | static unsigned long mag01[2] = {0x0, MATRIX_A}; 39 | if (mti >= mtRand_N) 40 | { 41 | int kk; 42 | for (kk=0;kk> 1) ^ mag01[y & 0x1]; 46 | } 47 | for (;kk> 1) ^ mag01[y & 0x1]; 51 | } 52 | y = (mt[mtRand_N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK); 53 | mt[mtRand_N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1]; 54 | mti = 0; 55 | } 56 | y = mt[mti++]; 57 | y ^= TEMPERING_SHIFT_U(y); 58 | y ^= TEMPERING_SHIFT_S(y) & TEMPERING_MASK_B; 59 | y ^= TEMPERING_SHIFT_T(y) & TEMPERING_MASK_C; 60 | y ^= TEMPERING_SHIFT_L(y); 61 | return ( (float)y * 2.3283064370807974e-10f ); 62 | } 63 | 64 | unsigned long Twister::RandL() 65 | { 66 | unsigned long y; 67 | static unsigned long mag01[2] = { 0x0, MATRIX_A }; 68 | if (mti >= mtRand_N) 69 | { 70 | int kk; 71 | for (kk=0;kk> 1) ^ mag01[y & 0x1]; 75 | } 76 | for (;kk> 1) ^ mag01[y & 0x1]; 80 | } 81 | y = (mt[mtRand_N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK); 82 | mt[mtRand_N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1]; 83 | mti = 0; 84 | } 85 | y = mt[mti++]; 86 | y ^= TEMPERING_SHIFT_U(y); 87 | y ^= TEMPERING_SHIFT_S(y) & TEMPERING_MASK_B; 88 | y ^= TEMPERING_SHIFT_T(y) & TEMPERING_MASK_C; 89 | y ^= TEMPERING_SHIFT_L(y); 90 | return y; 91 | } 92 | 93 | }; // namespace RayTracer 94 | -------------------------------------------------------------------------------- /raytracer.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | created: 2010/04/13 3 | file name: raytracer.h 4 | author: maxint lnychina@gmail.com 5 | *********************************************************************/ 6 | 7 | #ifndef _RT_RAYTRACER_H_ 8 | #define _RT_RAYTRACER_H_ 9 | 10 | #include "common.h" 11 | #include 12 | 13 | class QImage; 14 | 15 | #define RT_SAMPLES 128 16 | #define RT_REGULAR_SAMPLES 8 17 | 18 | namespace trimeshVec{ 19 | class CAccessObj; 20 | } 21 | 22 | namespace RayTracer { 23 | 24 | // ------------------------------------------------------------------------------ 25 | // Ray class definition 26 | // ------------------------------------------------------------------------------ 27 | 28 | class Ray 29 | { 30 | public: 31 | Ray() 32 | : mOrigin(0,0,0) 33 | , mDirection(0,0,0) 34 | , mID(0) 35 | {} 36 | 37 | Ray(const Vec3& aOrig, const Vec3& aDir, int aID) 38 | : mOrigin(aOrig) 39 | , mDirection(aDir) 40 | , mID(aID) 41 | {} 42 | 43 | void setOrigin(const Vec3& aOrig) 44 | { 45 | mOrigin = aOrig; 46 | } 47 | void setDirection(const Vec3& aDir) 48 | { 49 | mDirection = aDir; 50 | } 51 | const Vec3& getOrigin() const 52 | { 53 | return mOrigin; 54 | } 55 | const Vec3& getDir() const 56 | { 57 | return mDirection; 58 | } 59 | int getID() const { return mID; } 60 | void setID(int val) { mID = val; } 61 | 62 | private: 63 | Vec3 mOrigin; 64 | Vec3 mDirection; 65 | int mID; 66 | }; 67 | 68 | // ------------------------------------------------------------------------------ 69 | // Ray tracer Engine Core 70 | // ------------------------------------------------------------------------------ 71 | class Scene; 72 | class Primitive; 73 | class Twister; 74 | class CCamera; 75 | class Light; 76 | 77 | class Engine 78 | { 79 | public: 80 | Engine(); 81 | ~Engine(); 82 | 83 | /** Set the render target canvas 84 | \param 85 | _w width of the canvas 86 | _h height of the canvas 87 | _img which image it is rendered to? 88 | */ 89 | void setRenderTarget(int _w, int _h, QImage *_img); 90 | Scene* getScene() 91 | { 92 | return mScene; 93 | } 94 | int getNumOfPrimitives() const; 95 | 96 | /** Naive ray tracing 97 | Intersects the ray with every primitives in the scene to determine the 98 | closest intersection. 99 | \param 100 | _ray light ray 101 | _acc final accumulated color 102 | _dist the closest distance 103 | _depth maximum recurse depth 104 | _rIndex refraction index 105 | \return 106 | if no intersection return 0, otherwise the intersected primitive 107 | */ 108 | Primitive* rayTrace(const Ray& _ray, Color& _acc, Real& _dist, 109 | int _depth, Real _rIndex); 110 | 111 | /** Initializes the engine renderer 112 | Reset the line / tile counters and precalculate some values 113 | \param 114 | aPos the camera position 115 | aTarget the looking at position 116 | */ 117 | void initEngine(const Vec3& aPos, const Vec3& aTarget); 118 | 119 | /** Fires rays in the scene one scan line at a time, from left to right 120 | \return 121 | true render completed 122 | false over time, continue to render next time 123 | */ 124 | bool render(); 125 | 126 | /** Get and set tracing depth 127 | */ 128 | int getTraceDepth() const { return mTraceDepth; } 129 | void setTraceDepth(int val) { mTraceDepth = std::min(val, RT_TRACEDEPTH); } 130 | 131 | /** Get current progress 132 | */ 133 | int getCurrProgree() const { return static_cast(mCurrLine * 100.0f / mHeight); } 134 | 135 | /** Find the nearest intersection in a regular grid for a ray 136 | \param 137 | aRay light ray 138 | aDist the nearest distance 139 | aPrim the nearest primitive 140 | */ 141 | RTResult findNearest(const Ray& aRay, Real& aDist, Primitive*& aPrim); 142 | 143 | /** Helper function, fire one ray in the regular grid 144 | \param 145 | aScreenPos position of the screen to trace from 146 | aAccClr final color return 147 | \return 148 | the nearest primitive intersected 149 | */ 150 | Primitive* renderRay(Real x, Real y, Color& aAccClr); 151 | 152 | /** Determine the light intensity received from a point light (in case of 153 | a SHPERE primitive) or an area light (in case of an AABB primitive) 154 | \param 155 | aLight the light 156 | aIP the intersected position 157 | aDir return light direction 158 | \return 159 | shade parameter, 0~1. 160 | When it's point light, 0 indicates in shadow, 1 indicates in light. 161 | When it's area light, return the proportion of light region. 162 | */ 163 | Real calcShade(const Light* aLight, const Vec3& aIP, Vec3& aDir); 164 | 165 | /** Get and set regular sample size of light 166 | */ 167 | int getRegularSampleSize() const { return mRegularSampleSize; } 168 | void setRegularSampleSize(int val) { mRegularSampleSize = std::min(std::max(1, val), RT_REGULAR_SAMPLES); } 169 | 170 | /** Load obj model file 171 | */ 172 | void loadObjModel(const trimeshVec::CAccessObj* accessObj); 173 | 174 | private: 175 | /** Set the color of frame buffer 176 | */ 177 | void _setFrameBuffer(int _y, int _x, const Color& _clr); 178 | 179 | private: 180 | typedef std::vector PrimitiveList; 181 | 182 | private: 183 | bool mCreated; 184 | Scene* mScene; 185 | int mWidth, mHeight; 186 | Real mRatio; 187 | QImage* mImage; 188 | int mCurrLine; 189 | Real mDx, mDy, mSx, mSy; 190 | Vec3 mRCS; // 1 / size of a cell 191 | Vec3 mCS; // size of a cell 192 | int mCurRayID; 193 | 194 | /// last line primitives 195 | PrimitiveList mLastLinePrims; 196 | 197 | /// benchmark related 198 | int mTraceDepth; 199 | int mRegularSampleSize; 200 | Real mSampleScale; 201 | Real mSampleOffset; 202 | Real mSampleScale2; 203 | 204 | /// random number generator 205 | Twister* mTwister; 206 | CCamera* mCamera; 207 | }; 208 | 209 | }; // namespace RayTracer 210 | 211 | #endif // _RT_RAYTRACER_H_ -------------------------------------------------------------------------------- /Point3D.cpp: -------------------------------------------------------------------------------- 1 | #include "Point3D.h" 2 | #include 3 | #include 4 | 5 | #define Pi 3.14159 6 | 7 | using namespace std; 8 | 9 | namespace trimeshVec 10 | { 11 | //------------------------------------------------------------------------------ 12 | // constructor 13 | //------------------------------------------------------------------------------ 14 | CPoint3D::CPoint3D( ) // normal constructor 15 | { 16 | x = 0; y = 0; z = 0; 17 | } 18 | 19 | CPoint3D::CPoint3D( const CPoint3D &v ) 20 | { 21 | x = v.x; 22 | y = v.y; 23 | z = v.z; 24 | } 25 | 26 | CPoint3D::CPoint3D( int xx, int yy, int zz ) // normal constructor 27 | { 28 | x = (float)xx; y = (float)yy; z = (float)zz; 29 | } 30 | 31 | CPoint3D::CPoint3D( float xx, float yy, float zz ) // normal constructor 32 | { 33 | x = xx; y = yy; z = zz; 34 | } 35 | 36 | CPoint3D::CPoint3D( double xx, double yy, double zz ) // normal constructor 37 | { 38 | x = (float)xx; y = (float)yy; z = (float)zz; 39 | } 40 | 41 | //------------------------------------------------------------------------------ 42 | // destructor 43 | //------------------------------------------------------------------------------ 44 | CPoint3D::~CPoint3D(void) 45 | { 46 | } 47 | 48 | //------------------------------------------------------------------------------ 49 | // Plus 50 | //------------------------------------------------------------------------------ 51 | const CPoint3D& CPoint3D::operator +=(const CPoint3D &v) 52 | { 53 | x += v.x; 54 | y += v.y; 55 | z += v.z; 56 | return *this; 57 | } 58 | 59 | //------------------------------------------------------------------------------ 60 | // Subtraction 61 | //------------------------------------------------------------------------------ 62 | const CPoint3D& CPoint3D::operator -=(const CPoint3D &v) 63 | { 64 | x -= v.x; 65 | y -= v.y; 66 | z -= v.z; 67 | return *this; 68 | } 69 | 70 | //------------------------------------------------------------------------------ 71 | // CVectors plus 72 | //------------------------------------------------------------------------------ 73 | CPoint3D CPoint3D::operator + ( const CPoint3D& v ) 74 | { 75 | CPoint3D r(*this); 76 | r += v; 77 | return ( r ); 78 | } 79 | 80 | //------------------------------------------------------------------------------ 81 | // CVectors subtraction 82 | //------------------------------------------------------------------------------ 83 | CPoint3D CPoint3D::operator - ( const CPoint3D& v ) 84 | { 85 | CPoint3D r(*this); 86 | r -= v; 87 | return ( r ); 88 | } 89 | 90 | //------------------------------------------------------------------------------ 91 | // CVectors cross product 92 | //------------------------------------------------------------------------------ 93 | CPoint3D CPoint3D::operator * ( const CPoint3D& v ) 94 | { 95 | CPoint3D r; 96 | 97 | r.x = y*v.z - v.y*z; 98 | r.y = v.x*z - x*v.z; 99 | r.z = x*v.y - v.x*y; 100 | 101 | return( r ); 102 | } 103 | 104 | //------------------------------------------------------------------------------ 105 | // CPoint3D scale 106 | //------------------------------------------------------------------------------ 107 | CPoint3D CPoint3D::operator * ( int k ) // scaled by int 108 | { 109 | CPoint3D r; 110 | 111 | r.x = x * (float)k; 112 | r.y = y * (float)k; 113 | r.z = z * (float)k; 114 | 115 | return( r ); 116 | } 117 | CPoint3D CPoint3D::operator * ( float k ) // scaled by float 118 | { 119 | CPoint3D r; 120 | 121 | r.x = x * k; 122 | r.y = y * k; 123 | r.z = z * k; 124 | 125 | return( r ); 126 | } 127 | CPoint3D CPoint3D::operator * ( double k ) // scaled by double 128 | { 129 | CPoint3D r; 130 | 131 | r.x = x * (float)k; 132 | r.y = y * (float)k; 133 | r.z = z * (float)k; 134 | 135 | return( r ); 136 | } 137 | 138 | //------------------------------------------------------------------------------ 139 | // CVectors dot product 140 | //------------------------------------------------------------------------------ 141 | float CPoint3D::operator & ( const CPoint3D& v ) 142 | { 143 | return ( x * v.x + y * v.y + z * v.z ); 144 | } 145 | 146 | //------------------------------------------------------------------------------ 147 | // CPoint3D length 148 | //------------------------------------------------------------------------------ 149 | float CPoint3D::length() 150 | { 151 | return( (float)sqrt( x*x + y*y + z*z ) ); 152 | } 153 | 154 | //------------------------------------------------------------------------------ 155 | // CPoint3D unify 156 | //------------------------------------------------------------------------------ 157 | void CPoint3D::unify() 158 | { 159 | float len = length(); 160 | 161 | if( len < 1.0e-20 ) 162 | { 163 | x = float(rand()%1000+20); 164 | y = float(rand()%1000+20); 165 | z = float(rand()%1000+20); 166 | len=length(); 167 | x/=len; 168 | y/=len; 169 | z/=len; 170 | } 171 | 172 | 173 | else 174 | { 175 | len = 1.0f/len; 176 | x *= len; 177 | y *= len; 178 | z *= len; 179 | } 180 | } 181 | 182 | void CPoint3D::RangeUnify(float min,float max) 183 | { 184 | if (x>max) x=max; 185 | if (y>max) y=max; 186 | if (z>max) z=max; 187 | 188 | if (x 12 | 13 | #pragma warning(disable:4800) // int to bool 14 | 15 | namespace RayTracer { 16 | 17 | // ------------------------------------------------------------------------------ 18 | // Custom Texture class 19 | // ------------------------------------------------------------------------------ 20 | 21 | class Texture 22 | { 23 | public: 24 | Texture(const String& aFilename); 25 | Texture(Color* data, int w, int h); 26 | ~Texture() { SAFE_DELETE_ARRAY(mBitmap); } 27 | Color* getBitmap() { return mBitmap; } 28 | Color getTexel(Real u, Real v) const; 29 | int getWidth() const { return mWidth; } 30 | int getHeight() const { return mHeight; } 31 | private: 32 | Color* mBitmap; 33 | int mWidth, mHeight; 34 | }; 35 | 36 | // ------------------------------------------------------------------------------ 37 | // Simple Texture Manager 38 | // ------------------------------------------------------------------------------ 39 | 40 | class TextureManager 41 | { 42 | public: 43 | static TextureManager& getInstance(); 44 | Texture* createFromFile(const String& filename, const String& texName = ""); 45 | Texture* createFromData(Color* data, int w, int h, const String& texName = ""); 46 | Texture* getTexture(const String& texName); 47 | private: 48 | TextureManager(); 49 | TextureManager(const TextureManager&); 50 | TextureManager& operator=(const TextureManager&); 51 | ~TextureManager(); 52 | private: 53 | typedef std::map TextureList; 54 | typedef TextureList::iterator TexListItor; 55 | typedef std::pair TexListPair; 56 | private: 57 | TextureList mTexturePool; 58 | int mIDCounter; 59 | }; 60 | 61 | // ------------------------------------------------------------------------------ 62 | // Material class definition 63 | // ------------------------------------------------------------------------------ 64 | 65 | class Material 66 | { 67 | public: 68 | enum StateToggler 69 | { 70 | AMBIENT = 1, 71 | DIFFUSE = 1<<1, 72 | SPECULAR = 1<<2, 73 | EMISSION = 1<<3, 74 | REFLECTION = 1<<4, 75 | REFRACTION = 1<<5, 76 | DIFFUSEREFL = 1<<6 77 | }; 78 | Material() 79 | : mAmbient(0.2f,0.2f,0.2f) 80 | , mDiffuse(0.8f,0.8f,0.8f) 81 | , mSpecular(0,0,0) 82 | , mEmission(0,0,0) 83 | , mShininess(0) 84 | , mReflection(0) 85 | , mDiffuseRefl(0) 86 | , mRefraction(0) 87 | , mRefrIndex(1.5f) 88 | , mTexture(0) 89 | , mUScale(1) 90 | , mVScale(1) 91 | , mState(AMBIENT | DIFFUSE) 92 | {} 93 | ~Material() {} 94 | 95 | // ambient 96 | void setAmbient(Real _r, Real _g, Real _b) 97 | { 98 | mAmbient.r = _r; 99 | mAmbient.g = _g; 100 | mAmbient.b = _b; 101 | _setColorState(mAmbient, AMBIENT); 102 | } 103 | const Color& getAmbient() const { return mAmbient; } 104 | bool isAmbient() const { return (mState & AMBIENT); } 105 | void setAmbient(const Color& val) { setAmbient(val.r, val.g, val.b); } 106 | 107 | // diffuse 108 | void setDiffuse(Real _r, Real _g, Real _b) 109 | { 110 | mDiffuse.r = _r; 111 | mDiffuse.g = _g; 112 | mDiffuse.b = _b; 113 | _setColorState(mDiffuse, DIFFUSE); 114 | } 115 | const Color& getDiffuse() const { return mDiffuse; } 116 | bool isDiffuse() const { return (mState & DIFFUSE); } 117 | void setDiffuse(const Color& val) { setDiffuse(val.r, val.g, val.b); } 118 | 119 | // specular 120 | void setSpecular(Real _r, Real _g, Real _b) 121 | { 122 | mSpecular.r = _r; 123 | mSpecular.g = _g; 124 | mSpecular.b = _b; 125 | _setColorState(mSpecular, SPECULAR); 126 | } 127 | const Color& getSpecular() const { return mSpecular; } 128 | bool isSpecular() const { return (mState & SPECULAR); } 129 | void setSpecular(const Color& val) { setSpecular(val.r, val.b, val.g); } 130 | 131 | // shininess 132 | Real getShininess() const { return mShininess; } 133 | void setShininess(Real val) { mShininess = val; } 134 | 135 | // emission 136 | void setEmission(Real _r, Real _g, Real _b) 137 | { 138 | mEmission.r = _r; 139 | mEmission.g = _g; 140 | mEmission.b = _b; 141 | _setColorState(mEmission, EMISSION); 142 | } 143 | const Color& getEmission() const { return mEmission; } 144 | bool isEmission() const { return (mState & EMISSION); } 145 | void setEmission(const Color& val) { setEmission(val.r, val.g, val.b); } 146 | 147 | // reflection 148 | Real getReflection() const { return mReflection; } 149 | bool isReflection() const { return (mState & REFLECTION); } 150 | void setReflection(Real val) { mReflection = val; _setValueState(val, REFLECTION); } 151 | 152 | Real getDiffuseRefl() const { return mDiffuseRefl; } 153 | bool isDiffuseRefl() const { return (mState & DIFFUSEREFL); } 154 | void setDiffuseRefl(Real val) { mDiffuseRefl = val; _setValueState(val, DIFFUSEREFL); } 155 | 156 | // refraction 157 | Real getRefraction() const { return mRefraction; } 158 | bool isRefraction() const { return (mState & REFRACTION); } 159 | void setRefraction(Real val) { mRefraction = val; _setValueState(val, REFRACTION); } 160 | 161 | Real getRefrIndex() const { return mRefrIndex; } 162 | void setRefrIndex(Real val) { mRefrIndex = val; } 163 | 164 | // texture 165 | Texture* getTexture() const { return mTexture; } 166 | bool isTexture() const { return mTexture!=NULL; } 167 | void setTexture(Texture* val) { mTexture = val; } 168 | void setTexture(const String& texName); 169 | 170 | void setUVScale(Real uScale, Real vScale) 171 | { 172 | mUScale = uScale; 173 | mVScale = vScale; 174 | } 175 | Real getUScale() const { return mUScale; } 176 | Real getVScale() const { return mVScale; } 177 | private: 178 | void _setColorState(const Color& clr, StateToggler tog) 179 | { 180 | if (clr.Length() > RT_EPSILON) 181 | mState |= tog; 182 | else 183 | mState &= ~tog; 184 | } 185 | void _setValueState(Real val, StateToggler tog) 186 | { 187 | if (abs(val) > RT_EPSILON) 188 | mState |= tog; 189 | else 190 | mState &= ~tog; 191 | } 192 | private: 193 | Color mAmbient, mDiffuse, mSpecular, mEmission; 194 | Real mShininess; 195 | Real mReflection, mDiffuseRefl; 196 | Real mRefraction, mRefrIndex; 197 | Texture* mTexture; 198 | Real mUScale, mVScale; 199 | int mState; 200 | }; 201 | 202 | // ------------------------------------------------------------------------------ 203 | // Custom Material Manager 204 | // ------------------------------------------------------------------------------ 205 | 206 | class MaterialManager 207 | { 208 | public: 209 | static MaterialManager& getInstance(); 210 | Material* createManual(const String& matName = ""); 211 | Material* getMaterial(const String& matName); 212 | private: 213 | MaterialManager(); 214 | MaterialManager(const MaterialManager&); 215 | MaterialManager& operator=(const MaterialManager&); 216 | ~MaterialManager(); 217 | private: 218 | typedef std::map MaterialMap; 219 | typedef MaterialMap::iterator MatMapItor; 220 | private: 221 | MaterialMap mMaterialPool; 222 | int mIDCounter; 223 | }; 224 | 225 | }; // namespace RayTracer 226 | 227 | #endif // _RT_MATERIAL_H_ -------------------------------------------------------------------------------- /material.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | created: 2010/04/22 3 | file name: material.cpp 4 | author: maxint lnychina@gmail.com 5 | *********************************************************************/ 6 | 7 | #include "material.h" 8 | 9 | #include 10 | #include 11 | 12 | namespace RayTracer { 13 | 14 | using std::min; 15 | using std::max; 16 | using std::abs; 17 | 18 | static const String _DEFAULT_NAME = "_default_"; 19 | 20 | // ------------------------------------------------------------------------------ 21 | // Texture class implementation 22 | // ------------------------------------------------------------------------------ 23 | 24 | Texture::Texture(const String& aFilename) 25 | : mBitmap(0) 26 | , mWidth(0) 27 | , mHeight(0) 28 | { 29 | QImage img(aFilename.c_str()); 30 | if (img.isNull()) 31 | { 32 | return; 33 | } 34 | mWidth = img.width(); 35 | mHeight = img.height(); 36 | mBitmap = new Color[mWidth * mHeight]; 37 | Real reci = 1.0f / 256; 38 | for (int x=0; x(fu) % mWidth; 62 | int u2 = static_cast(fu +1) % mWidth; 63 | int v1 = static_cast(fv) % mHeight; 64 | int v2 = static_cast(fv +1) % mHeight; 65 | 66 | // calculate fractional parts of u and v 67 | Real fracu = fu - floorf(fu); 68 | Real fracv = fv - floorf(fv); 69 | 70 | // calculate weight factors 71 | Real w1 = (1 - fracu) * (1 - fracv); 72 | Real w2 = fracu * (1 - fracv); 73 | Real w3 = (1 - fracu) * fracv; 74 | Real w4 = fracu * fracv; 75 | 76 | // fetch four texels 77 | Color c1 = mBitmap[u1 + v1 * mWidth]; 78 | Color c2 = mBitmap[u2 + v1 * mWidth]; 79 | Color c3 = mBitmap[u1 + v2 * mWidth]; 80 | Color c4 = mBitmap[u2 + v2 * mWidth]; 81 | 82 | // scale and sum the four colors 83 | return c1 * w1 + c2 * w2 + c3 * w3 + c4 * w4; 84 | } 85 | 86 | // ------------------------------------------------------------------------------ 87 | // Texture Manager class implementation 88 | // ------------------------------------------------------------------------------ 89 | 90 | TextureManager::TextureManager() 91 | : mIDCounter(0) 92 | {} 93 | 94 | TextureManager::~TextureManager() 95 | { 96 | TexListItor it = mTexturePool.begin(); 97 | TexListItor it_end = mTexturePool.end(); 98 | for (; it!=it_end; ++it) 99 | { 100 | SAFE_DELETE(it->second); 101 | } 102 | mTexturePool.clear(); 103 | } 104 | 105 | TextureManager& TextureManager::getInstance() 106 | { 107 | static TextureManager mInstance; 108 | return mInstance; 109 | } 110 | 111 | Texture* TextureManager::createFromFile(const String& filename, const String& texName /* = */ ) 112 | { 113 | if (texName.length() == 0) 114 | { 115 | std::ostringstream oss; 116 | oss << "_Tex" << ++mIDCounter; 117 | Texture *tex = new Texture(filename); 118 | std::cout << "INFO: Texture " << oss.str() << " is created!" << std::endl; 119 | mTexturePool.insert(TexListPair(oss.str(), tex)); 120 | return tex; 121 | } 122 | 123 | TexListItor it = mTexturePool.find(texName); 124 | if (it != mTexturePool.end()) 125 | { // found 126 | std::cout << "WARNING: Texture " << texName << " has existed!" << std::endl; 127 | return it->second; 128 | } 129 | else 130 | { 131 | Texture *tex = new Texture(filename); 132 | mTexturePool.insert(TexListPair(texName, tex)); 133 | return tex; 134 | } 135 | } 136 | 137 | Texture* TextureManager::createFromData(Color* data, int w, int h, const String& texName /* = */ ) 138 | { 139 | if (texName.length() == 0) 140 | { 141 | std::ostringstream oss; 142 | oss << "_Tex" << ++mIDCounter; 143 | Texture *tex = new Texture(data, w, h); 144 | std::cout << "INFO: Texture " << oss.str() << " is created!" << std::endl; 145 | mTexturePool.insert(TexListPair(oss.str(), tex)); 146 | return tex; 147 | } 148 | 149 | TexListItor it = mTexturePool.find(texName); 150 | if (it != mTexturePool.end()) 151 | { // found 152 | std::cout << "WARNING: Texture " << texName << " has existed!" << std::endl; 153 | return it->second; 154 | } 155 | else 156 | { 157 | Texture *tex = new Texture(data, w, h); 158 | mTexturePool.insert(TexListPair(texName, tex)); 159 | return tex; 160 | } 161 | } 162 | 163 | Texture* TextureManager::getTexture(const String& texName) 164 | { 165 | TexListItor it = mTexturePool.find(texName); 166 | if (it != mTexturePool.end()) 167 | { // found 168 | return it->second; 169 | } 170 | else 171 | { 172 | std::cout << "WARNING: Texture " << texName << " did not existed!" << std::endl; 173 | return NULL; 174 | } 175 | } 176 | 177 | // ------------------------------------------------------------------------------ 178 | // Material class implementation 179 | // ------------------------------------------------------------------------------ 180 | 181 | void Material::setTexture(const String& texName) 182 | { 183 | mTexture = TextureManager::getInstance().getTexture(texName); 184 | } 185 | 186 | // ------------------------------------------------------------------------------ 187 | // Material Manager class implementation 188 | // ------------------------------------------------------------------------------ 189 | 190 | MaterialManager::MaterialManager() 191 | : mIDCounter(0) 192 | { 193 | Material *mat = new Material(); 194 | mMaterialPool.insert(std::make_pair(_DEFAULT_NAME, mat)); 195 | } 196 | 197 | MaterialManager::~MaterialManager() 198 | { 199 | MatMapItor it = mMaterialPool.begin(); 200 | MatMapItor it_end = mMaterialPool.end(); 201 | for (; it!=it_end; ++it) 202 | { 203 | SAFE_DELETE(it->second); 204 | } 205 | mMaterialPool.clear(); 206 | } 207 | 208 | MaterialManager& MaterialManager::getInstance() 209 | { 210 | static MaterialManager mInstance; 211 | return mInstance; 212 | } 213 | 214 | Material* MaterialManager::createManual(const String& matName /* = "" */) 215 | { 216 | if (matName.length() == 0) 217 | { 218 | std::ostringstream oss; 219 | oss << "_Mat" << ++mIDCounter; 220 | Material* mat = new Material(); 221 | std::cout << "INFO: Material " << oss.str() << " is created!" << std::endl; 222 | 223 | mMaterialPool.insert(std::make_pair(oss.str(), mat)); 224 | return mat; 225 | } 226 | 227 | MatMapItor it = mMaterialPool.find(matName); 228 | if (it!=mMaterialPool.end()) 229 | {// found 230 | std::cout << "WARNING: Material " << matName << " has existed!" << std::endl; 231 | return it->second; 232 | } 233 | else 234 | { 235 | Material* mat = new Material(); 236 | mMaterialPool.insert(std::make_pair(matName, mat)); 237 | return mat; 238 | } 239 | } 240 | 241 | Material* MaterialManager::getMaterial(const String& matName) 242 | { 243 | MatMapItor it = mMaterialPool.find(matName); 244 | if (it!=mMaterialPool.end()) 245 | { 246 | return it->second; 247 | } 248 | else 249 | { 250 | std::cout << "WARNING: Material " << matName << " did not existed!" << std::endl; 251 | return mMaterialPool[_DEFAULT_NAME]; 252 | } 253 | } 254 | 255 | }; // namespace RayTracer -------------------------------------------------------------------------------- /AccessObj.h: -------------------------------------------------------------------------------- 1 | // AccessObj.h: interface for the CAccessObj class. 2 | // 3 | ////////////////////////////////////////////////////////////////////// 4 | 5 | #ifndef _MY_ACCESS_OBJ_ 6 | #define _MY_ACCESS_OBJ_ 7 | 8 | #include "Point3D.h" 9 | #include 10 | 11 | #ifndef SAFE_DELETE 12 | #define SAFE_DELETE(p) if(p) { delete (p); (p)=0; } 13 | #endif 14 | 15 | #ifndef SAFE_DELETE_ARRAY 16 | #define SAFE_DELETE_ARRAY(p) if(p) { delete[] (p); (p)=0; } 17 | #endif 18 | 19 | 20 | #define OBJ_NONE (0) // render with only vertices 21 | #define OBJ_FLAT (1 << 0) // render with facet normals 22 | #define OBJ_SMOOTH (1 << 1) // render with vertex normals 23 | #define OBJ_TEXTURE (1 << 2) // render with texture coords 24 | #define OBJ_COLOR (1 << 3) // render with colors 25 | #define OBJ_MATERIAL (1 << 4) // render with materials 26 | 27 | namespace trimeshVec 28 | { 29 | // -------------------------------------------------------------------- 30 | // COBJmaterial: defines a material in a model. 31 | class COBJmaterial 32 | { 33 | public: 34 | char name[256]; // name of material 35 | float diffuse[4]; // diffuse component 36 | float ambient[4]; // ambient component 37 | float specular[4]; // specular component 38 | float emissive[4]; // emissive component 39 | float shininess[1]; // specular exponent 40 | 41 | // Texture Data 42 | bool bTextured; 43 | char sTexture[256]; // name of texture file 44 | 45 | char* MaterialName() { return (name);} 46 | 47 | float* DiffuseComponent() {return (diffuse);} 48 | float* AmbientComponent() {return (ambient);} 49 | float* SpecularComponent() {return (specular);} 50 | float* EmissiveComponent() {return (emissive);} 51 | float* ShininessComponent() {return (shininess);} 52 | 53 | COBJmaterial() 54 | { 55 | sprintf_s (name, "default"); 56 | sTexture[0] = '\0'; 57 | diffuse[0] = diffuse[1] = diffuse[2] = diffuse[3] = 1.0f; 58 | ambient[0] = ambient[1] = ambient[2] = ambient[3] = 0.1f; 59 | specular[0] = specular[1] = specular[2] = specular[3] = 0.0f; 60 | emissive[0] = emissive[1] = emissive[2] = emissive[3] = 0.0f; 61 | shininess[0] = 10; 62 | 63 | bTextured = false; 64 | } 65 | }; 66 | 67 | // -------------------------------------------------------------------- 68 | // COBJtriangle: defines a triangle in a model. 69 | class COBJtriangle 70 | { 71 | public: 72 | unsigned int vindices[3]; // array of triangle vertex indices 73 | unsigned int nindices[3]; // array of triangle normal indices 74 | unsigned int tindices[3]; // array of triangle texcoord indices 75 | unsigned int findex; // idxInTri of triangle facet normal 76 | unsigned int mindex; // idxInTri of material 77 | COBJtriangle() 78 | { 79 | vindices[0] = vindices[1] = vindices[2] = 80 | nindices[0] = nindices[1] = nindices[2] = 81 | tindices[0] = tindices[1] = tindices[2] = 0; 82 | } 83 | ~COBJtriangle() {} 84 | };// ------------------------------------------------------------------ 85 | 86 | // -------------------------------------------------------------------- 87 | // COBJgroup: defines a group in a model. 88 | class COBJgroup 89 | { 90 | public: 91 | char name[256]; // name of this group 92 | unsigned int nTriangles; // number of triangles in this group 93 | unsigned int* pTriangles; // array of triangle indices 94 | unsigned int material; // idxInTri to material for group 95 | char texturename[256]; 96 | bool m_bTexture; 97 | int m_iTextureType; 98 | float m_fTran_X; 99 | float m_fTran_Y; 100 | float m_fTran_Z; 101 | float m_fScale_X; 102 | float m_fScale_Y; 103 | float m_fScale_Z; 104 | float m_fRotate_X; 105 | float m_fRotate_Y; 106 | float m_fRotate_Z; 107 | class COBJgroup* next; // pointer to next group in model 108 | 109 | COBJgroup() 110 | { 111 | nTriangles = 0; 112 | pTriangles = NULL; 113 | next = NULL; 114 | } 115 | 116 | ~COBJgroup() 117 | { 118 | SAFE_DELETE_ARRAY(pTriangles) 119 | nTriangles = 0; 120 | } 121 | };// ------------------------------------------------------------------ 122 | 123 | // -------------------------------------------------------------------- 124 | // COBJmodel: defines a model. 125 | class COBJmodel 126 | { 127 | public: 128 | char pathname[256]; // path to this model 129 | char mtllibname[256]; // name of the material library 130 | unsigned int nVertices; // number of vertices in model 131 | CPoint3D* vpVertices; // array of vertices 132 | unsigned int nNormals; // number of normals in model 133 | CPoint3D* vpNormals; // array of normals 134 | unsigned int nTexCoords; // number of texcoords in model 135 | CPoint3D* vpTexCoords; // array of texture coordinates 136 | unsigned int nFacetnorms; // number of facet normals in model 137 | CPoint3D* vpFacetNorms; // array of facet normals 138 | unsigned int nTriangles; // number of triangles in model 139 | COBJtriangle* pTriangles; // array of triangles 140 | unsigned int nMaterials; // number of materials in model 141 | COBJmaterial* pMaterials; // array of materials 142 | unsigned int nGroups; // number of groups in model 143 | COBJgroup* pGroups; // linked list of groups 144 | CPoint3D position; // position of the model 145 | 146 | // construction 147 | COBJmodel() 148 | { 149 | nVertices = 0; 150 | vpVertices = NULL; 151 | nNormals = 0; 152 | vpNormals = NULL; 153 | nTexCoords = 0; 154 | vpTexCoords = NULL; 155 | nFacetnorms = 0; 156 | vpFacetNorms= NULL; 157 | nTriangles = 0; 158 | pTriangles = NULL; 159 | nMaterials = 0; 160 | pMaterials = NULL; 161 | nGroups = 0; 162 | pGroups = NULL; 163 | position = CPoint3D(0, 0, 0); 164 | } 165 | 166 | // free all memory 167 | void Destory() 168 | { 169 | COBJgroup *group; 170 | 171 | SAFE_DELETE_ARRAY(vpVertices) 172 | SAFE_DELETE_ARRAY(vpNormals) 173 | SAFE_DELETE_ARRAY(vpTexCoords) 174 | SAFE_DELETE_ARRAY(vpFacetNorms) 175 | SAFE_DELETE_ARRAY(pTriangles) 176 | SAFE_DELETE_ARRAY(pMaterials) 177 | 178 | while(pGroups) 179 | { 180 | group = pGroups; 181 | pGroups = pGroups->next; 182 | SAFE_DELETE(group); 183 | } 184 | 185 | nVertices = 0; 186 | nNormals = 0; 187 | nTexCoords = 0; 188 | nFacetnorms = 0; 189 | nTriangles = 0; 190 | nMaterials = 0; 191 | nGroups = 0; 192 | position = CPoint3D(0, 0, 0); 193 | } 194 | 195 | // destruction 196 | ~COBJmodel() 197 | { 198 | Destory(); 199 | } 200 | };// ------------------------------------------------------------------ 201 | 202 | 203 | /////////////////////////////////////////////////////////////////////////////// 204 | // Definition of the OBJ R/W class 205 | /////////////////////////////////////////////////////////////////////////////// 206 | class CAccessObj 207 | { 208 | public: 209 | /// A temporal class 210 | class OBJnode 211 | { 212 | public: 213 | unsigned int triIdx; 214 | unsigned int idxInTri; // idxInTri in triangle 215 | OBJnode() :triIdx(0), idxInTri(0) {} 216 | }; 217 | 218 | public: 219 | CAccessObj(); 220 | ~CAccessObj(); 221 | 222 | COBJmodel *m_pModel; 223 | CPoint3D m_vMax, m_vMin; 224 | 225 | protected: 226 | 227 | void CalcBoundingBox(); 228 | bool Equal(CPoint3D * u, CPoint3D * v, float epsilon); 229 | 230 | COBJgroup* FindGroup(char* name); 231 | COBJgroup* AddGroup(char* name); 232 | 233 | char* DirName(char* path); 234 | unsigned int FindMaterial(char* name); 235 | void ReadMTL(char* name); 236 | void WriteMTL(char* modelpath, char* mtllibname); 237 | 238 | bool FirstPass(FILE* file); 239 | void SecondPass(FILE* file); 240 | 241 | void Dimensions(float* dimensions); 242 | void Scale(float scale); 243 | void ReverseWinding(); 244 | void FacetNormals(); 245 | void VertexNormals(float angle); 246 | 247 | void LinearTexture(); 248 | void SpheremapTexture(); 249 | void WriteOBJ(char* filename, unsigned int mode); 250 | 251 | public: 252 | void Destory(); 253 | void Boundingbox(CPoint3D &vMax, CPoint3D &vMin); 254 | bool LoadOBJ(const char* filename); 255 | void UnifiedModel(); 256 | }; 257 | 258 | } //namespace trimeshVec 259 | 260 | #endif -------------------------------------------------------------------------------- /MathDefs.h: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------- 2 | // common.h 3 | // 2004 - Jacco Bikker - jacco@bik5.com - www.bik5.com - <>< 4 | // ----------------------------------------------------------- 5 | 6 | #ifndef _RT_MATH_DEFS_H_ 7 | #define _RT_MATH_DEFS_H_ 8 | 9 | #include 10 | #include 11 | 12 | namespace RayTracer { 13 | 14 | #ifndef RT_PI 15 | #define RT_PI 3.141592653589793239462f 16 | #endif 17 | 18 | #ifdef HIGH_PRECISION 19 | #define RT_EPSILON 1E-6 20 | #else 21 | #define RT_EPSILON 0.0001f 22 | #endif 23 | 24 | // ------------------------------------------------------------------------------ 25 | // Basic vector class 26 | // ------------------------------------------------------------------------------ 27 | 28 | template 29 | class Vector3 30 | { 31 | public: 32 | Vector3() : x( 0.0f ), y( 0.0f ), z( 0.0f ) {}; 33 | Vector3( _Tp a_X, _Tp a_Y, _Tp a_Z ) : x( a_X ), y( a_Y ), z( a_Z ) {}; 34 | void Set( _Tp a_X, _Tp a_Y, _Tp a_Z ) { x = a_X; y = a_Y; z = a_Z; } 35 | void Normalize() { _Tp l = 1.0f / Length(); x *= l; y *= l; z *= l; } 36 | _Tp Length() const { return (_Tp)sqrt( x * x + y * y + z * z ); } 37 | _Tp SqrLength() const { return x * x + y * y + z * z; } 38 | _Tp Dot(const Vector3& a_V ) const { return x * a_V.x + y * a_V.y + z * a_V.z; } 39 | Vector3 Cross(const Vector3& b ) const 40 | { return Vector3( y * b.z - z * b.y, z * b.x - x * b.z, x * b.y - y * b.x ); } 41 | 42 | // Accessor 43 | const _Tp operator[] (int i) const { return cell[i]; } 44 | _Tp& operator[] (int i) { return cell[i]; } 45 | 46 | void operator += ( const Vector3& a_V ) { x += a_V.x; y += a_V.y; z += a_V.z; } 47 | void operator += ( _Tp f ) { for (int i=0; i<3; ++i) cell[i] += f; } 48 | void operator -= ( const Vector3& a_V ) { x -= a_V.x; y -= a_V.y; z -= a_V.z; } 49 | void operator -= ( _Tp f ) { for (int i=0; i<3; ++i) cell[i] -= f; } 50 | void operator *= ( const Vector3& a_V ) { x *= a_V.x; y *= a_V.y; z *= a_V.z; } 51 | void operator *= ( _Tp f ) { for (int i=0; i<3; ++i) cell[i] *= f; } 52 | void operator /= ( const Vector3& a_V ) { x /= a_V.x; y /= a_V.y; z /= a_V.z; } 53 | void operator /= ( _Tp f ) { for (int i=0; i<3; ++i) cell[i] /= f; } 54 | Vector3 operator- () const { return Vector3( -x, -y, -z ); } 55 | 56 | /// Operations between vectors 57 | friend Vector3 operator + ( const Vector3& v1, const Vector3& v2 ) 58 | { return Vector3( v1.x + v2.x, v1.y + v2.y, v1.z + v2.z ); } 59 | friend Vector3 operator - ( const Vector3& v1, const Vector3& v2 ) 60 | { return Vector3( v1.x - v2.x, v1.y - v2.y, v1.z - v2.z ); } 61 | friend Vector3 operator * ( const Vector3& v1, const Vector3& v2 ) 62 | { return Vector3( v1.x * v2.x, v1.y * v2.y, v1.z * v2.z ); } 63 | friend Vector3 operator / ( const Vector3& v1, const Vector3& v2 ) 64 | { return Vector3( v1.x / v2.x, v1.y / v2.y, v1.z / v2.z ); } 65 | 66 | /// Operations between vector and scale 67 | friend Vector3 operator + ( const Vector3& v, _Tp f) 68 | { return Vector3( v.x + f, v.y + f, v.z + f ); } 69 | friend Vector3 operator + ( _Tp f, const Vector3& v ) 70 | { return Vector3( v.x + f, v.y + f, v.z + f ); } 71 | 72 | friend Vector3 operator - ( const Vector3& v, _Tp f) 73 | { return Vector3( v.x - f, v.y - f, v.z - f ); } 74 | friend Vector3 operator - ( _Tp f, const Vector3& v ) 75 | { return Vector3( f - v.x, f - v.y, f - v.z ); } 76 | 77 | friend Vector3 operator * ( const Vector3& v, _Tp f) 78 | { return Vector3( v.x * f, v.y * f, v.z * f ); } 79 | friend Vector3 operator * ( _Tp f, const Vector3& v ) 80 | { return Vector3( v.x * f, v.y * f, v.z * f ); } 81 | 82 | friend Vector3 operator / ( _Tp f, const Vector3& v ) 83 | { return Vector3( f / v.x, f / v.y, f / v.z ); } 84 | friend Vector3 operator / ( const Vector3& v, _Tp f) 85 | { return Vector3( v.x / f, v.y / f, v.z / f ); } 86 | 87 | /// Other operations 88 | const Vector3& Max(const Vector3& b); 89 | const Vector3& Min(const Vector3& b); 90 | const Vector3& Abs(); 91 | 92 | friend Vector3 Min(const Vector3& a, const Vector3& b) 93 | { return Vector3(a).Min(b); } 94 | friend Vector3 Max(const Vector3& a, const Vector3& b) 95 | { return Vector3(a).Max(b); } 96 | 97 | friend bool operator < (const Vector3& a, const Vector3& b) 98 | { return (a.cell[0] < b.cell[0]) && (a.cell[1] < b.cell[1]) && (a.cell[2] < b.cell[2]); } 99 | friend bool operator > (const Vector3& a, const Vector3& b) 100 | { return b < a; } 101 | bool operator < (_Tp f) const 102 | { return (cell[0] < f && cell[1] < f && cell[2] < f); } 103 | bool operator > (_Tp f) const 104 | { return (cell[0] > f && cell[1] > f && cell[2] > f); } 105 | 106 | union 107 | { 108 | struct { _Tp x, y, z; }; 109 | struct { _Tp r, g, b; }; 110 | struct { _Tp cell[3]; }; 111 | }; 112 | static const Vector3 ZERO; 113 | static const Vector3 ONE; 114 | static const Vector3 UNIT_X; 115 | static const Vector3 UNIT_Y; 116 | static const Vector3 UNIT_Z; 117 | }; 118 | 119 | // ------------------------------------------------------------------------------ 120 | // 4x4 Matrix_ class 121 | // ------------------------------------------------------------------------------ 122 | 123 | template 124 | class Matrix_ 125 | { 126 | public: 127 | _Tp cell[16]; 128 | enum 129 | { 130 | TX=3, 131 | TY=7, 132 | TZ=11, 133 | D0=0, D1=5, D2=10, D3=15, 134 | SX=D0, SY=D1, SZ=D2, 135 | W=D3 136 | }; 137 | Matrix_() { Identity(); } 138 | void ToZero() 139 | { 140 | memset(cell, 0, sizeof(_Tp)*16); 141 | } 142 | void Identity() 143 | { 144 | ToZero(); 145 | cell[D0] = cell[D1] = cell[D2] = cell[W] = 1; 146 | } 147 | void Rotate( Vector3<_Tp> a_Pos, _Tp a_RX, _Tp a_RY, _Tp a_RZ ) 148 | { 149 | Matrix_ t; 150 | t.RotateX( a_RZ ); 151 | RotateY( a_RY ); 152 | Concatenate( t ); 153 | t.RotateZ( a_RX ); 154 | Concatenate( t ); 155 | Translate( a_Pos ); 156 | } 157 | void RotateX( _Tp a_RX ) 158 | { 159 | _Tp sx = (_Tp)sin( a_RX * RT_PI / 180 ); 160 | _Tp cx = (_Tp)cos( a_RX * RT_PI / 180 ); 161 | Identity(); 162 | cell[5] = cx, cell[6] = sx, cell[9] = -sx, cell[10] = cx; 163 | } 164 | void RotateY( _Tp a_RY ) 165 | { 166 | _Tp sy = (_Tp)sin( a_RY * RT_PI / 180 ); 167 | _Tp cy = (_Tp)cos( a_RY * RT_PI / 180 ); 168 | Identity (); 169 | cell[0] = cy, cell[2] = -sy, cell[8] = sy, cell[10] = cy; 170 | } 171 | void RotateZ( _Tp a_RZ ) 172 | { 173 | _Tp sz = (_Tp)sin( a_RZ * RT_PI / 180 ); 174 | _Tp cz = (_Tp)cos( a_RZ * RT_PI / 180 ); 175 | Identity (); 176 | cell[0] = cz, cell[1] = sz, cell[4] = -sz, cell[5] = cz; 177 | } 178 | void Translate( Vector3<_Tp> a_Pos ) { cell[TX] += a_Pos.x; cell[TY] += a_Pos.y; cell[TZ] += a_Pos.z; } 179 | void Concatenate( Matrix_& m2 ) 180 | { 181 | Matrix_ res; 182 | int c; 183 | for ( c = 0; c < 4; c++ ) for ( int r = 0; r < 4; r++ ) 184 | res.cell[r * 4 + c] = cell[r * 4] * m2.cell[c] + 185 | cell[r * 4 + 1] * m2.cell[c + 4] + 186 | cell[r * 4 + 2] * m2.cell[c + 8] + 187 | cell[r * 4 + 3] * m2.cell[c + 12]; 188 | memcpy(cell, res.cell, sizeof(_Tp)*16); 189 | } 190 | Vector3<_Tp> Transformed( const Vector3<_Tp>& v ) const 191 | { 192 | _Tp x = cell[0] * v.x + cell[1] * v.y + cell[2] * v.z + cell[3]; 193 | _Tp y = cell[4] * v.x + cell[5] * v.y + cell[6] * v.z + cell[7]; 194 | _Tp z = cell[8] * v.x + cell[9] * v.y + cell[10] * v.z + cell[11]; 195 | return Vector3( x, y, z ); 196 | } 197 | void Transform( Vector3<_Tp>& v ) const 198 | { 199 | v.x = cell[0] * v.x + cell[1] * v.y + cell[2] * v.z + cell[3]; 200 | v.y = cell[4] * v.x + cell[5] * v.y + cell[6] * v.z + cell[7]; 201 | v.z = cell[8] * v.x + cell[9] * v.y + cell[10] * v.z + cell[11]; 202 | } 203 | void Invert() 204 | { 205 | Matrix_ t; 206 | _Tp tx = -cell[3], ty = -cell[7], tz = -cell[11]; 207 | for ( int h = 0; h < 3; h++ ) for ( int v = 0; v < 3; v++ ) t.cell[h + v * 4] = cell[v + h * 4]; 208 | for ( int i = 0; i < 11; i++ ) cell[i] = t.cell[i]; 209 | cell[3] = tx * cell[0] + ty * cell[1] + tz * cell[2]; 210 | cell[7] = tx * cell[4] + ty * cell[5] + tz * cell[6]; 211 | cell[11] = tx * cell[8] + ty * cell[9] + tz * cell[10]; 212 | } 213 | }; 214 | 215 | 216 | // ------------------------------------------------------------------------------ 217 | // A simple plane definition in math 218 | // ------------------------------------------------------------------------------ 219 | 220 | template 221 | class Plane_ 222 | { 223 | public: 224 | union 225 | { 226 | struct 227 | { 228 | Vector3<_Tp> N; 229 | _Tp D; 230 | }; 231 | _Tp cell[4]; 232 | }; 233 | Plane_() 234 | : N(0,0,0) 235 | , D(0) 236 | {} 237 | Plane_(const Vector3<_Tp>& aNormal, _Tp aDist) 238 | : N(aNormal) 239 | , D(aDist) 240 | {} 241 | }; 242 | 243 | // ------------------------------------------------------------------------------ 244 | // Axis Aligned Bounding Box 245 | // ------------------------------------------------------------------------------ 246 | 247 | template 248 | class AABB_ 249 | { 250 | public: 251 | AABB_() 252 | : mMin(0,0,0) 253 | , mMax(0,0,0) 254 | {} 255 | AABB_(const Vector3<_Tp>& aMin, const Vector3<_Tp>& aMax) 256 | : mMin(aMin) 257 | , mMax(aMax) 258 | {} 259 | const Vector3<_Tp>& getMin() const { return mMin; } 260 | void setMin(const Vector3<_Tp>& val) { mMin = val; } 261 | const Vector3<_Tp>& getMax() const { return mMax; } 262 | void setMax(const Vector3<_Tp>& val) { mMax = val; } 263 | const Vector3<_Tp> getDim() const { return mMax - mMin; } 264 | 265 | /** Detect intersection of two AABBs 266 | \param 267 | aB2 the other AABB_ 268 | \return 269 | true intersected 270 | false not intersected 271 | */ 272 | bool interset(const AABB_& aB2) const; 273 | 274 | /** Whether a point is inside the AABB_ 275 | \param 276 | aPos the point to be detected 277 | */ 278 | bool contains(const Vector3<_Tp>& aPos) const ; 279 | 280 | private: 281 | Vector3<_Tp> mMin, mMax; 282 | }; 283 | 284 | }; // namespace RayTracer 285 | 286 | #include "MathDefs.inl" 287 | 288 | #endif // _RT_MATH_DEFS_H_ -------------------------------------------------------------------------------- /scene.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | created: 2010/04/13 3 | file name: scene.cpp 4 | author: maxint lnychina@gmail.com 5 | *********************************************************************/ 6 | 7 | #include "scene.h" 8 | #include "raytracer.h" 9 | #include "material.h" 10 | #include "primitive.h" 11 | #include "AccessObj.h" 12 | 13 | #include 14 | 15 | namespace RayTracer { 16 | 17 | using std::min; 18 | using std::max; 19 | using std::abs; 20 | 21 | // ------------------------------------------------------------------------------ 22 | // Scene class implementation 23 | // ------------------------------------------------------------------------------ 24 | 25 | Scene::Scene() 26 | : mObjLoader(0) 27 | {} 28 | 29 | Scene::~Scene() 30 | { 31 | destroy(); 32 | destroyLights(); 33 | } 34 | 35 | void Scene::destroy() 36 | { 37 | // release primitives 38 | PrimListItor it = mPrimitives.begin(); 39 | PrimListItor it_end = mPrimitives.end(); 40 | for (; it != it_end; ++it) 41 | { 42 | SAFE_DELETE(*it); 43 | } 44 | mPrimitives.clear(); 45 | mLights.clear(); 46 | 47 | // remove regular grid 48 | removeGrid(); 49 | 50 | // release vertex buffer 51 | VertexItor vit = mVerticesPool.begin(); 52 | VertexItor vit_end = mVerticesPool.end(); 53 | for (; vit!=vit_end; ++vit) 54 | { 55 | SAFE_DELETE(*vit); 56 | } 57 | mVerticesPool.clear(); 58 | } 59 | 60 | void Scene::destroyLights() 61 | { 62 | // release lights 63 | LightItor lit = mLights.begin(); 64 | LightItor lit_end = mLights.end(); 65 | for (; lit!=lit_end; ++lit) 66 | { 67 | SAFE_DELETE(*lit); 68 | } 69 | mLights.clear(); 70 | } 71 | 72 | void Scene::setupMaterials() 73 | { 74 | // add textures to texture manager 75 | TextureManager::getInstance().createFromFile("textures/marble.png", "marble"); 76 | TextureManager::getInstance().createFromFile("textures/wood.png", "wood"); 77 | 78 | // add materials 79 | Material *mat = MaterialManager::getInstance().createManual("woodMat"); 80 | mat->setReflection(0); 81 | mat->setRefraction(0); 82 | mat->setDiffuse(0.5f*Vec3::ONE); 83 | mat->setUVScale(0.1f, 0.1f); 84 | mat->setAmbient(Vec3::ZERO); 85 | mat->setTexture("wood"); 86 | 87 | mat = MaterialManager::getInstance().createManual("marbleMat"); 88 | mat->setReflection(0.0f); 89 | mat->setRefraction(0.0f); 90 | mat->setRefrIndex(1.3f); 91 | mat->setDiffuse(Vec3::ONE); 92 | mat->setSpecular(0.3f, 0.3f, 0.3f); 93 | mat->setShininess(20.0f); 94 | //mat->setAmbient(Vec3::ZERO); 95 | mat->setTexture("marble"); 96 | 97 | mat = MaterialManager::getInstance().createManual("marbleMat2"); 98 | mat->setReflection(0.0f); 99 | mat->setRefraction(0.0f); 100 | mat->setSpecular(0.3f, 0.3f, 0.3f); 101 | mat->setShininess(20.0f); 102 | mat->setDiffuse(0.4f, 0.5f, 0.5f); 103 | mat->setUVScale(0.15f, 0.15f); 104 | mat->setTexture("marble"); 105 | 106 | mat = MaterialManager::getInstance().createManual("reflectMat"); 107 | mat->setReflection(1.0f); 108 | mat->setRefraction(0.0f); 109 | mat->setDiffuse(Vec3::ZERO); 110 | mat->setAmbient(Vec3::ZERO); 111 | 112 | mat = MaterialManager::getInstance().createManual("refraMat"); 113 | mat->setReflection(0.0f); 114 | mat->setRefraction(1.0f); 115 | mat->setRefrIndex(1.2f); 116 | mat->setSpecular(0.5f * Vec3::ONE); 117 | mat->setShininess(30.0f); 118 | mat->setDiffuse(Vec3::ZERO); 119 | mat->setAmbient(Vec3::ZERO); 120 | 121 | mat = MaterialManager::getInstance().createManual("cellingMat"); 122 | mat->setReflection(0.5f); 123 | mat->setDiffuse(0.4f, 0.3f, 0.3f); 124 | 125 | mat = MaterialManager::getInstance().createManual("whiteLightMat"); 126 | mat->setDiffuse(1,1,1); 127 | 128 | mat = MaterialManager::getInstance().createManual("blueMat"); 129 | mat->setDiffuse(0.2f, 0.2f, 3.0f); 130 | mat->setDiffuseRefl(0.2f); 131 | mat->setSpecular(0.3f * Vec3::ONE); 132 | mat->setShininess(20.0f); 133 | mat->setAmbient(Vec3::ZERO); 134 | 135 | mat = MaterialManager::getInstance().createManual("greenSphereMat"); 136 | mat->setReflection(0); 137 | mat->setRefraction(0); 138 | mat->setSpecular(0.6f * Vec3::ONE); 139 | mat->setShininess(20.0f); 140 | mat->setDiffuse(0.3f, 1.0f, 0.4f); 141 | } 142 | 143 | void Scene::setupLights() 144 | { 145 | Light *lit; 146 | 147 | // area lights 148 | //lit = new Light; 149 | //lit->mType = Light::LT_AREA; 150 | //lit->mAABB.setMin(Vec3(-3, 5, -1)); 151 | //lit->mAABB.setMax(Vec3(-1, 5.1f, 1)); 152 | //lit->setSpecular(0.2f*Vec3::ONE); 153 | //lit->setDiffuse(0.2f*Vec3::ONE); 154 | //lit->setAmbient(Vec3::ZERO); 155 | //mLights.push_back(lit); 156 | 157 | lit = new Light; 158 | lit->mType = Light::LT_AREA; 159 | lit->mAABB.setMin(Vec3(1, 5, -1)); 160 | lit->mAABB.setMax(Vec3(3, 5.1f, 1)); 161 | lit->setSpecular(0.2f*Vec3::ONE); 162 | lit->setDiffuse(0.2f*Vec3::ONE); 163 | mLights.push_back(lit); 164 | 165 | 166 | // point lights 167 | lit = new Light; 168 | lit->mType = Light::LT_POINT; 169 | lit->mPosition = Vec3(0,0,0); 170 | lit->setSpecular(0.3f*Vec3::ONE); 171 | lit->setDiffuse(0.5f*Vec3::ONE); 172 | mLights.push_back(lit); 173 | 174 | lit = new Light; 175 | lit->mType = Light::LT_POINT; 176 | lit->mPosition = Vec3(0, 1, 0); 177 | lit->setSpecular(0.3f*Vec3::ONE); 178 | lit->setDiffuse(0.5f*Vec3::ONE); 179 | mLights.push_back(lit); 180 | 181 | } 182 | 183 | void Scene::initScene() 184 | { 185 | setupMaterials(); 186 | setupLights(); 187 | 188 | Primitive *prim; 189 | 190 | #if 0 191 | // area light 192 | prim = new Box(AABB(Vec3(-3, 5, -1), Vec3(-1, 5.1f, 1))); 193 | prim->setName("area light 1"); 194 | prim->setLight(true); 195 | prim->setMaterial("whiteLightMat"); 196 | mPrimitives.push_back(prim); 197 | 198 | // area light 199 | prim = new Box(AABB(Vec3(1, 5, -1), Vec3(3, 5.1f, 1))); 200 | prim->setName("area light 2"); 201 | prim->setLight(true); 202 | prim->setMaterial("whiteLightMat"); 203 | mPrimitives.push_back(prim); 204 | 205 | // light source 1 206 | prim = new Sphere( Vec3(0, 0, 10), 0.2f); 207 | prim->setLight(true); 208 | prim->setMaterial("whiteLightMat"); 209 | mPrimitives.push_back(prim); 210 | 211 | // light source 2 212 | prim = new Sphere( Vec3(-3, 5, 1), 0.1f); 213 | prim->setLight(true); 214 | prim->setMaterial("whiteLightMat"); 215 | mPrimitives.push_back(prim); 216 | #endif 217 | 218 | #if 1 219 | // middle sphere 220 | prim = new Sphere(Vec3(0.0f, -2.0f, 0.0f), 1.0f); 221 | prim->setName("middle sphere"); 222 | prim->setMaterial("reflectMat"); 223 | //prim->setMaterial("refraMat"); 224 | mPrimitives.push_back(prim); 225 | #endif 226 | 227 | #if 0 228 | Vertex *vert1 = new Vertex(Vec3(-1, 1, 1), -Vec3::UNIT_Z, 0, 0); 229 | Vertex *vert2 = new Vertex(Vec3(1, 1, 1), -Vec3::UNIT_Z, 0, 1); 230 | Vertex *vert3 = new Vertex(Vec3(-2, -2, 1), -Vec3::UNIT_Z, 1, 0); 231 | mVerticesPool.push_back(vert1); 232 | mVerticesPool.push_back(vert2); 233 | mVerticesPool.push_back(vert3); 234 | prim = new TrianglePrim(vert1, vert2, vert3); 235 | prim->setName("a triangle"); 236 | prim->setMaterial("marbleMat"); 237 | mPrimitives.push_back(prim); 238 | #endif 239 | 240 | #if 0 241 | // left sphere 242 | prim = new Sphere(Vec3(-5.0f, -0.8f, 0.0f), 2.0f); 243 | prim->setName("left sphere"); 244 | prim->setMaterial("blueMat"); 245 | mPrimitives.push_back(prim); 246 | 247 | // right sphere 248 | prim = new Sphere(Vec3(5.0f, -0.8f, 0.0f), 2.0f); 249 | prim->setName("right sphere"); 250 | prim->setMaterial("marbleMat"); 251 | mPrimitives.push_back(prim); 252 | 253 | #endif 254 | 255 | #if 0 256 | // bottom box 257 | prim = new Box(AABB(Vec3(-1.5f, -4.3f, 5.5f), Vec3(1.5f, -3.0f, 8.5f))); 258 | prim->setName("middle box"); 259 | prim->getMaterial()->setDiffuse(0.5f); 260 | prim->getMaterial()->setSpecular(0.0f); 261 | prim->getMaterial()->setReflection(0.0f); 262 | prim->getMaterial()->setRefraction(0.8f); 263 | prim->getMaterial()->setRefrIndex(1.3f); 264 | prim->getMaterial()->setColor(0.8f, 0.8f, 0.8f); 265 | mPrimitives.push_back(prim); 266 | #endif 267 | 268 | #if 0 269 | // ground plane 270 | prim = new PlanePrim(Vec3::UNIT_Y, 4.4f); 271 | prim->setName("plane"); 272 | prim->setMaterial("woodMat"); 273 | mPrimitives.push_back(prim); 274 | 275 | // black plane 276 | prim = new PlanePrim(-Vec3::UNIT_Z, 5.0f); 277 | prim->setName("back plane"); 278 | prim->setMaterial("marbleMat2"); 279 | mPrimitives.push_back(prim); 280 | 281 | #endif 282 | #if 0 283 | // right plane 284 | prim = new PlanePrim(Vec3(-1,0,0), 8.0f); 285 | prim->setName("right wall"); 286 | prim->setMaterial("marbleMat2"); 287 | mPrimitives.push_back(prim); 288 | 289 | // ceiling plane 290 | prim = new PlanePrim(Vec3(0,-1,0), 5.2f); 291 | prim->setName("celling plane"); 292 | prim->setMaterial("cellingMat"); 293 | mPrimitives.push_back(prim); 294 | 295 | // left plane 296 | //prim = new PlanePrim(Vec3(1,0,0), 8.0f); 297 | //prim->setName("left wall"); 298 | //prim->setMaterial("reflectMat"); 299 | //mPrimitives.push_back(prim); 300 | #endif 301 | 302 | // build the regular grid 303 | buildGrid(); 304 | } 305 | 306 | void Scene::removeGrid() 307 | { 308 | GridMapItor git = mGird.begin(); 309 | GridMapItor git_end = mGird.end(); 310 | for (; git!=git_end; ++git) 311 | { 312 | SAFE_DELETE(*git); 313 | } 314 | mGird.clear(); 315 | } 316 | 317 | void Scene::buildGrid() 318 | { 319 | // initialize regular grid 320 | removeGrid(); 321 | mGird.resize(RT_GRIDSIZE * RT_GRIDSIZE * RT_GRIDSIZE, 0); 322 | 323 | updateExtends(); 324 | Vec3 dv = mExtends.getDim() / RT_GRIDSIZE; 325 | Vec3 rdv = 1.0f / dv; 326 | Vec3 rMin, rMax; 327 | AABB cell; 328 | 329 | // store primitives in the grid cells 330 | PrimListItor it = mPrimitives.begin(); 331 | PrimListItor it_end = mPrimitives.end(); 332 | Primitive *prim; 333 | int count = 0; 334 | for (; it!=it_end; ++it) 335 | { 336 | prim = *it; 337 | 338 | // find out which cells could contain the primitive ( based on aabb) 339 | rMin = (prim->getAABB().getMin() - mExtends.getMin()) * rdv; 340 | rMax = (prim->getAABB().getMax() - mExtends.getMin()) * rdv + 1.0f; 341 | rMin.Max(Vec3::ZERO); 342 | rMax.Min(Vec3::ONE * (RT_GRIDSIZE-1)); 343 | 344 | // loop over candidate cells 345 | for (int z=static_cast(rMin.z); z(rMax.z); ++z) 346 | for (int y=static_cast(rMin.y); y(rMax.y); ++y) 347 | for (int x=static_cast(rMin.x); x(rMax.x); ++x) 348 | { 349 | // construct aabb for current cell 350 | int idx = x + y * RT_GRIDSIZE + z * RT_GRIDSIZE * RT_GRIDSIZE; 351 | Vec3 pos(mExtends.getMin() + Vec3(static_cast(x), 352 | static_cast(y), static_cast(z) ) * dv); 353 | cell.setMin(pos); 354 | cell.setMax(pos + dv); 355 | // do an accurate aabb / primitive intersection test 356 | if (prim->intersetBox(cell)) 357 | { 358 | if (mGird[idx] == 0) 359 | mGird[idx] = new ObjectList; 360 | mGird[idx]->push_back(prim); 361 | //std::cout << count << ": " << idx << std::endl; 362 | ++count; 363 | } 364 | } // end for cells 365 | }// end for primitives 366 | count = count; 367 | } 368 | 369 | void Scene::updateExtends() 370 | { 371 | //Vec3 tMin(10000 * Vec3::ONE); 372 | //Vec3 tMax(-10000 * Vec3::ONE); 373 | Vec3 tMin(-3, -3, -6), tMax( 14, 8, 30 ); 374 | 375 | if (mObjLoader) 376 | { 377 | tMin.x = mObjLoader->m_vMin.x; 378 | tMin.y = mObjLoader->m_vMin.y; 379 | tMin.z = mObjLoader->m_vMin.z; 380 | tMax.x = mObjLoader->m_vMax.x; 381 | tMax.y = mObjLoader->m_vMax.y; 382 | tMax.z = mObjLoader->m_vMax.z; 383 | } 384 | 385 | //PrimListItor it = mPrimitives.begin(); 386 | //PrimListItor it_end = mPrimitives.end(); 387 | //for (; it!=it_end; ++it) 388 | //{ 389 | // if ((*it)->getType() != Primitive::PT_PLANE) 390 | // { 391 | // tMin.Min((*it)->getAABB().getMin()); 392 | // tMax.Max((*it)->getAABB().getMax()); 393 | // } 394 | //} 395 | //Vec3 tCentre = 0.5 * (tMin + tMax); 396 | //Vec3 tOffset = tMax - tCentre; 397 | //tMin -= 2.0f * tOffset; 398 | //tMax += 2.0f * tOffset; 399 | mExtends.setMin(tMin); 400 | mExtends.setMax(tMax); 401 | } 402 | 403 | void Scene::loadObjModel(const trimeshVec::CAccessObj* accessObj) 404 | { 405 | //destroy(); 406 | mObjLoader = accessObj; 407 | 408 | unsigned int i, k, count = 0; 409 | trimeshVec::CPoint3D *vpVertices = accessObj->m_pModel->vpVertices; 410 | trimeshVec::CPoint3D *vpNormals = accessObj->m_pModel->vpNormals; 411 | trimeshVec::CPoint3D *vpTexCoords = accessObj->m_pModel->vpTexCoords; 412 | trimeshVec::COBJmaterial *pMaterials = accessObj->m_pModel->pMaterials; 413 | for (i=0; im_pModel->nMaterials; ++i) 414 | { 415 | trimeshVec::COBJmaterial &mat = accessObj->m_pModel->pMaterials[i]; 416 | Material *newmat = MaterialManager::getInstance().createManual(mat.name); 417 | Texture *tex = NULL; 418 | if (mat.sTexture[0] != '\0') 419 | { 420 | tex = TextureManager::getInstance().createFromFile(mat.sTexture); 421 | } 422 | newmat->setTexture(tex); 423 | newmat->setAmbient(mat.ambient[0], mat.ambient[1], mat.ambient[2]); 424 | newmat->setDiffuse(mat.diffuse[0], mat.diffuse[1], mat.diffuse[2]); 425 | newmat->setSpecular(mat.specular[0], mat.specular[1], mat.specular[2]); 426 | newmat->setShininess(mat.shininess[0]); 427 | newmat->setEmission(mat.emissive[0], mat.emissive[1], mat.emissive[2]); 428 | } 429 | 430 | TrianglePrim *prim; 431 | for (i=0; im_pModel->nTriangles; ++i) 432 | { 433 | trimeshVec::COBJtriangle &tri = accessObj->m_pModel->pTriangles[i]; 434 | Vertex *v[3]; 435 | for (k=0; k<3; ++k) 436 | { 437 | trimeshVec::CPoint3D &pos = vpVertices[tri.vindices[k]]; 438 | if (vpNormals) 439 | { 440 | trimeshVec::CPoint3D &norm = vpNormals[tri.nindices[k]]; 441 | if (vpTexCoords) 442 | { 443 | trimeshVec::CPoint3D &texcoord = vpTexCoords[tri.tindices[k]]; 444 | v[k] = new Vertex(Vec3(pos.x, pos.y, pos.z), Vec3(norm.x, norm.y, norm.z), 445 | texcoord.x, texcoord.y); 446 | } 447 | else 448 | { 449 | v[k] = new Vertex(Vec3(pos.x, pos.y, pos.z), Vec3(norm.x, norm.y, norm.z)); 450 | } 451 | } 452 | else 453 | v[k] = new Vertex(Vec3(pos.x, pos.y, pos.z)); 454 | mVerticesPool.push_back(v[k]); 455 | } 456 | prim = new TrianglePrim(v[0], v[1], v[2], vpNormals==NULL); 457 | std::ostringstream oss; 458 | oss << "_Triangle" << ++count; 459 | prim->setName(oss.str()); 460 | if (pMaterials) 461 | { 462 | trimeshVec::COBJmaterial &mat = pMaterials[tri.mindex]; 463 | prim->setMaterial(mat.name); 464 | } 465 | mPrimitives.push_back(prim); 466 | } 467 | 468 | buildGrid(); 469 | } 470 | 471 | }; // namespace RayTracer -------------------------------------------------------------------------------- /mainwindow.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include "AccessObj.h" 3 | #include 4 | #include 5 | #include "raytracer.h" 6 | 7 | using namespace RayTracer; 8 | 9 | const QString WIDGET_NAME = "ImageView"; 10 | const QString WINDOW_TITLE = "Ray Tracer CPU"; 11 | const RayTracer::Vec3 EYE_POS(3, 4, 5); 12 | const RayTracer::Vec3 LIGHT_POS(2, 3, 4); 13 | const int TOOLTIP_STRETCH = 5000; 14 | 15 | MainWindow::MainWindow() 16 | : mImage(800, 600, QImage::Format_RGB888) 17 | , mpAccessObj(0) 18 | , mEngine(0) 19 | , mLastCostTime(0) 20 | { 21 | init(); 22 | } 23 | 24 | MainWindow::MainWindow(const QString &fileName) 25 | : mImage(800, 600, QImage::Format_RGB888) 26 | , mpAccessObj(0) 27 | , mEngine(0) 28 | { 29 | init(); 30 | if (QFile::exists(fileName)) 31 | { 32 | openObjFile(fileName); 33 | statusBar()->showMessage(tr("File loaded"), TOOLTIP_STRETCH); 34 | } 35 | else 36 | { 37 | statusBar()->showMessage(tr("File load fail")); 38 | } 39 | } 40 | 41 | MainWindow::~MainWindow() 42 | { 43 | SAFE_DELETE(mpAccessObj); 44 | } 45 | 46 | void MainWindow::init() 47 | { 48 | srand((unsigned int)time(0)); 49 | xRot = yRot = zRot = 30; 50 | 51 | initRenderSystem(); 52 | 53 | setupUi(); 54 | createActions(); 55 | createMenus(); 56 | createToolBars(); 57 | createStatusBar(); 58 | } 59 | 60 | void MainWindow::initRenderSystem() 61 | { 62 | mpAccessObj = new trimeshVec::CAccessObj; 63 | mEngine = new RayTracer::Engine; 64 | mEngine->setRenderTarget(mImage.width(), mImage.height(), &mImage); 65 | 66 | mImage.fill(qRgb(200, 200, 200)); 67 | } 68 | 69 | void MainWindow::setupUi() 70 | { 71 | mImgView = new QWidget; 72 | mImgView->setObjectName(WIDGET_NAME); 73 | mImgView->setFixedSize(mImage.size()); 74 | this->setCentralWidget(mImgView); 75 | mImgView->installEventFilter(this); 76 | this->layout()->setSizeConstraint(QLayout::SetFixedSize); 77 | 78 | mProgressBar = new QProgressBar(this); 79 | 80 | setWindowTitle(WINDOW_TITLE); 81 | } 82 | 83 | QSize MainWindow::sizeHint() const 84 | { 85 | return mImage.size(); 86 | } 87 | 88 | void MainWindow::createActions() 89 | { 90 | // file menu 91 | mOpenAct = new QAction(QIcon(":/images/open.png"), tr("&Open Obj..."), this); 92 | mOpenAct->setShortcut(QKeySequence::Open); 93 | mOpenAct->setToolTip(tr("Open an obj model file")); 94 | connect(mOpenAct, SIGNAL(triggered()), this, SLOT(open())); 95 | 96 | mSaveAsImageAct = new QAction(QIcon(":/images/save.png"), tr("&Save As Image..."), this); 97 | mSaveAsImageAct->setShortcut(QKeySequence::Save); 98 | mSaveAsImageAct->setToolTip(tr("Save the result as an image")); 99 | connect(mSaveAsImageAct, SIGNAL(triggered()), this, SLOT(saveAs())); 100 | 101 | mQuitAct = new QAction(tr("&Quit"), this); 102 | mQuitAct->setShortcut(tr("Ctrl+Q")); 103 | mQuitAct->setToolTip(tr("Exit the application")); 104 | connect(mQuitAct, SIGNAL(triggered()), qApp, SLOT(closeAllWindows())); 105 | 106 | // edit menu 107 | mResolutionAct = new QAction(tr("Set &Canvas..."), this); 108 | mResolutionAct->setShortcut(tr("Ctrl+C")); 109 | mResolutionAct->setToolTip(tr("Set the resolution")); 110 | connect(mResolutionAct, SIGNAL(triggered()), this, SLOT(resolution())); 111 | 112 | mRenderAct = new QAction(tr("&Render!"), this); 113 | mRenderAct->setToolTip(tr("Render the scene")); 114 | mRenderAct->setShortcut(tr("Ctrl+R")); 115 | 116 | mTraceDepthAct = new QAction(tr("Trace &Depth..."), this); 117 | mTraceDepthAct->setToolTip(tr("Set Ray tracing depth")); 118 | mTraceDepthAct->setShortcut(tr("Ctrl+D")); 119 | 120 | mRegularSamplesAct = new QAction(tr("Regular &Sampling Size..."), this); 121 | mRegularSamplesAct->setToolTip(tr("Set regular sampling size of box light")); 122 | 123 | mShadeActGroup = new QActionGroup(this); 124 | mShadeActGroup->setExclusive(false); 125 | mShadeActGroup->addAction(mRenderAct); 126 | mShadeActGroup->addAction(mTraceDepthAct); 127 | mShadeActGroup->addAction(mRegularSamplesAct); 128 | connect(mShadeActGroup, SIGNAL(triggered(QAction*)), this, SLOT(shadeModel(QAction*))); 129 | 130 | // view menu 131 | mViewToolBarAct = new QAction(tr("&Toolbar"), this); 132 | mViewToolBarAct->setToolTip(tr("Toggle toolbar")); 133 | mViewToolBarAct->setCheckable(true); 134 | mViewToolBarAct->setChecked(true); 135 | mViewToolBarAct->setShortcut(tr("Ctrl+T")); 136 | 137 | mInfoToolBarAct = new QAction(tr("&Information"), this); 138 | mInfoToolBarAct->setToolTip(tr("Toggle information toolbar")); 139 | mInfoToolBarAct->setCheckable(true); 140 | mInfoToolBarAct->setChecked(true); 141 | mInfoToolBarAct->setShortcut(tr("Ctrl+I")); 142 | 143 | mViewActGroup = new QActionGroup(this); 144 | mViewActGroup->addAction(mViewToolBarAct); 145 | mViewActGroup->addAction(mInfoToolBarAct); 146 | mViewActGroup->setExclusive(false); 147 | connect(mViewActGroup, SIGNAL(triggered(QAction*)), this, SLOT(toggleView(QAction*))); 148 | 149 | // help menu 150 | mAboutAct = new QAction(tr("&About"), this); 151 | mAboutAct->setToolTip(tr("Show the application's About box")); 152 | connect(mAboutAct, SIGNAL(triggered()), this, SLOT(about())); 153 | mAboutQtAct = new QAction(tr("About &Qt"), this); 154 | mAboutQtAct->setToolTip(tr("Show the Qt library's About box")); 155 | connect(mAboutQtAct, SIGNAL(triggered()), qApp, SLOT(aboutQt())); 156 | } 157 | 158 | void MainWindow::shadeModel(QAction* act) 159 | { 160 | if (act == mRenderAct) 161 | { 162 | renderObj(); 163 | } 164 | else if (act == mTraceDepthAct) 165 | { 166 | bool ok; 167 | int newDepth = QInputDialog::getInt(this, tr("Set Trace Depth"), 168 | tr("New Depth (1-%1) : ").arg(RT_TRACEDEPTH), mEngine->getTraceDepth(), 169 | 1, RT_TRACEDEPTH, 1, &ok); 170 | 171 | if (ok) 172 | { 173 | mEngine->setTraceDepth(newDepth); 174 | statusBar()->showMessage(tr("New ray tracing depth (%1) is applied").arg(newDepth), TOOLTIP_STRETCH); 175 | renderObj(); 176 | } 177 | } 178 | else if (act == mRegularSamplesAct) 179 | { 180 | bool ok; 181 | int newSize = QInputDialog::getInt(this, tr("Set Regular Sampling Size"), 182 | tr("New sampling size (1-%1) : ").arg(RT_REGULAR_SAMPLES), mEngine->getRegularSampleSize(), 183 | 1, RT_REGULAR_SAMPLES, 1, &ok); 184 | 185 | if (ok) 186 | { 187 | mEngine->setRegularSampleSize(newSize); 188 | statusBar()->showMessage(tr("New sampling size (%1) is applied").arg(newSize), TOOLTIP_STRETCH); 189 | renderObj(); 190 | } 191 | } 192 | updateInformationBar(); 193 | } 194 | 195 | void MainWindow::toggleView(QAction *act) 196 | { 197 | bool bShow = act->isChecked(); 198 | if (act == mViewToolBarAct) 199 | { 200 | mFileToolBar->setVisible(bShow); 201 | mEditToolBar->setVisible(bShow); 202 | mCameraLightToolBar->setVisible(bShow); 203 | } 204 | else if (act == mInfoToolBarAct) 205 | { 206 | mInfoToolBar->setVisible(bShow); 207 | } 208 | } 209 | 210 | void MainWindow::createMenus() 211 | { 212 | mFileMenu = menuBar()->addMenu(tr("&File")); 213 | mFileMenu->addAction(mOpenAct); 214 | mFileMenu->addAction(mSaveAsImageAct); 215 | mFileMenu->addSeparator(); 216 | mFileMenu->addAction(mQuitAct); 217 | 218 | menuBar()->addSeparator(); 219 | 220 | mViewMenu = menuBar()->addMenu(tr("&View")); 221 | mViewMenu->addAction(mViewToolBarAct); 222 | mViewMenu->addAction(mInfoToolBarAct); 223 | 224 | menuBar()->addSeparator(); 225 | 226 | mEditMenu = menuBar()->addMenu(tr("&Edit")); 227 | mEditMenu->addAction(mResolutionAct); 228 | mEditMenu->addAction(mRenderAct); 229 | mEditMenu->addAction(mTraceDepthAct); 230 | mEditMenu->addAction(mRegularSamplesAct); 231 | mEditMenu->addSeparator(); 232 | 233 | menuBar()->addSeparator(); 234 | mHelpMenu = menuBar()->addMenu(tr("&Help")); 235 | mHelpMenu->addAction(mAboutAct); 236 | mHelpMenu->addAction(mAboutQtAct); 237 | } 238 | 239 | void MainWindow::createToolBars() 240 | { 241 | // file toolbar 242 | mFileToolBar = addToolBar(tr("File")); 243 | mFileToolBar->addAction(mOpenAct); 244 | mFileToolBar->addAction(mSaveAsImageAct); 245 | 246 | // edit toolbar 247 | mEditToolBar = addToolBar(tr("Edit")); 248 | mEditToolBar->addAction(mResolutionAct); 249 | mEditToolBar->addAction(mRenderAct); 250 | mEditToolBar->addAction(mTraceDepthAct); 251 | mEditToolBar->addSeparator(); 252 | 253 | // camera and light toolbar 254 | //mSpinEyeX = new QDoubleSpinBox; 255 | //mSpinEyeY = new QDoubleSpinBox; 256 | //mSpinEyeZ = new QDoubleSpinBox; 257 | //mSpinLightX = new QDoubleSpinBox; 258 | //mSpinLightY = new QDoubleSpinBox; 259 | //mSpinLightZ = new QDoubleSpinBox; 260 | //mSpinEyeX->setMinimum(-100.0); 261 | //mSpinEyeY->setMinimum(-100.0); 262 | //mSpinEyeZ->setMinimum(-100.0); 263 | //mSpinLightX->setMinimum(-100.0); 264 | //mSpinLightY->setMinimum(-100.0); 265 | //mSpinLightX->setMinimum(-100.0); 266 | //mSpinEyeX->setSingleStep(0.2); 267 | //mSpinEyeY->setSingleStep(0.2); 268 | //mSpinEyeZ->setSingleStep(0.2); 269 | //mSpinLightX->setSingleStep(0.2); 270 | //mSpinLightY->setSingleStep(0.2); 271 | //mSpinLightZ->setSingleStep(0.2); 272 | //mSpinEyeX->setValue(EYE_POS.x); 273 | //mSpinEyeY->setValue(EYE_POS.y); 274 | //mSpinEyeZ->setValue(EYE_POS.z); 275 | //mSpinLightX->setValue(LIGHT_POS.x); 276 | //mSpinLightY->setValue(LIGHT_POS.y); 277 | //mSpinLightZ->setValue(LIGHT_POS.z); 278 | 279 | //mCameraLightToolBar = addToolBar(tr("CameraAndLight")); 280 | //addToolBar(Qt::BottomToolBarArea, mCameraLightToolBar); 281 | //mCameraLightToolBar->setToolTip(tr("Set position of camera or light")); 282 | 283 | //mCameraLightToolBar->addWidget(new QLabel(tr("Camera "))); 284 | //mCameraLightToolBar->addWidget(new QLabel(tr("x:"))); 285 | //mCameraLightToolBar->addWidget(mSpinEyeX); 286 | //mCameraLightToolBar->addWidget(new QLabel(tr("y:"))); 287 | //mCameraLightToolBar->addWidget(mSpinEyeY); 288 | //mCameraLightToolBar->addWidget(new QLabel(tr("z:"))); 289 | //mCameraLightToolBar->addWidget(mSpinEyeZ); 290 | //mCameraLightToolBar->addSeparator(); 291 | //mCameraLightToolBar->addWidget(new QLabel(tr("Light "))); 292 | //mCameraLightToolBar->addWidget(new QLabel(tr("x:"))); 293 | //mCameraLightToolBar->addWidget(mSpinLightX); 294 | //mCameraLightToolBar->addWidget(new QLabel(tr("y:"))); 295 | //mCameraLightToolBar->addWidget(mSpinLightY); 296 | //mCameraLightToolBar->addWidget(new QLabel(tr("z:"))); 297 | //mCameraLightToolBar->addWidget(mSpinLightZ); 298 | 299 | //connect(mSpinEyeX, SIGNAL(valueChanged(double)), this, SLOT(newFrustumOrLight())); 300 | //connect(mSpinEyeY, SIGNAL(valueChanged(double)), this, SLOT(newFrustumOrLight())); 301 | //connect(mSpinEyeZ, SIGNAL(valueChanged(double)), this, SLOT(newFrustumOrLight())); 302 | //connect(mSpinLightX, SIGNAL(valueChanged(double)), this, SLOT(newFrustumOrLight())); 303 | //connect(mSpinLightY, SIGNAL(valueChanged(double)), this, SLOT(newFrustumOrLight())); 304 | //connect(mSpinLightZ, SIGNAL(valueChanged(double)), this, SLOT(newFrustumOrLight())); 305 | 306 | // information toolbar 307 | mInfoToolBar = addToolBar(tr("Information")); 308 | addToolBar(Qt::RightToolBarArea, mInfoToolBar); 309 | 310 | QGroupBox *infoGroup = new QGroupBox(tr("Information")); 311 | QProgressBar *progressBarCopy = new QProgressBar(mProgressBar); 312 | connect(mProgressBar, SIGNAL(valueChanged(int)), progressBarCopy, SLOT(setValue(int))); 313 | QBoxLayout *infoLayout = new QBoxLayout(QBoxLayout::TopToBottom); 314 | mInfoLabel = new QLabel(""); 315 | infoLayout->addWidget(mInfoLabel); 316 | infoLayout->addWidget(progressBarCopy); 317 | infoGroup->setLayout(infoLayout); 318 | mInfoToolBar->addWidget(infoGroup); 319 | 320 | updateInformationBar(); 321 | } 322 | 323 | void MainWindow::createStatusBar() 324 | { 325 | mResLabel = new QLabel(this); 326 | statusBar()->addPermanentWidget(mResLabel); 327 | mResLabel->setText(tr("%1x%2").arg(mImage.width()).arg(mImage.height())); 328 | 329 | statusBar()->addWidget(mProgressBar); 330 | mProgressBar->hide(); 331 | 332 | statusBar()->showMessage(tr("Ready")); 333 | statusBar()->setSizeGripEnabled(false); 334 | } 335 | 336 | void MainWindow::open() 337 | { 338 | QString fileName = QFileDialog::getOpenFileName(this, 339 | tr("Open Obj Model"), "objs", tr("Obj Files (*.obj)")); 340 | if (!fileName.isEmpty()) 341 | { 342 | openObjFile(fileName); 343 | } 344 | } 345 | 346 | void MainWindow::saveAs() 347 | { 348 | QString fileName = QFileDialog::getSaveFileName(this, 349 | tr("Save Result"), "res", tr("Images (*.png *.jpg *.bmp)")); 350 | 351 | saveAsImageFile(fileName); 352 | } 353 | 354 | void MainWindow::resolution() 355 | { 356 | bool ok; 357 | QString strSize = QInputDialog::getText(this, tr("Set Resolution"), 358 | tr("New Resolution (e.g. 800 600) : "), QLineEdit::Normal, 359 | tr("1024 768"), &ok); 360 | 361 | int w = strSize.section(' ', 0, 0).toInt(); 362 | int h = strSize.section(' ', 1, 1).toInt(); 363 | if (ok && w>0 && h>0) 364 | { 365 | mImgView->setFixedSize(w, h); 366 | setResolution(w, h); 367 | statusBar()->showMessage(tr("New resolution (%1, %2) is applied").arg(w).arg(h), TOOLTIP_STRETCH); 368 | } 369 | else if(ok) 370 | { 371 | statusBar()->showMessage(tr("Invalid resolution"), TOOLTIP_STRETCH); 372 | } 373 | } 374 | 375 | void MainWindow::openObjFile(const QString& fileName) 376 | { 377 | if (mpAccessObj->LoadOBJ(fileName.toStdString().c_str())) 378 | { 379 | mpAccessObj->UnifiedModel(); 380 | 381 | mEngine->loadObjModel(mpAccessObj); 382 | 383 | renderObj(); 384 | 385 | setWindowTitle( tr("%1 - %2") 386 | .arg(strippedName(fileName)) 387 | .arg(WINDOW_TITLE) ); 388 | } 389 | else 390 | { 391 | QMessageBox::warning(this, tr("OBJ File Invalid"), 392 | tr("File %1 is not a valid obj model file.").arg(fileName)); 393 | } 394 | } 395 | 396 | void MainWindow::renderObj() 397 | { 398 | updateInformationBar(); 399 | mProgressBar->reset(); 400 | mProgressBar->show(); 401 | 402 | qApp->processEvents(); 403 | 404 | mEngine->initEngine(Vec3(0, -2, 4), Vec3(0, -2, 0)); 405 | clock_t tt = clock(); 406 | while(!mEngine->render()) 407 | { 408 | mImgView->update(); 409 | mProgressBar->setValue(mEngine->getCurrProgree()); 410 | qApp->processEvents(); 411 | } 412 | 413 | mProgressBar->setValue(100); 414 | mProgressBar->hide(); 415 | mLastCostTime = static_cast(clock()-tt); 416 | statusBar()->showMessage(tr("Ray Tracing finished in %1 ms with %2 primitives.") 417 | .arg(mLastCostTime).arg(mEngine->getNumOfPrimitives()), TOOLTIP_STRETCH); 418 | 419 | updateInformationBar(); 420 | 421 | mImgView->update(); 422 | } 423 | 424 | void MainWindow::saveAsImageFile(const QString& fileName) 425 | { 426 | mImage.save(fileName); 427 | } 428 | 429 | void MainWindow::setResolution(int width, int height) 430 | { 431 | mResLabel->setText(tr("%1x%2").arg(width).arg(height)); 432 | mImage = mImage.scaled(width, height); 433 | mEngine->setRenderTarget(mImage.width(), mImage.height(), &mImage); 434 | renderObj(); 435 | } 436 | 437 | void MainWindow::newFrustumOrLight() 438 | { 439 | renderObj(); 440 | } 441 | 442 | void MainWindow::about() 443 | { 444 | QMessageBox::about(this, tr("About Ray Tracer CPU"), 445 | tr("

A RayTracer project writed by maxint, in April, 2010.

" 446 | "

Email: lnychina@gmail.com

" 447 | "

Blog: http://hi.baidu.com/maxint

")); 448 | } 449 | 450 | void MainWindow::rotateBy(double xAngle, double yAngle, double zAngle) 451 | { 452 | //static Mat22d matRot; 453 | //static Vec3 eyePos; 454 | 455 | //xRot += xAngle; 456 | //yRot += yAngle; 457 | //zRot += zAngle; 458 | //xRot %= 360; 459 | //yRot %= 360; 460 | //zRot %= 360; 461 | 462 | //eyePos[0] = mSpinEyeX->value(); 463 | //eyePos[1] = mSpinEyeY->value(); 464 | //eyePos[2] = mSpinEyeZ->value(); 465 | //double len = eyePos.len(); 466 | //eyePos[0] = 0; 467 | //eyePos[1] = 0; 468 | //eyePos[2] = len; 469 | 470 | //matRot.rotate(-zRot); 471 | //matRot.map(eyePos[0], eyePos[1], &eyePos[0], &eyePos[1]); 472 | //std::cout << eyePos.len() << std::endl; 473 | //matRot.rotate(yRot); 474 | //matRot.map(eyePos[0], eyePos[2], &eyePos[0], &eyePos[2]); 475 | //std::cout << eyePos.len() << std::endl; 476 | //matRot.rotate(-xRot); 477 | //matRot.map(eyePos[1], eyePos[2], &eyePos[1], &eyePos[2]); 478 | //std::cout << eyePos.len() << std::endl; 479 | 480 | //mSpinEyeX->setValue(eyePos[0]); 481 | //mSpinEyeY->setValue(eyePos[1]); 482 | //mSpinEyeZ->setValue(eyePos[2]); 483 | 484 | newFrustumOrLight(); 485 | } 486 | 487 | bool MainWindow::eventFilter(QObject *obj, QEvent *e) 488 | { 489 | if (obj->isWidgetType() && 490 | obj->objectName() == WIDGET_NAME) 491 | { 492 | QWidget *wid = static_cast(obj); 493 | if (e->type() == QEvent::Paint) 494 | { 495 | QPainter painter(wid); 496 | QRect rect(QPoint(0,0), wid->size()); 497 | painter.drawImage(rect, mImage); 498 | e->accept(); 499 | return true; 500 | } 501 | } 502 | 503 | return QMainWindow::eventFilter(obj, e); 504 | } 505 | 506 | QString MainWindow::strippedName(const QString& fullFileName) 507 | { 508 | return QFileInfo(fullFileName).fileName(); 509 | } 510 | 511 | void MainWindow::updateInformationBar() 512 | { 513 | QString info = tr(""); 516 | info += tr(""); 517 | info += tr("").arg(mEngine->getNumOfPrimitives()); 518 | info += tr("") 519 | .arg(mImage.width()).arg(mImage.height()); 520 | info += tr("").arg(mLastCostTime); 521 | info += tr("").arg(mEngine->getTraceDepth()); 522 | info += tr("").arg(mEngine->getRegularSampleSize()); 523 | info += tr("
Primitives: %1
Resolution: %1 x %2
Last cost time: %1 ms
Trace depth: %1
Regular Samples: %1
"); 524 | mInfoLabel->setText(info); 525 | } -------------------------------------------------------------------------------- /RayTracerCPU.vcproj: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 15 | 16 | 17 | 18 | 19 | 27 | 30 | 33 | 36 | 39 | 45 | 64 | 67 | 71 | 74 | 88 | 91 | 94 | 97 | 100 | 103 | 106 | 109 | 110 | 118 | 121 | 124 | 127 | 130 | 136 | 155 | 158 | 162 | 165 | 178 | 181 | 184 | 187 | 190 | 193 | 196 | 199 | 200 | 201 | 202 | 203 | 204 | 209 | 212 | 213 | 216 | 217 | 220 | 221 | 224 | 225 | 228 | 229 | 232 | 233 | 236 | 237 | 240 | 241 | 244 | 245 | 248 | 249 | 250 | 255 | 258 | 261 | 268 | 269 | 272 | 279 | 280 | 281 | 284 | 287 | 294 | 295 | 298 | 305 | 306 | 307 | 310 | 313 | 320 | 321 | 324 | 331 | 332 | 333 | 336 | 339 | 346 | 347 | 350 | 357 | 358 | 359 | 362 | 365 | 372 | 373 | 376 | 383 | 384 | 385 | 388 | 391 | 398 | 399 | 402 | 409 | 410 | 411 | 414 | 417 | 424 | 425 | 428 | 435 | 436 | 437 | 440 | 443 | 450 | 451 | 454 | 461 | 462 | 463 | 466 | 469 | 476 | 477 | 480 | 487 | 488 | 489 | 492 | 495 | 502 | 503 | 506 | 513 | 514 | 515 | 518 | 521 | 528 | 529 | 532 | 539 | 540 | 541 | 544 | 547 | 554 | 555 | 558 | 565 | 566 | 567 | 568 | 573 | 576 | 580 | 583 | 584 | 585 | 588 | 592 | 595 | 596 | 597 | 600 | 604 | 607 | 608 | 609 | 612 | 616 | 619 | 620 | 621 | 622 | 628 | 631 | 634 | 641 | 642 | 645 | 652 | 653 | 654 | 657 | 660 | 667 | 668 | 671 | 678 | 679 | 680 | 683 | 686 | 693 | 694 | 697 | 704 | 705 | 706 | 709 | 712 | 719 | 720 | 723 | 730 | 731 | 732 | 735 | 738 | 745 | 746 | 749 | 756 | 757 | 758 | 761 | 764 | 771 | 772 | 775 | 782 | 783 | 784 | 787 | 790 | 797 | 798 | 801 | 808 | 809 | 810 | 811 | 812 | 813 | 817 | 818 | 819 | -------------------------------------------------------------------------------- /AccessObj.cpp: -------------------------------------------------------------------------------- 1 | #include "AccessObj.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #pragma warning(disable: 4996) 10 | 11 | using namespace std; 12 | 13 | #define objMax(a,b) (((a)>(b))?(a):(b)) 14 | #define objMin(a,b) (((a)<(b))?(a):(b)) 15 | #define objAbs(x) (((x)>0.f)?(x):(-x)) 16 | 17 | #define Tri(x) (m_pModel->pTriangles[(x)]) 18 | 19 | namespace trimeshVec 20 | { 21 | ////////////////////////////////////////////////////////////////////// 22 | // Construction/Destruction 23 | ////////////////////////////////////////////////////////////////////// 24 | CAccessObj::CAccessObj() 25 | { 26 | m_pModel = NULL; 27 | } 28 | 29 | CAccessObj::~CAccessObj() 30 | { 31 | Destory(); 32 | } 33 | 34 | ////////////////////////////////////////////////////////////////////// 35 | // Equal: compares two vectors and returns true if they are 36 | // equal (within a certain threshold) or false if not. An epsilon 37 | // that works fairly well is 0.000001. 38 | // 39 | // u - array of 3 GLfloats (float u[3]) 40 | // v - array of 3 GLfloats (float v[3]) 41 | ////////////////////////////////////////////////////////////////////// 42 | bool CAccessObj::Equal(CPoint3D * u, CPoint3D * v, float epsilon) 43 | { 44 | if (objAbs(u->x - v->x) < epsilon && 45 | objAbs(u->y - v->y) < epsilon && 46 | objAbs(u->z - v->z) < epsilon) 47 | { 48 | return true; 49 | } 50 | return false; 51 | } 52 | 53 | 54 | ////////////////////////////////////////////////////////////////////// 55 | // FindGroup: Find a group in the model 56 | ////////////////////////////////////////////////////////////////////// 57 | COBJgroup * CAccessObj::FindGroup(char* name) 58 | { 59 | COBJgroup * group; 60 | 61 | assert(m_pModel); 62 | 63 | group = m_pModel->pGroups; 64 | while(group) 65 | { 66 | if (!strcmp(name, group->name)) 67 | break; 68 | group = group->next; 69 | } 70 | 71 | return group; 72 | } 73 | 74 | ////////////////////////////////////////////////////////////////////// 75 | // AddGroup: Add a group to the model 76 | ////////////////////////////////////////////////////////////////////// 77 | COBJgroup * CAccessObj::AddGroup(char* name) 78 | { 79 | COBJgroup* group; 80 | 81 | group = FindGroup(name); 82 | if (!group) 83 | { 84 | group = new COBJgroup; 85 | sprintf_s(group->name, 256, "%s", name); 86 | group->nTriangles = 0; 87 | group->pTriangles = NULL; 88 | group->next = m_pModel->pGroups; 89 | m_pModel->pGroups = group; 90 | m_pModel->nGroups++; 91 | } 92 | 93 | return group; 94 | } 95 | 96 | ////////////////////////////////////////////////////////////////////// 97 | // DirName: return the directory given a path 98 | // 99 | // path - filesystem path 100 | // 101 | // NOTE: the return value should be free'd. 102 | ////////////////////////////////////////////////////////////////////// 103 | char * CAccessObj::DirName(char* path) 104 | { 105 | static char dir[256]; 106 | char *s; 107 | 108 | sprintf_s(dir,256, "%s", path); 109 | 110 | s = strrchr(dir, '\\'); // for windows format 111 | if (s == NULL) 112 | s = strrchr(dir, '/'); 113 | 114 | if (s) 115 | s[1] = '\0'; 116 | else 117 | dir[0] = '\0'; 118 | 119 | return dir; 120 | } 121 | 122 | ////////////////////////////////////////////////////////////////////// 123 | // FindGroup: Find a material in the model 124 | ////////////////////////////////////////////////////////////////////// 125 | unsigned int CAccessObj::FindMaterial(char* name) 126 | { 127 | unsigned int i; 128 | bool bFound = false; 129 | 130 | // XXX doing a linear search on a string key'd list is pretty lame, but it works and is fast enough for now. 131 | for (i = 0; i < m_pModel->nMaterials; i++) 132 | { 133 | if (!strcmp(m_pModel->pMaterials[i].name, name)) 134 | { 135 | bFound = true; 136 | break; 137 | } 138 | } 139 | 140 | // didn't find the name, so print a warning and return the default material (0). 141 | if (!bFound) 142 | { 143 | printf("FindMaterial(): can't find material \"%s\".\n", name); 144 | i = 0; 145 | } 146 | 147 | return i; 148 | } 149 | 150 | ////////////////////////////////////////////////////////////////////// 151 | // ReadMTL: read a wavefront material library file 152 | // 153 | // model - properly initialized COBJmodel structure 154 | // name - name of the material library 155 | ////////////////////////////////////////////////////////////////////// 156 | void CAccessObj::ReadMTL(char* name) 157 | { 158 | FILE* file; 159 | char dir[256]; 160 | char filename[256]; 161 | char buf[128]; 162 | unsigned int nMaterials, i; 163 | 164 | 165 | char *s; 166 | 167 | sprintf(dir, "%s", DirName(m_pModel->pathname)); 168 | strcpy(filename, dir); 169 | strcat(filename, name); 170 | 171 | file = fopen(filename, "r"); 172 | if (!file) 173 | { 174 | fprintf(stderr, "ReadMTL() failed: can't open material file \"%s\".\n", filename); 175 | exit(1); 176 | } 177 | 178 | // count the number of materials in the file 179 | nMaterials = 1; 180 | while(fscanf(file, "%s", buf) != EOF) 181 | { 182 | switch(buf[0]) 183 | { 184 | case '#': /* comment */ 185 | /* eat up rest of line */ 186 | fgets(buf, sizeof(buf), file); 187 | break; 188 | case 'n': /* newmtl */ 189 | fgets(buf, sizeof(buf), file); 190 | nMaterials++; 191 | sscanf(buf, "%s %s", buf, buf); 192 | break; 193 | default: 194 | /* eat up rest of line */ 195 | fgets(buf, sizeof(buf), file); 196 | break; 197 | } 198 | } 199 | 200 | rewind(file); 201 | 202 | m_pModel->pMaterials = new COBJmaterial [nMaterials]; 203 | m_pModel->nMaterials = nMaterials; 204 | 205 | // set the default material 206 | for (i = 0; i < nMaterials; i++) { 207 | m_pModel->pMaterials[i].name[0] = '\0'; 208 | m_pModel->pMaterials[i].shininess[0] = 32.0f; 209 | m_pModel->pMaterials[i].diffuse[0] = 0.8f; 210 | m_pModel->pMaterials[i].diffuse[1] = 0.8f; 211 | m_pModel->pMaterials[i].diffuse[2] = 0.8f; 212 | m_pModel->pMaterials[i].diffuse[3] = 1.0f; 213 | m_pModel->pMaterials[i].ambient[0] = 0.2f; 214 | m_pModel->pMaterials[i].ambient[1] = 0.2f; 215 | m_pModel->pMaterials[i].ambient[2] = 0.2f; 216 | m_pModel->pMaterials[i].ambient[3] = 1.0f; 217 | m_pModel->pMaterials[i].specular[0] = 0.0f; 218 | m_pModel->pMaterials[i].specular[1] = 0.0f; 219 | m_pModel->pMaterials[i].specular[2] = 0.0f; 220 | m_pModel->pMaterials[i].specular[3] = 1.0f; 221 | m_pModel->pMaterials[i].emissive[0] = 0.0f; 222 | m_pModel->pMaterials[i].emissive[1] = 0.0f; 223 | m_pModel->pMaterials[i].emissive[2] = 0.0f; 224 | m_pModel->pMaterials[i].emissive[3] = 1.0f; 225 | } 226 | sprintf(m_pModel->pMaterials[0].name, "default"); 227 | 228 | // now, read in the data 229 | nMaterials = 0; 230 | while(fscanf(file, "%s", buf) != EOF) 231 | { 232 | switch(buf[0]) 233 | { 234 | case '#': /* comment */ 235 | /* eat up rest of line */ 236 | fgets(buf, sizeof(buf), file); 237 | break; 238 | case 'n': /* newmtl */ 239 | fgets(buf, sizeof(buf), file); 240 | sscanf(buf, "%s %s", buf, buf); 241 | nMaterials++; 242 | sprintf(m_pModel->pMaterials[nMaterials].name, "%s", buf); 243 | break; 244 | case 'N': 245 | fscanf(file, "%f", &m_pModel->pMaterials[nMaterials].shininess); 246 | /* wavefront shininess is from [0, 1000], so scale for OpenGL */ 247 | m_pModel->pMaterials[nMaterials].shininess[0] /= 1000.0f; 248 | m_pModel->pMaterials[nMaterials].shininess[0] *= 128.0f; 249 | break; 250 | case 'K': 251 | switch(buf[1]) 252 | { 253 | case 'd': 254 | fscanf(file, "%f %f %f", 255 | &m_pModel->pMaterials[nMaterials].diffuse[0], 256 | &m_pModel->pMaterials[nMaterials].diffuse[1], 257 | &m_pModel->pMaterials[nMaterials].diffuse[2]); 258 | break; 259 | case 's': 260 | fscanf(file, "%f %f %f", 261 | &m_pModel->pMaterials[nMaterials].specular[0], 262 | &m_pModel->pMaterials[nMaterials].specular[1], 263 | &m_pModel->pMaterials[nMaterials].specular[2]); 264 | break; 265 | case 'a': 266 | fscanf(file, "%f %f %f", 267 | &m_pModel->pMaterials[nMaterials].ambient[0], 268 | &m_pModel->pMaterials[nMaterials].ambient[1], 269 | &m_pModel->pMaterials[nMaterials].ambient[2]); 270 | break; 271 | default: 272 | /* eat up rest of line */ 273 | fgets(buf, sizeof(buf), file); 274 | break; 275 | } 276 | break; 277 | case 'm': 278 | fscanf(file, "%s", filename); 279 | s = strrchr(filename, ':'); 280 | if (!s) 281 | sprintf(m_pModel->pMaterials[nMaterials].sTexture, "%s%s", dir, filename); 282 | else 283 | sprintf(m_pModel->pMaterials[nMaterials].sTexture, "%s", filename); 284 | break; 285 | default: 286 | /* eat up rest of line */ 287 | fgets(buf, sizeof(buf), file); 288 | break; 289 | } 290 | } 291 | 292 | fclose (file); 293 | } 294 | 295 | ////////////////////////////////////////////////////////////////////// 296 | // WriteMTL: write a wavefront material library file 297 | // 298 | // model - properly initialized COBJmodel structure 299 | // modelpath - pathname of the model being written 300 | // mtllibname - name of the material library to be written 301 | ////////////////////////////////////////////////////////////////////// 302 | void CAccessObj::WriteMTL(char* modelpath, char* mtllibname) 303 | { 304 | FILE* file; 305 | char* dir; 306 | char* filename; 307 | COBJmaterial* material; 308 | unsigned int i; 309 | 310 | dir = DirName(modelpath); 311 | filename = new char [(strlen(dir)+strlen(mtllibname))]; 312 | strcpy(filename, dir); 313 | strcat(filename, mtllibname); 314 | free(dir); 315 | 316 | /* open the file */ 317 | file = fopen(filename, "w"); 318 | if (!file) 319 | { 320 | fprintf(stderr, "WriteMTL() failed: can't open file \"%s\".\n", filename); 321 | exit(1); 322 | } 323 | delete [] filename; 324 | 325 | /* spit out a header */ 326 | fprintf(file, "# \n"); 327 | fprintf(file, "# Wavefront MTL generated by OBJ library\n"); 328 | fprintf(file, "# \n"); 329 | fprintf(file, "# OBJ library\n"); 330 | fprintf(file, "# Nate Robins\n"); 331 | fprintf(file, "# ndr@pobox.com\n"); 332 | fprintf(file, "# http://www.pobox.com/~ndr\n"); 333 | fprintf(file, "# \n\n"); 334 | 335 | for (i = 0; i < m_pModel->nMaterials; i++) 336 | { 337 | material = &m_pModel->pMaterials[i]; 338 | fprintf(file, "newmtl %s\n", material->name); 339 | fprintf(file, "Ka %f %f %f\n", 340 | material->ambient[0], material->ambient[1], material->ambient[2]); 341 | fprintf(file, "Kd %f %f %f\n", 342 | material->diffuse[0], material->diffuse[1], material->diffuse[2]); 343 | fprintf(file, "Ks %f %f %f\n", 344 | material->specular[0],material->specular[1],material->specular[2]); 345 | fprintf(file, "Ns %f\n", material->shininess[0] / 128.0 * 1000.0); 346 | fprintf(file, "\n"); 347 | } 348 | } 349 | 350 | ////////////////////////////////////////////////////////////////////// 351 | // FirstPass: first pass at a Wavefront OBJ file that gets all the 352 | // statistics of the model (such as #vertices, #normals, etc) 353 | // 354 | // model - properly initialized COBJmodel structure 355 | // file - (fopen'd) file descriptor 356 | ////////////////////////////////////////////////////////////////////// 357 | bool CAccessObj::FirstPass(FILE* file) 358 | { 359 | unsigned int nVertices; /* number of vertices in m_pModel */ 360 | unsigned int nNormals; /* number of normals in m_pModel */ 361 | unsigned int nTexCoords; /* number of texcoords in m_pModel */ 362 | unsigned int nTriangles; /* number of triangles in m_pModel */ 363 | COBJgroup* group; /* current group */ 364 | unsigned v, n, t; 365 | char buf[129]; 366 | 367 | /* make a default group */ 368 | group = AddGroup("default"); 369 | 370 | nVertices = nNormals = nTexCoords = nTriangles = 0; 371 | while(fscanf(file, "%128s", buf, 129) != EOF) 372 | { 373 | switch(buf[0]) 374 | { 375 | case '#': /* comment */ 376 | /* eat up rest of line */ 377 | fgets(buf, sizeof(buf), file); 378 | break; 379 | case 'v': /* v, vn, vt */ 380 | switch(buf[1]) 381 | { 382 | case '\0': /* vertex */ 383 | /* eat up rest of line */ 384 | fgets(buf, sizeof(buf), file); 385 | nVertices++; 386 | break; 387 | case 'n': /* normal */ 388 | /* eat up rest of line */ 389 | fgets(buf, sizeof(buf), file); 390 | nNormals++; 391 | break; 392 | case 't': /* texcoord */ 393 | /* eat up rest of line */ 394 | fgets(buf, sizeof(buf), file); 395 | nTexCoords++; 396 | break; 397 | default: 398 | printf("FirstPass(): Unknown token \"%s\".\n", buf); 399 | exit(1); 400 | break; 401 | } 402 | break; 403 | 404 | case 'm': 405 | fgets(buf, sizeof(buf), file); 406 | sscanf(buf, "%s %s", buf, buf); 407 | sprintf(m_pModel->mtllibname, "%s", buf); 408 | ReadMTL(buf); 409 | break; 410 | 411 | case 'u': 412 | /* eat up rest of line */ 413 | fgets(buf, sizeof(buf), file); 414 | break; 415 | case 'g': /* group */ 416 | /* eat up rest of line */ 417 | fgets(buf, sizeof(buf), file); 418 | buf[strlen(buf)-1] = '\0'; /* nuke '\n' */ 419 | group = AddGroup(buf); 420 | break; 421 | case 'f': /* face */ 422 | v = n = t = 0; 423 | fscanf(file, "%128s", buf, 129); 424 | /* can be one of %d, %d//%d, %d/%d, %d/%d/%d %d//%d */ 425 | if (strstr(buf, "//")) 426 | { 427 | /* v//n */ 428 | sscanf(buf, "%d//%d", &v, &n); 429 | fscanf(file, "%d//%d", &v, &n); 430 | fscanf(file, "%d//%d", &v, &n); 431 | nTriangles++; 432 | group->nTriangles++; 433 | while(fscanf(file, "%d//%d", &v, &n) > 0) 434 | { 435 | nTriangles++; 436 | group->nTriangles++; 437 | } 438 | } 439 | else if (sscanf(buf, "%d/%d/%d", &v, &t, &n) == 3) 440 | { 441 | /* v/t/n */ 442 | fscanf(file, "%d/%d/%d", &v, &t, &n); 443 | fscanf(file, "%d/%d/%d", &v, &t, &n); 444 | nTriangles++; 445 | group->nTriangles++; 446 | while(fscanf(file, "%d/%d/%d", &v, &t, &n) > 0) 447 | { 448 | nTriangles++; 449 | group->nTriangles++; 450 | } 451 | } 452 | else if (sscanf(buf, "%d/%d", &v, &t) == 2) 453 | { 454 | /* v/t */ 455 | fscanf(file, "%d/%d", &v, &t); 456 | fscanf(file, "%d/%d", &v, &t); 457 | nTriangles++; 458 | group->nTriangles++; 459 | while(fscanf(file, "%d/%d", &v, &t) > 0) 460 | { 461 | nTriangles++; 462 | group->nTriangles++; 463 | } 464 | } 465 | else 466 | { 467 | /* v */ 468 | fscanf(file, "%d", &v); 469 | fscanf(file, "%d", &v); 470 | nTriangles++; 471 | group->nTriangles++; 472 | while(fscanf(file, "%d", &v) > 0) 473 | { 474 | nTriangles++; 475 | group->nTriangles++; 476 | } 477 | } 478 | break; 479 | 480 | default: 481 | /* eat up rest of line */ 482 | if (isalpha(buf[0])) 483 | { 484 | fgets(buf, sizeof(buf), file); 485 | } 486 | else 487 | return false; 488 | break; 489 | } 490 | } 491 | 492 | /* set the stats in the m_pModel structure */ 493 | m_pModel->nVertices = nVertices; 494 | m_pModel->nNormals = nNormals; 495 | m_pModel->nTexCoords = nTexCoords; 496 | m_pModel->nTriangles = nTriangles; 497 | 498 | /* allocate memory for the triangles in each group */ 499 | group = m_pModel->pGroups; 500 | while(group) 501 | { 502 | if (group->nTriangles > 0) 503 | group->pTriangles = new unsigned int [group->nTriangles]; 504 | group->nTriangles = 0; 505 | group = group->next; 506 | } 507 | return true; 508 | } 509 | 510 | ////////////////////////////////////////////////////////////////////// 511 | // SecondPass: second pass at a Wavefront OBJ file that gets all 512 | // the data. 513 | // 514 | // model - properly initialized COBJmodel structure 515 | // file - (fopen'd) file descriptor 516 | ////////////////////////////////////////////////////////////////////// 517 | void CAccessObj::SecondPass(FILE* file) 518 | { 519 | unsigned int nVertices; /* number of vertices in m_pModel */ 520 | unsigned int nNormals; /* number of normals in m_pModel */ 521 | unsigned int nTexCoords; /* number of texcoords in m_pModel */ 522 | unsigned int nTriangles; /* number of triangles in m_pModel */ 523 | CPoint3D * vertices; /* array of vertices */ 524 | CPoint3D * normals; /* array of normals */ 525 | CPoint3D * texcoords; /* array of texture coordinates */ 526 | COBJgroup * group; /* current group pointer */ 527 | unsigned int material; /* current material */ 528 | unsigned int v, n, t; 529 | char buf[128]; 530 | 531 | /* set the pointer shortcuts */ 532 | vertices = m_pModel->vpVertices; 533 | normals = m_pModel->vpNormals; 534 | texcoords = m_pModel->vpTexCoords; 535 | group = m_pModel->pGroups; 536 | 537 | /* on the second pass through the file, read all the data into the 538 | allocated arrays */ 539 | nVertices = nNormals = nTexCoords = 1; 540 | nTriangles = 0; 541 | material = 0; 542 | 543 | int nNormalAdd = 0; 544 | int nNormalCount = 0; 545 | 546 | while(fscanf(file, "%s", buf) != EOF) 547 | { 548 | switch(buf[0]) 549 | { 550 | case '#': /* comment */ 551 | /* eat up rest of line */ 552 | fgets(buf, sizeof(buf), file); 553 | break; 554 | case 'v': /* v, vn, vt */ 555 | switch(buf[1]) 556 | { 557 | case '\0': /* vertex */ 558 | fscanf(file, "%f %f %f", 559 | &vertices[nVertices].x, 560 | &vertices[nVertices].y, 561 | &vertices[nVertices].z); 562 | nVertices++; 563 | break; 564 | case 'n': /* normal */ 565 | fscanf(file, "%f %f %f", 566 | &normals[nNormals].x, 567 | &normals[nNormals].y, 568 | &normals[nNormals].z); 569 | nNormals++; 570 | nNormalCount ++; 571 | break; 572 | case 't': /* texcoord */ 573 | fscanf(file, "%f %f", 574 | &texcoords[nTexCoords].x, 575 | &texcoords[nTexCoords].y); 576 | nTexCoords++; 577 | break; 578 | } 579 | break; 580 | case 'u': 581 | fgets(buf, sizeof(buf), file); 582 | sscanf(buf, "%s %s", buf, buf); 583 | group->material = material = FindMaterial(buf); 584 | 585 | break; 586 | case 'g': /* group */ 587 | /* eat up rest of line */ 588 | fgets(buf, sizeof(buf), file); 589 | buf[strlen(buf)-1] = '\0'; /* nuke '\n' */ 590 | group = FindGroup(buf); 591 | group->material = material; 592 | nNormalAdd += nNormalCount; 593 | nNormalCount = 0; 594 | break; 595 | case 'f': /* face */ 596 | v = n = t = 0; 597 | fscanf(file, "%s", buf); 598 | /* can be one of %d, %d//%d, %d/%d, %d/%d/%d %d//%d */ 599 | Tri(nTriangles).mindex = material; 600 | if (strstr(buf, "//")) 601 | { 602 | /* v//n */ 603 | sscanf(buf, "%d//%d", &v, &n); 604 | Tri(nTriangles).vindices[0] = v; 605 | Tri(nTriangles).nindices[0] = n;// + nNormalAdd; 606 | fscanf(file, "%d//%d", &v, &n); 607 | Tri(nTriangles).vindices[1] = v; 608 | Tri(nTriangles).nindices[1] = n;// + nNormalAdd; 609 | fscanf(file, "%d//%d", &v, &n); 610 | Tri(nTriangles).vindices[2] = v; 611 | Tri(nTriangles).nindices[2] = n;// + nNormalAdd; 612 | group->pTriangles[group->nTriangles++] = nTriangles; 613 | nTriangles++; 614 | while(fscanf(file, "%d//%d", &v, &n) > 0) 615 | { 616 | Tri(nTriangles).vindices[0] = Tri(nTriangles-1).vindices[0]; 617 | Tri(nTriangles).nindices[0] = Tri(nTriangles-1).nindices[0]; 618 | Tri(nTriangles).vindices[1] = Tri(nTriangles-1).vindices[2]; 619 | Tri(nTriangles).nindices[1] = Tri(nTriangles-1).nindices[2]; 620 | Tri(nTriangles).vindices[2] = v; 621 | Tri(nTriangles).nindices[2] = n;// + nNormalAdd; 622 | Tri(nTriangles).mindex = material; 623 | group->pTriangles[group->nTriangles++] = nTriangles; 624 | nTriangles++; 625 | } 626 | } 627 | else if (sscanf(buf, "%d/%d/%d", &v, &t, &n) == 3) 628 | { 629 | /* v/t/n */ 630 | Tri(nTriangles).vindices[0] = v; 631 | Tri(nTriangles).tindices[0] = t; 632 | Tri(nTriangles).nindices[0] = n;// + nNormalAdd; 633 | fscanf(file, "%d/%d/%d", &v, &t, &n); 634 | Tri(nTriangles).vindices[1] = v; 635 | Tri(nTriangles).tindices[1] = t; 636 | Tri(nTriangles).nindices[1] = n;// + nNormalAdd; 637 | fscanf(file, "%d/%d/%d", &v, &t, &n); 638 | Tri(nTriangles).vindices[2] = v; 639 | Tri(nTriangles).tindices[2] = t; 640 | Tri(nTriangles).nindices[2] = n;// + nNormalAdd; 641 | group->pTriangles[group->nTriangles++] = nTriangles; 642 | nTriangles++; 643 | while(fscanf(file, "%d/%d/%d", &v, &t, &n) > 0) 644 | { 645 | Tri(nTriangles).vindices[0] = Tri(nTriangles-1).vindices[0]; 646 | Tri(nTriangles).tindices[0] = Tri(nTriangles-1).tindices[0]; 647 | Tri(nTriangles).nindices[0] = Tri(nTriangles-1).nindices[0]; 648 | Tri(nTriangles).vindices[1] = Tri(nTriangles-1).vindices[2]; 649 | Tri(nTriangles).tindices[1] = Tri(nTriangles-1).tindices[2]; 650 | Tri(nTriangles).nindices[1] = Tri(nTriangles-1).nindices[2]; 651 | Tri(nTriangles).vindices[2] = v; 652 | Tri(nTriangles).tindices[2] = t; 653 | Tri(nTriangles).nindices[2] = n;// + nNormalAdd; 654 | Tri(nTriangles).mindex = material; 655 | group->pTriangles[group->nTriangles++] = nTriangles; 656 | nTriangles++; 657 | } 658 | } 659 | else if (sscanf(buf, "%d/%d", &v, &t) == 2) 660 | { 661 | /* v/t */ 662 | Tri(nTriangles).vindices[0] = v; 663 | Tri(nTriangles).tindices[0] = t; 664 | fscanf(file, "%d/%d", &v, &t); 665 | Tri(nTriangles).vindices[1] = v; 666 | Tri(nTriangles).tindices[1] = t; 667 | fscanf(file, "%d/%d", &v, &t); 668 | Tri(nTriangles).vindices[2] = v; 669 | Tri(nTriangles).tindices[2] = t; 670 | group->pTriangles[group->nTriangles++] = nTriangles; 671 | nTriangles++; 672 | while(fscanf(file, "%d/%d", &v, &t) > 0) 673 | { 674 | Tri(nTriangles).vindices[0] = Tri(nTriangles-1).vindices[0]; 675 | Tri(nTriangles).tindices[0] = Tri(nTriangles-1).tindices[0]; 676 | Tri(nTriangles).vindices[1] = Tri(nTriangles-1).vindices[2]; 677 | Tri(nTriangles).tindices[1] = Tri(nTriangles-1).tindices[2]; 678 | Tri(nTriangles).vindices[2] = v; 679 | Tri(nTriangles).tindices[2] = t; 680 | Tri(nTriangles).mindex = material; 681 | group->pTriangles[group->nTriangles++] = nTriangles; 682 | nTriangles++; 683 | } 684 | } 685 | else 686 | { 687 | /* v */ 688 | sscanf(buf, "%d", &v); 689 | Tri(nTriangles).vindices[0] = v; 690 | fscanf(file, "%d", &v); 691 | Tri(nTriangles).vindices[1] = v; 692 | fscanf(file, "%d", &v); 693 | Tri(nTriangles).vindices[2] = v; 694 | group->pTriangles[group->nTriangles++] = nTriangles; 695 | nTriangles++; 696 | while(fscanf(file, "%d", &v) > 0) 697 | { 698 | Tri(nTriangles).vindices[0] = Tri(nTriangles-1).vindices[0]; 699 | Tri(nTriangles).vindices[1] = Tri(nTriangles-1).vindices[2]; 700 | Tri(nTriangles).vindices[2] = v; 701 | group->pTriangles[group->nTriangles++] = nTriangles; 702 | Tri(nTriangles).mindex = material; 703 | nTriangles++; 704 | } 705 | } 706 | break; 707 | 708 | default: 709 | /* eat up rest of line */ 710 | fgets(buf, sizeof(buf), file); 711 | break; 712 | } 713 | } 714 | } 715 | 716 | ////////////////////////////////////////////////////////////////////// 717 | // Dimensions: Calculates the dimensions (width, height, depth) of 718 | // a model. 719 | // 720 | // model - initialized COBJmodel structure 721 | // dimensions - array of 3 GLfloats (float dimensions[3]) 722 | ////////////////////////////////////////////////////////////////////// 723 | void CAccessObj::Dimensions(float* dimensions) 724 | { 725 | unsigned int i; 726 | CPoint3D vMax, vMin; 727 | 728 | assert(m_pModel); 729 | assert(m_pModel->vpVertices); 730 | assert(dimensions); 731 | 732 | /* get the max/mins */ 733 | vMax = vMin = m_pModel->vpVertices[0]; 734 | for (i = 1; i <= m_pModel->nVertices; i++) 735 | { 736 | if (vMax.x < m_pModel->vpVertices[i].x) 737 | vMax.x = m_pModel->vpVertices[i].x; 738 | if (vMin.x > m_pModel->vpVertices[i].x) 739 | vMin.x = m_pModel->vpVertices[i].x; 740 | 741 | if (vMax.y < m_pModel->vpVertices[i].y) 742 | vMax.y = m_pModel->vpVertices[i].y; 743 | if (vMin.y > m_pModel->vpVertices[i].y) 744 | vMin.y = m_pModel->vpVertices[i].y; 745 | 746 | if (vMax.z < m_pModel->vpVertices[i].z) 747 | vMax.z = m_pModel->vpVertices[i].z; 748 | if (vMin.z > m_pModel->vpVertices[i].z) 749 | vMin.z = m_pModel->vpVertices[i].z; 750 | } 751 | 752 | /* calculate m_pModel width, height, and depth */ 753 | dimensions[0] = vMax.x-vMin.x; 754 | dimensions[1] = vMax.y-vMin.y; 755 | dimensions[2] = vMax.z-vMin.z; 756 | } 757 | 758 | ////////////////////////////////////////////////////////////////////// 759 | // Scale: Scales a model by a given amount. 760 | // 761 | // model - properly initialized COBJmodel structure 762 | // scale - scalefactor (0.5 = half as large, 2.0 = twice as large) 763 | ////////////////////////////////////////////////////////////////////// 764 | void CAccessObj::Scale(float scale) 765 | { 766 | unsigned int i; 767 | 768 | for (i = 1; i <= m_pModel->nVertices; i++) 769 | m_pModel->vpVertices[i] = m_pModel->vpVertices[i] * scale; 770 | } 771 | 772 | ////////////////////////////////////////////////////////////////////// 773 | // ReverseWinding: Reverse the polygon winding for all polygons in 774 | // this model. Default winding is counter-clockwise. Also changes 775 | // the direction of the normals. 776 | // 777 | // model - properly initialized COBJmodel structure 778 | ////////////////////////////////////////////////////////////////////// 779 | void CAccessObj::ReverseWinding() 780 | { 781 | unsigned int i, swap; 782 | 783 | assert(m_pModel); 784 | 785 | for (i = 0; i < m_pModel->nTriangles; i++) 786 | { 787 | swap = Tri(i).vindices[0]; 788 | Tri(i).vindices[0] = Tri(i).vindices[2]; 789 | Tri(i).vindices[2] = swap; 790 | 791 | if (m_pModel->nNormals) 792 | { 793 | swap = Tri(i).nindices[0]; 794 | Tri(i).nindices[0] = Tri(i).nindices[2]; 795 | Tri(i).nindices[2] = swap; 796 | } 797 | } 798 | 799 | /* reverse facet normals */ 800 | for (i = 1; i <= m_pModel->nFacetnorms; i++) 801 | m_pModel->vpFacetNorms[i] = m_pModel->vpFacetNorms[i]*(-1); 802 | 803 | /* reverse vertex normals */ 804 | for (i = 1; i <= m_pModel->nNormals; i++) 805 | m_pModel->vpNormals[i] = m_pModel->vpNormals[i]*(-1); 806 | } 807 | 808 | ////////////////////////////////////////////////////////////////////// 809 | // FacetNormals: Generates facet normals for a model (by taking the 810 | // cross product of the two vectors derived from the sides of each 811 | // triangle). Assumes a counter-clockwise winding. 812 | // 813 | // model - initialized COBJmodel structure 814 | ////////////////////////////////////////////////////////////////////// 815 | void CAccessObj::FacetNormals() 816 | { 817 | unsigned int i; 818 | CPoint3D u, v; 819 | 820 | assert(m_pModel); 821 | assert(m_pModel->vpVertices); 822 | 823 | /* clobber any old facetnormals */ 824 | SAFE_DELETE_ARRAY(m_pModel->vpFacetNorms); 825 | 826 | /* allocate memory for the new facet normals */ 827 | m_pModel->nFacetnorms = m_pModel->nTriangles; 828 | m_pModel->vpFacetNorms = new CPoint3D [m_pModel->nFacetnorms + 1]; 829 | 830 | for (i = 0; i < m_pModel->nTriangles; i++) 831 | { 832 | m_pModel->pTriangles[i].findex = i+1; 833 | 834 | u = m_pModel->vpVertices[Tri(i).vindices[1]] - m_pModel->vpVertices[Tri(i).vindices[0]]; 835 | v = m_pModel->vpVertices[Tri(i).vindices[2]] - m_pModel->vpVertices[Tri(i).vindices[0]]; 836 | 837 | m_pModel->vpFacetNorms[i+1] = u * v; 838 | m_pModel->vpFacetNorms[i+1].unify(); 839 | } 840 | } 841 | 842 | ////////////////////////////////////////////////////////////////////// 843 | // VertexNormals: Generates smooth vertex normals for a model. 844 | // First builds a list of all the triangles each vertex is in. Then 845 | // loops through each vertex in the the list averaging all the facet 846 | // normals of the triangles each vertex is in. Finally, sets the 847 | // normal idxInTri in the triangle for the vertex to the generated smooth 848 | // normal. If the dot product of a facet normal and the facet normal 849 | // associated with the first triangle in the list of triangles the 850 | // current vertex is in is greater than the cosine of the angle 851 | // parameter to the function, that facet normal is not added into the 852 | // average normal calculation and the corresponding vertex is given 853 | // the facet normal. This tends to preserve hard edges. The angle to 854 | // use depends on the model, but 90 degrees is usually a good start. 855 | // 856 | // model - initialized COBJmodel structure 857 | // angle - maximum angle (in degrees) to smooth across 858 | ////////////////////////////////////////////////////////////////////// 859 | void CAccessObj::VertexNormals(float angle) 860 | { 861 | OBJnode* node; 862 | typedef list NodeList; 863 | typedef NodeList::iterator NodeListItor; 864 | typedef vector ListArray; 865 | typedef ListArray::iterator ListArrItor; 866 | ListArray members; 867 | unsigned int nNormals; 868 | CPoint3D t_vAverage; 869 | float dot, cos_angle; 870 | unsigned int i; 871 | 872 | assert(m_pModel); 873 | assert(m_pModel->vpFacetNorms); 874 | 875 | /* calculate the cosine of the angle (in degrees) */ 876 | cos_angle = (float)cos(angle * 3.14159265 / 180.0); 877 | 878 | /* nuke any previous normals */ 879 | SAFE_DELETE_ARRAY(m_pModel->vpNormals); 880 | 881 | /* allocate space for new normals */ 882 | m_pModel->nNormals = m_pModel->nTriangles * 3; /* 3 normals per triangle */ 883 | m_pModel->vpNormals = new CPoint3D [m_pModel->nNormals+1]; 884 | 885 | /* allocate a structure that will hold a linked list of triangle 886 | indices for each vertex */ 887 | members.resize(m_pModel->nVertices + 1); 888 | 889 | /* for every triangle, create a node for each vertex in it */ 890 | for (i = 0; i < m_pModel->nTriangles; i++) 891 | { 892 | node = new OBJnode; 893 | node->triIdx = i; 894 | node->idxInTri = 0; 895 | members[Tri(i).vindices[0]].push_back(node); 896 | node = new OBJnode; 897 | node->triIdx = i; 898 | node->idxInTri = 1; 899 | members[Tri(i).vindices[1]].push_back(node); 900 | node = new OBJnode; 901 | node->triIdx = i; 902 | node->idxInTri = 2; 903 | members[Tri(i).vindices[2]].push_back(node); 904 | } 905 | 906 | /* calculate the average normal for each vertex */ 907 | nNormals = 1; 908 | for (i = 1; i <= m_pModel->nVertices; i++) 909 | { 910 | if (members[i].empty()) 911 | { 912 | continue; 913 | #ifdef _DEBUG 914 | //fprintf(stderr, "VertexNormals(): vertex w/o a triangle\n"); 915 | #endif // _DEBUG 916 | } 917 | 918 | // calculate an average normal for this vertex by averaging the 919 | // facet normal of every triangle this vertex is in 920 | NodeListItor itl = members[i].begin(); 921 | NodeListItor itl_end = members[i].end(); 922 | 923 | for (; itl!=itl_end; ++itl) 924 | { 925 | /* only average if the dot product of the angle between the two 926 | facet normals is greater than the cosine of the threshold 927 | angle -- or, said another way, the angle between the two 928 | facet normals is less than (or equal to) the threshold angle */ 929 | node = *itl; 930 | t_vAverage = CPoint3D(0, 0, 0); 931 | NodeListItor itl_t = members[i].begin(); 932 | for (; itl_t!=itl_end; ++itl_t) 933 | { 934 | OBJnode* node_t = *itl_t; 935 | dot = m_pModel->vpFacetNorms[Tri(node->triIdx).findex] & 936 | m_pModel->vpFacetNorms[Tri(node_t->triIdx).findex]; 937 | if (dot > cos_angle) 938 | { 939 | t_vAverage += m_pModel->vpFacetNorms[Tri(node_t->triIdx).findex]; 940 | /* we averaged at least one normal! */ 941 | } 942 | } 943 | /* normalize the averaged normal */ 944 | t_vAverage.unify(); 945 | 946 | /* add the normal to the vertex normals list */ 947 | m_pModel->vpNormals[nNormals] = t_vAverage; 948 | Tri(node->triIdx).nindices[node->idxInTri] = nNormals; 949 | ++nNormals; 950 | } 951 | } 952 | 953 | /* free the member information */ 954 | ListArrItor ita = members.begin(); 955 | ListArrItor ita_end = members.end(); 956 | for (; ita!=ita_end; ++ita) 957 | { 958 | NodeListItor itl = ita->begin(); 959 | NodeListItor itl_end = ita->end(); 960 | for (; itl!=itl_end; ++itl) 961 | { 962 | SAFE_DELETE(*itl); 963 | } 964 | } 965 | } 966 | 967 | 968 | 969 | ////////////////////////////////////////////////////////////////////// 970 | // LinearTexture: Generates texture coordinates according to a 971 | // linear projection of the texture map. It generates these by 972 | // linearly mapping the vertices onto a square. 973 | // 974 | // model - pointer to initialized COBJmodel structure 975 | ////////////////////////////////////////////////////////////////////// 976 | void CAccessObj::LinearTexture() 977 | { 978 | COBJgroup *group; 979 | float dimensions[3]; 980 | float x, y, scalefactor; 981 | unsigned int i; 982 | 983 | assert(m_pModel); 984 | 985 | if (m_pModel->vpTexCoords) 986 | free(m_pModel->vpTexCoords); 987 | m_pModel->nTexCoords = m_pModel->nVertices; 988 | m_pModel->vpTexCoords = new CPoint3D [m_pModel->nTexCoords+1]; 989 | 990 | Dimensions(dimensions); 991 | scalefactor = 2.0f / objAbs(objMax(objMax(dimensions[0], dimensions[1]), dimensions[2])); 992 | 993 | /* do the calculations */ 994 | for(i = 1; i <= m_pModel->nVertices; i++) 995 | { 996 | x = m_pModel->vpVertices[i].x * scalefactor; 997 | y = m_pModel->vpVertices[i].z * scalefactor; 998 | m_pModel->vpTexCoords[i] = CPoint3D((x + 1.0) / 2.0, (y + 1.0) / 2.0, 0.0); 999 | } 1000 | 1001 | /* go through and put texture coordinate indices in all the triangles */ 1002 | group = m_pModel->pGroups; 1003 | while(group) 1004 | { 1005 | for(i = 0; i < group->nTriangles; i++) 1006 | { 1007 | Tri(group->pTriangles[i]).tindices[0] = Tri(group->pTriangles[i]).vindices[0]; 1008 | Tri(group->pTriangles[i]).tindices[1] = Tri(group->pTriangles[i]).vindices[1]; 1009 | Tri(group->pTriangles[i]).tindices[2] = Tri(group->pTriangles[i]).vindices[2]; 1010 | } 1011 | group = group->next; 1012 | } 1013 | } 1014 | 1015 | ////////////////////////////////////////////////////////////////////// 1016 | // SpheremapTexture: Generates texture coordinates according to a 1017 | // spherical projection of the texture map. Sometimes referred to as 1018 | // spheremap, or reflection map texture coordinates. It generates 1019 | // these by using the normal to calculate where that vertex would map 1020 | // onto a sphere. Since it is impossible to map something flat 1021 | // perfectly onto something spherical, there is distortion at the 1022 | // poles. This particular implementation causes the poles along the X 1023 | // axis to be distorted. 1024 | // 1025 | // model - pointer to initialized COBJmodel structure 1026 | ////////////////////////////////////////////////////////////////////// 1027 | void CAccessObj::SpheremapTexture() 1028 | { 1029 | COBJgroup* group; 1030 | float theta, phi, rho, x, y, z, r; 1031 | unsigned int i; 1032 | 1033 | assert(m_pModel); 1034 | assert(m_pModel->vpNormals); 1035 | 1036 | if (m_pModel->vpTexCoords) 1037 | free(m_pModel->vpTexCoords); 1038 | m_pModel->nTexCoords = m_pModel->nNormals; 1039 | m_pModel->vpTexCoords = new CPoint3D [m_pModel->nTexCoords+1]; 1040 | 1041 | for (i = 1; i <= m_pModel->nNormals; i++) 1042 | { 1043 | z = m_pModel->vpNormals[i].x; /* re-arrange for pole distortion */ 1044 | y = m_pModel->vpNormals[i].y; 1045 | x = m_pModel->vpNormals[i].z; 1046 | r = (float)sqrt((x * x) + (y * y)); 1047 | rho = (float)sqrt((r * r) + (z * z)); 1048 | 1049 | if(r == 0.0f) 1050 | { 1051 | theta = 0.0f; 1052 | phi = 0.0f; 1053 | } 1054 | else 1055 | { 1056 | if(z == 0.0f) 1057 | phi = 3.14159265f / 2.0f; 1058 | else phi = (float)acos(z / rho); 1059 | 1060 | if(y == 0.0) 1061 | theta = 3.141592365f / 2.0f; 1062 | else theta = (float)asin(y / r) + (3.14159265f / 2.0f); 1063 | } 1064 | 1065 | m_pModel->vpTexCoords[i] = CPoint3D(theta / 3.14159265f, phi / 3.14159265f, 0.0f); 1066 | } 1067 | 1068 | /* go through and put texcoord indices in all the triangles */ 1069 | group = m_pModel->pGroups; 1070 | while(group) 1071 | { 1072 | for (i = 0; i < group->nTriangles; i++) 1073 | { 1074 | Tri(group->pTriangles[i]).tindices[0] = Tri(group->pTriangles[i]).nindices[0]; 1075 | Tri(group->pTriangles[i]).tindices[1] = Tri(group->pTriangles[i]).nindices[1]; 1076 | Tri(group->pTriangles[i]).tindices[2] = Tri(group->pTriangles[i]).nindices[2]; 1077 | } 1078 | group = group->next; 1079 | } 1080 | } 1081 | 1082 | ////////////////////////////////////////////////////////////////////// 1083 | // objDelete: Deletes a COBJmodel structure. 1084 | // 1085 | // model - initialized COBJmodel structure 1086 | ////////////////////////////////////////////////////////////////////// 1087 | void CAccessObj::Destory() 1088 | { 1089 | delete m_pModel; 1090 | m_pModel = NULL; 1091 | } 1092 | 1093 | ////////////////////////////////////////////////////////////////////// 1094 | // objReadOBJ: Reads a model description from a Wavefront .OBJ file. 1095 | // Returns a pointer to the created object which should be freed with 1096 | // objDelete(). 1097 | // 1098 | // filename - name of the file containing the Wavefront .OBJ format data. 1099 | ////////////////////////////////////////////////////////////////////// 1100 | bool CAccessObj::LoadOBJ(const char* filename) 1101 | { 1102 | FILE* file = NULL; 1103 | 1104 | // open the file 1105 | file = fopen(filename, "r"); 1106 | if (!file) 1107 | { 1108 | fprintf(stderr, "objReadOBJ() failed: can't open data file \"%s\".\n", 1109 | filename); 1110 | exit(1); 1111 | } 1112 | 1113 | // save old model for invalid obj file 1114 | COBJmodel *pOldModel = m_pModel; 1115 | 1116 | // allocate a new model 1117 | m_pModel = new COBJmodel; 1118 | 1119 | sprintf(m_pModel->pathname, "%s", filename); 1120 | m_pModel->mtllibname[0] = '\0'; 1121 | 1122 | m_pModel->nVertices = 0; 1123 | m_pModel->vpVertices = NULL; 1124 | m_pModel->nNormals = 0; 1125 | m_pModel->vpNormals = NULL; 1126 | m_pModel->nTexCoords = 0; 1127 | m_pModel->vpTexCoords = NULL; 1128 | m_pModel->nFacetnorms = 0; 1129 | m_pModel->vpFacetNorms = NULL; 1130 | m_pModel->nTriangles = 0; 1131 | m_pModel->pTriangles = NULL; 1132 | m_pModel->nMaterials = 0; 1133 | m_pModel->pMaterials = NULL; 1134 | m_pModel->nGroups = 0; 1135 | m_pModel->pGroups = NULL; 1136 | m_pModel->position = CPoint3D (0, 0, 0); 1137 | 1138 | // make a first pass through the file to get a count of the number 1139 | // of vertices, normals, texcoords & triangles 1140 | if (FirstPass(file)) 1141 | { 1142 | SAFE_DELETE(pOldModel); 1143 | 1144 | /* allocate memory */ 1145 | m_pModel->vpVertices = new CPoint3D [m_pModel->nVertices + 1]; 1146 | m_pModel->pTriangles = new COBJtriangle [m_pModel->nTriangles]; 1147 | if (m_pModel->nNormals) 1148 | { 1149 | m_pModel->vpNormals = new CPoint3D [m_pModel->nNormals + 1]; 1150 | } 1151 | if (m_pModel->nTexCoords) 1152 | { 1153 | m_pModel->vpTexCoords = new CPoint3D [m_pModel->nTexCoords + 1]; 1154 | } 1155 | 1156 | /* rewind to beginning of file and read in the data this pass */ 1157 | rewind(file); 1158 | SecondPass(file); 1159 | 1160 | // Calc bounding box 1161 | CalcBoundingBox(); 1162 | } 1163 | else 1164 | { 1165 | // restore old model 1166 | if (m_pModel != NULL) Destory(); 1167 | m_pModel = pOldModel; 1168 | } 1169 | 1170 | /* close the file */ 1171 | fclose(file); 1172 | 1173 | return (pOldModel==NULL); 1174 | } 1175 | 1176 | 1177 | ////////////////////////////////////////////////////////////////////// 1178 | // WriteOBJ: Writes a model description in Wavefront .OBJ format to 1179 | // a file. 1180 | // 1181 | // model - initialized COBJmodel structure 1182 | // filename - name of the file to write the Wavefront .OBJ format data to 1183 | // mode - a bitwise or of values describing what is written to the file 1184 | // OBJ_NONE - render with only vertices 1185 | // OBJ_FLAT - render with facet normals 1186 | // OBJ_SMOOTH - render with vertex normals 1187 | // OBJ_TEXTURE - render with texture coords 1188 | // OBJ_COLOR - render with colors (color material) 1189 | // OBJ_MATERIAL - render with materials 1190 | // OBJ_COLOR and OBJ_MATERIAL should not both be specified. 1191 | // OBJ_FLAT and OBJ_SMOOTH should not both be specified. 1192 | ////////////////////////////////////////////////////////////////////// 1193 | void CAccessObj::WriteOBJ(char* filename, unsigned int mode) 1194 | { 1195 | unsigned int i; 1196 | FILE* file; 1197 | COBJgroup* group; 1198 | 1199 | assert(m_pModel); 1200 | 1201 | /* do a bit of warning */ 1202 | if (mode & OBJ_FLAT && !m_pModel->vpFacetNorms) 1203 | { 1204 | printf("WriteOBJ() warning: flat normal output requested " 1205 | "with no facet normals defined.\n"); 1206 | mode &= ~OBJ_FLAT; 1207 | } 1208 | if (mode & OBJ_SMOOTH && !m_pModel->vpNormals) 1209 | { 1210 | printf("WriteOBJ() warning: smooth normal output requested " 1211 | "with no normals defined.\n"); 1212 | mode &= ~OBJ_SMOOTH; 1213 | } 1214 | if (mode & OBJ_TEXTURE && !m_pModel->vpTexCoords) 1215 | { 1216 | printf("WriteOBJ() warning: texture coordinate output requested " 1217 | "with no texture coordinates defined.\n"); 1218 | mode &= ~OBJ_TEXTURE; 1219 | } 1220 | if (mode & OBJ_FLAT && mode & OBJ_SMOOTH) 1221 | { 1222 | printf("WriteOBJ() warning: flat normal output requested " 1223 | "and smooth normal output requested (using smooth).\n"); 1224 | mode &= ~OBJ_FLAT; 1225 | } 1226 | if (mode & OBJ_COLOR && !m_pModel->pMaterials) 1227 | { 1228 | printf("WriteOBJ() warning: color output requested " 1229 | "with no colors (materials) defined.\n"); 1230 | mode &= ~OBJ_COLOR; 1231 | } 1232 | if (mode & OBJ_MATERIAL && !m_pModel->pMaterials) 1233 | { 1234 | printf("WriteOBJ() warning: material output requested " 1235 | "with no materials defined.\n"); 1236 | mode &= ~OBJ_MATERIAL; 1237 | } 1238 | if (mode & OBJ_COLOR && mode & OBJ_MATERIAL) 1239 | { 1240 | printf("WriteOBJ() warning: color and material output requested " 1241 | "outputting only materials.\n"); 1242 | mode &= ~OBJ_COLOR; 1243 | } 1244 | 1245 | 1246 | /* open the file */ 1247 | file = fopen(filename, "w"); 1248 | if (!file) 1249 | { 1250 | fprintf(stderr, "WriteOBJ() failed: can't open file \"%s\" to write.\n", 1251 | filename); 1252 | exit(1); 1253 | } 1254 | 1255 | /* spit out a header */ 1256 | fprintf(file, "# \n"); 1257 | fprintf(file, "# Wavefront OBJ\n"); 1258 | fprintf(file, "# \n"); 1259 | 1260 | if (mode & OBJ_MATERIAL && m_pModel->mtllibname) 1261 | { 1262 | fprintf(file, "\nmtllib %s\n\n", m_pModel->mtllibname); 1263 | WriteMTL(filename, m_pModel->mtllibname); 1264 | } 1265 | 1266 | /* spit out the vertices */ 1267 | fprintf(file, "\n"); 1268 | fprintf(file, "# %d vertices\n", m_pModel->nVertices); 1269 | for (i = 1; i <= m_pModel->nVertices; i++) 1270 | { 1271 | fprintf(file, "v %f %f %f\n", 1272 | m_pModel->vpVertices[i].x, 1273 | m_pModel->vpVertices[i].y, 1274 | m_pModel->vpVertices[i].z); 1275 | } 1276 | 1277 | /* spit out the smooth/flat normals */ 1278 | if (mode & OBJ_SMOOTH) 1279 | { 1280 | fprintf(file, "\n"); 1281 | fprintf(file, "# %d normals\n", m_pModel->nNormals); 1282 | for (i = 1; i <= m_pModel->nNormals; i++) 1283 | { 1284 | fprintf(file, "vn %f %f %f\n", 1285 | m_pModel->vpNormals[i].x, 1286 | m_pModel->vpNormals[i].y, 1287 | m_pModel->vpNormals[i].z); 1288 | } 1289 | } 1290 | else if (mode & OBJ_FLAT) 1291 | { 1292 | fprintf(file, "\n"); 1293 | fprintf(file, "# %d normals\n", m_pModel->nFacetnorms); 1294 | for (i = 1; i <= m_pModel->nNormals; i++) 1295 | { 1296 | fprintf(file, "vn %f %f %f\n", 1297 | m_pModel->vpFacetNorms[i].x, 1298 | m_pModel->vpFacetNorms[i].y, 1299 | m_pModel->vpFacetNorms[i].z); 1300 | } 1301 | } 1302 | 1303 | /* spit out the texture coordinates */ 1304 | if (mode & OBJ_TEXTURE) 1305 | { 1306 | fprintf(file, "\n"); 1307 | fprintf(file, "# %d texcoords\n", m_pModel->vpTexCoords); 1308 | for (i = 1; i <= m_pModel->nTexCoords; i++) 1309 | { 1310 | fprintf(file, "vt %f %f\n", 1311 | m_pModel->vpTexCoords[i].x, 1312 | m_pModel->vpTexCoords[i].y); 1313 | } 1314 | } 1315 | 1316 | fprintf(file, "\n"); 1317 | fprintf(file, "# %d groups\n", m_pModel->nGroups); 1318 | fprintf(file, "# %d faces (triangles)\n", m_pModel->nTriangles); 1319 | fprintf(file, "\n"); 1320 | 1321 | group = m_pModel->pGroups; 1322 | while(group) 1323 | { 1324 | fprintf(file, "g %s\n", group->name); 1325 | if (mode & OBJ_MATERIAL) 1326 | fprintf(file, "usemtl %s\n", m_pModel->pMaterials[group->material].name); 1327 | for (i = 0; i < group->nTriangles; i++) 1328 | { 1329 | if (mode & OBJ_SMOOTH && mode & OBJ_TEXTURE) 1330 | { 1331 | fprintf(file, "f %d/%d/%d %d/%d/%d %d/%d/%d\n", 1332 | Tri(group->pTriangles[i]).vindices[0], 1333 | Tri(group->pTriangles[i]).nindices[0], 1334 | Tri(group->pTriangles[i]).tindices[0], 1335 | Tri(group->pTriangles[i]).vindices[1], 1336 | Tri(group->pTriangles[i]).nindices[1], 1337 | Tri(group->pTriangles[i]).tindices[1], 1338 | Tri(group->pTriangles[i]).vindices[2], 1339 | Tri(group->pTriangles[i]).nindices[2], 1340 | Tri(group->pTriangles[i]).tindices[2]); 1341 | } 1342 | else if (mode & OBJ_FLAT && mode & OBJ_TEXTURE) 1343 | { 1344 | fprintf(file, "f %d/%d %d/%d %d/%d\n", 1345 | Tri(group->pTriangles[i]).vindices[0], 1346 | Tri(group->pTriangles[i]).findex, 1347 | Tri(group->pTriangles[i]).vindices[1], 1348 | Tri(group->pTriangles[i]).findex, 1349 | Tri(group->pTriangles[i]).vindices[2], 1350 | Tri(group->pTriangles[i]).findex); 1351 | } 1352 | else if (mode & OBJ_TEXTURE) 1353 | { 1354 | fprintf(file, "f %d/%d %d/%d %d/%d\n", 1355 | Tri(group->pTriangles[i]).vindices[0], 1356 | Tri(group->pTriangles[i]).tindices[0], 1357 | Tri(group->pTriangles[i]).vindices[1], 1358 | Tri(group->pTriangles[i]).tindices[1], 1359 | Tri(group->pTriangles[i]).vindices[2], 1360 | Tri(group->pTriangles[i]).tindices[2]); 1361 | } 1362 | else if (mode & OBJ_SMOOTH) 1363 | { 1364 | fprintf(file, "f %d//%d %d//%d %d//%d\n", 1365 | Tri(group->pTriangles[i]).vindices[0], 1366 | Tri(group->pTriangles[i]).nindices[0], 1367 | Tri(group->pTriangles[i]).vindices[1], 1368 | Tri(group->pTriangles[i]).nindices[1], 1369 | Tri(group->pTriangles[i]).vindices[2], 1370 | Tri(group->pTriangles[i]).nindices[2]); 1371 | } 1372 | else if (mode & OBJ_FLAT) 1373 | { 1374 | fprintf(file, "f %d//%d %d//%d %d//%d\n", 1375 | Tri(group->pTriangles[i]).vindices[0], 1376 | Tri(group->pTriangles[i]).findex, 1377 | Tri(group->pTriangles[i]).vindices[1], 1378 | Tri(group->pTriangles[i]).findex, 1379 | Tri(group->pTriangles[i]).vindices[2], 1380 | Tri(group->pTriangles[i]).findex); 1381 | } 1382 | else 1383 | { 1384 | fprintf(file, "f %d %d %d\n", 1385 | Tri(group->pTriangles[i]).vindices[0], 1386 | Tri(group->pTriangles[i]).vindices[1], 1387 | Tri(group->pTriangles[i]).vindices[2]); 1388 | } 1389 | } 1390 | fprintf(file, "\n"); 1391 | group = group->next; 1392 | } 1393 | 1394 | fclose(file); 1395 | } 1396 | 1397 | void CAccessObj::Boundingbox(CPoint3D &vMax, CPoint3D &vMin) 1398 | { 1399 | vMax = m_vMax; 1400 | vMin = m_vMin; 1401 | } 1402 | 1403 | void CAccessObj::CalcBoundingBox() 1404 | { 1405 | unsigned int i; 1406 | 1407 | m_vMax = m_vMin = m_pModel->vpVertices[1]; 1408 | for (i = 1; i <= m_pModel->nVertices; i++) 1409 | { 1410 | if (m_vMax.x < m_pModel->vpVertices[i].x) 1411 | m_vMax.x = m_pModel->vpVertices[i].x; 1412 | if (m_vMin.x > m_pModel->vpVertices[i].x) 1413 | m_vMin.x = m_pModel->vpVertices[i].x; 1414 | 1415 | if (m_vMax.y < m_pModel->vpVertices[i].y) 1416 | m_vMax.y = m_pModel->vpVertices[i].y; 1417 | if (m_vMin.y > m_pModel->vpVertices[i].y) 1418 | m_vMin.y = m_pModel->vpVertices[i].y; 1419 | 1420 | if (m_vMax.z < m_pModel->vpVertices[i].z) 1421 | m_vMax.z = m_pModel->vpVertices[i].z; 1422 | if (m_vMin.z > m_pModel->vpVertices[i].z) 1423 | m_vMin.z = m_pModel->vpVertices[i].z; 1424 | } 1425 | 1426 | CPoint3D vCent = (m_vMax + m_vMin)*0.5f; 1427 | 1428 | for (i = 1; i <= m_pModel->nVertices; i++) 1429 | m_pModel->vpVertices[i] = m_pModel->vpVertices[i] - vCent; 1430 | 1431 | m_vMax = m_vMax - vCent; 1432 | m_vMin = m_vMin -vCent; 1433 | } 1434 | 1435 | ////////////////////////////////////////////////////////////////////////// 1436 | // Unified the model to center 1437 | ////////////////////////////////////////////////////////////////////////// 1438 | void CAccessObj::UnifiedModel() 1439 | { 1440 | if (m_pModel==NULL) return; 1441 | 1442 | //CPoint3D vDiameter = m_vMax - m_vMin; 1443 | //float radius = vDiameter.length() * 0.4f / 1.414f; 1444 | //Scale(1.0f/radius); 1445 | CalcBoundingBox(); 1446 | if (m_pModel->nNormals==0) 1447 | { 1448 | FacetNormals(); 1449 | VertexNormals(90.f); 1450 | } 1451 | } 1452 | 1453 | } // namespace trimeshVec --------------------------------------------------------------------------------