├── CMakeLists.txt ├── README.md ├── media ├── models │ ├── leaves.obj │ └── trunk.obj ├── screenshot_001.png ├── screenshot_002.png └── textures │ ├── entities.png │ ├── gcanyon.dds │ ├── gcanyon.png │ └── gcanyond.png ├── nvImage ├── CMakeLists.txt ├── nvImage.cpp ├── nvImage.h ├── nvImageDDS.cpp ├── nvImageHdr.cpp ├── nvImagePng.cpp ├── rgbe.c └── rgbe.h ├── nvModel ├── CMakeLists.txt ├── Makefile ├── nvMath.h ├── nvMatrix.h ├── nvModel.cc ├── nvModel.h ├── nvModelObj.cc ├── nvModelQuery.cc ├── nvQuaternion.h ├── nvSDKPath.h ├── nvShaderUtils.h ├── nvUtils.cc ├── nvUtils.h └── nvVector.h └── src ├── GLSL ├── shadow_multi_leak_fragment.glsl ├── shadow_multi_noleak_fragment.glsl ├── shadow_pcf.glsl ├── shadow_pcf_4tap_fragment.glsl ├── shadow_pcf_8tap_random_fragment.glsl ├── shadow_pcf_fragment.glsl ├── shadow_pcf_gaussian_fragment.glsl ├── shadow_pcf_trilinear_fragment.glsl ├── shadow_single_fragment.glsl ├── shadow_single_hl_fragment.glsl ├── shadow_vertex.glsl ├── view_fragment.glsl ├── view_vertex.glsl ├── write_depth_fragment.glsl └── write_depth_vertex.glsl ├── camera.cpp ├── camera.hpp ├── cascaded_shadow_maps.cpp ├── frustum.cpp ├── frustum.hpp ├── main.h ├── math.hpp ├── shadow_map.cpp ├── shadow_map.hpp ├── terrain.cpp ├── terrain.h └── utility.cpp /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6 FATAL_ERROR) 2 | 3 | #link_directories (/usr/lib) 4 | #include_directories (/usr/include) 5 | 6 | IF(APPLE) 7 | FIND_LIBRARY(GLUT_LIBRARY GLUT) 8 | FIND_LIBRARY(OpenGL_LIBRARY OpenGL) 9 | find_package(PNG REQUIRED) 10 | include_directories(${PNG_INCLUDE_DIR}) 11 | include_directories(/usr/X11R6/include/) 12 | link_directories(/usr/X11R6/lib) 13 | set(EXT_LIBRARIES ${OpenGL_LIBRARY} GLEW GLU ${GLUT_LIBRARY} ${PNG_LIBRARY}) 14 | ELSE() 15 | set(EXT_LIBRARIES GL GLEW GLU glut png) 16 | ENDIF() 17 | 18 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/nvModel) 19 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/nvImage) 20 | 21 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src) 22 | 23 | add_subdirectory(nvModel) 24 | add_subdirectory(nvImage) 25 | 26 | add_executable ( 27 | csm_demo_glm 28 | src/cascaded_shadow_maps.cpp 29 | src/terrain.cpp 30 | src/utility.cpp 31 | src/camera.cpp 32 | src/frustum.cpp 33 | src/shadow_map.cpp 34 | ) 35 | 36 | target_link_libraries ( 37 | csm_demo_glm 38 | nvimage_static 39 | nvmodel_static 40 | ${EXT_LIBRARIES} 41 | ) 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Nvidia Cascaded Shadow Maps with shaders and GLM 2 | Modification of the Nvidia Cascading Shadow Maps demo using a pure shader based approach over immediate mode. This project is useful for anyone interested in learning more about cascaded shadow mapping techniques. 3 | 4 | ![Screenshot](media/screenshot_001.png "Screenshot") 5 | 6 | In the lower left hand corner of the screenshot you can see the depth textures of the shadow map. 7 | 8 | ## What does it do and how does it work? 9 | Shadow mapping is a technique where the scene is rendered to a depth texture from the point of view of a light source, and this depth texture is again sampled during normal scene rendering to create the illusion of shadows, or "non-lit" fragments. While rendering large scenes and using only one shadow map, the shadow map resolution will become an issue where the shadows become more pixelated as the view distance increases. In modern real-time graphics rendering this can be solved by using a technique called "Cascaded Shadow Mapping", where N shadow maps are rendered for each light source, each covering a larger area of the frustum the further away from the camera it is. 10 | 11 | Here we can see the camera frustum from above, and how the shadow map splits are distributed along the frustom (showing four splits) 12 | ![CSM Splits as seen from above](media/screenshot_002.png "Screenshot") 13 | 14 | 15 | Read more about (cascaded) shadow mapping here: https://en.wikipedia.org/wiki/Shadow_mapping 16 | 17 | ## Legal Disclamer 18 | The original code was not written by me, and the original copyright notices have been kept in place where applicable. 19 | The original whitepaper and code can be found here: 20 | * http://developer.download.nvidia.com/SDK/10.5/opengl/screenshots/samples/cascaded_shadow_maps.html 21 | * http://developer.download.nvidia.com/SDK/10.5/opengl/src/cascaded_shadow_maps/doc/cascaded_shadow_maps.pdf 22 | * http://developer.download.nvidia.com/SDK/10.5/Samples/cascaded_shadow_maps.zip 23 | 24 | ## Dependencies 25 | * GLUT (https://www.opengl.org/resources/libraries/glut/) 26 | * libpng (http://www.libpng.org/pub/png/libpng.html) 27 | * GLEW (http://glew.sourceforge.net/) 28 | * gml (http://glm.g-truc.net/0.9.7/index.html) 29 | 30 | ## Building 31 | The project uses CMake to generate build files for any platform (currently only tested on Linux and OS X). 32 | 33 | cd NvidiaCascadedShadowMapsGLM 34 | mkdir build && cd build 35 | cmake -G "Unix Makefiles" .. # NOTE: Replace "Unix Makefiles" with your platform / build tool of choice 36 | make 37 | ./csm_demo_glm 38 | 39 | -------------------------------------------------------------------------------- /media/screenshot_001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GKR/NvidiaCascadedShadowMapsGLM/ac6fdc9b22a3233038b77981490f904fef5527f5/media/screenshot_001.png -------------------------------------------------------------------------------- /media/screenshot_002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GKR/NvidiaCascadedShadowMapsGLM/ac6fdc9b22a3233038b77981490f904fef5527f5/media/screenshot_002.png -------------------------------------------------------------------------------- /media/textures/entities.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GKR/NvidiaCascadedShadowMapsGLM/ac6fdc9b22a3233038b77981490f904fef5527f5/media/textures/entities.png -------------------------------------------------------------------------------- /media/textures/gcanyon.dds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GKR/NvidiaCascadedShadowMapsGLM/ac6fdc9b22a3233038b77981490f904fef5527f5/media/textures/gcanyon.dds -------------------------------------------------------------------------------- /media/textures/gcanyon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GKR/NvidiaCascadedShadowMapsGLM/ac6fdc9b22a3233038b77981490f904fef5527f5/media/textures/gcanyon.png -------------------------------------------------------------------------------- /media/textures/gcanyond.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GKR/NvidiaCascadedShadowMapsGLM/ac6fdc9b22a3233038b77981490f904fef5527f5/media/textures/gcanyond.png -------------------------------------------------------------------------------- /nvImage/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6 FATAL_ERROR) 2 | 3 | set( 4 | LIBNVIMAGE_SRC 5 | nvImage.cpp 6 | nvImageDDS.cpp 7 | nvImageHdr.cpp 8 | nvImagePng.cpp 9 | rgbe.c 10 | ) 11 | 12 | #set(LIBRARY_OUTPUT_PATH ../bin_test/nvmodel) 13 | #add_library(nvmodel SHARED ${LIBNVMODEL_SRC}) 14 | 15 | add_library(nvimage_static STATIC ${LIBNVIMAGE_SRC}) 16 | set_target_properties(nvimage_static PROPERTIES OUTPUT_NAME "nvimage") 17 | 18 | #set_target_properties(nvmodel_static PROPERTIES CLEAN_DIRECT_OUTPUT 1) 19 | #set_target_properties(nvmodel PROPERTIES CLEAN_DIRECT_OUTPUT 1) 20 | 21 | -------------------------------------------------------------------------------- /nvImage/nvImage.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // nvImage.cpp - Image support class 3 | // 4 | // The nvImage class implements an interface for a multipurpose image 5 | // object. This class is useful for loading and formating images 6 | // for use as textures. The class supports dds, png, and hdr formats. 7 | // 8 | // This file implements the format independent interface. 9 | // 10 | // Author: Evan Hart 11 | // Email: sdkfeedback@nvidia.com 12 | // 13 | // Copyright (c) NVIDIA Corporation. All rights reserved. 14 | //////////////////////////////////////////////////////////////////////////////// 15 | 16 | #include 17 | #include 18 | 19 | #include "nvImage.h" 20 | 21 | using std::vector; 22 | using std::max; 23 | 24 | #ifdef WIN32 25 | #define strcasecmp _stricmp 26 | #endif 27 | 28 | namespace nv { 29 | 30 | Image::FormatInfo Image::formatTable[] = { 31 | { "png", Image::readPng, Image::writePng}, 32 | { "dds", Image::readDDS, 0}, 33 | { "hdr", Image::readHdr, 0} 34 | }; 35 | 36 | 37 | // 38 | // 39 | //////////////////////////////////////////////////////////// 40 | Image::Image() : _width(0), _height(0), _depth(0), _levelCount(0), _faces(0), _format(GL_RGBA), 41 | _internalFormat(GL_RGBA8), _type(GL_UNSIGNED_BYTE), _elementSize(0) { 42 | } 43 | 44 | // 45 | // 46 | //////////////////////////////////////////////////////////// 47 | Image::~Image() { 48 | freeData(); 49 | } 50 | 51 | // 52 | // 53 | //////////////////////////////////////////////////////////// 54 | void Image::freeData() { 55 | for (vector::iterator it = _data.begin(); it != _data.end(); it++) { 56 | delete []*it; 57 | } 58 | _data.clear(); 59 | } 60 | 61 | // 62 | // 63 | //////////////////////////////////////////////////////////// 64 | int Image::getImageSize( int level) const { 65 | bool compressed = isCompressed(); 66 | int w = _width >> level; 67 | int h = _height >> level; 68 | int d = _depth >> level; 69 | w = (w) ? w : 1; 70 | h = (h) ? h : 1; 71 | d = (d) ? d : 1; 72 | int bw = (compressed) ? ( w + 3 ) / 4 : w; 73 | int bh = (compressed) ? ( h + 3 ) / 4 : h; 74 | int elementSize = _elementSize; 75 | 76 | return bw*bh*d*elementSize; 77 | } 78 | 79 | 80 | // 81 | // 82 | //////////////////////////////////////////////////////////// 83 | const void* Image::getLevel( int level, GLenum face) const { 84 | assert( level < _levelCount); 85 | assert( _faces == 0 || ( face >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z)); 86 | 87 | face = face - GL_TEXTURE_CUBE_MAP_POSITIVE_X; 88 | 89 | assert( (face*_levelCount + level) < (int)_data.size()); 90 | return _data[ face*_levelCount + level]; 91 | } 92 | 93 | // 94 | // 95 | //////////////////////////////////////////////////////////// 96 | void* Image::getLevel( int level, GLenum face) { 97 | assert( level < _levelCount); 98 | assert( _faces == 0 || ( face >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z)); 99 | 100 | face = face - GL_TEXTURE_CUBE_MAP_POSITIVE_X; 101 | 102 | assert( (face*_levelCount + level) < (int)_data.size()); 103 | return _data[ face*_levelCount + level]; 104 | } 105 | 106 | // 107 | // 108 | //////////////////////////////////////////////////////////// 109 | bool Image::loadImageFromFile( const char* file) { 110 | const char* extension; 111 | extension = strrchr( file, '.'); 112 | 113 | if (extension) 114 | extension++; //start looking after the . 115 | else 116 | return false; 117 | 118 | int formatCount = sizeof(Image::formatTable) / sizeof(Image::FormatInfo); 119 | 120 | //try to match by format first 121 | for ( int ii = 0; ii < formatCount; ii++) { 122 | if ( ! strcasecmp( formatTable[ii].extension, extension)) { 123 | //extension matches, load it 124 | return formatTable[ii].reader( file, *this); 125 | } 126 | } 127 | 128 | 129 | return false; 130 | } 131 | 132 | // 133 | // 134 | //////////////////////////////////////////////////////////// 135 | void Image::flipSurface(GLubyte *surf, int width, int height, int depth) 136 | { 137 | unsigned int lineSize; 138 | 139 | depth = (depth) ? depth : 1; 140 | 141 | if (!isCompressed()) { 142 | lineSize = _elementSize * width; 143 | unsigned int sliceSize = lineSize * height; 144 | 145 | GLubyte *tempBuf = new GLubyte[lineSize]; 146 | 147 | for ( int ii = 0; ii < depth; ii++) { 148 | GLubyte *top = surf + ii*sliceSize; 149 | GLubyte *bottom = top + (sliceSize - lineSize); 150 | 151 | for ( int jj = 0; jj < (height >> 1); jj++) { 152 | memcpy( tempBuf, top, lineSize); 153 | memcpy( top, bottom, lineSize); 154 | memcpy( bottom, tempBuf, lineSize); 155 | 156 | top += lineSize; 157 | bottom -= lineSize; 158 | } 159 | } 160 | 161 | delete []tempBuf; 162 | } 163 | else 164 | { 165 | void (*flipblocks)(GLubyte*, unsigned int); 166 | width = (width + 3) / 4; 167 | height = (height + 3) / 4; 168 | unsigned int blockSize = 0; 169 | 170 | switch (_format) 171 | { 172 | case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: 173 | blockSize = 8; 174 | flipblocks = &Image::flip_blocks_dxtc1; 175 | break; 176 | case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: 177 | blockSize = 16; 178 | flipblocks = &Image::flip_blocks_dxtc3; 179 | break; 180 | case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: 181 | blockSize = 16; 182 | flipblocks = &Image::flip_blocks_dxtc5; 183 | break; 184 | default: 185 | return; 186 | } 187 | 188 | lineSize = width * blockSize; 189 | GLubyte *tempBuf = new GLubyte[lineSize]; 190 | 191 | GLubyte *top = surf; 192 | GLubyte *bottom = surf + (height-1) * lineSize; 193 | 194 | for (unsigned int j = 0; j < max( (unsigned int)height >> 1, (unsigned int)1); j++) 195 | { 196 | if (top == bottom) 197 | { 198 | flipblocks(top, width); 199 | break; 200 | } 201 | 202 | flipblocks(top, width); 203 | flipblocks(bottom, width); 204 | 205 | memcpy( tempBuf, top, lineSize); 206 | memcpy( top, bottom, lineSize); 207 | memcpy( bottom, tempBuf, lineSize); 208 | 209 | top += lineSize; 210 | bottom -= lineSize; 211 | } 212 | delete []tempBuf; 213 | } 214 | } 215 | 216 | // 217 | // 218 | //////////////////////////////////////////////////////////// 219 | bool Image::convertCrossToCubemap() { 220 | //can't already be a cubemap 221 | if (isCubeMap()) 222 | return false; 223 | 224 | //mipmaps are not supported 225 | if (_levelCount != 1) 226 | return false; 227 | 228 | //compressed textures are not supported 229 | if (isCompressed()) 230 | return false; 231 | 232 | //this function only supports vertical cross format for now (3 wide by 4 high) 233 | if ( (_width / 3 != _height / 4) || (_width % 3 != 0) || (_height % 4 != 0) || (_depth != 0)) 234 | return false; 235 | 236 | //get the source data 237 | GLubyte *data = _data[0]; 238 | 239 | int fWidth = _width / 3; 240 | int fHeight = _height / 4; 241 | 242 | //remove the old pointer from the vector 243 | _data.pop_back(); 244 | 245 | GLubyte *face = new GLubyte[ fWidth * fHeight * _elementSize]; 246 | GLubyte *ptr; 247 | 248 | //extract the faces 249 | 250 | // positive X 251 | ptr = face; 252 | for (int j=0; j 19 | #include 20 | 21 | #define GLEW_STATIC 22 | #include 23 | 24 | namespace nv { 25 | 26 | class Image { 27 | public: 28 | 29 | Image(); 30 | virtual ~Image(); 31 | 32 | // return the width of the image 33 | int getWidth() const { return _width; } 34 | 35 | //return the height of the image 36 | int getHeight() const { return _height; } 37 | 38 | //return the dpeth of the image (0 for images with no depth) 39 | int getDepth() const { return _depth; } 40 | 41 | //return the number of mipmap levels available for the image 42 | int getMipLevels() const { return _levelCount; } 43 | 44 | //return the number of cubemap faces available for the image (0 for non-cubemap images) 45 | int getFaces() const { return _faces; } 46 | 47 | //return the format of the image data (GL_RGB, GL_BGR, etc) 48 | GLenum getFormat() const { return _format; } 49 | 50 | //return the suggested internal format for the data 51 | GLenum getInternalFormat() const { return _internalFormat; } 52 | 53 | //return the type of the image data 54 | GLenum getType() const { return _type; } 55 | 56 | //return the Size in bytes of a level of the image 57 | int getImageSize(int level = 0) const; 58 | 59 | //return whether the data is a crompressed format 60 | bool isCompressed() const { 61 | switch(_format) { 62 | case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: 63 | case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: 64 | case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: 65 | case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: 66 | case GL_COMPRESSED_LUMINANCE_LATC1_EXT: 67 | case GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT: 68 | case GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT: 69 | case GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT: 70 | return true; 71 | } 72 | return false; 73 | } 74 | 75 | //return whether the image represents a cubemap 76 | bool isCubeMap() const { return _faces > 0; } 77 | 78 | //return whether the image represents a volume 79 | bool isVolume() const { return _depth > 0; } 80 | 81 | //get a pointer to level data 82 | const void* getLevel( int level, GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X) const; 83 | void* getLevel( int level, GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X); 84 | 85 | //initialize an image from a file 86 | bool loadImageFromFile( const char* file); 87 | 88 | //convert a suitable image from a cubemap cross to a cubemap (returns false for unsuitable images) 89 | bool convertCrossToCubemap(); 90 | 91 | //load an image from memory, for the purposes of saving 92 | bool setImage( int width, int height, GLenum format, GLenum type, const void* data); 93 | 94 | //save an image to a file 95 | bool saveImageToFile( const char* file); 96 | 97 | protected: 98 | int _width; 99 | int _height; 100 | int _depth; 101 | int _levelCount; 102 | int _faces; 103 | GLenum _format; 104 | GLenum _internalFormat; 105 | GLenum _type; 106 | int _elementSize; 107 | 108 | //pointers to the levels 109 | std::vector _data; 110 | 111 | void freeData(); 112 | void flipSurface(GLubyte *surf, int width, int height, int depth); 113 | 114 | 115 | // 116 | // Static elements used to dispatch to proper sub-readers 117 | // 118 | ////////////////////////////////////////////////////////////// 119 | struct FormatInfo { 120 | const char* extension; 121 | bool (*reader)( const char* file, Image& i); 122 | bool (*writer)( const char* file, Image& i); 123 | }; 124 | 125 | static FormatInfo formatTable[]; 126 | 127 | static bool readPng( const char *file, Image& i); 128 | static bool readDDS( const char *file, Image& i); 129 | static bool readHdr( const char *file, Image& i); 130 | 131 | static bool writePng( const char *file, Image& i); 132 | //static bool writeDDS( const char *file, Image& i); 133 | //static bool writeHdr( const char *file, Image& i); 134 | 135 | static void flip_blocks_dxtc1(GLubyte *ptr, unsigned int numBlocks); 136 | static void flip_blocks_dxtc3(GLubyte *ptr, unsigned int numBlocks); 137 | static void flip_blocks_dxtc5(GLubyte *ptr, unsigned int numBlocks); 138 | }; 139 | }; 140 | 141 | #endif //NV_IMAGE_H -------------------------------------------------------------------------------- /nvImage/nvImageHdr.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // nvImageHdr.cpp - Image support class 3 | // 4 | // The nvImage class implements an interface for a multipurpose image 5 | // object. This class is useful for loading and formating images 6 | // for use as textures. The class supports dds, png, and hdr formats. 7 | // 8 | // This file implements the HDR specific functionality. 9 | // 10 | // Author: Evan Hart 11 | // Email: sdkfeedback@nvidia.com 12 | // 13 | // Copyright (c) NVIDIA Corporation. All rights reserved. 14 | //////////////////////////////////////////////////////////////////////////////// 15 | 16 | #include "rgbe.h" 17 | 18 | #include "nvImage.h" 19 | 20 | using std::vector; 21 | 22 | namespace nv { 23 | 24 | // 25 | // readHdr 26 | // 27 | // Image loader function for hdr files. 28 | //////////////////////////////////////////////////////////// 29 | bool Image::readHdr( const char *file, Image& i) { 30 | int width, height; 31 | FILE *fp = fopen(file, "rb"); 32 | if (!fp) { 33 | return false; 34 | } 35 | 36 | rgbe_header_info header; 37 | 38 | if (RGBE_ReadHeader( fp, &width, &height, &header)) { 39 | fclose(fp); 40 | return false; 41 | } 42 | 43 | GLubyte *data = (GLubyte*)new float[width*height*3]; 44 | 45 | if (!data) { 46 | fclose(fp); 47 | return false; 48 | } 49 | 50 | if (RGBE_ReadPixels_RLE( fp, (float*)data, width, height)) { 51 | delete []data; 52 | fclose(fp); 53 | return false; 54 | } 55 | 56 | //set all the parameters 57 | i._width = width; 58 | i._height = height; 59 | i._depth = 0; 60 | i._levelCount = 1; 61 | i._type = GL_FLOAT; 62 | i._format = GL_RGB; 63 | i._internalFormat = GL_RGB32F_ARB; 64 | i._faces = 0; 65 | i._elementSize = 12; 66 | i._data.push_back( data); 67 | 68 | //hdr images come in upside down 69 | i.flipSurface( data, i._width, i._height, i._depth); 70 | 71 | fclose(fp); 72 | 73 | return true; 74 | } 75 | 76 | }; 77 | -------------------------------------------------------------------------------- /nvImage/nvImagePng.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // nvImagePng.cpp - Image support class 3 | // 4 | // The nvImage class implements an interface for a multipurpose image 5 | // object. This class is useful for loading and formating images 6 | // for use as textures. The class supports dds, png, and hdr formats. 7 | // 8 | // This file implements the PNG specific functionality. 9 | // 10 | // Author: Evan Hart 11 | // Email: sdkfeedback@nvidia.com 12 | // 13 | // Copyright (c) NVIDIA Corporation. All rights reserved. 14 | //////////////////////////////////////////////////////////////////////////////// 15 | 16 | #include 17 | 18 | #include "nvImage.h" 19 | 20 | using std::vector; 21 | 22 | namespace nv { 23 | 24 | 25 | // 26 | // 27 | //////////////////////////////////////////////////////////// 28 | void pngReadFn( png_structp png_ptr, png_bytep data, png_size_t length) { 29 | FILE* fp = (FILE*)png_get_io_ptr(png_ptr); 30 | 31 | if (!data) 32 | png_error( png_ptr, "Attempt to read from null file pointer"); 33 | 34 | fread( data, length, 1, fp); 35 | } 36 | 37 | // 38 | // 39 | //////////////////////////////////////////////////////////// 40 | void pngWriteFn( png_structp png_ptr, png_bytep data, png_size_t length) { 41 | FILE* fp = (FILE*)png_get_io_ptr(png_ptr); 42 | 43 | if (!data) 44 | png_error( png_ptr, "Attempt to write to null file pointer"); 45 | 46 | fwrite( data, length, 1, fp); 47 | } 48 | 49 | // 50 | // 51 | //////////////////////////////////////////////////////////// 52 | void pngFlushFn( png_structp png_ptr) { 53 | FILE* fp = (FILE*)png_get_io_ptr(png_ptr); 54 | 55 | fflush(fp); 56 | } 57 | 58 | // 59 | // readPng 60 | // 61 | // Image loader function for png files. The code is heavily 62 | // based on the example png loader code distributed with libPNG 63 | //////////////////////////////////////////////////////////// 64 | bool Image::readPng( const char *file, Image& i) { 65 | FILE *fp = fopen( file, "rb"); 66 | 67 | if ( !fp) 68 | return false; 69 | 70 | GLubyte signature[8]; 71 | fread( signature, 8, 1, fp); 72 | 73 | png_structp png_ptr = NULL; 74 | png_infop info_ptr = NULL; 75 | 76 | if (!png_check_sig( signature, 8)) { 77 | fclose(fp); 78 | return false; 79 | } 80 | 81 | png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); 82 | if (!png_ptr) { 83 | fclose(fp); 84 | return false; /* out of memory */ 85 | } 86 | 87 | info_ptr = png_create_info_struct(png_ptr); 88 | if (!info_ptr) { 89 | png_destroy_read_struct(&png_ptr, NULL, NULL); 90 | fclose(fp); 91 | return false; /* out of memory */ 92 | } 93 | 94 | // setjmp() is used for error handling with libPNG, if something goes wrong it is coming back here 95 | 96 | if (setjmp(png_jmpbuf(png_ptr))) { 97 | png_destroy_read_struct(&png_ptr, &info_ptr, NULL); 98 | fclose(fp); 99 | return false; 100 | } 101 | 102 | // Need to override the standard I/O methods since libPNG may be linked against a different run-time 103 | png_set_read_fn( png_ptr, fp, pngReadFn); 104 | 105 | png_set_sig_bytes(png_ptr, 8); // skip the sig bytes 106 | 107 | png_read_info(png_ptr, info_ptr); // automagically read everything to the image data 108 | 109 | i._width = png_get_image_width(png_ptr, info_ptr); 110 | i._height = png_get_image_height(png_ptr, info_ptr); 111 | i._depth = 0; // using the convention of depth == 0 for 2D images 112 | int colorType = png_get_color_type( png_ptr, info_ptr); 113 | int bitDepth = png_get_bit_depth( png_ptr, info_ptr); 114 | 115 | if (setjmp(png_jmpbuf(png_ptr))) { 116 | png_destroy_read_struct(&png_ptr, &info_ptr, NULL); 117 | fclose(fp); 118 | return false; 119 | } 120 | 121 | //Setup the read transforms 122 | // expand palette images to RGB and low-bit-depth grayscale images to 8 bits 123 | // convert transparency chunks to full alpha channel 124 | if (colorType == PNG_COLOR_TYPE_PALETTE) { 125 | png_set_palette_to_rgb(png_ptr); 126 | } 127 | if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) { 128 | //png_set_gray_1_2_4_to_8(png_ptr); 129 | png_set_expand_gray_1_2_4_to_8(png_ptr); 130 | } 131 | if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { 132 | png_set_tRNS_to_alpha(png_ptr); 133 | } 134 | 135 | // now configure for reading, and allocate the memory 136 | png_read_update_info(png_ptr, info_ptr); 137 | 138 | int rowBytes = png_get_rowbytes(png_ptr, info_ptr); 139 | 140 | GLubyte *data = new GLubyte[rowBytes * i._height]; 141 | 142 | GLubyte **rowPointers = new GLubyte*[i._height]; 143 | 144 | // set up the row pointers 145 | for ( int ii = 0; ii < i._height; ii++) { 146 | rowPointers[ii] = data + ii*rowBytes; 147 | } 148 | 149 | // read the image 150 | png_read_image(png_ptr, rowPointers); 151 | 152 | 153 | // reading is complete, configure other parameters 154 | 155 | delete []rowPointers; 156 | bool use16 = bitDepth > 8; 157 | 158 | i._type = (use16) ? GL_UNSIGNED_SHORT : GL_UNSIGNED_BYTE; 159 | 160 | switch ((int)png_get_channels(png_ptr, info_ptr)) { 161 | case 1: 162 | i._format = GL_LUMINANCE; 163 | i._internalFormat = (use16) ? GL_LUMINANCE16 : GL_LUMINANCE8; 164 | i._elementSize = (use16) ? 2 : 1; 165 | break; 166 | case 2: 167 | i._format = GL_LUMINANCE_ALPHA; 168 | i._internalFormat = (use16) ? GL_LUMINANCE16_ALPHA16 : GL_LUMINANCE8_ALPHA8; 169 | i._elementSize = (use16) ? 4 : 2; 170 | break; 171 | case 3: 172 | i._format = GL_RGB; 173 | i._internalFormat = (use16) ? GL_RGB16 : GL_RGB8; 174 | i._elementSize = (use16) ? 6 : 3; 175 | break; 176 | case 4: 177 | i._format = GL_RGBA; 178 | i._internalFormat = (use16) ? GL_RGBA16 : GL_RGBA8; 179 | i._elementSize = (use16) ? 8 : 4; 180 | break; 181 | } 182 | 183 | png_read_end(png_ptr, NULL); 184 | png_destroy_read_struct(&png_ptr, &info_ptr, NULL); 185 | fclose(fp); 186 | 187 | //finalize parameters 188 | i._data.push_back( data); 189 | i._levelCount = 1; 190 | i._faces = 0; 191 | i._depth = 0; 192 | i.flipSurface( data, i._width, i._height, i._depth); 193 | 194 | return true; 195 | 196 | } 197 | 198 | // 199 | // writePng 200 | // 201 | // Image saver function for png files. The code is heavily 202 | // based on the example png save code distributed with libPNG 203 | //////////////////////////////////////////////////////////// 204 | bool Image::writePng( const char *file, Image& i) { 205 | 206 | //check preconditions, not cubemap, not mipmapped, not a volume 207 | if (i._levelCount != 1 || i._faces != 0 || i._depth != 0) 208 | return false; 209 | 210 | 211 | FILE *fp = fopen( file, "wb"); 212 | 213 | if ( !fp) 214 | return false; 215 | 216 | png_structp png_ptr = NULL; 217 | png_infop info_ptr = NULL; 218 | 219 | png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); 220 | 221 | if (!png_ptr) { 222 | fclose(fp); 223 | return false; /* out of memory */ 224 | } 225 | 226 | info_ptr = png_create_info_struct(png_ptr); 227 | if (!info_ptr) { 228 | png_destroy_write_struct(&png_ptr, NULL); 229 | fclose(fp); 230 | return false; /* out of memory */ 231 | } 232 | 233 | 234 | // setjmp() is used for error handling with libPNG, if something goes wrong it is coming back here 235 | 236 | if (setjmp(png_jmpbuf(png_ptr))) { 237 | png_destroy_write_struct(&png_ptr, &info_ptr); 238 | fclose(fp); 239 | return false; 240 | } 241 | 242 | // Need to override the standard I/O methods since libPNG may be linked against a different run-time 243 | png_set_write_fn( png_ptr, fp, pngWriteFn, pngFlushFn); 244 | 245 | //TODO, set transforms and row pointers 246 | int bit_depth = 1; 247 | int color_type = PNG_COLOR_TYPE_GRAY; 248 | int row_bytes = 0; 249 | 250 | switch (i._type) { 251 | case GL_UNSIGNED_BYTE: 252 | bit_depth = 8; 253 | break; 254 | case GL_UNSIGNED_SHORT: 255 | bit_depth = 16; 256 | break; 257 | default: 258 | //throw error due to unsupported png type here 259 | break; 260 | }; 261 | 262 | switch (i._format) { 263 | case GL_LUMINANCE: 264 | color_type = PNG_COLOR_TYPE_GRAY; 265 | row_bytes = i._width * (bit_depth >> 3); 266 | break; 267 | case GL_LUMINANCE_ALPHA: 268 | color_type = PNG_COLOR_TYPE_GRAY_ALPHA; 269 | row_bytes = i._width * 2 * (bit_depth >> 3); 270 | break; 271 | case GL_RGB: 272 | color_type = PNG_COLOR_TYPE_RGB; 273 | row_bytes = i._width * 3 * (bit_depth >> 3); 274 | break; 275 | case GL_RGBA: 276 | color_type = PNG_COLOR_TYPE_RGB_ALPHA; 277 | row_bytes = i._width * 4 * (bit_depth >> 3); 278 | break; 279 | default: 280 | // throw error due to unsupported format 281 | break; 282 | }; 283 | 284 | png_set_IHDR(png_ptr, info_ptr, i._width, i._height, bit_depth, color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); 285 | 286 | 287 | GLubyte **row_pointers = new GLubyte*[i._height]; 288 | 289 | // set up the row pointers 290 | GLubyte *data = i._data[0]; 291 | for ( int ii = 0; ii < i._height; ii++) { 292 | row_pointers[ii] = data + (i._height - 1 - ii)*row_bytes; 293 | } 294 | 295 | png_write_image( png_ptr, row_pointers); 296 | 297 | delete []row_pointers; 298 | 299 | png_write_end(png_ptr, info_ptr); 300 | png_destroy_write_struct(&png_ptr, &info_ptr); 301 | fclose(fp); 302 | 303 | return true; 304 | } 305 | 306 | }; -------------------------------------------------------------------------------- /nvImage/rgbe.h: -------------------------------------------------------------------------------- 1 | #ifndef _H_RGBE 2 | #define _H_RGBE 3 | /* THIS CODE CARRIES NO GUARANTEE OF USABILITY OR FITNESS FOR ANY PURPOSE. 4 | * WHILE THE AUTHORS HAVE TRIED TO ENSURE THE PROGRAM WORKS CORRECTLY, 5 | * IT IS STRICTLY USE AT YOUR OWN RISK. */ 6 | 7 | /* utility for reading and writing Ward's rgbe image format. 8 | See rgbe.txt file for more details. 9 | */ 10 | 11 | #include 12 | 13 | typedef struct { 14 | int valid; /* indicate which fields are valid */ 15 | char programtype[16]; /* listed at beginning of file to identify it 16 | * after "#?". defaults to "RGBE" */ 17 | float gamma; /* image has already been gamma corrected with 18 | * given gamma. defaults to 1.0 (no correction) */ 19 | float exposure; /* a value of 1.0 in an image corresponds to 20 | * watts/steradian/m^2. 21 | * defaults to 1.0 */ 22 | } rgbe_header_info; 23 | 24 | /* flags indicating which fields in an rgbe_header_info are valid */ 25 | #define RGBE_VALID_PROGRAMTYPE 0x01 26 | #define RGBE_VALID_GAMMA 0x02 27 | #define RGBE_VALID_EXPOSURE 0x04 28 | 29 | /* return codes for rgbe routines */ 30 | #define RGBE_RETURN_SUCCESS 0 31 | #define RGBE_RETURN_FAILURE -1 32 | 33 | #ifdef __cplusplus 34 | extern "C" { 35 | #endif 36 | /* read or write headers */ 37 | /* you may set rgbe_header_info to null if you want to */ 38 | int RGBE_WriteHeader(FILE *fp, int width, int height, rgbe_header_info *info); 39 | int RGBE_ReadHeader(FILE *fp, int *width, int *height, rgbe_header_info *info); 40 | 41 | /* read or write pixels */ 42 | /* can read or write pixels in chunks of any size including single pixels*/ 43 | int RGBE_WritePixels(FILE *fp, float *data, int numpixels); 44 | int RGBE_ReadPixels(FILE *fp, float *data, int numpixels); 45 | 46 | /* read or write run length encoded files */ 47 | /* must be called to read or write whole scanlines */ 48 | int RGBE_WritePixels_RLE(FILE *fp, float *data, int scanline_width, 49 | int num_scanlines); 50 | int RGBE_ReadPixels_RLE(FILE *fp, float *data, int scanline_width, 51 | int num_scanlines); 52 | 53 | int RGBE_ReadPixels_Raw_RLE(FILE *fp, unsigned char *data, int scanline_width, 54 | int num_scanlines); 55 | 56 | #ifdef _CPLUSPLUS 57 | /* define if your compiler understands inline commands */ 58 | #define INLINE inline 59 | #else 60 | #define INLINE 61 | #endif 62 | 63 | INLINE void float2rgbe(unsigned char rgbe[4], float red, float green, float blue); 64 | INLINE void rgbe2float(float *red, float *green, float *blue, unsigned char rgbe[4]); 65 | 66 | #ifdef __cplusplus 67 | } 68 | #endif 69 | 70 | #endif /* _H_RGBE */ 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /nvModel/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6 FATAL_ERROR) 2 | 3 | set(LIBNVMODEL_SRC nvModel.cc nvModelObj.cc nvModelQuery.cc nvUtils.cc) 4 | #set(LIBRARY_OUTPUT_PATH ../bin_test/nvmodel) 5 | #add_library(nvmodel SHARED ${LIBNVMODEL_SRC}) 6 | add_library(nvmodel_static STATIC ${LIBNVMODEL_SRC}) 7 | set_target_properties(nvmodel_static PROPERTIES OUTPUT_NAME "nvmodel") 8 | #set_target_properties(nvmodel_static PROPERTIES CLEAN_DIRECT_OUTPUT 1) 9 | #set_target_properties(nvmodel PROPERTIES CLEAN_DIRECT_OUTPUT 1) 10 | 11 | -------------------------------------------------------------------------------- /nvModel/Makefile: -------------------------------------------------------------------------------- 1 | HEADERS=nvModel.h nvMath.h nvMatrix.h nvQuaternion.h nvSDKPath.h \ 2 | nvShaderUtils.h nvUtils.h nvVector.h 3 | 4 | SRC=nvModel.cc nvUtils.cc nvUtils.cc nvModelObj.cc nvModelQuery.cc 5 | 6 | obj/nvModel:$(HEADERS) $(SRC) 7 | g++ -c $(SRC) 8 | mv *.o obj/ 9 | 10 | clean: 11 | rm -f obj/*.o 12 | -------------------------------------------------------------------------------- /nvModel/nvMath.h: -------------------------------------------------------------------------------- 1 | // 2 | // Template math library for common 3D functionality 3 | // 4 | // This code is in part deriver from glh, a cross platform glut helper library. 5 | // The copyright for glh follows this notice. 6 | // 7 | // Copyright (c) NVIDIA Corporation. All rights reserved. 8 | //////////////////////////////////////////////////////////////////////////////// 9 | 10 | /* 11 | Copyright (c) 2000 Cass Everitt 12 | Copyright (c) 2000 NVIDIA Corporation 13 | All rights reserved. 14 | 15 | Redistribution and use in source and binary forms, with or 16 | without modification, are permitted provided that the following 17 | conditions are met: 18 | 19 | * Redistributions of source code must retain the above 20 | copyright notice, this list of conditions and the following 21 | disclaimer. 22 | 23 | * Redistributions in binary form must reproduce the above 24 | copyright notice, this list of conditions and the following 25 | disclaimer in the documentation and/or other materials 26 | provided with the distribution. 27 | 28 | * The names of contributors to this software may not be used 29 | to endorse or promote products derived from this software 30 | without specific prior written permission. 31 | 32 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 33 | ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 34 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 35 | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 36 | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 37 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 38 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 39 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 40 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 41 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 42 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 43 | POSSIBILITY OF SUCH DAMAGE. 44 | 45 | 46 | Cass Everitt - cass@r3.nu 47 | */ 48 | 49 | #ifndef NV_MATH_H 50 | #define NV_MATH_H 51 | 52 | #include 53 | 54 | #include "nvVector.h" 55 | #include "nvMatrix.h" 56 | #include "nvQuaternion.h" 57 | 58 | #define NV_PI float(3.1415926535897932384626433832795) 59 | 60 | namespace nv { 61 | 62 | typedef vec2 vec2f; 63 | typedef vec2 vec2i; 64 | typedef vec2 vec2ui; 65 | typedef vec3 vec3f; 66 | typedef vec3 vec3i; 67 | typedef vec3 vec3ui; 68 | typedef vec4 vec4f; 69 | typedef vec4 vec4i; 70 | typedef vec4 vec4ui; 71 | typedef matrix4 matrix4f; 72 | typedef quaternion quaternionf; 73 | 74 | }; 75 | 76 | #endif 77 | -------------------------------------------------------------------------------- /nvModel/nvMatrix.h: -------------------------------------------------------------------------------- 1 | // 2 | // Template math library for common 3D functionality 3 | // 4 | // nvMatrix.h - template matrix code 5 | // 6 | // This code is in part deriver from glh, a cross platform glut helper library. 7 | // The copyright for glh follows this notice. 8 | // 9 | // Copyright (c) NVIDIA Corporation. All rights reserved. 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | /* 13 | Copyright (c) 2000 Cass Everitt 14 | Copyright (c) 2000 NVIDIA Corporation 15 | All rights reserved. 16 | 17 | Redistribution and use in source and binary forms, with or 18 | without modification, are permitted provided that the following 19 | conditions are met: 20 | 21 | * Redistributions of source code must retain the above 22 | copyright notice, this list of conditions and the following 23 | disclaimer. 24 | 25 | * Redistributions in binary form must reproduce the above 26 | copyright notice, this list of conditions and the following 27 | disclaimer in the documentation and/or other materials 28 | provided with the distribution. 29 | 30 | * The names of contributors to this software may not be used 31 | to endorse or promote products derived from this software 32 | without specific prior written permission. 33 | 34 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 35 | ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 36 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 37 | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 38 | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 39 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 40 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 41 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 42 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 43 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 44 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 45 | POSSIBILITY OF SUCH DAMAGE. 46 | 47 | 48 | Cass Everitt - cass@r3.nu 49 | */ 50 | 51 | #ifndef NV_MATRIX_H 52 | #define NV_MATRIX_H 53 | 54 | namespace nv { 55 | 56 | template class vec2; 57 | template class vec3; 58 | template class vec4; 59 | 60 | //////////////////////////////////////////////////////////////////////////////// 61 | // 62 | // Matrix 63 | // 64 | //////////////////////////////////////////////////////////////////////////////// 65 | template 66 | class matrix4 67 | { 68 | 69 | public: 70 | 71 | matrix4() { make_identity(); } 72 | 73 | matrix4( T t ) 74 | { set_value(t); } 75 | 76 | matrix4( const T * m ) 77 | { set_value(m); } 78 | 79 | matrix4( T a00, T a01, T a02, T a03, 80 | T a10, T a11, T a12, T a13, 81 | T a20, T a21, T a22, T a23, 82 | T a30, T a31, T a32, T a33 ) : 83 | _11(a00), _12(a01), _13(a02), _14(a03), 84 | _21(a10), _22(a11), _23(a12), _24(a13), 85 | _31(a20), _32(a21), _33(a22), _34(a23), 86 | _41(a30), _42(a31), _43(a32), _44(a33) 87 | {} 88 | 89 | 90 | void get_value( T * mp ) const { 91 | int c = 0; 92 | for(int j=0; j < 4; j++) 93 | for(int i=0; i < 4; i++) 94 | mp[c++] = element(i,j); 95 | } 96 | 97 | const T * get_value() const { 98 | return _array; 99 | } 100 | 101 | void set_value( T * mp) { 102 | int c = 0; 103 | for(int j=0; j < 4; j++) 104 | for(int i=0; i < 4; i++) 105 | element(i,j) = mp[c++]; 106 | } 107 | 108 | void set_value( T r ) { 109 | for(int i=0; i < 4; i++) 110 | for(int j=0; j < 4; j++) 111 | element(i,j) = r; 112 | } 113 | 114 | void make_identity() { 115 | element(0,0) = 1.0; 116 | element(0,1) = 0.0; 117 | element(0,2) = 0.0; 118 | element(0,3) = 0.0; 119 | 120 | element(1,0) = 0.0; 121 | element(1,1) = 1.0; 122 | element(1,2) = 0.0; 123 | element(1,3) = 0.0; 124 | 125 | element(2,0) = 0.0; 126 | element(2,1) = 0.0; 127 | element(2,2) = 1.0; 128 | element(2,3) = 0.0; 129 | 130 | element(3,0) = 0.0; 131 | element(3,1) = 0.0; 132 | element(3,2) = 0.0; 133 | element(3,3) = 1.0; 134 | } 135 | 136 | // set a uniform scale 137 | void set_scale( T s ) { 138 | element(0,0) = s; 139 | element(1,1) = s; 140 | element(2,2) = s; 141 | } 142 | 143 | void set_scale( const vec3 & s ) { 144 | for (int i = 0; i < 3; i++) element(i,i) = s[i]; 145 | } 146 | 147 | 148 | void set_translate( const vec3 & t ) { 149 | for (int i = 0; i < 3; i++) element(i,3) = t[i]; 150 | } 151 | 152 | void set_row(int r, const vec4 & t) { 153 | for (int i = 0; i < 4; i++) element(r,i) = t[i]; 154 | } 155 | 156 | void set_column(int c, const vec4 & t) { 157 | for (int i = 0; i < 4; i++) element(i,c) = t[i]; 158 | } 159 | 160 | vec4 get_row(int r) const { 161 | vec4 v; 162 | for (int i = 0; i < 4; i++) v[i] = element(r,i); 163 | return v; 164 | } 165 | 166 | vec4 get_column(int c) const { 167 | vec4 v; 168 | for (int i = 0; i < 4; i++) v[i] = element(i,c); 169 | return v; 170 | } 171 | 172 | friend matrix4 inverse( const matrix4 & m) { 173 | matrix4 minv; 174 | 175 | T r1[8], r2[8], r3[8], r4[8]; 176 | T *s[4], *tmprow; 177 | 178 | s[0] = &r1[0]; 179 | s[1] = &r2[0]; 180 | s[2] = &r3[0]; 181 | s[3] = &r4[0]; 182 | 183 | register int i,j,p,jj; 184 | for(i=0;i<4;i++) { 185 | for(j=0;j<4;j++) { 186 | s[i][j] = m.element(i,j); 187 | if(i==j) s[i][j+4] = 1.0; 188 | else s[i][j+4] = 0.0; 189 | } 190 | } 191 | T scp[4]; 192 | for(i=0;i<4;i++) { 193 | scp[i] = T(fabs(s[i][0])); 194 | for(j=1;j<4;j++) 195 | if(T(fabs(s[i][j])) > scp[i]) scp[i] = T(fabs(s[i][j])); 196 | if(scp[i] == 0.0) return minv; // singular matrix! 197 | } 198 | 199 | int pivot_to; 200 | T scp_max; 201 | for(i=0;i<4;i++) { 202 | // select pivot row 203 | pivot_to = i; 204 | scp_max = T(fabs(s[i][i]/scp[i])); 205 | // find out which row should be on top 206 | for(p=i+1;p<4;p++) 207 | if (T(fabs(s[p][i]/scp[p])) > scp_max) { 208 | scp_max = T(fabs(s[p][i]/scp[p])); 209 | pivot_to = p; 210 | } 211 | // Pivot if necessary 212 | if(pivot_to != i) { 213 | tmprow = s[i]; 214 | s[i] = s[pivot_to]; 215 | s[pivot_to] = tmprow; 216 | T tmpscp; 217 | tmpscp = scp[i]; 218 | scp[i] = scp[pivot_to]; 219 | scp[pivot_to] = tmpscp; 220 | } 221 | 222 | T mji; 223 | // perform gaussian elimination 224 | for(j=i+1;j<4;j++) { 225 | mji = s[j][i]/s[i][i]; 226 | s[j][i] = 0.0; 227 | for(jj=i+1;jj<8;jj++) 228 | s[j][jj] -= mji*s[i][jj]; 229 | } 230 | } 231 | if(s[3][3] == 0.0) return minv; // singular matrix! 232 | 233 | // 234 | // Now we have an upper triangular matrix. 235 | // 236 | // x x x x | y y y y 237 | // 0 x x x | y y y y 238 | // 0 0 x x | y y y y 239 | // 0 0 0 x | y y y y 240 | // 241 | // we'll back substitute to get the inverse 242 | // 243 | // 1 0 0 0 | z z z z 244 | // 0 1 0 0 | z z z z 245 | // 0 0 1 0 | z z z z 246 | // 0 0 0 1 | z z z z 247 | // 248 | 249 | T mij; 250 | for(i=3;i>0;i--) { 251 | for(j=i-1;j > -1; j--) { 252 | mij = s[j][i]/s[i][i]; 253 | for(jj=j+1;jj<8;jj++) 254 | s[j][jj] -= mij*s[i][jj]; 255 | } 256 | } 257 | 258 | for(i=0;i<4;i++) 259 | for(j=0;j<4;j++) 260 | minv(i,j) = s[i][j+4] / s[i][i]; 261 | 262 | return minv; 263 | } 264 | 265 | 266 | friend matrix4 transpose( const matrix4 & m) { 267 | matrix4 mtrans; 268 | 269 | for(int i=0;i<4;i++) 270 | for(int j=0;j<4;j++) 271 | mtrans(i,j) = m.element(j,i); 272 | return mtrans; 273 | } 274 | 275 | matrix4 & operator *= ( const matrix4 & rhs ) { 276 | matrix4 mt(*this); 277 | set_value(T(0)); 278 | 279 | for(int i=0; i < 4; i++) 280 | for(int j=0; j < 4; j++) 281 | for(int c=0; c < 4; c++) 282 | element(i,j) += mt(i,c) * rhs(c,j); 283 | return *this; 284 | } 285 | 286 | friend matrix4 operator * ( const matrix4 & lhs, const matrix4 & rhs ) { 287 | matrix4 r(T(0)); 288 | 289 | for(int i=0; i < 4; i++) 290 | for(int j=0; j < 4; j++) 291 | for(int c=0; c < 4; c++) 292 | r.element(i,j) += lhs(i,c) * rhs(c,j); 293 | return r; 294 | } 295 | 296 | // dst = M * src 297 | vec4 operator *( const vec4 &src) const { 298 | vec4 r; 299 | for ( int i = 0; i < 4; i++) 300 | r[i] = ( src[0] * element(i,0) + src[1] * element(i,1) + 301 | src[2] * element(i,2) + src[3] * element(i,3)); 302 | return r; 303 | } 304 | 305 | // dst = src * M 306 | friend vec4 operator *( const vec4 &lhs, const matrix4 &rhs) { 307 | vec4 r; 308 | for ( int i = 0; i < 4; i++) 309 | r[i] = ( lhs[0] * rhs.element(0,i) + lhs[1] * rhs.element(1,i) + 310 | lhs[2] * rhs.element(2,i) + lhs[3] * rhs.element(3,i)); 311 | return r; 312 | } 313 | 314 | T & operator () (int row, int col) { 315 | return element(row,col); 316 | } 317 | 318 | const T & operator () (int row, int col) const { 319 | return element(row,col); 320 | } 321 | 322 | T & element (int row, int col) { 323 | return _array[row | (col<<2)]; 324 | } 325 | 326 | const T & element (int row, int col) const { 327 | return _array[row | (col<<2)]; 328 | } 329 | 330 | matrix4 & operator *= ( const T & r ) { 331 | for (int i = 0; i < 4; ++i) { 332 | element(0,i) *= r; 333 | element(1,i) *= r; 334 | element(2,i) *= r; 335 | element(3,i) *= r; 336 | } 337 | return *this; 338 | } 339 | 340 | matrix4 & operator += ( const matrix4 & mat ) { 341 | for (int i = 0; i < 4; ++i) { 342 | element(0,i) += mat.element(0,i); 343 | element(1,i) += mat.element(1,i); 344 | element(2,i) += mat.element(2,i); 345 | element(3,i) += mat.element(3,i); 346 | } 347 | return *this; 348 | } 349 | 350 | 351 | friend bool operator == ( const matrix4 & lhs, const matrix4 & rhs ) { 352 | bool r = true; 353 | for (int i = 0; i < 16; i++) 354 | r &= lhs._array[i] == rhs._array[i]; 355 | return r; 356 | } 357 | 358 | friend bool operator != ( const matrix4 & lhs, const matrix4 & rhs ) { 359 | bool r = true; 360 | for (int i = 0; i < 16; i++) 361 | r &= lhs._array[i] != rhs._array[i]; 362 | return r; 363 | } 364 | 365 | union { 366 | struct { 367 | T _11, _12, _13, _14; // standard names for components 368 | T _21, _22, _23, _24; // standard names for components 369 | T _31, _32, _33, _34; // standard names for components 370 | T _41, _42, _43, _44; // standard names for components 371 | }; 372 | T _array[16]; // array access 373 | }; 374 | }; 375 | 376 | }; 377 | 378 | #endif 379 | -------------------------------------------------------------------------------- /nvModel/nvModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // nvModel.h - Model support class 3 | // 4 | // The nvModel class implements an interface for a multipurpose model 5 | // object. This class is useful for loading and formatting meshes 6 | // for use by OpenGL. It can compute face normals, tangents, and 7 | // adjacency information. The class supports the obj file format. 8 | // 9 | // Author: Evan Hart 10 | // Email: sdkfeedback@nvidia.com 11 | // 12 | // Copyright (c) NVIDIA Corporation. All rights reserved. 13 | //////////////////////////////////////////////////////////////////////////////// 14 | 15 | #ifndef NV_MODEL_H 16 | #define NV_MODEL_H 17 | 18 | 19 | #define NVSDKENTRY 20 | 21 | #include 22 | #include 23 | 24 | #include 25 | #include "nvMath.h" 26 | 27 | namespace nv { 28 | 29 | class Model { 30 | public: 31 | 32 | // 33 | // Enumeration of primitive types 34 | // 35 | ////////////////////////////////////////////////////////////// 36 | enum PrimType { 37 | eptNone = 0x0, 38 | eptPoints = 0x1, 39 | eptEdges = 0x2, 40 | eptTriangles = 0x4, 41 | eptTrianglesWithAdjacency = 0x8, 42 | eptAll = 0xf 43 | }; 44 | 45 | static const int NumPrimTypes = 4; 46 | 47 | NVSDKENTRY static Model* CreateModel(); 48 | 49 | NVSDKENTRY Model(); 50 | NVSDKENTRY virtual ~Model(); 51 | 52 | // 53 | // loadModelFromFile 54 | // 55 | // This function attempts to determine the type of 56 | // the filename passed as a parameter. If it understands 57 | // that file type, it attempts to parse and load the file 58 | // into its raw data structures. If the file type is 59 | // recognized and successfully parsed, the function returns 60 | // true, otherwise it returns false. 61 | // 62 | ////////////////////////////////////////////////////////////// 63 | NVSDKENTRY bool loadModelFromFile( const char* file); 64 | 65 | // 66 | // compileModel 67 | // 68 | // This function takes the raw model data in the internal 69 | // structures, and attempts to bring it to a format directly 70 | // accepted for vertex array style rendering. This means that 71 | // a unique compiled vertex will exist for each unique 72 | // combination of position, normal, tex coords, etc that are 73 | // used in the model. The prim parameter, tells the model 74 | // what type of index list to compile. By default it compiles 75 | // a simple triangle mesh with no connectivity. 76 | // 77 | ////////////////////////////////////////////////////////////// 78 | NVSDKENTRY void compileModel( PrimType prim = eptTriangles); 79 | 80 | // 81 | // computeBoundingBox 82 | // 83 | // This function returns the points defining the axis- 84 | // aligned bounding box containing the model. 85 | // 86 | ////////////////////////////////////////////////////////////// 87 | NVSDKENTRY void computeBoundingBox( vec3f &minVal, vec3f &maxVal); 88 | 89 | // 90 | // rescale 91 | // 92 | // rescales object based on bounding box 93 | // 94 | ////////////////////////////////////////////////////////////// 95 | NVSDKENTRY void rescale( float radius); 96 | 97 | // 98 | // buildTangents 99 | // 100 | // This function computes tangents in the s direction on 101 | // the model. It operates on the raw data, so it should only 102 | // be used before compiling a model into a HW friendly form. 103 | // 104 | ////////////////////////////////////////////////////////////// 105 | NVSDKENTRY void computeTangents(); 106 | 107 | // 108 | // computeNormals 109 | // 110 | // This function computes vertex normals for a model 111 | // which did not have them. It computes them on the raw 112 | // data, so it should be done before compiling the model 113 | // into a HW friendly format. 114 | // 115 | ////////////////////////////////////////////////////////////// 116 | NVSDKENTRY void computeNormals(); 117 | 118 | NVSDKENTRY void removeDegeneratePrims(); 119 | 120 | // 121 | //general query functions 122 | // 123 | NVSDKENTRY bool hasNormals() const; 124 | NVSDKENTRY bool hasTexCoords() const; 125 | NVSDKENTRY bool hasTangents() const; 126 | NVSDKENTRY bool hasColors() const; 127 | 128 | NVSDKENTRY int getPositionSize() const; 129 | NVSDKENTRY int getNormalSize() const; 130 | NVSDKENTRY int getTexCoordSize() const; 131 | NVSDKENTRY int getTangentSize() const; 132 | NVSDKENTRY int getColorSize() const; 133 | 134 | // 135 | // Functions for the management of raw data 136 | // 137 | NVSDKENTRY void clearNormals(); 138 | NVSDKENTRY void clearTexCoords(); 139 | NVSDKENTRY void clearTangents(); 140 | NVSDKENTRY void clearColors(); 141 | 142 | // 143 | //raw data access functions 144 | // These are to be used to get the raw array data from the file, each array has its own index 145 | // 146 | NVSDKENTRY const float* getPositions() const; 147 | NVSDKENTRY const float* getNormals() const; 148 | NVSDKENTRY const float* getTexCoords() const; 149 | NVSDKENTRY const float* getTangents() const; 150 | NVSDKENTRY const float* getColors() const; 151 | 152 | NVSDKENTRY const GLuint* getPositionIndices() const; 153 | NVSDKENTRY const GLuint* getNormalIndices() const; 154 | NVSDKENTRY const GLuint* getTexCoordIndices() const; 155 | NVSDKENTRY const GLuint* getTangentIndices() const; 156 | NVSDKENTRY const GLuint* getColorIndices() const; 157 | 158 | NVSDKENTRY int getPositionCount() const; 159 | NVSDKENTRY int getNormalCount() const; 160 | NVSDKENTRY int getTexCoordCount() const; 161 | NVSDKENTRY int getTangentCount() const; 162 | NVSDKENTRY int getColorCount() const; 163 | 164 | NVSDKENTRY int getIndexCount() const; 165 | 166 | // 167 | //compiled data access functions 168 | // 169 | NVSDKENTRY const float* getCompiledVertices() const; 170 | NVSDKENTRY const GLuint* getCompiledIndices( PrimType prim = eptTriangles) const; 171 | 172 | NVSDKENTRY int getCompiledPositionOffset() const; 173 | NVSDKENTRY int getCompiledNormalOffset() const; 174 | NVSDKENTRY int getCompiledTexCoordOffset() const; 175 | NVSDKENTRY int getCompiledTangentOffset() const; 176 | NVSDKENTRY int getCompiledColorOffset() const; 177 | 178 | // returns the size of the merged vertex in # of floats 179 | NVSDKENTRY int getCompiledVertexSize() const; 180 | 181 | NVSDKENTRY int getCompiledVertexCount() const; 182 | NVSDKENTRY int getCompiledIndexCount( PrimType prim = eptTriangles) const; 183 | 184 | NVSDKENTRY int getOpenEdgeCount() const; 185 | 186 | protected: 187 | 188 | //Would all this be better done as a channel abstraction to handle more arbitrary data? 189 | 190 | //data structures for model data, not optimized for rendering 191 | std::vector _positions; 192 | std::vector _normals; 193 | std::vector _texCoords; 194 | std::vector _sTangents; 195 | std::vector _colors; 196 | int _posSize; 197 | int _tcSize; 198 | int _cSize; 199 | 200 | std::vector _pIndex; 201 | std::vector _nIndex; 202 | std::vector _tIndex; 203 | std::vector _tanIndex; 204 | std::vector _cIndex; 205 | 206 | //data structures optimized for rendering, compiled model 207 | std::vector _indices[NumPrimTypes]; 208 | std::vector _vertices; 209 | int _pOffset; 210 | int _nOffset; 211 | int _tcOffset; 212 | int _sTanOffset; 213 | int _cOffset; 214 | int _vtxSize; 215 | 216 | int _openEdges; 217 | 218 | // 219 | // Static elements used to dispatch to proper sub-readers 220 | // 221 | ////////////////////////////////////////////////////////////// 222 | struct FormatInfo { 223 | const char* extension; 224 | bool (*reader)( const char* file, Model& i); 225 | }; 226 | 227 | static FormatInfo formatTable[]; 228 | 229 | NVSDKENTRY static bool loadObjFromFile( const char *file, Model &m); 230 | }; 231 | }; 232 | 233 | 234 | #endif 235 | -------------------------------------------------------------------------------- /nvModel/nvModelObj.cc: -------------------------------------------------------------------------------- 1 | // 2 | // nvModelObj.cpp - Model support class 3 | // 4 | // The nvModel class implements an interface for a multipurpose model 5 | // object. This class is useful for loading and formatting meshes 6 | // for use by OpenGL. It can compute face normals, tangents, and 7 | // adjacency information. The class supports the obj file format. 8 | // 9 | // This file implements the obj file parser and translator. 10 | // 11 | // Author: Evan Hart 12 | // Email: sdkfeedback@nvidia.com 13 | // 14 | // Copyright (c) NVIDIA Corporation. All rights reserved. 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | #include "nvModel.h" 18 | 19 | #include 20 | 21 | #define BUF_SIZE 256 22 | 23 | using std::vector; 24 | 25 | static void skipLine(char * buf, int size, FILE * fp) 26 | { 27 | do { 28 | buf[size-1] = '$'; 29 | fgets(buf, size, fp); 30 | } while (buf[size-1] != '$'); 31 | } 32 | 33 | 34 | namespace nv { 35 | 36 | bool Model::loadObjFromFile( const char *file, Model &m) { 37 | FILE *fp; 38 | 39 | fp = fopen( file, "r"); 40 | if (!fp) { 41 | return false; 42 | } 43 | 44 | char buf[BUF_SIZE]; 45 | float val[4]; 46 | int idx[3][3]; 47 | int match; 48 | bool vtx4Comp = false; 49 | bool tex3Comp = false; 50 | bool hasTC = false; 51 | bool hasNormals = false; 52 | 53 | while ( fscanf( fp, "%s", buf) != EOF ) { 54 | 55 | switch (buf[0]) { 56 | case '#': 57 | //comment line, eat the remainder 58 | skipLine( buf, BUF_SIZE, fp); 59 | break; 60 | 61 | case 'v': 62 | switch (buf[1]) { 63 | 64 | case '\0': 65 | //vertex, 3 or 4 components 66 | val[3] = 1.0f; //default w coordinate 67 | match = fscanf( fp, "%f %f %f %f", &val[0], &val[1], &val[2], &val[3]); 68 | m._positions.push_back( val[0]); 69 | m._positions.push_back( val[1]); 70 | m._positions.push_back( val[2]); 71 | m._positions.push_back( val[3]); 72 | vtx4Comp |= ( match == 4); 73 | assert( match > 2 && match < 5); 74 | break; 75 | 76 | case 'n': 77 | //normal, 3 components 78 | match = fscanf( fp, "%f %f %f", &val[0], &val[1], &val[2]); 79 | m._normals.push_back( val[0]); 80 | m._normals.push_back( val[1]); 81 | m._normals.push_back( val[2]); 82 | assert( match == 3); 83 | break; 84 | 85 | case 't': 86 | //texcoord, 2 or 3 components 87 | val[2] = 0.0f; //default r coordinate 88 | match = fscanf( fp, "%f %f %f %f", &val[0], &val[1], &val[2]); 89 | m._texCoords.push_back( val[0]); 90 | m._texCoords.push_back( val[1]); 91 | m._texCoords.push_back( val[2]); 92 | tex3Comp |= ( match == 3); 93 | assert( match > 1 && match < 4); 94 | break; 95 | } 96 | break; 97 | 98 | case 'f': 99 | //face 100 | fscanf( fp, "%s", buf); 101 | 102 | //determine the type, and read the initial vertex, all entries in a face must have the same format 103 | if ( sscanf( buf, "%d//%d", &idx[0][0], &idx[0][1]) == 2) { 104 | //This face has vertex and normal indices 105 | 106 | //remap them to the right spot 107 | idx[0][0] = (idx[0][0] > 0) ? (idx[0][0] - 1) : ((int)m._positions.size() - idx[0][0]); 108 | idx[0][1] = (idx[0][1] > 0) ? (idx[0][1] - 1) : ((int)m._normals.size() - idx[0][1]); 109 | 110 | //grab the second vertex to prime 111 | fscanf( fp, "%d//%d", &idx[1][0], &idx[1][1]); 112 | 113 | //remap them to the right spot 114 | idx[1][0] = (idx[1][0] > 0) ? (idx[1][0] - 1) : ((int)m._positions.size() - idx[1][0]); 115 | idx[1][1] = (idx[1][1] > 0) ? (idx[1][1] - 1) : ((int)m._normals.size() - idx[1][1]); 116 | 117 | //create the fan 118 | while ( fscanf( fp, "%d//%d", &idx[2][0], &idx[2][1]) == 2) { 119 | //remap them to the right spot 120 | idx[2][0] = (idx[2][0] > 0) ? (idx[2][0] - 1) : ((int)m._positions.size() - idx[2][0]); 121 | idx[2][1] = (idx[2][1] > 0) ? (idx[2][1] - 1) : ((int)m._normals.size() - idx[2][1]); 122 | 123 | //add the indices 124 | for (int ii = 0; ii < 3; ii++) { 125 | m._pIndex.push_back( idx[ii][0]); 126 | m._nIndex.push_back( idx[ii][1]); 127 | m._tIndex.push_back(0); // dummy index, to ensure that the buffers are of identical size 128 | } 129 | 130 | //prepare for the next iteration 131 | idx[1][0] = idx[2][0]; 132 | idx[1][1] = idx[2][1]; 133 | } 134 | hasNormals = true; 135 | } 136 | else if ( sscanf( buf, "%d/%d/%d", &idx[0][0], &idx[0][1], &idx[0][2]) == 3) { 137 | //This face has vertex, texture coordinate, and normal indices 138 | 139 | //remap them to the right spot 140 | idx[0][0] = (idx[0][0] > 0) ? (idx[0][0] - 1) : ((int)m._positions.size() - idx[0][0]); 141 | idx[0][1] = (idx[0][1] > 0) ? (idx[0][1] - 1) : ((int)m._texCoords.size() - idx[0][1]); 142 | idx[0][2] = (idx[0][2] > 0) ? (idx[0][2] - 1) : ((int)m._normals.size() - idx[0][2]); 143 | 144 | //grab the second vertex to prime 145 | fscanf( fp, "%d/%d/%d", &idx[1][0], &idx[1][1], &idx[1][2]); 146 | 147 | //remap them to the right spot 148 | idx[1][0] = (idx[1][0] > 0) ? (idx[1][0] - 1) : ((int)m._positions.size() - idx[1][0]); 149 | idx[1][1] = (idx[1][1] > 0) ? (idx[1][1] - 1) : ((int)m._texCoords.size() - idx[1][1]); 150 | idx[1][2] = (idx[1][2] > 0) ? (idx[1][2] - 1) : ((int)m._normals.size() - idx[1][2]); 151 | 152 | //create the fan 153 | while ( fscanf( fp, "%d/%d/%d", &idx[2][0], &idx[2][1], &idx[2][2]) == 3) { 154 | //remap them to the right spot 155 | idx[2][0] = (idx[2][0] > 0) ? (idx[2][0] - 1) : ((int)m._positions.size() - idx[2][0]); 156 | idx[2][1] = (idx[2][1] > 0) ? (idx[2][1] - 1) : ((int)m._texCoords.size() - idx[2][1]); 157 | idx[2][2] = (idx[2][2] > 0) ? (idx[2][2] - 1) : ((int)m._normals.size() - idx[2][2]); 158 | 159 | //add the indices 160 | for (int ii = 0; ii < 3; ii++) { 161 | m._pIndex.push_back( idx[ii][0]); 162 | m._tIndex.push_back( idx[ii][1]); 163 | m._nIndex.push_back( idx[ii][2]); 164 | } 165 | 166 | //prepare for the next iteration 167 | idx[1][0] = idx[2][0]; 168 | idx[1][1] = idx[2][1]; 169 | idx[1][2] = idx[2][2]; 170 | } 171 | 172 | hasTC = true; 173 | hasNormals = true; 174 | } 175 | else if ( sscanf( buf, "%d/%d", &idx[0][0], &idx[0][1]) == 2) { 176 | //This face has vertex and texture coordinate indices 177 | 178 | //remap them to the right spot 179 | idx[0][0] = (idx[0][0] > 0) ? (idx[0][0] - 1) : ((int)m._positions.size() - idx[0][0]); 180 | idx[0][1] = (idx[0][1] > 0) ? (idx[0][1] - 1) : ((int)m._texCoords.size() - idx[0][1]); 181 | 182 | //grab the second vertex to prime 183 | fscanf( fp, "%d/%d", &idx[1][0], &idx[1][1]); 184 | 185 | //remap them to the right spot 186 | idx[1][0] = (idx[1][0] > 0) ? (idx[1][0] - 1) : ((int)m._positions.size() - idx[1][0]); 187 | idx[1][1] = (idx[1][1] > 0) ? (idx[1][1] - 1) : ((int)m._texCoords.size() - idx[1][1]); 188 | 189 | //create the fan 190 | while ( fscanf( fp, "%d/%d", &idx[2][0], &idx[2][1]) == 2) { 191 | //remap them to the right spot 192 | idx[2][0] = (idx[2][0] > 0) ? (idx[2][0] - 1) : ((int)m._positions.size() - idx[2][0]); 193 | idx[2][1] = (idx[2][1] > 0) ? (idx[2][1] - 1) : ((int)m._texCoords.size() - idx[2][1]); 194 | 195 | //add the indices 196 | for (int ii = 0; ii < 3; ii++) { 197 | m._pIndex.push_back( idx[ii][0]); 198 | m._tIndex.push_back( idx[ii][1]); 199 | m._nIndex.push_back( 0); //dummy normal index to keep everything in synch 200 | } 201 | 202 | //prepare for the next iteration 203 | idx[1][0] = idx[2][0]; 204 | idx[1][1] = idx[2][1]; 205 | } 206 | hasTC = true; 207 | } 208 | else if ( sscanf( buf, "%d", &idx[0][0]) == 1) { 209 | //This face has only vertex indices 210 | 211 | //remap them to the right spot 212 | idx[0][0] = (idx[0][0] > 0) ? (idx[0][0] - 1) : ((int)m._positions.size() - idx[0][0]); 213 | 214 | //grab the second vertex to prime 215 | fscanf( fp, "%d", &idx[1][0]); 216 | 217 | //remap them to the right spot 218 | idx[1][0] = (idx[1][0] > 0) ? (idx[1][0] - 1) : ((int)m._positions.size() - idx[1][0]); 219 | 220 | //create the fan 221 | while ( fscanf( fp, "%d", &idx[2][0]) == 1) { 222 | //remap them to the right spot 223 | idx[2][0] = (idx[2][0] > 0) ? (idx[2][0] - 1) : ((int)m._positions.size() - idx[2][0]); 224 | 225 | //add the indices 226 | for (int ii = 0; ii < 3; ii++) { 227 | m._pIndex.push_back( idx[ii][0]); 228 | m._tIndex.push_back( 0); //dummy index to keep things in synch 229 | m._nIndex.push_back( 0); //dummy normal index to keep everything in synch 230 | } 231 | 232 | //prepare for the next iteration 233 | idx[1][0] = idx[2][0]; 234 | } 235 | } 236 | else { 237 | //bad format 238 | assert(0); 239 | skipLine( buf, BUF_SIZE, fp); 240 | } 241 | break; 242 | 243 | case 's': 244 | case 'g': 245 | case 'u': 246 | //all presently ignored 247 | default: 248 | skipLine( buf, BUF_SIZE, fp); 249 | 250 | }; 251 | } 252 | 253 | fclose(fp); 254 | 255 | //post-process data 256 | 257 | //free anything that ended up being unused 258 | if (!hasNormals) { 259 | m._normals.clear(); 260 | m._nIndex.clear(); 261 | } 262 | 263 | if (!hasTC) { 264 | m._texCoords.clear(); 265 | m._tIndex.clear(); 266 | } 267 | 268 | //set the defaults as the worst-case for an obj file 269 | m._posSize = 4; 270 | m._tcSize = 3; 271 | 272 | //compact to 3 component vertices if possible 273 | if (!vtx4Comp) { 274 | vector::iterator src = m._positions.begin(); 275 | vector::iterator dst = m._positions.begin(); 276 | 277 | for ( ; src < m._positions.end(); ) { 278 | *(dst++) = *(src++); 279 | *(dst++) = *(src++); 280 | *(dst++) = *(src++); 281 | src++; 282 | } 283 | 284 | m._positions.resize( (m._positions.size() / 4) * 3); 285 | 286 | m._posSize = 3; 287 | } 288 | 289 | //compact to 2 component tex coords if possible 290 | if (!tex3Comp) { 291 | vector::iterator src = m._texCoords.begin(); 292 | vector::iterator dst = m._texCoords.begin(); 293 | 294 | for ( ; src < m._texCoords.end(); ) { 295 | *(dst++) = *(src++); 296 | *(dst++) = *(src++); 297 | src++; 298 | } 299 | 300 | m._texCoords.resize( (m._texCoords.size() / 3) * 2); 301 | 302 | m._tcSize = 2; 303 | } 304 | 305 | return true; 306 | } 307 | 308 | 309 | }; 310 | 311 | -------------------------------------------------------------------------------- /nvModel/nvModelQuery.cc: -------------------------------------------------------------------------------- 1 | // 2 | // nvModelQuery.h - Model support class 3 | // 4 | // The nvModel class implements an interface for a multipurpose model 5 | // object. This class is useful for loading and formatting meshes 6 | // for use by OpenGL. It can compute face normals, tangents, and 7 | // adjacency information. The class supports the obj file format. 8 | // 9 | // This function implements the query functions. (number of vertices, etc) 10 | // 11 | // Author: Evan Hart 12 | // Email: sdkfeedback@nvidia.com 13 | // 14 | // Copyright (c) NVIDIA Corporation. All rights reserved. 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | #include "nvModel.h" 18 | 19 | //fix for non-standard naming 20 | #ifdef WIN32 21 | #define strcasecmp _stricmp 22 | #endif 23 | 24 | using std::vector; 25 | 26 | namespace nv { 27 | 28 | // 29 | // 30 | //////////////////////////////////////////////////////////// 31 | bool Model::hasNormals() const { 32 | return _normals.size() > 0; 33 | } 34 | 35 | // 36 | // 37 | //////////////////////////////////////////////////////////// 38 | bool Model::hasTexCoords() const { 39 | return _texCoords.size() > 0; 40 | } 41 | 42 | // 43 | // 44 | //////////////////////////////////////////////////////////// 45 | bool Model::hasTangents() const { 46 | return _sTangents.size() > 0; 47 | } 48 | 49 | // 50 | // 51 | //////////////////////////////////////////////////////////// 52 | bool Model::hasColors() const { 53 | return _colors.size() > 0; 54 | } 55 | 56 | // 57 | // 58 | //////////////////////////////////////////////////////////// 59 | int Model::getPositionSize() const { 60 | return _posSize; 61 | } 62 | 63 | // 64 | // 65 | //////////////////////////////////////////////////////////// 66 | int Model::getNormalSize() const { 67 | return 3; 68 | } 69 | 70 | // 71 | // 72 | //////////////////////////////////////////////////////////// 73 | int Model::getTexCoordSize() const { 74 | return _tcSize; 75 | } 76 | 77 | // 78 | // 79 | //////////////////////////////////////////////////////////// 80 | int Model::getTangentSize() const { 81 | return 3; 82 | } 83 | 84 | // 85 | // 86 | //////////////////////////////////////////////////////////// 87 | int Model::getColorSize() const { 88 | return _cSize; 89 | } 90 | 91 | 92 | //raw data access functions 93 | // These are to be used to get the raw array data from the file, each array has its own index 94 | 95 | // 96 | // 97 | //////////////////////////////////////////////////////////// 98 | const float* Model::getPositions() const { 99 | return ( _positions.size() > 0) ? &(_positions[0]) : 0; 100 | } 101 | 102 | // 103 | // 104 | //////////////////////////////////////////////////////////// 105 | const float* Model::getNormals() const { 106 | return ( _normals.size() > 0) ? &(_normals[0]) : 0; 107 | } 108 | 109 | // 110 | // 111 | //////////////////////////////////////////////////////////// 112 | const float* Model::getTexCoords() const { 113 | return ( _texCoords.size() > 0) ? &(_texCoords[0]) : 0; 114 | } 115 | 116 | // 117 | // 118 | //////////////////////////////////////////////////////////// 119 | const float* Model::getTangents() const { 120 | return ( _sTangents.size() > 0) ? &(_sTangents[0]) : 0; 121 | } 122 | 123 | // 124 | // 125 | //////////////////////////////////////////////////////////// 126 | const float* Model::getColors() const { 127 | return ( _colors.size() > 0) ? &(_colors[0]) : 0; 128 | } 129 | 130 | // 131 | // 132 | //////////////////////////////////////////////////////////// 133 | const GLuint* Model::getPositionIndices() const { 134 | return ( _pIndex.size() > 0) ? &(_pIndex[0]) : 0; 135 | } 136 | 137 | // 138 | // 139 | //////////////////////////////////////////////////////////// 140 | const GLuint* Model::getNormalIndices() const { 141 | return ( _nIndex.size() > 0) ? &(_nIndex[0]) : 0; 142 | } 143 | 144 | // 145 | // 146 | //////////////////////////////////////////////////////////// 147 | const GLuint* Model::getTexCoordIndices() const { 148 | return ( _tIndex.size() > 0) ? &(_tIndex[0]) : 0; 149 | } 150 | 151 | // 152 | // 153 | //////////////////////////////////////////////////////////// 154 | const GLuint* Model::getTangentIndices() const { 155 | return ( _tanIndex.size() > 0) ? &(_tanIndex[0]) : 0; 156 | } 157 | 158 | // 159 | // 160 | //////////////////////////////////////////////////////////// 161 | const GLuint* Model::getColorIndices() const { 162 | return ( _cIndex.size() > 0) ? &(_cIndex[0]) : 0; 163 | } 164 | 165 | // 166 | // 167 | //////////////////////////////////////////////////////////// 168 | int Model::getPositionCount() const { 169 | return (_posSize > 0) ? (int)_positions.size() / _posSize : 0; 170 | } 171 | 172 | // 173 | // 174 | //////////////////////////////////////////////////////////// 175 | int Model::getNormalCount() const { 176 | return (int)_normals.size() / 3; 177 | } 178 | 179 | // 180 | // 181 | //////////////////////////////////////////////////////////// 182 | int Model::getTexCoordCount() const { 183 | return (_tcSize > 0) ? (int)_texCoords.size() / _tcSize : 0; 184 | } 185 | 186 | // 187 | // 188 | //////////////////////////////////////////////////////////// 189 | int Model::getTangentCount() const { 190 | return (int)_sTangents.size() / 3; 191 | } 192 | 193 | // 194 | // 195 | //////////////////////////////////////////////////////////// 196 | int Model::getColorCount() const { 197 | return (_cSize > 0) ? (int)_colors.size() / _cSize : 0; 198 | } 199 | 200 | // 201 | // 202 | //////////////////////////////////////////////////////////// 203 | int Model::getIndexCount() const { 204 | return (int)_pIndex.size(); 205 | } 206 | 207 | //compiled data access functions 208 | 209 | // 210 | // 211 | //////////////////////////////////////////////////////////// 212 | const float* Model::getCompiledVertices() const { 213 | return (_vertices.size() > 0) ? &_vertices[0] : 0; 214 | } 215 | 216 | // 217 | // 218 | //////////////////////////////////////////////////////////// 219 | const GLuint* Model::getCompiledIndices( Model::PrimType prim) const { 220 | switch (prim) { 221 | case Model::eptPoints: 222 | return (_indices[0].size() > 0) ? &_indices[0][0] : 0; 223 | case Model::eptEdges: 224 | return (_indices[1].size() > 0) ? &_indices[1][0] : 0; 225 | case Model::eptTriangles: 226 | return (_indices[2].size() > 0) ? &_indices[2][0] : 0; 227 | case Model::eptTrianglesWithAdjacency: 228 | return (_indices[3].size() > 0) ? &_indices[3][0] : 0; 229 | } 230 | 231 | return 0; 232 | } 233 | 234 | // 235 | // 236 | //////////////////////////////////////////////////////////// 237 | int Model::getCompiledPositionOffset() const { 238 | return _pOffset; 239 | } 240 | 241 | // 242 | // 243 | //////////////////////////////////////////////////////////// 244 | int Model::getCompiledNormalOffset() const { 245 | return _nOffset; 246 | } 247 | 248 | // 249 | // 250 | //////////////////////////////////////////////////////////// 251 | int Model::getCompiledTexCoordOffset() const { 252 | return _tcOffset; 253 | } 254 | 255 | // 256 | // 257 | //////////////////////////////////////////////////////////// 258 | int Model::getCompiledTangentOffset() const { 259 | return _sTanOffset; 260 | } 261 | 262 | // 263 | // 264 | //////////////////////////////////////////////////////////// 265 | int Model::getCompiledColorOffset() const { 266 | return _cOffset; 267 | } 268 | 269 | // returns the size of the merged vertex in # of floats 270 | // 271 | // 272 | //////////////////////////////////////////////////////////// 273 | int Model::getCompiledVertexSize() const { 274 | return _vtxSize; 275 | } 276 | 277 | // 278 | // 279 | //////////////////////////////////////////////////////////// 280 | int Model::getCompiledVertexCount() const { 281 | return (_vtxSize > 0) ? (int)_vertices.size() / _vtxSize : 0; 282 | } 283 | 284 | // 285 | // 286 | //////////////////////////////////////////////////////////// 287 | int Model::getCompiledIndexCount( Model::PrimType prim) const { 288 | switch (prim) { 289 | case Model::eptPoints: 290 | return (int)_indices[0].size(); 291 | case Model::eptEdges: 292 | return (int)_indices[1].size(); 293 | case Model::eptTriangles: 294 | return (int)_indices[2].size(); 295 | case Model::eptTrianglesWithAdjacency: 296 | return (int)_indices[3].size(); 297 | } 298 | 299 | return 0; 300 | } 301 | 302 | // 303 | // 304 | //////////////////////////////////////////////////////////// 305 | int Model::getOpenEdgeCount() const { 306 | return _openEdges; 307 | } 308 | 309 | }; -------------------------------------------------------------------------------- /nvModel/nvQuaternion.h: -------------------------------------------------------------------------------- 1 | // 2 | // Template math library for common 3D functionality 3 | // 4 | // nvQuaterion.h - quaternion template and utility functions 5 | // 6 | // This code is in part deriver from glh, a cross platform glut helper library. 7 | // The copyright for glh follows this notice. 8 | // 9 | // Copyright (c) NVIDIA Corporation. All rights reserved. 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | /* 13 | Copyright (c) 2000 Cass Everitt 14 | Copyright (c) 2000 NVIDIA Corporation 15 | All rights reserved. 16 | 17 | Redistribution and use in source and binary forms, with or 18 | without modification, are permitted provided that the following 19 | conditions are met: 20 | 21 | * Redistributions of source code must retain the above 22 | copyright notice, this list of conditions and the following 23 | disclaimer. 24 | 25 | * Redistributions in binary form must reproduce the above 26 | copyright notice, this list of conditions and the following 27 | disclaimer in the documentation and/or other materials 28 | provided with the distribution. 29 | 30 | * The names of contributors to this software may not be used 31 | to endorse or promote products derived from this software 32 | without specific prior written permission. 33 | 34 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 35 | ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 36 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 37 | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 38 | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 39 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 40 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 41 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 42 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 43 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 44 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 45 | POSSIBILITY OF SUCH DAMAGE. 46 | 47 | 48 | Cass Everitt - cass@r3.nu 49 | */ 50 | #ifndef NV_QUATERNION_H 51 | #define NV_QUATERNION_H 52 | 53 | namespace nv { 54 | 55 | template class vec2; 56 | template class vec3; 57 | template class vec4; 58 | 59 | //////////////////////////////////////////////////////////////////////////////// 60 | // 61 | // Quaternion 62 | // 63 | //////////////////////////////////////////////////////////////////////////////// 64 | 65 | template< class T> 66 | class quaternion 67 | { 68 | public: 69 | 70 | quaternion() : x(0.0), y(0.0), z(0.0), w(0.0) 71 | { 72 | } 73 | 74 | quaternion( const T v[4] ) 75 | { 76 | set_value( v ); 77 | } 78 | 79 | 80 | quaternion( T q0, T q1, T q2, T q3 ) 81 | { 82 | set_value( q0, q1, q2, q3 ); 83 | } 84 | 85 | 86 | quaternion( const matrix4 & m ) 87 | { 88 | set_value( m ); 89 | } 90 | 91 | 92 | quaternion( const vec3 &axis, T radians ) 93 | { 94 | set_value( axis, radians ); 95 | } 96 | 97 | 98 | quaternion( const vec3 &rotateFrom, const vec3 &rotateTo ) 99 | { 100 | set_value( rotateFrom, rotateTo ); 101 | } 102 | 103 | quaternion( const vec3 & from_look, const vec3 & from_up, 104 | const vec3& to_look, const vec3& to_up) 105 | { 106 | set_value(from_look, from_up, to_look, to_up); 107 | } 108 | 109 | const T * get_value() const 110 | { 111 | return &_array[0]; 112 | } 113 | 114 | void get_value( T &q0, T &q1, T &q2, T &q3 ) const 115 | { 116 | q0 = _array[0]; 117 | q1 = _array[1]; 118 | q2 = _array[2]; 119 | q3 = _array[3]; 120 | } 121 | 122 | quaternion & set_value( T q0, T q1, T q2, T q3 ) 123 | { 124 | _array[0] = q0; 125 | _array[1] = q1; 126 | _array[2] = q2; 127 | _array[3] = q3; 128 | return *this; 129 | } 130 | 131 | void get_value( vec3 &axis, T &radians ) const 132 | { 133 | radians = T(acos( _array[3] ) * T(2.0)); 134 | if ( radians == T(0.0) ) 135 | axis = vec3( 0.0, 0.0, 1.0 ); 136 | else 137 | { 138 | axis[0] = _array[0]; 139 | axis[1] = _array[1]; 140 | axis[2] = _array[2]; 141 | axis = normalize(axis); 142 | } 143 | } 144 | 145 | void get_value( matrix4 & m ) const 146 | { 147 | T s, xs, ys, zs, wx, wy, wz, xx, xy, xz, yy, yz, zz; 148 | 149 | T norm = _array[0] * _array[0] + _array[1] * _array[1] + _array[2] * _array[2] + _array[3] * _array[3]; 150 | 151 | s = ( norm == T(0.0)) ? T(0.0) : ( T(2.0) / norm ); 152 | 153 | xs = _array[0] * s; 154 | ys = _array[1] * s; 155 | zs = _array[2] * s; 156 | 157 | wx = _array[3] * xs; 158 | wy = _array[3] * ys; 159 | wz = _array[3] * zs; 160 | 161 | xx = _array[0] * xs; 162 | xy = _array[0] * ys; 163 | xz = _array[0] * zs; 164 | 165 | yy = _array[1] * ys; 166 | yz = _array[1] * zs; 167 | zz = _array[2] * zs; 168 | 169 | m(0,0) = T( T(1.0) - ( yy + zz )); 170 | m(1,0) = T ( xy + wz ); 171 | m(2,0) = T ( xz - wy ); 172 | 173 | m(0,1) = T ( xy - wz ); 174 | m(1,1) = T ( T(1.0) - ( xx + zz )); 175 | m(2,1) = T ( yz + wx ); 176 | 177 | m(0,2) = T ( xz + wy ); 178 | m(1,2) = T ( yz - wx ); 179 | m(2,2) = T ( T(1.0) - ( xx + yy )); 180 | 181 | m(3,0) = m(3,1) = m(3,2) = m(0,3) = m(1,3) = m(2,3) = T(0.0); 182 | m(3,3) = T(1.0); 183 | } 184 | 185 | quaternion & set_value( const T * qp ) 186 | { 187 | for ( int i = 0; i < 4; i++) _array[i] = qp[i]; 188 | 189 | return *this; 190 | } 191 | 192 | quaternion & set_value( const matrix4 & m ) 193 | { 194 | T tr, s; 195 | int i, j, k; 196 | const int nxt[3] = { 1, 2, 0 }; 197 | 198 | tr = m(0,0) + m(1,1) + m(2,2); 199 | 200 | if ( tr > T(0) ) 201 | { 202 | s = T(sqrt( tr + m(3,3) )); 203 | _array[3] = T ( s * 0.5 ); 204 | s = T(0.5) / s; 205 | 206 | _array[0] = T ( ( m(1,2) - m(2,1) ) * s ); 207 | _array[1] = T ( ( m(2,0) - m(0,2) ) * s ); 208 | _array[2] = T ( ( m(0,1) - m(1,0) ) * s ); 209 | } 210 | else 211 | { 212 | i = 0; 213 | if ( m(1,1) > m(0,0) ) 214 | i = 1; 215 | 216 | if ( m(2,2) > m(i,i) ) 217 | i = 2; 218 | 219 | j = nxt[i]; 220 | k = nxt[j]; 221 | 222 | s = T(sqrt( ( m(i,j) - ( m(j,j) + m(k,k) )) + T(1.0) )); 223 | 224 | _array[i] = T ( s * 0.5 ); 225 | s = T(0.5 / s); 226 | 227 | _array[3] = T ( ( m(j,k) - m(k,j) ) * s ); 228 | _array[j] = T ( ( m(i,j) + m(j,i) ) * s ); 229 | _array[k] = T ( ( m(i,k) + m(k,i) ) * s ); 230 | } 231 | 232 | return *this; 233 | } 234 | 235 | quaternion & set_value( const vec3 &axis, T theta ) 236 | { 237 | T sqnorm = square_norm(axis); 238 | 239 | if (sqnorm == T(0.0)) 240 | { 241 | // axis too small. 242 | x = y = z = T(0.0); 243 | w = T(1.0); 244 | } 245 | else 246 | { 247 | theta *= T(0.5); 248 | T sin_theta = T(sin(theta)); 249 | 250 | if ( sqnorm != T(1)) 251 | sin_theta /= T(sqrt(sqnorm)); 252 | x = sin_theta * axis[0]; 253 | y = sin_theta * axis[1]; 254 | z = sin_theta * axis[2]; 255 | w = T(cos(theta)); 256 | } 257 | return *this; 258 | } 259 | 260 | quaternion & set_value( const vec3 & rotateFrom, const vec3 & rotateTo ) 261 | { 262 | vec3 p1, p2; 263 | T alpha; 264 | 265 | p1 = normalize(rotateFrom); 266 | p2 = normalize(rotateTo); 267 | 268 | alpha = dot( p1, p2); 269 | 270 | if( alpha == T(1.0) ) { 271 | *this = quaternion(); 272 | return *this; 273 | } 274 | 275 | // ensures that the anti-parallel case leads to a positive dot 276 | if( alpha == T(-1.0)) 277 | { 278 | vec3 v; 279 | 280 | if(p1[0] != p1[1] || p1[0] != p1[2]) 281 | v = vec3(p1[1], p1[2], p1[0]); 282 | else 283 | v = vec3(-p1[0], p1[1], p1[2]); 284 | 285 | v -= p1 * dot( p1, v); 286 | v = normalize(v); 287 | 288 | set_value(v, T(3.1415926)); 289 | return *this; 290 | } 291 | 292 | p1 = normalize( cross( p1, p2)); 293 | 294 | set_value(p1,T(acos(alpha))); 295 | 296 | return *this; 297 | } 298 | 299 | quaternion & set_value( const vec3 & from_look, const vec3 & from_up, 300 | const vec3 & to_look, const vec3 & to_up) 301 | { 302 | quaternion r_look = quaternion(from_look, to_look); 303 | 304 | vec3 rotated_from_up(from_up); 305 | r_look.mult_vec(rotated_from_up); 306 | 307 | quaternion r_twist = quaternion(rotated_from_up, to_up); 308 | 309 | *this = r_twist; 310 | *this *= r_look; 311 | return *this; 312 | } 313 | 314 | quaternion & operator *= ( const quaternion & qr ) { 315 | quaternion ql(*this); 316 | 317 | w = ql.w * qr.w - ql.x * qr.x - ql.y * qr.y - ql.z * qr.z; 318 | x = ql.w * qr.x + ql.x * qr.w + ql.y * qr.z - ql.z * qr.y; 319 | y = ql.w * qr.y + ql.y * qr.w + ql.z * qr.x - ql.x * qr.z; 320 | z = ql.w * qr.z + ql.z * qr.w + ql.x * qr.y - ql.y * qr.x; 321 | 322 | return *this; 323 | } 324 | 325 | friend quaternion normalize( const quaternion &q) { 326 | quaternion r(q); 327 | T rnorm = T(1.0) / T(sqrt( q.w * q.w + q.x * q.x + q.y * q.y + q.z * q.z)); 328 | 329 | r.x *= rnorm; 330 | r.y *= rnorm; 331 | r.z *= rnorm; 332 | r.w *= rnorm; 333 | } 334 | 335 | friend quaternion conjugate( const quaternion & q) { 336 | quaternion r(q); 337 | r._array[0] *= T(-1.0); 338 | r._array[1] *= T(-1.0); 339 | r._array[2] *= T(-1.0); 340 | return r; 341 | } 342 | 343 | friend quaternion inverse( const quaternion & q) { 344 | return conjugate(q); 345 | } 346 | 347 | // 348 | // Quaternion multiplication with cartesian vector 349 | // v' = q*v*q(star) 350 | // 351 | void mult_vec( const vec3 &src, vec3 &dst ) const 352 | { 353 | T v_coef = w * w - x * x - y * y - z * z; 354 | T u_coef = T(2.0) * (src[0] * x + src[1] * y + src[2] * z); 355 | T c_coef = T(2.0) * w; 356 | 357 | dst.v[0] = v_coef * src.v[0] + u_coef * x + c_coef * (y * src.v[2] - z * src.v[1]); 358 | dst.v[1] = v_coef * src.v[1] + u_coef * y + c_coef * (z * src.v[0] - x * src.v[2]); 359 | dst.v[2] = v_coef * src.v[2] + u_coef * z + c_coef * (x * src.v[1] - y * src.v[0]); 360 | } 361 | 362 | void mult_vec( vec3 & src_and_dst) const 363 | { 364 | mult_vec(vec3(src_and_dst), src_and_dst); 365 | } 366 | 367 | void scale_angle( T scaleFactor ) { 368 | vec3 axis; 369 | T radians; 370 | 371 | get_value(axis, radians); 372 | radians *= scaleFactor; 373 | set_value(axis, radians); 374 | } 375 | 376 | friend quaternion slerp( const quaternion & p, const quaternion & q, T alpha ) 377 | { 378 | quaternion r; 379 | 380 | T cos_omega = p.x * q.x + p.y * q.y + p.z * q.z + p.w * q.w; 381 | // if B is on opposite hemisphere from A, use -B instead 382 | 383 | int bflip; 384 | if ( ( bflip = (cos_omega < T(0))) ) 385 | cos_omega = -cos_omega; 386 | 387 | // complementary interpolation parameter 388 | T beta = T(1) - alpha; 389 | 390 | if(cos_omega >= T(1)) 391 | return p; 392 | 393 | T omega = T(acos(cos_omega)); 394 | T one_over_sin_omega = T(1.0) / T(sin(omega)); 395 | 396 | beta = T(sin(omega*beta) * one_over_sin_omega); 397 | alpha = T(sin(omega*alpha) * one_over_sin_omega); 398 | 399 | if (bflip) 400 | alpha = -alpha; 401 | 402 | r.x = beta * p._array[0]+ alpha * q._array[0]; 403 | r.y = beta * p._array[1]+ alpha * q._array[1]; 404 | r.z = beta * p._array[2]+ alpha * q._array[2]; 405 | r.w = beta * p._array[3]+ alpha * q._array[3]; 406 | return r; 407 | } 408 | 409 | T & operator []( int i ) { 410 | return _array[i]; 411 | } 412 | 413 | const T & operator []( int i ) const { 414 | return _array[i]; 415 | } 416 | 417 | 418 | friend bool operator == ( const quaternion & lhs, const quaternion & rhs ) { 419 | bool r = true; 420 | for (int i = 0; i < 4; i++) 421 | r &= lhs._array[i] == rhs._array[i]; 422 | return r; 423 | } 424 | 425 | friend bool operator != ( const quaternion & lhs, const quaternion & rhs ) { 426 | bool r = true; 427 | for (int i = 0; i < 4; i++) 428 | r &= lhs._array[i] == rhs._array[i]; 429 | return r; 430 | } 431 | 432 | friend quaternion operator * ( const quaternion & lhs, const quaternion & rhs ) { 433 | quaternion r(lhs); 434 | r *= rhs; 435 | return r; 436 | } 437 | 438 | 439 | union 440 | { 441 | struct 442 | { 443 | T x; 444 | T y; 445 | T z; 446 | T w; 447 | }; 448 | T _array[4]; 449 | }; 450 | 451 | }; 452 | 453 | 454 | 455 | }; 456 | 457 | #endif 458 | -------------------------------------------------------------------------------- /nvModel/nvSDKPath.h: -------------------------------------------------------------------------------- 1 | // 2 | // 3 | // 4 | // 5 | // 6 | // 7 | //////////////////////////////////////////////////////////////////////////////////////////////////// 8 | 9 | #ifndef NV_SDK_PATH_H 10 | #define NV_SDK_PATH_H 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | namespace nv { 18 | 19 | class SDKPath { 20 | 21 | public: 22 | SDKPath() { 23 | const char *env = getenv("NVSDK10OGL_ROOT"); 24 | 25 | //search local paths first, in-case someone has the SDK installed while hacking another copy 26 | _pathList.push_back("./"); // present directory 27 | _pathList.push_back("../"); // back one 28 | _pathList.push_back("../../"); // back two 29 | 30 | if ( env) { 31 | _pathList.push_back(std::string(env) + "/"); // Path lacks a terminating slash 32 | } 33 | } 34 | 35 | void addPath( const std::string &path) { 36 | _pathList.push_back(path); 37 | } 38 | 39 | void clearPaths() { 40 | _pathList.clear(); 41 | } 42 | 43 | bool getFilePath( const std::string &file, std::string &path) { 44 | std::string pathString; 45 | 46 | for ( std::vector::iterator it = _pathList.begin(); it != _pathList.end(); it++) { 47 | pathString = *it + file; 48 | FILE *fp = fopen( pathString.c_str(), "rb"); 49 | if (fp) { 50 | fclose(fp); 51 | path = pathString; 52 | return true; 53 | } 54 | } 55 | 56 | return false; 57 | } 58 | 59 | bool getPath( const std::string &file, std::string &path) { 60 | std::string pathString; 61 | 62 | for ( std::vector::iterator it = _pathList.begin(); it != _pathList.end(); it++) { 63 | pathString = *it + file; 64 | FILE *fp = fopen( pathString.c_str(), "rb"); 65 | if (fp) { 66 | fclose(fp); 67 | path = *it; 68 | return true; 69 | } 70 | } 71 | 72 | return false; 73 | } 74 | 75 | private: 76 | std::vector _pathList; 77 | 78 | }; 79 | }; 80 | 81 | #endif -------------------------------------------------------------------------------- /nvModel/nvShaderUtils.h: -------------------------------------------------------------------------------- 1 | // 2 | // Utility functions for compiling shaders and programs 3 | // 4 | // Author: Evan Hart 5 | // Copyright (c) NVIDIA Corporation. All rights reserved. 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | 9 | #ifndef NV_SHADER_UTILS_H 10 | #define NV_SHADER_UTILS_H 11 | 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | namespace nv 18 | { 19 | 20 | 21 | // 22 | // 23 | //////////////////////////////////////////////////////////// 24 | inline GLuint CompileGLSLShader( GLenum target, const char* shader) 25 | { 26 | GLuint object; 27 | 28 | object = glCreateShader( target); 29 | 30 | if (!object) 31 | return object; 32 | 33 | glShaderSource( object, 1, &shader, NULL); 34 | 35 | glCompileShader(object); 36 | 37 | // check if shader compiled 38 | GLint compiled = 0; 39 | glGetShaderiv(object, GL_COMPILE_STATUS, &compiled); 40 | 41 | if (!compiled) 42 | { 43 | #ifdef NV_REPORT_COMPILE_ERRORS 44 | char temp[256] = ""; 45 | glGetShaderInfoLog( object, 256, NULL, temp); 46 | fprintf( stderr, "Compile failed:\n%s\n", temp); 47 | #endif 48 | glDeleteShader( object); 49 | return 0; 50 | } 51 | 52 | return object; 53 | } 54 | 55 | // 56 | // 57 | //////////////////////////////////////////////////////////// 58 | inline GLuint CompileGLSLShaderFromFile( GLenum target, const char* filename) 59 | { 60 | FILE *shaderFile; 61 | char *text; 62 | long size; 63 | 64 | //must read files as binary to prevent problems from newline translation 65 | shaderFile = fopen( filename, "rb"); 66 | 67 | if ( shaderFile == NULL) 68 | return 0; 69 | 70 | fseek( shaderFile, 0, SEEK_END); 71 | 72 | size = ftell(shaderFile); 73 | 74 | fseek( shaderFile, 0, SEEK_SET); 75 | 76 | text = new char[size+1]; 77 | 78 | fread( text, size, 1, shaderFile); 79 | 80 | fclose( shaderFile); 81 | 82 | text[size] = '\0'; 83 | 84 | GLuint object = CompileGLSLShader( target, text); 85 | 86 | delete []text; 87 | 88 | return object; 89 | } 90 | 91 | 92 | // Create a program composed of vertex and fragment shaders. 93 | inline GLuint LinkGLSLProgram( GLuint vertexShader, GLuint fragmentShader) 94 | { 95 | GLuint program = glCreateProgram(); 96 | glAttachShader(program, vertexShader); 97 | glAttachShader(program, fragmentShader); 98 | glLinkProgram(program); 99 | 100 | #ifdef NV_REPORT_COMPILE_ERRORS 101 | // Get error log. 102 | GLint charsWritten, infoLogLength; 103 | glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLength); 104 | 105 | char * infoLog = new char[infoLogLength]; 106 | glGetProgramInfoLog(program, infoLogLength, &charsWritten, infoLog); 107 | printf(infoLog); 108 | delete [] infoLog; 109 | #endif 110 | 111 | // Test linker result. 112 | GLint linkSucceed = GL_FALSE; 113 | glGetProgramiv(program, GL_LINK_STATUS, &linkSucceed); 114 | 115 | if (linkSucceed == GL_FALSE) 116 | { 117 | glDeleteProgram(program); 118 | return 0; 119 | } 120 | 121 | return program; 122 | } 123 | 124 | 125 | // Create a program composed of vertex, geometry and fragment shaders. 126 | inline GLuint LinkGLSLProgram( GLuint vertexShader, GLuint geometryShader, GLint inputType, GLint vertexOut, GLint outputType, GLuint fragmentShader) 127 | { 128 | GLuint program = glCreateProgram(); 129 | glAttachShader(program, vertexShader); 130 | glAttachShader(program, geometryShader); 131 | glProgramParameteriEXT(program, GL_GEOMETRY_INPUT_TYPE_EXT, inputType); 132 | glProgramParameteriEXT(program, GL_GEOMETRY_VERTICES_OUT_EXT, vertexOut); 133 | glProgramParameteriEXT(program, GL_GEOMETRY_OUTPUT_TYPE_EXT, outputType); 134 | glAttachShader(program, fragmentShader); 135 | glLinkProgram(program); 136 | 137 | #ifdef NV_REPORT_COMPILE_ERRORS 138 | // Get error log. 139 | GLint charsWritten, infoLogLength; 140 | glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLength); 141 | 142 | char * infoLog = new char[infoLogLength]; 143 | glGetProgramInfoLog(program, infoLogLength, &charsWritten, infoLog); 144 | printf(infoLog); 145 | delete [] infoLog; 146 | #endif 147 | 148 | // Test linker result. 149 | GLint linkSucceed = GL_FALSE; 150 | glGetProgramiv(program, GL_LINK_STATUS, &linkSucceed); 151 | 152 | if (linkSucceed == GL_FALSE) 153 | { 154 | glDeleteProgram(program); 155 | return 0; 156 | } 157 | 158 | return program; 159 | } 160 | 161 | 162 | // 163 | // 164 | //////////////////////////////////////////////////////////// 165 | inline GLuint CompileASMShader(GLenum program_type, const char *code) 166 | { 167 | GLuint program_id; 168 | glGenProgramsARB(1, &program_id); 169 | glBindProgramARB(program_type, program_id); 170 | glProgramStringARB(program_type, GL_PROGRAM_FORMAT_ASCII_ARB, (GLsizei) strlen(code), (GLubyte *) code); 171 | 172 | GLint error_pos; 173 | glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &error_pos); 174 | if (error_pos != -1) { 175 | #ifdef NV_REPORT_COMPILE_ERRORS 176 | const GLubyte *error_string; 177 | error_string = glGetString(GL_PROGRAM_ERROR_STRING_ARB); 178 | fprintf(stderr, "Program error at position: %d\n%s\n", (int)error_pos, error_string); 179 | #endif 180 | return 0; 181 | } 182 | return program_id; 183 | } 184 | 185 | // 186 | // 187 | //////////////////////////////////////////////////////////// 188 | inline GLuint CompileASMShaderFromFile( GLenum target, const char* filename) 189 | { 190 | FILE *shaderFile; 191 | char *text; 192 | long size; 193 | 194 | //must read files as binary to prevent problems from newline translation 195 | shaderFile = fopen( filename, "rb"); 196 | 197 | if ( shaderFile == NULL) 198 | return 0; 199 | 200 | fseek( shaderFile, 0, SEEK_END); 201 | 202 | size = ftell(shaderFile); 203 | 204 | fseek( shaderFile, 0, SEEK_SET); 205 | 206 | text = new char[size+1]; 207 | 208 | fread( text, size, 1, shaderFile); 209 | 210 | fclose( shaderFile); 211 | 212 | text[size] = '\0'; 213 | 214 | GLuint program_id = CompileASMShader( target, text); 215 | 216 | delete []text; 217 | 218 | return program_id; 219 | } 220 | 221 | } // nv namespace 222 | 223 | #endif -------------------------------------------------------------------------------- /nvModel/nvUtils.cc: -------------------------------------------------------------------------------- 1 | #include "nvUtils.h" 2 | #include "nvModel.h" 3 | 4 | namespace nv { 5 | inline void SetVertexNormal(Model *model) { 6 | glVertexPointer(model->getPositionSize(), GL_FLOAT, 7 | model->getCompiledVertexSize() * sizeof(float), 8 | model->getCompiledVertices()); 9 | glNormalPointer(GL_FLOAT, model->getCompiledVertexSize() * sizeof(float), 10 | model->getCompiledVertices() + 11 | model->getCompiledNormalOffset()); 12 | 13 | glEnableClientState( GL_VERTEX_ARRAY); 14 | glEnableClientState( GL_NORMAL_ARRAY); 15 | } 16 | 17 | inline void SetTexCoord(Model *model) { 18 | if ( model->hasTexCoords()) { 19 | glClientActiveTexture(GL_TEXTURE0); 20 | glTexCoordPointer(model->getTexCoordSize(), GL_FLOAT, 21 | model->getCompiledVertexSize() * sizeof(float), 22 | model->getCompiledVertices() + 23 | model->getCompiledTexCoordOffset()); 24 | glEnableClientState( GL_TEXTURE_COORD_ARRAY); 25 | } 26 | } 27 | inline void SetTangent(Model *model) { 28 | if ( model->hasTangents()) { 29 | glClientActiveTexture(GL_TEXTURE1); 30 | glTexCoordPointer(model->getTangentSize(), GL_FLOAT, 31 | model->getCompiledVertexSize() * sizeof(float), 32 | model->getCompiledVertices() + 33 | model->getCompiledTangentOffset()); 34 | glEnableClientState( GL_TEXTURE_COORD_ARRAY); 35 | } 36 | } 37 | 38 | inline void DisableVertexNormalTexCoord() { 39 | glDisableClientState( GL_VERTEX_ARRAY); 40 | glDisableClientState( GL_NORMAL_ARRAY); 41 | glDisableClientState( GL_TEXTURE_COORD_ARRAY); 42 | } 43 | 44 | inline void DisableVertexNormalTexCoordTangent() { 45 | glDisableClientState( GL_VERTEX_ARRAY); 46 | glDisableClientState( GL_NORMAL_ARRAY); 47 | glClientActiveTexture(GL_TEXTURE0); 48 | glDisableClientState( GL_TEXTURE_COORD_ARRAY); 49 | glClientActiveTexture(GL_TEXTURE1); 50 | glDisableClientState( GL_TEXTURE_COORD_ARRAY); 51 | } 52 | 53 | void DrawWithAdjacency(Model *model) { 54 | SetVertexNormal(model); 55 | SetTexCoord(model); 56 | SetTangent(model); 57 | nv::Model::PrimType prim_type = nv::Model::eptTrianglesWithAdjacency; 58 | glDrawElements(GL_TRIANGLES_ADJACENCY_EXT, 59 | model->getCompiledIndexCount(prim_type), 60 | GL_UNSIGNED_INT, 61 | model->getCompiledIndices(prim_type)); 62 | DisableVertexNormalTexCoordTangent(); 63 | } 64 | void DrawTriangles(Model *model) { 65 | SetVertexNormal(model); 66 | SetTexCoord(model); 67 | SetTangent(model); 68 | glDrawElements(GL_TRIANGLES, 69 | model->getCompiledIndexCount(nv::Model::eptTriangles), 70 | GL_UNSIGNED_INT, 71 | model->getCompiledIndices( nv::Model::eptTriangles)); 72 | DisableVertexNormalTexCoordTangent(); 73 | } 74 | void DrawEdges(Model *model) { 75 | SetVertexNormal(model); 76 | SetTexCoord(model); 77 | glDrawElements(GL_LINES, 78 | model->getCompiledIndexCount(nv::Model::eptEdges), 79 | GL_UNSIGNED_INT, 80 | model->getCompiledIndices(nv::Model::eptEdges)); 81 | DisableVertexNormalTexCoord(); 82 | } 83 | void DrawPoints(Model *model) { 84 | SetVertexNormal(model); 85 | SetTexCoord(model); 86 | glDrawElements(GL_POINTS, 87 | model->getCompiledIndexCount(nv::Model::eptPoints), 88 | GL_UNSIGNED_INT, 89 | model->getCompiledIndices(nv::Model::eptPoints)); 90 | DisableVertexNormalTexCoord(); 91 | } 92 | } // namespace nv 93 | -------------------------------------------------------------------------------- /nvModel/nvUtils.h: -------------------------------------------------------------------------------- 1 | #ifndef VENUS_NVMODEL_NVUTILS_H 2 | #define VENUS_NVMODEL_NVUTILS_H 3 | 4 | namespace nv { 5 | class Model; 6 | void DrawTriangles(Model *model); 7 | void DrawWithAdjacency(Model *model); 8 | void DrawPoints(Model *model); 9 | void DrawEdges(Model *model); 10 | //void DrawNvModel(Model *model); 11 | } // namespace nv 12 | 13 | #endif // VENUS_NVMODEL_NVUTILS_H -------------------------------------------------------------------------------- /src/GLSL/shadow_multi_leak_fragment.glsl: -------------------------------------------------------------------------------- 1 | //---------------------------------------------------------------------------------- 2 | // File: shadow_multi_leak_fragment.glsl 3 | // Author: Rouslan Dimitrov 4 | // Email: sdkfeedback@nvidia.com 5 | // Cascaded shadows maps, multiple shadow samples 6 | // Copyright (c) NVIDIA Corporation. All rights reserved. 7 | //---------------------------------------------------------------------------------- 8 | #version 120 9 | #extension GL_EXT_texture_array : enable 10 | 11 | uniform sampler2DArray shadowmap; 12 | uniform sampler2D tex; 13 | 14 | // List of shadow coord lookup matrices for each shadow map segment 15 | uniform mat4 textureMatrixList[4]; 16 | 17 | uniform vec4 farbounds; 18 | 19 | varying vec4 position; 20 | varying vec3 normal; 21 | 22 | // sample offsets 23 | const int nsamples = 8; 24 | uniform vec4 offset[nsamples]; 25 | 26 | float getOccCoef(vec4 shadow_coord) { 27 | // get the stored depth 28 | float shadow_d = texture2DArray(shadowmap, shadow_coord.xyz).x; 29 | 30 | // get the difference of the stored depth and the distance of this fragment to the light 31 | float diff = shadow_d - shadow_coord.w; 32 | 33 | // smoothen the result a bit, to avoid aliasing at shadow contact point 34 | return clamp( diff*250.0 + 1.0, 0.0, 1.0); 35 | } 36 | 37 | float shadowCoef() { 38 | const float scale = 2.0/4096.0; 39 | int index = 3; 40 | 41 | // find the appropriate depth map to look up in based on the depth of this fragment 42 | if(gl_FragCoord.z < farbounds.x) { 43 | index = 0; 44 | } else if(gl_FragCoord.z < farbounds.y) { 45 | index = 1; 46 | } else if(gl_FragCoord.z < farbounds.z) { 47 | index = 2; 48 | } 49 | 50 | // transform this fragment's position from world space to scaled light clip space 51 | // such that the xy coordinates are in [0;1] 52 | vec4 shadow_coord = textureMatrixList[index] * position; 53 | 54 | shadow_coord.w = shadow_coord.z; 55 | 56 | // tell glsl in which layer to do the look up 57 | shadow_coord.z = float(index); 58 | 59 | // sum shadow samples 60 | float shadow_coef = getOccCoef(shadow_coord); 61 | 62 | for(int i = 1; i < nsamples ; i++) { 63 | shadow_coef += getOccCoef(shadow_coord + scale*offset[i]); 64 | } 65 | shadow_coef /= nsamples; 66 | 67 | return shadow_coef; 68 | } 69 | 70 | void main() { 71 | const float shadow_ambient = 0.9; 72 | vec4 color_tex = texture2D(tex, gl_TexCoord[0].st); 73 | float shadow_coef = shadowCoef(); 74 | float fog = clamp(gl_Fog.scale*(gl_Fog.end + position.z), 0.0, 1.0); 75 | gl_FragColor = mix(gl_Fog.color, (shadow_ambient * shadow_coef * gl_Color * color_tex + (1.0 - shadow_ambient) * color_tex), fog); 76 | } 77 | -------------------------------------------------------------------------------- /src/GLSL/shadow_multi_noleak_fragment.glsl: -------------------------------------------------------------------------------- 1 | //---------------------------------------------------------------------------------- 2 | // File: shadow_multi_noleak_fragment.glsl 3 | // Author: Rouslan Dimitrov 4 | // Email: sdkfeedback@nvidia.com 5 | // Cascaded shadows maps, multiple shadow samples with leak suppression 6 | // Copyright (c) NVIDIA Corporation. All rights reserved. 7 | //---------------------------------------------------------------------------------- 8 | #version 120 9 | #extension GL_EXT_texture_array : enable 10 | 11 | uniform sampler2DArray stex; 12 | uniform sampler2D tex; 13 | 14 | uniform vec4 farbounds; 15 | 16 | varying vec4 position; 17 | varying vec3 normal; 18 | 19 | // sample offsets 20 | const int nsamples = 8; 21 | uniform vec4 offset[nsamples] = { vec4(0.000000, 0.000000, 0.0, 0.0), 22 | vec4(0.079821, 0.165750, 0.0, 0.0), 23 | vec4(-0.331500, 0.159642, 0.0, 0.0), 24 | vec4(-0.239463, -0.497250, 0.0, 0.0), 25 | vec4(0.662999, -0.319284, 0.0, 0.0), 26 | vec4(0.399104, 0.828749, 0.0, 0.0), 27 | vec4(-0.994499, 0.478925, 0.0, 0.0), 28 | vec4(-0.558746, -1.160249, 0.0, 0.0) }; 29 | 30 | float getOccCoef(vec4 shadow_coord) 31 | { 32 | // get the stored depth 33 | float shadow_d = texture2DArray(stex, shadow_coord.xyz).x; 34 | 35 | // get the difference of the stored depth and the distance of this fragment to the light 36 | float diff = shadow_d - shadow_coord.w; 37 | 38 | // smoothen the result a bit, so that we don't get hard shadows 39 | return clamp( diff*250.0 + 1.0, 0.0, 1.0); 40 | } 41 | 42 | 43 | float shadowCoef() 44 | { 45 | const float scale = 2.0/4096.0; 46 | int index = 3; 47 | 48 | // find the appropriate depth map to look up in based on the depth of this fragment 49 | if(gl_FragCoord.z < farbounds.x) 50 | index = 0; 51 | else if(gl_FragCoord.z < farbounds.y) 52 | index = 1; 53 | else if(gl_FragCoord.z < farbounds.z) 54 | index = 2; 55 | 56 | // transform this fragment's position from world space to scaled light clip space 57 | // such that the xy coordinates are in [0;1] 58 | vec4 shadow_coord = gl_TextureMatrix[index]*position; 59 | 60 | vec4 light_normal4 = gl_TextureMatrix[index+4]*vec4(normal, 0.0); 61 | vec3 light_normal = normalize(light_normal4.xyz); 62 | 63 | float d = -dot(light_normal, shadow_coord.xyz); 64 | 65 | shadow_coord.w = shadow_coord.z; 66 | 67 | // tell glsl in which layer to do the look up 68 | shadow_coord.z = float(index); 69 | 70 | // sum shadow samples 71 | float shadow_coef = getOccCoef(shadow_coord); 72 | 73 | for(int i=1; i 0.0) { 51 | shadow_coord = textureMatrixList[index+1] * position; 52 | 53 | shadow_coord.w = shadow_coord.z; 54 | shadow_coord.z = float(index+1); 55 | 56 | ret = ret*(1.0-blend) + shadow2DArray(shadowmap, shadow_coord).x*blend; 57 | } 58 | 59 | return ret; 60 | } 61 | 62 | void main() { 63 | const float shadow_ambient = 0.9; 64 | vec4 color_tex = texture2D(tex, gl_TexCoord[0].st); 65 | float shadow_coef = shadowCoef(); 66 | float fog = clamp(gl_Fog.scale*(gl_Fog.end + position.z), 0.0, 1.0); 67 | 68 | gl_FragColor = vec4(shadow_coef, shadow_coef, shadow_coef, 1.0); 69 | } 70 | -------------------------------------------------------------------------------- /src/GLSL/shadow_single_fragment.glsl: -------------------------------------------------------------------------------- 1 | //---------------------------------------------------------------------------------- 2 | // File: shadow_single_fragment.glsl 3 | // Author: Rouslan Dimitrov 4 | // Email: sdkfeedback@nvidia.com 5 | // Cascaded shadows maps, single shadow sample 6 | // Copyright (c) NVIDIA Corporation. All rights reserved. 7 | //---------------------------------------------------------------------------------- 8 | #version 120 9 | #extension GL_EXT_texture_array : enable 10 | 11 | uniform sampler2D tex; 12 | uniform vec4 farbounds; 13 | 14 | varying vec4 position; 15 | 16 | uniform sampler2DArray shadowmap; 17 | 18 | // List of shadow coord lookup matrices for each shadow map segment 19 | uniform mat4 textureMatrixList[4]; 20 | 21 | float shadowCoef() { 22 | int index = 3; 23 | 24 | // find the appropriate depth map to look up in based on the depth of this fragment 25 | if(gl_FragCoord.z < farbounds.x) { 26 | index = 0; 27 | } else if(gl_FragCoord.z < farbounds.y) { 28 | index = 1; 29 | } else if(gl_FragCoord.z < farbounds.z) { 30 | index = 2; 31 | } 32 | 33 | // transform this fragment's position from view space to scaled light clip space 34 | // such that the xy coordinates are in [0;1] 35 | // note there is no need to divide by w for othogonal light sources 36 | vec4 shadow_coord = textureMatrixList[index] * position; 37 | 38 | shadow_coord.w = shadow_coord.z; 39 | 40 | // tell glsl in which layer to do the look up 41 | shadow_coord.z = float(index); 42 | 43 | // get the stored depth 44 | float shadow_d = texture2DArray(shadowmap, shadow_coord.xyz).x; 45 | 46 | // get the difference of the stored depth and the distance of this fragment to the light 47 | float diff = shadow_d - shadow_coord.w; 48 | 49 | // smoothen the result a bit, to avoid aliasing at shadow contact point 50 | return clamp(diff * 250.0 + 1.0, 0.0, 1.0); 51 | } 52 | 53 | void main() { 54 | const float shadow_ambient = 0.9; 55 | vec4 color_tex = texture2D(tex, gl_TexCoord[0].st); 56 | float shadow_coef = shadowCoef(); 57 | float fog = clamp(gl_Fog.scale*(gl_Fog.end + position.z), 0.0, 1.0); 58 | gl_FragColor = mix(gl_Fog.color, (shadow_ambient * shadow_coef * gl_Color * color_tex + (1.0 - shadow_ambient) * color_tex), fog); 59 | } -------------------------------------------------------------------------------- /src/GLSL/shadow_single_hl_fragment.glsl: -------------------------------------------------------------------------------- 1 | //---------------------------------------------------------------------------------- 2 | // File: shadow_single_hl_fragment.glsl 3 | // Author: Rouslan Dimitrov 4 | // Email: sdkfeedback@nvidia.com 5 | // Cascaded shadows maps, highlight layers using different colors 6 | // Copyright (c) NVIDIA Corporation. All rights reserved. 7 | //---------------------------------------------------------------------------------- 8 | #version 120 9 | #extension GL_EXT_texture_array : enable 10 | 11 | uniform sampler2D tex; 12 | uniform vec4 farbounds; 13 | 14 | varying vec4 position; 15 | 16 | // List of shadow coord lookup matrices for each shadow map segment 17 | uniform mat4 textureMatrixList[4]; 18 | 19 | // Shadow split colors 20 | uniform vec4 color[4] = vec4[4]( 21 | vec4(0.7, 0.7, 1.0, 1.0), 22 | vec4(0.7, 1.0, 0.7, 1.0), 23 | vec4(1.0, 0.7, 0.7, 1.0), 24 | vec4(1.0, 1.0, 1.0, 1.0)); 25 | 26 | uniform sampler2DArray shadowmap; 27 | 28 | vec4 shadowCoef() { 29 | int index = 3; 30 | 31 | if(gl_FragCoord.z < farbounds.x) { 32 | index = 0; 33 | } else if(gl_FragCoord.z < farbounds.y) { 34 | index = 1; 35 | } else if(gl_FragCoord.z < farbounds.z) { 36 | index = 2; 37 | } 38 | 39 | vec4 shadow_coord = textureMatrixList[index] * position; 40 | 41 | shadow_coord.w = shadow_coord.z; 42 | shadow_coord.z = float(index); 43 | 44 | float shadow_d = texture2DArray(shadowmap, shadow_coord.xyz).x; 45 | float diff = shadow_d - shadow_coord.w; 46 | return clamp( diff*250.0 + 1.0, 0.0, 1.0) * color[index]; 47 | } 48 | 49 | void main() { 50 | const float shadow_ambient = 0.9; 51 | vec4 color_tex = vec4(1.0); //texture2D(tex, gl_TexCoord[0].st); 52 | vec4 shadow_coef = shadowCoef(); 53 | float fog = clamp(gl_Fog.scale*(gl_Fog.end + position.z), 0.0, 1.0); 54 | gl_FragColor = mix(gl_Fog.color, (shadow_ambient * shadow_coef * gl_Color * color_tex + (1.0 - shadow_ambient) * color_tex), fog); 55 | } 56 | -------------------------------------------------------------------------------- /src/GLSL/shadow_vertex.glsl: -------------------------------------------------------------------------------- 1 | //---------------------------------------------------------------------------------- 2 | // File: shadow_vertex.glsl 3 | // Author: Rouslan Dimitrov 4 | // Email: sdkfeedback@nvidia.com 5 | // Copyright (c) NVIDIA Corporation. All rights reserved. 6 | //---------------------------------------------------------------------------------- 7 | 8 | varying vec4 position; 9 | 10 | uniform vec4 lightdir; 11 | uniform vec4 lightcolor; 12 | 13 | uniform mat3 normalMatrix; 14 | uniform mat4 modelViewMatrix; 15 | uniform mat4 projectionMatrix; 16 | 17 | void main() { 18 | position = modelViewMatrix * gl_Vertex; 19 | gl_Position = projectionMatrix * position; 20 | vec3 normal = normalize(normalMatrix * gl_Normal); 21 | 22 | gl_FrontColor = gl_Color * lightcolor * vec4(max(dot(normal, lightdir.xyz), 0.0)); 23 | 24 | gl_TexCoord[0] = gl_MultiTexCoord0; 25 | } -------------------------------------------------------------------------------- /src/GLSL/view_fragment.glsl: -------------------------------------------------------------------------------- 1 | //---------------------------------------------------------------------------------- 2 | // File: view_fragment.glsl 3 | // Author: Rouslan Dimitrov 4 | // Email: sdkfeedback@nvidia.com 5 | // Copyright (c) NVIDIA Corporation. All rights reserved. 6 | //---------------------------------------------------------------------------------- 7 | #version 120 8 | #extension GL_EXT_texture_array : enable 9 | 10 | uniform sampler2DArray tex; 11 | uniform float layer; 12 | 13 | void main() { 14 | vec4 tex_coord = vec4(gl_TexCoord[0].x, gl_TexCoord[0].y, layer, 1.0); 15 | gl_FragColor = texture2DArray(tex, tex_coord.xyz); 16 | } -------------------------------------------------------------------------------- /src/GLSL/view_vertex.glsl: -------------------------------------------------------------------------------- 1 | //---------------------------------------------------------------------------------- 2 | // File: view_vertex.glsl 3 | // Author: Rouslan Dimitrov 4 | // Email: sdkfeedback@nvidia.com 5 | // Copyright (c) NVIDIA Corporation. All rights reserved. 6 | //---------------------------------------------------------------------------------- 7 | 8 | void main() { 9 | gl_TexCoord[0] = vec4(0.5) * gl_Vertex + vec4(0.5); 10 | gl_Position = gl_Vertex; 11 | } 12 | -------------------------------------------------------------------------------- /src/GLSL/write_depth_fragment.glsl: -------------------------------------------------------------------------------- 1 | //---------------------------------------------------------------------------------- 2 | // File: shadow_single_fragment.glsl 3 | // Author: Rouslan Dimitrov 4 | // Email: sdkfeedback@nvidia.com 5 | // Cascaded shadows maps, single shadow sample 6 | // Copyright (c) NVIDIA Corporation. All rights reserved. 7 | //---------------------------------------------------------------------------------- 8 | #version 120 9 | 10 | void main() { 11 | //gl_FragColor = vec4(1.0); 12 | gl_FragDepth = gl_FragCoord.z; 13 | } 14 | -------------------------------------------------------------------------------- /src/GLSL/write_depth_vertex.glsl: -------------------------------------------------------------------------------- 1 | //---------------------------------------------------------------------------------- 2 | // File: shadow_vertex.glsl 3 | // Author: Rouslan Dimitrov 4 | // Email: sdkfeedback@nvidia.com 5 | // Copyright (c) NVIDIA Corporation. All rights reserved. 6 | //---------------------------------------------------------------------------------- 7 | 8 | uniform mat3 normalMatrix; 9 | uniform mat4 modelViewMatrix; 10 | uniform mat4 projectionMatrix; 11 | 12 | void main() { 13 | vec4 position = projectionMatrix * modelViewMatrix * gl_Vertex; 14 | gl_Position = position; 15 | } 16 | -------------------------------------------------------------------------------- /src/camera.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /** */ 4 | namespace GKR { 5 | 6 | /** */ 7 | Viewport::Viewport() : 8 | m_x(0), 9 | m_y(0), 10 | m_width(0), 11 | m_height(0), 12 | m_half_width(0), 13 | m_half_height(0) { 14 | } 15 | 16 | /** */ 17 | void Viewport::set(int n_x, int n_y, int n_width, int n_height) { 18 | m_x = n_x; 19 | m_y = n_y; 20 | m_width = n_width; 21 | m_height = n_height; 22 | m_half_width = m_width / 2; 23 | m_half_height = m_height / 2; 24 | } 25 | 26 | /** */ 27 | int Viewport::x() const { 28 | return m_x; 29 | } 30 | 31 | /** */ 32 | int Viewport::y() const { 33 | return m_y; 34 | } 35 | 36 | /** */ 37 | int Viewport::width() const { 38 | return m_width; 39 | } 40 | 41 | /** */ 42 | int Viewport::height() const { 43 | return m_height; 44 | } 45 | 46 | /** */ 47 | int Viewport::half_width() const { 48 | return m_half_width; 49 | } 50 | 51 | /** */ 52 | int Viewport::half_height() const { 53 | return m_half_height; 54 | } 55 | 56 | /** */ 57 | CameraTracker::CameraTracker() : 58 | m_camera(NULL), 59 | m_prev_point(0.0), 60 | m_sensitivity(0.005) { 61 | } 62 | 63 | /** */ 64 | void CameraTracker::set_camera(Camera* t_camera) { 65 | m_camera = t_camera; 66 | } 67 | 68 | /** */ 69 | void CameraTracker::begin(int x, int y) { 70 | if(!m_camera) { return; } 71 | m_prev_point = vec2(x - m_camera->viewport()->half_width(), y - m_camera->viewport()->half_height()); 72 | } 73 | 74 | /** */ 75 | void CameraTracker::track(int x, int y) { 76 | if(!m_camera) { return; } 77 | 78 | vec2 n_point(x, y); 79 | vec2 n_rotation(0.0f); 80 | vec3 t_rotation_axis(0.0f, 1.0f, 0.0f); 81 | 82 | n_point.x -= m_camera->viewport()->half_width(); 83 | n_point.y -= m_camera->viewport()->half_height(); 84 | 85 | n_rotation.x = -(float)(n_point.x - m_prev_point.x) * m_sensitivity; 86 | n_rotation.y = -(float)(n_point.y - m_prev_point.y) * m_sensitivity; 87 | 88 | m_camera->rotate(n_rotation.x, t_rotation_axis); 89 | 90 | vec3 t_camtarget = m_camera->target(); 91 | t_rotation_axis = glm::normalize(vec3(-t_camtarget.z, 0.0f, t_camtarget.x)); 92 | m_camera->rotate(n_rotation.y, t_rotation_axis); 93 | 94 | m_prev_point = n_point; 95 | } 96 | 97 | CameraMover::CameraMover() : 98 | m_camera(NULL), 99 | m_forward(false), 100 | m_backward(false), 101 | m_left(false), 102 | m_right(false) { 103 | } 104 | 105 | /** */ 106 | void CameraMover::set_camera(Camera* t_camera) { 107 | m_camera = t_camera; 108 | } 109 | 110 | void CameraMover::forward(bool t_value) { 111 | if(m_backward) { 112 | m_backward = false; 113 | } 114 | m_forward = t_value; 115 | } 116 | void CameraMover::backward(bool t_value) { 117 | if(m_forward) { 118 | m_forward = false; 119 | } 120 | m_backward = t_value; 121 | } 122 | void CameraMover::left(bool t_value) { 123 | if(m_right) { 124 | m_right = false; 125 | } 126 | m_left = t_value; 127 | } 128 | void CameraMover::right(bool t_value) { 129 | if(m_left) { 130 | m_left = false; 131 | } 132 | m_right = t_value; 133 | } 134 | 135 | void CameraMover::update(double dt) { 136 | if(!m_camera) { return; } 137 | 138 | vec3 t_campos = m_camera->position(); 139 | vec3 t_camtarget = m_camera->target(); 140 | 141 | // Move 5 units per second 142 | float t_speed = 5.0f * dt; 143 | 144 | if(m_forward) { 145 | t_campos += t_camtarget * t_speed; 146 | } else if(m_backward) { 147 | t_campos -= t_camtarget * t_speed; 148 | } 149 | 150 | if(m_left) { 151 | t_campos += vec3(t_camtarget.z, 0.0f, -t_camtarget.x) * t_speed; 152 | } else if(m_right) { 153 | t_campos += vec3(-t_camtarget.z, 0.0f, t_camtarget.x) * t_speed; 154 | } 155 | 156 | m_camera->position(t_campos); 157 | } 158 | 159 | /** */ 160 | Camera::Camera() : 161 | m_position(0.0f), 162 | m_target(0.0f, 0.0f, 1.0f), 163 | m_up(0.0f, 1.0f, 0.0f) { 164 | m_tracker.set_camera(this); 165 | m_mover.set_camera(this); 166 | } 167 | 168 | Camera::Camera(const vec3& t_position, const vec3& t_target, const vec3& t_up) : 169 | m_position(t_position), 170 | m_target(t_target), 171 | m_up(t_up) { 172 | m_tracker.set_camera(this); 173 | m_mover.set_camera(this); 174 | } 175 | 176 | Camera::~Camera() { 177 | } 178 | 179 | /** */ 180 | void Camera::update(double dt) { 181 | m_mover.update(dt); 182 | } 183 | 184 | /** */ 185 | void Camera::rotate(float angle, const vec3& axis) { 186 | vec3 n_target = glm::rotate(m_target, glm::degrees(angle), axis); 187 | m_target = glm::normalize(n_target); 188 | 189 | /*vec3 n_target(0.0); 190 | 191 | float c = cos(angle); 192 | float s = sin(angle); 193 | 194 | n_target.x = (x * x * (1.0 - c) + c) * m_target.x; 195 | n_target.x += (x * y * (1.0 - c) - z * s) * m_target.y; 196 | n_target.x += (x * z * (1.0 - c) + y * s) * m_target.z; 197 | 198 | n_target.y = (y * x * (1.0 - c) + z * s) * m_target.x; 199 | n_target.y += (y * y * (1.0 - c) + c) * m_target.y; 200 | n_target.y += (y * z * (1.0 - c) - x * s) * m_target.z; 201 | 202 | n_target.z = (x * z * (1.0 - c) - y * s) * m_target.x; 203 | n_target.z += (y * z * (1.0 - c) + x * s) * m_target.y; 204 | n_target.z += (z * z * (1.0 - c) + c) * m_target.z; 205 | 206 | m_target = glm::normalize(n_target);*/ 207 | } 208 | 209 | /** */ 210 | mat4 Camera::view_matrix() { 211 | vec3 n_target = m_position + m_target; 212 | mat4 t_view_matrix = glm::lookAt(m_position, n_target, m_up); 213 | return t_view_matrix; 214 | } 215 | 216 | /** */ 217 | mat4 Camera::projection_matrix() { 218 | return glm::perspective(m_frustum.fov(), m_frustum.ratio(), m_frustum.near(), m_frustum.far()); 219 | } 220 | 221 | /** */ 222 | Viewport* Camera::viewport() { 223 | return &m_viewport; 224 | } 225 | 226 | /** */ 227 | CameraTracker* Camera::tracker() { 228 | return &m_tracker; 229 | } 230 | 231 | /** */ 232 | CameraMover* Camera::mover() { 233 | return &m_mover; 234 | } 235 | 236 | /** */ 237 | vec3 Camera::position() const { 238 | return m_position; 239 | } 240 | 241 | /** */ 242 | vec3 Camera::target() const { 243 | return m_target; 244 | } 245 | 246 | /** */ 247 | vec3 Camera::up() const { 248 | return m_up; 249 | } 250 | 251 | /** */ 252 | Frustum* Camera::frustum() { 253 | return &m_frustum; 254 | } 255 | 256 | /** */ 257 | void Camera::position(const vec3& t_position) { 258 | m_position = t_position; 259 | } 260 | 261 | /** */ 262 | void Camera::target(const vec3& t_target) { 263 | m_target = t_target; 264 | } 265 | 266 | /** */ 267 | void Camera::up(const vec3& t_up) { 268 | m_up = t_up; 269 | } 270 | 271 | } 272 | -------------------------------------------------------------------------------- /src/camera.hpp: -------------------------------------------------------------------------------- 1 | #ifndef GKR_CAMERA_HPP 2 | #define GKR_CAMERA_HPP 3 | 4 | #include 5 | #include 6 | 7 | /** */ 8 | namespace GKR { 9 | 10 | /** Some forward declarations */ 11 | class Camera; 12 | 13 | /** */ 14 | class Viewport { 15 | private: 16 | int m_x; 17 | int m_y; 18 | int m_width; 19 | int m_height; 20 | int m_half_width; 21 | int m_half_height; 22 | 23 | public: 24 | Viewport(); 25 | void set(int n_x, int n_y, int n_width, int n_height); 26 | 27 | int x() const; 28 | int y() const; 29 | int width() const; 30 | int height() const; 31 | int half_width() const; 32 | int half_height() const; 33 | }; 34 | 35 | /** */ 36 | class CameraTracker { 37 | private: 38 | Camera* m_camera; 39 | vec2 m_prev_point; 40 | float m_sensitivity; 41 | 42 | public: 43 | CameraTracker(); 44 | 45 | /** Sets the camera this tracker will use */ 46 | void set_camera(Camera* t_camera); 47 | 48 | /** Marks the start of mouse tracking (e.g. called from onMouseClick() ) */ 49 | void begin(int x, int y); 50 | 51 | /** Updates mouse tracking (e.g. called from onMouseMove() ) */ 52 | void track(int x, int y); 53 | }; 54 | 55 | /** */ 56 | class CameraMover { 57 | private: 58 | Camera* m_camera; 59 | 60 | bool m_forward; 61 | bool m_backward; 62 | bool m_left; 63 | bool m_right; 64 | public: 65 | CameraMover(); 66 | 67 | void update(double dt); 68 | 69 | void set_camera(Camera* t_camera); 70 | 71 | void forward(bool t_value); 72 | void backward(bool t_value); 73 | void left(bool t_value); 74 | void right(bool t_value); 75 | }; 76 | 77 | /** */ 78 | class Camera { 79 | private: 80 | Frustum m_frustum; 81 | 82 | vec3 m_position; 83 | vec3 m_target; 84 | vec3 m_up; 85 | 86 | Viewport m_viewport; 87 | CameraTracker m_tracker; 88 | CameraMover m_mover; 89 | public: 90 | Camera(); 91 | Camera(const vec3& t_position, const vec3& t_target, const vec3& t_up); 92 | ~Camera(); 93 | 94 | void update(double dt); 95 | 96 | /** Rotates target vector by angle, given */ 97 | void rotate(float angle, const vec3& axis); 98 | 99 | /** Returns the view matrix of the camera */ 100 | mat4 view_matrix(); 101 | 102 | /** Returns the projection matrix defined by the current Frustum */ 103 | mat4 projection_matrix(); 104 | 105 | /** Returns the viewport used by this camera */ 106 | Viewport* viewport(); 107 | 108 | /** Returns the mouse track class that can be used to control this camera */ 109 | CameraTracker* tracker(); 110 | 111 | /** Returns the class that contols the camera movement */ 112 | CameraMover* mover(); 113 | 114 | vec3 position() const; 115 | vec3 target() const; 116 | vec3 up() const; 117 | 118 | void position(const vec3& t_position); 119 | void target(const vec3& t_target); 120 | void up(const vec3& t_up); 121 | 122 | /** */ 123 | Frustum* frustum(); 124 | }; 125 | 126 | } 127 | 128 | #endif 129 | -------------------------------------------------------------------------------- /src/cascaded_shadow_maps.cpp: -------------------------------------------------------------------------------- 1 | //---------------------------------------------------------------------------------- 2 | // File: shadowmapping.cpp 3 | // Author: Rouslan Dimitrov 4 | // Email: sdkfeedback@nvidia.com 5 | // 6 | // Copyright (c) 2007 NVIDIA Corporation. All rights reserved. 7 | // 8 | // TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THIS SOFTWARE IS PROVIDED 9 | // *AS IS* AND NVIDIA AND ITS SUPPLIERS DISCLAIM ALL WARRANTIES, EITHER EXPRESS 10 | // OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY 11 | // AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL NVIDIA OR ITS SUPPLIERS 12 | // BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT, OR CONSEQUENTIAL DAMAGES 13 | // WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, 14 | // BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR ANY OTHER PECUNIARY LOSS) 15 | // ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF NVIDIA HAS 16 | // BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17 | // 18 | //---------------------------------------------------------------------------------- 19 | 20 | // This sample shows an implementation of cascaded shadow maps. 21 | // The important pieces of code are fairly commented, so you can jump straight to display(); 22 | // The main idea of cascaded shadow maps is to split the camera view frustum into several 23 | // smaller frusta and then calculate a separate shadow map for each. This has the advantage 24 | // that objects near the camera get more resolution of the shadow map and ones that are far 25 | // away get less, in an attempt to provide an uniform error in screen space. 26 | 27 | // This sample uses the following artwork: 28 | // Palmtree model 29 | // http://telias.free.fr/zipsz/models_3ds/plants/palm1.zip 30 | // 31 | // Terrain textures 32 | // http://www.cc.gatech.edu/projects/large_models/gcanyon.html 33 | 34 | #include "main.h" 35 | #include "terrain.h" 36 | 37 | #include 38 | #include 39 | 40 | using std::string; 41 | using std::cout; 42 | using std::endl; 43 | 44 | //using namespace nv; 45 | 46 | struct obj_BoundingSphere { 47 | vec3 center; 48 | float radius; 49 | }; 50 | 51 | //int cur_num_splits = 2; 52 | int show_depth_tex = 1; 53 | int shadow_type = 0; 54 | //const int m_num_matrices = 4; 55 | 56 | Terrain *terrain; 57 | 58 | bool m_uniform_offsets = false; 59 | bool m_uniform_poisson = false; 60 | 61 | int width = 1152; 62 | int height = 720; 63 | //int depth_size = 1024; 64 | 65 | //GLuint depth_fb;//, depth_rb; 66 | //GLuint depth_tex_ar; 67 | 68 | GLuint write_depth_prog; 69 | GLuint view_prog; 70 | GLuint shad_single_prog; 71 | 72 | //frustum f[MAX_SPLITS]; 73 | //float shad_cpm[MAX_SPLITS][16]; 74 | //glm::mat4 t_mat_shad_cpm[MAX_SPLITS]; 75 | 76 | obj_BoundingSphere obj_BSphere[NUM_OBJECTS]; 77 | 78 | //float split_weight = 0.75f; 79 | 80 | void makeScene() { 81 | terrain = new Terrain; 82 | if(!terrain->Load()) { 83 | printf("Couldn't find terrain textures.\n"); 84 | exit(0); 85 | } 86 | 87 | int td = terrain->getDim()/2; 88 | obj_BSphere[0].center = glm::vec3(-td, 50.0f, -td); 89 | obj_BSphere[1].center = glm::vec3(-td, 50.0f, td); 90 | obj_BSphere[2].center = glm::vec3( td, 50.0f, td); 91 | obj_BSphere[3].center = glm::vec3( td, 50.0f, -td); 92 | obj_BSphere[0].radius = 1.0f; 93 | obj_BSphere[1].radius = 1.0f; 94 | obj_BSphere[2].radius = 1.0f; 95 | obj_BSphere[3].radius = 1.0f; 96 | } 97 | 98 | /** here all shadow map textures and their corresponding matrices are created */ 99 | void render_shadow_map() { 100 | 101 | GKR::Camera* camera = get_camera(); 102 | GKR::ShadowMap* shadow_map = get_shadow_map(); 103 | 104 | vec4 t_lightdir = m_light_dir; 105 | 106 | glDisable(GL_TEXTURE_2D); 107 | // since the shadow maps have only a depth channel, we don't need color computation 108 | // glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); 109 | 110 | glUseProgram(write_depth_prog); 111 | 112 | // redirect rendering to the depth texture 113 | glBindFramebuffer(GL_FRAMEBUFFER, shadow_map->fbo()); 114 | 115 | // store the screen viewport 116 | glPushAttrib(GL_VIEWPORT_BIT); 117 | 118 | // and render only to the shadowmap 119 | glViewport(0, 0, shadow_map->depth_tex_size(), shadow_map->depth_tex_size()); 120 | 121 | // offset the geometry slightly to prevent z-fighting 122 | // note that this introduces some light-leakage artifacts 123 | glPolygonOffset(1.0f, 4096.0f); 124 | glEnable(GL_POLYGON_OFFSET_FILL); 125 | 126 | // draw all faces since our terrain is not closed. 127 | glDisable(GL_CULL_FACE); 128 | 129 | // Generate crop and projection matrices 130 | shadow_map->pre_depth_write(camera, t_lightdir); 131 | 132 | mat4 t_modelview = shadow_map->modelview_matrix(); 133 | glUniformMatrix4fv(glGetUniformLocation(write_depth_prog, "modelViewMatrix"), 1, GL_FALSE, glm::value_ptr(t_modelview)); 134 | 135 | // Write depth to shadow map segments (draw geometry) 136 | for(int i = 0 ; i < shadow_map->num_splits() ; i++) { 137 | mat4 t_projection = shadow_map->projection_matrix(i); 138 | 139 | glUniformMatrix4fv(glGetUniformLocation(write_depth_prog, "projectionMatrix"), 1, GL_FALSE, glm::value_ptr(t_projection)); 140 | glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, shadow_map->texture(), 0, i); 141 | 142 | // clear the depth texture from last time 143 | glClear(GL_DEPTH_BUFFER_BIT); 144 | 145 | // draw the scene 146 | terrain->Draw(write_depth_prog, t_modelview); 147 | } 148 | 149 | // revert to normal back face culling as used for rendering 150 | glEnable(GL_CULL_FACE); 151 | 152 | glDisable(GL_POLYGON_OFFSET_FILL); 153 | glPopAttrib(); 154 | glBindFramebuffer(GL_FRAMEBUFFER, 0); 155 | 156 | glEnable(GL_TEXTURE_2D); 157 | 158 | glUseProgram(0); 159 | } 160 | 161 | /** */ 162 | void render_scene() { 163 | GKR::ShadowMap* shadow_map = get_shadow_map(); 164 | GKR::Camera* camera = get_camera(); 165 | 166 | vec4 t_lightdir = camera->view_matrix() * m_light_dir; 167 | 168 | // approximate the atmosphere's filtering effect as a linear function 169 | vec4 t_skycolor(0.8f, t_lightdir.y * 0.1f + 0.7f, t_lightdir.y * 0.4f + 0.5f, 1.0f); 170 | 171 | glClearColor(t_skycolor.x, t_skycolor.y, t_skycolor.z, t_skycolor.w); 172 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 173 | 174 | // update the camera, so that the user can have a free look 175 | mat4 t_view = camera->view_matrix(); 176 | //mat4 t_view_inverse = glm::inverse(t_view); 177 | mat4 t_projection = camera->projection_matrix(); 178 | 179 | // Update far bounds and texture matrices 180 | //shadow_map->pre_render(t_projection, t_view_inverse); 181 | 182 | // Bind all depth maps 183 | glBindTexture(GL_TEXTURE_2D_ARRAY, shadow_map->texture()); 184 | /*if(shadow_type >= 4) { 185 | glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE); 186 | } else { 187 | glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_COMPARE_MODE, GL_NONE); 188 | }*/ 189 | 190 | // == Setup shader 191 | GLuint t_current_program = shad_single_prog; 192 | glUseProgram(shad_single_prog); 193 | glUniform1i(glGetUniformLocation(shad_single_prog, "shadowmap"), 0); // depth-maps 194 | glUniform1i(glGetUniformLocation(shad_single_prog, "tex"), 1); // terrain tex 195 | // the shader needs to know the split distances, so that it can choose in which 196 | // texture to to the look up. Note that we pass them in homogeneous coordinates - 197 | // this the same space as gl_FragCoord is in. In this way the shader is more efficient 198 | glUniform4fv(glGetUniformLocation(shad_single_prog, "farbounds"), 1, shadow_map->far_bounds()); 199 | glUniform4fv(glGetUniformLocation(shad_single_prog, "lightdir"), 1, glm::value_ptr(t_lightdir)); 200 | glUniform4fv(glGetUniformLocation(shad_single_prog, "lightcolor"), 1, glm::value_ptr(t_skycolor)); 201 | 202 | if(m_uniform_offsets) { 203 | const int nsamples = 8; 204 | const vec4 offset[nsamples] = { 205 | vec4(0.000000, 0.000000, 0.0, 0.0), 206 | vec4(0.079821, 0.165750, 0.0, 0.0), 207 | vec4(-0.331500, 0.159642, 0.0, 0.0), 208 | vec4(-0.239463, -0.497250, 0.0, 0.0), 209 | vec4(0.662999, -0.319284, 0.0, 0.0), 210 | vec4(0.399104, 0.828749, 0.0, 0.0), 211 | vec4(-0.994499, 0.478925, 0.0, 0.0), 212 | vec4(-0.558746, -1.160249, 0.0, 0.0) 213 | }; 214 | 215 | glUniform4fv(glGetUniformLocation(shad_single_prog, "offset"), nsamples, glm::value_ptr(offset[0])); 216 | } 217 | 218 | // http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-16-shadow-mapping/#PCF 219 | if(m_uniform_poisson) { 220 | const int nsamples = 4; 221 | const vec2 poissonDisk[nsamples] = { 222 | vec2(-0.94201624, -0.39906216), 223 | vec2(0.94558609, -0.76890725), 224 | vec2(-0.094184101, -0.92938870), 225 | vec2(0.34495938, 0.29387760) 226 | }; 227 | glUniform4fv(glGetUniformLocation(shad_single_prog, "poissonDisk"), nsamples, glm::value_ptr(poissonDisk[0])); 228 | } 229 | 230 | glUniformMatrix4fv(glGetUniformLocation(shad_single_prog, "projectionMatrix"), 1, GL_FALSE, glm::value_ptr(t_projection)); 231 | //glUniformMatrix4fv(glGetUniformLocation(shad_single_prog, "modelViewMatrix"), 1, GL_FALSE, glm::value_ptr(t_view)); 232 | glUniformMatrix4fv(glGetUniformLocation(shad_single_prog, "textureMatrixList"), shadow_map->num_splits(), GL_FALSE, shadow_map->texture_matrices()); 233 | // == Setup shader 234 | 235 | //glLightfv(GL_LIGHT0, GL_POSITION, light_dir); 236 | //glLightfv(GL_LIGHT0, GL_DIFFUSE, sky_color); 237 | 238 | glFogfv(GL_FOG_COLOR, glm::value_ptr(t_skycolor)); 239 | 240 | // finally, draw the scene 241 | terrain->Draw(t_current_program, t_view); 242 | 243 | glUseProgram(0); 244 | 245 | GET_GLERROR() 246 | } 247 | 248 | // here we render the terrain from top and show the camera frusta. 249 | // this display can be enabled from within the sample 250 | void overviewCam() { 251 | /*glActiveTexture(GL_TEXTURE0); 252 | glMatrixMode(GL_TEXTURE); 253 | glLoadIdentity(); 254 | 255 | glMatrixMode(GL_MODELVIEW); 256 | glLoadIdentity(); 257 | glDisable(GL_LIGHTING); 258 | glPointSize(10); 259 | glColor3f(1.0f, 1.0f, 0.0f); 260 | gluLookAt(0, FAR_DIST/2, 0, 0, 0, 0, 0, 0, 1.0f); 261 | 262 | glScalef(0.2f, 0.2f, 0.2f); 263 | glRotatef(20, 1, 0, 0); 264 | for(int i = 0 ; i < cur_num_splits ; i++) { 265 | glBegin(GL_LINE_LOOP); 266 | for(int j = 0 ; j < 4 ; j++) { 267 | glVertex3f(f[i].point[j].x, f[i].point[j].y, f[i].point[j].z); 268 | } 269 | glEnd(); 270 | 271 | glBegin(GL_LINE_LOOP); 272 | for(int j = 4 ; j < 8 ; j++) { 273 | glVertex3f(f[i].point[j].x, f[i].point[j].y, f[i].point[j].z); 274 | } 275 | glEnd(); 276 | } 277 | 278 | for(int j = 0 ; j < 4 ; j++) { 279 | glBegin(GL_LINE_STRIP); 280 | glVertex3fv(cam_pos); 281 | for(int i = 0 ; i < cur_num_splits ; i++) { 282 | glVertex3f(f[i].point[j].x, f[i].point[j].y, f[i].point[j].z); 283 | } 284 | glVertex3f(f[cur_num_splits-1].point[j+4].x, f[cur_num_splits-1].point[j+4].y, f[cur_num_splits-1].point[j+4].z); 285 | glEnd(); 286 | } 287 | 288 | glLightfv(GL_LIGHT0, GL_POSITION, glm::value_ptr(m_light_dir)); 289 | glColor3f(0.9f, 0.9f, 1.0f); 290 | glEnable(GL_LIGHTING); 291 | glEnable(GL_LIGHT0); 292 | terrain->DrawCoarse();*/ 293 | } 294 | 295 | // here we show all depth maps that have been generated for the current frame 296 | // note that a special shader is required to display the depth-component-only textures 297 | void showDepthTex() { 298 | GKR::ShadowMap* shadow_map = get_shadow_map(); 299 | 300 | int loc; 301 | glPushAttrib(GL_VIEWPORT_BIT | GL_DEPTH_BUFFER_BIT); 302 | glDisable(GL_DEPTH_TEST); 303 | glDisable(GL_CULL_FACE); 304 | glUseProgram(view_prog); 305 | glUniform1i(glGetUniformLocation(view_prog,"tex"), 0); 306 | loc = glGetUniformLocation(view_prog,"layer"); 307 | 308 | for(int i = 0 ; i < shadow_map->num_splits() ; i++) { 309 | glViewport(130*i, 0, 128, 128); 310 | glBindTexture(GL_TEXTURE_2D_ARRAY_EXT, shadow_map->texture()); 311 | glTexParameteri( GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_COMPARE_MODE, GL_NONE); 312 | glUniform1f(loc, (float)i); 313 | 314 | glBegin(GL_QUADS); 315 | glVertex3f(-1.0f, -1.0f, 0.0f); 316 | glVertex3f( 1.0f, -1.0f, 0.0f); 317 | glVertex3f( 1.0f, 1.0f, 0.0f); 318 | glVertex3f(-1.0f, 1.0f, 0.0f); 319 | glEnd(); 320 | } 321 | glUseProgram(0); 322 | 323 | glViewport(width - 129, 0, 128, 128); 324 | glEnable(GL_DEPTH_TEST); 325 | glClear(GL_DEPTH_BUFFER_BIT); 326 | overviewCam(); 327 | 328 | glEnable(GL_CULL_FACE); 329 | glPopAttrib(); 330 | } 331 | 332 | // this is the main display function that glut calls for us. There are 2 main steps in the algorithm: 333 | void display() { 334 | updateKeys(); 335 | 336 | GKR::Camera* camera = get_camera(); 337 | camera->update(0.1); 338 | 339 | // 1. Render the shadow map 340 | render_shadow_map(); 341 | 342 | // 2. Render the world by applying the shadow maps 343 | render_scene(); 344 | 345 | // additionally, we can display information to aid the understanding 346 | // of what is going on 347 | //if(show_depth_tex) { 348 | showDepthTex(); 349 | //} 350 | 351 | glutSwapBuffers(); 352 | } 353 | 354 | void init() { 355 | glClearColor(0.8f, 0.8f , 0.9f, 1.0f); 356 | glEnable(GL_CULL_FACE); 357 | glEnable(GL_DEPTH_TEST); 358 | 359 | makeScene(); 360 | 361 | string t_vertex_shader("../../src/GLSL/shadow_vertex.glsl"); 362 | //string t_fragment_shader("../../src/GLSL/shadow_single_fragment.glsl"); 363 | //string t_fragment_shader("../../src/GLSL/shadow_pcf.glsl"); m_uniform_poisson = true; 364 | 365 | //string t_fragment_shader("../../src/GLSL/shadow_single_hl_fragment.glsl"); 366 | string t_fragment_shader("../../src/GLSL/shadow_multi_leak_fragment.glsl"); m_uniform_offsets = true; 367 | //string t_fragment_shader("../../src/GLSL/shadow_pcf_fragment.glsl"); 368 | //string t_fragment_shader("../../src/GLSL/shadow_pcf_gaussian_fragment.glsl"); 369 | //string t_fragment_shader("../../src/GLSL/shadow_pcf_trilinear_fragment.glsl"); 370 | 371 | string t_depth_vertex_shader("../../src/GLSL/write_depth_vertex.glsl"); 372 | string t_depth_fragment_shader("../../src/GLSL/write_depth_fragment.glsl"); 373 | 374 | string t_debugview_vertex_shader("../../src/GLSL/view_vertex.glsl"); 375 | string t_debugview_fragment_shader("../../src/GLSL/view_fragment.glsl"); 376 | 377 | shad_single_prog = createShaders(t_vertex_shader.c_str(), t_fragment_shader.c_str()); 378 | view_prog = createShaders(t_debugview_vertex_shader.c_str(), t_debugview_fragment_shader.c_str()); 379 | write_depth_prog = createShaders(t_depth_vertex_shader.c_str(), t_depth_fragment_shader.c_str()); 380 | 381 | /*for(int i = 0 ; i < MAX_SPLITS ; i++) { 382 | // note that fov is in radians here and in OpenGL it is in degrees. 383 | // the 0.2f factor is important because we might get artifacts at 384 | // the screen borders. 385 | f[i].fov = CAMERA_FOV / 57.2957795 + 0.2f; 386 | f[i].ratio = (double)width/(double)height; 387 | }*/ 388 | 389 | glFogf(GL_FOG_DENSITY, 0.4f); 390 | glFogf(GL_FOG_START, 16.0f); 391 | glFogf(GL_FOG_END, FAR_DIST); 392 | 393 | GET_GLERROR() 394 | } 395 | 396 | /** */ 397 | int main(int argc, char** argv) { 398 | glutInit(&argc, argv); 399 | glutInitDisplayString("double rgb~8 depth~24 samples=4"); 400 | glutInitWindowSize(width, height); 401 | glutCreateWindow("Cascaded Shadow Maps"); 402 | 403 | glewInit(); 404 | if(!glewIsSupported( "GL_VERSION_2_0 ")) { 405 | printf( "Required extensions not supported.\n"); 406 | return 1; 407 | } 408 | 409 | glutIgnoreKeyRepeat(true); 410 | 411 | glutDisplayFunc(display); 412 | //glutKeyboardFunc(keys); 413 | glutReshapeFunc(reshape); 414 | glutMouseFunc(mouse); 415 | glutMotionFunc(motion); 416 | glutIdleFunc(idle); 417 | 418 | glutKeyboardFunc(key_down); 419 | glutKeyboardUpFunc(key_up); 420 | 421 | glutCreateMenu(menu); 422 | glutAddMenuEntry("SSM (1 split) [1]", '1'); 423 | glutAddMenuEntry("CSM (2 split) [2]", '2'); 424 | glutAddMenuEntry("CSM (3 split) [3]", '3'); 425 | glutAddMenuEntry("CSM (4 split) [4]", '4'); 426 | glutAddMenuEntry("Show Shadow Maps [`]", '`'); 427 | glutAddMenuEntry("------------", -1); 428 | glutAddMenuEntry("Normal Mode", 0); 429 | glutAddMenuEntry("Show Splits", 1); 430 | glutAddMenuEntry("Smooth shadows", 2); 431 | glutAddMenuEntry("Smooth shadows, no leak", 3); 432 | glutAddMenuEntry("PCF", 4); 433 | glutAddMenuEntry("PCF w/ trilinear", 5); 434 | glutAddMenuEntry("PCF w/ 4 taps", 6); 435 | glutAddMenuEntry("PCF w/ 8 random taps", 7); 436 | glutAddMenuEntry("PCF w/ gaussian blur", 8); 437 | glutAddMenuEntry("------------", -1); 438 | glutAddMenuEntry("512x512", 10); 439 | glutAddMenuEntry("1024x1024", 11); 440 | glutAddMenuEntry("2048x2048", 12); 441 | glutAddMenuEntry("4096x4096", 13); 442 | glutAddMenuEntry("------------", -1); 443 | glutAddMenuEntry("Exit [q]", 'q'); 444 | glutAttachMenu(GLUT_RIGHT_BUTTON); 445 | 446 | init(); 447 | 448 | printf("\nKeys:\n"); 449 | printf("W, A, S, D - move around\n"); 450 | printf("Left Mouse Button - free look\n"); 451 | printf("Shift + LMB - move light\n"); 452 | printf("1, 2, 3, 4 - number of splits\n"); 453 | printf("~ - show depth textures\n"); 454 | 455 | glutMainLoop(); 456 | 457 | return 0; 458 | } 459 | -------------------------------------------------------------------------------- /src/frustum.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /** */ 4 | namespace GKR { 5 | 6 | /** */ 7 | Frustum::Frustum() : 8 | m_fov(45.0), 9 | m_ratio(0.5), 10 | m_near(1.0), 11 | m_far(200.0) { 12 | } 13 | 14 | /** */ 15 | void Frustum::set(float t_fov, float t_ratio, float t_near, float t_far) { 16 | m_fov = t_fov; 17 | m_ratio = t_ratio; 18 | m_near = t_near; 19 | m_far = t_far; 20 | } 21 | 22 | /** */ 23 | float Frustum::fov() const { 24 | return m_fov; 25 | } 26 | 27 | /** */ 28 | float Frustum::ratio() const { 29 | return m_ratio; 30 | } 31 | 32 | /** */ 33 | float Frustum::near() const { 34 | return m_near; 35 | } 36 | 37 | /** */ 38 | float Frustum::far() const { 39 | return m_far; 40 | } 41 | 42 | /** */ 43 | void Frustum::near(float t_near) { 44 | m_near = t_near; 45 | } 46 | 47 | void Frustum::far(float t_far) { 48 | m_far = t_far; 49 | } 50 | 51 | /** */ 52 | void Frustum::fov(float t_fov) { 53 | m_fov = t_fov; 54 | } 55 | 56 | /** */ 57 | void Frustum::ratio(float t_ratio) { 58 | m_ratio = t_ratio; 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/frustum.hpp: -------------------------------------------------------------------------------- 1 | #ifndef GKR_FRUSTUM_HPP 2 | #define GKR_FRUSTUM_HPP 3 | 4 | #include 5 | 6 | /** */ 7 | namespace GKR { 8 | 9 | /** */ 10 | class Frustum { 11 | private: 12 | float m_fov; 13 | float m_ratio; 14 | float m_near; 15 | float m_far; 16 | 17 | public: 18 | vec3 m_points[8]; 19 | 20 | Frustum(); 21 | 22 | /** */ 23 | void set(float t_fov, float t_ratio, float t_near, float t_far); 24 | 25 | float fov() const; 26 | float ratio() const; 27 | float near() const; 28 | float far() const; 29 | 30 | void near(float t_near); 31 | void far(float t_far); 32 | void fov(float t_fov); 33 | void ratio(float t_ratio); 34 | }; 35 | 36 | } 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /src/main.h: -------------------------------------------------------------------------------- 1 | // Email: sdkfeedback@nvidia.com 2 | // 3 | // Copyright (c) NVIDIA Corporation. All rights reserved. 4 | 5 | #ifndef _MAIN_H_ 6 | #define _MAIN_H_ 7 | 8 | #define _CRT_SECURE_NO_DEPRECATE 9 | #define _WIN32_LEAN_AND_MEAN 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | 27 | //#include 28 | 29 | //#include 30 | 31 | struct frustum { 32 | float neard; 33 | float fard; 34 | float fov; 35 | float ratio; 36 | glm::vec3 point[8]; 37 | }; 38 | 39 | #define FAR_DIST 200.0f 40 | #define MAX_SPLITS 4 41 | #define NUM_OBJECTS 4 42 | #define LIGHT_FOV 45.0 43 | #define CAMERA_FOV 45.0f 44 | 45 | void motion(int x, int y); 46 | void mouse(int button, int state, int x, int y); 47 | void idle(); 48 | void keys(unsigned char c, int x, int y); 49 | 50 | void key_down(unsigned char c, int x, int y); 51 | void key_up(unsigned char c, int x, int y); 52 | 53 | void reshape(int w, int h); 54 | void menu(int m); 55 | void updateKeys(); 56 | void camLook(); 57 | glm::mat4 camLook2(); 58 | 59 | GKR::Camera* get_camera(); 60 | GKR::ShadowMap* get_shadow_map(); 61 | 62 | void compare_matrix(float* t_mat_orig, const glm::mat4& t_glm_mat); 63 | void cameraInverse(float dst[16], float src[16]); 64 | GLuint createShaders(const char* vert, const char* frag); 65 | void CheckFramebufferStatus(); 66 | 67 | //extern GLuint depth_tex_ar; 68 | 69 | extern float cam_pos[3]; 70 | extern float cam_view[3]; 71 | //extern GLfloat light_dir[4]; 72 | extern vec4 m_light_dir; 73 | extern int depth_size; 74 | extern GLuint depth_tex_ar; 75 | 76 | extern int width; 77 | extern int height; 78 | 79 | extern int cur_num_splits; 80 | extern int show_depth_tex; 81 | extern int shadow_type; 82 | extern frustum f[MAX_SPLITS]; 83 | 84 | extern float split_weight; 85 | 86 | #define GET_GLERROR() \ 87 | { \ 88 | GLenum err = glGetError(); \ 89 | if (err != GL_NO_ERROR) { \ 90 | fprintf(stderr, "[line %d] GL Error: %s\n", \ 91 | __LINE__, gluErrorString(err)); \ 92 | fflush(stderr); \ 93 | } \ 94 | } 95 | 96 | #endif -------------------------------------------------------------------------------- /src/math.hpp: -------------------------------------------------------------------------------- 1 | #ifndef GKR_MATH_HPP 2 | #define GKR_MATH_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | using glm::vec2; 11 | using glm::vec3; 12 | using glm::vec4; 13 | using glm::mat3; 14 | using glm::mat4; 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /src/shadow_map.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | using std::cout; 7 | using std::endl; 8 | 9 | /** */ 10 | namespace GKR { 11 | 12 | struct obj_BoundingSphere { 13 | vec3 center; 14 | float radius; 15 | }; 16 | 17 | #define NUM_OBJECTS 4 18 | obj_BoundingSphere obj_BSphere[NUM_OBJECTS]; 19 | 20 | /** */ 21 | ShadowMap::ShadowMap() : 22 | m_fbo(0), 23 | m_texture_array(0), 24 | m_num_splits(4), 25 | m_depth_tex_size(2048), // 1024, 2048 26 | m_split_weight(0.75f) { 27 | 28 | int td = 128; //terrain->getDim()/2; 29 | obj_BSphere[0].center = vec3(-td, 50.0, -td); 30 | obj_BSphere[1].center = vec3(-td, 50.0, td); 31 | obj_BSphere[2].center = vec3( td, 50.0, td); 32 | obj_BSphere[3].center = vec3( td, 50.0, -td); 33 | obj_BSphere[0].radius = 1.0f; 34 | obj_BSphere[1].radius = 1.0f; 35 | obj_BSphere[2].radius = 1.0f; 36 | obj_BSphere[3].radius = 1.0f; 37 | } 38 | 39 | /** */ 40 | ShadowMap::~ShadowMap() { 41 | } 42 | 43 | /** */ 44 | /*mat4 ShadowMap::crop_matrix(int t_split_index) { 45 | return m_crop_matrices[t_split_index]; 46 | }*/ 47 | 48 | /** */ 49 | mat4 ShadowMap::projection_matrix(int t_split_index) { 50 | return m_projection_matrices[t_split_index]; 51 | } 52 | 53 | /** */ 54 | mat4 ShadowMap::modelview_matrix() { 55 | return m_modelview; 56 | } 57 | 58 | /** */ 59 | /*mat4 ShadowMap::bias_matrix() { 60 | return m_bias; 61 | }*/ 62 | 63 | /** */ 64 | /*Frustum* ShadowMap::frustum(int t_split_index) { 65 | return &m_frustums[t_split_index]; 66 | }*/ 67 | 68 | /** */ 69 | int ShadowMap::num_splits() const { 70 | return m_num_splits; 71 | } 72 | 73 | /** */ 74 | int ShadowMap::depth_tex_size() const { 75 | return m_depth_tex_size; 76 | } 77 | 78 | /** */ 79 | GLuint ShadowMap::fbo() const { 80 | return m_fbo; 81 | } 82 | 83 | /** */ 84 | GLuint ShadowMap::texture() const { 85 | return m_texture_array; 86 | } 87 | 88 | /** */ 89 | float* ShadowMap::far_bounds() { 90 | return &m_far_bounds[0]; 91 | } 92 | 93 | /** */ 94 | float* ShadowMap::texture_matrices() { 95 | //return &m_texture_matrices[0][0][0]; 96 | return glm::value_ptr(m_texture_matrices[0]); 97 | } 98 | 99 | /** */ 100 | void ShadowMap::init(Camera* camera) { 101 | float camera_fov = camera->frustum()->fov(); 102 | float width = camera->viewport()->width(); 103 | float height = camera->viewport()->height(); 104 | float ratio = width / height; 105 | 106 | // note that fov is in radians here and in OpenGL it is in degrees. 107 | // the 0.2f factor is important because we might get artifacts at 108 | // the screen borders. 109 | for(int i = 0 ; i < CSM_MAX_SPLITS ; i++) { 110 | m_frustums[i].fov(camera_fov / 57.2957795 + 0.2f); 111 | m_frustums[i].ratio(ratio); 112 | } 113 | 114 | m_bias = mat4( 115 | 0.5f, 0.0f, 0.0f, 0.0f, 116 | 0.0f, 0.5f, 0.0f, 0.0f, 117 | 0.0f, 0.0f, 0.5f, 0.0f, 118 | 0.5f, 0.5f, 0.5f, 1.0f 119 | ); 120 | 121 | update_split_distances(camera); 122 | 123 | create_fbo(); 124 | create_texture(); 125 | } 126 | 127 | /** */ 128 | void ShadowMap::pre_depth_write(Camera* camera, const vec4& lightdir) { 129 | mat4 t_modelview = glm::lookAt( 130 | vec3(0.0, 0.0, 0.0), 131 | vec3(-lightdir.x, -lightdir.y, -lightdir.z), 132 | vec3(-1.0f, 0.0f, 0.0f)); 133 | 134 | //update_split_distances(camera); 135 | update_split_frustum_points(camera); 136 | generate_crop_matrices(t_modelview); 137 | m_modelview = t_modelview; 138 | 139 | // Required camera matices 140 | mat4 t_view = camera->view_matrix(); 141 | mat4 t_view_inverse = glm::inverse(t_view); 142 | mat4 t_projection = camera->projection_matrix(); 143 | 144 | update_far_bounds(t_projection, t_view_inverse); 145 | update_texture_matrices(t_projection, t_view_inverse); 146 | } 147 | 148 | /** */ 149 | void ShadowMap::update_far_bounds(const mat4& projection, const mat4& view_inverse) { 150 | for(int i = m_num_splits ; i < CSM_MAX_SPLITS ; i++) { 151 | m_far_bounds[i] = 0; 152 | } 153 | 154 | // for every active split 155 | for(int i = 0 ; i < m_num_splits ; i++) { 156 | // f[i].fard is originally in eye space - tell's us how far we can see. 157 | // Here we compute it in camera homogeneous coordinates. Basically, we calculate 158 | // cam_proj * (0, 0, f[i].fard, 1)^t and then normalize to [0; 1] 159 | 160 | Frustum& split_frustum = m_frustums[i]; 161 | m_far_bounds[i] = 0.5f * (-split_frustum.far() * projection[2][2] + projection[3][2]) / split_frustum.far() + 0.5f; 162 | } 163 | } 164 | 165 | /** */ 166 | void ShadowMap::update_texture_matrices(const mat4& projection, const mat4& view_inverse) { 167 | for(int i = 0 ; i < m_num_splits ; i++) { // for every active split 168 | // compute a matrix that transforms from camera eye space to light clip space 169 | // and pass it to the shader through the OpenGL texture matrices, since we 170 | // don't use them now 171 | 172 | // multiply the light's (bias*crop*proj*modelview) by the inverse camera modelview 173 | // so that we can transform a pixel as seen from the camera 174 | m_texture_matrices[i] = m_bias * m_crop_matrices[i] * view_inverse; 175 | 176 | // compute a normal matrix for the same thing (to transform the normals) 177 | // Basically, N = ((L)^-1)^-t 178 | 179 | /* TODO: Why is this here? 180 | glm::mat4 t_mat_nm = glm::inverse(t_mat_texture); 181 | t_mat_nm = glm::transpose(t_mat_nm); 182 | 183 | glActiveTexture(GL_TEXTURE0 + (GLenum)(i+4)); 184 | glMatrixMode(GL_TEXTURE); 185 | glLoadMatrixf(glm::value_ptr(t_mat_nm));*/ 186 | } 187 | } 188 | 189 | /** */ 190 | void ShadowMap::create_fbo() { 191 | glGenFramebuffersEXT(1, &m_fbo); 192 | glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo); 193 | glDrawBuffer(GL_NONE); 194 | glReadBuffer(GL_NONE); 195 | glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); 196 | } 197 | 198 | /** */ 199 | void ShadowMap::create_texture() { 200 | if(m_texture_array) { 201 | glDeleteTextures(1, &m_texture_array); 202 | } 203 | 204 | glGenTextures(1, &m_texture_array); 205 | glBindTexture(GL_TEXTURE_2D_ARRAY, m_texture_array); 206 | glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_DEPTH_COMPONENT24, m_depth_tex_size, m_depth_tex_size, CSM_MAX_SPLITS, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL); 207 | glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 208 | glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 209 | glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 210 | glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 211 | glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); 212 | 213 | glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_COMPARE_MODE, GL_NONE); 214 | //glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE); 215 | 216 | glBindTexture(GL_TEXTURE_2D_ARRAY_EXT, 0); 217 | } 218 | 219 | /** 220 | * Computes the near and far distances for every frustum slice 221 | * in camera eye space - that is, at what distance does a slice start and end 222 | */ 223 | void ShadowMap::update_split_distances(Camera* camera) { 224 | float nd = camera->frustum()->near(); 225 | float fd = camera->frustum()->far(); 226 | 227 | float lambda = m_split_weight; 228 | float ratio = fd / nd; 229 | m_frustums[0].near(nd); 230 | 231 | for(int i = 1 ; i < m_num_splits ; i++) { 232 | float si = i / (float)m_num_splits; 233 | 234 | float t_near = lambda * (nd * powf(ratio, si)) + (1 - lambda) * (nd + (fd - nd) * si); 235 | float t_far = t_near * 1.005f; 236 | m_frustums[i].near(t_near); 237 | m_frustums[i-1].far(t_far); 238 | } 239 | m_frustums[m_num_splits-1].far(fd); 240 | } 241 | 242 | /** Compute the camera frustum slice boundary points in world space */ 243 | void ShadowMap::update_split_frustum_points(Camera* camera) { 244 | vec3 center = camera->position(); 245 | vec3 view_dir = camera->target(); 246 | 247 | vec3 up(0.0f, 1.0f, 0.0f); 248 | vec3 right = glm::cross(view_dir, up); 249 | 250 | for(int i = 0 ; i < m_num_splits ; i++) { 251 | Frustum& t_frustum = m_frustums[i]; 252 | 253 | vec3 fc = center + view_dir * t_frustum.far(); 254 | vec3 nc = center + view_dir * t_frustum.near(); 255 | 256 | right = glm::normalize(right); 257 | up = glm::normalize(glm::cross(right, view_dir)); 258 | 259 | // these heights and widths are half the heights and widths of 260 | // the near and far plane rectangles 261 | float near_height = tan(t_frustum.fov() / 2.0f) * t_frustum.near(); 262 | float near_width = near_height * t_frustum.ratio(); 263 | float far_height = tan(t_frustum.fov() / 2.0f) * t_frustum.far(); 264 | float far_width = far_height * t_frustum.ratio(); 265 | 266 | t_frustum.m_points[0] = nc - up * near_height - right * near_width; 267 | t_frustum.m_points[1] = nc + up * near_height - right * near_width; 268 | t_frustum.m_points[2] = nc + up * near_height + right * near_width; 269 | t_frustum.m_points[3] = nc - up * near_height + right * near_width; 270 | 271 | t_frustum.m_points[4] = fc - up * far_height - right * far_width; 272 | t_frustum.m_points[5] = fc + up * far_height - right * far_width; 273 | t_frustum.m_points[6] = fc + up * far_height + right * far_width; 274 | t_frustum.m_points[7] = fc - up * far_height + right * far_width; 275 | } 276 | } 277 | 278 | /** 279 | * Adjust the view frustum of the light, so that it encloses the camera frustum slice fully. 280 | * Note that this function sets the projection matrix as it sees best fit 281 | * minZ is just for optimization to cull trees that do not affect the shadows 282 | */ 283 | void ShadowMap::generate_crop_matrices(const mat4& t_modelview) { 284 | mat4 t_projection; 285 | for(int i = 0 ; i < m_num_splits ; i++) { 286 | Frustum& t_frustum = m_frustums[i]; 287 | 288 | vec3 tmax(-1000.0f, -1000.0f, 0.0f); 289 | vec3 tmin(1000.0f, 1000.0f, 0.0f); 290 | 291 | // find the z-range of the current frustum as seen from the light 292 | // in order to increase precision 293 | 294 | // note that only the z-component is need and thus 295 | // the multiplication can be simplified 296 | // transf.z = shad_modelview[2] * f.point[0].x + shad_modelview[6] * f.point[0].y + shad_modelview[10] * f.point[0].z + shad_modelview[14]; 297 | vec4 t_transf = t_modelview * vec4(t_frustum.m_points[0], 1.0f); 298 | 299 | tmin.z = t_transf.z; 300 | tmax.z = t_transf.z; 301 | for(int j = 1 ; j < 8 ; j++) { 302 | t_transf = t_modelview * vec4(t_frustum.m_points[j], 1.0f); 303 | if(t_transf.z > tmax.z) { tmax.z = t_transf.z; } 304 | if(t_transf.z < tmin.z) { tmin.z = t_transf.z; } 305 | } 306 | 307 | // make sure all relevant shadow casters are included 308 | // note that these here are dummy objects at the edges of our scene 309 | /*for(int i = 0 ; i < NUM_OBJECTS ; i++) { 310 | t_transf = t_modelview * vec4(obj_BSphere[i].center, 1.0f); 311 | if(t_transf.z + obj_BSphere[i].radius > tmax.z) { 312 | tmax.z = t_transf.z + obj_BSphere[i].radius; 313 | } 314 | //if(transf.z - obj_BSphere[i].radius < minZ) { minZ = transf.z - obj_BSphere[i].radius; } 315 | }*/ 316 | 317 | tmax.z += 50; // TODO: This solves the dissapearing shadow problem. but how to fix? 318 | 319 | mat4 t_ortho = glm::ortho(-1.0f, 1.0f, -1.0f, 1.0f, -tmax.z, -tmin.z); 320 | mat4 t_shad_mvp = t_ortho * t_modelview; 321 | 322 | // find the extends of the frustum slice as projected in light's homogeneous coordinates 323 | for(int j = 0 ; j < 8 ; j++) { 324 | t_transf = t_shad_mvp * vec4(t_frustum.m_points[j], 1.0f); 325 | 326 | t_transf.x /= t_transf.w; 327 | t_transf.y /= t_transf.w; 328 | 329 | if(t_transf.x > tmax.x) { tmax.x = t_transf.x; } 330 | if(t_transf.x < tmin.x) { tmin.x = t_transf.x; } 331 | if(t_transf.y > tmax.y) { tmax.y = t_transf.y; } 332 | if(t_transf.y < tmin.y) { tmin.y = t_transf.y; } 333 | } 334 | 335 | vec2 tscale(2.0f / (tmax.x - tmin.x), 2.0f / (tmax.y - tmin.y)); 336 | vec2 toffset(-0.5f * (tmax.x + tmin.x) * tscale.x, -0.5f * (tmax.y + tmin.y) * tscale.y); 337 | 338 | mat4 t_shad_crop; 339 | t_shad_crop[0][0] = tscale.x; 340 | t_shad_crop[1][1] = tscale.y; 341 | t_shad_crop[0][3] = toffset.x; 342 | t_shad_crop[1][3] = toffset.y; 343 | t_shad_crop = glm::transpose(t_shad_crop); 344 | 345 | t_projection = t_shad_crop * t_ortho; 346 | 347 | //return tmin.z; 348 | 349 | // Store the projection matrix 350 | m_projection_matrices[i] = t_projection; 351 | 352 | // store the product of all shadow matries for later 353 | m_crop_matrices[i] = t_projection * t_modelview; 354 | } 355 | } 356 | 357 | } 358 | -------------------------------------------------------------------------------- /src/shadow_map.hpp: -------------------------------------------------------------------------------- 1 | #ifndef GKR_SHADOW_MAP_HPP 2 | #define GKR_SHADOW_MAP_HPP 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | /** */ 10 | namespace GKR { 11 | 12 | #define CSM_MAX_SPLITS 4 13 | 14 | class Camera; 15 | 16 | /** */ 17 | class ShadowMap { 18 | private: 19 | GLuint m_fbo; 20 | GLuint m_texture_array; 21 | 22 | int m_num_splits; 23 | int m_depth_tex_size; 24 | float m_split_weight; 25 | float m_far_bounds[CSM_MAX_SPLITS]; 26 | 27 | Frustum m_frustums[CSM_MAX_SPLITS]; 28 | 29 | mat4 m_bias; 30 | mat4 m_modelview; 31 | mat4 m_crop_matrices[CSM_MAX_SPLITS]; 32 | mat4 m_projection_matrices[CSM_MAX_SPLITS]; 33 | mat4 m_texture_matrices[CSM_MAX_SPLITS]; 34 | public: 35 | ShadowMap(); 36 | ~ShadowMap(); 37 | 38 | void init(Camera* camera); 39 | 40 | /** Generate crop and projection matrices */ 41 | void pre_depth_write(Camera* camera, const vec4& lightdir); 42 | 43 | /** Getters for the various matrices (shadow map generation) */ 44 | mat4 projection_matrix(int t_split_index); 45 | mat4 modelview_matrix(); 46 | 47 | //Frustum* frustum(int t_split_index); 48 | 49 | int num_splits() const; 50 | int depth_tex_size() const; 51 | 52 | /** OpenGL handles for FBO and texture array */ 53 | GLuint fbo() const; 54 | GLuint texture() const; 55 | 56 | /** Array of depth far values to use in shader lookup during rendering */ 57 | float* far_bounds(); 58 | 59 | /** Returns texture matrices as float array (that can be passed to shader) */ 60 | float* texture_matrices(); 61 | private: 62 | void create_fbo(); 63 | void create_texture(); 64 | 65 | void update_split_distances(Camera* camera); 66 | void update_split_frustum_points(Camera* camera); 67 | void generate_crop_matrices(const mat4& t_modelview); 68 | 69 | /** Update far bounds */ 70 | void update_far_bounds(const mat4& projection, const mat4& view_inverse); 71 | void update_texture_matrices(const mat4& projection, const mat4& view_inverse); 72 | }; 73 | 74 | } 75 | 76 | #endif 77 | 78 | -------------------------------------------------------------------------------- /src/terrain.cpp: -------------------------------------------------------------------------------- 1 | // Email: sdkfeedback@nvidia.com 2 | // 3 | // Copyright (c) NVIDIA Corporation. All rights reserved. 4 | 5 | #include 6 | #include "terrain.h" 7 | 8 | #ifndef max 9 | #define max(a,b) (((a) > (b)) ? (a) : (b)) 10 | #endif 11 | 12 | #ifndef min 13 | #define min(a,b) (((a) < (b)) ? (a) : (b)) 14 | #endif 15 | 16 | Terrain::Terrain() 17 | { 18 | tex = 0; 19 | heights = NULL; 20 | normals = NULL; 21 | } 22 | 23 | Terrain::~Terrain() 24 | { 25 | if(tex) 26 | glDeleteTextures(1, &tex); 27 | if(heights) 28 | delete [] heights; 29 | if(normals) 30 | delete [] normals; 31 | if(modelT) 32 | delete modelT; 33 | if(modelL) 34 | delete modelL; 35 | height = 0; 36 | width = 0; 37 | 38 | for(unsigned int i=0; ix = e_ratio_x * (float)x; 117 | v->y = heights[x + z*width]; 118 | v->z = e_ratio_z * (float)z; 119 | entities.push_back(v); 120 | } 121 | } 122 | } 123 | 124 | modelT = new nv::Model; 125 | modelL = new nv::Model; 126 | if(!LoadTree()) 127 | { 128 | printf("Couldn't find model .obj.\n"); 129 | exit(0); 130 | } 131 | 132 | 133 | MakeTerrain(); 134 | 135 | return true; 136 | } 137 | 138 | void Terrain::Draw(GLuint t_current_program, const glm::mat4& t_view) { 139 | float minCamZ = -10000.0f; 140 | 141 | float half_width = 0.5f*(float)width; 142 | float half_height = 0.5f*(float)height; 143 | 144 | glm::mat4 t_modelview1 = glm::translate(t_view, glm::vec3(-half_width, 0, -half_height)); 145 | glm::mat3 t_normalmatrix1 = glm::inverseTranspose(glm::mat3(t_modelview1)); 146 | 147 | int far_dist = (int)FAR_DIST - 1; 148 | 149 | int camx = (int)(cam_pos[0] + half_width); 150 | int camz = (int)(cam_pos[2] + half_height); 151 | 152 | int zmin = max(camz-far_dist, 1); 153 | int zmax = min(camz+far_dist, height - 1); 154 | 155 | int xmin = max(camx-far_dist, 1); 156 | int xmax = min(camx+far_dist, width - 1); 157 | 158 | for(unsigned int i=0; ix-half_width) + m_light_dir.y*v->y + m_light_dir.z*(v->z-half_height); 161 | if(minCamZ < d) { //MODEL_HEIGHT 162 | glm::mat4 t_modelview2 = glm::translate(t_modelview1, glm::vec3(v->x, v->y, v->z)); 163 | glm::mat3 t_normalmatrix2 = glm::inverseTranspose(glm::mat3(t_modelview2)); 164 | 165 | glUniformMatrix4fv(glGetUniformLocation(t_current_program, "modelViewMatrix"), 1, GL_FALSE, glm::value_ptr(t_modelview2)); 166 | glUniformMatrix3fv(glGetUniformLocation(t_current_program, "normalMatrix"), 1, GL_FALSE, glm::value_ptr(t_normalmatrix2)); 167 | 168 | DrawTree(); 169 | } 170 | } 171 | 172 | glUniformMatrix4fv(glGetUniformLocation(t_current_program, "modelViewMatrix"), 1, GL_FALSE, glm::value_ptr(t_modelview1)); 173 | glUniformMatrix3fv(glGetUniformLocation(t_current_program, "normalMatrix"), 1, GL_FALSE, glm::value_ptr(t_normalmatrix1)); 174 | 175 | glActiveTexture(GL_TEXTURE1); 176 | glCallList(terrain_list); 177 | 178 | glMatrixMode(GL_MODELVIEW); 179 | glActiveTexture(GL_TEXTURE0); 180 | } 181 | 182 | void Terrain::DrawCoarse() 183 | { 184 | float half_width = 0.5f*(float)width; 185 | float half_height = 0.5f*(float)height; 186 | 187 | const float inv_height = 1.0f / (float)height; 188 | const float inv_width = 1.0f / (float)width; 189 | 190 | glMatrixMode(GL_MODELVIEW); 191 | glPushMatrix(); 192 | glTranslatef(-half_width, 0, -half_height); 193 | 194 | glBindTexture(GL_TEXTURE_2D, tex); 195 | 196 | for(int z=1; zloadModelFromFile(&MODEL_FILENAMET[0])) { 258 | if (!modelT->loadModelFromFile(&MODEL_FILENAMET[3])) 259 | return false; 260 | } 261 | modelT->compileModel(); 262 | 263 | int totalVertexSize = modelT->getCompiledVertexCount() * modelT->getCompiledVertexSize() * sizeof(GLfloat); 264 | int totalIndexSize = modelT->getCompiledIndexCount() * sizeof(GLuint); 265 | 266 | glGenBuffers(1, &vboIdT); 267 | glBindBuffer(GL_ARRAY_BUFFER, vboIdT); 268 | glBufferData(GL_ARRAY_BUFFER, totalVertexSize, modelT->getCompiledVertices(), GL_STATIC_DRAW); 269 | glBindBuffer(GL_ARRAY_BUFFER, 0); 270 | 271 | glGenBuffers(1, &eboIdT); 272 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eboIdT); 273 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, totalIndexSize, modelT->getCompiledIndices(), GL_STATIC_DRAW); 274 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 275 | 276 | printf("loading OBJ leaves...\n"); 277 | if (!modelL->loadModelFromFile(&MODEL_FILENAMEL[0])) { 278 | if (!modelL->loadModelFromFile(&MODEL_FILENAMEL[3])) 279 | return false; 280 | } 281 | modelL->compileModel(); 282 | 283 | totalVertexSize = modelL->getCompiledVertexCount() * modelL->getCompiledVertexSize() * sizeof(GLfloat); 284 | totalIndexSize = modelL->getCompiledIndexCount() * sizeof(GLuint); 285 | 286 | glGenBuffers(1, &vboIdL); 287 | glBindBuffer(GL_ARRAY_BUFFER, vboIdL); 288 | glBufferData(GL_ARRAY_BUFFER, totalVertexSize, modelL->getCompiledVertices(), GL_STATIC_DRAW); 289 | glBindBuffer(GL_ARRAY_BUFFER, 0); 290 | 291 | glGenBuffers(1, &eboIdL); 292 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eboIdL); 293 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, totalIndexSize, modelL->getCompiledIndices(), GL_STATIC_DRAW); 294 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 295 | return true; 296 | } 297 | 298 | 299 | void Terrain::DrawTree() 300 | { 301 | glBindBuffer(GL_ARRAY_BUFFER, vboIdT); 302 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eboIdT); 303 | int stride = modelT->getCompiledVertexSize() * sizeof(GLfloat); 304 | int normalOffset = modelT->getCompiledNormalOffset() * sizeof(GLfloat); 305 | glVertexPointer(modelT->getPositionSize(), GL_FLOAT, stride, NULL); 306 | glNormalPointer(GL_FLOAT, stride, (GLubyte *)NULL + normalOffset); 307 | glEnableClientState(GL_VERTEX_ARRAY); 308 | glEnableClientState(GL_NORMAL_ARRAY); 309 | 310 | glColor3f(0.917647f, 0.776471f, 0.576471f); 311 | glDrawElements(GL_TRIANGLES, modelT->getCompiledIndexCount(), GL_UNSIGNED_INT, NULL); 312 | 313 | glBindBuffer(GL_ARRAY_BUFFER, vboIdL); 314 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eboIdL); 315 | stride = modelL->getCompiledVertexSize() * sizeof(GLfloat); 316 | normalOffset = modelL->getCompiledNormalOffset() * sizeof(GLfloat); 317 | glVertexPointer(modelL->getPositionSize(), GL_FLOAT, stride, NULL); 318 | glNormalPointer(GL_FLOAT, stride, (GLubyte *)NULL + normalOffset); 319 | glEnableClientState(GL_VERTEX_ARRAY); 320 | glEnableClientState(GL_NORMAL_ARRAY); 321 | 322 | glColor3f(0.301961f, 0.588235f, 0.309804f); 323 | glDrawElements(GL_TRIANGLES, modelL->getCompiledIndexCount(), GL_UNSIGNED_INT, NULL); 324 | 325 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 326 | glBindBuffer(GL_ARRAY_BUFFER, 0); 327 | glDisableClientState(GL_VERTEX_ARRAY); 328 | glDisableClientState(GL_NORMAL_ARRAY); 329 | glColor3f(1.0f, 1.0f, 1.0f); 330 | } 331 | -------------------------------------------------------------------------------- /src/terrain.h: -------------------------------------------------------------------------------- 1 | // Email: sdkfeedback@nvidia.com 2 | // 3 | // Copyright (c) NVIDIA Corporation. All rights reserved. 4 | 5 | #pragma once 6 | 7 | #include "main.h" 8 | #include 9 | #include 10 | 11 | #define SCALE 0.2f; 12 | #define MODEL_Y_TRANSLATE -0.1f 13 | #define MODEL_HEIGHT 3.0f 14 | 15 | 16 | const char TERRAIN_TEX_FILENAME[] = "../../media/textures/gcanyon.png"; 17 | const char DEPTH_TEX_FILENAME[] = "../../media/textures/gcanyond.png"; 18 | const char ENTITIES_TEX_FILENAME[] = "../../media/textures/entities.png"; 19 | const char MODEL_FILENAMET[] = "../../media/models/trunk.obj"; 20 | const char MODEL_FILENAMEL[] = "../../media/models/leaves.obj"; 21 | 22 | class Terrain 23 | { 24 | public: 25 | Terrain(); 26 | ~Terrain(); 27 | bool Load(); 28 | void Draw(GLuint t_current_program, const glm::mat4& t_view); 29 | void DrawCoarse(); 30 | int getDim(){ return (width>height)?width:height; } 31 | private: 32 | void MakeTerrain(); 33 | bool LoadTree(); 34 | void DrawTree(); 35 | 36 | GLuint tex; 37 | float *heights; 38 | float *normals; 39 | std::vector entities; 40 | 41 | int height; 42 | int width; 43 | 44 | nv::Model *modelT; 45 | nv::Model *modelL; 46 | 47 | GLuint vboIdT; 48 | GLuint eboIdT; 49 | GLuint vboIdL; 50 | GLuint eboIdL; 51 | 52 | GLuint terrain_list; 53 | }; 54 | -------------------------------------------------------------------------------- /src/utility.cpp: -------------------------------------------------------------------------------- 1 | // Email: sdkfeedback@nvidia.com 2 | // 3 | // Copyright (c) NVIDIA Corporation. All rights reserved. 4 | 5 | #include 6 | #define NV_REPORT_COMPILE_ERRORS 7 | #include 8 | #include "main.h" 9 | 10 | #include 11 | #include 12 | 13 | using std::string; 14 | using std::cout; 15 | using std::endl; 16 | 17 | GKR::Camera m_camera; 18 | GKR::ShadowMap m_shadow_map; 19 | 20 | float cam_pos[3] = {75.5, 30.0f, -110}; 21 | float cam_view[3] = {-0.7f, 0.0f, 0.7f}; 22 | 23 | //GLfloat light_dir[4] = {0.2f, 0.99f, 0.0f , 0.0f}; 24 | vec4 m_light_dir(0.2f, 0.99f, 0.0f , 0.0f); 25 | 26 | bool rotate_light_dir = false; 27 | 28 | const float sensitivity = 0.005; 29 | const float walk_speed = 0.5; 30 | 31 | static int old_x, old_y; 32 | int half_width; 33 | int half_height; 34 | 35 | GKR::Camera* get_camera() { 36 | return &m_camera; 37 | } 38 | 39 | GKR::ShadowMap* get_shadow_map() { 40 | return &m_shadow_map; 41 | } 42 | 43 | void normalize(float *v) { 44 | float magnitude = sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]); 45 | v[0] /= magnitude; 46 | v[1] /= magnitude; 47 | v[2] /= magnitude; 48 | } 49 | 50 | void rotate_view(float *view, float angle, float x, float y, float z) { 51 | float new_x; 52 | float new_y; 53 | float new_z; 54 | 55 | float c = cos(angle); 56 | float s = sin(angle); 57 | 58 | new_x = (x*x*(1-c) + c) * view[0]; 59 | new_x += (x*y*(1-c) - z*s) * view[1]; 60 | new_x += (x*z*(1-c) + y*s) * view[2]; 61 | 62 | new_y = (y*x*(1-c) + z*s) * view[0]; 63 | new_y += (y*y*(1-c) + c) * view[1]; 64 | new_y += (y*z*(1-c) - x*s) * view[2]; 65 | 66 | new_z = (x*z*(1-c) - y*s) * view[0]; 67 | new_z += (y*z*(1-c) + x*s) * view[1]; 68 | new_z += (z*z*(1-c) + c) * view[2]; 69 | 70 | view[0] = new_x; 71 | view[1] = new_y; 72 | view[2] = new_z; 73 | 74 | normalize(view); 75 | } 76 | 77 | void motion(int x, int y) { 78 | 79 | int orig_x = x; 80 | int orig_y = y; 81 | float rot_x, rot_y; 82 | float rot_axis[3]; 83 | 84 | x -= half_width; 85 | y -= half_height; 86 | 87 | rot_x = -(float)(x - old_x) * sensitivity; 88 | rot_y = -(float)(y - old_y) * sensitivity; 89 | 90 | old_x = x; 91 | old_y = y; 92 | 93 | //if(GetAsyncKeyState(VK_SHIFT)) 94 | if(rotate_light_dir) { 95 | m_light_dir.y += rot_y; 96 | if(m_light_dir.y < 0.2f) { 97 | m_light_dir.y = 0.2f; 98 | } 99 | 100 | m_light_dir = glm::normalize(m_light_dir); 101 | m_light_dir = glm::rotate(m_light_dir, glm::degrees(-rot_x), m_camera.target()); 102 | //rotate_view(light_dir, -rot_x, cam_view[0], cam_view[1], cam_view[2]); 103 | } else { 104 | m_camera.tracker()->track(orig_x, orig_y); 105 | 106 | rotate_view(cam_view, rot_x, 0.0f, 1.0f, 0.0f); 107 | 108 | rot_axis[0] = -cam_view[2]; 109 | rot_axis[1] = 0.0f; 110 | rot_axis[2] = cam_view[0]; 111 | 112 | normalize(rot_axis); 113 | 114 | rotate_view(cam_view, rot_y, rot_axis[0], rot_axis[1], rot_axis[2]); 115 | } 116 | } 117 | 118 | glm::mat4 camLook2() { 119 | /*glm::vec3 eye(cam_pos[0], cam_pos[1], cam_pos[2]); 120 | glm::vec3 center(cam_pos[0] + cam_view[0], cam_pos[1] + cam_view[1], cam_pos[2] + cam_view[2]); 121 | glm::vec3 up(0.0f, 1.0f, 0.0f); 122 | 123 | glm::mat4 t_mat_cam = glm::lookAt(eye, center, up); 124 | return t_mat_cam;*/ 125 | 126 | return m_camera.view_matrix(); 127 | } 128 | 129 | void camLook() { 130 | gluLookAt( 131 | cam_pos[0], cam_pos[1], cam_pos[2], 132 | cam_pos[0] + cam_view[0], cam_pos[1] + cam_view[1], cam_pos[2] + cam_view[2], 133 | 0.0f, 1.0f, 0.0f); 134 | } 135 | 136 | void compare_matrix(float* t_mat_orig, const glm::mat4& t_glm_mat) { 137 | cout << "==============" << endl; 138 | cout << "-- GLM MAT4 --" << endl; 139 | for(int i = 0 ; i < 4 ; i++) { 140 | for(int j = 0 ; j < 4 ; j++) { 141 | cout << "[" << t_glm_mat[i][j] << "]"; 142 | } 143 | cout << endl; 144 | } 145 | cout << "-- GLM MAT4 --" << endl; 146 | 147 | cout << "-- FLOAT MAT4 --" << endl; 148 | for(int i = 0 ; i < 4 ; i++) { 149 | for(int j = 0 ; j < 4 ; j++) { 150 | cout << "[" << t_mat_orig[i*4+j] << "]"; 151 | } 152 | cout << endl; 153 | } 154 | cout << "-- FLOAT MAT4 --" << endl; 155 | cout << "================" << endl; 156 | } 157 | 158 | void updateKeys() { 159 | /*if(GetAsyncKeyState('W')){ 160 | cam_pos[0] += cam_view[0] * walk_speed; 161 | cam_pos[1] += cam_view[1] * walk_speed; 162 | cam_pos[2] += cam_view[2] * walk_speed; 163 | } 164 | if(GetAsyncKeyState('S')){ 165 | cam_pos[0] -= cam_view[0] * walk_speed; 166 | cam_pos[1] -= cam_view[1] * walk_speed; 167 | cam_pos[2] -= cam_view[2] * walk_speed; 168 | } 169 | if(GetAsyncKeyState('A')){ 170 | cam_pos[0] += cam_view[2] * walk_speed; 171 | 172 | cam_pos[2] -= cam_view[0] * walk_speed; 173 | } 174 | if(GetAsyncKeyState('D')){ 175 | cam_pos[0] -= cam_view[2] * walk_speed; 176 | 177 | cam_pos[2] += cam_view[0] * walk_speed; 178 | } 179 | 180 | if(GetAsyncKeyState(VK_SPACE)){ 181 | cam_pos[1] += walk_speed; 182 | }*/ 183 | } 184 | 185 | void mouse(int button, int state, int x, int y) { 186 | old_x = x - half_width; 187 | old_y = y - half_height; 188 | m_camera.tracker()->begin(x, y); 189 | glutPostRedisplay(); 190 | } 191 | 192 | void idle() { 193 | glutPostRedisplay(); 194 | } 195 | 196 | void key_down(unsigned char c, int x, int y) { 197 | switch (c) { 198 | case 27: //escape key 199 | case 'q': 200 | case 'Q': 201 | exit(0); 202 | break; 203 | case 'r': { 204 | rotate_light_dir = true; break; 205 | } 206 | case 'w': { 207 | m_camera.mover()->forward(true); break; 208 | } 209 | case 's': { 210 | m_camera.mover()->backward(true); break; 211 | } 212 | case 'a': { 213 | m_camera.mover()->left(true); break; 214 | } 215 | case 'd': { 216 | m_camera.mover()->right(true); break; 217 | } 218 | } 219 | } 220 | 221 | void key_up(unsigned char c, int x, int y) { 222 | switch (c) { 223 | case 'r': { 224 | rotate_light_dir = false; break; 225 | } 226 | case 'w': { 227 | m_camera.mover()->forward(false); break; 228 | } 229 | case 's': { 230 | m_camera.mover()->backward(false); break; 231 | } 232 | case 'a': { 233 | m_camera.mover()->left(false); break; 234 | } 235 | case 'd': { 236 | m_camera.mover()->right(false); break; 237 | } 238 | } 239 | } 240 | 241 | void keys( unsigned char c, int x, int y) { 242 | 243 | switch (c) { 244 | case 27: //escape key 245 | case 'q': 246 | case 'Q': 247 | exit(0); 248 | break; 249 | case 'w': { 250 | cam_pos[0] += cam_view[0] * walk_speed; 251 | cam_pos[1] += cam_view[1] * walk_speed; 252 | cam_pos[2] += cam_view[2] * walk_speed; 253 | break; 254 | } 255 | case 's': { 256 | cam_pos[0] -= cam_view[0] * walk_speed; 257 | cam_pos[1] -= cam_view[1] * walk_speed; 258 | cam_pos[2] -= cam_view[2] * walk_speed; 259 | break; 260 | } 261 | case 'a': { 262 | cam_pos[0] += cam_view[2] * walk_speed; 263 | cam_pos[2] -= cam_view[0] * walk_speed; 264 | break; 265 | } 266 | case 'd': { 267 | cam_pos[0] -= cam_view[2] * walk_speed; 268 | cam_pos[2] += cam_view[0] * walk_speed; 269 | break; 270 | } 271 | /*case '1': 272 | cur_num_splits = 1; 273 | break; 274 | case '2': 275 | cur_num_splits = 2; 276 | break; 277 | case '3': 278 | cur_num_splits = 3; 279 | break; 280 | case '4': 281 | cur_num_splits = 4; 282 | break;*/ 283 | case 'g': shadow_type = 0; break; 284 | case 'h': shadow_type = 1; break; 285 | case 'j': shadow_type = 2; break; 286 | case 'k': shadow_type = 3; break; 287 | case 'l': shadow_type = 4; break; 288 | case '`': 289 | show_depth_tex = !show_depth_tex; 290 | break; 291 | case 'p': 292 | printf("cam pos: %f %f %f\n", cam_pos[0], cam_pos[1], cam_pos[2]); 293 | break; 294 | /*case '+': 295 | split_weight += 0.05f; 296 | printf( "split_weight changed to %f\n", split_weight); 297 | break; 298 | case '-': 299 | split_weight -= 0.05f; 300 | printf( "split_weight changed to %f\n", split_weight); 301 | break;*/ 302 | } 303 | 304 | glutPostRedisplay(); 305 | } 306 | 307 | void reshape(int w, int h) { 308 | width = w; 309 | height = h; 310 | half_height = height/2; 311 | half_width = width/2; 312 | 313 | float ratio = (float)width / (float)height; 314 | m_camera.position(vec3(75.5f, 30.0f, -110.0f)); 315 | m_camera.target(vec3(-0.7f, 0.0f, 0.7f)); 316 | m_camera.viewport()->set(0, 0, width, height); 317 | m_camera.frustum()->set(45.0, ratio, 1.0, FAR_DIST); 318 | 319 | m_shadow_map.init(&m_camera); 320 | 321 | //glMatrixMode(GL_PROJECTION); 322 | //glLoadIdentity(); 323 | //gluPerspective(45.0, (double)width/(double)height, 1.0, FAR_DIST); 324 | 325 | glViewport(0, 0, width, height); 326 | /*for(int i = 0 ; i < MAX_SPLITS ; i++) { 327 | f[i].fov = CAMERA_FOV / 57.2957795 + 0.2f; 328 | f[i].ratio = (double)width/(double)height; 329 | }*/ 330 | } 331 | 332 | void regenerateDepthTex(GLuint depth_size) { 333 | /*glDeleteTextures(1, &depth_tex_ar); 334 | glGenTextures(1, &depth_tex_ar); 335 | 336 | glBindTexture(GL_TEXTURE_2D_ARRAY_EXT, depth_tex_ar); 337 | glTexImage3D(GL_TEXTURE_2D_ARRAY_EXT, 0, GL_DEPTH_COMPONENT24, depth_size, depth_size, MAX_SPLITS, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL); 338 | glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 339 | glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 340 | glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 341 | glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 342 | glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);*/ 343 | } 344 | 345 | void menu(int m) { 346 | switch (m) { 347 | case 'q': 348 | exit(0); 349 | break; 350 | /*case '1': 351 | cur_num_splits = 1; 352 | break; 353 | case '2': 354 | cur_num_splits = 2; 355 | break; 356 | case '3': 357 | cur_num_splits = 3; 358 | break; 359 | case '4': 360 | cur_num_splits = 4; 361 | break;*/ 362 | case '`': 363 | show_depth_tex = !show_depth_tex; 364 | break; 365 | case 0: 366 | shadow_type = 0; 367 | break; 368 | case 1: 369 | shadow_type = 1; 370 | break; 371 | case 2: 372 | shadow_type = 2; 373 | break; 374 | case 3: 375 | shadow_type = 3; 376 | break; 377 | case 4: 378 | shadow_type = 4; 379 | break; 380 | case 5: 381 | shadow_type = 5; 382 | break; 383 | case 6: 384 | shadow_type = 6; 385 | break; 386 | case 7: 387 | shadow_type = 7; 388 | break; 389 | case 8: 390 | shadow_type = 8; 391 | break; 392 | /*case 10: 393 | depth_size = 512; 394 | regenerateDepthTex(512); 395 | break; 396 | case 11: 397 | depth_size = 1024; 398 | regenerateDepthTex(1024); 399 | break; 400 | case 12: 401 | depth_size = 2048; 402 | regenerateDepthTex(2048); 403 | break; 404 | case 13: 405 | depth_size = 4096; 406 | regenerateDepthTex(4096); 407 | break;*/ 408 | } 409 | glutPostRedisplay(); 410 | } 411 | 412 | void cameraInverse(float dst[16], float src[16]) { 413 | dst[0] = src[0]; 414 | dst[1] = src[4]; 415 | dst[2] = src[8]; 416 | dst[3] = 0.0f; 417 | dst[4] = src[1]; 418 | dst[5] = src[5]; 419 | dst[6] = src[9]; 420 | dst[7] = 0.0f; 421 | dst[8] = src[2]; 422 | dst[9] = src[6]; 423 | dst[10] = src[10]; 424 | dst[11] = 0.0f; 425 | dst[12] = -(src[12] * src[0]) - (src[13] * src[1]) - (src[14] * src[2]); 426 | dst[13] = -(src[12] * src[4]) - (src[13] * src[5]) - (src[14] * src[6]); 427 | dst[14] = -(src[12] * src[8]) - (src[13] * src[9]) - (src[14] * src[10]); 428 | dst[15] = 1.0f; 429 | } 430 | 431 | GLuint createShaders(const char* vert, const char* frag) { 432 | GLuint v, f; 433 | 434 | if(!(v = nv::CompileGLSLShaderFromFile(GL_VERTEX_SHADER, vert))) { 435 | v = nv::CompileGLSLShaderFromFile(GL_VERTEX_SHADER, &vert[3]); //skip the first three chars to deal with path differences 436 | } 437 | 438 | if(!(f = nv::CompileGLSLShaderFromFile(GL_FRAGMENT_SHADER, frag))) { 439 | f = nv::CompileGLSLShaderFromFile(GL_FRAGMENT_SHADER, &frag[3]); //skip the first three chars to deal with path differences 440 | } 441 | 442 | return nv::LinkGLSLProgram(v, f); 443 | } 444 | 445 | void CheckFramebufferStatus() { 446 | int status; 447 | status = (GLenum) glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); 448 | switch(status) { 449 | case GL_FRAMEBUFFER_COMPLETE_EXT: 450 | break; 451 | case GL_FRAMEBUFFER_UNSUPPORTED_EXT: 452 | printf("Unsupported framebuffer format\n"); 453 | break; 454 | case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: 455 | printf("Framebuffer incomplete, missing attachment\n"); 456 | break; 457 | case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: 458 | printf("Framebuffer incomplete, incomplete attachment\n"); 459 | break; 460 | case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: 461 | printf("Framebuffer incomplete, attached images must have same dimensions\n"); 462 | break; 463 | case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: 464 | printf("Framebuffer incomplete, attached images must have same format\n"); 465 | break; 466 | case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: 467 | printf("Framebuffer incomplete, missing draw buffer\n"); 468 | break; 469 | case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: 470 | printf("Framebuffer incomplete, missing read buffer\n"); 471 | break; 472 | default: 473 | exit(0); 474 | } 475 | } 476 | --------------------------------------------------------------------------------