1 /+ 2 Vasaro Copyright © 2018 Andrea Fontana 3 This file is part of Vasaro. 4 5 Vasaro is free software: you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation, either version 3 of the License, or 8 (at your option) any later version. 9 10 Vasaro is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with Vasaro. If not, see <http://www.gnu.org/licenses/>. 17 +/ 18 19 module viewer; 20 21 import generator; 22 23 import std.math : abs, sqrt, tan, PI; 24 import std.algorithm : max, min; 25 26 import derelict.opengl; 27 import derelict.sdl2.sdl; 28 29 public: 30 31 struct VaseModel 32 { 33 GLuint vertexVbo; 34 GLuint vertexNormalsVbo; 35 36 float[] vertex; 37 float[] vertexNormals; 38 } 39 40 __gshared VaseModel[2] model; 41 42 void start() 43 { 44 if (status.actual == ViewerStatus.STARTED) 45 return; 46 47 status.requested = ViewerStatus.STARTED; 48 49 if (renderWindow == null) initRenderingWindow(); 50 } 51 52 void stop() { status.requested = ViewerStatus.STOPPED; } 53 void clear() { hasModel = false; } 54 bool isRunning() { return !(status.requested == ViewerStatus.STOPPED && status.actual == ViewerStatus.STOPPED); } 55 56 void uninit() 57 { 58 if (context && renderWindow) 59 { 60 SDL_GL_DeleteContext(context); 61 SDL_DestroyWindow(renderWindow); 62 SDL_Quit(); 63 64 debug 65 { 66 import std.stdio : writeln; 67 writeln("SDL cleared."); 68 } 69 } 70 } 71 72 void setIcon(char* data, int width, int height) 73 { 74 SDL_Surface* icon = SDL_CreateRGBSurfaceFrom(data, width, height, 32, width*4, 0x000000FF, 0x0000FF00, 0x00FF0000,0xFF000000); 75 SDL_SetWindowIcon(renderWindow, icon); 76 SDL_FreeSurface(icon); 77 } 78 79 private: 80 81 SDL_Window *renderWindow = null; 82 SDL_GLContext context = null; 83 84 enum ViewerStatus 85 { 86 STARTED = 0, 87 STOPPED 88 } 89 90 struct ViewerStatusSync 91 { 92 ViewerStatus requested = ViewerStatus.STOPPED; 93 ViewerStatus actual = ViewerStatus.STOPPED; 94 } 95 96 __gshared ViewerStatusSync status; 97 __gshared bool hasModel = false; 98 99 // Mixing-in opengl functions declarations 100 mixin glFreeFuncs!(GLVersion.gl21, true); 101 102 // Camera/Rendering settings 103 float cameraRotationX = 0; 104 float cameraRotationY = 0; 105 bool autoRotate = true; 106 float lastSign = 1; 107 size_t lastRendered = -1; 108 float currentSpeed = 1; 109 int currentZoom = 0; 110 111 void setupScene(int w, int h) 112 { 113 glMatrixMode(GL_PROJECTION); 114 glLoadIdentity(); 115 116 int width = w, height = h; 117 double aspect_root = sqrt(width * 1.0f / height); 118 double nearest = 0.125; 119 120 float DEG2RAD = PI / 180; 121 122 float front = 10.0f; 123 float back = 500.0f; 124 float fovY = 80.0f; 125 float tangent = tan(fovY/2 * DEG2RAD); 126 float fheight = front * tangent; 127 float fwidth = fheight * width/height; 128 129 glFrustum(-fwidth, fwidth, -fheight, fheight, front, back); 130 131 132 float[] light_ambient = [ 1.0, 1.0, 1.0, 0.5 ]; 133 float[] light_diffuse = [ 1.0, 1.0, 1.0, 0.5 ]; 134 float[] light_specular = [ 0.6, 0.6, 0.6, 0.6 ]; 135 float[] light_position = [ 1.0, 1.0, 100.0, 0.0 ]; 136 137 // Put lights into from POV 138 glMatrixMode(GL_MODELVIEW); 139 glLoadIdentity(); 140 141 glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient.ptr); 142 glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse.ptr); 143 glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular.ptr); 144 glLightfv(GL_LIGHT0, GL_POSITION, light_position.ptr); 145 } 146 147 private void createWindow() 148 { 149 if (context) SDL_GL_DeleteContext(context); 150 if (renderWindow) SDL_DestroyWindow(renderWindow); 151 152 renderWindow = SDL_CreateWindow( 153 "Vasaro Rendering", 154 SDL_WINDOWPOS_CENTERED, 155 SDL_WINDOWPOS_CENTERED, 156 512, 157 512, 158 SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_SKIP_TASKBAR | SDL_WINDOW_MOUSE_CAPTURE 159 ); 160 161 context = SDL_GL_CreateContext(renderWindow); 162 163 // Some una-tantum settings for opengl rendering 164 glMatrixMode(GL_PROJECTION); 165 glClearDepth(1.0f); 166 glEnable (GL_DEPTH_TEST); 167 glDepthFunc(GL_LEQUAL); 168 glClearColor(0.1f, 0.18f, 0.24f, 1); 169 170 glEnable(GL_LIGHTING); 171 glEnable(GL_CULL_FACE); 172 glClearDepth(1.0f); 173 glDepthFunc(GL_LEQUAL); 174 glFrontFace(GL_CCW); 175 glCullFace(GL_BACK); 176 177 glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_TRANSFORM_BIT); 178 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 179 glEnable(GL_BLEND); 180 glEnable(GL_MULTISAMPLE); 181 182 glEnable (GL_LIGHTING); 183 glEnable (GL_LIGHT0); 184 185 SDL_CaptureMouse(SDL_TRUE); 186 187 status.actual = ViewerStatus.STARTED; 188 189 } 190 191 private void initRenderingWindow() 192 { 193 DerelictGL3.load(); 194 DerelictSDL2.load(); 195 196 SDL_Init(SDL_INIT_VIDEO); 197 198 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); 199 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); 200 SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); 201 SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4); 202 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); 203 204 createWindow(); 205 206 DerelictGL3.reload(); 207 } 208 209 210 211 public bool renderFrame() 212 { 213 float[3] clear_color = [0.10f, 0.18f, 0.24f]; 214 215 int windowWidth; 216 int windowHeight; 217 218 // Init scene 219 { 220 SDL_GetWindowSize(renderWindow, &windowWidth, &windowHeight); 221 glViewport(0,0,windowWidth,windowHeight); 222 glClear(GL_COLOR_BUFFER_BIT); 223 setupScene(windowWidth, windowHeight); 224 } 225 226 // Hide request received 227 if (status.actual == ViewerStatus.STARTED && status.requested == ViewerStatus.STOPPED) 228 { 229 SDL_HideWindow(renderWindow); 230 status.actual = ViewerStatus.STOPPED; 231 hasModel = false; 232 } 233 234 // Show request received 235 else if (status.actual == ViewerStatus.STOPPED && status.requested == ViewerStatus.STARTED) 236 { 237 //SDL_ShowWindow(renderWindow); 238 createWindow(); 239 status.actual = ViewerStatus.STARTED; 240 } 241 242 // Render frame if requested 243 else if (status.actual == ViewerStatus.STARTED) 244 { 245 auto lastGenerated = lastGenerated(); 246 247 // Just started. Still no model available. 248 if (lastGenerated != 0) 249 { 250 // New model to show? 251 if (!hasModel || (lastRendered != lastGenerated)) 252 { 253 // Buffers binding 254 if (model[currentModel].vertex.length > 0) { 255 glGenBuffers(1, &model[currentModel].vertexVbo); 256 glBindBuffer(GL_ARRAY_BUFFER, model[currentModel].vertexVbo); 257 glBufferData(GL_ARRAY_BUFFER, model[currentModel].vertex.length*float.sizeof, model[currentModel].vertex.ptr, GL_STATIC_DRAW); 258 glBindBuffer(GL_ARRAY_BUFFER, 0); /* release binding */ 259 260 glGenBuffers(1, &model[currentModel].vertexNormalsVbo); 261 glBindBuffer(GL_ARRAY_BUFFER, model[currentModel].vertexNormalsVbo); 262 glBufferData(GL_ARRAY_BUFFER, model[currentModel].vertexNormals.length*float.sizeof, model[currentModel].vertexNormals.ptr, GL_STATIC_DRAW); 263 glBindBuffer(GL_ARRAY_BUFFER, 0); /* release binding */ 264 } 265 266 hasModel = true; 267 lastRendered = lastGenerated; 268 } 269 270 if(hasModel) renderVase(); 271 } 272 } 273 274 // Handling window event 275 SDL_Event event; 276 while (SDL_PollEvent(&event)) 277 { 278 if ( 279 // Hide window on closing request 280 event.type == SDL_QUIT 281 282 // If window is minimieze 283 || (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_MINIMIZED) 284 285 // Also on escape-button press 286 || (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_ESCAPE) 287 ) stop(); 288 289 // Moving mouse with left button down 290 else if (event.type == SDL_MOUSEMOTION && (event.motion.state & SDL_BUTTON_LMASK) == SDL_BUTTON_LMASK) 291 { 292 293 cameraRotationX += 2*PI/360*event.motion.yrel*10; 294 cameraRotationX = max(-50, min(50, cameraRotationX)); 295 296 cameraRotationY += 2*PI/360*event.motion.xrel*10; 297 currentSpeed = 2*PI/360*event.motion.xrel*10; 298 299 if (currentSpeed == 0) currentSpeed = 0.1f; 300 lastSign = event.motion.xrel >= 0?1:-1; 301 } 302 303 // I stop autorotation when user has left button down! 304 else if (event.type == SDL_MOUSEBUTTONDOWN && event.button.button == SDL_BUTTON_LEFT) 305 { 306 autoRotate = false; 307 currentSpeed = lastSign*0.1; 308 } 309 else if (event.type == SDL_MOUSEBUTTONUP && event.button.button == SDL_BUTTON_LEFT) autoRotate = true; 310 311 // Zoom 312 else if (event.type == SDL_MOUSEWHEEL) 313 { 314 currentZoom += event.wheel.y; 315 currentZoom = max(-20, min(5, currentZoom)); 316 } 317 } 318 319 // Send back buffer to window 320 SDL_GL_SwapWindow(renderWindow); 321 return true; 322 } 323 324 private void renderVase() 325 { 326 // Clear the whole scene 327 glClear(GL_DEPTH_BUFFER_BIT); 328 glMatrixMode(GL_MODELVIEW); 329 glLoadIdentity(); 330 331 // Zoom 332 glTranslatef(0.0f, -50.0 + currentZoom * 3 , -150.0 + currentZoom * 5); 333 334 // Rotation 335 glRotatef(cameraRotationX, 1.0, 0.0, 0.0); 336 glRotatef(cameraRotationY, 0.0, 1.0, 0.0); 337 338 if (autoRotate) 339 { 340 cameraRotationY = (cameraRotationY + currentSpeed); 341 342 // Accelerate / Decelerate 343 if (abs(currentSpeed) > 1) 344 { 345 currentSpeed *= 0.97; 346 if (abs(currentSpeed) < 1) currentSpeed = 1* lastSign; 347 } 348 else if(abs(currentSpeed) < 1) 349 { 350 currentSpeed *= 1.05; 351 if (abs(currentSpeed) > 1) currentSpeed = 1 * lastSign; 352 } 353 } 354 if (cameraRotationY > 360) cameraRotationY -= 360; 355 if (cameraRotationY < 0) cameraRotationY += 360; 356 357 358 // Gummy material 359 float[] no_mat = [0.0f, 0.0f, 0.0f, 1.0f]; 360 float[] mat_ambient = [0.05f, 0.05f, 0.05f, 1.0f]; 361 float[] mat_ambient_color = [0.8f, 0.8f, 0.2f, 1.0f]; 362 float[] mat_diffuse = [0.8f, 0.7f, 0.1f, 1.0f]; 363 float[] mat_specular = [1.0f, 1.0f, 1.0f, 1.0f]; 364 float high_shininess = 80.0f; 365 366 glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat.ptr); 367 glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse.ptr); 368 glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular.ptr); 369 glMaterialf(GL_FRONT, GL_SHININESS, high_shininess); 370 glMaterialfv(GL_FRONT, GL_EMISSION, mat_ambient.ptr); 371 372 // Push vertices and triangles 373 glEnableClientState(GL_VERTEX_ARRAY); 374 glBindBuffer(GL_ARRAY_BUFFER, model[currentModel].vertexVbo); 375 glVertexPointer(3, GL_FLOAT, 0, cast(void*)(0)); 376 377 glEnableClientState(GL_NORMAL_ARRAY); 378 glBindBuffer(GL_ARRAY_BUFFER, model[currentModel].vertexNormalsVbo); 379 glNormalPointer(GL_FLOAT, 0, cast(void*)(0)); 380 381 glBindBuffer(GL_ARRAY_BUFFER, 0); 382 glDrawArrays(GL_TRIANGLES, 0, cast(int)(model[currentModel].vertex.length/3)); 383 glDisableClientState(GL_VERTEX_ARRAY); 384 } 385