| @@ -190,6 +190,9 @@ $splash-height: 600px; | ||
| line-height:1.75em; | ||
| margin:1.25em 0; | ||
| } | ||
| ul, ol { | ||
| line-height:1.75em; | ||
| } | ||
| p.overview { | ||
| font-size: 1.25em; | ||
| margin: 1em; | ||
| @@ -0,0 +1,79 @@ | ||
| <!DOCTYPE html> | ||
| <html> | ||
| <% include ../_head %> | ||
| <body> | ||
| <div class='content'> | ||
| <% include ../_header %> | ||
| <section id='main'> | ||
| <article class='readable-section'> | ||
|
|
||
| <section id='project'> | ||
| <a href='/cs488f2011project/app/index.html'> | ||
| <img src='/img/lab/cs488-srpg/screenshot.min.png' style='max-width:100%;' /> | ||
| Launch! | ||
| </a> | ||
| </section> | ||
|
|
||
| <section> | ||
| <h1>CS488 F2011 A5 – WebGL Strategy RPG Engine</h1> | ||
| <p>Originally published December 5, 2011. Last edited September 16, 2015 (adapted for this website's layout).</p> | ||
|
|
||
| <h4>Topics/Keywords</h4> | ||
| <ul class='keywords'> | ||
| <li>Texture mapping</li> | ||
| <li>Texture atlas</li> | ||
| <li>Bump mapping</li> | ||
| <li>Procedural terrain</li> | ||
| <li>Water reflections</li> | ||
| <li>Camera on spline</li> | ||
| <li>Phong shading/lighting model</li> | ||
| <li>WebGL</li> | ||
| <li>OpenGL ES 2.0</li> | ||
| <li>Pathfinding</li> | ||
| <li>Perlin noise</li> | ||
| <li>Procedural content</li> | ||
| <li>Context-free trees</li> | ||
| </ul> | ||
|
|
||
| <h4>Contents</h4> | ||
| <ul class='contents'> | ||
| <li> | ||
| <a href='#introduction'>Introduction</a> | ||
| </li> | ||
| <li> | ||
| <a href='#vision'>Initial Vision</a> | ||
| </li> | ||
| <li> | ||
| <a href='#objectives'>Objectives</a> | ||
| </li> | ||
| <li> | ||
| <a href='#techoutline'>Technical Outline</a> | ||
| </li> | ||
| <li> | ||
| <a href='#caveats'> | ||
| Bugs, Caveats, Cautions | ||
| </a> | ||
| </li> | ||
| <li> | ||
| <a href='#codemap'>Code Map</a> | ||
| </li> | ||
| <li> | ||
| <a href='#references'>References</a> | ||
| </li> | ||
| </ul> | ||
| </section> | ||
|
|
||
| <% include cs488-srpg/_introduction %> | ||
| <% include cs488-srpg/_vision %> | ||
| <% include cs488-srpg/_manual %> | ||
| <% include cs488-srpg/_technical_outline %> | ||
| <% include cs488-srpg/_caveats %> | ||
| <% include cs488-srpg/_future_work %> | ||
| <% include cs488-srpg/_codemap %> | ||
| <% include cs488-srpg/_references %> | ||
| </article> | ||
| </section> | ||
| <% include ../_footer %> | ||
| </div> | ||
| </body> | ||
| </html> |
| @@ -0,0 +1,8 @@ | ||
| <section id='caveats'> | ||
| <h1><a href="#caveats">Bugs / Caveats / Cautions</a></h1> | ||
| <p>If anything breaks or stops working, just try to refresh (F5) the page.</p> | ||
| <p>I'd give the program some time to appear to finish "working" after the execution of the significant animations. Namely, these are the Q,E keys (rotation about spline) and the character MOVE option. This is because JavaScript doesn't really support any kind of threaded model, and instead events are queued in what I would call a ridiculous way: they're all queued at once, but with different timeouts. Queueing up more before the first queue has finished is probably what breaks it.</p> | ||
| <p>Moving with W,A,S,D and then attempting to rotate (in any capacity) will seem to be / is buggy. I'm not happy with the behaviour.</p> | ||
| <p>Point sprite characters: If you zoom in too much, they will become very small. They should be a constant size, but this is surprisingly harder than I thought it'd be. At least they stop growing after a certain point when zooming out!</p> | ||
| <p>Texture atlasing with OpenGL2.0 is a pain. If I used the nearest neighbour texture filtering, you would not see those lines that appear on the terrain where one texture bleeds into the next. However, it also looks more like crap. Texture Arrays are not supported, and texture atlasing, I have concluded, is the work of a demon.</p> | ||
| </section> |
| @@ -0,0 +1,34 @@ | ||
| <section id='codemap'> | ||
| <h1><a href="#codemap">Code Map</a></h1> | ||
| <h2>Third-party code</h2> | ||
| <p> | ||
| <strong>jQuery:</strong> | ||
| Was very useful in the UI related aspects, binding events to keydown, mouseenter, click, and so on. | ||
| </p> | ||
| <p> | ||
| <strong>jquery.mousewheel:</strong> | ||
| Another library that might not provide complete support for all browsers. I suppose mouse scrolling is something that isn't very well supported by the browsers? | ||
| </p> | ||
| <p> | ||
| <strong>gl-matrix-1.1.js:</strong> | ||
| Basically, the JS port of the algebra library. I did not want to port the entire algebra library if it already existed. | ||
| </p> | ||
| <h2>Scene</h2> | ||
| <p> | ||
| <strong>scene.js:</strong> | ||
| A skeleton for a scene. | ||
| </p> | ||
| <p> | ||
| <strong>small_grassy_game.js, big_terrain.js:</strong> | ||
| The content of these files are executed depending on which option you select in the select box. These are probably what you might want to play with. | ||
| </p> | ||
| <h2>Application relevant</h2> | ||
| <p> | ||
| <strong>perlin.js:</strong> | ||
| CPU implementation of improved Perlin noise. Consulted in the LUT generation phase of terrain. This is a port of Ken Perlin's original code that I did a while back. | ||
| </p> | ||
| <p> | ||
| <strong>*.js:</strong> | ||
| All other files have descriptive names of the feature sets they implement. Please see the files themselves for more documentation. | ||
| </p> | ||
| </section> |
| @@ -0,0 +1,12 @@ | ||
| <section id='futurework'> | ||
| <h1><a href="#futurework">Future Work</a></h1> | ||
| <ul> | ||
| <li>Networking could very easily be achieved by using the browser's suite of networking tools to provide for saved game storage and multiplayer.</li> | ||
| <li>HTML5 Local Storage might be used to store the results of any significant computations, such as the look-up tables used by the noise or current game state.</li> | ||
| <li>Application source update delivery would be trivial by simply changing the pages involved.</li> | ||
| <li>The scope of the project should be kept 'rather light' so as to avoid horrendous situations like repeatedly transmitting complex meshes over HTML.</li> | ||
| <li>Use a spline with convex-hull property for camera animation and character movement</li> | ||
| <li>For the reflection map, reject those items that are below the water surface. This would probably be best accomplished through creation of a new shader, and this new shader could have intensive features like bump-mapping disabled.</li> | ||
| <li>Tessellate the trees, perhaps sampling 1 circle randomly and using that as a piecewise approximation to a cylinder. Use this circle as the top and bottom face for each of the segments.</li> | ||
| </ul> | ||
| </section> |
| @@ -0,0 +1,9 @@ | ||
| <section id='introduction'> | ||
| <h1><a href="#introduction">Introduction</a></h1> | ||
| <p>For the final project of CS488 offered by the University of Waterloo, the students are instructed to set 10 computer graphics related objectives they need to achieve. The instructor and TAs review this objective list and interact with the students to determine if it is sufficiently difficult enough to implement. I'll discuss the objectives I chose here, those I accomplished, those I could not, and those I chose not to implement and why.</p> | ||
| <p> | ||
| To the reader: I do aim for correctness, but unfortunately cannot guarantee it. If you'd like to e-mail me at | ||
| <a href='mailto:ar.cameron@gmail.com'>ar.cameron@gmail.com</a> | ||
| I'd be glad to respond to any concerns regarding accuracy. | ||
| </p> | ||
| </section> |
| @@ -0,0 +1,45 @@ | ||
| <section id='manual'> | ||
| <h1><a href="#manual">User Manual</a></h1> | ||
| <h3>Running the application</h3> | ||
| <ol> | ||
| <li> | ||
| Simplest: visit | ||
| <a href='/cs488f2011project/app/index.html' target="_blank">this page</a> | ||
| while running the latest Chrome or Firefox | ||
| </li> | ||
| <li> | ||
| Via local files: it would be nice to download all the files and just double-click the index.html and have it run, no? | ||
| However, due to browser security policy CORS, this isn't possible without doing something else. | ||
| <ul> | ||
| <li> | ||
| Either start up a local proxy or local web server to host the files, and work through that | ||
| </li> | ||
| <li> | ||
| Or: Find out how to disable web security for your browser of choice. In Chrome, this is "--disable-web-security" flag provided to the executable. | ||
| </li> | ||
| </ul> | ||
| Without doing at least one of those things, you will be unable to see textures. | ||
| </li> | ||
| </ol> | ||
| <h3>Using the application</h3> | ||
| <ul> | ||
| <li> | ||
| Q, E keys will rotate the camera about the various corners of the game. You may want to wait for one rotation to finish before pressing it again! | ||
| </li> | ||
| <li> | ||
| W, A, S, D keys will move the camera in space | ||
| </li> | ||
| <li> | ||
| Mouse towards top/bottom of game area will change camera elevation | ||
| </li> | ||
| <li> | ||
| Mouse towards left/right of game area will rotate camera | ||
| </li> | ||
| <li> | ||
| Mouse wheel scroll will zoom in/out | ||
| </li> | ||
| <li> | ||
| When the game is active, and the menu is up, you can select Move/Attack/etc. However, only Move works. Press Enter to being picking your move, then use the arrow keys to select a destination, and enter to finalize your move. | ||
| </li> | ||
| </ul> | ||
| </section> |
| @@ -0,0 +1,29 @@ | ||
| <section id='objectives'> | ||
| <h1><a href="#objectives">Objectives</a></h1> | ||
| <ol class='objectives'> | ||
| <li class='achieved'> | ||
| <a href='#obj1'>Bump mapping</a> | ||
| </li> | ||
| <li class='achieved'> | ||
| <a href='#obj2'>Texturing</a> | ||
| </li> | ||
| <li class='notachieved'>Screen-space ambient occlusion</li> | ||
| <li class='notachieved'>Particle effects, animated</li> | ||
| <li class='achieved'> | ||
| <a href='#obj5'>Procedural terrain modeling via combinations of noise and blending functions</a> | ||
| </li> | ||
| <li class='notachieved'>Procedural architecture via CFG</li> | ||
| <li class='achieved'> | ||
| <a href='#obj7'>Camera animation on splines</a> | ||
| </li> | ||
| <li class='achieved'> | ||
| <a href='#obj8'>AI pathing</a> | ||
| </li> | ||
| <li class='achieved'> | ||
| <a href='#obj9'>Reflections on water surfaces</a> | ||
| </li> | ||
| <li class='achieved'> | ||
| <a href='#obj10'>Trees</a> | ||
| </li> | ||
| </ol> | ||
| </section> |
| @@ -0,0 +1,64 @@ | ||
| <section id='references'> | ||
| <h1><a href="#references">References & Acknowledgements</a></h1> | ||
| <p>The following were of use to me in the implementation of this project. I'd like to thank these authors for their great work, and apologize to anyone I forgot to mention here; I've probably forgot many in my haste.</p> | ||
| <h2>Papers</h2> | ||
| <ul> | ||
| <li>Blinn, J F. (1978). Simulation of wrinkled surfaces. ACM SIGGRAPH Computer Graphics, 12(3).</li> | ||
| <li>Gustavson, S. (2005, March 22). Simplex noise demystified.</li> | ||
| <li>Vlachos, A. Isidoro, J. Oat, C. Rippling Reflective and Refractive Water. ATI Research.</li> | ||
| </ul> | ||
| <h2>Web References</h2> | ||
| <ul> | ||
| <li> | ||
| Cameron, A. Procedural Textures. | ||
| <a href='http://anthonycameron.com/lab#texture'>http://anthonycameron.com/lab#texture</a> | ||
| </li> | ||
| <li> | ||
| Finch, M. Effective Water Simulation from Physical Models. GPU Gems. | ||
| <a href='http://http.developer.nvidia.com/GPUGems/gpugems_ch01.html'></a> | ||
| </li> | ||
| <li> | ||
| Using textures in WebGL. Mozilla Developer Network. | ||
| <a href='https://developer.mozilla.org/en/WebGL/Using_textures_in_WebGL'>https://developer.mozilla.org/en/WebGL/Using_textures_in_WebGL</a> | ||
| </li> | ||
| <li> | ||
| WebGL Lesson 16 – rendering to textures | ||
| <a href='http://learningwebgl.com/blog/?p=1786'>http://learningwebgl.com/blog/?p=1786</a> | ||
| </li> | ||
| <li> | ||
| Screen Space Ambient Occlusion Shader | ||
| <a href='http://www.coniserver.net/wiki/index.php/Screen_Space_Ambient_Occlusion_Shader'>http://www.coniserver.net/wiki/index.php/Screen_Space_Ambient_Occlusion_Shader</a> | ||
| </li> | ||
| <li> | ||
| Bourke, P. (2000, January). | ||
| <a href='http://paulbourke.net/texture_colour/perlin/'>Perlin noise and turbulence.</a> | ||
| </li> | ||
| <li> | ||
| Procedural Content Generation Wiki | ||
| <a href='http://pcg.wikidot.com/'>http://pcg.wikidot.com/</a> | ||
| </li> | ||
| <li> | ||
| Perlin, K. | ||
| Implementing Improved Perlin Noise. GPU Gems. | ||
| <a href='http://http.developer.nvidia.com/GPUGems/gpugems_ch05.html'>http://http.developer.nvidia.com/GPUGems/gpugems_ch05.html</a> | ||
| </li> | ||
| <li> | ||
| Green, S. | ||
| Implementing Improved Perlin Noise. GPU Gems. | ||
| <a href='http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter26.html'>http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter26.html</a> | ||
| </li> | ||
| <li> | ||
| Young, S. | ||
| Terrains, Part 1, | ||
| <a href='http://www.shamusyoung.com/twentysidedtale/?p=141'>http://www.shamusyoung.com/twentysidedtale/?p=141</a> | ||
| </li> | ||
| <li> | ||
| Minecraft Like Rendering Experiments in OpenGL 4, | ||
| <a href='http://codeflow.org/entries/2010/dec/09/minecraft-like-rendering-experiments-in-opengl-4/'>http://codeflow.org/entries/2010/dec/09/minecraft-like-rendering-experiments-in-opengl-4/</a> | ||
| </li> | ||
| <li> | ||
| More on Minecraft-type world gen, | ||
| <a href='http://www.gamedev.net/blog/33/entry-2227887-more-on-minecraft-type-world-gen/'>http://www.gamedev.net/blog/33/entry-2227887-more-on-minecraft-type-world-gen/</a> | ||
| </li> | ||
| </ul> | ||
| </section> |
| @@ -0,0 +1,38 @@ | ||
| <section id='techoutline'> | ||
| <h1><a href="#techoutline">Technical Outline</a></h1> | ||
| <h2 id='obj1'>Bump Mapping</h2> | ||
| <p>Terrain bump mapping is accomplished by consulting 2 near neighbours for the corresponding fragment. These differentials are then used, with a scaling factor, to perturb the surface normal in tangent-bitangent space. This process occurs in the fragment program.</p> | ||
| <p>Water bump mapping is accomplished via Phong normal interpolation. This process occurs in the vertex program. The tangent-bitangent space is not necessary as the water is planar, so the normal is purely modified and then interpolated for fragment processing.</p> | ||
| <h2 id='obj2'>Texturing</h2> | ||
| <p>Terrain texture is consulted by means of a texture atlas. I would have liked to use a Texture Array or even a 3D Texture, but these features were unavailable in this GL context. Depending on an attribute signaling which type of block it is, the texture UV coordinates are altered to index into the correct texture. Texture atlasing was a major pain for me, so you can still see some colour bleeding due to the texture interpolation mode.</p> | ||
| <p>Sprite texturing is a straight-forward case of consulting a texture. Nothing special is happening here, except alpha blending to hide the rectangularity of the sprite. I would have also liked to use atlases here and animate the sprites, but there was not enough time.</p> | ||
| <p> | ||
| To perform water displacement, I consult 2 textures, the gradients and the permutations of Perlin's improved noise in the vertex program. This is a parallel-processing port of his normal algorithm that allows me to calculate the noise in real-time. I've ripped the basic technology from my earlier work at | ||
| <a href='http://anthonycameron.com/lab#perlinshader'>http://anthonycameron.com/lab#perlinshader</a> | ||
| </p> | ||
| <p>These same gradient/permutation textures may be used in other places, for example, to add some more fine detail in the texture/normals of the waves. Or, it could be used as a basis for more procedural textures.</p> | ||
| <h2 id='obj5'>Procedural terrain modeling via combinations of noise and blending functions</h2> | ||
| <p>3D improved Perlin noise and a blending function are combined to produce what resembles a terrain. The noise is continuous, so it must be discretized. The discretized results are stored in a look-up table (LUT), of resolution^3 size, where resolution is developer specifiable. This LUT is consulted for tessellation of terrain. A clever algorithm is employed to minimize the hidden surfaces that effectively result as we carve out the landscape in discretized chunks.</p> | ||
| <h2 id='obj7'>Camera animation on splines</h2> | ||
| <p>2 Catmull-Rom splines are created. The first, position, is just simply a linear interpolator, so I'm not sure why I even used a spline. I suppose the motivation is to allow for changing it to something else in the future. The second spline, velocity, modulates the evaluation of the first, and the end result is a camera that moves somewhat more fluidly than pure linear interpolation. To note: my splines are limited to just 4 control points.</p> | ||
| <h2 id='obj8'>AI pathing</h2> | ||
| <p>You can see the pathing system at work with the 2 characters on the game board when you select the MOVE option.</p> | ||
| <p>When the game starts, it consults the LUT of the terrain to determine which positions are "standable". A position is standable if it is air and below it is ground. More complex rules would allow for checks to character height – perhaps another constraint would be to make sure there is n blocks of headroom for the character to occupy.</p> | ||
| <p>The result is a map in [x,z] of same dimensions as the [x,y,z] LUT of the terrain. In each square is an array of >=0 possible y-values that a character might occupy. I call this map the pseudo-heightmap.</p> | ||
| <p>To path find, a feeler is sent out from character position [a,b,c] to the 4 immediately adjacent game tiles. Each feeler might add >1 path segment to the currently generated path, for example, if there are >1 possible standable spots for that particular [x,z] point. The process repeats recursively from the newly added segments.</p> | ||
| <p>A segment is only added to the path under some specific stipulations. First, it must not already exist in the path as a shorter path. Secondly, it must be within the character's jump height. There are some other stipulations, but please refer to game.js if you're curious.</p> | ||
| <p>The recursion halts after a maximum number of "steps" has occurred, here referred to as the character.MOVE_DISTANCE.</p> | ||
| <p>This pathing system guarantees that, between 2 squares that are game-legal for a character, the character will generate the shortest path.</p> | ||
| <p>I've used the splines I created for the camera system to animate the position of the moving character. I put a slight bias on the first and last control point, depending on the relative heights of where he is going to and coming from, so as to give the appearance of him jumping.</p> | ||
| <h2 id='obj9'>Reflections on water surfaces</h2> | ||
| <p>The terrain is flipped about the plane that represents the water. Then, it is translated by the world-height of the water plane. It is rendered to an off-screen render buffer of 512x512. The same viewport dimensions are used.</p> | ||
| <p>This off-screen render buffer is then consulted as a texture in the water fragment shader. The texture coordinates are calculated in screenspace: as gl_FragCoord.x / 1024.0 and gl_FragCoord.y / 1024.0. I'm not certain why 1024 seemed to provide the best results, because I would have thought dividing by the context viewport dimensions would yield UV in [0..1]. I have a hypothesis that it may be related to 1024 being a factor of 2 more than the render target resolution, namely 512x512.</p> | ||
| <p>The texture coordinates are then altered by the interpolated water normal. This allows the reflected scene to appear to warp and bend, which we expect to see when we look at turbulent water. The water is then shaded as per Phong shading.</p> | ||
| <p>I'm pretty sure the water reflections are highly hackish, but they produced somewhat compelling results.</p> | ||
| <h2 id='obj10'>Trees</h2> | ||
| <p>I wanted to create some kind of context-free tree. I didn't actually go about created a grammar or anything of the sort. I just defined a few simple rules. I will now describe these rules to you, because I feel that is the best way to explain these trees.</p> | ||
| <p>A root segment is created with a defined number of iterations. Some simple rules are used to recursively generate the rest of the segments. In my demonstration, all segments are the same length.</p> | ||
| <p>The branching recursion terminates if the max iterations has been reached. N branches are added. N is calculated randomly, but is biased by the lifetime of the tree. For instance, as the iteration count increases, the maximum possible value of N decreases. For each new branch, 2 random rotations are applied to the up vector [0,1,0] to produce something like a conical distribution. The extent of these rotations can be controlled on a per-axis basis. The new segment is produced, with starting point equal to the ending point of the previous segment. The new segment's ending point is determined by this randomly rotated vector.</p> | ||
| <p>If you remove the randomness, it easily creates a fractal tree. If you remove the conical distribution and reduce the extent of the rotation, it easily creates a fractal fern.</p> | ||
| <p>The tree branches are rendered with colours determined by their segment iteration. The higher the iteration, the more the channel green contributes to the colour.</p> | ||
| </section> |
| @@ -0,0 +1,21 @@ | ||
| <section id='vision'> | ||
| <h1><a href="#vision">Initial Vision</a></h1> | ||
| <p> | ||
| You can view my proposal | ||
| <a href='/cs488f2011project/img/proposal2.pdf'>here.</a> | ||
| For purposes of this report, I'll focus on those objectives I did accomplish, as well as provide some discussion as to how those I did not implement might be attained. | ||
| </p> | ||
| <p> | ||
| The inspiration for this style of game were others like | ||
| <em>Final Fantasy Tactics,</em> | ||
| <em>Ogre Tactics,</em> | ||
| and | ||
| <em>Disgaea.</em> | ||
| Please see those games for the type of gameplay I anticipated this engine being useful for. | ||
| </p> | ||
| <p>However, I wanted to somehow bring something of my own to this genre. Many of these games would seem to suffer from needing to have maps completely designed by humans. I figured that it would be nice to allow the developer to perhaps offload some of this work to random noise, perhaps tweaking the random terrain. It was my hope that intriguing and believable landscapes could be created with very little or no modeling hours.</p> | ||
| <p>I also wanted this engine to be playable on just about any computer without the need to install or update. To me, WebGL seemed perfect for this: as long as the user has a browser that supports the technology, and adoption of such browsers will only increase going into the future, then it should be very easy to extend this project to be a playable game.</p> | ||
| <p>I also wanted to explore real-time rendering techniques and interactive modes by making an entirely GPU shader driven project. I had heard great things about what they could be used to accomplish and have definitely come to respect their</p> | ||
| <p>Since the environment is WebGL, the focus then is (hopefully) doing things in a smart way to provide interactive framerates in this reduced environment. There is a constant struggle, as many allowed OpenGL features are not available in the current WebGL context. Of note, Texture Arrays, and even GL_QUADS were unavailable to me.</p> | ||
| <p>Finally, I initially started this in C++. I quickly became frustrated with the lack of easy anonymous functions and re-compiling. JavaScript, while perhaps hundreds of times slower, allowed me to develop this prototype as quickly as I did.</p> | ||
| </section> |
| @@ -0,0 +1,146 @@ | ||
| <!DOCTYPE html> | ||
| <html> | ||
| <% include _head %> | ||
| <body id='resume'> | ||
| <div class='readable-section'> | ||
| <section id='title'> | ||
| <h1>Hi, I'm Anthony Cameron</h1> | ||
| <h2>I'm a software engineer with a passion for the web and computer graphics.</h2> | ||
| <div class='meta'> | ||
| <a href='mailto: ar.cameron@gmail.com'>ar.cameron@gmail.com</a> | ||
| <br> | ||
| <a href='http://anthonycameron.com'>http://anthonycameron.com</a> | ||
| <br> | ||
| <a href='https://github.com/qq99'>https://github.com/qq99</a> | ||
| </div> | ||
| </section> | ||
| <section id='work-experience'> | ||
| <h4 class='timeline-title'>Timeline</h4> | ||
| <h3>Work Experience</h3> | ||
| <div class='job'> | ||
| <div class='position'> | ||
| <h4 class='job-title'>Software Developer</h4> | ||
| <h4 class='employer'> | ||
| <a href='https://www.shopify.com/' target='_blank' title='Shopify homepage'>Shopify</a> | ||
| </h4> | ||
| </div> | ||
| <div class='duration'> | ||
| <div class='from'>Oct 2013</div> | ||
| <div class='to'>Now</div> | ||
| </div> | ||
| <p> | ||
| I work primarily on the admin team – the section store owners use – implementing features, fixing bugs, filing issues, and writing maintenance tasks. Since Shopify encourages a strong culture of ownership, I'm always looking out for potential problems regarding user experience across project and team boundaries. | ||
| </p> | ||
| <p> | ||
| I worked with the client-side MVC framework | ||
| <a href='http://batmanjs.org/' target='_blank' title='Batman.js homepage'>Batman.js</a> | ||
| before we started to move to a custom in-house server-client hybrid approach. This launched me from no previous CoffeeScript experience to relative expertise, imparting in me a thorough love of the language. | ||
| </p> | ||
| <p>My role shifted from primarily front-end to full-stack in our transition: As a team of 8~10, we've developed a powerful system that allows the server to be the primary source of truth in rendering, but still gives us enough flexibility that at first glance, you might think we've made a single-page app. It drastically reduced our mean development time for new features, and greatly simplified bug fixes. Everybody loves it, and we're all quite proud of it :)</p> | ||
| <p>Shopify is the largest company (and codebase) I've ever worked for, allowing me to improve my collaboration skills with both my immediate team, and even some people I've never met in person. I've been called the "powerhouse of the issues list". There's a lot more to say, but not much space here!</p> | ||
| </div> | ||
| <div class='job'> | ||
| <div class='position'> | ||
| <h4 class='job-title'>UI Developer</h4> | ||
| <h4 class='employer'> | ||
| <a href='http://www.exinda.com/' target='_blank' title='Exinda Networks homepage'>Exinda Networks</a> | ||
| </h4> | ||
| </div> | ||
| <div class='duration'> | ||
| <div class='from'>Aug 2013</div> | ||
| <div class='to'>Aug 2012</div> | ||
| </div> | ||
| <p>At Exinda, I was working on a large-scope single-page JavaScript application. We used Backbone to structure our core application, Underscore and jQuery for developer convenience, and Handlebars and i18n.js for templating. I was deeply involved in the design and implementation of many interactive modules (e.g., line and bar graphs) at the core of the application. We unit tested our frontend with Sinon, Chai, Mocha, and Jasmine. I wrote many acceptance tests using Cucumber (Capybara & Watir).</p> | ||
| <p>It was at Exinda that I first got really involved with JavaScript-based developer utilities: node, bbb, jshint, and many others. I wrote an intercepting proxy in Ruby to aid with manual testing when targeting different versions of our RESTful API. Combined with Apache2 load balancers, this proxy also enabled us to simulate low-latency and unreliable-link scenarios while developing UI components, discovering bugs preemptively.</p> | ||
| <p>I was responsible for the creation of a set of utilities that added dummy data for demonstration purposes. These utilities were also used for creating fixtures for acceptance tests. Git became a part of my daily workflow; I could not do without it.</p> | ||
| </div> | ||
| <div class='job'> | ||
| <div class='position'> | ||
| <h4 class='job-title'>Freelance Web Developer</h4> | ||
| <h4 class='employer'> | ||
| <a href='http://www.uptowncreativeinc.com' target='_blank' title="Uptown Creative Inc's homepage">Uptown Creative Inc,</a> | ||
| ShirtsByMe, others | ||
| </h4> | ||
| </div> | ||
| <div class='duration'> | ||
| <div class='from'>Dec 2009</div> | ||
| <div class='to'>Aug 2012</div> | ||
| </div> | ||
| <p>Uptown Creative Group specializes in providing clean, cross-browser, interactive HTML5 websites. During my time there, I participated in the creation of many static and CMS-driven sites, with a heavy focus on interactive maps, dynamic map searches, Yelp and weather integration. I also tailored 3rd party feeds, themed iHomeFinder sites, and provided integration for search, filtering, and pagination of MLS results.</p> | ||
| <p>As a freelance web developer, I was responsible for the entirety of the development phase: I received a set of design documents, and from there I created semantic HTML5 mark-up using HAML (to insure well-formedness), sensible and minified CSS with the SASS compiler, and image spritesheets for anything that couldn't be done without them.</p> | ||
| <p>jQuery was employed heavily on nearly every site for interactive widgets, image lazy loading, and more. From this shell template, we moved on to PHP or WordPress, depending on client needs.</p> | ||
| <p>In this time, I improved my web accessibility knowledge by creating responsive layouts and mobile-aware sites. While the sites I create greatly benefit from JavaScript, I make sure to always provide room for sensible graceful degradation.</p> | ||
| </div> | ||
| <div class='job'> | ||
| <div class='position'> | ||
| <h4 class='job-title'>Web Developer (co-op)</h4> | ||
| <h4 class='employer'>University of Waterloo, Special Projects Group</h4> | ||
| </div> | ||
| <div class='duration'> | ||
| <div class='from'>Jan 2009</div> | ||
| <div class='to'>Apr 2009</div> | ||
| </div> | ||
| <p> | ||
| At the University of Waterloo, I participated in a small team of around 6 students and 6 full-time developers, working under an agile methodology. Daily scrum is no stranger to me! We had fluid teamwork, using Microsoft Team Foundation Server for version control. We developed pages for an application in C#.net, with a heavy focus on AJAX. During this time, I took initiative to learn jQuery UI design principals, and as a result, created a fully functional widget to replace the | ||
| <code><select></code> | ||
| element. | ||
| </p> | ||
| </div> | ||
| <div class='job'> | ||
| <em>I've had a few other co-op positions, but I've omitted them here.</em> | ||
| </div> | ||
| </section> | ||
| <section id='education'> | ||
| <h3>Education</h3> | ||
| <div class='position'> | ||
| <h4 class='job-title'>Bachelor of Computer Science, Honours, Co-op</h4> | ||
| <h4 class='employer'>University of Waterloo</h4> | ||
| </div> | ||
| <p> | ||
| <strong>Key Courses:</strong> | ||
| Introduction to Computer Graphics, Medical Image Processing, Distributed Systems, Introduction to Artificial Intelligence, Computer Networks, Numerical Computation, Algorithms, Computer Security and Privacy, Operating Systems | ||
| </p> | ||
| <p> | ||
| <strong>Awards:</strong> | ||
| University of Waterloo Merit Scholarship, 2006 | ||
| </p> | ||
| </section> | ||
| <section id='projects'> | ||
| <h3>Personal Projects & Open Source</h3> | ||
| <p>In my spare time, I'm either playing video games, spending time with my wife, or working on software.</p> | ||
| <h4>echoplexus</h4> | ||
| <p> | ||
| <a href='https://github.com/qq99/echoplexus' target='_blank' title='echoplexus on GitHub'>echoplexus</a> | ||
| is perhaps my most substantial and successful personal project to date. It aims to provide web-based anonymous chat, collaborative coding and drawing, as well as WebRTC audio & video calls. Please see | ||
| <a href='https://echoplex.us' target='_blank' title='echoplexus homepage'>https://echoplex.us</a> | ||
| for a full description of all features! | ||
| </p> | ||
| <h4 style='text-transform: lowercase;'>μv</h4> | ||
| <p> | ||
| <a href='https://github.com/qq99/muvee' target='_blank' title='μv on GitHub'>μv</a> | ||
| (pronounced mew-vee) is a Rails 4 application that I've recently started working on in hopes of replacing | ||
| <a href='http://xbmc.org/' target='_blank' title='XBMC homepage'>XBMC</a> | ||
| as my home streaming system. Right now, it's able to query external datasources ( | ||
| <a href='http://thetvdb.com/' target='_blank' title='thetvdb.com homepage'>thetvdb.com,</a> | ||
| <a href='http://www.omdbapi.com/' target='_blank' title='omdb homepage'>omdb</a> | ||
| ), gather metadata, download posters, and generate screenshots from video files. It groups episodes by series and displays a Netflix-esque interface, streaming video down to any web client in your home. | ||
| </p> | ||
| <h4>Others</h4> | ||
| <p> | ||
| Please visit | ||
| <a href='http://anthonycameron.com' target='_blank' title="Anthony Cameron's homepage">http://anthonycameron.com</a> | ||
| for a picturesque overview of some things I've worked on. | ||
| </p> | ||
| <p> | ||
| I also contribute to OSS (my own and others) | ||
| <a href='https://github.com/qq99?tab=repositories' target='_blank' title='My work on GitHub'>on GitHub,</a> | ||
| and write at | ||
| <a href='https://blog.echoplex.us' target='_blank' title='echoplexus blog'>https://blog.echoplex.us.</a> | ||
| </p> | ||
| </section> | ||
| </div> | ||
| </body> | ||
| <div class='scripts'> | ||
| <% include _ga %> | ||
| </div> | ||
| </html> |