But first, it's time to present it to the world!
Sphericles
Sphericles is an audiovisual experiment where you can fly around a space full of user-created objects hunting for "Sphericles", an object that is different from the rest. We've made it as simple as possible to add objects, and the entire thing is done through the editor, giving the user total creative control. We've made tutorials on how to make objects, and we'd be honored if people played with it/made things. NOTE: It requires you to install Unreal 4, which is fortunately free now!
Click these to learn more and see it in action!
INTRODUCTION
HOW TO ADD OBJECTS
GITHUB PAGE
Wrap Ups:
Design
From a design standpoint, it's *there*. We set out to make an interactive audiovisual experience where you fly around, see interesting user-created objects, and try to find the "one" object known as Sphericles.
The only thing I'm not pleased with is that I'd have liked to have more objects, but weaker computers could not run it. Also it was a stretch but I'd have liked to implement a editing tool to simplify the creation and distribution process.
Technology
Working with Unreal 4 was a lot of fun. I'd used it before, so I was pretty comfortable. It was Amier's first time with it, but he caught on extremely quickly and started churning out content. We programmed the entire project in the Blueprint interface, a visual-flow-chart system. Through this we implemented the player, the gamemode, and most importantly the Caplet concept, allowing us to make creative objects out of spheres by dynamically generating between them, and made it fast despite using only the Blueprints.
Sound
Sounds were also interesting. Due to a hard limit (maybe not a hard limit, but it would stop playing sounds) in UE4, we had to limit how many sounds were playing at once. We did this by using a hard-coded distance-check. If the player is within that distance play the sound, and use an attenuation set at run-time to set the volume lower the farther the player is. If the framerate of the user wasn't high enough, the sounds would choke as well. So to keep the creative capabilities robust this demanded more performance improvements. Smart pre-caching of certain values saved some time here.
Caplets
Implementing the objects was an interesting task as well. Each object was comprised of a caplet collection, which contained a list of the edges (which contained vertices that were the spheres). Each frame a tube would be generated between each edge, filling out the object. The math of this is discussed in a slightly unoptimized fashion in previous blog posts.
In order to allow the objects to move, make noise, grow, shrink, and be active we implemented a series of arrays for each sphere that contained locations to move to, sizes to grow/shrink to, and change color. Each frame we interpolate between these and update the outward position/color/size of the sphere. We managed to make it very fast by modulating through the array instead of using a ton of branching to check if our index is past the end, and keeping things relative to the parent object.
Optimizations
Computationally that was expensive, so we came up with tons of interesting ways to limit the amount of "updating" we did in a frame. We limited it by first checking if the object was on-screen, and then by updating less if the object was farther away. We had to remove the hyper-accurate form of on-screen checking, where we update the spheres that were on screen even if other parts of the caplet collection were off screen. However, this took numSpheres number of visibility tests, for which the overhead was not worth it. The LOD-updating algorithm was also not good enough, since it was a CPU optimization it didn't change the performance much on older and less powerful hardware. The image quality hit it took also wasn't worth the meager boosts it gave.
In the end the most important optimizations we made were pre-caching EVERYTHING used more than a couple times, implementing LOD for the amount of triangles in each tube, the visibility checks, and lowering the amount of polygons in the spheres. The spheres we'd been using up until the last week were UE4's default spheres, which had 700-900 triangles! I hopped into blender and made a much simpler UV sphere and integrated it. It's lighting is much more poor, but at least the program can run fast enough on old PCs to even have lighting and anti-aliasing with a reasonable frame-rate.
So you're probably wondering why it's so slow in the first place, to require all these cut-corners and optimizations in 2015 - as were we. We pulled up the profiler and started recording data. We saw most of our time was spent in stalls and generically marked overhead times - and we realized it was the Blueprint interface.
We learned that the Blueprint interface compiles to byte-code which is run on a virtual machine. One of the UE4 engineers said he Blueprint interface executes instructions up to 10 times slower. We were pretty crushed to learn we essentially programmed our entire project in a slightly slower javascript, but at the same time impressed we got so much out of it.
If we could start over we'd implement it all in C++.
Cool Things
We implemented Caplets into Unreal 4, and allowed the objects to move around do interesting things. We also implemented a global scaling factor to scale all the objects to be bigger, as well as passive rotations with a hack-y solution for what I think was gimbal lock. The object's sound fade in and out, and their sound is altered in a custom way to make the experience of flying around more responsive to what you are actually looking at vs. what is near you. Adding objects is also super simple!
The craziest thing to me is that we accomplished everything here in spite of using the Blueprint interface. When we got the first caplet collection object working, drawing tubes, changing color, sizes, and sounds, the framerate was 15 frames per second, for only one object.
Now it can run hundreds and get good framerates with lighting/anti-aliasing on mid-range computers from 2010.
Regrets
We should have used C++ instead of the Blueprint interface. It was hip, cool, and alluring, so we fell for it. It's a shame, because we had already implemented the entire project and couldn't afford to do a full re-write a week before the "end" of the project. You know what they say though, you can never trust an engineer who hasn't had to deal with a poor initial design choice.
We also should have been a little more active in the blog. We changed and rewrote entire systems so often that a blog post made on optimizations would become immediately obsolete.
Anyway, thanks for going on this journey with us, download it off Github and add some objects! Send me your changes through Github and I may add your custom object to the project as a whole!