├── screenshots ├── drawframe.png ├── no_rendering.png ├── screenshot1.png └── screenshot2.png ├── .gitignore ├── timer.h ├── timer.cpp ├── event_listener.h ├── Makefile ├── camera.h ├── main.cpp ├── core.h ├── README.md ├── controller.h ├── renderer.h ├── camera.cpp ├── object.h ├── fluid.h ├── arcball.h ├── controller.cpp ├── arcball.cpp ├── object.cpp ├── fluid.cpp ├── renderer.cpp └── LICENSE /screenshots/drawframe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nevermoe/SmokeSimulation/HEAD/screenshots/drawframe.png -------------------------------------------------------------------------------- /screenshots/no_rendering.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nevermoe/SmokeSimulation/HEAD/screenshots/no_rendering.png -------------------------------------------------------------------------------- /screenshots/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nevermoe/SmokeSimulation/HEAD/screenshots/screenshot1.png -------------------------------------------------------------------------------- /screenshots/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nevermoe/SmokeSimulation/HEAD/screenshots/screenshot2.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /* 2 | !*.cpp 3 | !*.hpp 4 | !*.h 5 | !.gitignore 6 | !Makefile 7 | !ycm* 8 | !README* 9 | !screenshots/ 10 | -------------------------------------------------------------------------------- /timer.h: -------------------------------------------------------------------------------- 1 | #ifndef _TIMER_H 2 | #define _TIMER_H 3 | 4 | #include "core.h" 5 | 6 | class Timer { 7 | public: 8 | Timer(); 9 | void StartTimer(); 10 | double StopTimer(); 11 | private: 12 | double _startTime; 13 | double _stopTime; 14 | double _elapsedTime; 15 | }; 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /timer.cpp: -------------------------------------------------------------------------------- 1 | #include "core.h" 2 | #include "timer.h" 3 | 4 | 5 | Timer::Timer() 6 | { 7 | _startTime = _stopTime = _elapsedTime = 0; 8 | } 9 | 10 | void Timer::StartTimer() 11 | { 12 | _startTime = glfwGetTime(); 13 | } 14 | 15 | double Timer::StopTimer() 16 | { 17 | _stopTime = glfwGetTime(); 18 | return (_stopTime - _startTime); 19 | } 20 | -------------------------------------------------------------------------------- /event_listener.h: -------------------------------------------------------------------------------- 1 | class EventListener { 2 | public: 3 | virtual void MouseButton(GLFWwindow *window, int button,int action,int mods) = 0; 4 | virtual void MouseMotion(GLFWwindow *window, double nx, double ny) = 0; 5 | virtual void Keyboard(GLFWwindow * window, int key, int scancode, int action, int mods) = 0; 6 | virtual void Resize(GLFWwindow *window, int x, int y) = 0; 7 | }; 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC = g++ 2 | TARGET = main 3 | 4 | RELEASE := 1 5 | ifeq ($(RELEASE),1) 6 | CXXFLAGS += -O3 7 | else 8 | CXXFLAGS += -g 9 | endif 10 | 11 | #CXXFLAGS += -DENABLE_WRITE_FILE 12 | 13 | LIBS_PATH = -L/usr/local/lib/OpenMesh 14 | LIBS_PATH += -L/usr/local/lib 15 | 16 | LIBS = -lGLEW -lGL -lGLU -lglfw3 -lX11 -lXrandr -lpthread -lXi 17 | LIBS += -lOpenMeshCore -lOpenMeshTools 18 | 19 | SRC := $(shell find . -name "*.cpp") 20 | 21 | all: 22 | @make $(TARGET) 23 | 24 | 25 | %.o : %.cpp 26 | @echo "Compiling $< ..." 27 | @$(CC) $(LIBS_PATH) $(LIBS) $(CXXFLAGS) -o $@ -c $< 28 | 29 | $(TARGET): $(SRC:.cpp=.o) 30 | @echo "Linking $@..." 31 | @$(CC) -o $@ $(SRC:.cpp=.o) $(LIBS_PATH) $(LIBS) 32 | 33 | .PHONY: clean 34 | clean: 35 | \rm -f *.o 36 | \rm -f $(TARGET) 37 | -------------------------------------------------------------------------------- /camera.h: -------------------------------------------------------------------------------- 1 | #ifndef _CAMERA_H 2 | #define _CAMERA_H 3 | 4 | #include "core.h" 5 | 6 | class Camera { 7 | public: 8 | Camera(GLFWwindow* windowHandle); 9 | 10 | void Reset(); 11 | void SetLight(); 12 | void RegisterParentWindow(GLFWwindow* windowHandle); 13 | 14 | 15 | // Access functions 16 | void SetAspect(GLfloat aspect); 17 | 18 | private: 19 | // Perspective controls 20 | GLfloat _FOV; // Field of View Angle 21 | GLfloat _aspect; // Aspect Ratio 22 | GLfloat _nearClip; // Near clipping plane distance 23 | GLfloat _farClip; // Far clipping plane distance 24 | int _winX; 25 | int _winY; 26 | 27 | 28 | GLFWwindow* _windowHandle; 29 | }; 30 | 31 | /* 32 | The Camera class provides a simple means to controlling the 3D camera. It could 33 | be extended to support more interactive controls. Ultimately. the camera sets the 34 | GL projection and viewing matrices. 35 | */ 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include "core.h" 2 | #include "controller.h" 3 | #include "fluid.h" 4 | 5 | #define WINDOW_NAME "Smoke3D" 6 | 7 | //#define VERTEX_PROG_NAME1 "backface.vert" 8 | //#define FRAGMENT_PROG_NAME1 "backface.frag" 9 | //#define VERTEX_PROG_NAME2 "raycasting.vert" 10 | //#define FRAGMENT_PROG_NAME2 "raycasting.frag" 11 | 12 | Controller* g_controller = NULL; 13 | 14 | int main(int argc, char **argv) { 15 | ::g_controller = new Controller(argc, argv, WINDOW_NAME); 16 | Object* object = new Fluid; 17 | 18 | #if 0 19 | //enable shader 20 | object->RegisterShader(VERTEX_PROG_NAME1, GL_VERTEX_SHADER); 21 | object->RegisterShader(FRAGMENT_PROG_NAME1, GL_FRAGMENT_SHADER); 22 | object->RegisterShader(VERTEX_PROG_NAME2, GL_VERTEX_SHADER); 23 | object->RegisterShader(FRAGMENT_PROG_NAME2, GL_FRAGMENT_SHADER); 24 | object->EnableShader(); 25 | #endif 26 | 27 | ::g_controller->InitCamera(); 28 | ::g_controller->RegisterObject(object); 29 | ::g_controller->BeginLoop(); 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /core.h: -------------------------------------------------------------------------------- 1 | #ifndef _CORE_H 2 | #define _CORE_H 3 | 4 | #define PI 3.14159 5 | #define INF (1<<31) 6 | //#define DEBUG_LEVEL 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #define GLFW_INCLUDE_GLU 36 | #include 37 | 38 | #include 39 | #include 40 | 41 | #include "event_listener.h" 42 | 43 | //#include 44 | //#include 45 | //#include 46 | //#include 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a fluid simulation program. The smoke is simulated using Euler method (grid-based method), and rendered with volume ray casting. See [this video](https://www.youtube.com/watch?v=7UAFDXSXJu4&feature=youtu.be). 2 | 3 | 4 | 1. Prerequisites: 5 | 6 | opengl 7 | glew 8 | glfw 9 | Eigen 10 | 11 | 12 | 2. How to run: 13 | 14 | 1. make 15 | 2. ./main 16 | 17 | 3. Controll: 18 | 19 | Mouse: 20 | 21 | 1. Change angle of view with mouse left key, and zoom with middle key. 22 | 2. Select the Light and drag to change light position. 23 | 24 | Keyboard: 25 | 26 | 1. R to reset the scene. 27 | 2. S to switch between rendering and none rendering mode. 28 | 3. W to toggle slices outline on/off. 29 | 4. ESC to quit. 30 | 31 | 4. Screenshots: 32 | 33 | ![ScreenShot](https://raw.githubusercontent.com/nevermoe/SmokeSimulation/master/screenshots/screenshot1.png) 34 | ![ScreenShot](https://raw.githubusercontent.com/nevermoe/SmokeSimulation/master/screenshots/screenshot2.png) 35 | ![ScreenShot](https://raw.githubusercontent.com/nevermoe/SmokeSimulation/master/screenshots/no_rendering.png) 36 | ![ScreenShot](https://raw.githubusercontent.com/nevermoe/SmokeSimulation/master/screenshots/drawframe.png) 37 | 38 | -------------------------------------------------------------------------------- /controller.h: -------------------------------------------------------------------------------- 1 | #ifndef _CONTROLLER_H 2 | #define _CONTROLLER_H 3 | 4 | #include "core.h" 5 | #include "object.h" 6 | #include "arcball.h" 7 | #include "camera.h" 8 | #include "timer.h" 9 | 10 | class Controller { 11 | public: 12 | Controller(int argc,char **argv, const char *windowName); 13 | ~Controller(); 14 | 15 | void BeginLoop(); 16 | void RegisterObject(Object* object); 17 | 18 | void Reset(); 19 | void Render(); 20 | void InitCamera(); 21 | 22 | int GetActiveObject(int mx, int my); 23 | 24 | void Quit(); 25 | 26 | // Event handlers 27 | void Resize(GLFWwindow *window, int x, int y); 28 | void Keyboard(GLFWwindow * window, int key, int scancode,int action, int mods); 29 | void MouseButton(GLFWwindow *window, int btn, int action, int mods); 30 | void MouseMotion(GLFWwindow *window, double x, double y); 31 | void MouseScroll(GLFWwindow *window, double x, double y); 32 | 33 | private: 34 | // Window management 35 | std::string _windowName; 36 | std::stringstream _titleInfo; 37 | GLFWwindow *_windowHandle; 38 | int _winX, _winY; 39 | 40 | // Input 41 | bool _isLeftKeyPressed, _isCtrlPressed, _isRightKeyPressed, _isMiddleKeyPressed; 42 | bool _isLightOn; 43 | 44 | double _prevCursorX, _prevCursorY; 45 | 46 | Object** _objects; 47 | int _objectNo; 48 | int _maxObjectNo; 49 | int _activeObj; 50 | 51 | Camera* _camera; 52 | 53 | void _ComputeFPS(); 54 | Timer _timer; 55 | double _elapsedTime; 56 | int _numOfFrame; 57 | double _fps; 58 | }; 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /renderer.h: -------------------------------------------------------------------------------- 1 | #ifndef _RENDERER_H 2 | #define _RENDERER_H 3 | 4 | #include "core.h" 5 | 6 | #ifndef ALMOST_EQUAL 7 | #define ALMOST_EQUAL(a, b) ((fabs(a-b)<0.00001f)?true:false) 8 | #endif 9 | 10 | #ifndef _I 11 | #define _I(x,y,z) (((x)*(_RES)*(_RES))+((y)*(_RES))+(z)) //FIXME 12 | #endif 13 | 14 | #ifndef FOR_ALL_CELL 15 | #define FOR_ALL_CELL for (int i=1; i<=(_N); i++) {\ 16 | for (int j=1; j<=(_N); j++) {\ 17 | for (int k=1; k<=(_N); k++) { 18 | #define END_FOR }}} 19 | #endif 20 | 21 | #define SLICE_NUM 64.0f 22 | 23 | 24 | class Renderer 25 | { 26 | private: 27 | 28 | // texture data 29 | unsigned char* _textureData; 30 | // texture handle 31 | unsigned int _hTexture; 32 | //lighting infomations 33 | Eigen::Vector3f _lightDir; 34 | Eigen::Vector3f _lightPos; 35 | int _rayTemplate[4096][3]; 36 | float *_volumeData; 37 | 38 | GLfloat _cubeVertices[8][3]; 39 | GLfloat _cubeEdges[12][2][3]; 40 | 41 | // draw the slices. mvMatrix must be the MODELVIEW_MATRIX 42 | void DrawSlices(GLdouble mvMatrix[16]); 43 | 44 | // intersect a plane with the cube, helper function for DrawSlices() 45 | // plane equation is Ax + By + Cz + D = 0 46 | std::vector IntersectEdges(float A, float B, float C, float D); 47 | 48 | void GenerateRayTemplate(int edgeLen); 49 | void CastLight(int edgelen, const float* dens, unsigned char* intensity); 50 | inline void LightRay(int x, int y, int z, int n, float decay, 51 | const float* dens, unsigned char* intensity); 52 | 53 | 54 | void InitGL(); 55 | 56 | // if _isDrawSliceOutline==true, the outline of the slices will be drawn as well 57 | bool _isDrawSliceOutline; 58 | bool _isRendering; 59 | 60 | int _SIZE; //size of volume data 61 | int _N; // 62 | int _RES; // 63 | public: 64 | Renderer(float* volumeData, int RES); 65 | ~Renderer(); 66 | void SetLightPostion(Eigen::Vector3f &pos); 67 | void SetRendering(bool isRendering); 68 | void SetSliceOutline(bool isDrawSliceOutline); 69 | 70 | 71 | void FillTexture(); // generate texture from smoke density 72 | void Render(); // draw the volume 73 | // draw the outline of the cube 74 | void DrawCube(); 75 | void DrawLight(); 76 | void DrawVolumeData(); 77 | 78 | }; 79 | 80 | 81 | #endif 82 | -------------------------------------------------------------------------------- /camera.cpp: -------------------------------------------------------------------------------- 1 | #include "core.h" 2 | #include "camera.h" 3 | 4 | Camera::Camera(GLFWwindow* windowHandle) { 5 | #ifdef DEBUG_LEVEL 6 | std::cout << __FILE__ << " " << __FUNCTION__ << std::endl; 7 | #endif 8 | _windowHandle = windowHandle; 9 | 10 | //SetLight(); 11 | Reset(); 12 | } 13 | 14 | void Camera::SetLight() 15 | { 16 | //set light 17 | static const GLfloat light_position[] = {2.0f, 1.0f, 10.0f, 1.0f}; 18 | static const GLfloat light_ambient[] = {0.2f, 0.2f, 0.2f, 1.0f}; 19 | static const GLfloat light_diffuse[] = {1.0f, 1.0f, 0.0f, 1.0f}; 20 | static const GLfloat light_specular[] = {1.0f, 0.0f, 1.0f, 1.0f}; 21 | 22 | glLightfv(GL_LIGHT0, GL_POSITION, light_position); 23 | glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient); 24 | glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse); 25 | glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular); 26 | 27 | 28 | /* 29 | const static GLfloat mat_diffuse[] = {1.0f, 0.0f, 0.0f, 1.0f}; 30 | const static GLfloat green_color[] = {0.0f, 1.0f, 0.0f, 0.3333f}; 31 | const static GLfloat blue_color[] = {0.0f, 0.0f, 1.0f, 0.5f}; 32 | 33 | 34 | GLfloat mat_shininess = 30.0; 35 | static const GLfloat mat_specular[] = {0.0f, 1.0f, 0.0f, 1.0f}; 36 | static const GLfloat mat_emission[] = {0.0f, 0.0f, 0.0f, 1.0f}; 37 | 38 | glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, mat_diffuse); 39 | glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular); 40 | glMaterialfv(GL_FRONT, GL_EMISSION, mat_emission); 41 | glMaterialf (GL_FRONT, GL_SHININESS, mat_shininess); 42 | */ 43 | 44 | 45 | glEnable(GL_LIGHTING); 46 | glEnable(GL_LIGHT0); 47 | } 48 | 49 | void Camera::RegisterParentWindow(GLFWwindow* windowHandle) 50 | { 51 | _windowHandle = windowHandle; 52 | } 53 | 54 | void Camera::Reset() 55 | { 56 | 57 | glfwGetWindowSize(_windowHandle, &_winX, &_winY); 58 | _FOV = 60.0f; 59 | _aspect = (GLfloat)_winX / _winY; 60 | _nearClip = 0.1f; 61 | _farClip = 100.0f; 62 | 63 | #if 1 64 | glEnable(GL_DEPTH_TEST); 65 | #else 66 | glDisable(GL_DEPTH_TEST); 67 | #endif 68 | glMatrixMode(GL_PROJECTION); 69 | glLoadIdentity(); 70 | 71 | // Set perspective projection 72 | gluPerspective(_FOV, _aspect, _nearClip, _farClip); 73 | 74 | glViewport(0, 0, _winX, _winY); 75 | 76 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 77 | 78 | } 79 | 80 | void Camera::SetAspect(GLfloat aspect) 81 | { 82 | _aspect = aspect; 83 | } 84 | -------------------------------------------------------------------------------- /object.h: -------------------------------------------------------------------------------- 1 | #ifndef DRAWER_H 2 | #define DRAWER_H 3 | 4 | #include "core.h" 5 | #include "arcball.h" 6 | 7 | //////////////////////////////////////////////////////////////////////////////// 8 | 9 | class Object: public EventListener{ 10 | public: 11 | Object(); 12 | ~Object(); 13 | 14 | void RegisterShader(const char* vertexProgName, GLenum shaderType); 15 | int LoadShaderFile(const char* fileName, GLuint shader); 16 | void RebindShader(GLuint hVertexShader, GLuint hfragShader); 17 | void CreateShaderProgram(); 18 | void EnableShader(); 19 | 20 | void rcSetUinforms(); 21 | 22 | 23 | void ComputeNormal(GLfloat v[3][3], GLfloat normal[]); 24 | 25 | void Cube(); 26 | 27 | virtual void SimulateStep() {}; 28 | virtual void Show(); 29 | virtual bool LoadFile(char* fileName); 30 | virtual void ComputeVertexNormal() {} 31 | virtual void Reset(); 32 | 33 | 34 | virtual void MouseButton(GLFWwindow *window, int button,int action,int mods); 35 | virtual void MouseMotion(GLFWwindow *window, double nx, double ny); 36 | virtual void MouseScroll(GLFWwindow *window, double nx, double ny); 37 | virtual void Keyboard(GLFWwindow * window, int key, int scancode, int action, int mods) {} 38 | virtual void Resize(GLFWwindow *window, int x, int y); 39 | 40 | void RegisterParentWindow(GLFWwindow* windowHandle); 41 | 42 | //for rendering 43 | GLuint initTFF1DTex(const char* filename); 44 | GLuint initFace2DTex(GLuint bfTexWidth, GLuint bfTexHeight); 45 | GLuint initVol3DTex(const char* filename, GLuint w, GLuint h, GLuint d); 46 | void checkFramebufferStatus(); 47 | void initFrameBuffer(GLuint texObj, GLuint texWidth, GLuint texHeight); 48 | void render(GLenum cullFace); 49 | 50 | protected: 51 | std::fstream _file; 52 | 53 | GLfloat _rotX; 54 | GLfloat _rotY; 55 | GLfloat _depth; 56 | 57 | //for event handling 58 | bool _isLeftKeyPressed, _isCtrlPressed, _isRightKeyPressed, _isMiddleKeyPressed; 59 | 60 | Arcball _arcball; 61 | 62 | GLFWwindow* _windowHandle; 63 | int _winX, _winY; 64 | 65 | GLuint _shaderProg; //shader 66 | GLuint _hShaders[10]; //support upto 10 shaders; 67 | int _shaderNum; //current number of shaders 68 | 69 | //for rendering 70 | float _stepSize; 71 | GLuint _vao; 72 | GLuint _frameBuffer; 73 | // transfer function 74 | GLuint _tffTexObj; 75 | GLuint _bfTexObj; 76 | GLuint _volTexObj; 77 | 78 | }; 79 | 80 | //////////////////////////////////////////////////////////////////////////////// 81 | 82 | #endif 83 | -------------------------------------------------------------------------------- /fluid.h: -------------------------------------------------------------------------------- 1 | #ifndef _FLUID_H 2 | #define _FLUID_H 3 | 4 | #include "core.h" 5 | #include "object.h" 6 | #include "renderer.h" 7 | 8 | #define DT 0.1 // time step 9 | #define RES 40 // box resolution 10 | #define N ((RES)-2) // valid simulation area 11 | #define SIZE ((RES)*(RES)*(RES)) 12 | 13 | #undef _I 14 | #define _I(x,y,z) (((x)*(RES)*(RES))+((y)*(RES))+(z)) //FIXME 15 | 16 | #undef FOR_ALL_CELL 17 | #define FOR_ALL_CELL for (int i=1; i<=(N); i++) {\ 18 | for (int j=1; j<=(N); j++) {\ 19 | for (int k=1; k<=(N); k++) { 20 | 21 | #undef END_FOR 22 | #define END_FOR }}} 23 | 24 | #define ALMOST_EQUAL(a, b) ((fabs(a-b)<0.00001f)?true:false) 25 | 26 | class Renderer; 27 | 28 | class Fluid: public Object 29 | { 30 | protected: 31 | float _buffers[10][SIZE]; 32 | float *_density, *_densityTmp; // density 33 | float *_velX, *_velXTmp; // velocity in x direction 34 | float *_velY, *_velYTmp; // velocity in y direction 35 | float *_velZ, *_velZTmp; // velocity in z direction 36 | float _dt; 37 | 38 | 39 | //for rendering 40 | Renderer * _renderer; //register a renderer 41 | Eigen::Vector3f _lightPos; 42 | bool _isLightSelected; 43 | bool _isRendering; 44 | bool _isDrawSliceOutline; 45 | 46 | protected: 47 | // simulation methods 48 | // beware: i changed stam's implementation from a destiation, source ordering 49 | // of the parameters to source, destiation 50 | void AddSource(float* src, float *dst); 51 | void AddBuoyancy(); 52 | void EnforceBoundary(int b, float* quantity); 53 | void Diffuse(int b, float* velTmp, float* vel, float diff); 54 | void Advect(int b, float* quantityTmp, float* quantity, float* velX, float* velY, float* velZ); 55 | void Project(); 56 | void VorticityConfinement(); 57 | 58 | void VelocityStep(); 59 | void DensityStep(); 60 | 61 | // utility methods 62 | void ClearBuffer(float* buf); 63 | void ClearSources(void); 64 | 65 | void GenerateSmoke(); 66 | 67 | virtual void MouseButton(GLFWwindow *window, int button,int action,int mods); 68 | virtual void MouseMotion(GLFWwindow *window, double nx, double ny); 69 | virtual void Keyboard(GLFWwindow * window, int key, int scancode, int action, int mods); 70 | bool LightSelected(double mouseX, double mouseY); 71 | 72 | public: 73 | float sd[SIZE], su[SIZE], sv[SIZE], sw[SIZE], sT[SIZE]; // sources for density and velocities 74 | float diffusion, viscosity, buoyancy, vc_eps; 75 | 76 | Fluid(void); 77 | ~Fluid(void); 78 | 79 | virtual void SimulateStep(); 80 | virtual void Show(); 81 | const float* GetDensity(); 82 | }; 83 | 84 | #endif 85 | 86 | -------------------------------------------------------------------------------- /arcball.h: -------------------------------------------------------------------------------- 1 | #ifndef _ARCBALL_H 2 | #define _ARCBALL_H 3 | 4 | /* 5 | * \class Arcball 6 | * \ingroup GLVisualization 7 | * \brief Arcball is a method to manipulate and rotate objects in 3D intuitively. 8 | * 9 | * The motivation behind the trackball (aka arcball) is to provide an intuitive user interface for complex 3D 10 | * object rotation via a simple, virtual sphere - the screen space analogy to the familiar input device bearing 11 | * the same name. 12 | * 13 | * The sphere is a good choice for a virtual trackball because it makes a good enclosure for most any object; 14 | * and its surface is smooth and continuous, which is important in the generation of smooth rotations in response 15 | * to smooth mouse movements. 16 | * Any smooth, continuous shape, however, could be used, so long as points on its surface can be generated 17 | * in a consistent way. 18 | * The algorithm for accomplishing this, needs to perform the following steps (not neseccarily in order). 19 | * 20 | * 21 | * 22 | * 23 | * \b ArcBall Algorithm made easy 24 | * - Detect the left-button of the mouse being depressed. 25 | * - Keep track of the last known mouse position. 26 | * - Treat the mouse position as the projection of a point on the hemi-sphere down to the image plane (along the z-axis), and determine that point on the hemi-sphere. 27 | * - Detect the mouse movement 28 | * - Determine the great circle connecting the old mouse-hemi-sphere point to the current mouse-hemi-sphere point. 29 | * - Calculate the normal to this plane. This will be the axis about which to rotate. 30 | * - Set the OpenGL state to modify the MODELVIEW matrix. 31 | * - Read off the current matrix, since we want this operation to be the last transformation, not the first, and OpenGL does things LIFO. 32 | * - Reset the model-view matrix to the identity 33 | * - Rotate about the axis 34 | * - Multiply the resulting matrix by the saved matrix. 35 | * - Force a redraw of the scene. 36 | * 37 | * An example of the code for using successfully this class is the following: 38 | * First set the radius of the arcball by using the appropriate method when you call the handle to the GLUT resize method (this is usually accomplished with a 39 | * \code 40 | * void handleResize(int w, int h) 41 | * \endcode 42 | * function in GLUT (if you are using Qt or other, please refer to the relative API ). 43 | * An example of a \code handleResize(int w, int h) \endcode function is the following: 44 | * \code 45 | * void handleResize(int w, int h) 46 | * { 47 | * arcball.setWidthHeight(w, h); 48 | * glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 49 | * glViewport(0, 0, w, h); 50 | * 51 | * glMatrixMode(GL_PROJECTION); 52 | * glLoadIdentity(); 53 | * gluPerspective(viewAngle, (float)w / (float)w, zNear, zFar ); 54 | * 55 | * glMatrixMode(GL_MODELVIEW); 56 | * } 57 | * \endcode 58 | * 59 | * 60 | These are the two functions (GLUT) that control the mouse events: 61 | * \code 62 | * void mouseFunc(int state, int button, int _x , int _y) 63 | * { 64 | * if ( button == GLUT_LEFT_BUTTON ) 65 | * arcball.startRotation(_x,_y); 66 | * else 67 | * arcball.stopRotation(); 68 | * 69 | * 70 | * glutPostRedisplay(); 71 | * } 72 | * 73 | * void mouseDrag(int _x, int _y) 74 | * { 75 | * 76 | * arcball.updateRotation(_x,_y); 77 | * glutPostRedisplay(); 78 | * } 79 | * 80 | * \endcode 81 | * 82 | */ 83 | 84 | 85 | 86 | #include "core.h" 87 | 88 | typedef float GLMatrix[16]; 89 | 90 | class Arcball 91 | { 92 | private: 93 | 94 | //window size 95 | int _winX, _winY; 96 | 97 | //for zooming 98 | float _zoomRate; 99 | float _startZoomX, _startZoomY; 100 | 101 | //for dragging (points in 3d scenes) 102 | float _dragRate; 103 | float _startDragX, _startDragY; 104 | 105 | Eigen::Vector3f _viewDir; 106 | Eigen::Vector3f _upDir; 107 | Eigen::Vector3f _rightDir; 108 | 109 | GLMatrix _startMatrix; 110 | Eigen::Vector3d _startRotationVector; 111 | Eigen::Vector3d _currentRotationVector; 112 | 113 | 114 | bool _isZooming; 115 | bool _isRotating; 116 | bool _isDragging; 117 | float _ballRadius; 118 | 119 | //tool functions 120 | Eigen::Vector3d ConvertXY(int x, int y); 121 | void ApplyRotationMatrix(); 122 | public: 123 | Arcball(); 124 | 125 | void SetWidthHeight(int w, int h); 126 | void SetRadius(float newRadius); 127 | 128 | //rotation 129 | void StartRotation(int x, int y); 130 | void UpdateRotation(int x, int y); 131 | void StopRotation(); 132 | 133 | //zooming 134 | void StartZooming(int x, int y); 135 | void UpdateZooming(int x, int y); 136 | void StopZooming(); 137 | 138 | //dragging 139 | void StartDragging(int x, int y); 140 | Eigen::Vector3f UpdateDragging(int x, int y); 141 | void StopDragging(); 142 | 143 | 144 | void Reset(); 145 | }; 146 | 147 | 148 | #endif 149 | -------------------------------------------------------------------------------- /controller.cpp: -------------------------------------------------------------------------------- 1 | #include "core.h" 2 | #include "controller.h" 3 | 4 | extern Controller* g_controller; 5 | 6 | //set callback functions 7 | static void error_callback(int error, const char* description) 8 | { 9 | fputs(description, stderr); 10 | } 11 | 12 | static void resize(GLFWwindow *window, int x,int y) 13 | { 14 | ::g_controller->Resize(window, x, y); 15 | } 16 | 17 | static void keyboard(GLFWwindow * window, int key, int scancode,int action, int mods) 18 | { 19 | ::g_controller->Keyboard(window, key, scancode, action, mods); 20 | } 21 | 22 | static void mousebutton(GLFWwindow *window,int button,int action,int mods) 23 | { 24 | ::g_controller->MouseButton(window, button, action, mods); 25 | } 26 | 27 | static void mousemotion(GLFWwindow *window, double x, double y) 28 | { 29 | ::g_controller->MouseMotion(window, x, y); 30 | } 31 | 32 | static void mousescroll(GLFWwindow *window, double x, double y) 33 | { 34 | ::g_controller->MouseScroll(window, x, y); 35 | } 36 | 37 | void Controller::RegisterObject(Object* object) 38 | { 39 | object->RegisterParentWindow(_windowHandle); 40 | object->Reset(); 41 | _objects[_objectNo++] = object; 42 | _activeObj++; 43 | } 44 | 45 | 46 | Controller::Controller(int argc,char **argv, const char *windowName) 47 | { 48 | _windowName = windowName; 49 | 50 | //for timer 51 | _elapsedTime = 0; 52 | _numOfFrame = 0; 53 | _fps = 0; 54 | 55 | _winX = 1280; 56 | _winY = 960; 57 | 58 | _activeObj = -1; 59 | _objectNo = 0; 60 | _maxObjectNo = 100; 61 | _objects = new Object*[_maxObjectNo]; 62 | 63 | _isCtrlPressed = _isLeftKeyPressed = _isMiddleKeyPressed = _isRightKeyPressed = false; 64 | _isLightOn = true; 65 | _prevCursorX = _prevCursorY = 0; 66 | 67 | // Initialize components 68 | if (!glfwInit()) { 69 | std::cerr << "glfwInit() failed!" << std::endl; 70 | exit(-1); 71 | } 72 | 73 | // Create the window 74 | _windowHandle = glfwCreateWindow(_winX, _winY, windowName, NULL, NULL); 75 | if (!_windowHandle) { 76 | std::cerr << "Create Window failed" << std::endl; 77 | exit(-1); 78 | } 79 | glfwMakeContextCurrent(_windowHandle); 80 | glfwSetWindowPos(_windowHandle, 0, 0); 81 | 82 | // Background color 83 | glClearColor( 0., 0., 0., 1. ); 84 | 85 | // Callbacks 86 | glfwSetErrorCallback(error_callback); 87 | glfwSetMouseButtonCallback(_windowHandle, mousebutton); 88 | glfwSetScrollCallback(_windowHandle, mousescroll); 89 | glfwSetCursorPosCallback(_windowHandle, mousemotion); 90 | glfwSetKeyCallback(_windowHandle, keyboard); 91 | glfwSetWindowSizeCallback(_windowHandle, resize); 92 | 93 | if(glewInit() != GLEW_OK) { 94 | std::cerr << "glewInit() failed!" << std::endl; 95 | exit(-1); 96 | } 97 | 98 | //InitCamera(); 99 | } 100 | 101 | void Controller::InitCamera() 102 | { 103 | _camera = new Camera(_windowHandle); 104 | } 105 | 106 | void Controller::BeginLoop() 107 | { 108 | while (!glfwWindowShouldClose(_windowHandle)) 109 | { 110 | /* Render here */ 111 | ::g_controller->Render(); 112 | 113 | /* Swap front and back buffers */ 114 | glfwSwapBuffers(_windowHandle); 115 | 116 | /* Poll for and process events */ 117 | glfwPollEvents(); 118 | } 119 | } 120 | 121 | ////////////////////////////////////////////////////////////////////////////// 122 | 123 | Controller::~Controller() { 124 | glFinish(); 125 | glfwDestroyWindow(_windowHandle); 126 | glfwTerminate(); 127 | } 128 | 129 | void Controller::Reset() { 130 | #ifdef DEBUG_LEVEL 131 | std::cout << __FILE__ << " " << __FUNCTION__ << std::endl; 132 | #endif 133 | _camera->Reset(); 134 | 135 | for(int i = 0 ; i < _objectNo ; i++) 136 | _objects[i]->Reset(); 137 | } 138 | 139 | void Controller::_ComputeFPS() 140 | { 141 | _numOfFrame++; 142 | _elapsedTime += _timer.StopTimer(); 143 | _timer.StartTimer(); 144 | if(_elapsedTime > 1) { 145 | _fps = _numOfFrame / _elapsedTime; 146 | //std::cout << _fps << " "<< _numOfFrame << std::endl; 147 | _elapsedTime = 0; 148 | _numOfFrame = 0; 149 | } 150 | 151 | //clear stringstream buffer 152 | _titleInfo.str(""); 153 | _titleInfo << _windowName; 154 | _titleInfo << " FPS: "; 155 | _titleInfo << std::setprecision(4) << _fps; 156 | _titleInfo.width(2); 157 | } 158 | 159 | void Controller::Render() { 160 | 161 | //compute fps and display 162 | _ComputeFPS(); 163 | glfwSetWindowTitle(_windowHandle, _titleInfo.str().c_str() ); 164 | 165 | 166 | //Begin drawing scene 167 | _camera->Reset(); 168 | for(int i = 0 ; i < _objectNo ; i++) { 169 | _objects[i]->SimulateStep(); 170 | _objects[i]->Show(); 171 | } 172 | 173 | 174 | //Finish drawing scene 175 | //glFinish(); 176 | } 177 | 178 | int Controller::GetActiveObject(int mx, int my) 179 | { 180 | //FIXME: do some judgement; 181 | return _activeObj; 182 | } 183 | 184 | void Controller::Quit() { 185 | glFinish(); 186 | glfwDestroyWindow(_windowHandle); 187 | exit(0); 188 | } 189 | 190 | 191 | void Controller::Resize(GLFWwindow *window, int x, int y) { 192 | _winX = x; 193 | _winY = y; 194 | 195 | for(int i = 0 ; i < _objectNo ; i++) 196 | _objects[i]->Resize(window, x, y); 197 | } 198 | 199 | void Controller::Keyboard(GLFWwindow * window, int key, int scancode, int action, int mods) 200 | { 201 | if (action == GLFW_PRESS) { 202 | switch(key) { 203 | case GLFW_KEY_ESCAPE: // Escape 204 | Quit(); 205 | break; 206 | case GLFW_KEY_R: //reset 207 | Reset(); 208 | break; 209 | case GLFW_KEY_LEFT_CONTROL: 210 | _isCtrlPressed = true; 211 | break; 212 | } 213 | } 214 | else if(action == GLFW_RELEASE) 215 | switch (key) { 216 | case GLFW_KEY_LEFT_CONTROL: 217 | _isCtrlPressed = false; 218 | break; 219 | } 220 | 221 | //route message to active object 222 | _objects[_activeObj]->Keyboard(window, key, scancode, action, mods); 223 | } 224 | 225 | 226 | void Controller::MouseButton(GLFWwindow *window, int button,int action,int mods) 227 | { 228 | //get active object and then transfer the message to the object 229 | if(action == GLFW_PRESS) { 230 | glfwGetCursorPos(window, &_prevCursorX, &_prevCursorY); 231 | } 232 | _activeObj = GetActiveObject(_prevCursorX, _prevCursorY); 233 | _objects[_activeObj]->MouseButton(window, button, action, mods); 234 | } 235 | 236 | 237 | void Controller::MouseMotion(GLFWwindow *window, double nx, double ny) 238 | { 239 | _objects[_activeObj]->MouseMotion(window, nx, ny); 240 | } 241 | 242 | void Controller::MouseScroll(GLFWwindow *window, double nx, double ny) 243 | { 244 | _objects[_activeObj]->MouseScroll(window, nx, ny); 245 | } 246 | -------------------------------------------------------------------------------- /arcball.cpp: -------------------------------------------------------------------------------- 1 | #include "core.h" 2 | #include "arcball.h" 3 | 4 | /** 5 | * \ingroup GLVisualization 6 | * Default constructor, it sets the _ballRadius to 600 7 | **/ 8 | Arcball::Arcball() 9 | { 10 | _ballRadius = 600; 11 | _isRotating = false; 12 | _isDragging = false; 13 | _zoomRate = 0.3; 14 | _winX = _winY = 0; 15 | Reset(); 16 | } 17 | 18 | /** 19 | * \ingroup GLVisualization 20 | * Set _winX and _winY of the current windows, it's needed every time you resize the window 21 | * \param w _winX of the rendering window 22 | * \param h _winY of the rendering window 23 | **/ 24 | void Arcball::SetWidthHeight(int w, int h) 25 | { 26 | _winX = w; 27 | _winY = h; 28 | _ballRadius = std::min((int)(w/2), (int)(h/2)); 29 | } 30 | 31 | /** 32 | * \ingroup GLVisualization 33 | * Set the radius of the ball (a typical radius for a 1024x768 window is 600 34 | * \param newRadius The radius of the spherical dragging area 35 | **/ 36 | void Arcball::SetRadius(float newRadius) 37 | { 38 | _ballRadius = newRadius; 39 | } 40 | 41 | void Arcball::StartDragging(int x, int y) 42 | { 43 | _startDragX = x; 44 | _startDragY = y; 45 | _isDragging = true; 46 | _upDir = Eigen::Vector3f(0.0, 1.0, 0.0); 47 | 48 | GLdouble mvMatrix[16]; 49 | glGetDoublev(GL_MODELVIEW_MATRIX, mvMatrix); 50 | _viewDir[0] = -mvMatrix[2]; 51 | _viewDir[1] = -mvMatrix[6]; 52 | _viewDir[2] = -mvMatrix[10]; 53 | #if 0 54 | _upDir[0] = mvMatrix[1]; 55 | _upDir[1] = mvMatrix[5]; 56 | _upDir[2] = mvMatrix[9]; 57 | _rightDir[0] = mvMatrix[0]; 58 | _rightDir[1] = mvMatrix[4]; 59 | _rightDir[2] = mvMatrix[8]; 60 | #else 61 | _upDir = Eigen::Vector3f(0.0, 1.0, 0.0); 62 | _rightDir = _viewDir.cross(_upDir); 63 | #endif 64 | } 65 | 66 | Eigen::Vector3f Arcball::UpdateDragging(int nx, int ny) 67 | { 68 | _dragRate = 0.003 * _winX/1024; 69 | Eigen::Vector3f offset; 70 | #if 1 71 | offset[0] = _dragRate * _rightDir[0] * (nx - _startDragX); 72 | offset[1] = _dragRate * _upDir[1] * (_startDragY - ny); 73 | offset[2] = _dragRate * _rightDir[2]*(nx-_startDragX) + _upDir[2]* (_startDragY-ny); 74 | #else 75 | _viewDir.normalize(); 76 | offset[0] = _dragRate * _viewDir[0] * (nx - _startDragX); 77 | offset[1] = _dragRate * _rightDir[0] * (_startDragY - ny); 78 | offset[2] = _dragRate * _upDir[0]*(nx-_startDragX) + _upDir[0]* (_startDragY-ny); 79 | #endif 80 | 81 | _startDragX = nx; 82 | _startDragY = ny; 83 | return offset; 84 | } 85 | 86 | void Arcball::StopDragging() 87 | { 88 | _isDragging = false; 89 | } 90 | 91 | void Arcball::StartZooming(int x, int y) 92 | { 93 | //store original transform matrix 94 | glGetFloatv(GL_MODELVIEW_MATRIX, _startMatrix); 95 | 96 | _startZoomX = x; 97 | _startZoomY = y; 98 | _isZooming = true; 99 | } 100 | 101 | void Arcball::UpdateZooming(int x, int y) 102 | { 103 | if (_isZooming) { 104 | glMatrixMode(GL_MODELVIEW); 105 | glLoadIdentity(); 106 | glTranslatef(0.0f, 0.0f, _zoomRate * (x - _startZoomX) ); 107 | } 108 | ApplyRotationMatrix(); 109 | } 110 | 111 | void Arcball::StopZooming() 112 | { 113 | _isZooming = false; 114 | } 115 | 116 | /** 117 | * \ingroup GLVisualization 118 | * Start the rotation. Use this method in association with the left click. 119 | * Here you must give directly the coordinates of the mouse as the glut functions extract. This method supposes that the 0,0 is in the upper-left part of the screen 120 | * \param _x Horizontal position of the mouse (0,0) = upperleft corner (w,h) = lower right 121 | * \param _y Vertical position of the mouse (0,0) = upperleft corner (w,h) = lower right 122 | * 123 | **/ 124 | void Arcball::StartRotation(int _x, int _y) 125 | { 126 | int x = ( (_x)-(_winX/2) ); 127 | int y = ((_winY/2)-_y); 128 | 129 | //store original transform matrix 130 | glGetFloatv(GL_MODELVIEW_MATRIX, _startMatrix); 131 | 132 | _startRotationVector = ConvertXY(x,y); 133 | _startRotationVector.normalize(); 134 | 135 | _currentRotationVector= _startRotationVector; 136 | _isRotating = true; 137 | 138 | } 139 | 140 | /** 141 | * \ingroup GLVisualization 142 | * Update the rotation. Use this method in association with the drag event. 143 | * Here you must give directly the coordinates of the mouse as the glut functions extract. This method supposes that the 0,0 is in the upper-left part of the screen 144 | * \param _x Horizontal position of the mouse (0,0) = upperleft corner (w,h) = lower right 145 | * \param _y Vertical position of the mouse (0,0) = upperleft corner (w,h) = lower right 146 | **/ 147 | void Arcball::UpdateRotation(int _x, int _y) 148 | { 149 | int x = ( (_x)-(_winX/2) ); 150 | int y = ((_winY/2)-_y); 151 | 152 | _currentRotationVector = ConvertXY(x,y); 153 | 154 | _currentRotationVector.normalize(); 155 | 156 | //Fixed by MY 157 | glMatrixMode(GL_MODELVIEW); 158 | glLoadIdentity(); 159 | ApplyRotationMatrix(); 160 | } 161 | 162 | /** 163 | * \ingroup GLVisualization 164 | * Apply the computed rotation matrix 165 | * This method must be invoked inside the \code glutDisplayFunc() \endcode 166 | * 167 | **/ 168 | void Arcball::ApplyRotationMatrix() 169 | { 170 | //recover original matrix 171 | glMultMatrixf(_startMatrix); 172 | 173 | if (_isRotating) { 174 | // Do some rotation according to start and current rotation vectors 175 | //cerr << _currentRotationVector.transpose() << " " << _startRotationVector.transpose() << endl; 176 | if ( ( _currentRotationVector - _startRotationVector).norm() > 1E-6 ) { 177 | Eigen::Vector3d rotationAxis = _currentRotationVector.cross(_startRotationVector); 178 | rotationAxis.normalize(); 179 | 180 | //FIXED by MY 181 | Eigen::Matrix3d sm; 182 | for(int i = 0 ; i < 3 ; i++) 183 | for(int j = 0 ; j < 3 ; j++) 184 | sm(i,j) = (double)_startMatrix[4*i+j]; 185 | rotationAxis = sm * rotationAxis; 186 | 187 | double val = _currentRotationVector.dot(_startRotationVector); 188 | val > (1-1E-10) ? val=1.0 : val=val ; 189 | double rotationAngle = acos(val) * 180.0f/(float)M_PI; 190 | 191 | // rotate around the current position 192 | glRotatef(rotationAngle * 2, -rotationAxis.x(), -rotationAxis.y(),-rotationAxis.z()); 193 | } 194 | } 195 | } 196 | 197 | /** 198 | * \ingroup GLVisualization 199 | * Stop the current rotation and prepare for a new click-then-drag event 200 | * 201 | **/ 202 | void Arcball::StopRotation() 203 | { 204 | glMatrixMode(GL_MODELVIEW); 205 | glLoadIdentity(); 206 | ApplyRotationMatrix(); 207 | _isRotating = false; 208 | } 209 | 210 | 211 | Eigen::Vector3d Arcball::ConvertXY(int x, int y) 212 | { 213 | 214 | int d = x*x+y*y; 215 | float radiusSquared = _ballRadius * _ballRadius; 216 | if (d > radiusSquared) { 217 | return Eigen::Vector3d((float)x,(float)y, 0 ); 218 | } 219 | else { 220 | return Eigen::Vector3d((float)x,(float)y, sqrt(radiusSquared - d)); 221 | } 222 | } 223 | 224 | /** 225 | * \ingroup GLVisualization 226 | * Reset the current transformation to the identity 227 | **/ 228 | void Arcball::Reset() 229 | { 230 | // reset matrix 231 | memset(_startMatrix, 0, sizeof(_startMatrix)); 232 | _startMatrix[0] = 1; 233 | _startMatrix[1] =0; 234 | _startMatrix[2] = 0; 235 | _startMatrix[3] = 0; 236 | _startMatrix[4] = 0; 237 | _startMatrix[5] =1; 238 | _startMatrix[6] = 0; 239 | _startMatrix[7] = 0; 240 | _startMatrix[8] = 0; 241 | _startMatrix[9] =0; 242 | _startMatrix[10] = 1; 243 | _startMatrix[11] = 0; 244 | _startMatrix[12] = 0; 245 | _startMatrix[13] =0; 246 | _startMatrix[14] = 0; 247 | _startMatrix[15] = 1; 248 | 249 | _startZoomX = _startZoomY = 0; 250 | } 251 | 252 | -------------------------------------------------------------------------------- /object.cpp: -------------------------------------------------------------------------------- 1 | #include "core.h" 2 | #include "object.h" 3 | 4 | Object::Object() 5 | { 6 | #ifdef DEBUG_LEVEL 7 | std::cout << __FILE__ << " " << __FUNCTION__ << std::endl; 8 | #endif 9 | //Reset(); 10 | _shaderProg = 0; 11 | memset(_hShaders, 0, sizeof(_hShaders) ); 12 | _shaderNum = 0; 13 | 14 | //for rendering 15 | _stepSize = 0.001f; 16 | } 17 | 18 | Object::~Object() 19 | { 20 | } 21 | 22 | 23 | int Object::LoadShaderFile(const char* fileName, GLuint shader) 24 | { 25 | std::fstream file; 26 | GLint compiled; 27 | int size = 0; 28 | char* buffer = NULL; 29 | 30 | file.open(fileName, std::ios::in | std::ios::binary); 31 | if(!file) { 32 | std::cerr << "Open file " << fileName << " failed!" << std::endl; 33 | return -1; 34 | } 35 | 36 | //get file size and allocate memory 37 | file.seekg(0, std::ios::end); 38 | size = file.tellg(); 39 | file.seekg(0, std::ios::beg); 40 | buffer = new char[size]; 41 | 42 | file.read(buffer, size); 43 | //load to shader 44 | glShaderSource(shader, 1, (const GLchar **)&buffer, &size); 45 | 46 | delete []buffer; 47 | file.close(); 48 | 49 | return 0; 50 | } 51 | 52 | void Object::EnableShader() 53 | { 54 | CreateShaderProgram(); 55 | } 56 | 57 | void Object::RebindShader(GLuint hVertexShader, GLuint hfragShader) 58 | { 59 | 60 | GLint testVal; 61 | GLsizei maxCount = 2; 62 | GLsizei count; 63 | GLuint shaders[maxCount]; 64 | 65 | //get exists shaders 66 | glGetAttachedShaders(_shaderProg, maxCount, &count, shaders); 67 | //and detach then 68 | for (int i = 0; i < count; i++) { 69 | glDetachShader(_shaderProg, shaders[i]); 70 | } 71 | 72 | // Bind index 0 to the shader input variable "VerPos" 73 | glBindAttribLocation(_shaderProg, 0, "VerPos"); 74 | // Bind index 1 to the shader input variable "VerClr" 75 | glBindAttribLocation(_shaderProg, 1, "VerClr"); 76 | 77 | glAttachShader(_shaderProg, hVertexShader); 78 | glAttachShader(_shaderProg, hfragShader); 79 | 80 | // Attempt to link 81 | glLinkProgram(_shaderProg); 82 | 83 | // Make sure link worked too 84 | glGetProgramiv(_shaderProg, GL_LINK_STATUS, &testVal); 85 | if(testVal == GL_FALSE) { 86 | char infoLog[1024]; 87 | glGetProgramInfoLog(_shaderProg, 1024, NULL, infoLog); 88 | std::cerr << "The program " << _shaderProg 89 | << " failed to link with the following error:" << std::endl 90 | << infoLog << std::endl; 91 | glDeleteProgram(_shaderProg); 92 | exit(-1); 93 | } 94 | 95 | glUseProgram(_shaderProg); 96 | } 97 | 98 | void Object::CreateShaderProgram() 99 | { 100 | GLint testVal; 101 | GLuint hReturn; 102 | 103 | // Create the final program object, and attach the shaders 104 | hReturn = glCreateProgram(); 105 | 106 | // All done, return our ready to use shader program 107 | _shaderProg = hReturn; 108 | } 109 | 110 | 111 | void Object::RegisterShader(const char* progName, GLenum shaderType) 112 | { 113 | // Temporary Shader objects 114 | GLuint hShader; 115 | GLuint hReturn = 0; 116 | GLint testVal; 117 | 118 | // Create shader objects 119 | hShader = glCreateShader(shaderType); 120 | 121 | // Load, If fail clean up and return null 122 | if(LoadShaderFile(progName, hShader) < 0) { 123 | glDeleteShader(hShader); 124 | std::cerr << "The shader at " << progName 125 | << " could not be found." << std::endl; 126 | exit(-1); 127 | } 128 | 129 | // Compile 130 | glCompileShader(hShader); 131 | // Check for errors in shader 132 | glGetShaderiv(hShader, GL_COMPILE_STATUS, &testVal); 133 | if(testVal == GL_FALSE) { 134 | char infoLog[1024]; 135 | glGetShaderInfoLog(hShader, 1024, NULL, infoLog); 136 | std::cerr << "The shader at " << progName 137 | << " failed to compile with the following error:" << std::endl 138 | << infoLog << std::endl; 139 | glDeleteShader(hShader); 140 | exit(-1); 141 | } 142 | // Check for errors in shader 143 | glGetShaderiv(hShader, GL_COMPILE_STATUS, &testVal); 144 | if(testVal == GL_FALSE) { 145 | char infoLog[1024]; 146 | glGetShaderInfoLog(hShader, 1024, NULL, infoLog); 147 | std::cerr << "The shader at " << progName 148 | << " failed to compile with the following error:" << std::endl 149 | << infoLog << std::endl; 150 | glDeleteShader(hShader); 151 | exit(-1); 152 | } 153 | 154 | _hShaders[_shaderNum++] = hShader; 155 | 156 | 157 | #if 0 158 | // Now, we need to bind the attribute names to their specific locations 159 | // List of attributes 160 | va_list attributeList; 161 | va_start(attributeList, fragmentProgName); 162 | // Iterate over this argument list 163 | char *szNextArg; 164 | int iArgCount = va_arg(attributeList, int); 165 | // Number of attributes 166 | for(int i = 0; i < iArgCount; i++) 167 | { 168 | int index = va_arg(attributeList, int); 169 | szNextArg = va_arg(attributeList, char*); 170 | glBindAttribLocation(hReturn, index, szNextArg); 171 | } 172 | va_end(attributeList); 173 | #endif 174 | } 175 | 176 | void Object::RegisterParentWindow(GLFWwindow* windowHandle) 177 | { 178 | _windowHandle = windowHandle; 179 | } 180 | 181 | void Object::Resize(GLFWwindow* windowHandle, int x, int y) 182 | { 183 | _arcball.SetWidthHeight(x, y); 184 | } 185 | 186 | void Object::MouseButton(GLFWwindow *window, int button,int action,int mods) 187 | { 188 | double mouseX, mouseY; 189 | 190 | if(button == GLFW_MOUSE_BUTTON_LEFT) { 191 | ::glfwGetCursorPos(window, &mouseX, &mouseY); 192 | if(action == GLFW_PRESS) { 193 | _isLeftKeyPressed = true; 194 | _arcball.StartRotation(mouseX, mouseY); 195 | } 196 | else if(action == GLFW_RELEASE) { 197 | _isLeftKeyPressed = false; 198 | _arcball.StopRotation(); 199 | } 200 | } 201 | else if(button == GLFW_MOUSE_BUTTON_MIDDLE) { 202 | _isMiddleKeyPressed = (action == GLFW_PRESS); 203 | } 204 | else if(button == GLFW_MOUSE_BUTTON_RIGHT) { 205 | #if 0 206 | if (action == GLFW_PRESS) { 207 | _isRightKeyPressed = true; 208 | ::glfwGetCursorPos(window, &mouseX, &mouseY); 209 | _arcball.StartZooming(mouseX, mouseY); 210 | } 211 | else if(action ==GLFW_RELEASE) { 212 | _isRightKeyPressed = false; 213 | _arcball.StopZooming(); 214 | } 215 | #endif 216 | } 217 | } 218 | 219 | void Object::MouseMotion(GLFWwindow *window, double nx, double ny) 220 | { 221 | if(_isLeftKeyPressed && _isCtrlPressed) { 222 | } 223 | else if(_isLeftKeyPressed) { 224 | _arcball.UpdateRotation(nx, ny); 225 | } 226 | #if 0 227 | else if(_isRightKeyPressed) { 228 | _arcball.UpdateZooming(nx, ny); 229 | } 230 | #endif 231 | } 232 | 233 | void Object::MouseScroll(GLFWwindow *window, double nx, double ny) 234 | { 235 | _arcball.StartZooming(0, 0); 236 | _arcball.UpdateZooming(-ny, nx); 237 | _arcball.StopZooming(); 238 | } 239 | 240 | void Object::ComputeNormal(GLfloat v[3][3], GLfloat normal[]) 241 | { 242 | float v1[3],v2[3]; 243 | static const int x = 0; 244 | static const int y = 1; 245 | static const int z = 2; 246 | 247 | // Calculate two vectors from the three points 248 | v1[x] = v[0][x] - v[1][x]; 249 | v1[y] = v[0][y] - v[1][y]; 250 | v1[z] = v[0][z] - v[1][z]; 251 | 252 | v2[x] = v[1][x] - v[2][x]; 253 | v2[y] = v[1][y] - v[2][y]; 254 | v2[z] = v[1][z] - v[2][z]; 255 | 256 | // Take the cross product of the two vectors to get 257 | // the normal vector which will be stored in out 258 | normal[x] = v1[y]*v2[z] - v1[z]*v2[y]; 259 | normal[y] = v1[z]*v2[x] - v1[x]*v2[z]; 260 | normal[z] = v1[x]*v2[y] - v1[y]*v2[x]; 261 | 262 | // Normalize the vector (shorten length to one) 263 | } 264 | 265 | void Object::Reset() { 266 | #ifdef DEBUG_LEVEL 267 | std::cout << __FILE__ << " " << __FUNCTION__ << std::endl; 268 | #endif 269 | int width, height; 270 | glfwGetWindowSize(_windowHandle, &width, &height); 271 | _winX = width; 272 | _winY = height; 273 | _arcball.SetWidthHeight(width, height); 274 | 275 | #if 1 276 | _depth = 3.8; 277 | _rotX = 10; 278 | _rotY = -20; 279 | 280 | 281 | glMatrixMode(GL_MODELVIEW); 282 | glLoadIdentity(); 283 | 284 | glTranslatef(0, 0, -_depth); 285 | glRotatef(_rotX, 1, 0, 0); 286 | glRotatef(_rotY, 0, 1, 0); 287 | #endif 288 | } 289 | 290 | bool Object::LoadFile(char* fileName) 291 | { 292 | _file.open(fileName); 293 | if (!_file) { 294 | std::cout << "Open file: " << fileName << " failed!" << std::endl; 295 | return false; 296 | } 297 | std::cout << "Open file: " << fileName << " succeed!" << std::endl; 298 | 299 | return true; 300 | } 301 | 302 | //this is a test function, just draw a cube 303 | void Object::Cube() { 304 | glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); 305 | GLfloat vertices[][3] = { { -1.0f, -1.0f, -1.0f }, 306 | { 1.0f, -1.0f, -1.0f}, { 1.0f, 1.0f, -1.0f }, 307 | { -1.0f, 1.0f, -1.0f }, { -1.0f, -1.0f, 1.0f }, 308 | { 1.0f, -1.0f, 1.0f }, { 1.0f, 1.0f, 1.0f }, 309 | { -1.0f, 1.0f, 1.0f } }; // Vertices 310 | int faces[][4] = { { 1, 2, 6, 5 }, { 2, 3, 7, 6 }, { 4, 5, 6, 7 }, 311 | { 0, 4, 7, 3 }, { 0, 1, 5, 4 }, { 0, 3, 2, 1 } }; 312 | 313 | GLfloat colors[][3] = { { 0.0f, 1.0f, 1.0f }, { 1.0f, 0.0f, 1.0f }, 314 | { 1.0f, 1.0f, 0.0f }, { 0.0f, 0.5f, 0.5f }, 315 | { 0.5f, 0.0f, 0.5f }, { 0.5f, 0.5f, 0.0f } }; 316 | 317 | //glBegin(GL_QUADS); 318 | for (int i = 0; i < 6; i++) { 319 | //glColor3fv(colors[i]); 320 | 321 | Eigen::Vector3f nm; 322 | Eigen::Vector3f v0(vertices[faces[i][0]][0], vertices[faces[i][0]][1], vertices[faces[i][0]][2]); 323 | Eigen::Vector3f v1(vertices[faces[i][1]][0], vertices[faces[i][1]][1], vertices[faces[i][1]][2]); 324 | Eigen::Vector3f v2(vertices[faces[i][2]][0], vertices[faces[i][2]][1], vertices[faces[i][2]][2]); 325 | Eigen::Vector3f l1 = v1-v0; 326 | Eigen::Vector3f l2 = v2-v0; 327 | nm = l1.cross(l2); 328 | nm.normalize(); 329 | 330 | glNormal3f(nm(0),nm(1),nm(2)); 331 | glBegin(GL_LINE_STRIP); 332 | for (int j = 0; j < 4; j++) 333 | glVertex3fv(vertices[faces[i][j]]); 334 | glEnd(); 335 | } 336 | //glEnd(); 337 | } 338 | 339 | void Object::Show() 340 | { 341 | Cube(); 342 | } 343 | -------------------------------------------------------------------------------- /fluid.cpp: -------------------------------------------------------------------------------- 1 | #include "fluid.h" 2 | 3 | Fluid::Fluid() 4 | { 5 | diffusion = 0.00001f; 6 | viscosity = 0.000000f; 7 | buoyancy = 4.0f; 8 | vc_eps = 5.0f; 9 | _dt = DT; 10 | _isLightSelected = false; 11 | _isRendering = true; 12 | _isDrawSliceOutline = false; 13 | 14 | for (int i=0; i<10; i++) 15 | ClearBuffer(_buffers[i]); 16 | 17 | int i=0; 18 | _density=_buffers[i++]; _densityTmp=_buffers[i++]; 19 | _velX=_buffers[i++]; _velXTmp=_buffers[i++]; 20 | _velY=_buffers[i++]; _velYTmp=_buffers[i++]; 21 | _velZ=_buffers[i++]; _velZTmp=_buffers[i++]; 22 | 23 | ClearSources(); 24 | 25 | _lightPos[0] = -1.2f; 26 | _lightPos[1] = 0.2f; 27 | _lightPos[2] = 1.2f; 28 | _renderer = new Renderer(_density, RES); 29 | _renderer->SetLightPostion(_lightPos); 30 | } 31 | 32 | const float* Fluid::GetDensity() 33 | { 34 | return _density; 35 | } 36 | 37 | bool Fluid::LightSelected(double mouseX, double mouseY) 38 | { 39 | GLdouble mvMatrix[16], projMatrix[16]; 40 | GLint viewportMatrix[4]; 41 | double objX = 0, objY = 0, objZ = 0; 42 | glGetDoublev(GL_MODELVIEW_MATRIX, mvMatrix); 43 | glGetDoublev(GL_PROJECTION_MATRIX, projMatrix); 44 | glGetIntegerv(GL_VIEWPORT, viewportMatrix); 45 | 46 | //point on 'near' plane 47 | gluUnProject(mouseX, _winY-mouseY, 0.0, mvMatrix, projMatrix, 48 | viewportMatrix, &objX, &objY, &objZ); 49 | Eigen::Vector3f ptNear(objX,objY, objZ); 50 | 51 | //point on 'far' plane 52 | gluUnProject(mouseX, _winY-mouseY, 1.0, mvMatrix, projMatrix, 53 | viewportMatrix, &objX, &objY, &objZ); 54 | Eigen::Vector3f ptFar(objX,objY, objZ); 55 | 56 | #if 1 57 | //calculate distance between point and line 58 | Eigen::Vector3f crossProduct = (_lightPos-ptNear).cross(_lightPos-ptFar); 59 | float dist = crossProduct.norm() / 60 | (ptFar-ptNear).norm(); 61 | #else 62 | //another method: calculate distance between point and line 63 | Eigen::Vector3f lineDir = (ptFar-ptNear); 64 | lineDir.normalize(); 65 | Eigen::Vector3f pointDir = (_lightPos-ptNear); 66 | float t = pointDir.dot(lineDir); 67 | Eigen::Vector3f projection = ptNear + (lineDir * t); 68 | float dist = (projection-_lightPos).norm(); 69 | #endif 70 | if (dist < 0.1) 71 | return true; 72 | 73 | return false; 74 | } 75 | 76 | void Fluid::MouseButton(GLFWwindow *window, int button,int action,int mods) 77 | { 78 | double mouseX, mouseY; 79 | if(button == GLFW_MOUSE_BUTTON_LEFT) { 80 | ::glfwGetCursorPos(window, &mouseX, &mouseY); 81 | if(action == GLFW_PRESS) { 82 | _isLeftKeyPressed = true; 83 | 84 | if (LightSelected(mouseX, mouseY)) { 85 | std::cout << "light selected" << std::endl; 86 | _isLightSelected = true; 87 | _arcball.StartDragging(mouseX, mouseY); 88 | } 89 | _arcball.StartRotation(mouseX, mouseY); 90 | } 91 | else if(action == GLFW_RELEASE) { 92 | _isLeftKeyPressed = false; 93 | _isLightSelected = false; 94 | _arcball.StopRotation(); 95 | _arcball.StopDragging(); 96 | } 97 | } 98 | else if(button == GLFW_MOUSE_BUTTON_MIDDLE) { 99 | _isMiddleKeyPressed = (action == GLFW_PRESS); 100 | } 101 | else if(button == GLFW_MOUSE_BUTTON_RIGHT) { 102 | #if 0 103 | if (action == GLFW_PRESS) { 104 | _isRightKeyPressed = true; 105 | ::glfwGetCursorPos(window, &mouseX, &mouseY); 106 | _arcball.StartZooming(mouseX, mouseY); 107 | } 108 | else if(action ==GLFW_RELEASE) { 109 | _isRightKeyPressed = false; 110 | _arcball.StopZooming(); 111 | } 112 | #endif 113 | } 114 | } 115 | 116 | void Fluid::MouseMotion(GLFWwindow *window, double nx, double ny) 117 | { 118 | if(_isLeftKeyPressed && _isCtrlPressed) { 119 | } 120 | else if(_isLeftKeyPressed && _isLightSelected) { 121 | _lightPos += _arcball.UpdateDragging(nx, ny); 122 | _renderer->SetLightPostion(_lightPos); 123 | } 124 | else if(_isLeftKeyPressed) { 125 | _arcball.UpdateRotation(nx, ny); 126 | } 127 | #if 0 128 | else if(_isRightKeyPressed) { 129 | _arcball.UpdateZooming(nx, ny); 130 | } 131 | #endif 132 | } 133 | 134 | void Fluid::Keyboard(GLFWwindow * window, int key, int scancode, int action, int mods) 135 | { 136 | if (action == GLFW_PRESS) { 137 | switch(key) { 138 | case GLFW_KEY_S: // Rendering on/off 139 | _isRendering = !_isRendering; 140 | _renderer->SetRendering(_isRendering); 141 | break; 142 | case GLFW_KEY_W: //wireframe on/off 143 | _isDrawSliceOutline = !_isDrawSliceOutline; 144 | _renderer->SetSliceOutline(_isDrawSliceOutline); 145 | break; 146 | } 147 | } 148 | } 149 | 150 | Fluid::~Fluid() 151 | { 152 | } 153 | 154 | #define BOUNDARY 155 | #undef BOUNDARY 156 | void Fluid::EnforceBoundary(int b, float* quantity) 157 | { 158 | #ifdef BOUNDARY 159 | int i, j; 160 | for (i=1; i<=N; i++) 161 | { 162 | for (j=1; j<=N; j++) { 163 | quantity[_I(0,i,j)] = (b==1) ? -quantity[_I(1,i,j)] : quantity[_I(1,i,j)]; 164 | quantity[_I(N+1,i,j)] = (b==1) ? -quantity[_I(N,i,j)] : quantity[_I(N,i,j)]; 165 | quantity[_I(i,0,j)] = (b==2) ? -quantity[_I(i,1,j)] : quantity[_I(i,1,j)]; 166 | quantity[_I(i,N+1,j)] = (b==2) ? -quantity[_I(i,N,j)] : quantity[_I(i,N,j)]; 167 | quantity[_I(i,j,0)] = (b==3) ? -quantity[_I(i,j,1)] : quantity[_I(i,j,1)]; 168 | quantity[_I(i,j,N+1)] = (b==3) ? -quantity[_I(i,j,N)] : quantity[_I(i,j,N)]; 169 | } 170 | } 171 | 172 | quantity[_I(0,0,0)] = (quantity[_I(1,0,0)] + quantity[_I(0,1,0)] + quantity[_I(0,0,1)]) / 3; 173 | quantity[_I(0,N+1,0)] = (quantity[_I(1,N+1,0)] + quantity[_I(0,N,0)] + quantity[_I(0,N+1,1)]) / 3; 174 | quantity[_I(N+1,0,0)] = (quantity[_I(N,0,0)] + quantity[_I(N+1,1,0)] + quantity[_I(N+1,0,1)]) / 3; 175 | quantity[_I(N+1,N+1,0)] = (quantity[_I(N,N+1,0)] + quantity[_I(N+1,N,0)] + quantity[_I(N+1,N+1,1)])/ 3; 176 | quantity[_I(0,0,N+1)] = (quantity[_I(1,0,N+1)] + quantity[_I(0,1,N+1)] + quantity[_I(0,0,N)]) / 3; 177 | quantity[_I(0,N+1,N+1)] = (quantity[_I(1,N+1,N+1)]+ quantity[_I(0,N,N+1)] + quantity[_I(0,N+1,N)]) / 3; 178 | quantity[_I(N+1,0,N+1)] = (quantity[_I(N,0,N+1)] + quantity[_I(N+1,1,N+1)] + quantity[_I(N+1,0,N)]) / 3; 179 | quantity[_I(N+1,N+1,N+1)] = (quantity[_I(N,N+1,N+1)]+ quantity[_I(N+1,N,N+1)] + quantity[_I(N+1,N+1,N)])/ 3; 180 | #endif 181 | } 182 | 183 | void Fluid::AddSource(float* src, float *dst) 184 | { 185 | int i; 186 | 187 | for (i=0; iN+0.5) xx=N+0.5f; i0=(int)xx; i1=i0+1; 226 | if (yy<0.5) yy=0.5f; if (yy>N+0.5) yy=N+0.5f; j0=(int)yy; j1=j0+1; 227 | if (zz<0.5) zz=0.5f; if (zz>N+0.5) zz=N+0.5f; k0=(int)zz; k1=k0+1; 228 | sx1 = xx-i0; sx0 = 1-sx1; 229 | sy1 = yy-j0; sy0 = 1-sy1; 230 | sz1 = zz-k0; sz0 = 1-sz1; 231 | v0 = sx0 * ( sy0*quantityTmp[_I(i0,j0,k0)] + sy1*quantityTmp[_I(i0,j1,k0)] ) + 232 | sx1 * ( sy0*quantityTmp[_I(i1,j0,k0)] + sy1*quantityTmp[_I(i1,j1,k0)] ); 233 | v1 = sx0 * ( sy0*quantityTmp[_I(i0,j0,k1)] + sy1*quantityTmp[_I(i0,j1,k1)] ) + 234 | sx1 * ( sy0*quantityTmp[_I(i1,j0,k1)] + sy1*quantityTmp[_I(i1,j1,k1)] ); 235 | quantity[_I(i,j,k)] = sz0*v0 + sz1*v1; 236 | } END_FOR 237 | EnforceBoundary(b,_density); 238 | } 239 | 240 | void Fluid::Project(void) 241 | { 242 | float* p = _velXTmp; 243 | float* div = _velYTmp; // temporary buffers, use old velocity buffers 244 | int l; 245 | float h; 246 | h = 1.0f/N; 247 | FOR_ALL_CELL { 248 | div[_I(i,j,k)] = -h*( 249 | _velX[_I(i+1,j,k)]-_velX[_I(i-1,j,k)]+ 250 | _velY[_I(i,j+1,k)]-_velY[_I(i,j-1,k)]+ 251 | _velZ[_I(i,j,k+1)]-_velZ[_I(i,j,k-1)])/3; 252 | p[_I(i,j,k)] = 0; 253 | } END_FOR 254 | EnforceBoundary(0,div); EnforceBoundary(0,p); 255 | for (l=0; l<20; l++) 256 | { 257 | FOR_ALL_CELL { 258 | p[_I(i,j,k)] = (div[_I(i,j,k)]+ 259 | p[_I(i-1,j,k)]+p[_I(i+1,j,k)]+ 260 | p[_I(i,j-1,k)]+p[_I(i,j+1,k)]+ 261 | p[_I(i,j,k-1)]+p[_I(i,j,k+1)])/6; 262 | } END_FOR 263 | EnforceBoundary(0,p); 264 | } 265 | FOR_ALL_CELL { 266 | _velX[_I(i,j,k)] -= (p[_I(i+1,j,k)]-p[_I(i-1,j,k)])/3/h; 267 | _velY[_I(i,j,k)] -= (p[_I(i,j+1,k)]-p[_I(i,j-1,k)])/3/h; 268 | _velZ[_I(i,j,k)] -= (p[_I(i,j,k+1)]-p[_I(i,j,k-1)])/3/h; 269 | } END_FOR 270 | EnforceBoundary(1,_velX); EnforceBoundary(2,_velY); 271 | } 272 | 273 | void Fluid::VorticityConfinement() 274 | { 275 | int ijk; 276 | //temp buffers 277 | float *curlX = _velXTmp, *curlY = _velYTmp, *curlZ=_velZTmp, *curl=_densityTmp; 278 | float dt0 = _dt * vc_eps; 279 | 280 | FOR_ALL_CELL { 281 | ijk = _I(i,j,k); 282 | // curlx = dw/dy - dv/dz 283 | curlX[ijk] = (_velZ[_I(i,j+1,k)] - _velZ[_I(i,j-1,k)]) * 0.5f - 284 | (_velY[_I(i,j,k+1)] - _velY[_I(i,j,k-1)]) * 0.5f; 285 | 286 | // curly = du/dz - dw/dx 287 | curlY[ijk] = (_velX[_I(i,j,k+1)] - _velX[_I(i,j,k-1)]) * 0.5f - 288 | (_velZ[_I(i+1,j,k)] - _velZ[_I(i-1,j,k)]) * 0.5f; 289 | 290 | // curlz = dv/dx - du/dy 291 | curlZ[ijk] = (_velY[_I(i+1,j,k)] - _velY[_I(i-1,j,k)]) * 0.5f - 292 | (_velX[_I(i,j+1,k)] - _velX[_I(i,j-1,k)]) * 0.5f; 293 | 294 | // curl = |curl| 295 | curl[ijk] = sqrtf(curlX[ijk]*curlX[ijk] + 296 | curlY[ijk]*curlY[ijk] + 297 | curlZ[ijk]*curlZ[ijk]); 298 | } END_FOR 299 | 300 | FOR_ALL_CELL { 301 | ijk = _I(i,j,k); 302 | float nX = (curl[_I(i+1,j,k)] - curl[_I(i-1,j,k)]) * 0.5f; 303 | float nY = (curl[_I(i,j+1,k)] - curl[_I(i,j-1,k)]) * 0.5f; 304 | float nZ = (curl[_I(i,j,k+1)] - curl[_I(i,j,k-1)]) * 0.5f; 305 | float len1 = 1.0f/(sqrtf(nX*nX+nY*nY+nZ*nZ)+0.0000001f); 306 | nX *= len1; 307 | nY *= len1; 308 | nZ *= len1; 309 | _velX[ijk] += (nY*curlZ[ijk] - nZ*curlY[ijk]) * dt0; 310 | _velY[ijk] += (nZ*curlX[ijk] - nX*curlZ[ijk]) * dt0; 311 | _velZ[ijk] += (nX*curlY[ijk] - nY*curlX[ijk]) * dt0; 312 | } END_FOR 313 | } 314 | 315 | #define DIFFUSE 316 | #define ADVECT 317 | 318 | void Fluid::VelocityStep() 319 | { 320 | #if 1 321 | AddSource(su, _velX); 322 | AddSource(sv, _velY); 323 | AddSource(sw, _velZ); 324 | #endif 325 | AddBuoyancy(); 326 | VorticityConfinement(); 327 | 328 | #ifdef DIFFUSE 329 | std::swap(_velXTmp, _velX); std::swap(_velYTmp, _velY); std::swap(_velZTmp, _velZ); 330 | Diffuse(1, _velXTmp, _velX, viscosity); 331 | Diffuse(2, _velYTmp, _velY, viscosity); 332 | Diffuse(3, _velZTmp, _velZ, viscosity); 333 | Project(); 334 | #endif 335 | #ifdef ADVECT 336 | std::swap(_velXTmp, _velX); std::swap(_velYTmp, _velY); std::swap(_velZTmp, _velZ); 337 | Advect(1, _velXTmp, _velX, _velXTmp, _velYTmp, _velZTmp); 338 | Advect(2, _velYTmp, _velY, _velXTmp, _velYTmp, _velZTmp); 339 | Advect(3, _velZTmp, _velZ, _velXTmp, _velYTmp, _velZTmp); 340 | Project(); 341 | #endif 342 | } 343 | 344 | void Fluid::DensityStep() 345 | { 346 | #if 1 347 | AddSource(sd, _density); 348 | #endif 349 | #ifdef DIFFUSE 350 | std::swap(_densityTmp, _density); 351 | Diffuse(0, _densityTmp, _density, diffusion); 352 | #endif 353 | #ifdef ADVECT 354 | std::swap(_densityTmp, _density); 355 | Advect(0, _densityTmp, _density, _velX, _velY, _velZ); 356 | 357 | #if 1 358 | //decrease density 359 | FOR_ALL_CELL { 360 | _density[_I(k,j,i)] -= 0.001; 361 | if(_density[_I(k,j,i)] < 0) 362 | _density[_I(k,j,i)] = 0; 363 | } END_FOR 364 | #endif 365 | 366 | #endif 367 | } 368 | 369 | void Fluid::GenerateSmoke() 370 | { 371 | const int centerY = RES/4; 372 | const int centerZ = RES/2; 373 | float dens = (rand()%1000)/1000.0f; 374 | for (int i = 1 ; i <= N ; i++) { 375 | for (int j = 1 ; j <= N ; j++) { 376 | if (hypot(i-centerY, j-centerZ) < RES/10) { 377 | this->_density[_I(5,i,j)] = dens; 378 | this->_velX[_I(5,i,j)] = 2.0f; 379 | } 380 | } 381 | } 382 | 383 | } 384 | 385 | void Fluid::SimulateStep() 386 | { 387 | GenerateSmoke(); 388 | 389 | VelocityStep(); 390 | DensityStep(); 391 | 392 | } 393 | 394 | 395 | void Fluid::Show() 396 | { 397 | _renderer->FillTexture(); 398 | _renderer->Render(); 399 | /* 400 | else { //without rendering 401 | Cube(); 402 | glBegin(GL_POINTS); 403 | FOR_ALL_CELL { 404 | if(!ALMOST_EQUAL(_density[_I(i,j,k)], 0) ) { 405 | glVertex3f(((float)i/N-0.5)*2, ((float)j/N-0.5)*2, ((float)k/N-0.5)*2 ); 406 | } 407 | } END_FOR 408 | glEnd(); 409 | 410 | glPointSize(13.0f); 411 | glBegin(GL_POINTS); 412 | glColor4f(0.0f, 1.0f, 1.0f, 1.0f); 413 | glVertex3f(_lightPos[0], _lightPos[1], _lightPos[2]); 414 | glColor4f(1.0f, 1.0f, 1.0f, 1.0f); 415 | glEnd(); 416 | glPointSize(1.0f); 417 | } 418 | */ 419 | } 420 | 421 | 422 | void Fluid::ClearBuffer(float* buf) 423 | { 424 | for (int i=0; i= 0; 206 | return up.dot(va.cross(vb)) >= 0; 207 | } 208 | }; 209 | 210 | void Renderer::DrawSlices(GLdouble mvMatrix[16]) 211 | { 212 | int i; 213 | Eigen::Vector3f viewdir(-mvMatrix[2], -mvMatrix[6], -mvMatrix[10]); //FIXME 214 | 215 | viewdir.normalize(); 216 | // find cube vertex that is closest to the viewer 217 | GLfloat (*cv)[3] = _cubeVertices; 218 | for (i=0; i<8; i++) { 219 | float x = cv[i][0] + viewdir[0]; 220 | float y = cv[i][1] + viewdir[1]; 221 | float z = cv[i][2] + viewdir[2]; 222 | if ((x>=-1.0f)&&(x<=1.0f) 223 | &&(y>=-1.0f)&&(y<=1.0f) 224 | &&(z>=-1.0f)&&(z<=1.0f)) 225 | { 226 | break; 227 | } 228 | } 229 | assert(i != 8); 230 | 231 | // our slices are defined by the plane equation A*x + B*y +C*z + D = 0 232 | // (a,b,c), the plane normal, are given by viewdir 233 | // d is the parameter along the view direction. the first d is given by 234 | // inserting previously found vertex into the plane equation 235 | float d0 = -(viewdir[0]*cv[i][0] + viewdir[1]*cv[i][1] + viewdir[2]*cv[i][2]); 236 | float dStep = 2*d0/SLICE_NUM; 237 | int n = 0; 238 | for (float d = -d0; d < d0; d += dStep) { 239 | // IntersectEdges returns the intersection points of all cube edges with 240 | // the given plane that lie within the cube 241 | std::vector pt = IntersectEdges(viewdir[0], viewdir[1], viewdir[2], d); 242 | 243 | if (pt.size() > 2) { 244 | // sort points to get a convex polygon 245 | std::sort(pt.begin()+1, pt.end(), Convexcomp(pt[0], viewdir)); 246 | 247 | glEnable(GL_TEXTURE_3D); 248 | glBegin(GL_POLYGON); 249 | for (i=0; i Renderer::IntersectEdges(float A, float B, float C, float D) 276 | { 277 | float t; 278 | Eigen::Vector3f p; 279 | std::vector res; 280 | GLfloat (*edges)[2][3] = _cubeEdges; 281 | 282 | 283 | for (int i=0; i<12; i++) { 284 | t = -(A*edges[i][0][0] + B*edges[i][0][1] + C*edges[i][0][2] + D) 285 | / (A*edges[i][1][0] + B*edges[i][1][1] + C*edges[i][1][2]); 286 | if ((t>0)&&(t<2)) { 287 | p[0] = edges[i][0][0] + edges[i][1][0]*t; 288 | p[1] = edges[i][0][1] + edges[i][1][1]*t; 289 | p[2] = edges[i][0][2] + edges[i][1][2]*t; 290 | res.push_back(p); 291 | } 292 | } 293 | 294 | return res; 295 | } 296 | 297 | 298 | void Renderer::FillTexture() 299 | { 300 | if (_textureData == NULL) { 301 | _textureData = new unsigned char[_SIZE*4]; 302 | } 303 | memset(_textureData, 0, _SIZE*4); 304 | 305 | const float *density = _volumeData; 306 | unsigned char* intensity = new unsigned char[_SIZE]; 307 | memset(intensity, 0, _SIZE); 308 | CastLight(_RES, density, intensity); 309 | 310 | #if 1 311 | //FIXME: It is important to beware that texture coordinate 312 | //is in reverse order of the simulation coordinate 313 | for (int i = 0 ; i < _RES ; i++) { 314 | for (int j = 0 ; j < _RES ; j++) { 315 | for (int k = 0 ; k < _RES ; k++) { 316 | //unsigned char c = 200; 317 | int texIndex = i*_RES*_RES + j*_RES + k; /*reverse order*/ 318 | int densIndex = k*_RES*_RES + j*_RES + i; 319 | unsigned char c = intensity[densIndex]; 320 | _textureData[texIndex*4] = c; 321 | _textureData[texIndex*4+1] = c; 322 | _textureData[texIndex*4+2] = c; 323 | _textureData[texIndex*4+3] = (density[densIndex]>0.1f) ? 324 | 255 : (unsigned char) (density[densIndex]*2550.0f); 325 | } 326 | } 327 | } 328 | #else 329 | for (int i=0; i<_SIZE; i++) { 330 | unsigned char c = intensity[i]; 331 | //unsigned char c = 200; 332 | _textureData[(i<<2)] = c; 333 | _textureData[(i<<2)+1] = c; 334 | _textureData[(i<<2)+2] = c; 335 | _textureData[(i<<2)+3] = (density[i]>0.1f) ? 255 : (unsigned char) (density[i]*2550.0f); 336 | } 337 | #endif 338 | 339 | delete []intensity; 340 | 341 | glActiveTextureARB(GL_TEXTURE0_ARB); 342 | glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, _RES, _RES, _RES, 0, GL_RGBA, GL_UNSIGNED_BYTE, _textureData); 343 | } 344 | 345 | void Renderer::GenerateRayTemplate(int edgeLen) 346 | { 347 | _rayTemplate[0][0] = _rayTemplate[0][2] = _rayTemplate[0][2] = 0; 348 | float fx = 0.0f, fy = 0.0f, fz = 0.0f; 349 | int x = 0, y = 0, z = 0; 350 | float lx = _lightDir[0] + 0.000001f, ly = _lightDir[1] + 0.000001f, lz = _lightDir[2] + 0.000001f; 351 | int xinc = (lx > 0) ? 1 : -1; 352 | int yinc = (ly > 0) ? 1 : -1; 353 | int zinc = (lz > 0) ? 1 : -1; 354 | float tx, ty, tz; 355 | int i = 1; 356 | int len = 0; 357 | int maxlen = 3*edgeLen*edgeLen; 358 | while (len <= maxlen) 359 | { 360 | // fx + t*lx = (x+1) -> t = (x+1-fx)/lx 361 | tx = (x+xinc-fx)/lx; 362 | ty = (y+yinc-fy)/ly; 363 | tz = (z+zinc-fz)/lz; 364 | 365 | if ((tx<=ty)&&(tx<=tz)) { 366 | _rayTemplate[i][0] = _rayTemplate[i-1][0] + xinc; 367 | x =+ xinc; 368 | fx = x; 369 | 370 | if (ALMOST_EQUAL(ty,tx)) { 371 | _rayTemplate[i][1] = _rayTemplate[i-1][1] + yinc; 372 | y += yinc; 373 | fy = y; 374 | } else { 375 | _rayTemplate[i][1] = _rayTemplate[i-1][1]; 376 | fy += tx*ly; 377 | } 378 | 379 | if (ALMOST_EQUAL(tz,tx)) { 380 | _rayTemplate[i][2] = _rayTemplate[i-1][2] + zinc; 381 | z += zinc; 382 | fz = z; 383 | } else { 384 | _rayTemplate[i][2] = _rayTemplate[i-1][2]; 385 | fz += tx*lz; 386 | } 387 | } else if ((ty0) ? 0 : n-1; 427 | int sy = (_lightDir[1]>0) ? 0 : n-1; 428 | int sz = (_lightDir[2]>0) ? 0 : n-1; 429 | 430 | float decay = 1.0f/(n*DECAY); 431 | 432 | for (i=0; i 0) 456 | intensity[offset] = (unsigned char) ((intensity[offset] + l)*0.5f); 457 | else 458 | intensity[offset] = (unsigned char) l; 459 | d = dens[offset]*255.0f; 460 | if (l > AMBIENT) { 461 | l -= d*decay; 462 | if (l < AMBIENT) 463 | l = AMBIENT; 464 | } 465 | 466 | i++; 467 | xx = x + _rayTemplate[i][0]; 468 | yy = y + _rayTemplate[i][1]; 469 | zz = z + _rayTemplate[i][2]; 470 | } while ((xx>=0)&&(xx=0)&&(yy=0)&&(zz 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | --------------------------------------------------------------------------------