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