Compress primitives.
This commit is contained in:
parent
68e0e791a4
commit
c0c0daa427
|
@ -22,7 +22,7 @@ uniform vec2 uViewBoxOrigin;
|
|||
in vec2 aTessCoord;
|
||||
in vec2 aTileOrigin;
|
||||
in int aBackdrop;
|
||||
in int aObject;
|
||||
in uint aObject;
|
||||
|
||||
out vec2 vTexCoord;
|
||||
out float vBackdrop;
|
||||
|
|
|
@ -20,7 +20,7 @@ uniform vec2 uViewBoxOrigin;
|
|||
|
||||
in vec2 aTessCoord;
|
||||
in vec2 aTileOrigin;
|
||||
in int aObject;
|
||||
in uint aObject;
|
||||
|
||||
out vec4 vColor;
|
||||
|
||||
|
|
|
@ -33,9 +33,9 @@ const QUAD_VERTEX_POSITIONS: Uint8Array = new Uint8Array([
|
|||
0, 1,
|
||||
]);
|
||||
|
||||
const FILL_INSTANCE_SIZE: number = 20;
|
||||
const SOLID_TILE_INSTANCE_SIZE: number = 8;
|
||||
const MASK_TILE_INSTANCE_SIZE: number = 12;
|
||||
const FILL_INSTANCE_SIZE: number = 8;
|
||||
const SOLID_TILE_INSTANCE_SIZE: number = 6;
|
||||
const MASK_TILE_INSTANCE_SIZE: number = 8;
|
||||
|
||||
interface Color {
|
||||
r: number;
|
||||
|
@ -58,7 +58,10 @@ class App {
|
|||
private stencilTexture: WebGLTexture;
|
||||
private stencilFramebuffer: WebGLFramebuffer;
|
||||
private fillProgram: Program<'FramebufferSize' | 'TileSize' | 'AreaLUT',
|
||||
'TessCoord' | 'From' | 'To' | 'TileIndex'>;
|
||||
'TessCoord' |
|
||||
'FromPx' | 'ToPx' |
|
||||
'FromSubpx' | 'ToSubpx' |
|
||||
'TileIndex'>;
|
||||
private solidTileProgram: Program<'FramebufferSize' |
|
||||
'TileSize' |
|
||||
'FillColorsTexture' | 'FillColorsTextureSize' |
|
||||
|
@ -116,6 +119,19 @@ class App {
|
|||
|
||||
this.fillColorsTexture = unwrapNull(gl.createTexture());
|
||||
|
||||
/*
|
||||
const benchData = new Uint8Array(1600 * 1600 * 4);
|
||||
for (let i = 0; i < benchData.length; i++)
|
||||
benchData[i] = (Math.random() * 256) | 0;
|
||||
const benchTexture = unwrapNull(gl.createTexture());
|
||||
gl.bindTexture(gl.TEXTURE_2D, benchTexture);
|
||||
const startTime = performance.now();
|
||||
for (let i = 0; i < 100; i++)
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1600, 1600, 0, gl.RGBA, gl.UNSIGNED_BYTE, benchData);
|
||||
const elapsedTime = (performance.now() - startTime) / 100;
|
||||
console.log("texture upload: ", elapsedTime, "ms");
|
||||
*/
|
||||
|
||||
this.stencilTexture = unwrapNull(gl.createTexture());
|
||||
gl.bindTexture(gl.TEXTURE_2D, this.stencilTexture);
|
||||
gl.texImage2D(gl.TEXTURE_2D,
|
||||
|
@ -180,7 +196,12 @@ class App {
|
|||
STENCIL_VERTEX_SHADER_SOURCE,
|
||||
STENCIL_FRAGMENT_SHADER_SOURCE,
|
||||
['FramebufferSize', 'TileSize', 'AreaLUT'],
|
||||
['TessCoord', 'From', 'To', 'TileIndex']);
|
||||
[
|
||||
'TessCoord',
|
||||
'FromPx', 'ToPx',
|
||||
'FromSubpx', 'ToSubpx',
|
||||
'TileIndex'
|
||||
]);
|
||||
this.fillProgram = fillProgram;
|
||||
|
||||
// Initialize quad VBO.
|
||||
|
@ -203,29 +224,43 @@ class App {
|
|||
0,
|
||||
0);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this.fillVertexBuffer);
|
||||
gl.vertexAttribPointer(fillProgram.attributes.From,
|
||||
2,
|
||||
gl.FLOAT,
|
||||
false,
|
||||
gl.vertexAttribIPointer(fillProgram.attributes.FromPx,
|
||||
1,
|
||||
gl.UNSIGNED_BYTE,
|
||||
FILL_INSTANCE_SIZE,
|
||||
0);
|
||||
gl.vertexAttribDivisor(fillProgram.attributes.From, 1);
|
||||
gl.vertexAttribPointer(fillProgram.attributes.To,
|
||||
2,
|
||||
gl.FLOAT,
|
||||
false,
|
||||
gl.vertexAttribDivisor(fillProgram.attributes.FromPx, 1);
|
||||
gl.vertexAttribIPointer(fillProgram.attributes.ToPx,
|
||||
1,
|
||||
gl.UNSIGNED_BYTE,
|
||||
FILL_INSTANCE_SIZE,
|
||||
8);
|
||||
gl.vertexAttribDivisor(fillProgram.attributes.To, 1);
|
||||
1);
|
||||
gl.vertexAttribDivisor(fillProgram.attributes.ToPx, 1);
|
||||
gl.vertexAttribPointer(fillProgram.attributes.FromSubpx,
|
||||
2,
|
||||
gl.UNSIGNED_BYTE,
|
||||
true,
|
||||
FILL_INSTANCE_SIZE,
|
||||
2);
|
||||
gl.vertexAttribDivisor(fillProgram.attributes.FromSubpx, 1);
|
||||
gl.vertexAttribPointer(fillProgram.attributes.ToSubpx,
|
||||
2,
|
||||
gl.UNSIGNED_BYTE,
|
||||
true,
|
||||
FILL_INSTANCE_SIZE,
|
||||
4);
|
||||
gl.vertexAttribDivisor(fillProgram.attributes.ToSubpx, 1);
|
||||
gl.vertexAttribIPointer(fillProgram.attributes.TileIndex,
|
||||
1,
|
||||
gl.UNSIGNED_INT,
|
||||
gl.UNSIGNED_SHORT,
|
||||
FILL_INSTANCE_SIZE,
|
||||
16);
|
||||
6);
|
||||
gl.vertexAttribDivisor(fillProgram.attributes.TileIndex, 1);
|
||||
gl.enableVertexAttribArray(fillProgram.attributes.TessCoord);
|
||||
gl.enableVertexAttribArray(fillProgram.attributes.From);
|
||||
gl.enableVertexAttribArray(fillProgram.attributes.To);
|
||||
gl.enableVertexAttribArray(fillProgram.attributes.FromPx);
|
||||
gl.enableVertexAttribArray(fillProgram.attributes.ToPx);
|
||||
gl.enableVertexAttribArray(fillProgram.attributes.FromSubpx);
|
||||
gl.enableVertexAttribArray(fillProgram.attributes.ToSubpx);
|
||||
gl.enableVertexAttribArray(fillProgram.attributes.TileIndex);
|
||||
|
||||
// Initialize tile VBOs and IBOs.
|
||||
|
@ -253,7 +288,7 @@ class App {
|
|||
gl.vertexAttribDivisor(solidTileProgram.attributes.TileOrigin, 1);
|
||||
gl.vertexAttribIPointer(solidTileProgram.attributes.Object,
|
||||
1,
|
||||
gl.INT,
|
||||
gl.UNSIGNED_SHORT,
|
||||
SOLID_TILE_INSTANCE_SIZE,
|
||||
4);
|
||||
gl.vertexAttribDivisor(solidTileProgram.attributes.Object, 1);
|
||||
|
@ -282,15 +317,15 @@ class App {
|
|||
gl.vertexAttribDivisor(maskTileProgram.attributes.TileOrigin, 1);
|
||||
gl.vertexAttribIPointer(maskTileProgram.attributes.Backdrop,
|
||||
1,
|
||||
gl.INT,
|
||||
gl.SHORT,
|
||||
MASK_TILE_INSTANCE_SIZE,
|
||||
4);
|
||||
gl.vertexAttribDivisor(maskTileProgram.attributes.Backdrop, 1);
|
||||
gl.vertexAttribIPointer(maskTileProgram.attributes.Object,
|
||||
1,
|
||||
gl.INT,
|
||||
gl.UNSIGNED_SHORT,
|
||||
MASK_TILE_INSTANCE_SIZE,
|
||||
8);
|
||||
6);
|
||||
gl.vertexAttribDivisor(maskTileProgram.attributes.Object, 1);
|
||||
gl.enableVertexAttribArray(maskTileProgram.attributes.TessCoord);
|
||||
gl.enableVertexAttribArray(maskTileProgram.attributes.TileOrigin);
|
||||
|
@ -313,14 +348,17 @@ class App {
|
|||
|
||||
//console.log("viewBox", this.viewBox);
|
||||
|
||||
// Start timer.
|
||||
let timerQuery = null;
|
||||
// Initialize timers.
|
||||
let fillTimerQuery = null, solidTimerQuery = null, maskTimerQuery = null;
|
||||
if (this.disjointTimerQueryExt != null) {
|
||||
timerQuery = unwrapNull(gl.createQuery());
|
||||
gl.beginQuery(this.disjointTimerQueryExt.TIME_ELAPSED_EXT, timerQuery);
|
||||
fillTimerQuery = unwrapNull(gl.createQuery());
|
||||
solidTimerQuery = unwrapNull(gl.createQuery());
|
||||
maskTimerQuery = unwrapNull(gl.createQuery());
|
||||
}
|
||||
|
||||
// Fill.
|
||||
if (fillTimerQuery != null)
|
||||
gl.beginQuery(this.disjointTimerQueryExt.TIME_ELAPSED_EXT, fillTimerQuery);
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, this.stencilFramebuffer);
|
||||
gl.viewport(0, 0, STENCIL_FRAMEBUFFER_SIZE.width, STENCIL_FRAMEBUFFER_SIZE.height);
|
||||
gl.clearColor(0.0, 0.0, 0.0, 0.0);
|
||||
|
@ -340,6 +378,8 @@ class App {
|
|||
gl.enable(gl.BLEND);
|
||||
gl.drawArraysInstanced(gl.TRIANGLE_FAN, 0, 4, unwrapNull(this.fillPrimitiveCount));
|
||||
gl.disable(gl.BLEND);
|
||||
if (fillTimerQuery != null)
|
||||
gl.endQuery(this.disjointTimerQueryExt.TIME_ELAPSED_EXT);
|
||||
|
||||
// Read back stencil and dump it.
|
||||
//this.dumpStencil();
|
||||
|
@ -351,6 +391,8 @@ class App {
|
|||
gl.clearColor(0.85, 0.85, 0.85, 1.0);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
|
||||
if (solidTimerQuery != null)
|
||||
gl.beginQuery(this.disjointTimerQueryExt.TIME_ELAPSED_EXT, solidTimerQuery);
|
||||
gl.bindVertexArray(this.solidVertexArray);
|
||||
gl.useProgram(this.solidTileProgram.program);
|
||||
gl.uniform2f(this.solidTileProgram.uniforms.FramebufferSize,
|
||||
|
@ -369,8 +411,12 @@ class App {
|
|||
this.viewBox.origin.y);
|
||||
gl.disable(gl.BLEND);
|
||||
gl.drawArraysInstanced(gl.TRIANGLE_FAN, 0, 4, this.solidTileCount);
|
||||
if (solidTimerQuery != null)
|
||||
gl.endQuery(this.disjointTimerQueryExt.TIME_ELAPSED_EXT);
|
||||
|
||||
// Draw masked tiles.
|
||||
if (maskTimerQuery != null)
|
||||
gl.beginQuery(this.disjointTimerQueryExt.TIME_ELAPSED_EXT, maskTimerQuery);
|
||||
gl.bindVertexArray(this.maskVertexArray);
|
||||
gl.useProgram(this.maskTileProgram.program);
|
||||
gl.uniform2f(this.maskTileProgram.uniforms.FramebufferSize,
|
||||
|
@ -398,11 +444,16 @@ class App {
|
|||
gl.enable(gl.BLEND);
|
||||
gl.drawArraysInstanced(gl.TRIANGLE_FAN, 0, 4, this.maskTileCount);
|
||||
gl.disable(gl.BLEND);
|
||||
if (maskTimerQuery != null)
|
||||
gl.endQuery(this.disjointTimerQueryExt.TIME_ELAPSED_EXT);
|
||||
|
||||
// End timer.
|
||||
if (timerQuery != null) {
|
||||
gl.endQuery(this.disjointTimerQueryExt.TIME_ELAPSED_EXT);
|
||||
waitForQuery(gl, this.disjointTimerQueryExt, timerQuery);
|
||||
if (fillTimerQuery != null && solidTimerQuery != null && maskTimerQuery != null) {
|
||||
processQueries(gl, this.disjointTimerQueryExt, {
|
||||
fill: fillTimerQuery,
|
||||
solid: solidTimerQuery,
|
||||
mask: maskTimerQuery,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -611,16 +662,37 @@ class RIFFChunk {
|
|||
}
|
||||
}
|
||||
|
||||
function waitForQuery(gl: WebGL2RenderingContext, disjointTimerQueryExt: any, query: WebGLQuery):
|
||||
void {
|
||||
interface Queries {
|
||||
fill: WebGLQuery;
|
||||
solid: WebGLQuery;
|
||||
mask: WebGLQuery;
|
||||
};
|
||||
|
||||
function getQueryResult(gl: WebGL2RenderingContext, disjointTimerQueryExt: any, query: WebGLQuery):
|
||||
Promise<number> {
|
||||
function go(resolve: (n: number) => void): void {
|
||||
const queryResultAvailable = disjointTimerQueryExt.QUERY_RESULT_AVAILABLE_EXT;
|
||||
const queryResult = disjointTimerQueryExt.QUERY_RESULT_EXT;
|
||||
if (!disjointTimerQueryExt.getQueryObjectEXT(query, queryResultAvailable)) {
|
||||
setTimeout(() => waitForQuery(gl, disjointTimerQueryExt, query), 10);
|
||||
setTimeout(() => go(resolve), 10);
|
||||
return;
|
||||
}
|
||||
const elapsed = disjointTimerQueryExt.getQueryObjectEXT(query, queryResult) / 1000000.0;
|
||||
console.log(elapsed + "ms elapsed");
|
||||
resolve(disjointTimerQueryExt.getQueryObjectEXT(query, queryResult) / 1000000.0);
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => go(resolve));
|
||||
}
|
||||
|
||||
function processQueries(gl: WebGL2RenderingContext, disjointTimerQueryExt: any, queries: Queries):
|
||||
void {
|
||||
Promise.all([
|
||||
getQueryResult(gl, disjointTimerQueryExt, queries.fill),
|
||||
getQueryResult(gl, disjointTimerQueryExt, queries.solid),
|
||||
getQueryResult(gl, disjointTimerQueryExt, queries.mask),
|
||||
]).then(results => {
|
||||
const [fillResult, solidResult, maskResult] = results;
|
||||
console.log(fillResult, "ms fill,", solidResult, "ms solid,", maskResult, "ms mask");
|
||||
});
|
||||
}
|
||||
|
||||
function loadAreaLUT(): Promise<HTMLImageElement> {
|
||||
|
|
|
@ -15,14 +15,13 @@ precision highp float;
|
|||
uniform sampler2D uAreaLUT;
|
||||
|
||||
in vec2 vFrom;
|
||||
in vec2 vCtrl;
|
||||
in vec2 vTo;
|
||||
|
||||
out vec4 oFragColor;
|
||||
|
||||
void main() {
|
||||
// Unpack.
|
||||
vec2 from = vFrom, ctrl = vCtrl, to = vTo;
|
||||
vec2 from = vFrom, to = vTo;
|
||||
|
||||
// Determine winding, and sort into a consistent order so we only need to find one root below.
|
||||
bool winding = from.x < to.x;
|
||||
|
|
|
@ -16,12 +16,13 @@ uniform vec2 uFramebufferSize;
|
|||
uniform vec2 uTileSize;
|
||||
|
||||
in vec2 aTessCoord;
|
||||
in vec2 aFrom;
|
||||
in vec2 aTo;
|
||||
in uint aFromPx;
|
||||
in uint aToPx;
|
||||
in vec2 aFromSubpx;
|
||||
in vec2 aToSubpx;
|
||||
in uint aTileIndex;
|
||||
|
||||
out vec2 vFrom;
|
||||
out vec2 vCtrl;
|
||||
out vec2 vTo;
|
||||
|
||||
vec2 computeTileOffset(uint tileIndex, float stencilTextureWidth) {
|
||||
|
@ -33,8 +34,8 @@ vec2 computeTileOffset(uint tileIndex, float stencilTextureWidth) {
|
|||
void main() {
|
||||
vec2 tileOrigin = computeTileOffset(aTileIndex, uFramebufferSize.x);
|
||||
|
||||
vec2 from = clamp(aFrom, vec2(0.0), uTileSize);
|
||||
vec2 to = clamp(aTo, vec2(0.0), uTileSize);
|
||||
vec2 from = vec2(aFromPx & 15u, aFromPx >> 4u) + aFromSubpx;
|
||||
vec2 to = vec2(aToPx & 15u, aToPx >> 4u) + aToSubpx;
|
||||
|
||||
vec2 position;
|
||||
bool zeroArea = !(abs(from.x - to.x) > 0.1) || !(abs(uTileSize.y - min(from.y, to.y)) > 0.1);
|
||||
|
@ -50,8 +51,6 @@ void main() {
|
|||
vFrom = from - position;
|
||||
vTo = to - position;
|
||||
|
||||
vCtrl = mix(vFrom, vTo, 0.5);
|
||||
|
||||
if (zeroArea)
|
||||
gl_Position = vec4(0.0);
|
||||
else
|
||||
|
|
|
@ -40,7 +40,7 @@ use std::ops::Range;
|
|||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use std::time::Instant;
|
||||
use std::u32;
|
||||
use std::u16;
|
||||
use svgtypes::{Color as SvgColor, PathParser, PathSegment as SvgPathSegment, TransformListParser};
|
||||
use svgtypes::{TransformListToken};
|
||||
|
||||
|
@ -339,7 +339,7 @@ impl Scene {
|
|||
&self.styles[style.0 as usize]
|
||||
}
|
||||
|
||||
fn build_shader(&self, object_index: u32) -> ObjectShader {
|
||||
fn build_shader(&self, object_index: u16) -> ObjectShader {
|
||||
ObjectShader {
|
||||
fill_color: self.objects[object_index as usize].color,
|
||||
}
|
||||
|
@ -349,9 +349,9 @@ impl Scene {
|
|||
fn build_objects_sequentially(&self) -> Vec<BuiltObject> {
|
||||
self.objects.iter().enumerate().map(|(object_index, object)| {
|
||||
let mut tiler = Tiler::new(&object.outline,
|
||||
object_index as u32,
|
||||
object_index as u16,
|
||||
&self.view_box,
|
||||
&self.build_shader(object_index as u32));
|
||||
&self.build_shader(object_index as u16));
|
||||
tiler.generate_tiles();
|
||||
tiler.built_object
|
||||
}).collect()
|
||||
|
@ -360,9 +360,9 @@ impl Scene {
|
|||
fn build_objects(&self) -> Vec<BuiltObject> {
|
||||
self.objects.par_iter().enumerate().map(|(object_index, object)| {
|
||||
let mut tiler = Tiler::new(&object.outline,
|
||||
object_index as u32,
|
||||
object_index as u16,
|
||||
&self.view_box,
|
||||
&self.build_shader(object_index as u32));
|
||||
&self.build_shader(object_index as u16));
|
||||
tiler.generate_tiles();
|
||||
tiler.built_object
|
||||
}).collect()
|
||||
|
@ -946,7 +946,7 @@ const TILE_HEIGHT: f32 = 16.0;
|
|||
|
||||
struct Tiler<'o> {
|
||||
outline: &'o Outline,
|
||||
object_index: u32,
|
||||
object_index: u16,
|
||||
built_object: BuiltObject,
|
||||
|
||||
view_box: Rect<f32>,
|
||||
|
@ -958,7 +958,7 @@ struct Tiler<'o> {
|
|||
}
|
||||
|
||||
impl<'o> Tiler<'o> {
|
||||
fn new(outline: &'o Outline, object_index: u32, view_box: &Rect<f32>, shader: &ObjectShader)
|
||||
fn new(outline: &'o Outline, object_index: u16, view_box: &Rect<f32>, shader: &ObjectShader)
|
||||
-> Tiler<'o> {
|
||||
let bounds = outline.bounds.intersection(&view_box).unwrap_or(Rect::zero());
|
||||
let built_object = BuiltObject::new(&bounds, shader);
|
||||
|
@ -1255,16 +1255,16 @@ impl BuiltScene {
|
|||
}
|
||||
|
||||
let scene_tile_index = scene.scene_tile_index(tile.tile_x, tile.tile_y);
|
||||
if z_buffer[scene_tile_index as usize] > object_index {
|
||||
if z_buffer[scene_tile_index as usize] > object_index as u16 {
|
||||
// Occluded.
|
||||
continue
|
||||
}
|
||||
z_buffer[scene_tile_index as usize] = object_index;
|
||||
z_buffer[scene_tile_index as usize] = object_index as u16;
|
||||
|
||||
scene.solid_tiles.push(SolidTileScenePrimitive {
|
||||
tile_x: tile.tile_x,
|
||||
tile_y: tile.tile_y,
|
||||
object_index: object_index as u32,
|
||||
object_index: object_index as u16,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1278,23 +1278,23 @@ impl BuiltScene {
|
|||
for (tile_index, tile) in object.tiles.iter().enumerate() {
|
||||
// Skip solid tiles, since we handled them above already.
|
||||
if object.solid_tiles[tile_index] {
|
||||
object_tile_index_to_scene_mask_tile_index.push(u32::MAX);
|
||||
object_tile_index_to_scene_mask_tile_index.push(u16::MAX);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Cull occluded tiles.
|
||||
let scene_tile_index = scene.scene_tile_index(tile.tile_x, tile.tile_y);
|
||||
if z_buffer[scene_tile_index as usize] > object_index {
|
||||
object_tile_index_to_scene_mask_tile_index.push(u32::MAX);
|
||||
if z_buffer[scene_tile_index as usize] as usize > object_index {
|
||||
object_tile_index_to_scene_mask_tile_index.push(u16::MAX);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Visible mask tile.
|
||||
let scene_mask_tile_index = scene.mask_tiles.len() as u32;
|
||||
let scene_mask_tile_index = scene.mask_tiles.len() as u16;
|
||||
object_tile_index_to_scene_mask_tile_index.push(scene_mask_tile_index);
|
||||
scene.mask_tiles.push(MaskTileScenePrimitive {
|
||||
tile: *tile,
|
||||
object_index: object_index as u32,
|
||||
object_index: object_index as u16,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1302,11 +1302,13 @@ impl BuiltScene {
|
|||
for fill in &object.fills {
|
||||
let object_tile_index = object.tile_coords_to_index(fill.tile_x, fill.tile_y);
|
||||
match object_tile_index_to_scene_mask_tile_index[object_tile_index as usize] {
|
||||
u32::MAX => {}
|
||||
u16::MAX => {}
|
||||
scene_mask_tile_index => {
|
||||
scene.fills.push(FillScenePrimitive {
|
||||
from: fill.from,
|
||||
to: fill.to,
|
||||
from_px: fill.from_px,
|
||||
to_px: fill.to_px,
|
||||
from_subpx: fill.from_subpx,
|
||||
to_subpx: fill.to_subpx,
|
||||
mask_tile_index: scene_mask_tile_index,
|
||||
})
|
||||
}
|
||||
|
@ -1351,8 +1353,10 @@ struct BuiltScene {
|
|||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct FillObjectPrimitive {
|
||||
from: Point2D<f32>,
|
||||
to: Point2D<f32>,
|
||||
from_px: Point2DU4,
|
||||
to_px: Point2DU4,
|
||||
from_subpx: Point2D<u8>,
|
||||
to_subpx: Point2D<u8>,
|
||||
tile_x: i16,
|
||||
tile_y: i16,
|
||||
}
|
||||
|
@ -1361,27 +1365,29 @@ struct FillObjectPrimitive {
|
|||
struct TileObjectPrimitive {
|
||||
tile_x: i16,
|
||||
tile_y: i16,
|
||||
backdrop: i32,
|
||||
backdrop: i16,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct FillScenePrimitive {
|
||||
from: Point2D<f32>,
|
||||
to: Point2D<f32>,
|
||||
mask_tile_index: u32,
|
||||
from_px: Point2DU4,
|
||||
to_px: Point2DU4,
|
||||
from_subpx: Point2D<u8>,
|
||||
to_subpx: Point2D<u8>,
|
||||
mask_tile_index: u16,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct SolidTileScenePrimitive {
|
||||
tile_x: i16,
|
||||
tile_y: i16,
|
||||
object_index: u32,
|
||||
object_index: u16,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct MaskTileScenePrimitive {
|
||||
tile: TileObjectPrimitive,
|
||||
object_index: u32,
|
||||
object_index: u16,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
|
@ -1431,6 +1437,11 @@ impl BuiltObject {
|
|||
let tile_index = self.tile_coords_to_index(tile_x, tile_y);
|
||||
let (from, to) = (*from - tile_origin, *to - tile_origin);
|
||||
|
||||
let from = Point2D::new(clamp(from.x, 0.0, MAX_U12), clamp(from.y, 0.0, MAX_U12));
|
||||
let to = Point2D::new(clamp(to.x, 0.0, MAX_U12), clamp(to.y, 0.0, MAX_U12));
|
||||
|
||||
const MAX_U12: f32 = 16.0 - 1.0 / 256.0;
|
||||
|
||||
/*
|
||||
println!("from={:?} to={:?}", from, to);
|
||||
debug_assert!(from.x > -EPSILON);
|
||||
|
@ -1443,7 +1454,18 @@ impl BuiltObject {
|
|||
debug_assert!(to.y < TILE_HEIGHT + EPSILON);
|
||||
*/
|
||||
|
||||
self.fills.push(FillObjectPrimitive { from, to, tile_x, tile_y });
|
||||
let from_px = Point2DU4::new(from.x as u8, from.y as u8);
|
||||
let to_px = Point2DU4::new(to.x as u8, to.y as u8);
|
||||
let from_subpx = Point2D::new((from.x.fract() * 256.0) as u8,
|
||||
(from.y.fract() * 256.0) as u8);
|
||||
let to_subpx = Point2D::new((to.x.fract() * 256.0) as u8, (to.y.fract() * 256.0) as u8);
|
||||
|
||||
self.fills.push(FillObjectPrimitive {
|
||||
from_px, to_px,
|
||||
from_subpx, to_subpx,
|
||||
tile_x, tile_y,
|
||||
});
|
||||
|
||||
self.solid_tiles.set(tile_index as usize, false);
|
||||
|
||||
// FIXME(pcwalton): This is really sloppy!
|
||||
|
@ -1496,9 +1518,11 @@ impl BuiltScene {
|
|||
writer.write_all(b"fill")?;
|
||||
writer.write_u32::<LittleEndian>(fill_size as u32)?;
|
||||
for fill_primitive in &self.fills {
|
||||
write_point(writer, &fill_primitive.from)?;
|
||||
write_point(writer, &fill_primitive.to)?;
|
||||
writer.write_u32::<LittleEndian>(fill_primitive.mask_tile_index)?;
|
||||
writer.write_u8(fill_primitive.from_px.0)?;
|
||||
writer.write_u8(fill_primitive.to_px.0)?;
|
||||
write_point2d_u8(writer, fill_primitive.from_subpx)?;
|
||||
write_point2d_u8(writer, fill_primitive.to_subpx)?;
|
||||
writer.write_u16::<LittleEndian>(fill_primitive.mask_tile_index)?;
|
||||
}
|
||||
|
||||
writer.write_all(b"soli")?;
|
||||
|
@ -1506,7 +1530,7 @@ impl BuiltScene {
|
|||
for &tile_primitive in &self.solid_tiles {
|
||||
writer.write_i16::<LittleEndian>(tile_primitive.tile_x)?;
|
||||
writer.write_i16::<LittleEndian>(tile_primitive.tile_y)?;
|
||||
writer.write_u32::<LittleEndian>(tile_primitive.object_index)?;
|
||||
writer.write_u16::<LittleEndian>(tile_primitive.object_index)?;
|
||||
}
|
||||
|
||||
writer.write_all(b"mask")?;
|
||||
|
@ -1514,8 +1538,8 @@ impl BuiltScene {
|
|||
for &tile_primitive in &self.mask_tiles {
|
||||
writer.write_i16::<LittleEndian>(tile_primitive.tile.tile_x)?;
|
||||
writer.write_i16::<LittleEndian>(tile_primitive.tile.tile_y)?;
|
||||
writer.write_i32::<LittleEndian>(tile_primitive.tile.backdrop)?;
|
||||
writer.write_u32::<LittleEndian>(tile_primitive.object_index)?;
|
||||
writer.write_i16::<LittleEndian>(tile_primitive.tile.backdrop)?;
|
||||
writer.write_u16::<LittleEndian>(tile_primitive.object_index)?;
|
||||
}
|
||||
|
||||
writer.write_all(b"shad")?;
|
||||
|
@ -1527,16 +1551,17 @@ impl BuiltScene {
|
|||
|
||||
return Ok(());
|
||||
|
||||
fn write_point<W>(writer: &mut W, point: &Point2D<f32>) -> io::Result<()> where W: Write {
|
||||
writer.write_f32::<LittleEndian>(point.x)?;
|
||||
writer.write_f32::<LittleEndian>(point.y)?;
|
||||
fn write_point2d_u8<W>(writer: &mut W, point: Point2D<u8>)
|
||||
-> io::Result<()> where W: Write {
|
||||
writer.write_u8(point.x)?;
|
||||
writer.write_u8(point.y)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SolidTileScenePrimitive {
|
||||
fn new(tile_x: i16, tile_y: i16, object_index: u32) -> SolidTileScenePrimitive {
|
||||
fn new(tile_x: i16, tile_y: i16, object_index: u16) -> SolidTileScenePrimitive {
|
||||
SolidTileScenePrimitive { tile_x, tile_y, object_index }
|
||||
}
|
||||
}
|
||||
|
@ -1987,6 +2012,18 @@ impl PartialOrd<ActiveEdge> for ActiveEdge {
|
|||
}
|
||||
}
|
||||
|
||||
// Geometry
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct Point2DU4(pub u8);
|
||||
|
||||
impl Point2DU4 {
|
||||
fn new(x: u8, y: u8) -> Point2DU4 { Point2DU4(x | (y << 4)) }
|
||||
|
||||
fn x(self) -> u8 { self.0 & 0xf }
|
||||
fn y(self) -> u8 { self.0 >> 4 }
|
||||
}
|
||||
|
||||
// Path utilities
|
||||
|
||||
/*
|
||||
|
|
Loading…
Reference in New Issue