Space Monsters!

•February 26, 2018 • Leave a Comment

February 26, 2018 — I’ve been working on adding Space Monsters into Space Nerds In Space.


I had worked on this before, but in my previous implementation, all the work of generating the space monster’s tentacle movements was done on the server side, and a lot of per-tentacle-segment information was transmitted from the server to each client, which was not a very good design, and I had never pushed any of this code to the main repository.  This new implementation avoids that problem. The motion of the tentacles is random. On the server side, a new per-monster seed value is periodically generated and transmitted to the clients. The clients use this to seed a per-monster Mersenne Twister which is then used to generate new desired tentacle segment joint angles, and the monster’s tentacles are gradually moved to conform to these new desired angles. The result is that multiple clients will see the monsters with consistent tentacle positions which are all genereted client side, with only the seed generated on the server and transmitted over the network. It seems to work quite well.

The space monsters are made from two meshes, one for the head or torso, and one for each tentacle segment. The albedo and emittance textures are static, and are derived from Gray Scott reaction diffusion patterns. The variable lighting effect is done by passing an emittance intensity value to the shader which is used to modulate the emittance color values.

Some problems remain. The tentacles do not avoid each other, and may pass through one another. I think this is a pretty hard problem to solve without unduly restricting each tentacle’s range of motion.

I still need to work on the behavior of the space monsters. Right now, they periodically choose a new random direction and change course to begin moving in that direction, and that is the extent of it. When velocity is low, this can produce rapid and very unnatural looking rotations, as only the overall velocity is controlled, while the orientation is simply derived from the velocity by making the space monster’s mouth face in the direction of motion.

Space Nerds In Space Navigation Tutorial

•December 4, 2017 • Leave a Comment

December 4, 2017 — Here’s a tutorial about how the Navigation screen works in Space Nerds In Space.

On Creativity

•October 20, 2017 • Leave a Comment

October 19, 2017

One time, a long long time ago, more than quarter century ago actually, a friend and I were talking, and he asked me, “Do you think of yourself as a creative person?” And I said, “Sure, of course.” He said, “So, on a scale of 1 to 10, how creative?” And I said, “I guess about 8 or 9.” After that, the topic of conversation drifted to something else. But the next morning, and for many days after, I couldn’t stop thinking about this conversation, and I thought to myself, “8 or 9, huh? So you think you’re more creative than 80 or 90 percent of people? Ok, if you’re so creative, Mr. 8 or 9, then where’s all the stuff you’ve created?” And I didn’t really have a good answer. And this conversation and this question has stuck with me through all these years, “If you’re so creative, then where’s all the stuff you’ve created?” And it’s kind of served as something to drive me to complete things even when I’ve become a bit bored of them. And now, many years later, I have quite a few things that I’ve created that I can point to and am proud of, and if it weren’t for that conversation way back then, I strongly suspect this wouldn’t be nearly so true.

Adding a Real Time Strategy game into Space Nerds In Space

•October 19, 2017 • Leave a Comment

October 18, 2017 — Adding a Real Time Strategy game into Space Nerds in Space.

Leaving Google

•October 12, 2017 • 1 Comment

October 12, 2017 — So I’ve left Google. Last Friday, Oct 6, 2017 was my last day working there. It was a very interesting and overall a super positive experience working at Google for the last few years, but still, I’ve left. Why? A few reasons:

  • I’ve managed to save up enough money that I don’t actually need a job at all anymore, with reasonable but not lavish expenses. I will need to leave the Bay Area pretty soon of course, since rent is pretty lavish around these parts. I’ll miss the weather, and the close access to the gorgeousness of California for sure.
  • While the first 1.5 years or so at Google were super interesting, mainly learning how all the Google stuff works, after awhile once the novelty wore off it became less interesting — not easy by any stretch — but not really so interesting, and quite often a bit tedious. And if it’s difficult but not really that interesting, and I don’t even actually need a job, why am I working so hard at this again? I couldn’t really answer that question.
  • Time is one thing you cannot buy more of. I’m getting a little old. If I wait until I’m 65 to retire, well, I might not even live that long, who knows? It’s easy to think, “Well, if I stay one more year, I can save a bit more…” But, if I can safely retire now, I figure I probably should. Tomorrow is guaranteed to no man. It will be nice to have more time to spend with family, and to do things that a job generally gets in the way of doing. If I can swing it, why not? I couldn’t think of a good reason why not.

Since I’ll be leaving California soon, and since it’s October already and the weather will be getting cold soon, especially in the mountains, I figured I should drive out to King’s Canyon and Sequoia National Parks while I still have the chance to do so easily and cheaply. So this past Sunday I loaded up the car and hit the road. I spent three nights camping there, driving around seeing the sights, and doing a bit of hiking. This turned out to be a fantastic idea, and I’m very glad I did it. Here are a few pictures from my trip to King’s Canyon and Sequoia National Parks.

So here I am, retired. Pretty strange feeling, though I’m sure I’ll get used to it. Today is the first day that has this strange kind of feeling, as it is the first weekday I’ve had where I’m just kind of sitting around the apartment, not going to work (the trip to King’s Canyon felt more like vacation, so not really so strange, just different.) Went for a walk in the early morning, trying to soak up a bit more of this fantastic crisp blue-sky California weather before I have to leave. I’ll probably spend some time playing guitar later (something I’ve neglected a bit over the past couple years) and work on Space Nerds In Space some more (also neglected over the last couple years.) I’ve given myself all this extra time, I need to be a little wary of frittering it away in meaningless ways.

“No Squandering!”

Tour of a Procedurally Generated Solar System

•February 1, 2017 • Leave a Comment

2017-01-31 — Here’s a little tour of a procedurally generated solar system in my little hobby project, an open source multiplayer networked starship bridge simulator game, Space Nerds In Space.

Code for 3D turret aiming

•January 15, 2017 • Leave a Comment

January 14, 2017 — Say you have a 3D space game, with NPC ships that have turrets on them, and the turrets can be oriented any which way on those ships. How do you make the turrets swivel around and aim smoothly and intelligently?

Note: The code in these examples is released under the GPL v2 license.  See:

Step 1: Understand your turret model.

In my case, my turret model is oriented such that it shoots down the positive X axis, and can rotate around the vertical Y axis and rotate around the horizontal Z axis. This is the same canonical orientation used by all the models in my game — the “unrotated state” has from the model’s point of view, down the X axis being straight ahead, positive Y as “up” and positive Z to the right.

Step 2. Understanding the turret’s state.

  • The turret has a location (x, y, z)
  • The turret has a “rest orientation” This is the orientation when the turret is in an unrotated state, but perhaps attached to some other object in a way that gives this state some arbitrary non-constant rotation that is different than the unrotated state the identity quaternion represents.
  • The turret has a “current orientation” This is the “rest orientation” plus some other arbitrary orientation accounting for where ever the turret might currently be pointed.
  • The turret cannot rotate infinitely fast. There is some limit to how much the turret can rotate in each of its two axes per unit time.

Step 3. What do we want to be able to do?

We’d like to be able to ask the turret to aim at something, and get a new orientation that obeys the constraints of a turret (not freely rotating, but only rotating on two axes) and allowing the turret to be attached to something.

So let’s say we’d like a C function something like this:

struct turret_params {
        float elevation_rate_limit;     /* how much can turret move in one step in radians */
        float azimuth_rate_limit;

/* union quat is a quaternion */

union quat *turret_aim(double target_x, double target_y, double target_z,       /* in: world coord position of target */
                        double turret_x, double turret_y, double turret_z,      /* in: world coord position of turret */
                        union quat *turret_rest_orientation,                    /* in: orientation of turret at rest */
                        union quat *current_turret_orientation,                 /* in: Current orientation of turret */
                        const struct turret_params *turret,                     /* in: turret params, can be NULL */
                        union quat *new_turret_orientation);                    /* out: new orientation of turret */

So how do we write such a function? Here’s one approach.

1. Transform the target into the turret’s coordinate space
2. Calculate the desired azimuth and elevation angles
3. Calculate the turret’s current azimuth and elevation
4. Calculate the delta between current and desired azimuth and elevation
5. clamp these deltas to the maximum angular rate the turret can move
6. Apply deltas to current elevation and azimuth
7. Convert new elevation and azimuth to the new orientation quaternion

So, going through these one by one:

Step 0. Declare some variables we’ll need:

        union vec3 yaw_axis = { { 0.0, 1.0, 0.0 } };
        union quat yaw, pitch, inverse_rest;
        union vec3 pitch_axis;
        union vec3 to_target, to_current_aim;
        float current_azimuth, azimuth;
        float current_elevation, elevation;
        float delta_elevation, delta_azimuth;
        float xzdist;

Step 1. Transform the target into the turret’s coordinate space
(because we want to make step 2 easier)

	/* Compute vector from turret to target */
	union vec3 to_target;
        to_target.v.x = target_x - turret_x;
        to_target.v.y = target_y - turret_y;
        to_target.v.z = target_z - turret_z;		
	/* Compute inverse rotation from turret rest orientation */
        quat_inverse(&inverse_rest, turret_rest_orientation);
	/* Convert target into turret's coordinate space */
	quat_rot_vec_self(&to_target, &inverse_rest);

Step 2. Calculate the desired azimuth and elevation angles:

        azimuth = atan2f(-to_target.v.z, to_target.v.x);
        xzdist = sqrtf(to_target.v.z * to_target.v.z + to_target.v.x * to_target.v.x);
        elevation = atan2f(to_target.v.y, xzdist);

At this point, you could skip to step 7, and the turret would snap instantly
to the correct orientation — but we want it to move smoothly.

Step 3. Calculate the turret’s current azimuth and elevation

        to_current_aim.v.x = 1.0; /* vector pointing down the x-axis */
        to_current_aim.v.y = 0.0;
        to_current_aim.v.z = 0.0;

	/* Rotate it into the current turret's orientation -- now the vector
	 * points in the same direction as the turret points in world coord
	 * system.
        quat_rot_vec_self(&to_current_aim, current_turret_orientation);

	/* Now convert into the turret's local coord system */
        quat_rot_vec_self(&to_current_aim, &inverse_rest);

	/* Now calculate current azimuth and elevation of the turret */
        current_azimuth = atan2f(-to_current_aim.v.z, to_current_aim.v.x);
        xzdist = sqrtf(to_current_aim.v.z * to_current_aim.v.z + to_current_aim.v.x * to_current_aim.v.x);
        current_elevation = atan2f(to_current_aim.v.y, xzdist);

Step 4. Calculate the delta between current and desired azimuth and elevation

        delta_elevation = elevation - current_elevation;
        delta_azimuth = azimuth - current_azimuth;

Step 5. Clamp these deltas to the maximum angular rate the turret can move in both axes.

        delta_elevation = elevation - current_elevation;
        delta_azimuth = azimuth - current_azimuth;
        if (delta_azimuth > turret->azimuth_rate_limit)
                delta_azimuth = turret->azimuth_rate_limit;
        else if (delta_azimuth < -turret->azimuth_rate_limit)
                delta_azimuth = -turret->azimuth_rate_limit;
        if (delta_elevation > turret->elevation_rate_limit)
                delta_elevation = turret->elevation_rate_limit;
        else if (delta_elevation < -turret->elevation_rate_limit)
                delta_elevation = -turret->elevation_rate_limit;

Step 6. Apply deltas to current elevation and azimuth

        azimuth = current_azimuth + delta_azimuth;
        elevation = current_elevation + delta_elevation;

Step 7. Convert new elevation and azimuth to the new orientation quaternion

	/* Recall yaw_axis is a vector pointing up the Y axis.  Rotate it
	 * into the turret's coordinate space, and build a yaw quaternion
	 * from this rotated yaw_axis and the azimuth
        quat_rot_vec_self(&yaw_axis, turret_rest_orientation);
        quat_init_axis(&yaw, yaw_axis.v.x, yaw_axis.v.y, yaw_axis.v.z, azimuth);
	/* Rotate the turret_rest_quaternion by the yaw quaternion */
        quat_mul(new_turret_orientation, &yaw, turret_rest_orientation);

	/* Set up a pitch axis -- around the Z axis */
        pitch_axis.v.x = 0.0;
        pitch_axis.v.y = 0.0;
        pitch_axis.v.z = 1.0;

	/* Rotate it into the turret's coordinate space (which has the yaw
	 * incorporated already now) and build a pitch quaternion out of the
	 * rotated pitch axis and elevation
        quat_rot_vec_self(&pitch_axis, new_turret_orientation);
        quat_init_axis(&pitch, pitch_axis.v.x, pitch_axis.v.y, pitch_axis.v.z, elevation);
	/* Rotate the turret orientation by the pitch quaternion */
        quat_mul(new_turret_orientation, &pitch, new_turret_orientation);

        return new_turret_orientation; /* and we're done. */