/* * screen.c * * Copyright (c) 2003-2016, NVIDIA CORPORATION. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ // // Pseudo-random number generation // #include #include "nvgldemo.h" #include "nvtexfont.h" #include "array.h" #include "random.h" #include "vector.h" #include "screen.h" #include "shaders.h" #include "tree.h" #include "leaves.h" #include "branches.h" #include "firefly.h" #include "ground.h" #include "sky.h" #include "picture.h" #include "slider.h" #include "ground_img.h" #include "ground_s_img.h" #include "bark_img.h" #include "bark_s_img.h" #include "sky_night_img.h" #include "sky_night_s_img.h" #include "leaf_img.h" #include "leaf_s_img.h" #include "leaf_back_img.h" #include "leaf_back_s_img.h" #include "NVidiaLogo_img.h" #include "label_balance_img.h" #include "label_spread_img.h" #include "label_leaf_size_img.h" #include "label_branch_size_img.h" #include "label_depth_img.h" #include "label_twist_img.h" #include "label_fullness_img.h" // Maximum number of lights (fireflies) // NOTE: any changes to NUM_LIGHTS must also be made to lighting_vert.glslv #define NUM_LIGHTS 8 // Texture font NVTexfontRasterFont *nvtxf = NULL; // Scene textures (full size and small versions) static unsigned char* texBark[] = {bark_img, bark_s_img}; static unsigned char* texLeafFront[] = {leaf_img, leaf_s_img}; static unsigned char* texLeafBack[] = {leaf_back_img, leaf_back_s_img}; static unsigned char* texSky[] = {sky_night_img, sky_night_s_img}; static unsigned char* texGround[] = {ground_img, ground_s_img}; // Array of data structures for overlay texture info static struct pictInfoStruct { unsigned char *filename; float left, right, bottom, top; } pictInfo[] = { {label_depth_img, 0, 150, 260, 275}, {label_balance_img, 0, 150, 220, 235}, {label_twist_img, 0, 150, 180, 195}, {label_spread_img, 0, 150, 140, 155}, {label_leaf_size_img, 0, 150, 100, 115}, {label_branch_size_img, 0, 150, 60, 75}, {label_fullness_img, 0, 150, 20, 35}, {NVidiaLogo_img, 0, 150, 450, 480}, }; #define NUM_PICTS (sizeof(pictInfo) / sizeof(struct pictInfoStruct)) static int const LOGO_PICT = NUM_PICTS - 1; // Object containing a tree and a set of fireflies typedef struct { float x; float y; float angle; Firefly fireflies[NUM_LIGHTS]; } TreePos; // Create a new tree/firefly set static TreePos* TreePos_new( float nx, float ny, float a) { int i; TreePos *o = (TreePos*)MALLOC(sizeof(TreePos)); o->x = nx; o->y = ny; o->angle = a; for (i = 0; i < NUM_LIGHTS; i++) { Firefly_init(o->fireflies + i, i); } return o; } static void TreePos_delete( TreePos *o) { int i; for (i = 0; i < NUM_LIGHTS; i++) { Firefly_destroy(o->fireflies + i); } FREE(o); } ////////////////////////////////////////////////////////////////////// // variables used in this module. // Properties of the window. static int width, height; static int swapInterval = 0; // light_count is the current number of lights (bugs) where // NUM_LIGHTS is the max number of lights. static int lightCount = 3; // camera properties. static float3 eye = {0.0f, 1.0f, 7.0f}; static float heading = 0.0f; static float pitch = 17.0f; static float dh = 0.0f; static float dp = 0.0f; static float velocity = 0.0f; // keep track of all the trees in the scene. static Array treePosList; // 2D items in the window. static Slider *sliders[NUM_TREE_PARAMS]; static int selectedSlider = TREE_PARAM_DEPTH; static Picture *picts[8]; static GLboolean overlayFlag = GL_TRUE; // FPS is visible on screen static GLboolean fpsFlag = GL_TRUE; // FPS is displayed/logged on the text console/terminal static GLboolean fpsLogFlag = GL_FALSE; // Time tracking #define FPS_PERIOD 40 // How many frames to recompute fps static double startTime; static double currentTime; static double duration; static double dt = 1.0 / 20.0; static double fps = 0.0; static double fpsTime; static int fpsCount = 0; #ifdef DUMP_VERTS_PER_SEC int vertCount = 0; static double vps = 0.0; #endif // Use low res textures static GLboolean smalltex = GL_FALSE; // Don't render sky static GLboolean nosky = GL_FALSE; // Don't render menus static GLboolean nomenu = GL_FALSE; ////////////////////////////////////////////////////////////////////// // utility functions. static void setSlider( int selectedSlider, float val, GLboolean mustSet) { if (Slider_setValue(sliders[selectedSlider], val) || mustSet) { Tree_setParam(selectedSlider, val); } } static void tick(void) { double t; double delta; // Get current time and compute delta since last frame t = (double)SYSTIME() / ((long long)1000000000LL); if (!currentTime) currentTime = fpsTime = t; delta = t - currentTime; currentTime = t; // If framerate is less than one frame per second, we clamp the time // delta used for the animation to 1, in order to prevent things // from appearing to pop around randomly and make the controls // somewhat usable. if (delta >= 1.0f) delta = 1.0; // Now smooth out the delta a bit if (delta != 0.0f) dt = dt * 0.7f + delta * 0.3f; else dt = 0.0f; // In theory, 1 / dt is the fps, but that changes too often. // We find out fps only once in FPS_PERIOD frames. if (++fpsCount == FPS_PERIOD) { double fpsDelta = t - fpsTime; fpsCount = 0; fpsTime = t; fps = FPS_PERIOD / fpsDelta; # ifdef DUMP_VERTS_PER_SEC vps = vertCount / fpsDelta; vertCount = 0; if (fpsLogFlag) { NvGlDemoLog("fps: %f vps: %f\n", fps, vps); } # else if (fpsLogFlag) { NvGlDemoLog("fps: %f\n", fps); } # endif } } static void refreshLights(void) { glUseProgram(prog_solids); glUniform1i(uloc_solidsLights, lightCount); glUseProgram(prog_leaves); glUniform1i(uloc_leavesLights, lightCount); } ////////////////////////////////////////////////////////////////////// // initialize and de-initialize the module. int Screen_initialize( float d, GLsizei w, GLsizei h, GLboolean fpsFlagRequest) { int i, ypos; TreePos *treeposPtr; fpsFlag = fpsLogFlag = fpsFlagRequest; // Load shaders if (!LoadShaders()) return 0; // Initialize the module parameters. width = w; height = h; // // 3D components initialization. // // Initialize fireflies Firefly_global_init(NUM_LIGHTS); // Initialize trees Tree_initialize(NvGlDemoLoadTgaFromBuffer(GL_TEXTURE_2D, 1, &texBark[smalltex]), NvGlDemoLoadTgaFromBuffer(GL_TEXTURE_2D, 1, &texLeafFront[smalltex]), NvGlDemoLoadTgaFromBuffer(GL_TEXTURE_2D, 1, &texLeafBack[smalltex])); Array_init(&treePosList, sizeof(TreePos)); treeposPtr = TreePos_new(0.0f, 0.0f, 0.0f); Array_push(&treePosList, treeposPtr); TreePos_delete (treeposPtr); // Initialize sky if (!nosky) { Sky_initialize(NvGlDemoLoadTgaFromBuffer(GL_TEXTURE_2D, 1, &texSky[smalltex])); } // Initialize ground Ground_initialize(NvGlDemoLoadTgaFromBuffer(GL_TEXTURE_2D, 1, &texGround[smalltex])); // Time tracking initialization. startTime = currentTime = (double)SYSTIME() / ((long long)1000*1000000); fpsTime = startTime; duration = d; tick(); // Slider initialization. for (i = 0, ypos = 240; i < NUM_TREE_PARAMS; i++, ypos -= 40) { sliders[i] = Slider_new(treeParamsMin[i], treeParamsMax[i], treeParams[i]); Slider_setPos(sliders[i], 0.0f, 150.0f, (float) ypos, (float) (ypos + 20)); } Slider_select(sliders[selectedSlider], GL_TRUE); // Picture initialization. for (i = (nomenu ? LOGO_PICT : 0); i < (int)NUM_PICTS; i++) { picts[i] = Picture_new(NvGlDemoLoadTgaFromBuffer(GL_TEXTURE_2D, 1, &pictInfo[i].filename)); Picture_setPos(picts[i], pictInfo[i].left, pictInfo[i].right, pictInfo[i].bottom, pictInfo[i].top); } // Some GL initializations here. NvGlDemoSwapInterval(demoState.display, swapInterval); glViewport(0, 0, width, height); glClearDepthf(1.0f); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glDepthRangef(0.0f, 1.0f); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); refreshLights(); // All shaders use texture unit 0 glUseProgram(prog_solids); glUniform1i(uloc_solidsTexUnit, 0); glUseProgram(prog_leaves); glUniform1i(uloc_leavesTexUnit, 0); glUseProgram(prog_simpletex); glUniform1i(uloc_simpletexTexUnit, 0); glUseProgram(prog_overlaytex); glUniform1i(uloc_overlaytexTexUnit, 0); nvtxf = nvtexfontInitRasterFont(NV_TEXFONT_DEFAULT, 0, GL_TRUE, GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR); return 1; } void Screen_resize( GLsizei w, GLsizei h) { width = w; height = h; glViewport(0, 0, width, height); } void Screen_deinitialize(void) { int i; for (i = (nomenu ? LOGO_PICT : 0); i < (int)NUM_PICTS; i++) { Picture_delete(picts[i]); } if (!nomenu) { for (i = 0; i < NUM_TREE_PARAMS; i++) { Slider_delete(sliders[i]); } } Tree_deinitialize(); Ground_deinitialize(); if (!nosky) Sky_deinitialize(); Array_destroy(&treePosList); Firefly_global_destroy(); if (nvtxf != NULL) nvtexfontUnloadRasterFont(nvtxf); } static void addTree(void) { float scale = SKY_RADIUS - GROUND_SIZE; static float cone = 60.0f; float r = ((float) GetRandom()) * ((float) GetRandom()) * scale; float h = degToRadF(heading + (((float)GetRandom()) * cone - cone / 2.0f)); TreePos *treeposPtr; treeposPtr = TreePos_new(COS(h) * r, SIN(h) * r, ((float) GetRandom()) * 360.0f); Array_push(&treePosList, treeposPtr); TreePos_delete(treeposPtr); } static GLboolean sliderControl( int key) { if (nomenu) { return GL_TRUE; } switch (key) { case 'i': case 'k': { int dir = (key == 'i' ? -1 : 1); Slider_select(sliders[selectedSlider], GL_FALSE); selectedSlider += dir; if (selectedSlider == -1) { selectedSlider = NUM_TREE_PARAMS - 1; } if (selectedSlider == NUM_TREE_PARAMS) { selectedSlider = 0; } Slider_select(sliders[selectedSlider], GL_TRUE); return GL_TRUE; } case 'j': case 'l': { float val; val = treeParamsMax[selectedSlider]-treeParamsMin[selectedSlider]; val *= 0.02f * (key == 'j' ? -1.0f : +1.0f); val += Slider_getValue(sliders[selectedSlider]); val = clamp(val, treeParamsMin[selectedSlider], treeParamsMax[selectedSlider]), setSlider(selectedSlider, val, GL_FALSE); return GL_TRUE; } } return GL_FALSE; } static GLboolean cameraControl(int key) { switch(key) { case 'j': case 'l': { float h = degToRadF(heading); float ch = COS(h); float sh = SIN(h); float3 right_vec = {ch, 0.0f, sh}; float3 tmp; mult_f3f(tmp, right_vec, 3.0f); multi_f3f(tmp, (float)(key == 'l' ? dt : -dt)); addi_f3(eye, tmp); return GL_TRUE; } case 'i': case 'k': velocity += key == 'i' ? 0.2f : -0.2f; return GL_TRUE; case 'I': case 'K': eye[1] += (float)(key == 'I' ? dt : -dt); return GL_TRUE; } return GL_FALSE; } static GLboolean sceneControl(int key) { switch (key) { case ' ': if (!nomenu) { overlayFlag = !overlayFlag; } return GL_TRUE; case 'q': case 'Q': // Artificially set the start time so we'll terminate. if (duration <= 0.0) { duration = 0.1; } startTime = currentTime - duration; return GL_TRUE; case 'h': case 'H': NvGlDemoLog("\n" " SPACE: toggles the controls overlay\n" "\n" " If the controls overlay is on:\n" " i : select next higher parameter bar\n" " j : decrease current parameter\n" " k : select next lower parameter bar\n" " l : increase current parameter\n" "\n" " If the controls overlay is off\n" " i : move forwards\n" " j : move left\n" " k : move backwards\n" " l : move right\n" " I : move up\n" " K : move down\n" "\n" " c : change \"character\" of the tree\n" " f : toggle frames/sec display\n" " S : output polygon count to terminal window\n" " v : decrease swap interval\n" " V : increase swap interval\n" " 1-8 : number of fireflies (colored point lights)\n" " r : toggle use of VBO\n" " q : quit\n" "\n"); return GL_TRUE; case '+': addTree(); return GL_TRUE; case '-': if (treePosList.elemCount>1) { Array_pop(&treePosList); } return GL_TRUE; case 'c': Tree_newCharacter(); return GL_TRUE; case 'f': if (!nomenu) { fpsFlag = !fpsFlag; } fpsLogFlag = !fpsLogFlag; return GL_TRUE; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': lightCount = key - '0'; refreshLights(); return GL_TRUE; case 'S': { int polygons = Leaves_polyCount() + Branches_polyCount() + Ground_polyCount(); NvGlDemoLog("frames per second : %d\n", (int)fps); NvGlDemoLog("leaves : %d\n", Leaves_leafCount()); NvGlDemoLog("branches : %d\n", Branches_branchCount()); NvGlDemoLog("polygons per frame : %d\n", polygons); NvGlDemoLog("polygons per second : %d\n", (int)(polygons * fps)); return GL_TRUE; } case 'V': case 'v': { swapInterval += (key == 'V' ? 1 : -1); if (swapInterval < 0) { swapInterval = 0; } NvGlDemoSwapInterval(demoState.display, swapInterval); NvGlDemoLog("swap interval set to: %d\n", swapInterval); return GL_TRUE; } case 'r': Tree_toggleVBO(); return GL_TRUE; } return GL_FALSE; } void Screen_callback( int key, int x, int y) { if (overlayFlag && sliderControl(key)) { return; } if (!overlayFlag && cameraControl(key)) { return; } sceneControl(key); } void Screen_draw(void) { const float s = 0.05f; double aspect = ((double)width)/((double)height); float to_h; int i, j; float h, p, sh, ch, sp, cp; float3 forward_vec, tmp; float scenemvp[16]; float treemvp[16]; // Update the clock tick(); // Clear the buffers glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // // Adjust the camera so view direction shifts towards origin // velocity = velocity * 0.95f; dp = dp * 0.8f; to_h = (float)(ATAN2(-eye[0], eye[2]) * 180.0f/PI); dh = (to_h - heading); while (dh > 180.0f) { dh -= 360.0f; } while (dh <= -180.0f) { dh += 360.0f; } dh *= 20.0f; heading += (float)(dh*dt*0.05f); pitch += (float)(dp*dt*0.05f); h = degToRadF(heading); p = degToRadF(pitch); sh = SIN(h); ch = COS(h); sp = SIN(p); cp = COS(p); forward_vec[0] = sh*cp; forward_vec[1] = sp; forward_vec[2] = -ch*cp; mult_f3f(tmp, forward_vec, velocity); multi_f3f(tmp, (float)dt); addi_f3(eye, tmp); /* clamps */ if (eye[1] < 0.5f) { eye[1] = 0.5f; } if (pitch > 80.0f) { pitch = 80.0f; } if (pitch < -80.0f) { pitch = -80.0f; } // Set up modelview/projection matrix for current camera settings NvGlDemoMatrixIdentity(scenemvp); NvGlDemoMatrixFrustum(scenemvp, -s * ((float)aspect), s * ((float)aspect), -s, s, 0.1f, 1000.0f); NvGlDemoMatrixRotate(scenemvp, pitch, -1.0f, 0.0f, 0.0f); NvGlDemoMatrixRotate(scenemvp, heading, 0.0f, 1.0f, 0.0f); NvGlDemoMatrixTranslate(scenemvp, -eye[0], -eye[1], -eye[2]); NvGlDemoMatrixRotate(scenemvp, -90, 1.0f, 0.0f, 0.0f); // Enable depth testing for the scene glEnable(GL_DEPTH_TEST); // Render the trees and the ground beneath them for (j = 0; j < treePosList.elemCount; j++) { TreePos *treePos = (TreePos*)Array_get(&treePosList, j); Firefly* fireflies = treePos->fireflies; if(dt != 0.0f) { // Update firefly positions for (i=0; ix, treePos->y, 0.0f); NvGlDemoMatrixRotate(treemvp, treePos->angle, 0.0f, 0.0f, 1.0f); // Set up branch/ground shader glUseProgram(prog_solids); glUniformMatrix4fv(uloc_solidsMvpMat, 1, GL_FALSE, treemvp); glUniform3fv(uloc_solidsLightPos, lightCount, fPos); glUniform3fv(uloc_solidsLightCol, lightCount, fColor); // Set up leaf shader glUseProgram(prog_leaves); glUniformMatrix4fv(uloc_leavesMvpMat, 1, GL_FALSE, treemvp); glUniform3fv(uloc_leavesLightPos, lightCount, fPos); glUniform3fv(uloc_leavesLightCol, lightCount, fColor); // Render the tree Tree_draw(); } // Draw the sky if (!nosky) { glUseProgram(prog_simplecol); glUniformMatrix4fv(uloc_simplecolMvpMat, 1, GL_FALSE, scenemvp); glUseProgram(prog_simpletex); glUniformMatrix4fv(uloc_simpletexMvpMat, 1, GL_FALSE, scenemvp); Sky_draw(); } // Fireflies must be rendered after the rest of the scene because // they disable depth mask so as not to self-occlude. glUseProgram(prog_simplecol); for (j = 0; j < treePosList.elemCount; j++) { TreePos *treePos = (TreePos*)Array_get(&treePosList, j); Firefly* fireflies = treePos->fireflies; // Adjust modelview/projection for tree position/orientation MEMCPY(treemvp, scenemvp, sizeof(treemvp)); NvGlDemoMatrixTranslate(treemvp, treePos->x, treePos->y, 0.0f); NvGlDemoMatrixRotate(treemvp, treePos->angle, 0.0f, 0.0f, 1.0f); glUniformMatrix4fv(uloc_simplecolMvpMat, 1, GL_FALSE, treemvp); // Draw fireflies for (i=0; i 0.0) && (currentTime - startTime >= duration); }