Wayback Machine
OCT FEB JUL
Previous capture 5 Next capture
2006 2007 2008
6 captures
18 Feb 06 - 12 Aug 07
sparklines
Close Help
  home · browse · search · game entities · user directory · message board · IRC | register

February 4, 2007, 9:19 pm PST
username  
password  
forgot password?

Popular Resources
  • Half-Life 2 Mod FAQ
  • Valve Hammer Editor
  • Hammer 3.5 beta test
  • Half-Life Utilities
  • game data files
  • ZHLT 2.5.3 custom build
  • Half-Life SDK
  • Feedback
    If you've got any feedback, suggestions, or bugs to report regarding the Collective website, go here!

  • Feedback (302)
  • Newsletter
     
    Enter your email address in the above form to add yourself to the email newsletter list. Click here for more info.

    Hosted Sites
  • Valve ERC
  • Collective
  • TFMapped
  • Spirit of Half-Life
  • Selective Design
  • Pixel Reviews
  • recent articles

    NPC and Item Placement Theory
    17/03/05 11:35pm PST
    Non-Player Character (NPC) and item placement can influence both the gameflow and immersion of a level. This article aims to give some pointers on how to properly place them.
    - Hugh 'Hugh' Lloyd

    Got Props?
    13/03/05 08:32am PST
    A common problem in HL2 mapping is props not showing up in game. This article explains why and offers solutions.
    - Jeff 'Yesukai' Pritchard

    Simulating Randomness
    18/12/04 11:29pm PST
    This article focuses on how to properly simulate random events that should occur at a certain average frequency, or within a certain probability per period of time.
    - Skyler 'Zipster' York

    Adding Single-Player Weapons to Half-Life 2
    15/12/04 06:52pm PST
    Covers the process behind adding weapons to a single-player Half-Life 2 modification.
    - Skyler 'Zipster' York

    Your world in HL2
    06/12/04 12:17am PST
    This article gives tips and advice to anyone wanting to make custom photorealistic textures to be used in Half-Life 2.
    - Oksid

    Hiding in Shadow
    21/08/04 01:11pm PDT
    Describes how to create a function that has monsters disregard you if you are hiding in a certain level of "darkness," which can be set from within map properties.
    - Anders [Wolf] Jenbo (NoBody)

    XSI EXP for Half-Life 2 Tutorial - Camera Control
    23/09/04 12:43am PDT
    A SOFTIMAGE|XSI tutorial explaining all of the camera controls available to you in XSI!
    - Josh Enes

    Bump Mapping in Half-Life
    08/08/04 11:58am PDT
    Details a method of achieving real-time bump mapping in Half-Life, and provides an implementation of the algorithm.
    - Francis 'DeathWish' Woodhouse

    Real-Time "TRON 2.0" Glow For Low-Spec Hardware
    19/06/04 02:06pm PDT
    A sequel to the original "Real-Time 'TRON 2.0' Glow" article, this describes how to implement real-time glow that works on low-spec graphics cards.
    - Francis 'DeathWish' Woodhouse

    Hitboxes and Code
    05/06/04 06:25pm PDT
    How do I make only one part of a monster take damage? Learn about the relationship between model hitboxes and what you can do with them in a characters code.
    - Jonathan 'Teh_Freak' Smith

    Dynamically Resizing Models
    [Mon Mar 03, 2003 / 12:26pm PST] Laurie Cheers - comments (20) comments enabled

    In the 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.

    So, how does this actually work?
    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, will hate) in maths lessons. Well, this is the moment where you get to learn what matrices are actually for!

    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 x
    d e f y
    g 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] [?]
    1) Take the top line of the first matrix:
    [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]
    [ ? ]
    [ ? ]
    Then, repeat this process for the second row of the matrix, and put the result in as the second entry in the result vector. And then do the same for the third.
    [a b c]   [x]   [ax+by+cz]
    [d e f] * [y] = [dx+ey+fz]
    [g h i] [z] [gx+hy+iz]
    With me still? Once you get your head around it, you'll hopefully realise that the idea of a matrix is actually quite simple: it's like a recipe. Our ingredients are x, y and z from the vector; the matrix is telling us how much of each ingredient we need to stir into a given part of the result.

    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]
    Since the three lines in the matrix were the same, we got the same answer for each entry in the result.
    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]
    No change at all! This "no change" matrix is known as the "identity" matrix.

    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]
    Or making a vector longer, without changing its direction?
    [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]
    If you've been paying attention, that last one should have made you remember the beginning. By scaling up each of the components in a matrix, we scale up each value in the result.

    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...
    article created on Fri Feb 28, 2003 / 10:13am PST
    this item has been viewed 4445 times
    [Half-Life / coding]

    Only registered users can post comments. Have you registered yet?

    user comments

    displaying comments of normal or higher rating

    1.

    Michiel "MuzzleFlash" Breddels
    Tue Mar 04, 2003 / 02:08am PST

      Nice, this can do great things. lol, a huge baby headcrab, lol or a very tiny Gonarch (Big Momma)

    Let me guess, there's NO WAY we can use this in normal HL maps. Is this only possible in Spirit???

    MuzzleFlash

    2.

    Daniel 'Lord Booga' Koppes
    Tue Mar 04, 2003 / 03:42am PST

      If you do the modification he mentioned, it's possible in any mod (other than normal HL etc)

    3.

    Laurie Cheers
    Tue Mar 04, 2003 / 05:59am PST

      Yeah, it's really easy to add it.
    Also, note that the changes are client-side, so you don't even have to provide a new hl.dll.

    4.

    Skyler 'Zipster' York
    Tue Mar 04, 2003 / 08:30am PST

      I think it's worth pointing out that this effect only affects the rendered model, and not it's physical size. Don't want people to get confused :)

    5.

    Eddy 'Cobra' Loughton
    Tue Mar 04, 2003 / 09:20am PST

      Nice job

    Probably a good idea for the higher-profile SP episode creators to include this in their episodes, nice to see some different scientists and Barneys. Gives the impression of a different model :)

    6.

    Chris 'autolycus' Bokitch
    Tue Mar 04, 2003 / 09:58am PST

      Would it be possible to increase different aspects of the scale? For example, increase the x scale to make a tall skinny scientist, or the y and z scales to make a fat barney... basically, to squash or stretch a model.

    7.

    Francis 'DeathWish' Woodhouse
    Tue Mar 04, 2003 / 10:50am PST

      This article should've been called "Scaling Models and Multiplying a 3x4 by a 3x1 Matrix" :)

    Good article, well-written. Perhaps I should supplement the matrices bit with an article specifically focussing on matrices and their math, and their applications in transformations...

    8.

    Marc 'Delete_Me' Henry
    Tue Mar 04, 2003 / 12:57pm PST

      Elaborating on what Zip said; I tried this on a Spirit 1.0 Launch map I was originally desiging. The scale only effects the rendered picture of the model, and not the hitboxes nor events like where bullets appear from (well, maybe the latter, I forget). Headcrab of doom would have been fun...

    9.

    Skyler 'Zipster' York
    Tue Mar 04, 2003 / 03:21pm PST

      How's the texture scaling affected on the larger models?

    10.

    Patrick 'ComCray' Kanne
    Tue Mar 04, 2003 / 03:42pm PST

      dynamically adjusting x, y and/or z scales? Hmm.. am I the onlyone considering storylines involving certain mind-altering substances? castaneda meets freeman?

    11.

    Laurie Cheers
    Tue Mar 04, 2003 / 04:18pm PST

      Fear and Loathing in Black Mesa Research Facility? :)

    12.

    Brian 'Fletch' Thomas
    Tue Mar 04, 2003 / 07:12pm PST

      mmmm SOHL dream sequences.... army of tiny scientists gets attacked by headcrab'o'doom, huge barney comes and saves the day. zoom out of Barney sleeping in a room as aliens run by his room slaughtering folk. oh the joys of scripted sequences.

    13.

    Jon 'DesPlesda' Manning
    Wed Mar 05, 2003 / 12:14am PST

      Meh, I won't be satisfied until we have Octospiders :D

    14.

    Dorian "DD" Gorski
    Wed Mar 05, 2003 / 02:17am PST

      I'm confused on how the matrix 3 x 3 matrix represents scale, rotation, and scew. I mean, isn't it just a representation of a vector? This is pretty sad considering I'm taking linear algebra and we're past mid terms yet I have an 'A'. One of those here's this, do that classes I suppose.

    The code above appears to multiply all elements of the matrix, not just the diagonal of the matrix.

    15.

    Daniel 'Lord Booga' Koppes
    Wed Mar 05, 2003 / 03:58am PST

      I think in this example, it doesn't, it just represents x, y, and z of a 3D vector.

    I suck at this kind of math at the moment, don't mind me if I'm horribly wrong :P

    16.

    Skyler 'Zipster' York
    Wed Mar 05, 2003 / 08:17am PST

      Matrices are simply a fancy way to organize numbers and systems of equations. A matrix representation of a vector is nice because, in matrix form, it can take on the mathematical properties and behaviors of a matrix and thus can operate on other matrices which represent other vectors or systems of equations. All of this is going on right here in this article.

    Mathematic operations with matrices on other scalars or matrices is well-defined, so all you really have to do is "work backwards" from these operations to determine which elements in the original transformation matrix (the 3x3) correspond to which changes in the final vector. Matrix transformation are done primarily by multiplying them together, so if you want to keep a value, multiplying it by 1 works great. On the same token, if you want to eliminate a value, you use a 0.

    This is where the trick comes in. You know that any given any element in the final vector is really a sum of several sub-multiplications of the original matrix operands. This means that you can't have all zero factors, because that would get rid of everything, however at the same time, you can't have all 1 factors, because that would keep everything. Working backwards, we find that multiplying by the identity matrix, which consists of 1's down the main diagonal, maintains the original vector. If these numbers down the diagonal aren't 1, then they scale, as demonstrated.

    In effect, you are multiplying the entire matrices, however the zero factors eliminate everything but what you want. That's why it appears you are only multiplying down the diagonal, because zero destroys everything else.

    When in doubt, expand the math. Make a really big matrix and plug in the actual equations into each element.
    comment modified on Wed Mar 05, 2003 / 08:19am PST

    17.

    Francis 'DeathWish' Woodhouse
    Wed Mar 05, 2003 / 08:31am PST

      I shall supplement what Zipster has said above.

    Vector as matrix (one column, three rows):

    [x]
    [y]
    [z]

    Vector transpose as matrix (don't worry about this, just here for the people who'll lynch me if I don't mention it) (three columns, one row):

    [x][y][z]

    (Normally matrices are represented with just big square brackets around either end of the whole thing, not with square brackets around each element. I'll use brackets around each element to make things easier to read.)

    A 3x3 matrix can be used to do rotations and scaling. However, a 4x4 matrix is normally used for full transformations, with the vector having a 4th component added (the 'w' component, set to 1). If you don't want to have a 'w' component in the vector, then a four-column three-row matrix can be used to represent transformations. However, as demonstrated above, a 3x3 matrix is only required for rotations and scaling.

    I hope that made sense.

    18.

    Laurie Cheers
    Wed Mar 05, 2003 / 09:43am PST

      Ok. A 3x3 rotation matrix looks something like this:
    [cos(angle)  -sin(angle)  0]
    [sin(angle) cos(angle) 0]
    [ 0 0 1]
    This example rotates around the Z axis, but you can use it for any axis by just switching the rows and columns around.

    A simple worked example - rotating a vector by 90 degrees...
    sin 90 = 1
    cos 90 = 0
    [0 -1 0]   [453]   [0*453 + -1*7 + 0*99]   [ -7]
    [1 0 0] * [ 7] = [1*453 + 0*7 + 0*99] = [453]
    [0 0 1] [ 99] [0*453 + 0*7 + 1*99] [ 99]
    The really useful thing about matrices is that by multiplying them together, you automatically combine their effects. So if you take two rotation matrices and multiply them together, you get a single matrix that performs both rotations - just as if you'd multiplied a vector by one, then by the other.

    And yes, my code scales up every part of the matrix, not just the leading diagonal. (sorry I wasn't clear about that). That's simply the way you scale up a matrix - you need to scale up every component. (Think about scaling up the one above, for instance. If you scale up the leading diagonal, you'll only affect the 1 at the bottom right - so you'll only scale up the Z axis.)

    From a Half-Life programming point of view, you don't really need to know the details. You can construct a rotation matrix by simply calling the AngleMatrix function in studio_util.cpp. To multiply two matrices together, you can use the ConcatTransforms function. And to apply a matrix to a vector, you can use VectorTransform.
    comment modified on Wed Mar 05, 2003 / 10:11am PST

    19.

    Masahiro Sakuta
    Sat Nov 15, 2003 / 05:21am PST

      This technique doesn't work properly in software mode. Model itself never scales, but attachment points seem to move relative to scale. Anyone have an idea why and how to deal with it?

    20.

    Anders [Wolf] Jenbo (NoBody)
    Tue Jun 15, 2004 / 04:02pm PDT

      just wanted to point out that thers a } to mutch in the code posted here in the artical :)
    nice worke thow

    VERC © 2004. All content copyright its respective owner, all rights reserved.
    script execution time: 0.253170013428 seconds