-
-
Save dom96/485c7a97472d31dc6b6e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import opengl, glfw3/glfw3, math, unsigned, strutils | |
| type | |
| TPt = object | |
| x, y, z, vx, vy, vz, r, life: float64 | |
| bis: bool | |
| TVertex = object | |
| pos: array[3, GLfloat] | |
| normal: array[3, GLfloat] | |
| const | |
| PrintFrames = true | |
| Title = "ParticleBench" | |
| Width = 800 | |
| Height = 600 | |
| MinX = -80 | |
| MaxX = 80 | |
| MinY = -90 | |
| MaxY = 50 | |
| MinDepth = 50 | |
| MaxDepth = 250 | |
| StartRange = 15 | |
| StartX = (MinX + (MinX+MaxX)/2) | |
| StartY = MaxY | |
| StartDepth = (MinDepth + (MinDepth+MaxDepth)/2) | |
| PointsPerSec = 2000 | |
| MaxInitVel = 7 | |
| MaxLife = 5000 | |
| MaxScale = 4 | |
| WindChange = 2000 | |
| MaxWind = 3 | |
| SpawnInterval = 0.01 | |
| RunningTime = ((MaxLife div 1000) * 5) | |
| MaxPts = RunningTime * PointsPerSec | |
| NumVertices = 24 | |
| NumNormals = NumVertices / 4 | |
| var | |
| initT = 0.0 | |
| endT = 0.0 | |
| frameDur = 0.0 | |
| spwnTmr = 0.0 | |
| cleanupTmr = 0.0 | |
| runTmr = 0.0 | |
| frames: array[RunningTime * 1000, float64] | |
| curFrame = 0 | |
| pts: array[MaxPts, TPt] | |
| vertices: array[NumVertices, TVertex] | |
| numPts = 0 | |
| minPt = 0 | |
| seed = 1234569'u32 | |
| curVertex = 0 | |
| curNormal = 0 | |
| gVBO: GLuint = 0 | |
| windX = 0.0'f64 | |
| windY = 0.0'f64 | |
| windZ = 0.0'f64 | |
| gravity = 0.0'f64 | |
| ambient = [Glfloat(0.8), 0.05, 0.1, 1.0] | |
| diffuse = [Glfloat(1.0), 1.0, 1.0, 1.0] | |
| lightPos = [GlFloat(MinX + (MaxX-MinX)/2), MaxY, MinDepth, 0] | |
| proc newVertex(x, y, z: GLfloat) = | |
| var thisVertex: TVertex | |
| thisVertex.pos[0] = x | |
| thisVertex.pos[1] = y | |
| thisVertex.pos[2] = z | |
| vertices[curVertex] = thisVertex | |
| curVertex.inc | |
| proc newNormal(nx, ny, nz: GLfloat) = | |
| for i in curNormal * 4 .. <((curNormal + 1) * 4): | |
| vertices[i].normal[0] = nx | |
| vertices[i].normal[1] = ny | |
| vertices[i].normal[2] = nz | |
| curNormal.inc | |
| proc xorRand: uint32 = | |
| seed = seed xor (seed shl 13) | |
| seed = seed xor (seed shr 17) | |
| seed = seed xor (seed shl 5) | |
| return seed | |
| proc movePts(secs: float64) = | |
| for i in minPt .. numPts: | |
| if not pts[i].bis: | |
| continue | |
| pts[i].x += pts[i].vx * secs | |
| pts[i].y += pts[i].vy * secs | |
| pts[i].z += pts[i].vz * secs | |
| pts[i].vx += windX * 1 / pts[i].r | |
| pts[i].vy += windY * 1 / pts[i].r | |
| pts[i].vy -= gravity | |
| pts[i].vz += windZ * 1 / pts[i].r | |
| pts[i].life -= secs | |
| if pts[i].life <= 0: | |
| pts[i].bis = false | |
| proc spawnPts(secs: float64) = | |
| let num = secs * PointsPerSec | |
| for i in 0 .. <num.int: | |
| var pt = TPt( | |
| x: 0 + float64(xorRand() mod START_RANGE) - START_RANGE/2, | |
| y: startY, | |
| z: startDepth + float64(xorRand() mod START_RANGE) - START_RANGE/2, | |
| vx: float64(xorRand() mod MaxInitVel), | |
| vy: float64(xorRand() mod MaxInitVel), | |
| vz: float64(xorRand() mod MaxInitVel), | |
| r: float64(xorRand() mod (MAX_SCALE*100)) / 200, | |
| life: float64(xorRand() mod MaxLife) / 1000, | |
| bis: true | |
| ) | |
| pts[numPts] = pt | |
| numPts.inc | |
| proc doWind() = | |
| windX += ( float64(xorRand() mod WIND_CHANGE)/WIND_CHANGE - WIND_CHANGE/2000) * frameDur | |
| windY += ( float64(xorRand() mod WIND_CHANGE)/WIND_CHANGE - WIND_CHANGE/2000) * frameDur | |
| windZ += ( float64(xorRand() mod WIND_CHANGE)/WIND_CHANGE - WIND_CHANGE/2000) * frameDur | |
| if abs(windX) > MAX_WIND: | |
| windX = windX * -0.5 | |
| if abs(windY) > MAX_WIND: | |
| windY *= -0.5 | |
| if abs(windZ) > MAX_WIND: | |
| windZ *= -0.5 | |
| proc checkColls() = | |
| for i in minPt .. numPts: | |
| if not pts[i].bis: | |
| continue | |
| if Pts[i].X < MIN_X: | |
| Pts[i].X = MIN_X + Pts[i].R | |
| Pts[i].VX *= -1.1 # These particles are magic; they accelerate by 10% at every bounce off the bounding box | |
| if Pts[i].X > MAX_X: | |
| Pts[i].X = MAX_X - Pts[i].R | |
| Pts[i].VX *= -1.1 | |
| if Pts[i].Y < MIN_Y: | |
| Pts[i].Y = MIN_Y + Pts[i].R | |
| Pts[i].VY *= -1.1 | |
| if Pts[i].Y > MAX_Y: | |
| Pts[i].Y = MAX_Y - Pts[i].R | |
| Pts[i].VY *= -1.1 | |
| if Pts[i].Z < MIN_DEPTH: | |
| Pts[i].Z = MIN_DEPTH + Pts[i].R | |
| Pts[i].VZ *= -1.1 | |
| if Pts[i].Z > MAX_DEPTH: | |
| Pts[i].Z = MAX_DEPTH - Pts[i].R | |
| Pts[i].VZ *= -1.1 | |
| proc cleanupPtPool = | |
| for i in minPt .. numPts: | |
| if Pts[i].bis: | |
| minPt = i | |
| break | |
| proc initScene = | |
| glEnable(GL_DEPTH_TEST) | |
| glEnable(GL_LIGHTING) | |
| glClearColor(0.1, 0.1, 0.6, 1.0) | |
| glClearDepth(1) | |
| glDepthFunc(GL_LEQUAL) | |
| glLightfv(GL_LIGHT0, GL_AMBIENT, addr ambient[0]) | |
| glLightfv(GL_LIGHT0, GL_DIFFUSE, addr diffuse[0]) | |
| glLightfv(GL_LIGHT0, GL_POSITION, addr lightPos[0]) | |
| glEnable(GL_LIGHT0) | |
| glViewport(0, 0, WIDTH, HEIGHT) | |
| glMatrixMode(GL_PROJECTION) | |
| glLoadIdentity() | |
| glFrustum(-1, 1, -1, 1, 1.0, 1000.0) | |
| glRotatef(20, 1, 0, 0) | |
| glMatrixMode(GL_MODELVIEW) | |
| glLoadIdentity() | |
| glPushMatrix() | |
| template offsetof(typ, field): expr = (var dummy: typ; cast[int](addr(dummy.field)) - cast[int](addr(dummy))) | |
| proc loadCubeToGPU: bool = | |
| newVertex(-1, -1, 1) | |
| newVertex(1, -1, 1) | |
| newVertex(1, 1, 1) | |
| newVertex(-1, 1, 1) | |
| newNormal(0, 0, 1) | |
| newVertex(-1, -1, -1) | |
| newVertex(-1, 1, -1) | |
| newVertex(1, 1, -1) | |
| newVertex(1, -1, -1) | |
| newNormal(0, 0, -1) | |
| newVertex(-1, 1, -1) | |
| newVertex(-1, 1, 1) | |
| newVertex(1, 1, 1) | |
| newVertex(1, 1, -1) | |
| newNormal(0, 1, 0) | |
| newVertex(-1, -1, -1) | |
| newVertex(1, -1, -1) | |
| newVertex(1, -1, 1) | |
| newVertex(-1, -1, 1) | |
| newNormal(0, -1, 0) | |
| newVertex(1, -1, -1) | |
| newVertex(1, 1, -1) | |
| newVertex(1, 1, 1) | |
| newVertex(1, -1, 1) | |
| newNormal(1, 0, 0) | |
| newVertex(-1, -1, -1) | |
| newVertex(-1, -1, 1) | |
| newVertex(-1, 1, 1) | |
| newVertex(-1, 1, -1) | |
| newNormal(-1, 0, 0) | |
| glGenBuffers(1, addr gVBO) | |
| glBindBuffer(GL_ARRAY_BUFFER, gVBO) | |
| glBufferData(GL_ARRAY_BUFFER, GLsizeiptr(NUM_VERTICES * sizeof(TVertex)), addr vertices[0], GL_STATIC_DRAW) | |
| glEnableClientState(GL_VERTEX_ARRAY) | |
| glEnableClientState(GL_NORMAL_ARRAY) | |
| glVertexPointer(3, cGL_FLOAT, sizeof(TVertex).glsizei, nil) | |
| glNormalPointer(cGL_FLOAT, sizeof(TVertex).glsizei, cast[pglvoid](offsetof(TVertex, normal))) | |
| return true | |
| proc cleanupBuffers = | |
| glDeleteBuffers( 1, addr gVBO) | |
| glDisableClientState( GL_NORMAL_ARRAY ) | |
| glDisableClientState( GL_VERTEX_ARRAY ) | |
| proc renderPts = | |
| for i in minPt .. numPts: | |
| if (Pts[i].bis == false): | |
| continue | |
| var pt: ptr TPt = addr pts[i] | |
| glMatrixMode(GL_MODELVIEW) | |
| glPopMatrix() | |
| glPushMatrix() | |
| glTranslatef(pt.X, pt.Y, -(pt.Z)) | |
| glScalef(pt.R * 2, pt.R * 2, pt.R * 2) | |
| glColor4f(0.7, 0.9, 0.2, 1) | |
| glDrawArrays(GL_QUADS, 0, NUM_VERTICES) | |
| when isMainModule: | |
| init() | |
| glfwWindowHint(GLFW_SAMPLES, 2) | |
| glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2) | |
| glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1) | |
| var window = newWin((Width.positive, Height.positive), Title) | |
| window.setCurrentContext() | |
| glfwSwapInterval(0) | |
| loadExtensions() | |
| initScene() | |
| let version = window.glVersion() | |
| if version.ver < glv21: | |
| quit("OpenGL 2 not supported.") | |
| discard loadCubeToGPU() | |
| while not shouldClose(window): | |
| initT = glfwGetTime() | |
| movePts(frameDur) | |
| doWind() | |
| if (spwnTmr >= SPAWN_INTERVAL): | |
| spawnPts(SPAWN_INTERVAL) | |
| spwnTmr -= SPAWN_INTERVAL | |
| if (cleanupTmr >= MAX_LIFE/1000): | |
| cleanupPtPool() | |
| cleanupTmr = 0 | |
| checkColls() | |
| glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT) | |
| renderPts() | |
| window.swapBufs() | |
| pollEvents() | |
| endT = glfwGetTime() | |
| frameDur = endT-initT | |
| spwnTmr += frameDur | |
| cleanupTmr += frameDur | |
| runTmr += frameDur | |
| if (runTmr > MAX_LIFE/1000): | |
| frames[curFrame] = frameDur | |
| curFrame += 1 | |
| if (runTmr >= RUNNING_TIME): | |
| var sum = 0'f64 | |
| for i in 0 .. <curFrame: | |
| sum += frames[i] | |
| var mean = sum / curFrame.float64 | |
| echo("Average framerate was: $1 frames per second." % formatFloat(1/mean)) | |
| var sumDiffs = 0.0 | |
| for i in 0 .. <curFrame: | |
| sumDiffs += pow((1/frames[i])-(1/mean), 2) | |
| var variance = sumDiffs / curFrame.float64 | |
| var sd = sqrt(variance) | |
| echo("The standard deviation was: $1 frames per second." % sd.formatFloat) | |
| if PRINT_FRAMES: | |
| stdout.write("--:") | |
| for i in 0 .. <curFrame: | |
| stdout.write(formatFloat(1/frames[i])) | |
| stdout.write(",") | |
| stdout.write(".--") | |
| break | |
| cleanupBuffers() | |
| window.destroy() | |
| glfwTerminate() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment