`client.dll`

source code, there's a file called `StudoModelRenderer.cpp`

. It handles the drawing of all the `.mdl`

models in the game.Each time the engine wants to draw a model, it calls the function

`StudioDrawModel`

. This function then works out where the model is, where all the bones of its skeleton should go, whether there are any special effects that need applying, and finally draws it.Needless to say, having access to all this code gives us a great opportunity to do special effects!

For this article, I'll be looking at something simple - scaling. I added the following code at the very end of

`StudioSetupTransform`

(a function in `StudioModelRenderer.cpp`

which decides where the model will be drawn on the screen):`if (m_pCurrentEntity->curstate.scale != 0){ int j; for (i = 0; i < 3; i++) for (j = 0; j < 3; j++) { (*m_protationmatrix)[i][j] *= m_pCurrentEntity->curstate.scale; } }}`

That's it. Add this code, compile, and you've finished! To test it, you just need to make a monster in Worldcraft, turn off Smartedit and give it a "scale" setting of 2.0 (double size) or 0.5 (half size), or whatever.

`m_pCurrentEntity->curstate`

is the current state of the entity we're supposed to be drawing. The "scale" variable is one of the properties that make up that state. (I didn't create it - it's been there all along, although previously it was only used by sprites. I'm using it here because having a pre-existing variable makes the example nice and simple. And it seems sensible to reuse it for such a similar purpose.)So, that's the simple stuff out of the way. What are these "for" loops doing? Multiplying up the numbers in

`m_protationmatrix`

.To understand that, we need to talk a little bit about matrices. Yeah, matrices - the seemingly pointless things you hated (or, for the younger audience,

A matrix is a rectangular grid of numbers.

`m_protationmatrix`

is a matrix with 4 columns and 3 rows, and its 12 numbers are used to define exactly where a model will appear on the screen. Don't let the name fool you: it can express all sorts of transformations, not just a rotation. It comes in two parts:`a b c xd e f yg h i z`

Numbers in the first three columns are used to scale, rotate and/or skew the model. (I'll explain how in a minute.) Numbers in the right-hand column simply define where the model is in 3d space.

In the

`StudioSetupTransform`

function, there's a good example of how the last column can be used - the very first and very last things it does.`void CStudioModelRenderer::StudioSetUpTransform (int trivial_accept){//...some variables... vec3_t modelpos; VectorCopy( m_pCurrentEntity->origin, modelpos );//...the rest of the function... (*m_protationmatrix)[0][3] = modelpos[0]; (*m_protationmatrix)[1][3] = modelpos[1]; (*m_protationmatrix)[2][3] = modelpos[2];}`

As I hope you can see, this code first saves the entity's origin into the

`modelpos`

vector, and at the end, copies that vector directly into the last column of the matrix. Yes, it really is that simple to use.Sadly, the same isn't true of the other three columns. To understand how those work, let's talk about matrices.

Here's how you multiply a 3-by-3 matrix by a 3d vector, to produce a new 3d vector:

`[a b c] [x] [?][d e f] * [y] = [?][g h i] [z] [?]`

`[a b c]`

2) Multiply the first number in that line by the first number in the vector, multiply the second by the second, and so on.

`[ax by cz]`

3) Add up the results, and write the answer as the first entry in the result vector.

`[ax+by+cz][ ? ][ ? ]`

`[a b c] [x] [ax+by+cz][d e f] * [y] = [dx+ey+fz][g h i] [z] [gx+hy+iz]`

So for example, with a matrix like this...`[1 0 0][1 0 0][1 0 0]`

...each line says "stir in 100% of the vector's x value, and 0% of the other two values". Let's see how that works on a vector containing a bunch of random numbers:

`[1 0 0] [453] [1*453 + 0*7 + 0*99] [453][1 0 0] * [ 7] = [1*453 + 0*7 + 0*99] = [453][1 0 0] [ 99] [1*453 + 0*7 + 0*99] [453]`

Now, here's a related one:`[1 0 0][0 1 0][0 0 1]`

Here, the first line says "use 100% of x, and nothing else". The second line says "use 100% of y, and nothing else". The third line says "use 100% of z, and nothing else".

Guess what the result is -

`[1 0 0] [453] [1*453 + 0*7 + 0*99] [453][0 1 0] * [ 7] = [0*453 + 1*7 + 0*99] = [ 7][0 0 1] [ 99] [0*453 + 0*7 + 1*99] [ 99]`

Ok. How about if we wanted to rearrange the components of a vector? (don't ask me why...)
`[0 1 0] [453] [0*453 + 1*7 + 0*99] [ 7][0 0 1] * [ 7] = [0*453 + 0*7 + 1*99] = [ 99][1 0 0] [ 99] [1*453 + 0*7 + 0*99] [453]`

`[2 0 0] [453] [2*453 + 0*7 + 0*99] [906][0 2 0] * [ 7] = [0*453 + 2*7 + 0*99] = [ 14][0 0 2] [ 99] [0*453 + 0*7 + 2*99] [198]`

So, two pages later, we finally get to see why the scaling code works. Half-Life supplies me with a matrix, I multiply each entry by the scale factor, and as a result, when the model gets drawn, each bit is scaled up! (note that the scaling will be relative to its origin - which for most monsters is at its feet, but not for all).I hope this was helpful. I'll go into more interesting effects in a future article - assuming people are interested...

You must log in to post a comment. You can login or register a new account.