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

linux, art, music, anti-religious screeds, and rants. Oh yes, there will be rants.

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

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.

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

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!”

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.

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:

- http://github.com/smcameron/space-nerds-in-space/blob/master/turret_aimer.h
- http://github.com/smcameron/space-nerds-in-space/blob/master/turret_aimer.c

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. */

January 2, 2017 — Star Wars has its Death Star, Star Trek has its Borg Cube, its high time Space Nerds In Space had its Big Honking Death Machine, which is what I’ve been working on lately.

The simplest possible approach would have been to build a 3D model much as I’ve done previously and just put it in. But I wanted the players to be able to interact with this thing in a way that you can’t interact with a simple model, at least the way they are currently implemented. I wanted players to be able to fly around and even inside this giant thing, and bump into the solid parts but fly through the empty parts. Doing that with a normal 3D model is pretty hard, too hard for me at the moment. It occurred to me that if I could make a kind of generic “block” with arbitrary height, width, and depth and orientation and I could get texture mapping and more importantly collision detection working with such a block, then I could arrange such “block” objects in a hierarchy, with relative positions and orientations and in this way be able to construct large “ships” out of many of these “block” objects — much like LEGOs, but without the alignment constraints. And so that’s what I’ve done.

Each of the blocks uses the same mesh, a unit cube, but each block has its own non-uniform scaling factors for x, y, and z. The collision detection is done using “oriented bounding boxes”, as described in Christer Ericsson’s book, Real-Time Collision Detection. The collision resolution is very simple minded at the moment. In the detection phase, the “closest” point on the surface of the block is identified, and a vector is constructed from this point to the player’s ship and the ship is “pushed back” a fixed amount in this direction and the velocity is set to zero. This mostly works, though it doesn’t look or feel all that natural, and once in a while penetration of walls may occur.

## Recent Comments