Difference between revisions of "Talk:Mission Scripting (Overview)"

From GTAMods Wiki
Jump to navigation Jump to search
(Tools: new section)
(Added fixed-point remark.)
Line 18: Line 18:
  
 
We should move the information from the [[Mission_Scripting_(Overview)#The_Tools|Tools section]] into the articles describing each of them and leave only a link to the [[Mission Scripting Tools]] [[User:Seemann|Seemann]] 06:24, 20 February 2009 (UTC)
 
We should move the information from the [[Mission_Scripting_(Overview)#The_Tools|Tools section]] into the articles describing each of them and leave only a link to the [[Mission Scripting Tools]] [[User:Seemann|Seemann]] 06:24, 20 February 2009 (UTC)
 +
 +
== Fixed-point remark ==
 +
 +
III uses a fixed-point data type to store floating-point values. Nothing new but it's worth making a remark about. After parsing '''FLOAT''' arguments, R* compiler multiplies the value by 16 and casts the result to integer, hence it truncates the decimal part completely and rounds the integer one toward 0. This way any fraction past 1/16 will be cut off after the division, resulting in maximum 4 bits of precision for decimals and a step value of 0.0625 units. Nonetheless ''GTA3script'' programmers discarded hundredths while writing the source code aside from the compilation standards they established. Although truncation doesn't make much difference for spawning points, it can compromise the precision of relatively small values for math calculations, that's why rounding to the nearest lowest fraction seems preferred:
 +
 +
<source lang="cpp">short FloatToFixedPoint(float fFloatValue)
 +
{
 +
    if(!fFloatValue)
 +
        return 0;
 +
 +
    fFloatValue *= 16.0f;
 +
 +
    return (short)(fFloatValue + (fFloatValue > 0 ? 0.5f : -0.5f));
 +
}</source>
 +
 +
The function above may be optimized to run presumably faster upon machines performing FPU instructions slowly using bitwise operations:
 +
 +
<source lang="cpp">short FloatToFixedPoint(float fFloatValue)
 +
{
 +
    unsigned int uiRawFloat, uiMantissa;
 +
    unsigned char ucBiasedExp, ucIntegerPosn;
 +
    short sNegativeBit, sReturnValue;
 +
 +
    uiRawFloat = *(unsigned int *)&fFloatValue;
 +
    ucBiasedExp = uiRawFloat >> 23;
 +
 +
    // Return zero if less than 1/16 (from the 5th binary digit position onward).
 +
    if(ucBiasedExp < 123)
 +
        return 0;
 +
 +
    // Increase the exponent by 4 positions to simulate a multiplication by 16 and normalize it:
 +
    //    int_posn = man_bits - ((biased_exp + 4) - bias)
 +
    ucIntegerPosn = 146 - ucBiasedExp;
 +
    sNegativeBit = uiRawFloat >> 31;
 +
    uiMantissa = uiRawFloat & 0x007FFFFF;
 +
 +
    // Retrieve the computed integer part.
 +
    sReturnValue = (uiMantissa | 0x00800000) >> ucIntegerPosn;
 +
    // Round always to the nearest 1/16.
 +
    sReturnValue += (uiMantissa >> (ucIntegerPosn - 1)) & 1;
 +
    // Negate the value conditionally without branching.
 +
    sReturnValue = (sReturnValue ^ -sNegativeBit) + sNegativeBit;
 +
 +
    return sReturnValue;
 +
}</source>
 +
 +
Below is the inverse function which shows what happens under the hood, left just for learning purposes as it would hardly execute quicker than the obvious way (of course, a bigger lookup table occupying 64KiB would speed up the code):
 +
 +
<source lang="cpp">#include <math.h>
 +
 +
char g_aLeadingZerosCounts8b1[] =
 +
{
 +
    16, 15, 14, 14, 13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 12,
 +
    11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
 +
    10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
 +
    10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
 +
    9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,
 +
    9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,
 +
    9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,
 +
    9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,
 +
    8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
 +
    8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
 +
    8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
 +
    8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
 +
    8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
 +
    8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
 +
    8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
 +
    8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
 +
};
 +
 +
float FixedPointToFloat(short sShortValue)
 +
{
 +
    unsigned int uiMeaningfulBits, uiRawFloat;
 +
    char cDiscardedBits, cNumLeadingZeros;
 +
 +
    if(!sShortValue)
 +
        return 0.0f;
 +
 +
    uiMeaningfulBits = abs(sShortValue);
 +
    cDiscardedBits = ((uiMeaningfulBits >> 8) != 0) << 3;
 +
    cNumLeadingZeros = g_aLeadingZerosCounts8b1[uiMeaningfulBits >> cDiscardedBits] - cDiscardedBits;
 +
    uiRawFloat = ((unsigned int)sShortValue & 0x80000000)
 +
              // Make the exponent according to 11 integer bits and 4 decimal bits:
 +
              //    exp = (bias + int_bits) - lzcnt
 +
              | ((138 - cNumLeadingZeros) << 23)
 +
              // Shift the significand by the number of leading 0 bits including the implicit bit:
 +
              //    man_shifts = (man_bits - (max_lzcnt - lzcnt)) + 1
 +
              | (uiMeaningfulBits << (8 + cNumLeadingZeros)) & 0x007FFFFF;
 +
 +
    return *(float *)&uiRawFloat;
 +
}</source>
 +
 +
I didn't fire up any benchmark though. In the end, there could be an incontrovertible rationale behind flooring rather than rounding to the nearest half after multiplication. I would rule out the "issue" got underestimated, so I'm open for any suggestion. -- [[User:Wesser|Wesser]] ([[User talk:Wesser|talk]]) 14:16, 9 November 2015 (UTC)

Revision as of 14:16, 9 November 2015

Jernej, why not correct/remove/extend odd phrases instead of making stupid comments? --steve-m 14:52, 17 Feb 2007 (CST)

Rewrite

The Tools section is old and incomplete. Cracking the SCM is based on the Mission Builder syntax which is obsolete. And this article says nothing about variables, labels, threads, external scripts. As this article is a Top 10 popular page, I believe it should be cleaned and improved. Seemann 02:19, 31 December 2008 (UTC)

SCM Highlightning

Hey,

there's one thing I'd like to add to the SCM highlightning. Look at this:

:MYTHREAD01
0001: wait 1 ms
0002: jump @MYTHREAD01

Now the label descriptions (@...) should be green, too, don't you think so? Also can we somehow create a backround box

like in this text?

This would look realy better, I think. --Aschratt 12:24, 31 January 2009 (UTC)

Both fixed. --Steve-m 17:04, 2 February 2009 (UTC)

Tools

We should move the information from the Tools section into the articles describing each of them and leave only a link to the Mission Scripting Tools Seemann 06:24, 20 February 2009 (UTC)

Fixed-point remark

III uses a fixed-point data type to store floating-point values. Nothing new but it's worth making a remark about. After parsing FLOAT arguments, R* compiler multiplies the value by 16 and casts the result to integer, hence it truncates the decimal part completely and rounds the integer one toward 0. This way any fraction past 1/16 will be cut off after the division, resulting in maximum 4 bits of precision for decimals and a step value of 0.0625 units. Nonetheless GTA3script programmers discarded hundredths while writing the source code aside from the compilation standards they established. Although truncation doesn't make much difference for spawning points, it can compromise the precision of relatively small values for math calculations, that's why rounding to the nearest lowest fraction seems preferred:

short FloatToFixedPoint(float fFloatValue)
{
    if(!fFloatValue)
        return 0;

    fFloatValue *= 16.0f;

    return (short)(fFloatValue + (fFloatValue > 0 ? 0.5f : -0.5f));
}

The function above may be optimized to run presumably faster upon machines performing FPU instructions slowly using bitwise operations:

short FloatToFixedPoint(float fFloatValue)
{
    unsigned int uiRawFloat, uiMantissa;
    unsigned char ucBiasedExp, ucIntegerPosn;
    short sNegativeBit, sReturnValue;

    uiRawFloat = *(unsigned int *)&fFloatValue;
    ucBiasedExp = uiRawFloat >> 23;

    // Return zero if less than 1/16 (from the 5th binary digit position onward).
    if(ucBiasedExp < 123)
        return 0;

    // Increase the exponent by 4 positions to simulate a multiplication by 16 and normalize it:
    //     int_posn = man_bits - ((biased_exp + 4) - bias)
    ucIntegerPosn = 146 - ucBiasedExp;
    sNegativeBit = uiRawFloat >> 31;
    uiMantissa = uiRawFloat & 0x007FFFFF;

    // Retrieve the computed integer part.
    sReturnValue = (uiMantissa | 0x00800000) >> ucIntegerPosn;
    // Round always to the nearest 1/16.
    sReturnValue += (uiMantissa >> (ucIntegerPosn - 1)) & 1;
    // Negate the value conditionally without branching.
    sReturnValue = (sReturnValue ^ -sNegativeBit) + sNegativeBit;

    return sReturnValue;
}

Below is the inverse function which shows what happens under the hood, left just for learning purposes as it would hardly execute quicker than the obvious way (of course, a bigger lookup table occupying 64KiB would speed up the code):

#include <math.h>

char g_aLeadingZerosCounts8b1[] =
{
    16, 15, 14, 14, 13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 12,
    11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
    10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
    10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
    9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,
    9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,
    9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,
    9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,
    8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
    8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
    8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
    8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
    8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
    8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
    8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
    8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
};

float FixedPointToFloat(short sShortValue)
{
    unsigned int uiMeaningfulBits, uiRawFloat;
    char cDiscardedBits, cNumLeadingZeros;

    if(!sShortValue)
        return 0.0f;

    uiMeaningfulBits = abs(sShortValue);
    cDiscardedBits = ((uiMeaningfulBits >> 8) != 0) << 3;
    cNumLeadingZeros = g_aLeadingZerosCounts8b1[uiMeaningfulBits >> cDiscardedBits] - cDiscardedBits;
    uiRawFloat = ((unsigned int)sShortValue & 0x80000000)
               // Make the exponent according to 11 integer bits and 4 decimal bits:
               //     exp = (bias + int_bits) - lzcnt
               | ((138 - cNumLeadingZeros) << 23)
               // Shift the significand by the number of leading 0 bits including the implicit bit:
               //     man_shifts = (man_bits - (max_lzcnt - lzcnt)) + 1
               | (uiMeaningfulBits << (8 + cNumLeadingZeros)) & 0x007FFFFF;

    return *(float *)&uiRawFloat;
}

I didn't fire up any benchmark though. In the end, there could be an incontrovertible rationale behind flooring rather than rounding to the nearest half after multiplication. I would rule out the "issue" got underestimated, so I'm open for any suggestion. -- Wesser (talk) 14:16, 9 November 2015 (UTC)