Object4D - Adding native animation to THREE.js

Tuesday, December 17, 2013 Posted by Josh Staples 0 comments
In some of my development, I've had to figure out a way to animate a number of different kind of objects synchronously between geospatial locations.  Additionally, I have to sometimes stop, redirect or delete a mesh.

In other words, PubSub animation for THREE.js.

DEMO of Object4D

GitHub Code

Initially I tried TWEEN.js but quickly found that it didn't provide the kind of micro-control I needed in my objects.  In lieu of TWEEN.js, I ended up creating a dozen or so animation objects for each kind of mesh I had to animate.   After creating these classes, I quickly realized they were all essentially the same and I could easily extend the THREE.Object3D class and create a new one called Object4D.

Here is what Object4D looks like:

After creating an Object4D object, I also created a manager for the animated objects.  The manager passes a clock delta to each object between frame renders.   Each Object4D element updates it's own position based upon the latter clock delta, it's speed, movement vector and target location.   In essence, these objects are self-tweening.   Unlike many tweening approaches, you can easily stop or interrupt the animation and even re-direct the target movement mid-tween.   The demo above demonstrates this behaviour.  Here is a Gist of the manager class:

In short, it works great for my purposes and I think it is pretty clever to extend the base Object3D class.   Additionally, it makes it really easy to extend the animation functionality just by modifying the base Object4D class.

Cached Marquee Selection with THREE.js

Thursday, December 5, 2013 Posted by Josh Staples 0 comments
In my previous post on marquee selection I demonstrated how to marquee select a bunch of elements within a WebGL canvas using THREE.js.  While the approach works great, once you start loading a larger number of meshes into the scene, the approach slows to a crawl.

To resolve this slowdown, I created a caching object that takes all unprojected 3D coordinates and associated meshes and throws them into an array.   This allows us to circumvent gobs of math and processing required to unproject rays and do a comparison during each mouse event.  Using this approach, the cache is created on the first mousemove event (you could pre-create it as well) and subsequently used in all future mousemove events until the "dirty" flag is set.

The speedup is incredible.   Easily 1000 cubes in real time.   If you bump that up to 10,000 cubes, disabled cache use is impossible and there is only a slight lag on the marquee selection with the cache enabled.

One MAJOR caveat:
  • If the camera moves, the cache is worthless.  You'll need to recreate it.
In that regard, I added a "dirty" flag so when the camera does move you can tell the cache and it will re-create the cache on the first mousemove event.   I haven't experimented too much with a moving camera (my current work's 3D environment is pretty static) but it should still provide a pretty good speedup even within a dynamic scene.

FYI, I also included an "active" flag in the cache.   This was included so you can turn the cache on and off from the browser.

Here is a demo

Here is a Gist of the Cache object:

3D Tic-Tac-Toe with Ray Intersection via THREE.js

Wednesday, December 4, 2013 Posted by Josh Staples 0 comments
I have wanted to create a 3D tic-tac-toe game for years but was always put out by my ham-fisted approach to determining a winner. When resolving an issue at work the other day I realized I could put together a cool demo using rays and the raycaster object of THREE.js. See the demo here:  3D Tic-Tac-Toe Demo

3D tic tac toe using rays
While click interactions with THREE.js are old-hat by now, I thought it would be interesting to use rays and intersections to determine the winner.

In short I've created 49 rays that are checked after each user cube selection.  In my demo, I also created THREE.js ArrowHelpers to demonstrate the ray placement.  Here is a Gist showing the ray creation:

After setting up all the rays, the last step was to loop through an array of the rays and inspect each ray's individual collisions. If every intersected object has been selected by a single user, a winner is declared and the game is over.    Please note that intersected objects are not Object3D meshes but rather the faces of each individual mesh. In other words, we aren't looking for 3 sequential user intersections but rather twice that number.

The code is pretty modular and wouldn't take much work to make this demo work for  a 4x4x4 or NxNxN tic-tac-toe grid.   Feel free to check out the code at Github.

Marquee Selection with THREE.js

Tuesday, November 26, 2013 Posted by Josh Staples 1 comments
While click interaction is great, the real bees knees with THREE.js is to drag a selection box around a bunch of meshes.

Before you read further, please take a look at the demo.

EDIT:   I have a new post that demonstrates how to speed up large selections with a cache.  When you finish this article, check out this one as well: Cached Marquee Selection.

So, what data do we drag around and how do we figure out what is selected?

Drag select example

What mechanism do we use to do the selection comparison?

  1. Project a bunch of rays into the scene
    • Pro:
      • Bound to hit something if you pack the rays closely enough
    • Con:
      • Too many rays and objects make this really, really slow
      • Rays packed too loosely will miss some intersections
  2. Unproject 3D coordinate data and do a 2D comparison
    • Pro:
      • Much faster comparison
      • Only unproject the 3D coordinates once (if the scene/camera is static)
    • Con:
      • Limited to point data unless you do a lot more work
So after a little experimentation, unprojecting the 3D data into 2D space and then doing a simple "within bounds" comparison is much, much faster.

In that regard, we have two kinds of data to unproject.   We could use both types but that is too correct of an answer.  For discussion's sake, let's say we can only use a single kind of data.

What coordinate data do we compare to the marquee coordinates?

  1. Centroids
    • Pro: 
      • Single centroid for each mesh
    • Con:
      • You have to drag around the exact center of the mesh
      • Dragging around non-centroid areas will not result in a selection
  2. Vertices
    • Pro:
      • 8 vertices is a lot more data than a single centroid
      • Encapsulates the area of a volume a little better.  You can drag around corners
    • Con:
      • Still not perfect.  You could drag through a mesh and not hit any corners
      • Lots more data.  Might get duplicate intersections

Marquee drag select process:

  1. Detect drag area
    1. Detect the mousedown event location (one corner of the selection rectangle)
    2. Detect the mousemove event location (the other corner of the selection rectangle)
    3. On the mouseup event, record the selection rectangle coordinates and clear the marquee
  2. Unproject all 3D meshes within the scene into 2D canvas coordinates
  3. Compare unprojected 3D coordinates to the marquee selection area
    • The selection area has to take the canvas offset into consideration for this latter comparison
  4. If the unprojected 3D coordinates are within the marquee selection area, the color of the "selected" mesh is changed to a random color
    • The cubes will change colors to illustrate selection
Drag select with THREE.js

Here is a Gist of the process:

THREE.js Ray Intersection with a page offset

Posted by Josh Staples 0 comments
One consideration you'll have to make when projecting rays, clicks, swipes or otherwise interacting with the Canvas element is the OFFSET.   The mouse X,Y event clicks need to be adjusted by the Canvas offset.

Here is a demo.

canvasX = mouse.clientX - canvas.offset.left;
canvasY = mouse.clientY - canvas.offset.top;

If you don't take this offset into consideration, you'll be projecting a ray at the wrong location and won't intersect anything!   Very very frustrating if your code works and you're not getting good results.

Here is a gist of the code offset:
Labels: , , ,

Ray Intersection with THREE.js

Posted by Josh Staples 0 comments

Rays & Intersections

The first step is to create a simple scene with a few cube meshes.

Here is a demo.
    Simple 3D scene with cubes.
    Step 1:  Create a simple scene with cubes

The next step is to add an event listener and callback to deal with the ray projection and like.  Check out the following Gist code here:
Labels: ,

JavaScript and WebGL in the East Bay

Monday, November 25, 2013 Posted by Josh Staples 0 comments

Greetings from the East Bay.  

I am a WebGL JavaScript R&D Software Engineer at Navis, LLC located at Jack London Square in Oakland, California.

Navis provides a software product to shipping terminals around the world.   The Terminal Operating System (TOS) facilitates the organization and movement of shipping containers in over 250 ports around the world.

In short, Navis provide the "smarts" for moving TRILLIONS of dollars worth of cargo around the world.
In WebGL short, we move cubes around the world. :)   Translation and rotation!

Recently I gave a presentation at the WebGL Meetup in San Francisco.

Poorly Converted Slidedeck:

Video of the Presentation:

    NOTE:  The sound and picture at the latter are a bit off as it was filmed via a webcam.

One of reasons I've started this coding blog is to demonstrate some of the work I am doing in JavaScript and WebGL in the East Bay.  In that regard, I hope to provide solid code examples for my fellow WebGL developers.

Feel free to take a look at all of my code at my Github account:  https://github.com/cubicleDowns

In the near future, I hope to present the following topics:
  1. Ray Intersection
  2. Ray Intersection with a DIV offset.
  3. Marquee Selection of 3D objects (with a DIV offset)
  4. Marquee Selection with caching.

Happy Coding!
- Josh Staples