Colour Cube: Difference between revisions

m
Changed ^3 to ³ to avoid confusion with bitwise XOR.
No edit summary
m (Changed ^3 to ³ to avoid confusion with bitwise XOR.)
 
(14 intermediate revisions by 4 users not shown)
Line 1:
{{ParadiseResourceTypeInfobox
| name = ColourCube
| othernames = RwColourCube
| example = [[File:Paradise_ColourCubes.png|frameless|200px]]<br />ColourCubes used in Burnout Paradise.
| id = 0x2B
| category = Generic
| memdist = Main Memory only
| importedby = [[Environment Keyframe]]
}}
 
ColourCube resources are used by both the [[EnvironmentSettings]] and [[PostFX]] to change the colour and tone of the world.
 
A ColourCube is a set of textures forming a [https://en.wikipedia.org/wiki/Palette_(computing) CLUT], which is usually colour corrected to give a different art style to the game at a given time (e.g., the red filter when wrecking a vehicle in versions 1.0-1.3 of ''Burnout Paradise'').
[[File:Paradise_ColourCubes.png|thumb|200px|ColourCubes used in Burnout Paradise.]]
 
Some time after the [[Burnout 5 (2007-02-22 build)|February 22, 2007]] build of ''Paradise'', ColourCubes were added to the EnvironmentSettings folder and were used specifically to give the game a unique art style.
A ColourCube is a set of 32 32x32 textures forming a [https://en.wikipedia.org/wiki/Palette_(computing) CLUT], which is usually colour corrected to give a different art style to the game at a given time (i.e., the red filter when wrecking a vehicle in versions 1.0-1.3 of Burnout Paradise).
 
In the 1.4 update to ''Burnout Paradise'', the ColourCubes were changed to a standard CLUT, possibly due to the extended time cycle conflicting with the ColourCube's art style. In the 1.6 update, these were again updated to use a default RGB CLUT.
Some time after the [[Burnout_5_(2007-02-22_build)|February 22, 2007]] build of Paradise, ColourCubes were added to the EnvironmentSettings folder and were used specifically to give the game a unique art style.
 
= Structures =
In the 1.4 update to Burnout Paradise, the ColourCubes were changed to a standard CLUT, possibly due to the extended time cycle conflicting with the ColourCube's art style. In the 1.6 update, these were again updated to use a default RGB CLUT.
=== rw::graphics::postfx::ColourCube ===
 
==== Layout32-bit ====
=== 32-bit ===
{| class="wikitable"
|-
! Offset !! Length !! Type !! Name !! Description !! Comments
|-
| 0x0 || 0x4 || uint32_t || m_size || Either theThe number of textures orand thetheir width/height. || Used to calculate file size (m_size<sup><small>3</small></sup> * 3 + 16)
|-
| 0x4 || 0x4 || uint8_t* || m_pixels || Pointer to the texture data. ||
|}
 
=== 64-bit ===
==== 64-bit ====
{| class="wikitable"
|-
! Offset !! Length !! Type !! Name !! Description !! Comments
|-
| 0x0 || 0x4 || uint32_t || m_size || Either theThe number of textures orand thetheir width/height. || Used to calculate file size (m_size<sup><small>3</small></sup> * 3 + 16)
|-
| 0x4 || 0x4 || || Padding || ||
|-
| 0x8 || 0x8 || uint8_t* || m_pixels || Pointer to the texture data. ||
Line 30 ⟶ 40:
 
= Data =
In ''Burnout Paradise: The Ultimate Box'' on PC and ''Burnout Paradise Remastered'' on PC and PS4, data is in a standard RGB24 format (that is, each pixel takes up 3 bytes, one for each color). Textures in the PS3, Xbox 360, and Switch versions of the game are also RGB24, but swizzled. There is a functional implementation of an Xbox 360 to PC texture converter [https://github.com/burninrubber0/ColourCube_Converter on GitHub].
 
== Xbox 360 Swizzle Algorithm ==
The following algorithm assumes that the data is stored and accessed as m_sizea cubescube of size 32, each m_size*m_size pixels, oriented left to right with an m_size*m_size*3 bytes stridepixels, ratheraccessed than as m_size cubes oriented topaccording to bottom with a m_size*3certain bytes stride (i.ealgorithm. onlyThe one cube's data per line). Regardless of whichalgorithm is true,different itfor seems''Burnout thatParadise'' the following algorithm works to produce exact conversions from Xbox 360 colourcubes to PC colourcubes. Smaller32*32*32 cubes, likeand those''Need fromfor NFSSpeed:HP willHot likelyPursuit'' need16*16*16 a different algorithm for calculating the offsetscubes.
 
With the above in mind, toTo find the 3-byte bytes (R, G and B) of pixel data for position (x,y,z) in the PC m_pixels data, 3 bytes (R, G and B) can be read from the Xbox 360 m_pixels data at the offset calculated as follows where x, y and yz are represented as a number of bits, x0 to x9x4, (valuesy0 0-m_size*m_size)to y4 and y0z0 to y4z4 (all values between 0- and m_size - 1).
 
{| class="wikitable"
! x !! y !! z !! offset (m_size = 16)
|-
! x !! y !! offset
|- style="vertical-align:top;"
| <pre>xxxx
| <syntaxhighlight>xxxxxxxxxx
3210</pre> || <pre>yyyy
9876543210</syntaxhighlight> || <syntaxhighlight>yyyyy
3210</pre> || <pre>zzzz
43210</syntaxhighlight> || <syntaxhighlight>o0=x0
3210</pre> || <pre>o0=x0
o1=x1
o2=x5y0
o3=x2
o4=x3
o5=x4^x8^y2y1
o6=x6y2
o7=x7z0
o8=y0y3^z2
o9=x8^y2z1
o10=y1z2
o11=x9z3</pre>
|}
o12=y2
 
o13=y3
{| class="wikitable"
o14=y4</syntaxhighlight>
! x !! y !! z !! offset (m_size = 32)
|- style="vertical-align:top;"
| <pre>xxxxx
43210</pre> || <pre>yyyyy
43210</pre> || <pre>zzzzz
43210</pre> || <pre>o0=x0
o1=x1
o2=y0
o3=x2
o4=x3
o5=x4^y3^z2
o6=y1
o7=y2
o8=z0
o9=y3^z2
o10=z1
o11=y4
o12=z2
o13=z3
o14=z4</pre>
|}
 
Line 69 ⟶ 99:
end
 
# calculate offset, in pixels, of where to find the (x,y,z) pixel in the Xbox 360 data
# pixel offset in PC file is m_size * m_size * z + m_size * y + x
def calc_offset(x,y)
def calc_offset(m_size, x, y, z)
case m_size
when 16
calc_offset16(x, y, z)
when 32
calc_offset32(x, y, z)
else
raise "Unsupported m_size: #{m_size}"
end
end
 
def calc_offset16(x, y, z)
x0 = (x >> 0) & 0b1
x1 = (x >> 1) & 0b1
x2 = (x >> 2) & 0b1
x3 = (x >> 3) & 0b1
y0 = (y >> 0) & 0b1
y1 = (y >> 1) & 0b1
y2 = (y >> 2) & 0b1
y3 = (y >> 3) & 0b1
z0 = (z >> 0) & 0b1
z1 = (z >> 1) & 0b1
z2 = (z >> 2) & 0b1
z3 = (z >> 3) & 0b1
 
o=(z3<<11)|
(z2<<10)|
(z1<<9)|
((y3<<8)^(z2<<8))|
(z0<<7)|
(y2<<6)|
(y1<<5)|
(x3<<4)|
(x2<<3)|
(y0<<2)|
(x1<<1)|
(x0<<0)
end
 
def calc_offset32(x, y, z)
x0 = (x >> 0) & 0b1
x1 = (x >> 1) & 0b1
Line 76 ⟶ 146:
x3 = (x >> 3) & 0b1
x4 = (x >> 4) & 0b1
x5 = (x >> 5) & 0b1
x6 = (x >> 6) & 0b1
x7 = (x >> 7) & 0b1
x8 = (x >> 8) & 0b1
x9 = (x >> 9) & 0b1
y0 = (y >> 0) & 0b1
y1 = (y >> 1) & 0b1
Line 86 ⟶ 151:
y3 = (y >> 3) & 0b1
y4 = (y >> 4) & 0b1
z0 = (z >> 0) & 0b1
z1 = (z >> 1) & 0b1
z2 = (z >> 2) & 0b1
z3 = (z >> 3) & 0b1
z4 = (z >> 4) & 0b1
 
o=(y4z4<<14)|
(y3z3<<13)|
(y2z2<<12)|
(x9y4<<11)|
(y1z1<<10)|
((y2z2<<9)^(x8y3<<9))|
(y0z0<<8)|
(x7y2<<7)|
(x6y1<<6)|
((y2z2<<5)^(x8y3<<5)^(x4<<5))|
(x3<<4)|
(x2<<3)|
(x5y0<<2)|
(x1<<1)|
(x0<<0)
Line 109 ⟶ 179:
# now, convert the m_pixels data
data="".b
m_size.times do |yz|
(m_size * m_size).times do |xy|
m_size.times do |x|
offset = calc_offset(x, y)
pixel_data = m_pixels[offset.pixels... = calc_offset(offsetx, +y, 1z).pixels]
data << pixel_data = m_pixels[offset.pixels...(offset + 1).pixels]
data << pixel_data
end
end
end
 
# write "converted to PC" data to file
# ...
‎</syntaxhighlight>