Difference between revisions of "Talk:Mission Scripting (Overview)"
(→Rewrite) |
m (→Fixed-point remark) |
||
(2 intermediate revisions by 2 users not shown) | |||
Line 7: | Line 7: | ||
: In my honest opinion everything should be thrown out, except for ''2. Script instructions''. Then the page should be renamed. And for the sake of history, ''The Tools'' (aka ''The Legacy Tools'') should be kept somewhere, not as detailed, perhaps either a link to the official page or each legacy tool have its own page on GTAModding. Opinions? --[[User:Link2012|Link2012]] ([[User talk:Link2012|talk]]) 23:05, 27 August 2016 (UTC) | : In my honest opinion everything should be thrown out, except for ''2. Script instructions''. Then the page should be renamed. And for the sake of history, ''The Tools'' (aka ''The Legacy Tools'') should be kept somewhere, not as detailed, perhaps either a link to the official page or each legacy tool have its own page on GTAModding. Opinions? --[[User:Link2012|Link2012]] ([[User talk:Link2012|talk]]) 23:05, 27 August 2016 (UTC) | ||
:: Yes, this article has to be rewritten. In the current state it's a mix of general talk about scripting and bunch of technical details interesting only for the tool developers. I expect this article to be a first introduction in the scripting world covering only general concepts of it. Other topics must be described in detail in separate articles (mission tools -> [[Mission Scripting Tools]], script instructions -> [[Opcode]], etc). [[User:Seemann|Seemann]] ([[User talk:Seemann|talk]]) 16:29, 28 August 2016 (UTC) | :: Yes, this article has to be rewritten. In the current state it's a mix of general talk about scripting and bunch of technical details interesting only for the tool developers. I expect this article to be a first introduction in the scripting world covering only general concepts of it. Other topics must be described in detail in separate articles (mission tools -> [[Mission Scripting Tools]], script instructions -> [[Opcode]], etc). [[User:Seemann|Seemann]] ([[User talk:Seemann|talk]]) 16:29, 28 August 2016 (UTC) | ||
− | ::: Tools are out. [[User:Seemann|Seemann]] ([[User talk:Seemann|talk]]) 16:03, 5 September 2016 (UTC) | + | ::: Tools and script instructions are out. [[User:Seemann|Seemann]] ([[User talk:Seemann|talk]]) 16:03, 5 September 2016 (UTC) |
== SCM Highlightning == | == SCM Highlightning == | ||
Line 25: | Line 25: | ||
== Fixed-point remark == | == 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 | + | 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 lost after the division resulting in maximum 4 bits of precision for decimals, a step value of 0.0625 units and a range of [-2048, 2047.9375]. Nonetheless, GTA3script programmers discarded hundredths while writing the source code in compliance to the compilation standards they established since only tenths are supposed to hold arbitrary numbers. 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 | + | <source lang="cpp">short FloatToFixedPoint(float value) |
{ | { | ||
− | + | value *= 16.0f; | |
− | |||
− | + | // Originally, only (short)value is returned. | |
+ | return (short)(value > 0.0f ? value+0.5f : value-0.5f); | ||
+ | } | ||
− | return ( | + | float FixedPointToFloat(short value) |
+ | { | ||
+ | return (float)value/16.0f; | ||
}</source> | }</source> | ||
− | The function above may be optimized to run presumably faster upon machines performing FPU instructions slowly using bitwise operations: | + | The first function above may be optimized to run presumably faster upon machines performing FPU instructions slowly using bitwise operations (no more applicable nowadays): |
− | <source lang="cpp">short FloatToFixedPoint(float | + | <source lang="cpp">short FloatToFixedPoint(float value) |
{ | { | ||
− | unsigned int | + | unsigned int bits = *(unsigned int*)&value; |
− | + | int biasedExp = bits >> 23; | |
− | |||
− | + | // Return zero if less than 1/16. | |
− | + | if(biasedExp < 127-4) | |
+ | { | ||
+ | return 0; | ||
+ | } | ||
− | // | + | // Simulate a multiplication by 16 and normalize. |
− | + | int intPosn = 23-((biasedExp+4)-127); | |
− | + | ||
+ | short signBit = bits >> 31; | ||
+ | unsigned int mantissa = bits & 0x007FFFFF; | ||
− | + | short out; | |
− | |||
− | |||
− | |||
− | |||
− | // | + | // Grab the integer part. |
− | + | out = (mantissa | 0x00800000) >> intPosn; | |
// Round always to the nearest 1/16. | // Round always to the nearest 1/16. | ||
− | + | out += (mantissa >> (intPosn-1)) & 1; | |
− | // Negate | + | // Negate without branching. |
− | + | out = (out ^ -signBit) + signBit; | |
− | return | + | return out; |
}</source> | }</source> | ||
Line 72: | Line 75: | ||
<source lang="cpp">#include <math.h> | <source lang="cpp">#include <math.h> | ||
− | char | + | const char kLeadingZerosCounts8b1[] = |
{ | { | ||
16, 15, 14, 14, 13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 12, | 16, 15, 14, 14, 13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 12, | ||
Line 92: | Line 95: | ||
}; | }; | ||
− | float FixedPointToFloat(short | + | float FixedPointToFloat(short value) |
{ | { | ||
− | unsigned int | + | if(value == 0) |
− | + | { | |
+ | return 0.0f; | ||
+ | } | ||
+ | |||
+ | unsigned int bits = abs(value); | ||
+ | |||
+ | int n = ((bits >> 8) != 0)*8; | ||
+ | int lzcnt = kLeadingZerosCounts8b1[bits >> n] - n; | ||
− | + | unsigned int out; | |
− | |||
− | + | // Grab the sign bit. | |
− | + | out = (value & 0x8000) << 16; | |
− | + | // Make the exponent according to 11 integer bits (due to division by 16). | |
− | + | out |= (127+11-lzcnt) << 23; | |
− | + | // Shift the significand by leading zero bits including the implicit bit. | |
− | + | out |= (bits << (23-(16-lzcnt)+1)) & 0x007FFFFF; | |
− | |||
− | |||
− | |||
− | |||
− | return *(float *)& | + | return *(float*)&out; |
}</source> | }</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) | 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) |
Latest revision as of 13:54, 20 March 2021
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)
- In my honest opinion everything should be thrown out, except for 2. Script instructions. Then the page should be renamed. And for the sake of history, The Tools (aka The Legacy Tools) should be kept somewhere, not as detailed, perhaps either a link to the official page or each legacy tool have its own page on GTAModding. Opinions? --Link2012 (talk) 23:05, 27 August 2016 (UTC)
- Yes, this article has to be rewritten. In the current state it's a mix of general talk about scripting and bunch of technical details interesting only for the tool developers. I expect this article to be a first introduction in the scripting world covering only general concepts of it. Other topics must be described in detail in separate articles (mission tools -> Mission Scripting Tools, script instructions -> Opcode, etc). Seemann (talk) 16:29, 28 August 2016 (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 lost after the division resulting in maximum 4 bits of precision for decimals, a step value of 0.0625 units and a range of [-2048, 2047.9375]. Nonetheless, GTA3script programmers discarded hundredths while writing the source code in compliance to the compilation standards they established since only tenths are supposed to hold arbitrary numbers. 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 value)
{
value *= 16.0f;
// Originally, only (short)value is returned.
return (short)(value > 0.0f ? value+0.5f : value-0.5f);
}
float FixedPointToFloat(short value)
{
return (float)value/16.0f;
}
The first function above may be optimized to run presumably faster upon machines performing FPU instructions slowly using bitwise operations (no more applicable nowadays):
short FloatToFixedPoint(float value)
{
unsigned int bits = *(unsigned int*)&value;
int biasedExp = bits >> 23;
// Return zero if less than 1/16.
if(biasedExp < 127-4)
{
return 0;
}
// Simulate a multiplication by 16 and normalize.
int intPosn = 23-((biasedExp+4)-127);
short signBit = bits >> 31;
unsigned int mantissa = bits & 0x007FFFFF;
short out;
// Grab the integer part.
out = (mantissa | 0x00800000) >> intPosn;
// Round always to the nearest 1/16.
out += (mantissa >> (intPosn-1)) & 1;
// Negate without branching.
out = (out ^ -signBit) + signBit;
return out;
}
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 performance):
#include <math.h>
const char kLeadingZerosCounts8b1[] =
{
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 value)
{
if(value == 0)
{
return 0.0f;
}
unsigned int bits = abs(value);
int n = ((bits >> 8) != 0)*8;
int lzcnt = kLeadingZerosCounts8b1[bits >> n] - n;
unsigned int out;
// Grab the sign bit.
out = (value & 0x8000) << 16;
// Make the exponent according to 11 integer bits (due to division by 16).
out |= (127+11-lzcnt) << 23;
// Shift the significand by leading zero bits including the implicit bit.
out |= (bits << (23-(16-lzcnt)+1)) & 0x007FFFFF;
return *(float*)&out;
}
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)