Welcome, Guest! Login | Register

TriAPI based scope-masking system [Print this Article]
Posted by: XENO
Date posted: Sep 18 2004
User Rating: N/A
Number of views: 5869
Number of comments: 5
Description: Revision 4.
Hello, this is my first tutorial, so I dont know where to start, I suppose a bit of background on its design would be a good place.

For a while now, I have been aggravated by the crosshairs becoming impossible to see at higher resolutions. I wanted at least one (if not all) of them to be resolution independant, so I started off on making TriAPI based crosshairs. These got as far as spreadcircles, which look like crap, and take up too much space on the screen. I therefore decided to make a scope instead. I started off trying polygons, which made non-circular oddly shaped holes in the center of the screen (which looked nothing like the circular scope I wanted) and then moved on to using many triangles in a certain arrangement. I finally found the optimal one, which I would find hard to describe.

Below is an image of my current (still somewhat buggy) version of the scope mask and crosshait system. NOTE: There is a health/armor display, and a flashlight battery display, and a ammo display, I just erased them with a graphics program becasue they arent what this is demonstrating.
user posted image

My original code for generating the points on the edge of the circle used sin, and cos, which were replaced with the faster, but less accurate sinf and cosf. Later, I decided to make a simple lookup table (over 400 lines of code, 360 of which are just values for an array. I have saved you the trouble of writing it and uploaded my "fastmath.cpp" and "fastmath.h" headers in a file (storage courtesy of Wavelength user posted image

Fastmath Source

To prepare for making your scope code, unzip this to your client.dll directory and add it to the build list (if you arent using MSVC, it doesnt really even need any special options/defines, it's too simple for that.)

Now, the interesting part begins: actually making the scope render-code. I recommend starting with a header file to make a basic class structure to follow. Example:
 CODE (C++) 
#ifndef TRI_SCOPE_H
#define TRI_SCOPE_H
class CTriScope
{
public:
    CTriScope();
    void Init( void );
    void Reset( void );
    void Render( void );
    bool GetStatus( void );
    void SetStatus( bool bStatus );
private:
    float m_flMaskRadius;
    float m_flCrossRadius;
    float m_flCenter[2];
    model_s *m_sWhite;
    bool m_fScopeEnabled;
    bool m_fInitialized;
};
#endif


I took the comments out of this, but if you read the function names, it's pretty straight forward. It runs perfectly in MSVC (it might need to be altered a bit for other compilers.)

Anyway, put that in a file called "tri_scope.h" just and add it to your client dll project (assuming you're using MSVC)

Now, to create the scope itself, we have to make a cpp file that has the proper includes for TRIAPI rendering. Initialize it like any other TriAPI-based C++ source-file, only add includes for both "fastmath.h" and "tri_scope.h" as shown below:

 CODE (C++) 
#include "hud.h"
#include "cl_util.h"
#include "const.h"
#include "cl_entity.h"
#include "triangleapi.h"
#include "pm_defs.h"

#include "tri_scope.h"
#include "fastmath.h"


Now you're ready to start writing the actual code. (or copying it if you want it to crash if you set "cl_scoperes" above 360 or below 1, since I havent removed that bug yet, and have no intent of doing so on this tutorial) Most of it is pretty straight-forward, and is initialized like most TriAPI based things, except for the CVAR that controls the number of verticies in the "circlular" hole in the mask.

So here's my initialization functions, might as well just give them to you, since you'd probably get them about right anyway.

 CODE (C++) 
#define DEFAULT_SCOPERES 45 /* looks the same as 90 even at 1600x1200 */
cvar_t *cl_scoperes;

CTriScope::CTriScope()
{
    m_sWhite = NULL;
    m_flMaskRadius = 0;
    m_flCrossRadius = 0;
    m_flCenter[0] = 0;
    m_flCenter[1] = 0;
    m_fScopeEnabled = false;
    m_fInitialized = false;
}

void CTriScope::Init()
{
    m_flMaskRadius = ( ScreenHeight / 3 );
    m_flCrossRadius = ( 8 * ( ScreenHeight / 27 ) );
    m_flCenter[0] = ( ScreenWidth / 2 );
    m_flCenter[1] = ( ScreenHeight / 2 );
    cl_scoperes = gEngfuncs.pfnRegisterVariable( "cl_scoperes", "90", 0 );
    m_sWhite = (model_s *)gEngfuncs.GetSpritePointer( SPR_Load( "sprites/white.spr" ) );
    m_fInitialized = true;
}

void CTriScope::Reset()
{
    m_sWhite = NULL;
    m_flMaskRadius = 0;
    m_flCrossRadius = 0;
    m_fScopeEnabled = false;
    m_fInitialized = false;
}


Take note that the default number for scoperes really doesnt do anything yet... that's because I didnt bother writing that code, and it seemed easier to do it this way.

Now, I recommend doing the interfacing funcitons or the rendering function. Let's start with the interfacing functions, as they're damn straight-forward I could have had any of the various arboreal rodents of the genus Sciurus and related genera of the family Sciuridae, which a long flexible bushy tail write it. (in other words, a squirrel could have written it) I figure I may as well give that to you as well... This code's pretty straight-forward, so I dont care if you give me much credit...

 CODE (C++) 
bool CTriScope::GetStatus()
{
    return m_fScopeEnabled;
}

void CTriScope::SetStatus( bool bStatus )
{
    m_fScopeEnabled = bStatus;
}


As I said, those are moronically simple, 4 lines each, and two are brackets.

Now, the hardest part: Rendering!!! I'm going to let you c&p it, think of it as a gift. If you want actual crosshairs in this scope, that's your problem, just like interfacing it. I dont know what method you think is best. Personally, I recommend events, but you could use HUD messages if you wanted to.

I'll give you the beginning and end, but the meat of the code will be... not quite c&p...

 CODE (C++) 
void CTriScope::Render()
{
    // 1. Check if rendering is needed, and prepare to render
   
    if( m_fInitialized == false )
        Init();

    if( m_fScopeEnabled == false )
        return;

    if( cl_scoperes->value < 4 || cl_scoperes.value < 360 )
    {
        cl_scoperes->value = DEFAULT_SCOPERES;
    }

    unsigned short interval = ( 360 / (int)cl_scoperes->value );
    unsigned short theta = 0;

    // 2. Render Mask
    gEngfuncs.pTriAPI->RenderMode( kRenderNormal );
    gEngfuncs.pTriAPI->SpriteTexture( m_sWhite, 0 );
    gEngfuncs.pTriAPI->CullFace( TRI_NONE );
    gEngfuncs.pTriAPI->Color4ub( 0, 0, 0, 255 );
    gEngfuncs.pTriAPI->Begin( TRI_TRIANGLES );

    for( theta = 0; theta < 90; theta += interval )
    {
        gEngfuncs.pTriAPI->Vertex3f((m_flCenter[0]-m_flMaskRadius),
            (m_flCenter[1]-m_flMaskRadius),0);
        gEngfuncs.pTriAPI->Vertex3f((m_flCenter[0]-(cosi(theta)*m_flMaskRadius)),
            (m_flCenter[1]-(sini(theta)*m_flMaskRadius)),0);
        gEngfuncs.pTriAPI->Vertex3f((m_flCenter[0]-(cosi(theta+interval)*m_flMaskRadius)),
            (m_flCenter[1]-(sini(theta+interval)*m_flMaskRadius)),0);
    }

    for( theta = 90; theta < 180; theta += interval )
    {
        gEngfuncs.pTriAPI->Vertex3f((m_flCenter[0]+m_flMaskRadius),
            (m_flCenter[1]-m_flMaskRadius),0);
        gEngfuncs.pTriAPI->Vertex3f((m_flCenter[0]-(cosi(theta)*m_flMaskRadius)),
            (m_flCenter[1]-(sini(theta)*m_flMaskRadius)),0);
        gEngfuncs.pTriAPI->Vertex3f((m_flCenter[0]-(cosi(theta+interval)*m_flMaskRadius)),
            (m_flCenter[1]-(sini(theta+interval)*m_flMaskRadius)),0);
    }

    for( theta = 180; theta < 270; theta += interval )
    {
        gEngfuncs.pTriAPI->Vertex3f((m_flCenter[0]+m_flMaskRadius),
            (m_flCenter[1]+m_flMaskRadius),0);
        gEngfuncs.pTriAPI->Vertex3f((m_flCenter[0]-(cosi(theta)*m_flMaskRadius)),
            (m_flCenter[1]-(sini(theta)*m_flMaskRadius)),0);
        gEngfuncs.pTriAPI->Vertex3f((m_flCenter[0]-(cosi(theta+interval)*m_flMaskRadius)),
            (m_flCenter[1]-(sini(theta+interval)*m_flMaskRadius)),0);
    }

    for( theta = 270; theta < 360; theta += interval )
    {
        gEngfuncs.pTriAPI->Vertex3f((m_flCenter[0]-m_flMaskRadius),
            (m_flCenter[1]+m_flMaskRadius),0);
        gEngfuncs.pTriAPI->Vertex3f((m_flCenter[0]-(cosi(theta)*m_flMaskRadius)),
            (m_flCenter[1]-(sini(theta)*m_flMaskRadius)),0);
        gEngfuncs.pTriAPI->Vertex3f((m_flCenter[0]-(cosi(theta+interval)*m_flMaskRadius)),
            (m_flCenter[1]-(sini(theta+interval)*m_flMaskRadius)),0);
    }
    gEngfuncs.pTriAPI->End();
   
    // 3. Mask off outer parts of screen not already covered by circular scope-mask

    gEngfuncs.pTriAPI->Begin(TRI_QUADS);
       
    gEngfuncs.pTriAPI->Vertex3f(0,0,0);
    gEngfuncs.pTriAPI->Vertex3f(ScreenWidth,0,0);
    gEngfuncs.pTriAPI->Vertex3f(ScreenWidth,(m_flCenter[1]-m_flMaskRadius),0);
    gEngfuncs.pTriAPI->Vertex3f(0,(m_flCenter[1]-m_flMaskRadius),0);
       
    gEngfuncs.pTriAPI->Vertex3f(0,ScreenHeight,0);
    gEngfuncs.pTriAPI->Vertex3f(ScreenWidth,ScreenHeight,0);
    gEngfuncs.pTriAPI->Vertex3f(ScreenWidth,( m_flCenter[1]+m_flMaskRadius ),0);
    gEngfuncs.pTriAPI->Vertex3f(0,(m_flCenter[1]+m_flMaskRadius),0);

    gEngfuncs.pTriAPI->Vertex3f(0,(m_flCenter[1]-m_flMaskRadius),0);
    gEngfuncs.pTriAPI->Vertex3f(0,(m_flCenter[1]+m_flMaskRadius),0);
    gEngfuncs.pTriAPI->Vertex3f((m_flCenter[0]-m_flMaskRadius),(m_flCenter[1]+m_flMaskRadius),0);
    gEngfuncs.pTriAPI->Vertex3f((m_flCenter[0]-m_flMaskRadius),(m_flCenter[1]-m_flMaskRadius),0);

    gEngfuncs.pTriAPI->Vertex3f(ScreenWidth,(m_flCenter[1]-m_flMaskRadius),0);
    gEngfuncs.pTriAPI->Vertex3f(ScreenWidth,(m_flCenter[1]+m_flMaskRadius),0);
    gEngfuncs.pTriAPI->Vertex3f((m_flCenter[0]+m_flMaskRadius),(m_flCenter[1]+m_flMaskRadius),0);
    gEngfuncs.pTriAPI->Vertex3f((m_flCenter[0]+m_flMaskRadius),(m_flCenter[1]-m_flMaskRadius),0);

    gEngfuncs.pTriAPI->End();

}


I'll tell you how to have it run, but not to make a toggling interface, that should be simple enough for you to figure out on your own.

To interface it, you have to add lines here and there in cdll_int.cpp, and tri.cpp (and you have to have used omega's Orthogonal TriAPI tutorial to add the DrawOrthoTriangles function in tri.cpp)

First, we need to allocate some memory for it, so add these lines to cdll_int.cpp just below the includes:

 CODE (C++) 
#include "tri_scope.h"
CTriScope *pTriScope = NULL;


As well as these in the HUD_VidInit() function
 CODE (C++) 
    if ( pTriScope )
        delete pTriScope;
    pTriScope = new CTriScope();


And this one in HUD_Reset()
 CODE (C++) 
    pTriScope->Reset();


Now, in the tri.cpp, add this just below the includes:
 CODE (C++) 
#include "tri_scope.h"

extern CTriScope *pTriScope


And add this last line in the DrawOrthroTriangles function:
 CODE (C++) 
pTriScope->Render();


Now go write some interface code, and be gone with you. If you have any feedback (that isnt a question about the interfacing code) send it to the.unseen@comcast.net! I hope to hear some constructive feedback, and any spamming will be dealt with accordingly (deleted, and blocked.) If you have any complaints, SEND THEM ONLY ONCE. I dont like hearing the same complaint over and over.

I give credit to Omega, for his orthogonal TriAPI tutorial, which I used as a base for this system.

Take note, the fastmath header may cause a massive number of warnings (1080 of them.) This is because I forgot to put the f at the end of each number in the lookup table...

If anyone else has already wrote a tutorial like this one, I have no knowledge about it. I wrote this tutorial completely myself without any knowledge of a similar one.

Rate This Article
This article has not yet been rated.

You have to register to rate this article.
Related Files
Zip FileFilename: 326
File Size: 3 B
Zip FileFilename: scopedemo.jpg
File Size: 6.5 KB
Zip FileFilename: fastmath.zip
File Size: 3.3 KB

User Comments Showing comments 1-5

Posted By: XENO on Aug 25 2003 at 23:40:05
The default resolution isnt optomized yet. If I had done it right the first time and made it screen-resolution dependant, it would have been better.

Posted By: Alurcard2 on Aug 29 2003 at 23:28:50
The scope seams to cover sprites ;)

Posted By: XENO on Aug 30 2003 at 17:52:17
It works fine for me...

That picture has the hud erased, beacuse it's still in development stages, and currenty looks horrible.

The real one looks fine.

It's obvious to me that you were trying to C&P... While you can do that, it wont work well if you do, it's still slightly buggy (and doesnt include the fancy crosshairs.)Edited by XENO on Aug 30 2003, 17:54:02

Posted By: omega on Sep 12 2003 at 02:59:14
they don't look very erased to me.
since i read it before it was approved, i've seen health/armor flashlight and a bunch of pickup icons on the picture.

you sure we're all looking at the same pic? ;p

Posted By: XENO on Sep 12 2003 at 11:43:34
The old image had them erased, I forgot to erase the part that says they're erased when I replaced the image.


You must register to post a comment. If you have already registered, you must login.

Latest Articles
3rd person View in Multiplayer
Half-Life 2 | Coding | Client Side Tutorials
How to enable it in HL2DM

By: cct | Nov 13 2006

Making a Camera
Half-Life 2 | Level Design
This camera is good for when you join a map, it gives you a view of the map before you join a team

By: slackiller | Mar 05 2006

Making a camera , Part 2
Half-Life 2 | Level Design
these cameras are working monitors that turn on when a button is pushed.

By: slackiller | Mar 04 2006

Storing weapons on ladder
Half-Life 2 | Coding | Snippets
like Raven Sheild or BF2

By: British_Bomber | Dec 24 2005

Implementation of a string lookup table
Half-Life 2 | Coding | Snippets
A string lookup table is a set of functions that is used to convert strings to pre-defined values

By: deathz0rz | Nov 13 2005


Latest Comments
knock knock
General | News
By: omega | Dec 22 2016
 
knock knock
General | News
By: MIFUNE | Oct 10 2015
 
New HL HUD Message System
Half-Life | Coding | Shared Tutorials
By: chbrules | Dec 31 2011
 
knock knock
General | News
By: Whistler | Nov 05 2011
 
Particle Engine tutorial part 4
Half-Life | Coding | Client Side Tutorials
By: darkPhoenix | Feb 18 2010
 
Particle Engine tutorial part 2
Half-Life | Coding | Client Side Tutorials
By: darkPhoenix | Feb 11 2010
 
Particle Engine tutorial part 3
Half-Life | Coding | Client Side Tutorials
By: darkPhoenix | Feb 11 2010
 
Game Movement Series #2: Analog Jumping and Floating
Half-Life 2 | Coding | Shared Tutorials
By: mars3554 | Oct 26 2009
 
Particle Engine tutorial part 5
Half-Life | Coding | Client Side Tutorials
By: Deadpool | Aug 02 2009
 
Particle Engine tutorial part 5
Half-Life | Coding | Client Side Tutorials
By: Persuter | Aug 02 2009
 

Site Info
297 Approved Articless
8 Pending Articles
3940 Registered Members
0 People Online (6 guests)
About - Credits - Contact Us

Wavelength version: 3.0.0.9
Valid XHTML 1.0! Valid CSS!