use "32 bit alignment" and "explicit shaders" pathfinder branches
|
@ -12,8 +12,8 @@ bevy_asset = { path = "../bevy_asset" }
|
||||||
bevy_render = { path = "../bevy_render" }
|
bevy_render = { path = "../bevy_render" }
|
||||||
legion = { path = "../bevy_legion" }
|
legion = { path = "../bevy_legion" }
|
||||||
|
|
||||||
pathfinder_geometry = { path = "../pathfinder/geometry" }
|
pathfinder_geometry = { path = "../pathfinder/geometry", features = ["shader_alignment_32_bits"] }
|
||||||
pathfinder_gpu = { path = "../pathfinder/gpu" }
|
pathfinder_gpu = { path = "../pathfinder/gpu", features = ["shader_alignment_32_bits"] }
|
||||||
pathfinder_renderer = { path = "../pathfinder/renderer" }
|
pathfinder_renderer = { path = "../pathfinder/renderer" }
|
||||||
pathfinder_simd = { path = "../pathfinder/simd" }
|
pathfinder_simd = { path = "../pathfinder/simd" }
|
||||||
pathfinder_resources = { path = "../pathfinder/resources" }
|
pathfinder_resources = { path = "../pathfinder/resources" }
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use crate::shaders;
|
|
||||||
use bevy_asset::{AssetStorage, Handle};
|
use bevy_asset::{AssetStorage, Handle};
|
||||||
use bevy_render::{
|
use bevy_render::{
|
||||||
pipeline::{
|
pipeline::{
|
||||||
|
@ -6,7 +5,7 @@ use bevy_render::{
|
||||||
VertexAttributeDescriptor, VertexBufferDescriptor, VertexFormat,
|
VertexAttributeDescriptor, VertexBufferDescriptor, VertexFormat,
|
||||||
},
|
},
|
||||||
render_resource::{BufferInfo, BufferUsage, RenderResource},
|
render_resource::{BufferInfo, BufferUsage, RenderResource},
|
||||||
renderer::{RenderContext, RenderResourceContext},
|
renderer::RenderContext,
|
||||||
shader::{Shader, ShaderSource, ShaderStage, ShaderStages},
|
shader::{Shader, ShaderSource, ShaderStage, ShaderStages},
|
||||||
texture::{
|
texture::{
|
||||||
AddressMode, Extent3d, FilterMode, SamplerDescriptor, TextureDescriptor, TextureDimension,
|
AddressMode, Extent3d, FilterMode, SamplerDescriptor, TextureDescriptor, TextureDimension,
|
||||||
|
@ -16,9 +15,9 @@ use bevy_render::{
|
||||||
use pathfinder_canvas::vec2i;
|
use pathfinder_canvas::vec2i;
|
||||||
use pathfinder_geometry::{rect::RectI, vector::Vector2I};
|
use pathfinder_geometry::{rect::RectI, vector::Vector2I};
|
||||||
use pathfinder_gpu::{
|
use pathfinder_gpu::{
|
||||||
BufferData, BufferTarget, BufferUploadMode, Device, RenderState, RenderTarget, ShaderKind,
|
BufferData, BufferTarget, BufferUploadMode, Device, ProgramKind, RenderState, RenderTarget,
|
||||||
TextureData, TextureDataRef, TextureSamplingFlags, VertexAttrClass, VertexAttrDescriptor,
|
ShaderKind, TextureData, TextureDataRef, TextureSamplingFlags, VertexAttrClass,
|
||||||
VertexAttrType,
|
VertexAttrDescriptor, VertexAttrType, FeatureLevel,
|
||||||
};
|
};
|
||||||
use pathfinder_resources::ResourceLoader;
|
use pathfinder_resources::ResourceLoader;
|
||||||
use std::{borrow::Cow, cell::RefCell, collections::HashMap, mem, rc::Rc, time::Duration};
|
use std::{borrow::Cow, cell::RefCell, collections::HashMap, mem, rc::Rc, time::Duration};
|
||||||
|
@ -46,7 +45,7 @@ impl<'a> BevyPathfinderDevice<'a> {
|
||||||
pub struct BevyTimerQuery {}
|
pub struct BevyTimerQuery {}
|
||||||
pub struct BevyTextureDataReceiver {}
|
pub struct BevyTextureDataReceiver {}
|
||||||
pub struct BevyUniform {
|
pub struct BevyUniform {
|
||||||
name: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct BevyVertexAttr {
|
pub struct BevyVertexAttr {
|
||||||
|
@ -56,8 +55,8 @@ pub struct BevyVertexAttr {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct BevyVertexArray {
|
pub struct BevyVertexArray {
|
||||||
requested_descriptors: RefCell<HashMap<u32, VertexBufferDescriptor>>,
|
requested_descriptors: RefCell<HashMap<u32, VertexBufferDescriptor>>,
|
||||||
vertex_buffers: RefCell<Vec<RenderResource>>,
|
vertex_buffers: RefCell<Vec<BevyBuffer>>,
|
||||||
index_buffer: RefCell<Option<RenderResource>>,
|
index_buffer: RefCell<Option<BevyBuffer>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct BevyTexture {
|
pub struct BevyTexture {
|
||||||
|
@ -66,15 +65,19 @@ pub struct BevyTexture {
|
||||||
sampler_resource: RefCell<Option<RenderResource>>,
|
sampler_resource: RefCell<Option<RenderResource>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct BevyBuffer {
|
pub struct BevyBuffer {
|
||||||
handle: Rc<RefCell<Option<RenderResource>>>,
|
handle: Rc<RefCell<Option<RenderResource>>>,
|
||||||
|
mode: BufferUploadMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Device for BevyPathfinderDevice<'a> {
|
impl<'a> Device for BevyPathfinderDevice<'a> {
|
||||||
type Buffer = BevyBuffer;
|
type Buffer = BevyBuffer;
|
||||||
|
type Fence = ();
|
||||||
type Framebuffer = BevyTexture;
|
type Framebuffer = BevyTexture;
|
||||||
type Program = PipelineDescriptor;
|
type Program = PipelineDescriptor;
|
||||||
type Shader = Handle<Shader>;
|
type Shader = Handle<Shader>;
|
||||||
|
type StorageBuffer = ();
|
||||||
type Texture = BevyTexture;
|
type Texture = BevyTexture;
|
||||||
type TextureDataReceiver = BevyTextureDataReceiver;
|
type TextureDataReceiver = BevyTextureDataReceiver;
|
||||||
type TimerQuery = BevyTimerQuery;
|
type TimerQuery = BevyTimerQuery;
|
||||||
|
@ -127,31 +130,38 @@ impl<'a> Device for BevyPathfinderDevice<'a> {
|
||||||
}
|
}
|
||||||
fn create_shader(
|
fn create_shader(
|
||||||
&self,
|
&self,
|
||||||
_resources: &dyn ResourceLoader,
|
resources: &dyn ResourceLoader,
|
||||||
name: &str,
|
name: &str,
|
||||||
kind: ShaderKind,
|
kind: ShaderKind,
|
||||||
) -> Self::Shader {
|
) -> Self::Shader {
|
||||||
let shader_bytes = match (name, kind) {
|
// let shader_bytes = match (name, kind) {
|
||||||
("blit", ShaderKind::Fragment) => shaders::BLIT_FS,
|
// ("blit", ShaderKind::Fragment) => shaders::BLIT_FS,
|
||||||
("blit", ShaderKind::Vertex) => shaders::BLIT_VS,
|
// ("blit", ShaderKind::Vertex) => shaders::BLIT_VS,
|
||||||
// ("debug_solid", ShaderKind::Fragment) => shaders::DEMO_GROUND_FS,
|
// // ("debug_solid", ShaderKind::Fragment) => shaders::DEMO_GROUND_FS,
|
||||||
// ("demo_ground", ShaderKind::Vertex) => shaders::DEMO_GROUND_VS,
|
// // ("demo_ground", ShaderKind::Vertex) => shaders::DEMO_GROUND_VS,
|
||||||
("fill", ShaderKind::Fragment) => shaders::FILL_FS,
|
// ("fill", ShaderKind::Fragment) => shaders::FILL_FS,
|
||||||
("fill", ShaderKind::Vertex) => shaders::FILL_VS,
|
// ("fill", ShaderKind::Vertex) => shaders::FILL_VS,
|
||||||
("reproject", ShaderKind::Fragment) => shaders::REPROJECT_FS,
|
// ("reproject", ShaderKind::Fragment) => shaders::REPROJECT_FS,
|
||||||
("reproject", ShaderKind::Vertex) => shaders::REPROJECT_VS,
|
// ("reproject", ShaderKind::Vertex) => shaders::REPROJECT_VS,
|
||||||
("stencil", ShaderKind::Fragment) => shaders::STENCIL_FS,
|
// ("stencil", ShaderKind::Fragment) => shaders::STENCIL_FS,
|
||||||
("stencil", ShaderKind::Vertex) => shaders::STENCIL_VS,
|
// ("stencil", ShaderKind::Vertex) => shaders::STENCIL_VS,
|
||||||
("tile_clip", ShaderKind::Fragment) => shaders::TILE_CLIP_FS,
|
// ("tile_clip", ShaderKind::Fragment) => shaders::TILE_CLIP_FS,
|
||||||
("tile_clip", ShaderKind::Vertex) => shaders::TILE_CLIP_VS,
|
// ("tile_clip", ShaderKind::Vertex) => shaders::TILE_CLIP_VS,
|
||||||
("tile_copy", ShaderKind::Fragment) => shaders::TILE_COPY_FS,
|
// ("tile_copy", ShaderKind::Fragment) => shaders::TILE_COPY_FS,
|
||||||
("tile_copy", ShaderKind::Vertex) => shaders::TILE_COPY_VS,
|
// ("tile_copy", ShaderKind::Vertex) => shaders::TILE_COPY_VS,
|
||||||
("tile", ShaderKind::Fragment) => shaders::TILE_FS,
|
// ("tile", ShaderKind::Fragment) => shaders::TILE_FS,
|
||||||
("tile", ShaderKind::Vertex) => shaders::TILE_VS,
|
// ("tile", ShaderKind::Vertex) => shaders::TILE_VS,
|
||||||
_ => panic!("encountered unexpected shader {} {:?}", name, kind),
|
// _ => panic!("encountered unexpected shader {} {:?}", name, kind),
|
||||||
|
// };
|
||||||
|
let suffix = match kind {
|
||||||
|
ShaderKind::Vertex => 'v',
|
||||||
|
ShaderKind::Fragment => 'f',
|
||||||
|
ShaderKind::Compute => 'c',
|
||||||
};
|
};
|
||||||
|
let path = format!("shaders/vulkan/{}.{}s.spv", name, suffix);
|
||||||
|
let bytes = resources.slurp(&path).unwrap();
|
||||||
|
|
||||||
self.create_shader_from_source(name, shader_bytes, kind)
|
self.create_shader_from_source(name, &bytes, kind)
|
||||||
}
|
}
|
||||||
fn create_shader_from_source(
|
fn create_shader_from_source(
|
||||||
&self,
|
&self,
|
||||||
|
@ -162,6 +172,7 @@ impl<'a> Device for BevyPathfinderDevice<'a> {
|
||||||
let stage = match kind {
|
let stage = match kind {
|
||||||
ShaderKind::Fragment => ShaderStage::Fragment,
|
ShaderKind::Fragment => ShaderStage::Fragment,
|
||||||
ShaderKind::Vertex => ShaderStage::Vertex,
|
ShaderKind::Vertex => ShaderStage::Vertex,
|
||||||
|
ShaderKind::Compute => panic!("bevy does not currently support compute shaders"),
|
||||||
};
|
};
|
||||||
let shader = Shader::new(stage, ShaderSource::spirv_from_bytes(source));
|
let shader = Shader::new(stage, ShaderSource::spirv_from_bytes(source));
|
||||||
let mut shaders = self.shaders.borrow_mut();
|
let mut shaders = self.shaders.borrow_mut();
|
||||||
|
@ -183,18 +194,21 @@ impl<'a> Device for BevyPathfinderDevice<'a> {
|
||||||
fn create_program_from_shaders(
|
fn create_program_from_shaders(
|
||||||
&self,
|
&self,
|
||||||
_resources: &dyn ResourceLoader,
|
_resources: &dyn ResourceLoader,
|
||||||
name: &str,
|
_name: &str,
|
||||||
vertex_shader: Self::Shader,
|
shaders: ProgramKind<Handle<Shader>>,
|
||||||
fragment_shader: Self::Shader,
|
|
||||||
) -> Self::Program {
|
) -> Self::Program {
|
||||||
println!("{}", name);
|
match shaders {
|
||||||
|
ProgramKind::Compute(_) => panic!("bevy does not currently support compute shaders"),
|
||||||
|
ProgramKind::Raster { vertex, fragment } => {
|
||||||
let mut descriptor = PipelineDescriptor::new(ShaderStages {
|
let mut descriptor = PipelineDescriptor::new(ShaderStages {
|
||||||
vertex: vertex_shader,
|
vertex,
|
||||||
fragment: Some(fragment_shader),
|
fragment: Some(fragment),
|
||||||
});
|
});
|
||||||
descriptor.reflect_layout(&self.shaders.borrow(), false, None, None);
|
descriptor.reflect_layout(&self.shaders.borrow(), false, None, None);
|
||||||
descriptor
|
descriptor
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
fn get_vertex_attr(
|
fn get_vertex_attr(
|
||||||
&self,
|
&self,
|
||||||
descriptor: &PipelineDescriptor,
|
descriptor: &PipelineDescriptor,
|
||||||
|
@ -228,19 +242,20 @@ impl<'a> Device for BevyPathfinderDevice<'a> {
|
||||||
}
|
}
|
||||||
fn bind_buffer(
|
fn bind_buffer(
|
||||||
&self,
|
&self,
|
||||||
vertex_array: &Self::VertexArray,
|
vertex_array: &BevyVertexArray,
|
||||||
buffer: &Self::Buffer,
|
buffer: &BevyBuffer,
|
||||||
target: BufferTarget,
|
target: BufferTarget,
|
||||||
) {
|
) {
|
||||||
match target {
|
match target {
|
||||||
BufferTarget::Vertex => vertex_array
|
BufferTarget::Vertex => vertex_array
|
||||||
.vertex_buffers
|
.vertex_buffers
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.push(buffer.handle.borrow().unwrap().clone()),
|
.push(buffer.clone()),
|
||||||
BufferTarget::Index => {
|
BufferTarget::Index => {
|
||||||
*vertex_array.index_buffer.borrow_mut() =
|
*vertex_array.index_buffer.borrow_mut() =
|
||||||
Some(buffer.handle.borrow().unwrap().clone())
|
Some(buffer.clone())
|
||||||
}
|
}
|
||||||
|
_ => panic!("Buffers bound to vertex arrays must be vertex or index buffers!"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn configure_vertex_attr(
|
fn configure_vertex_attr(
|
||||||
|
@ -282,6 +297,14 @@ impl<'a> Device for BevyPathfinderDevice<'a> {
|
||||||
// VertexFormat::Short3Normalized
|
// VertexFormat::Short3Normalized
|
||||||
// }
|
// }
|
||||||
(VertexAttrClass::FloatNorm, VertexAttrType::I16, 4) => VertexFormat::Short4Norm,
|
(VertexAttrClass::FloatNorm, VertexAttrType::I16, 4) => VertexFormat::Short4Norm,
|
||||||
|
(VertexAttrClass::Int, VertexAttrType::I32, 1) => VertexFormat::Int,
|
||||||
|
(VertexAttrClass::Int, VertexAttrType::I32, 2) => VertexFormat::Int2,
|
||||||
|
(VertexAttrClass::Int, VertexAttrType::I32, 3) => VertexFormat::Int3,
|
||||||
|
(VertexAttrClass::Int, VertexAttrType::I32, 4) => VertexFormat::Int4,
|
||||||
|
(VertexAttrClass::Int, VertexAttrType::U32, 1) => VertexFormat::Uint,
|
||||||
|
(VertexAttrClass::Int, VertexAttrType::U32, 2) => VertexFormat::Uint2,
|
||||||
|
(VertexAttrClass::Int, VertexAttrType::U32, 3) => VertexFormat::Uint3,
|
||||||
|
(VertexAttrClass::Int, VertexAttrType::U32, 4) => VertexFormat::Uint4,
|
||||||
(VertexAttrClass::Float, VertexAttrType::F32, 1) => VertexFormat::Float,
|
(VertexAttrClass::Float, VertexAttrType::F32, 1) => VertexFormat::Float,
|
||||||
(VertexAttrClass::Float, VertexAttrType::F32, 2) => VertexFormat::Float2,
|
(VertexAttrClass::Float, VertexAttrType::F32, 2) => VertexFormat::Float2,
|
||||||
(VertexAttrClass::Float, VertexAttrType::F32, 3) => VertexFormat::Float3,
|
(VertexAttrClass::Float, VertexAttrType::F32, 3) => VertexFormat::Float3,
|
||||||
|
@ -340,20 +363,15 @@ impl<'a> Device for BevyPathfinderDevice<'a> {
|
||||||
texture
|
texture
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_buffer(&self) -> Self::Buffer {
|
fn create_buffer(&self, mode: BufferUploadMode) -> Self::Buffer {
|
||||||
BevyBuffer {
|
BevyBuffer {
|
||||||
handle: Rc::new(RefCell::new(None)),
|
handle: Rc::new(RefCell::new(None)),
|
||||||
|
mode,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn allocate_buffer<T>(
|
fn allocate_buffer<T>(&self, buffer: &BevyBuffer, data: BufferData<T>, _target: BufferTarget) {
|
||||||
&self,
|
let buffer_usage = match buffer.mode {
|
||||||
buffer: &BevyBuffer,
|
|
||||||
data: BufferData<T>,
|
|
||||||
_target: BufferTarget,
|
|
||||||
mode: BufferUploadMode,
|
|
||||||
) {
|
|
||||||
let buffer_usage = match mode {
|
|
||||||
BufferUploadMode::Dynamic => BufferUsage::WRITE_ALL,
|
BufferUploadMode::Dynamic => BufferUsage::WRITE_ALL,
|
||||||
BufferUploadMode::Static => BufferUsage::COPY_DST,
|
BufferUploadMode::Static => BufferUsage::COPY_DST,
|
||||||
};
|
};
|
||||||
|
@ -504,8 +522,8 @@ impl<'a> Device for BevyPathfinderDevice<'a> {
|
||||||
}
|
}
|
||||||
fn read_pixels(
|
fn read_pixels(
|
||||||
&self,
|
&self,
|
||||||
target: &RenderTarget<Self>,
|
_target: &RenderTarget<Self>,
|
||||||
viewport: RectI,
|
_viewport: RectI,
|
||||||
) -> Self::TextureDataReceiver {
|
) -> Self::TextureDataReceiver {
|
||||||
// TODO: this might actually be optional, which is great because otherwise this requires a command buffer sync
|
// TODO: this might actually be optional, which is great because otherwise this requires a command buffer sync
|
||||||
todo!()
|
todo!()
|
||||||
|
@ -516,17 +534,17 @@ impl<'a> Device for BevyPathfinderDevice<'a> {
|
||||||
fn end_commands(&self) {
|
fn end_commands(&self) {
|
||||||
// NOTE: the Bevy Render Graph handles command buffer submission
|
// NOTE: the Bevy Render Graph handles command buffer submission
|
||||||
}
|
}
|
||||||
fn draw_arrays(&self, index_count: u32, render_state: &RenderState<Self>) {
|
fn draw_arrays(&self, _index_count: u32, _render_state: &RenderState<Self>) {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
fn draw_elements(&self, index_count: u32, render_state: &RenderState<Self>) {
|
fn draw_elements(&self, _index_count: u32, _render_state: &RenderState<Self>) {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
fn draw_elements_instanced(
|
fn draw_elements_instanced(
|
||||||
&self,
|
&self,
|
||||||
index_count: u32,
|
_index_count: u32,
|
||||||
instance_count: u32,
|
_instance_count: u32,
|
||||||
render_state: &RenderState<Self>,
|
_render_state: &RenderState<Self>,
|
||||||
) {
|
) {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
@ -534,27 +552,82 @@ impl<'a> Device for BevyPathfinderDevice<'a> {
|
||||||
// TODO: maybe not needed
|
// TODO: maybe not needed
|
||||||
BevyTimerQuery {}
|
BevyTimerQuery {}
|
||||||
}
|
}
|
||||||
fn begin_timer_query(&self, query: &Self::TimerQuery) {}
|
fn begin_timer_query(&self, _query: &Self::TimerQuery) {}
|
||||||
fn end_timer_query(&self, query: &Self::TimerQuery) {}
|
fn end_timer_query(&self, _query: &Self::TimerQuery) {}
|
||||||
fn try_recv_timer_query(&self, query: &Self::TimerQuery) -> Option<std::time::Duration> {
|
fn try_recv_timer_query(&self, _query: &Self::TimerQuery) -> Option<std::time::Duration> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
fn recv_timer_query(&self, query: &Self::TimerQuery) -> std::time::Duration {
|
fn recv_timer_query(&self, _query: &Self::TimerQuery) -> std::time::Duration {
|
||||||
Duration::from_millis(0)
|
Duration::from_millis(0)
|
||||||
}
|
}
|
||||||
fn try_recv_texture_data(&self, receiver: &Self::TextureDataReceiver) -> Option<TextureData> {
|
fn try_recv_texture_data(&self, _receiver: &Self::TextureDataReceiver) -> Option<TextureData> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
fn recv_texture_data(&self, receiver: &Self::TextureDataReceiver) -> TextureData {
|
fn recv_texture_data(&self, _receiver: &Self::TextureDataReceiver) -> TextureData {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
fn feature_level(&self) -> pathfinder_gpu::FeatureLevel {
|
||||||
|
// TODO: change to 11 when compute is added
|
||||||
|
FeatureLevel::D3D10
|
||||||
|
}
|
||||||
|
fn set_compute_program_local_size(
|
||||||
|
&self,
|
||||||
|
_program: &mut Self::Program,
|
||||||
|
_local_size: pathfinder_gpu::ComputeDimensions,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
fn get_storage_buffer(
|
||||||
|
&self,
|
||||||
|
_program: &Self::Program,
|
||||||
|
_name: &str,
|
||||||
|
_binding: u32,
|
||||||
|
) -> Self::StorageBuffer {
|
||||||
|
panic!("Compute shader is unsupported in Bevy!");
|
||||||
|
}
|
||||||
|
fn upload_to_buffer<T>(
|
||||||
|
&self,
|
||||||
|
buffer: &BevyBuffer,
|
||||||
|
position: usize,
|
||||||
|
data: &[T],
|
||||||
|
_target: BufferTarget,
|
||||||
|
) {
|
||||||
|
let data_slice = &data[position..];
|
||||||
|
let temp_buffer = self
|
||||||
|
.render_context
|
||||||
|
.borrow()
|
||||||
|
.resources()
|
||||||
|
.create_buffer_with_data(
|
||||||
|
BufferInfo {
|
||||||
|
buffer_usage: BufferUsage::COPY_DST,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
slice_to_u8(data_slice),
|
||||||
|
);
|
||||||
|
let buffer_handle = buffer.handle.borrow().unwrap();
|
||||||
|
self.render_context.borrow_mut().copy_buffer_to_buffer(
|
||||||
|
temp_buffer,
|
||||||
|
0,
|
||||||
|
buffer_handle,
|
||||||
|
0,
|
||||||
|
data_slice.len() as u64,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
fn dispatch_compute(
|
||||||
|
&self,
|
||||||
|
_dimensions: pathfinder_gpu::ComputeDimensions,
|
||||||
|
_state: &pathfinder_gpu::ComputeState<Self>,
|
||||||
|
) {
|
||||||
|
panic!("Compute shader is unsupported in Bevy!");
|
||||||
|
}
|
||||||
|
fn add_fence(&self) -> Self::Fence {}
|
||||||
|
fn wait_for_fence(&self, _fence: &Self::Fence) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_texture_bytes<'a>(data_ref: &'a TextureDataRef) -> &'a [u8] {
|
fn get_texture_bytes<'a>(data_ref: &'a TextureDataRef) -> &'a [u8] {
|
||||||
match data_ref {
|
match data_ref {
|
||||||
TextureDataRef::U8(data) => data,
|
TextureDataRef::U8(data) => data,
|
||||||
TextureDataRef::F16(data) => {
|
TextureDataRef::F16(data) => {
|
||||||
panic!("we dont do half measures");
|
slice_to_u8(data)
|
||||||
}
|
}
|
||||||
TextureDataRef::F32(data) => data.as_bytes(),
|
TextureDataRef::F32(data) => data.as_bytes(),
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
mod device;
|
mod device;
|
||||||
mod resource_loader;
|
|
||||||
mod pathfinder_node;
|
mod pathfinder_node;
|
||||||
mod shaders;
|
|
||||||
use bevy_app::{AppBuilder, AppPlugin};
|
use bevy_app::{AppBuilder, AppPlugin};
|
||||||
pub use device::*;
|
pub use device::*;
|
||||||
pub use resource_loader::*;
|
|
||||||
|
|
||||||
use bevy_render::render_graph::RenderGraph;
|
use bevy_render::render_graph::RenderGraph;
|
||||||
use pathfinder_node::PathfinderNode;
|
use pathfinder_node::PathfinderNode;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{BevyPathfinderDevice, BevyResourceLoader};
|
use crate::BevyPathfinderDevice;
|
||||||
use bevy_asset::AssetStorage;
|
use bevy_asset::AssetStorage;
|
||||||
use bevy_render::{
|
use bevy_render::{
|
||||||
render_graph::{Node, ResourceSlots},
|
render_graph::{Node, ResourceSlots},
|
||||||
|
@ -15,6 +15,7 @@ use pathfinder_renderer::{
|
||||||
},
|
},
|
||||||
options::BuildOptions,
|
options::BuildOptions,
|
||||||
};
|
};
|
||||||
|
use pathfinder_resources::embedded::EmbeddedResourceLoader;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct PathfinderNode;
|
pub struct PathfinderNode;
|
||||||
|
@ -33,10 +34,11 @@ impl Node for PathfinderNode {
|
||||||
let window_size = Vector2I::new(640 as i32, 480 as i32);
|
let window_size = Vector2I::new(640 as i32, 480 as i32);
|
||||||
let mut renderer = Renderer::new(
|
let mut renderer = Renderer::new(
|
||||||
device,
|
device,
|
||||||
&BevyResourceLoader::new(),
|
&EmbeddedResourceLoader::new(),
|
||||||
DestFramebuffer::full_window(window_size),
|
DestFramebuffer::full_window(window_size),
|
||||||
RendererOptions {
|
RendererOptions {
|
||||||
background_color: Some(ColorF::white()),
|
background_color: Some(ColorF::white()),
|
||||||
|
..Default::default()
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
Before Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 338 B |
|
@ -1,14 +0,0 @@
|
||||||
pub const BLIT_FS: &'static [u8] = include_bytes!("shaders/spirv/blit.fs.spv");
|
|
||||||
pub const BLIT_VS: &'static [u8] = include_bytes!("shaders/spirv/blit.vs.spv");
|
|
||||||
pub const FILL_FS: &'static [u8] = include_bytes!("shaders/spirv/fill.fs.spv");
|
|
||||||
pub const FILL_VS: &'static [u8] = include_bytes!("shaders/spirv/fill.vs.spv");
|
|
||||||
pub const REPROJECT_FS: &'static [u8] = include_bytes!("shaders/spirv/reproject.fs.spv");
|
|
||||||
pub const REPROJECT_VS: &'static [u8] = include_bytes!("shaders/spirv/reproject.vs.spv");
|
|
||||||
pub const STENCIL_FS: &'static [u8] = include_bytes!("shaders/spirv/stencil.fs.spv");
|
|
||||||
pub const STENCIL_VS: &'static [u8] = include_bytes!("shaders/spirv/stencil.vs.spv");
|
|
||||||
pub const TILE_CLIP_FS: &'static [u8] = include_bytes!("shaders/spirv/tile_clip.fs.spv");
|
|
||||||
pub const TILE_CLIP_VS: &'static [u8] = include_bytes!("shaders/spirv/tile_clip.vs.spv");
|
|
||||||
pub const TILE_COPY_FS: &'static [u8] = include_bytes!("shaders/spirv/tile_copy.fs.spv");
|
|
||||||
pub const TILE_COPY_VS: &'static [u8] = include_bytes!("shaders/spirv/tile_copy.vs.spv");
|
|
||||||
pub const TILE_FS: &'static [u8] = include_bytes!("shaders/spirv/tile.fs.spv");
|
|
||||||
pub const TILE_VS: &'static [u8] = include_bytes!("shaders/spirv/tile.vs.spv");
|
|
|
@ -1,43 +0,0 @@
|
||||||
TARGET_DIR?=spirv
|
|
||||||
|
|
||||||
EMPTY=
|
|
||||||
|
|
||||||
SHADERS=\
|
|
||||||
blit.fs.glsl \
|
|
||||||
blit.vs.glsl \
|
|
||||||
fill.fs.glsl \
|
|
||||||
fill.vs.glsl \
|
|
||||||
reproject.fs.glsl \
|
|
||||||
reproject.vs.glsl \
|
|
||||||
stencil.fs.glsl \
|
|
||||||
stencil.vs.glsl \
|
|
||||||
tile.fs.glsl \
|
|
||||||
tile.vs.glsl \
|
|
||||||
tile_clip.fs.glsl \
|
|
||||||
tile_clip.vs.glsl \
|
|
||||||
tile_copy.fs.glsl \
|
|
||||||
tile_copy.vs.glsl \
|
|
||||||
$(EMPTY)
|
|
||||||
|
|
||||||
INCLUDES=\
|
|
||||||
$(EMPTY)
|
|
||||||
|
|
||||||
OUT=\
|
|
||||||
$(SHADERS:%.glsl=$(TARGET_DIR)/%.spv) \
|
|
||||||
$(EMPTY)
|
|
||||||
|
|
||||||
GLSLANGFLAGS=--auto-map-locations -I.
|
|
||||||
GLSLANGFLAGS_VULKAN=$(GLSLANGFLAGS) -V
|
|
||||||
|
|
||||||
all: $(OUT)
|
|
||||||
|
|
||||||
.PHONY: clean
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -f $(OUT)
|
|
||||||
|
|
||||||
$(TARGET_DIR)/%.fs.spv: glsl/%.fs.glsl $(INCLUDES)
|
|
||||||
mkdir -p $(TARGET_DIR) && glslangValidator $(GLSLANGFLAGS_VULKAN) -S frag -o $@ $<
|
|
||||||
|
|
||||||
$(TARGET_DIR)/%.vs.spv: glsl/%.vs.glsl $(INCLUDES)
|
|
||||||
mkdir -p $(TARGET_DIR) && glslangValidator $(GLSLANGFLAGS_VULKAN) -S vert -o $@ $<
|
|
|
@ -1,26 +0,0 @@
|
||||||
#version 450
|
|
||||||
|
|
||||||
// pathfinder/shaders/blit.fs.glsl
|
|
||||||
//
|
|
||||||
// Copyright © 2020 The Pathfinder Project Developers.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
|
|
||||||
precision highp float;
|
|
||||||
precision highp sampler2D;
|
|
||||||
|
|
||||||
layout(set=0, binding=0) uniform texture2D uSrc;
|
|
||||||
layout(set=0, binding=1) uniform sampler uSrcSampler;
|
|
||||||
|
|
||||||
in vec2 vTexCoord;
|
|
||||||
|
|
||||||
out vec4 oFragColor;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec4 color = texture(sampler2D(uSrc, uSrcSampler), vTexCoord);
|
|
||||||
oFragColor = vec4(color.rgb * color.a, color.a);
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
#version 450
|
|
||||||
|
|
||||||
// pathfinder/shaders/blit.vs.glsl
|
|
||||||
//
|
|
||||||
// Copyright © 2020 The Pathfinder Project Developers.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
|
|
||||||
precision highp float;
|
|
||||||
precision highp sampler2D;
|
|
||||||
|
|
||||||
in ivec2 aPosition;
|
|
||||||
|
|
||||||
out vec2 vTexCoord;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec2 texCoord = vec2(aPosition);
|
|
||||||
#ifdef PF_ORIGIN_UPPER_LEFT
|
|
||||||
texCoord.y = 1.0 - texCoord.y;
|
|
||||||
#endif
|
|
||||||
vTexCoord = texCoord;
|
|
||||||
gl_Position = vec4(mix(vec2(-1.0), vec2(1.0), vec2(aPosition)), 0.0, 1.0);
|
|
||||||
}
|
|
|
@ -1,63 +0,0 @@
|
||||||
#version 450
|
|
||||||
|
|
||||||
// pathfinder/shaders/fill.vs.glsl
|
|
||||||
//
|
|
||||||
// Copyright © 2019 The Pathfinder Project Developers.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
|
|
||||||
precision highp float;
|
|
||||||
precision highp sampler2D;
|
|
||||||
|
|
||||||
layout(set=0, binding=0) uniform uFramebufferSize {
|
|
||||||
vec2 framebufferSize;
|
|
||||||
};
|
|
||||||
layout(set=0, binding=1) uniform uTileSize {
|
|
||||||
vec2 tileSize;
|
|
||||||
};
|
|
||||||
|
|
||||||
in uvec2 aTessCoord;
|
|
||||||
in uint aFromPx;
|
|
||||||
in uint aToPx;
|
|
||||||
in vec2 aFromSubpx;
|
|
||||||
in vec2 aToSubpx;
|
|
||||||
in uint aTileIndex;
|
|
||||||
|
|
||||||
out vec2 vFrom;
|
|
||||||
out vec2 vTo;
|
|
||||||
|
|
||||||
vec2 computeTileOffset(uint tileIndex, float stencilTextureWidth) {
|
|
||||||
uint tilesPerRow = uint(stencilTextureWidth / tileSize.x);
|
|
||||||
uvec2 tileOffset = uvec2(tileIndex % tilesPerRow, tileIndex / tilesPerRow);
|
|
||||||
return vec2(tileOffset) * tileSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec2 tileOrigin = computeTileOffset(aTileIndex, framebufferSize.x);
|
|
||||||
|
|
||||||
vec2 from = vec2(aFromPx & 15u, aFromPx >> 4u) + aFromSubpx;
|
|
||||||
vec2 to = vec2(aToPx & 15u, aToPx >> 4u) + aToSubpx;
|
|
||||||
|
|
||||||
vec2 position;
|
|
||||||
if (aTessCoord.x == 0u)
|
|
||||||
position.x = floor(min(from.x, to.x));
|
|
||||||
else
|
|
||||||
position.x = ceil(max(from.x, to.x));
|
|
||||||
if (aTessCoord.y == 0u)
|
|
||||||
position.y = floor(min(from.y, to.y));
|
|
||||||
else
|
|
||||||
position.y = tileSize.y;
|
|
||||||
|
|
||||||
vFrom = from - position;
|
|
||||||
vTo = to - position;
|
|
||||||
|
|
||||||
vec2 globalPosition = (tileOrigin + position) / framebufferSize * 2.0 - 1.0;
|
|
||||||
#ifdef PF_ORIGIN_UPPER_LEFT
|
|
||||||
globalPosition.y = -globalPosition.y;
|
|
||||||
#endif
|
|
||||||
gl_Position = vec4(globalPosition, 0.0, 1.0);
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
#version 450
|
|
||||||
|
|
||||||
// pathfinder/shaders/reproject.fs.glsl
|
|
||||||
//
|
|
||||||
// Copyright © 2019 The Pathfinder Project Developers.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
|
|
||||||
precision highp float;
|
|
||||||
precision highp sampler2D;
|
|
||||||
|
|
||||||
layout(set=1, binding=0) uniform uOldTransform {
|
|
||||||
mat4 oldTransform;
|
|
||||||
};
|
|
||||||
|
|
||||||
layout(set=1, binding=1) uniform texture2D uTexture;
|
|
||||||
layout(set=1, binding=2) uniform sampler uTextureSampler;
|
|
||||||
|
|
||||||
in vec2 vTexCoord;
|
|
||||||
|
|
||||||
out vec4 oFragColor;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec4 normTexCoord = oldTransform * vec4(vTexCoord, 0.0, 1.0);
|
|
||||||
vec2 texCoord = ((normTexCoord.xy / normTexCoord.w) + 1.0) * 0.5;
|
|
||||||
oFragColor = texture(sampler2D(uTexture, uTextureSampler), texCoord);
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
#version 450
|
|
||||||
|
|
||||||
// pathfinder/shaders/reproject.vs.glsl
|
|
||||||
//
|
|
||||||
// Copyright © 2019 The Pathfinder Project Developers.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
|
|
||||||
precision highp float;
|
|
||||||
precision highp sampler2D;
|
|
||||||
|
|
||||||
layout(set=0, binding=0) uniform uNewTransform {
|
|
||||||
mat4 newTransform;
|
|
||||||
};
|
|
||||||
|
|
||||||
in ivec2 aPosition;
|
|
||||||
|
|
||||||
out vec2 vTexCoord;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec2 position = vec2(aPosition);
|
|
||||||
vTexCoord = position;
|
|
||||||
|
|
||||||
#ifdef PF_ORIGIN_UPPER_LEFT
|
|
||||||
// FIXME(pcwalton): This is wrong.
|
|
||||||
position.y = 1.0 - position.y;
|
|
||||||
#endif
|
|
||||||
gl_Position = newTransform * vec4(position, 0.0, 1.0);
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
#version 450
|
|
||||||
|
|
||||||
// pathfinder/shaders/stencil.fs.glsl
|
|
||||||
//
|
|
||||||
// Copyright © 2018 The Pathfinder Project Developers.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
|
|
||||||
precision highp float;
|
|
||||||
precision highp sampler2D;
|
|
||||||
|
|
||||||
out vec4 oFragColor;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
// This should be color masked out.
|
|
||||||
oFragColor = vec4(1.0, 0.0, 0.0, 1.0);
|
|
||||||
}
|
|
|
@ -1,615 +0,0 @@
|
||||||
#version 450
|
|
||||||
|
|
||||||
// pathfinder/shaders/tile.fs.glsl
|
|
||||||
//
|
|
||||||
// Copyright © 2020 The Pathfinder Project Developers.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
|
|
||||||
// Mask UV 0 Mask UV 1
|
|
||||||
// + +
|
|
||||||
// | |
|
|
||||||
// +-----v-----+ +-----v-----+
|
|
||||||
// | | MIN | |
|
|
||||||
// | Mask 0 +-----> Mask 1 +------+
|
|
||||||
// | | | | |
|
|
||||||
// +-----------+ +-----------+ v +-------------+
|
|
||||||
// Apply | | GPU
|
|
||||||
// Mask +----> Composite +---->Blender
|
|
||||||
// ^ | |
|
|
||||||
// +-----------+ +-----------+ | +-------------+
|
|
||||||
// | | | | |
|
|
||||||
// | Color 0 +-----> Color 1 +------+
|
|
||||||
// | Filter | × | |
|
|
||||||
// | | | |
|
|
||||||
// +-----^-----+ +-----^-----+
|
|
||||||
// | |
|
|
||||||
// + +
|
|
||||||
// Color UV 0 Color UV 1
|
|
||||||
|
|
||||||
#extension GL_GOOGLE_include_directive : enable
|
|
||||||
|
|
||||||
precision highp float;
|
|
||||||
precision highp sampler2D;
|
|
||||||
|
|
||||||
#define EPSILON 0.00001
|
|
||||||
|
|
||||||
#define FRAC_6_PI 1.9098593171027443
|
|
||||||
#define FRAC_PI_3 1.0471975511965976
|
|
||||||
|
|
||||||
#define TILE_CTRL_MASK_MASK 0x3
|
|
||||||
#define TILE_CTRL_MASK_WINDING 0x1
|
|
||||||
#define TILE_CTRL_MASK_EVEN_ODD 0x2
|
|
||||||
|
|
||||||
#define TILE_CTRL_MASK_0_SHIFT 0
|
|
||||||
|
|
||||||
#define COMBINER_CTRL_COLOR_COMBINE_MASK 0x3
|
|
||||||
#define COMBINER_CTRL_COLOR_COMBINE_SRC_IN 0x1
|
|
||||||
#define COMBINER_CTRL_COLOR_COMBINE_DEST_IN 0x2
|
|
||||||
|
|
||||||
#define COMBINER_CTRL_FILTER_MASK 0x3
|
|
||||||
#define COMBINER_CTRL_FILTER_RADIAL_GRADIENT 0x1
|
|
||||||
#define COMBINER_CTRL_FILTER_TEXT 0x2
|
|
||||||
#define COMBINER_CTRL_FILTER_BLUR 0x3
|
|
||||||
|
|
||||||
#define COMBINER_CTRL_COMPOSITE_MASK 0xf
|
|
||||||
#define COMBINER_CTRL_COMPOSITE_NORMAL 0x0
|
|
||||||
#define COMBINER_CTRL_COMPOSITE_MULTIPLY 0x1
|
|
||||||
#define COMBINER_CTRL_COMPOSITE_SCREEN 0x2
|
|
||||||
#define COMBINER_CTRL_COMPOSITE_OVERLAY 0x3
|
|
||||||
#define COMBINER_CTRL_COMPOSITE_DARKEN 0x4
|
|
||||||
#define COMBINER_CTRL_COMPOSITE_LIGHTEN 0x5
|
|
||||||
#define COMBINER_CTRL_COMPOSITE_COLOR_DODGE 0x6
|
|
||||||
#define COMBINER_CTRL_COMPOSITE_COLOR_BURN 0x7
|
|
||||||
#define COMBINER_CTRL_COMPOSITE_HARD_LIGHT 0x8
|
|
||||||
#define COMBINER_CTRL_COMPOSITE_SOFT_LIGHT 0x9
|
|
||||||
#define COMBINER_CTRL_COMPOSITE_DIFFERENCE 0xa
|
|
||||||
#define COMBINER_CTRL_COMPOSITE_EXCLUSION 0xb
|
|
||||||
#define COMBINER_CTRL_COMPOSITE_HUE 0xc
|
|
||||||
#define COMBINER_CTRL_COMPOSITE_SATURATION 0xd
|
|
||||||
#define COMBINER_CTRL_COMPOSITE_COLOR 0xe
|
|
||||||
#define COMBINER_CTRL_COMPOSITE_LUMINOSITY 0xf
|
|
||||||
|
|
||||||
#define COMBINER_CTRL_COLOR_FILTER_SHIFT 4
|
|
||||||
#define COMBINER_CTRL_COLOR_COMBINE_SHIFT 6
|
|
||||||
#define COMBINER_CTRL_COMPOSITE_SHIFT 8
|
|
||||||
|
|
||||||
layout(set=1, binding=0) uniform sampler textureSampler;
|
|
||||||
layout(set=1, binding=1) uniform texture2D uColorTexture0;
|
|
||||||
layout(set=1, binding=2) uniform texture2D uMaskTexture0;
|
|
||||||
layout(set=1, binding=3) uniform texture2D uDestTexture;
|
|
||||||
layout(set=1, binding=4) uniform texture2D uGammaLUT;
|
|
||||||
|
|
||||||
layout(set=2, binding=0) uniform uFilterParams0 {
|
|
||||||
vec4 filterParams0;
|
|
||||||
};
|
|
||||||
layout(set=2, binding=1) uniform uFilterParams1 {
|
|
||||||
vec4 filterParams1;
|
|
||||||
};
|
|
||||||
layout(set=2, binding=2) uniform uFilterParams2 {
|
|
||||||
vec4 filterParams2;
|
|
||||||
};
|
|
||||||
layout(set=2, binding=3) uniform uFramebufferSize {
|
|
||||||
vec2 framebufferSize;
|
|
||||||
};
|
|
||||||
layout(set=2, binding=4) uniform uColorTexture0Size {
|
|
||||||
vec2 colorTexture0Size;
|
|
||||||
};
|
|
||||||
layout(set=2, binding=5) uniform uCtrl {
|
|
||||||
int ctrl;
|
|
||||||
};
|
|
||||||
|
|
||||||
in vec3 vMaskTexCoord0;
|
|
||||||
in vec2 vColorTexCoord0;
|
|
||||||
in vec4 vBaseColor;
|
|
||||||
in float vTileCtrl;
|
|
||||||
|
|
||||||
out vec4 oFragColor;
|
|
||||||
|
|
||||||
// Color sampling
|
|
||||||
|
|
||||||
vec4 sampleColor(texture2D colorTexture, vec2 colorTexCoord) {
|
|
||||||
return texture(sampler2D(colorTexture, textureSampler), colorTexCoord);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Color combining
|
|
||||||
|
|
||||||
vec4 combineColor0(vec4 destColor, vec4 srcColor, int op) {
|
|
||||||
switch (op) {
|
|
||||||
case COMBINER_CTRL_COLOR_COMBINE_SRC_IN:
|
|
||||||
return vec4(srcColor.rgb, srcColor.a * destColor.a);
|
|
||||||
case COMBINER_CTRL_COLOR_COMBINE_DEST_IN:
|
|
||||||
return vec4(destColor.rgb, srcColor.a * destColor.a);
|
|
||||||
}
|
|
||||||
return destColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Text filter
|
|
||||||
|
|
||||||
float filterTextSample1Tap(float offset, texture2D colorTexture, vec2 colorTexCoord) {
|
|
||||||
return texture(sampler2D(colorTexture, textureSampler), colorTexCoord + vec2(offset, 0.0)).r;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Samples 9 taps around the current pixel.
|
|
||||||
void filterTextSample9Tap(out vec4 outAlphaLeft,
|
|
||||||
out float outAlphaCenter,
|
|
||||||
out vec4 outAlphaRight,
|
|
||||||
texture2D colorTexture,
|
|
||||||
vec2 colorTexCoord,
|
|
||||||
vec4 kernel,
|
|
||||||
float onePixel) {
|
|
||||||
bool wide = kernel.x > 0.0;
|
|
||||||
outAlphaLeft =
|
|
||||||
vec4(wide ? filterTextSample1Tap(-4.0 * onePixel, colorTexture, colorTexCoord) : 0.0,
|
|
||||||
filterTextSample1Tap(-3.0 * onePixel, colorTexture, colorTexCoord),
|
|
||||||
filterTextSample1Tap(-2.0 * onePixel, colorTexture, colorTexCoord),
|
|
||||||
filterTextSample1Tap(-1.0 * onePixel, colorTexture, colorTexCoord));
|
|
||||||
outAlphaCenter = filterTextSample1Tap(0.0, colorTexture, colorTexCoord);
|
|
||||||
outAlphaRight =
|
|
||||||
vec4(filterTextSample1Tap(1.0 * onePixel, colorTexture, colorTexCoord),
|
|
||||||
filterTextSample1Tap(2.0 * onePixel, colorTexture, colorTexCoord),
|
|
||||||
filterTextSample1Tap(3.0 * onePixel, colorTexture, colorTexCoord),
|
|
||||||
wide ? filterTextSample1Tap(4.0 * onePixel, colorTexture, colorTexCoord) : 0.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
float filterTextConvolve7Tap(vec4 alpha0, vec3 alpha1, vec4 kernel) {
|
|
||||||
return dot(alpha0, kernel) + dot(alpha1, kernel.zyx);
|
|
||||||
}
|
|
||||||
|
|
||||||
float filterTextGammaCorrectChannel(float bgColor, float fgColor, texture2D gammaLUT) {
|
|
||||||
return texture(sampler2D(gammaLUT, textureSampler), vec2(fgColor, 1.0 - bgColor)).r;
|
|
||||||
}
|
|
||||||
|
|
||||||
// `fgColor` is in linear space.
|
|
||||||
vec3 filterTextGammaCorrect(vec3 bgColor, vec3 fgColor, texture2D gammaLUT) {
|
|
||||||
return vec3(filterTextGammaCorrectChannel(bgColor.r, fgColor.r, gammaLUT),
|
|
||||||
filterTextGammaCorrectChannel(bgColor.g, fgColor.g, gammaLUT),
|
|
||||||
filterTextGammaCorrectChannel(bgColor.b, fgColor.b, gammaLUT));
|
|
||||||
}
|
|
||||||
|
|
||||||
// | x y z w
|
|
||||||
// --------------+--------------------------------------------------------
|
|
||||||
// filterParams0 | kernel[0] kernel[1] kernel[2] kernel[3]
|
|
||||||
// filterParams1 | bgColor.r bgColor.g bgColor.b -
|
|
||||||
// filterParams2 | fgColor.r fgColor.g fgColor.b gammaCorrectionEnabled
|
|
||||||
vec4 filterText(vec2 colorTexCoord,
|
|
||||||
texture2D colorTexture,
|
|
||||||
texture2D gammaLUT,
|
|
||||||
vec2 colorTextureSize,
|
|
||||||
vec4 filterParams0,
|
|
||||||
vec4 filterParams1,
|
|
||||||
vec4 filterParams2) {
|
|
||||||
// Unpack.
|
|
||||||
vec4 kernel = filterParams0;
|
|
||||||
vec3 bgColor = filterParams1.rgb;
|
|
||||||
vec3 fgColor = filterParams2.rgb;
|
|
||||||
bool gammaCorrectionEnabled = filterParams2.a != 0.0;
|
|
||||||
|
|
||||||
// Apply defringing if necessary.
|
|
||||||
vec3 alpha;
|
|
||||||
if (kernel.w == 0.0) {
|
|
||||||
alpha = texture(sampler2D(colorTexture, textureSampler), colorTexCoord).rrr;
|
|
||||||
} else {
|
|
||||||
vec4 alphaLeft, alphaRight;
|
|
||||||
float alphaCenter;
|
|
||||||
filterTextSample9Tap(alphaLeft,
|
|
||||||
alphaCenter,
|
|
||||||
alphaRight,
|
|
||||||
colorTexture,
|
|
||||||
colorTexCoord,
|
|
||||||
kernel,
|
|
||||||
1.0 / colorTextureSize.x);
|
|
||||||
|
|
||||||
float r = filterTextConvolve7Tap(alphaLeft, vec3(alphaCenter, alphaRight.xy), kernel);
|
|
||||||
float g = filterTextConvolve7Tap(vec4(alphaLeft.yzw, alphaCenter), alphaRight.xyz, kernel);
|
|
||||||
float b = filterTextConvolve7Tap(vec4(alphaLeft.zw, alphaCenter, alphaRight.x),
|
|
||||||
alphaRight.yzw,
|
|
||||||
kernel);
|
|
||||||
|
|
||||||
alpha = vec3(r, g, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply gamma correction if necessary.
|
|
||||||
if (gammaCorrectionEnabled)
|
|
||||||
alpha = filterTextGammaCorrect(bgColor, alpha, gammaLUT);
|
|
||||||
|
|
||||||
// Finish.
|
|
||||||
return vec4(mix(bgColor, fgColor, alpha), 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Other filters
|
|
||||||
|
|
||||||
// This is based on Pixman (MIT license). Copy and pasting the excellent comment
|
|
||||||
// from there:
|
|
||||||
|
|
||||||
// Implementation of radial gradients following the PDF specification.
|
|
||||||
// See section 8.7.4.5.4 Type 3 (Radial) Shadings of the PDF Reference
|
|
||||||
// Manual (PDF 32000-1:2008 at the time of this writing).
|
|
||||||
//
|
|
||||||
// In the radial gradient problem we are given two circles (c₁,r₁) and
|
|
||||||
// (c₂,r₂) that define the gradient itself.
|
|
||||||
//
|
|
||||||
// Mathematically the gradient can be defined as the family of circles
|
|
||||||
//
|
|
||||||
// ((1-t)·c₁ + t·(c₂), (1-t)·r₁ + t·r₂)
|
|
||||||
//
|
|
||||||
// excluding those circles whose radius would be < 0. When a point
|
|
||||||
// belongs to more than one circle, the one with a bigger t is the only
|
|
||||||
// one that contributes to its color. When a point does not belong
|
|
||||||
// to any of the circles, it is transparent black, i.e. RGBA (0, 0, 0, 0).
|
|
||||||
// Further limitations on the range of values for t are imposed when
|
|
||||||
// the gradient is not repeated, namely t must belong to [0,1].
|
|
||||||
//
|
|
||||||
// The graphical result is the same as drawing the valid (radius > 0)
|
|
||||||
// circles with increasing t in [-∞, +∞] (or in [0,1] if the gradient
|
|
||||||
// is not repeated) using SOURCE operator composition.
|
|
||||||
//
|
|
||||||
// It looks like a cone pointing towards the viewer if the ending circle
|
|
||||||
// is smaller than the starting one, a cone pointing inside the page if
|
|
||||||
// the starting circle is the smaller one and like a cylinder if they
|
|
||||||
// have the same radius.
|
|
||||||
//
|
|
||||||
// What we actually do is, given the point whose color we are interested
|
|
||||||
// in, compute the t values for that point, solving for t in:
|
|
||||||
//
|
|
||||||
// length((1-t)·c₁ + t·(c₂) - p) = (1-t)·r₁ + t·r₂
|
|
||||||
//
|
|
||||||
// Let's rewrite it in a simpler way, by defining some auxiliary
|
|
||||||
// variables:
|
|
||||||
//
|
|
||||||
// cd = c₂ - c₁
|
|
||||||
// pd = p - c₁
|
|
||||||
// dr = r₂ - r₁
|
|
||||||
// length(t·cd - pd) = r₁ + t·dr
|
|
||||||
//
|
|
||||||
// which actually means
|
|
||||||
//
|
|
||||||
// hypot(t·cdx - pdx, t·cdy - pdy) = r₁ + t·dr
|
|
||||||
//
|
|
||||||
// or
|
|
||||||
//
|
|
||||||
// ⎷((t·cdx - pdx)² + (t·cdy - pdy)²) = r₁ + t·dr.
|
|
||||||
//
|
|
||||||
// If we impose (as stated earlier) that r₁ + t·dr ≥ 0, it becomes:
|
|
||||||
//
|
|
||||||
// (t·cdx - pdx)² + (t·cdy - pdy)² = (r₁ + t·dr)²
|
|
||||||
//
|
|
||||||
// where we can actually expand the squares and solve for t:
|
|
||||||
//
|
|
||||||
// t²cdx² - 2t·cdx·pdx + pdx² + t²cdy² - 2t·cdy·pdy + pdy² =
|
|
||||||
// = r₁² + 2·r₁·t·dr + t²·dr²
|
|
||||||
//
|
|
||||||
// (cdx² + cdy² - dr²)t² - 2(cdx·pdx + cdy·pdy + r₁·dr)t +
|
|
||||||
// (pdx² + pdy² - r₁²) = 0
|
|
||||||
//
|
|
||||||
// A = cdx² + cdy² - dr²
|
|
||||||
// B = pdx·cdx + pdy·cdy + r₁·dr
|
|
||||||
// C = pdx² + pdy² - r₁²
|
|
||||||
// At² - 2Bt + C = 0
|
|
||||||
//
|
|
||||||
// The solutions (unless the equation degenerates because of A = 0) are:
|
|
||||||
//
|
|
||||||
// t = (B ± ⎷(B² - A·C)) / A
|
|
||||||
//
|
|
||||||
// The solution we are going to prefer is the bigger one, unless the
|
|
||||||
// radius associated to it is negative (or it falls outside the valid t
|
|
||||||
// range).
|
|
||||||
//
|
|
||||||
// Additional observations (useful for optimizations):
|
|
||||||
// A does not depend on p
|
|
||||||
//
|
|
||||||
// A < 0 ⟺ one of the two circles completely contains the other one
|
|
||||||
// ⟺ for every p, the radii associated with the two t solutions have
|
|
||||||
// opposite sign
|
|
||||||
//
|
|
||||||
// | x y z w
|
|
||||||
// --------------+-----------------------------------------------------
|
|
||||||
// filterParams0 | lineFrom.x lineFrom.y lineVector.x lineVector.y
|
|
||||||
// filterParams1 | radii.x radii.y uvOrigin.x uvOrigin.y
|
|
||||||
// filterParams2 | - - - -
|
|
||||||
vec4 filterRadialGradient(vec2 colorTexCoord,
|
|
||||||
texture2D colorTexture,
|
|
||||||
vec2 colorTextureSize,
|
|
||||||
vec2 fragCoord,
|
|
||||||
vec2 framebufferSize,
|
|
||||||
vec4 filterParams0,
|
|
||||||
vec4 filterParams1) {
|
|
||||||
vec2 lineFrom = filterParams0.xy, lineVector = filterParams0.zw;
|
|
||||||
vec2 radii = filterParams1.xy, uvOrigin = filterParams1.zw;
|
|
||||||
|
|
||||||
vec2 dP = colorTexCoord - lineFrom, dC = lineVector;
|
|
||||||
float dR = radii.y - radii.x;
|
|
||||||
|
|
||||||
float a = dot(dC, dC) - dR * dR;
|
|
||||||
float b = dot(dP, dC) + radii.x * dR;
|
|
||||||
float c = dot(dP, dP) - radii.x * radii.x;
|
|
||||||
float discrim = b * b - a * c;
|
|
||||||
|
|
||||||
vec4 color = vec4(0.0);
|
|
||||||
if (abs(discrim) >= EPSILON) {
|
|
||||||
vec2 ts = vec2(sqrt(discrim) * vec2(1.0, -1.0) + vec2(b)) / vec2(a);
|
|
||||||
if (ts.x > ts.y)
|
|
||||||
ts = ts.yx;
|
|
||||||
float t = ts.x >= 0.0 ? ts.x : ts.y;
|
|
||||||
color = texture(sampler2D(colorTexture, textureSampler), uvOrigin + vec2(clamp(t, 0.0, 1.0), 0.0));
|
|
||||||
}
|
|
||||||
|
|
||||||
return color;
|
|
||||||
}
|
|
||||||
|
|
||||||
// | x y z w
|
|
||||||
// --------------+----------------------------------------------------
|
|
||||||
// filterParams0 | srcOffset.x srcOffset.y support -
|
|
||||||
// filterParams1 | gaussCoeff.x gaussCoeff.y gaussCoeff.z -
|
|
||||||
// filterParams2 | - - - -
|
|
||||||
vec4 filterBlur(vec2 colorTexCoord,
|
|
||||||
texture2D colorTexture,
|
|
||||||
vec2 colorTextureSize,
|
|
||||||
vec4 filterParams0,
|
|
||||||
vec4 filterParams1) {
|
|
||||||
// Unpack.
|
|
||||||
vec2 srcOffsetScale = filterParams0.xy / colorTextureSize;
|
|
||||||
int support = int(filterParams0.z);
|
|
||||||
vec3 gaussCoeff = filterParams1.xyz;
|
|
||||||
|
|
||||||
// Set up our incremental calculation.
|
|
||||||
float gaussSum = gaussCoeff.x;
|
|
||||||
vec4 color = texture(sampler2D(colorTexture, textureSampler), colorTexCoord) * gaussCoeff.x;
|
|
||||||
gaussCoeff.xy *= gaussCoeff.yz;
|
|
||||||
|
|
||||||
// This is a common trick that lets us use the texture filtering hardware to evaluate two
|
|
||||||
// texels at a time. The basic principle is that, if c0 and c1 are colors of adjacent texels
|
|
||||||
// and k0 and k1 are arbitrary factors, the formula `k0 * c0 + k1 * c1` is equivalent to
|
|
||||||
// `(k0 + k1) * lerp(c0, c1, k1 / (k0 + k1))`. Linear interpolation, as performed by the
|
|
||||||
// texturing hardware when sampling adjacent pixels in one direction, evaluates
|
|
||||||
// `lerp(c0, c1, t)` where t is the offset from the texel with color `c0`. To evaluate the
|
|
||||||
// formula `k0 * c0 + k1 * c1`, therefore, we can use the texture hardware to perform linear
|
|
||||||
// interpolation with `t = k1 / (k0 + k1)`.
|
|
||||||
for (int i = 1; i <= support; i += 2) {
|
|
||||||
float gaussPartialSum = gaussCoeff.x;
|
|
||||||
gaussCoeff.xy *= gaussCoeff.yz;
|
|
||||||
gaussPartialSum += gaussCoeff.x;
|
|
||||||
|
|
||||||
vec2 srcOffset = srcOffsetScale * (float(i) + gaussCoeff.x / gaussPartialSum);
|
|
||||||
color += (texture(sampler2D(colorTexture, textureSampler), colorTexCoord - srcOffset) +
|
|
||||||
texture(sampler2D(colorTexture, textureSampler), colorTexCoord + srcOffset)) * gaussPartialSum;
|
|
||||||
|
|
||||||
gaussSum += 2.0 * gaussPartialSum;
|
|
||||||
gaussCoeff.xy *= gaussCoeff.yz;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finish.
|
|
||||||
return color / gaussSum;
|
|
||||||
}
|
|
||||||
|
|
||||||
vec4 filterNone(vec2 colorTexCoord, texture2D colorTexture) {
|
|
||||||
return sampleColor(colorTexture, colorTexCoord);
|
|
||||||
}
|
|
||||||
|
|
||||||
vec4 filterColor(vec2 colorTexCoord,
|
|
||||||
texture2D colorTexture,
|
|
||||||
texture2D gammaLUT,
|
|
||||||
vec2 colorTextureSize,
|
|
||||||
vec2 fragCoord,
|
|
||||||
vec2 framebufferSize,
|
|
||||||
vec4 filterParams0,
|
|
||||||
vec4 filterParams1,
|
|
||||||
vec4 filterParams2,
|
|
||||||
int colorFilter) {
|
|
||||||
switch (colorFilter) {
|
|
||||||
case COMBINER_CTRL_FILTER_RADIAL_GRADIENT:
|
|
||||||
return filterRadialGradient(colorTexCoord,
|
|
||||||
colorTexture,
|
|
||||||
colorTextureSize,
|
|
||||||
fragCoord,
|
|
||||||
framebufferSize,
|
|
||||||
filterParams0,
|
|
||||||
filterParams1);
|
|
||||||
case COMBINER_CTRL_FILTER_BLUR:
|
|
||||||
return filterBlur(colorTexCoord,
|
|
||||||
colorTexture,
|
|
||||||
colorTextureSize,
|
|
||||||
filterParams0,
|
|
||||||
filterParams1);
|
|
||||||
case COMBINER_CTRL_FILTER_TEXT:
|
|
||||||
return filterText(colorTexCoord,
|
|
||||||
colorTexture,
|
|
||||||
gammaLUT,
|
|
||||||
colorTextureSize,
|
|
||||||
filterParams0,
|
|
||||||
filterParams1,
|
|
||||||
filterParams2);
|
|
||||||
}
|
|
||||||
return filterNone(colorTexCoord, colorTexture);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compositing
|
|
||||||
|
|
||||||
vec3 compositeSelect(bvec3 cond, vec3 ifTrue, vec3 ifFalse) {
|
|
||||||
return vec3(cond.x ? ifTrue.x : ifFalse.x,
|
|
||||||
cond.y ? ifTrue.y : ifFalse.y,
|
|
||||||
cond.z ? ifTrue.z : ifFalse.z);
|
|
||||||
}
|
|
||||||
|
|
||||||
float compositeDivide(float num, float denom) {
|
|
||||||
return denom != 0.0 ? num / denom : 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
vec3 compositeColorDodge(vec3 destColor, vec3 srcColor) {
|
|
||||||
bvec3 destZero = equal(destColor, vec3(0.0)), srcOne = equal(srcColor, vec3(1.0));
|
|
||||||
return compositeSelect(destZero,
|
|
||||||
vec3(0.0),
|
|
||||||
compositeSelect(srcOne, vec3(1.0), destColor / (vec3(1.0) - srcColor)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://en.wikipedia.org/wiki/HSL_and_HSV#HSL_to_RGB_alternative
|
|
||||||
vec3 compositeHSLToRGB(vec3 hsl) {
|
|
||||||
float a = hsl.y * min(hsl.z, 1.0 - hsl.z);
|
|
||||||
vec3 ks = mod(vec3(0.0, 8.0, 4.0) + vec3(hsl.x * FRAC_6_PI), 12.0);
|
|
||||||
return hsl.zzz - clamp(min(ks - vec3(3.0), vec3(9.0) - ks), -1.0, 1.0) * a;
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://en.wikipedia.org/wiki/HSL_and_HSV#From_RGB
|
|
||||||
vec3 compositeRGBToHSL(vec3 rgb) {
|
|
||||||
float v = max(max(rgb.r, rgb.g), rgb.b), xMin = min(min(rgb.r, rgb.g), rgb.b);
|
|
||||||
float c = v - xMin, l = mix(xMin, v, 0.5);
|
|
||||||
vec3 terms = rgb.r == v ? vec3(0.0, rgb.gb) :
|
|
||||||
rgb.g == v ? vec3(2.0, rgb.br) :
|
|
||||||
vec3(4.0, rgb.rg);
|
|
||||||
float h = FRAC_PI_3 * compositeDivide(terms.x * c + terms.y - terms.z, c);
|
|
||||||
float s = compositeDivide(c, v);
|
|
||||||
return vec3(h, s, l);
|
|
||||||
}
|
|
||||||
|
|
||||||
vec3 compositeScreen(vec3 destColor, vec3 srcColor) {
|
|
||||||
return destColor + srcColor - destColor * srcColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
vec3 compositeHardLight(vec3 destColor, vec3 srcColor) {
|
|
||||||
return compositeSelect(lessThanEqual(srcColor, vec3(0.5)),
|
|
||||||
destColor * vec3(2.0) * srcColor,
|
|
||||||
compositeScreen(destColor, vec3(2.0) * srcColor - vec3(1.0)));
|
|
||||||
}
|
|
||||||
|
|
||||||
vec3 compositeSoftLight(vec3 destColor, vec3 srcColor) {
|
|
||||||
vec3 darkenedDestColor =
|
|
||||||
compositeSelect(lessThanEqual(destColor, vec3(0.25)),
|
|
||||||
((vec3(16.0) * destColor - 12.0) * destColor + 4.0) * destColor,
|
|
||||||
sqrt(destColor));
|
|
||||||
vec3 factor = compositeSelect(lessThanEqual(srcColor, vec3(0.5)),
|
|
||||||
destColor * (vec3(1.0) - destColor),
|
|
||||||
darkenedDestColor - destColor);
|
|
||||||
return destColor + (srcColor * 2.0 - 1.0) * factor;
|
|
||||||
}
|
|
||||||
|
|
||||||
vec3 compositeHSL(vec3 destColor, vec3 srcColor, int op) {
|
|
||||||
switch (op) {
|
|
||||||
case COMBINER_CTRL_COMPOSITE_HUE:
|
|
||||||
return vec3(srcColor.x, destColor.y, destColor.z);
|
|
||||||
case COMBINER_CTRL_COMPOSITE_SATURATION:
|
|
||||||
return vec3(destColor.x, srcColor.y, destColor.z);
|
|
||||||
case COMBINER_CTRL_COMPOSITE_COLOR:
|
|
||||||
return vec3(srcColor.x, srcColor.y, destColor.z);
|
|
||||||
default:
|
|
||||||
return vec3(destColor.x, destColor.y, srcColor.z);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vec3 compositeRGB(vec3 destColor, vec3 srcColor, int op) {
|
|
||||||
switch (op) {
|
|
||||||
case COMBINER_CTRL_COMPOSITE_MULTIPLY:
|
|
||||||
return destColor * srcColor;
|
|
||||||
case COMBINER_CTRL_COMPOSITE_SCREEN:
|
|
||||||
return compositeScreen(destColor, srcColor);
|
|
||||||
case COMBINER_CTRL_COMPOSITE_OVERLAY:
|
|
||||||
return compositeHardLight(srcColor, destColor);
|
|
||||||
case COMBINER_CTRL_COMPOSITE_DARKEN:
|
|
||||||
return min(destColor, srcColor);
|
|
||||||
case COMBINER_CTRL_COMPOSITE_LIGHTEN:
|
|
||||||
return max(destColor, srcColor);
|
|
||||||
case COMBINER_CTRL_COMPOSITE_COLOR_DODGE:
|
|
||||||
return compositeColorDodge(destColor, srcColor);
|
|
||||||
case COMBINER_CTRL_COMPOSITE_COLOR_BURN:
|
|
||||||
return vec3(1.0) - compositeColorDodge(vec3(1.0) - destColor, vec3(1.0) - srcColor);
|
|
||||||
case COMBINER_CTRL_COMPOSITE_HARD_LIGHT:
|
|
||||||
return compositeHardLight(destColor, srcColor);
|
|
||||||
case COMBINER_CTRL_COMPOSITE_SOFT_LIGHT:
|
|
||||||
return compositeSoftLight(destColor, srcColor);
|
|
||||||
case COMBINER_CTRL_COMPOSITE_DIFFERENCE:
|
|
||||||
return abs(destColor - srcColor);
|
|
||||||
case COMBINER_CTRL_COMPOSITE_EXCLUSION:
|
|
||||||
return destColor + srcColor - vec3(2.0) * destColor * srcColor;
|
|
||||||
case COMBINER_CTRL_COMPOSITE_HUE:
|
|
||||||
case COMBINER_CTRL_COMPOSITE_SATURATION:
|
|
||||||
case COMBINER_CTRL_COMPOSITE_COLOR:
|
|
||||||
case COMBINER_CTRL_COMPOSITE_LUMINOSITY:
|
|
||||||
return compositeHSLToRGB(compositeHSL(compositeRGBToHSL(destColor),
|
|
||||||
compositeRGBToHSL(srcColor),
|
|
||||||
op));
|
|
||||||
}
|
|
||||||
return srcColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
vec4 composite(vec4 srcColor,
|
|
||||||
texture2D destTexture,
|
|
||||||
vec2 destTextureSize,
|
|
||||||
vec2 fragCoord,
|
|
||||||
int op) {
|
|
||||||
if (op == COMBINER_CTRL_COMPOSITE_NORMAL)
|
|
||||||
return srcColor;
|
|
||||||
|
|
||||||
// FIXME(pcwalton): What should the output alpha be here?
|
|
||||||
vec2 destTexCoord = fragCoord / destTextureSize;
|
|
||||||
vec4 destColor = texture(sampler2D(destTexture, textureSampler), destTexCoord);
|
|
||||||
vec3 blendedRGB = compositeRGB(destColor.rgb, srcColor.rgb, op);
|
|
||||||
return vec4(srcColor.a * (1.0 - destColor.a) * srcColor.rgb +
|
|
||||||
srcColor.a * destColor.a * blendedRGB +
|
|
||||||
(1.0 - srcColor.a) * destColor.rgb,
|
|
||||||
1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Masks
|
|
||||||
float sampleMask(float maskAlpha,
|
|
||||||
texture2D maskTexture,
|
|
||||||
vec3 maskTexCoord,
|
|
||||||
int maskCtrl) {
|
|
||||||
if (maskCtrl == 0)
|
|
||||||
return maskAlpha;
|
|
||||||
float coverage = texture(sampler2D(maskTexture, textureSampler), maskTexCoord.xy).r + maskTexCoord.z;
|
|
||||||
if ((maskCtrl & TILE_CTRL_MASK_WINDING) != 0)
|
|
||||||
coverage = abs(coverage);
|
|
||||||
else
|
|
||||||
coverage = 1.0 - abs(1.0 - mod(coverage, 2.0));
|
|
||||||
return min(maskAlpha, coverage);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Main function
|
|
||||||
|
|
||||||
void calculateColor(int tileCtrl, int ctrl) {
|
|
||||||
// Sample mask.
|
|
||||||
int maskCtrl0 = (tileCtrl >> TILE_CTRL_MASK_0_SHIFT) & TILE_CTRL_MASK_MASK;
|
|
||||||
float maskAlpha = 1.0;
|
|
||||||
maskAlpha = sampleMask(maskAlpha, uMaskTexture0, vMaskTexCoord0, maskCtrl0);
|
|
||||||
|
|
||||||
// Sample color.
|
|
||||||
vec4 color = vBaseColor;
|
|
||||||
int color0Combine = (ctrl >> COMBINER_CTRL_COLOR_COMBINE_SHIFT) &
|
|
||||||
COMBINER_CTRL_COLOR_COMBINE_MASK;
|
|
||||||
if (color0Combine != 0) {
|
|
||||||
int color0Filter = (ctrl >> COMBINER_CTRL_COLOR_FILTER_SHIFT) & COMBINER_CTRL_FILTER_MASK;
|
|
||||||
vec4 color0 = filterColor(vColorTexCoord0,
|
|
||||||
uColorTexture0,
|
|
||||||
uGammaLUT,
|
|
||||||
colorTexture0Size,
|
|
||||||
gl_FragCoord.xy,
|
|
||||||
framebufferSize,
|
|
||||||
filterParams0,
|
|
||||||
filterParams1,
|
|
||||||
filterParams2,
|
|
||||||
color0Filter);
|
|
||||||
color = combineColor0(color, color0, color0Combine);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply mask.
|
|
||||||
color.a *= maskAlpha;
|
|
||||||
|
|
||||||
// Apply composite.
|
|
||||||
int compositeOp = (ctrl >> COMBINER_CTRL_COMPOSITE_SHIFT) & COMBINER_CTRL_COMPOSITE_MASK;
|
|
||||||
color = composite(color, uDestTexture, framebufferSize, gl_FragCoord.xy, compositeOp);
|
|
||||||
|
|
||||||
// Premultiply alpha.
|
|
||||||
color.rgb *= color.a;
|
|
||||||
oFragColor = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Entry point
|
|
||||||
//
|
|
||||||
// TODO(pcwalton): Generate this dynamically.
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
calculateColor(int(vTileCtrl), ctrl);
|
|
||||||
}
|
|
|
@ -1,61 +0,0 @@
|
||||||
#version 450
|
|
||||||
|
|
||||||
// pathfinder/shaders/tile.vs.glsl
|
|
||||||
//
|
|
||||||
// Copyright © 2020 The Pathfinder Project Developers.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
|
|
||||||
precision highp float;
|
|
||||||
precision highp sampler2D;
|
|
||||||
|
|
||||||
layout(set=0, binding=0) uniform uTransform {
|
|
||||||
mat4 transform;
|
|
||||||
};
|
|
||||||
layout(set=0, binding=1) uniform uTileSize {
|
|
||||||
vec2 tileSize;
|
|
||||||
};
|
|
||||||
layout(set=0, binding=2) uniform texture2D uTextureMetadata;
|
|
||||||
layout(set=0, binding=3) uniform sampler uTextureMetadataSampler;
|
|
||||||
layout(set=0, binding=4) uniform uTextureMetadataSize {
|
|
||||||
ivec2 textureMetadataSize;
|
|
||||||
};
|
|
||||||
|
|
||||||
in ivec2 aTileOffset;
|
|
||||||
in ivec2 aTileOrigin;
|
|
||||||
in uvec2 aMaskTexCoord0;
|
|
||||||
in ivec2 aMaskBackdrop;
|
|
||||||
in int aColor;
|
|
||||||
in int aTileCtrl;
|
|
||||||
|
|
||||||
out vec3 vMaskTexCoord0;
|
|
||||||
out vec2 vColorTexCoord0;
|
|
||||||
out vec4 vBaseColor;
|
|
||||||
out float vTileCtrl;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec2 tileOrigin = vec2(aTileOrigin), tileOffset = vec2(aTileOffset);
|
|
||||||
vec2 position = (tileOrigin + tileOffset) * tileSize;
|
|
||||||
|
|
||||||
vec2 maskTexCoord0 = (vec2(aMaskTexCoord0) + tileOffset) / 256.0;
|
|
||||||
|
|
||||||
vec2 textureMetadataScale = vec2(1.0) / vec2(textureMetadataSize);
|
|
||||||
vec2 metadataEntryCoord = vec2(aColor % 128 * 4, aColor / 128);
|
|
||||||
vec2 colorTexMatrix0Coord = (metadataEntryCoord + vec2(0.5, 0.5)) * textureMetadataScale;
|
|
||||||
vec2 colorTexOffsetsCoord = (metadataEntryCoord + vec2(1.5, 0.5)) * textureMetadataScale;
|
|
||||||
vec2 baseColorCoord = (metadataEntryCoord + vec2(2.5, 0.5)) * textureMetadataScale;
|
|
||||||
|
|
||||||
vec4 colorTexMatrix0 = texture(sampler2D(uTextureMetadata, uTextureMetadataSampler), colorTexMatrix0Coord);
|
|
||||||
vec4 colorTexOffsets = texture(sampler2D(uTextureMetadata, uTextureMetadataSampler), colorTexOffsetsCoord);
|
|
||||||
vec4 baseColor = texture(sampler2D(uTextureMetadata, uTextureMetadataSampler), baseColorCoord);
|
|
||||||
|
|
||||||
vColorTexCoord0 = mat2(colorTexMatrix0) * position + colorTexOffsets.xy;
|
|
||||||
vMaskTexCoord0 = vec3(maskTexCoord0, float(aMaskBackdrop.x));
|
|
||||||
vBaseColor = baseColor;
|
|
||||||
vTileCtrl = float(aTileCtrl);
|
|
||||||
gl_Position = transform * vec4(position, 0.0, 1.0);
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
#version 450
|
|
||||||
|
|
||||||
// pathfinder/shaders/tile_clip.fs.glsl
|
|
||||||
//
|
|
||||||
// Copyright © 2020 The Pathfinder Project Developers.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
|
|
||||||
precision highp float;
|
|
||||||
precision highp sampler2D;
|
|
||||||
|
|
||||||
layout(set=0, binding=0) uniform texture2D uSrc;
|
|
||||||
layout(set=0, binding=1) uniform sampler uSrcSampler;
|
|
||||||
|
|
||||||
in vec2 vTexCoord;
|
|
||||||
in float vBackdrop;
|
|
||||||
|
|
||||||
out vec4 oFragColor;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec4 alpha_texture_color = texture(sampler2D(uSrc, uSrcSampler), vTexCoord);
|
|
||||||
float alpha = clamp(abs(alpha_texture_color.r + vBackdrop), 0.0, 1.0);
|
|
||||||
oFragColor = vec4(alpha, 0.0, 0.0, 1.0);
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
#version 450
|
|
||||||
|
|
||||||
// pathfinder/shaders/tile_clip.vs.glsl
|
|
||||||
//
|
|
||||||
// Copyright © 2020 The Pathfinder Project Developers.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
|
|
||||||
precision highp float;
|
|
||||||
precision highp sampler2D;
|
|
||||||
|
|
||||||
in ivec2 aTileOffset;
|
|
||||||
in ivec2 aDestTileOrigin;
|
|
||||||
in ivec2 aSrcTileOrigin;
|
|
||||||
in int aSrcBackdrop;
|
|
||||||
|
|
||||||
out vec2 vTexCoord;
|
|
||||||
out float vBackdrop;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec2 destPosition = vec2(aDestTileOrigin + aTileOffset) / vec2(256.0);
|
|
||||||
vec2 srcPosition = vec2(aSrcTileOrigin + aTileOffset) / vec2(256.0);
|
|
||||||
vTexCoord = srcPosition;
|
|
||||||
vBackdrop = float(aSrcBackdrop);
|
|
||||||
gl_Position = vec4(mix(vec2(-1.0), vec2(1.0), destPosition), 0.0, 1.0);
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
#version 450
|
|
||||||
|
|
||||||
// pathfinder/shaders/tile_copy.vs.glsl
|
|
||||||
//
|
|
||||||
// Copyright © 2020 The Pathfinder Project Developers.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
|
|
||||||
precision highp float;
|
|
||||||
precision highp sampler2D;
|
|
||||||
|
|
||||||
layout(set=0, binding=0) uniform uTransform {
|
|
||||||
mat4 transform;
|
|
||||||
};
|
|
||||||
layout(set=0, binding=1) uniform uTileSize {
|
|
||||||
vec2 tileSize;
|
|
||||||
};
|
|
||||||
|
|
||||||
in ivec2 aTilePosition;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec2 position = vec2(aTilePosition) * tileSize;
|
|
||||||
gl_Position = transform * vec4(position, 0.0, 1.0);
|
|
||||||
}
|
|
|
@ -14,7 +14,6 @@ members = [
|
||||||
"examples/canvas_moire",
|
"examples/canvas_moire",
|
||||||
"examples/canvas_nanovg",
|
"examples/canvas_nanovg",
|
||||||
"examples/canvas_text",
|
"examples/canvas_text",
|
||||||
"examples/canvas_webgpu_minimal",
|
|
||||||
"examples/lottie_basic",
|
"examples/lottie_basic",
|
||||||
"examples/swf_basic",
|
"examples/swf_basic",
|
||||||
"geometry",
|
"geometry",
|
||||||
|
@ -35,7 +34,6 @@ members = [
|
||||||
"utils/svg-to-skia",
|
"utils/svg-to-skia",
|
||||||
"utils/convert",
|
"utils/convert",
|
||||||
"webgl",
|
"webgl",
|
||||||
"webgpu",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
default-members = [
|
default-members = [
|
||||||
|
|
|
@ -1,6 +1,109 @@
|
||||||
# Pathfinder Fork
|
# Pathfinder 3
|
||||||
|
|
||||||
Why fork pathfinder? WebGPU does not support some of the datatypes pathfinder uses in its shaders (ex: Short1, Char1). This means both the base shaders and the pathfinder_renderer code needed to change to accommodate that. Ideally these changes can either be merged directly into pathfinder, or be hidden behind a feature flag.
|
![Logo](https://github.com/servo/pathfinder/raw/master/resources/textures/pathfinder-logo.png)
|
||||||
|
|
||||||
|
Pathfinder 3 is a fast, practical, GPU-based rasterizer for fonts and vector graphics using OpenGL
|
||||||
|
3.0+, OpenGL ES 3.0+, WebGL 2, and Metal.
|
||||||
|
|
||||||
Forked from commit 84bf4341c253ae36756d27671a17cb44d59cd250
|
Please note that Pathfinder is under heavy development and is incomplete in various areas.
|
||||||
|
|
||||||
|
## Quick start
|
||||||
|
|
||||||
|
Pathfinder contains a library that implements a subset of the
|
||||||
|
[HTML canvas API](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API). You can quickly add
|
||||||
|
vector rendering to any Rust app with it. The library is available on `crates.io`. See
|
||||||
|
`examples/canvas_minimal` for a small example of usage.
|
||||||
|
|
||||||
|
### Demos
|
||||||
|
|
||||||
|
Demo app sources are available in [demo/](https://github.com/servo/pathfinder/tree/master/demo). A prebuilt package for Magic Leap can be found in [releases](https://github.com/servo/pathfinder/releases).
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
The project features:
|
||||||
|
|
||||||
|
* High quality antialiasing. Pathfinder can compute exact fractional trapezoidal area coverage on a
|
||||||
|
per-pixel basis for the highest-quality antialiasing possible (effectively 256xAA).
|
||||||
|
|
||||||
|
* Fast CPU setup, making full use of parallelism. Pathfinder 3 uses the Rayon library to quickly
|
||||||
|
perform a CPU tiling prepass to prepare vector scenes for the GPU. This prepass can be pipelined
|
||||||
|
with the GPU to hide its latency.
|
||||||
|
|
||||||
|
* Fast GPU rendering, even at small pixel sizes. Even on lower-end GPUs, Pathfinder typically
|
||||||
|
matches or exceeds the performance of the best CPU rasterizers. The difference is particularly
|
||||||
|
pronounced at large sizes, where Pathfinder regularly achieves multi-factor speedups.
|
||||||
|
|
||||||
|
* GPU compute-based rendering, where available. Pathfinder can optionally use compute shaders to
|
||||||
|
achieve better performance than what the built-in GPU rasterization hardware can provide. Compute
|
||||||
|
shader capability is not required, and all features are available without it.
|
||||||
|
|
||||||
|
* Advanced font rendering. Pathfinder can render fonts with slight hinting and can perform subpixel
|
||||||
|
antialiasing on LCD screens. It can do stem darkening/font dilation like macOS and FreeType in
|
||||||
|
order to make text easier to read at small sizes. The library also has support for gamma
|
||||||
|
correction.
|
||||||
|
|
||||||
|
* Support for SVG. Pathfinder 3 is designed to efficiently handle workloads that consist of many
|
||||||
|
overlapping vector paths, such as those commonly found in SVG and PDF files. It can perform
|
||||||
|
occlusion culling, which often results in dramatic performance wins over typical software
|
||||||
|
renderers that use the painter's algorithm. A simple loader that leverages the `resvg` library
|
||||||
|
to render a subset of SVG is included, so it's easy to get started.
|
||||||
|
|
||||||
|
* 3D capability. Pathfinder can render fonts and vector paths in 3D environments without any loss
|
||||||
|
in quality. This is intended to be useful for vector-graphics-based user interfaces in VR, for
|
||||||
|
example.
|
||||||
|
|
||||||
|
* Lightweight. Unlike large vector graphics packages that mix and match many different algorithms,
|
||||||
|
Pathfinder 3 uses a single, simple technique. It consists of a set of modular crates, so
|
||||||
|
applications can pick and choose only the components that are necessary to minimize dependencies.
|
||||||
|
|
||||||
|
* Portability to most GPUs manufactured in the last decade, including integrated and mobile GPUs.
|
||||||
|
Geometry, tessellation, and compute shader functionality is not required.
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
Pathfinder 3 is a set of modular packages, allowing you to choose which parts of the library you
|
||||||
|
need. An SVG rendering demo, written in Rust, is included, so you can try Pathfinder out right
|
||||||
|
away. It also provides an example of how to use the library. (Note that, like the rest of
|
||||||
|
Pathfinder, the demo is under heavy development and has known bugs.)
|
||||||
|
|
||||||
|
Running the demo is as simple as:
|
||||||
|
|
||||||
|
$ cd demo/native
|
||||||
|
$ cargo run --release
|
||||||
|
|
||||||
|
Running examples (e.g. `canvas_nanovg`) can be done with:
|
||||||
|
|
||||||
|
$ cd examples/canvas_nanovg
|
||||||
|
$ cargo run --release
|
||||||
|
|
||||||
|
Pathfinder libraries are available on `crates.io` with the `pathfinder_` prefix (e.g.
|
||||||
|
`pathfinder_canvas`), but you may wish to use the `master` branch for the latest features and bug
|
||||||
|
fixes.
|
||||||
|
|
||||||
|
## Community
|
||||||
|
|
||||||
|
There's a Matrix chat room available at
|
||||||
|
[`#pathfinder:mozilla.org`](https://matrix.to/#/!XiDASQfNTTMrJbXHTw:mozilla.org?via=mozilla.org).
|
||||||
|
If you're on the Mozilla Matrix server, you can search for Pathfinder to find it. For more
|
||||||
|
information on connecting to the Matrix network, see
|
||||||
|
[this `wiki.mozilla.org` page](https://wiki.mozilla.org/Matrix).
|
||||||
|
|
||||||
|
The entire Pathfinder community, including the chat room and GitHub project, is expected to abide
|
||||||
|
by the same Code of Conduct that the Rust project itself follows.
|
||||||
|
|
||||||
|
## Build status
|
||||||
|
|
||||||
|
[![Build Status](https://travis-ci.org/servo/pathfinder.svg?branch=master)](https://travis-ci.org/servo/pathfinder)
|
||||||
|
|
||||||
|
## Authors
|
||||||
|
|
||||||
|
The primary author is Patrick Walton (@pcwalton), with contributions from the Servo development
|
||||||
|
community.
|
||||||
|
|
||||||
|
The logo was designed by Jay Vining.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Pathfinder is licensed under the same terms as Rust itself. See `LICENSE-APACHE` and `LICENSE-MIT`.
|
||||||
|
|
||||||
|
Material Design icons are copyright Google Inc. and licensed under the Apache 2.0 license.
|
||||||
|
|
|
@ -1,52 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "pathfinder_c"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["Patrick Walton <pcwalton@mimiga.net>"]
|
|
||||||
edition = "2018"
|
|
||||||
build = "build.rs"
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
crate-type = ["staticlib"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
font-kit = "0.6"
|
|
||||||
foreign-types = "0.3"
|
|
||||||
gl = "0.14"
|
|
||||||
libc = "0.2"
|
|
||||||
|
|
||||||
[dependencies.pathfinder_canvas]
|
|
||||||
features = ["pf-text"]
|
|
||||||
path = "../canvas"
|
|
||||||
|
|
||||||
[dependencies.pathfinder_color]
|
|
||||||
path = "../color"
|
|
||||||
|
|
||||||
[dependencies.pathfinder_content]
|
|
||||||
path = "../content"
|
|
||||||
|
|
||||||
[dependencies.pathfinder_geometry]
|
|
||||||
path = "../geometry"
|
|
||||||
|
|
||||||
[dependencies.pathfinder_gl]
|
|
||||||
path = "../gl"
|
|
||||||
|
|
||||||
[dependencies.pathfinder_gpu]
|
|
||||||
path = "../gpu"
|
|
||||||
|
|
||||||
[dependencies.pathfinder_renderer]
|
|
||||||
path = "../renderer"
|
|
||||||
|
|
||||||
[dependencies.pathfinder_resources]
|
|
||||||
path = "../resources"
|
|
||||||
|
|
||||||
[dependencies.pathfinder_simd]
|
|
||||||
path = "../simd"
|
|
||||||
|
|
||||||
[target.'cfg(target_os = "macos")'.dependencies]
|
|
||||||
metal = "0.17"
|
|
||||||
|
|
||||||
[target.'cfg(target_os = "macos")'.dependencies.pathfinder_metal]
|
|
||||||
path = "../metal"
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
cbindgen = "0.13"
|
|
|
@ -1,20 +0,0 @@
|
||||||
// pathfinder/c/build.rs
|
|
||||||
//
|
|
||||||
// Copyright © 2019 The Pathfinder Project Developers.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
|
|
||||||
use cbindgen;
|
|
||||||
use std::env;
|
|
||||||
use std::fs;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
fs::create_dir_all("build/include/pathfinder").expect("Failed to create directories!");
|
|
||||||
let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
|
|
||||||
cbindgen::generate(crate_dir).expect("cbindgen failed!")
|
|
||||||
.write_to_file("build/include/pathfinder/pathfinder.h");
|
|
||||||
}
|
|
|
@ -1,454 +0,0 @@
|
||||||
/* Generated code. Do not edit; instead run `cargo build` in `pathfinder_c`. */
|
|
||||||
|
|
||||||
#ifndef PF_PATHFINDER_H
|
|
||||||
#define PF_PATHFINDER_H
|
|
||||||
|
|
||||||
#ifdef __APPLE__
|
|
||||||
#include <QuartzCore/QuartzCore.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/* Generated with cbindgen:0.13.2 */
|
|
||||||
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#define PF_ARC_DIRECTION_CCW 1
|
|
||||||
|
|
||||||
#define PF_ARC_DIRECTION_CW 0
|
|
||||||
|
|
||||||
#define PF_GL_VERSION_GL3 0
|
|
||||||
|
|
||||||
#define PF_GL_VERSION_GLES3 1
|
|
||||||
|
|
||||||
#define PF_LINE_CAP_BUTT 0
|
|
||||||
|
|
||||||
#define PF_LINE_CAP_ROUND 2
|
|
||||||
|
|
||||||
#define PF_LINE_CAP_SQUARE 1
|
|
||||||
|
|
||||||
#define PF_LINE_JOIN_BEVEL 1
|
|
||||||
|
|
||||||
#define PF_LINE_JOIN_MITER 0
|
|
||||||
|
|
||||||
#define PF_LINE_JOIN_ROUND 2
|
|
||||||
|
|
||||||
#define PF_RENDERER_OPTIONS_FLAGS_HAS_BACKGROUND_COLOR 1
|
|
||||||
|
|
||||||
#define PF_TEXT_ALIGN_CENTER 1
|
|
||||||
|
|
||||||
#define PF_TEXT_ALIGN_LEFT 0
|
|
||||||
|
|
||||||
#define PF_TEXT_ALIGN_RIGHT 2
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Options that influence scene building.
|
|
||||||
*/
|
|
||||||
typedef struct PFBuildOptionsPrivate PFBuildOptionsPrivate;
|
|
||||||
|
|
||||||
typedef struct PFCanvasFontContextPrivate PFCanvasFontContextPrivate;
|
|
||||||
|
|
||||||
typedef struct PFCanvasFontContextPrivate PFCanvasFontContextPrivate;
|
|
||||||
|
|
||||||
typedef struct PFCanvasRenderingContext2DPrivate PFCanvasRenderingContext2DPrivate;
|
|
||||||
|
|
||||||
typedef struct PFDestFramebufferGLDevicePrivate PFDestFramebufferGLDevicePrivate;
|
|
||||||
|
|
||||||
typedef struct PFDestFramebufferMetalDevicePrivate PFDestFramebufferMetalDevicePrivate;
|
|
||||||
|
|
||||||
typedef struct PFFillStylePrivate PFFillStylePrivate;
|
|
||||||
|
|
||||||
typedef struct PFGLDevicePrivate PFGLDevicePrivate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encapsulates the information needed to locate and open a font.
|
|
||||||
*
|
|
||||||
* This is either the path to the font or the raw in-memory font data.
|
|
||||||
*
|
|
||||||
* To open the font referenced by a handle, use a loader.
|
|
||||||
*/
|
|
||||||
typedef struct FKHandlePrivate FKHandlePrivate;
|
|
||||||
|
|
||||||
typedef struct PFMetalDevicePrivate PFMetalDevicePrivate;
|
|
||||||
|
|
||||||
typedef struct PFPath2DPrivate PFPath2DPrivate;
|
|
||||||
|
|
||||||
typedef struct PFRenderTransformPrivate PFRenderTransformPrivate;
|
|
||||||
|
|
||||||
typedef struct PFRendererGLDevicePrivate PFRendererGLDevicePrivate;
|
|
||||||
|
|
||||||
typedef struct PFRendererMetalDevicePrivate PFRendererMetalDevicePrivate;
|
|
||||||
|
|
||||||
typedef struct PFResourceLoaderWrapperPrivate PFResourceLoaderWrapperPrivate;
|
|
||||||
|
|
||||||
typedef struct PFScenePrivate PFScenePrivate;
|
|
||||||
|
|
||||||
typedef struct PFSceneProxyPrivate PFSceneProxyPrivate;
|
|
||||||
|
|
||||||
typedef PFBuildOptionsPrivate *PFBuildOptionsRef;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
float x;
|
|
||||||
float y;
|
|
||||||
} PFVector2F;
|
|
||||||
|
|
||||||
typedef PFRenderTransformPrivate *PFRenderTransformRef;
|
|
||||||
|
|
||||||
typedef PFCanvasRenderingContext2DPrivate *PFCanvasRef;
|
|
||||||
|
|
||||||
typedef PFCanvasFontContextPrivate *PFCanvasFontContextRef;
|
|
||||||
|
|
||||||
typedef PFScenePrivate *PFSceneRef;
|
|
||||||
|
|
||||||
typedef PFPath2DPrivate *PFPathRef;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
PFVector2F origin;
|
|
||||||
PFVector2F lower_right;
|
|
||||||
} PFRectF;
|
|
||||||
|
|
||||||
typedef FKHandlePrivate *FKHandleRef;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
float width;
|
|
||||||
} PFTextMetrics;
|
|
||||||
|
|
||||||
typedef PFFillStylePrivate *PFFillStyleRef;
|
|
||||||
|
|
||||||
typedef uint8_t PFLineCap;
|
|
||||||
|
|
||||||
typedef uint8_t PFLineJoin;
|
|
||||||
|
|
||||||
typedef uint8_t PFTextAlign;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Row-major order.
|
|
||||||
*/
|
|
||||||
typedef struct {
|
|
||||||
float m00;
|
|
||||||
float m01;
|
|
||||||
float m10;
|
|
||||||
float m11;
|
|
||||||
} PFMatrix2x2F;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Row-major order.
|
|
||||||
*/
|
|
||||||
typedef struct {
|
|
||||||
PFMatrix2x2F matrix;
|
|
||||||
PFVector2F vector;
|
|
||||||
} PFTransform2F;
|
|
||||||
|
|
||||||
typedef PFResourceLoaderWrapperPrivate *PFResourceLoaderRef;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint8_t r;
|
|
||||||
uint8_t g;
|
|
||||||
uint8_t b;
|
|
||||||
uint8_t a;
|
|
||||||
} PFColorU;
|
|
||||||
|
|
||||||
typedef PFDestFramebufferGLDevicePrivate *PFGLDestFramebufferRef;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
int32_t x;
|
|
||||||
int32_t y;
|
|
||||||
} PFVector2I;
|
|
||||||
|
|
||||||
typedef PFGLDevicePrivate *PFGLDeviceRef;
|
|
||||||
|
|
||||||
typedef uint8_t PFGLVersion;
|
|
||||||
|
|
||||||
typedef const void *(*PFGLFunctionLoader)(const char *name, void *userdata);
|
|
||||||
|
|
||||||
typedef PFRendererGLDevicePrivate *PFGLRendererRef;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
float r;
|
|
||||||
float g;
|
|
||||||
float b;
|
|
||||||
float a;
|
|
||||||
} PFColorF;
|
|
||||||
|
|
||||||
typedef uint8_t PFRendererOptionsFlags;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
PFColorF background_color;
|
|
||||||
PFRendererOptionsFlags flags;
|
|
||||||
} PFRendererOptions;
|
|
||||||
|
|
||||||
typedef PFDestFramebufferMetalDevicePrivate *PFMetalDestFramebufferRef;
|
|
||||||
|
|
||||||
typedef PFMetalDevicePrivate *PFMetalDeviceRef;
|
|
||||||
|
|
||||||
typedef PFRendererMetalDevicePrivate *PFMetalRendererRef;
|
|
||||||
|
|
||||||
typedef uint8_t PFArcDirection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Row-major order.
|
|
||||||
*/
|
|
||||||
typedef struct {
|
|
||||||
float m00;
|
|
||||||
float m01;
|
|
||||||
float m02;
|
|
||||||
float m03;
|
|
||||||
float m10;
|
|
||||||
float m11;
|
|
||||||
float m12;
|
|
||||||
float m13;
|
|
||||||
float m20;
|
|
||||||
float m21;
|
|
||||||
float m22;
|
|
||||||
float m23;
|
|
||||||
float m30;
|
|
||||||
float m31;
|
|
||||||
float m32;
|
|
||||||
float m33;
|
|
||||||
} PFTransform4F;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
PFTransform4F transform;
|
|
||||||
PFVector2I window_size;
|
|
||||||
} PFPerspective;
|
|
||||||
|
|
||||||
typedef PFSceneProxyPrivate *PFSceneProxyRef;
|
|
||||||
|
|
||||||
PFBuildOptionsRef PFBuildOptionsCreate(void);
|
|
||||||
|
|
||||||
void PFBuildOptionsDestroy(PFBuildOptionsRef options);
|
|
||||||
|
|
||||||
void PFBuildOptionsSetDilation(PFBuildOptionsRef options, const PFVector2F *dilation);
|
|
||||||
|
|
||||||
void PFBuildOptionsSetSubpixelAAEnabled(PFBuildOptionsRef options, bool subpixel_aa_enabled);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Consumes the transform.
|
|
||||||
*/
|
|
||||||
void PFBuildOptionsSetTransform(PFBuildOptionsRef options, PFRenderTransformRef transform);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function internally adds a reference to the font context. Therefore, if you created the
|
|
||||||
* font context, you must release it yourself to avoid a leak.
|
|
||||||
*/
|
|
||||||
PFCanvasRef PFCanvasCreate(PFCanvasFontContextRef font_context, const PFVector2F *size);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function takes ownership of the supplied canvas and will automatically destroy it when
|
|
||||||
* the scene is destroyed.
|
|
||||||
*/
|
|
||||||
PFSceneRef PFCanvasCreateScene(PFCanvasRef canvas);
|
|
||||||
|
|
||||||
void PFCanvasDestroy(PFCanvasRef canvas);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function automatically destroys the path. If you wish to use the path again, clone it
|
|
||||||
* first.
|
|
||||||
*/
|
|
||||||
void PFCanvasFillPath(PFCanvasRef canvas, PFPathRef path);
|
|
||||||
|
|
||||||
void PFCanvasFillRect(PFCanvasRef canvas, const PFRectF *rect);
|
|
||||||
|
|
||||||
void PFCanvasFillText(PFCanvasRef canvas,
|
|
||||||
const char *string,
|
|
||||||
uintptr_t string_len,
|
|
||||||
const PFVector2F *position);
|
|
||||||
|
|
||||||
PFCanvasFontContextRef PFCanvasFontContextAddRef(PFCanvasFontContextRef font_context);
|
|
||||||
|
|
||||||
PFCanvasFontContextRef PFCanvasFontContextCreateWithFonts(const FKHandleRef *fonts,
|
|
||||||
uintptr_t font_count);
|
|
||||||
|
|
||||||
PFCanvasFontContextRef PFCanvasFontContextCreateWithSystemSource(void);
|
|
||||||
|
|
||||||
void PFCanvasFontContextRelease(PFCanvasFontContextRef font_context);
|
|
||||||
|
|
||||||
void PFCanvasMeasureText(PFCanvasRef canvas,
|
|
||||||
const char *string,
|
|
||||||
uintptr_t string_len,
|
|
||||||
PFTextMetrics *out_text_metrics);
|
|
||||||
|
|
||||||
void PFCanvasResetTransform(PFCanvasRef canvas);
|
|
||||||
|
|
||||||
void PFCanvasRestore(PFCanvasRef canvas);
|
|
||||||
|
|
||||||
void PFCanvasSave(PFCanvasRef canvas);
|
|
||||||
|
|
||||||
void PFCanvasSetFillStyle(PFCanvasRef canvas, PFFillStyleRef fill_style);
|
|
||||||
|
|
||||||
void PFCanvasSetFontByPostScriptName(PFCanvasRef canvas,
|
|
||||||
const char *postscript_name,
|
|
||||||
uintptr_t postscript_name_len);
|
|
||||||
|
|
||||||
void PFCanvasSetFontSize(PFCanvasRef canvas, float new_font_size);
|
|
||||||
|
|
||||||
void PFCanvasSetLineCap(PFCanvasRef canvas, PFLineCap new_line_cap);
|
|
||||||
|
|
||||||
void PFCanvasSetLineDash(PFCanvasRef canvas,
|
|
||||||
const float *new_line_dashes,
|
|
||||||
uintptr_t new_line_dash_count);
|
|
||||||
|
|
||||||
void PFCanvasSetLineDashOffset(PFCanvasRef canvas, float new_offset);
|
|
||||||
|
|
||||||
void PFCanvasSetLineJoin(PFCanvasRef canvas, PFLineJoin new_line_join);
|
|
||||||
|
|
||||||
void PFCanvasSetLineWidth(PFCanvasRef canvas, float new_line_width);
|
|
||||||
|
|
||||||
void PFCanvasSetMiterLimit(PFCanvasRef canvas, float new_miter_limit);
|
|
||||||
|
|
||||||
void PFCanvasSetStrokeStyle(PFCanvasRef canvas, PFFillStyleRef stroke_style);
|
|
||||||
|
|
||||||
void PFCanvasSetTextAlign(PFCanvasRef canvas, PFTextAlign new_text_align);
|
|
||||||
|
|
||||||
void PFCanvasSetTransform(PFCanvasRef canvas, const PFTransform2F *transform);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function automatically destroys the path. If you wish to use the path again, clone it
|
|
||||||
* first.
|
|
||||||
*/
|
|
||||||
void PFCanvasStrokePath(PFCanvasRef canvas, PFPathRef path);
|
|
||||||
|
|
||||||
void PFCanvasStrokeRect(PFCanvasRef canvas, const PFRectF *rect);
|
|
||||||
|
|
||||||
void PFCanvasStrokeText(PFCanvasRef canvas,
|
|
||||||
const char *string,
|
|
||||||
uintptr_t string_len,
|
|
||||||
const PFVector2F *position);
|
|
||||||
|
|
||||||
PFResourceLoaderRef PFFilesystemResourceLoaderLocate(void);
|
|
||||||
|
|
||||||
PFFillStyleRef PFFillStyleCreateColor(const PFColorU *color);
|
|
||||||
|
|
||||||
void PFFillStyleDestroy(PFFillStyleRef fill_style);
|
|
||||||
|
|
||||||
PFGLDestFramebufferRef PFGLDestFramebufferCreateFullWindow(const PFVector2I *window_size);
|
|
||||||
|
|
||||||
void PFGLDestFramebufferDestroy(PFGLDestFramebufferRef dest_framebuffer);
|
|
||||||
|
|
||||||
PFGLDeviceRef PFGLDeviceCreate(PFGLVersion version, uint32_t default_framebuffer);
|
|
||||||
|
|
||||||
void PFGLDeviceDestroy(PFGLDeviceRef device);
|
|
||||||
|
|
||||||
void PFGLLoadWith(PFGLFunctionLoader loader, void *userdata);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function takes ownership of and automatically takes responsibility for destroying `device`
|
|
||||||
* and `dest_framebuffer`. However, it does not take ownership of `resources`; therefore, if you
|
|
||||||
* created the resource loader, you must destroy it yourself to avoid a memory leak.
|
|
||||||
*/
|
|
||||||
PFGLRendererRef PFGLRendererCreate(PFGLDeviceRef device,
|
|
||||||
PFResourceLoaderRef resources,
|
|
||||||
PFGLDestFramebufferRef dest_framebuffer,
|
|
||||||
const PFRendererOptions *options);
|
|
||||||
|
|
||||||
void PFGLRendererDestroy(PFGLRendererRef renderer);
|
|
||||||
|
|
||||||
PFGLDeviceRef PFGLRendererGetDevice(PFGLRendererRef renderer);
|
|
||||||
|
|
||||||
PFMetalDestFramebufferRef PFMetalDestFramebufferCreateFullWindow(const PFVector2I *window_size);
|
|
||||||
|
|
||||||
void PFMetalDestFramebufferDestroy(PFMetalDestFramebufferRef dest_framebuffer);
|
|
||||||
|
|
||||||
PFMetalDeviceRef PFMetalDeviceCreate(CAMetalLayer *layer);
|
|
||||||
|
|
||||||
void PFMetalDeviceDestroy(PFMetalDeviceRef device);
|
|
||||||
|
|
||||||
void PFMetalDevicePresentDrawable(PFMetalDeviceRef device);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function takes ownership of and automatically takes responsibility for destroying `device`
|
|
||||||
* and `dest_framebuffer`. However, it does not take ownership of `resources`; therefore, if you
|
|
||||||
* created the resource loader, you must destroy it yourself to avoid a memory leak.
|
|
||||||
*/
|
|
||||||
PFMetalRendererRef PFMetalRendererCreate(PFMetalDeviceRef device,
|
|
||||||
PFResourceLoaderRef resources,
|
|
||||||
PFMetalDestFramebufferRef dest_framebuffer,
|
|
||||||
const PFRendererOptions *options);
|
|
||||||
|
|
||||||
void PFMetalRendererDestroy(PFMetalRendererRef renderer);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a reference to the Metal device in the renderer.
|
|
||||||
*
|
|
||||||
* This reference remains valid as long as the device is alive.
|
|
||||||
*/
|
|
||||||
PFMetalDeviceRef PFMetalRendererGetDevice(PFMetalRendererRef renderer);
|
|
||||||
|
|
||||||
void PFPathArc(PFPathRef path,
|
|
||||||
const PFVector2F *center,
|
|
||||||
float radius,
|
|
||||||
float start_angle,
|
|
||||||
float end_angle,
|
|
||||||
PFArcDirection direction);
|
|
||||||
|
|
||||||
void PFPathArcTo(PFPathRef path, const PFVector2F *ctrl, const PFVector2F *to, float radius);
|
|
||||||
|
|
||||||
void PFPathBezierCurveTo(PFPathRef path,
|
|
||||||
const PFVector2F *ctrl0,
|
|
||||||
const PFVector2F *ctrl1,
|
|
||||||
const PFVector2F *to);
|
|
||||||
|
|
||||||
PFPathRef PFPathClone(PFPathRef path);
|
|
||||||
|
|
||||||
void PFPathClosePath(PFPathRef path);
|
|
||||||
|
|
||||||
PFPathRef PFPathCreate(void);
|
|
||||||
|
|
||||||
void PFPathDestroy(PFPathRef path);
|
|
||||||
|
|
||||||
void PFPathEllipse(PFPathRef path,
|
|
||||||
const PFVector2F *center,
|
|
||||||
const PFVector2F *axes,
|
|
||||||
float rotation,
|
|
||||||
float start_angle,
|
|
||||||
float end_angle);
|
|
||||||
|
|
||||||
void PFPathLineTo(PFPathRef path, const PFVector2F *to);
|
|
||||||
|
|
||||||
void PFPathMoveTo(PFPathRef path, const PFVector2F *to);
|
|
||||||
|
|
||||||
void PFPathQuadraticCurveTo(PFPathRef path, const PFVector2F *ctrl, const PFVector2F *to);
|
|
||||||
|
|
||||||
void PFPathRect(PFPathRef path, const PFRectF *rect);
|
|
||||||
|
|
||||||
PFRenderTransformRef PFRenderTransformCreate2D(const PFTransform2F *transform);
|
|
||||||
|
|
||||||
PFRenderTransformRef PFRenderTransformCreatePerspective(const PFPerspective *perspective);
|
|
||||||
|
|
||||||
void PFRenderTransformDestroy(PFRenderTransformRef transform);
|
|
||||||
|
|
||||||
void PFResourceLoaderDestroy(PFResourceLoaderRef loader);
|
|
||||||
|
|
||||||
void PFSceneDestroy(PFSceneRef scene);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function does not take ownership of `renderer` or `build_options`. Therefore, if you
|
|
||||||
* created the renderer and/or options, you must destroy them yourself to avoid a leak.
|
|
||||||
*/
|
|
||||||
void PFSceneProxyBuildAndRenderGL(PFSceneProxyRef scene_proxy,
|
|
||||||
PFGLRendererRef renderer,
|
|
||||||
PFBuildOptionsRef build_options);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function does not take ownership of `renderer` or `build_options`. Therefore, if you
|
|
||||||
* created the renderer and/or options, you must destroy them yourself to avoid a leak.
|
|
||||||
*/
|
|
||||||
void PFSceneProxyBuildAndRenderMetal(PFSceneProxyRef scene_proxy,
|
|
||||||
PFMetalRendererRef renderer,
|
|
||||||
PFBuildOptionsRef build_options);
|
|
||||||
|
|
||||||
PFSceneProxyRef PFSceneProxyCreateFromSceneAndRayonExecutor(PFSceneRef scene);
|
|
||||||
|
|
||||||
void PFSceneProxyDestroy(PFSceneProxyRef scene_proxy);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,54 +0,0 @@
|
||||||
language = "C"
|
|
||||||
header = """\
|
|
||||||
/* Generated code. Do not edit; instead run `cargo build` in `pathfinder_c`. */
|
|
||||||
|
|
||||||
#ifndef PF_PATHFINDER_H
|
|
||||||
#define PF_PATHFINDER_H
|
|
||||||
|
|
||||||
#ifdef __APPLE__
|
|
||||||
#include <QuartzCore/QuartzCore.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern \"C\" {
|
|
||||||
#endif
|
|
||||||
"""
|
|
||||||
trailer = """\
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
"""
|
|
||||||
include_version = true
|
|
||||||
|
|
||||||
[parse]
|
|
||||||
parse_deps = true
|
|
||||||
include = [
|
|
||||||
"font-kit",
|
|
||||||
"pathfinder_canvas",
|
|
||||||
"pathfinder_content",
|
|
||||||
"pathfinder_geometry",
|
|
||||||
"pathfinder_gl",
|
|
||||||
"pathfinder_gpu",
|
|
||||||
"pathfinder_metal",
|
|
||||||
"pathfinder_renderer",
|
|
||||||
]
|
|
||||||
|
|
||||||
[export.rename]
|
|
||||||
"BuildOptions" = "PFBuildOptionsPrivate"
|
|
||||||
"CanvasFontContext" = "PFCanvasFontContextPrivate"
|
|
||||||
"CanvasRenderingContext2D" = "PFCanvasRenderingContext2DPrivate"
|
|
||||||
"DestFramebuffer_GLDevice" = "PFDestFramebufferGLDevicePrivate"
|
|
||||||
"DestFramebuffer_MetalDevice" = "PFDestFramebufferMetalDevicePrivate"
|
|
||||||
"FillStyle" = "PFFillStylePrivate"
|
|
||||||
"GLDevice" = "PFGLDevicePrivate"
|
|
||||||
"Handle" = "FKHandlePrivate"
|
|
||||||
"MetalDevice" = "PFMetalDevicePrivate"
|
|
||||||
"Path2D" = "PFPath2DPrivate"
|
|
||||||
"RenderTransform" = "PFRenderTransformPrivate"
|
|
||||||
"Renderer_GLDevice" = "PFRendererGLDevicePrivate"
|
|
||||||
"Renderer_MetalDevice" = "PFRendererMetalDevicePrivate"
|
|
||||||
"ResourceLoaderWrapper" = "PFResourceLoaderWrapperPrivate"
|
|
||||||
"Scene" = "PFScenePrivate"
|
|
||||||
"SceneProxy" = "PFSceneProxyPrivate"
|
|
|
@ -1,811 +0,0 @@
|
||||||
// pathfinder/c/src/lib.rs
|
|
||||||
//
|
|
||||||
// Copyright © 2019 The Pathfinder Project Developers.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
|
|
||||||
//! C bindings to Pathfinder.
|
|
||||||
|
|
||||||
use font_kit::handle::Handle;
|
|
||||||
use foreign_types::ForeignTypeRef;
|
|
||||||
use gl;
|
|
||||||
use pathfinder_canvas::{Canvas, CanvasFontContext, CanvasRenderingContext2D, FillStyle, LineJoin};
|
|
||||||
use pathfinder_canvas::{Path2D, TextAlign, TextMetrics};
|
|
||||||
use pathfinder_color::{ColorF, ColorU};
|
|
||||||
use pathfinder_content::fill::FillRule;
|
|
||||||
use pathfinder_content::outline::ArcDirection;
|
|
||||||
use pathfinder_content::stroke::LineCap;
|
|
||||||
use pathfinder_geometry::rect::{RectF, RectI};
|
|
||||||
use pathfinder_geometry::transform2d::{Matrix2x2F, Transform2F};
|
|
||||||
use pathfinder_geometry::transform3d::{Perspective, Transform4F};
|
|
||||||
use pathfinder_geometry::vector::{Vector2F, Vector2I};
|
|
||||||
use pathfinder_gl::{GLDevice, GLVersion};
|
|
||||||
use pathfinder_resources::ResourceLoader;
|
|
||||||
use pathfinder_resources::fs::FilesystemResourceLoader;
|
|
||||||
use pathfinder_renderer::concurrent::rayon::RayonExecutor;
|
|
||||||
use pathfinder_renderer::concurrent::scene_proxy::SceneProxy;
|
|
||||||
use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererOptions};
|
|
||||||
use pathfinder_renderer::gpu::renderer::Renderer;
|
|
||||||
use pathfinder_renderer::options::{BuildOptions, RenderTransform};
|
|
||||||
use pathfinder_renderer::scene::Scene;
|
|
||||||
use pathfinder_simd::default::F32x4;
|
|
||||||
use std::ffi::CString;
|
|
||||||
use std::os::raw::{c_char, c_void};
|
|
||||||
use std::slice;
|
|
||||||
use std::str;
|
|
||||||
|
|
||||||
#[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
|
|
||||||
use metal::{CAMetalLayer, CoreAnimationLayerRef};
|
|
||||||
#[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
|
|
||||||
use pathfinder_metal::MetalDevice;
|
|
||||||
|
|
||||||
// Constants
|
|
||||||
|
|
||||||
// `canvas`
|
|
||||||
|
|
||||||
pub const PF_LINE_CAP_BUTT: u8 = 0;
|
|
||||||
pub const PF_LINE_CAP_SQUARE: u8 = 1;
|
|
||||||
pub const PF_LINE_CAP_ROUND: u8 = 2;
|
|
||||||
|
|
||||||
pub const PF_LINE_JOIN_MITER: u8 = 0;
|
|
||||||
pub const PF_LINE_JOIN_BEVEL: u8 = 1;
|
|
||||||
pub const PF_LINE_JOIN_ROUND: u8 = 2;
|
|
||||||
|
|
||||||
pub const PF_TEXT_ALIGN_LEFT: u8 = 0;
|
|
||||||
pub const PF_TEXT_ALIGN_CENTER: u8 = 1;
|
|
||||||
pub const PF_TEXT_ALIGN_RIGHT: u8 = 2;
|
|
||||||
|
|
||||||
// `content`
|
|
||||||
|
|
||||||
pub const PF_ARC_DIRECTION_CW: u8 = 0;
|
|
||||||
pub const PF_ARC_DIRECTION_CCW: u8 = 1;
|
|
||||||
|
|
||||||
// `gl`
|
|
||||||
|
|
||||||
pub const PF_GL_VERSION_GL3: u8 = 0;
|
|
||||||
pub const PF_GL_VERSION_GLES3: u8 = 1;
|
|
||||||
|
|
||||||
// `renderer`
|
|
||||||
|
|
||||||
pub const PF_RENDERER_OPTIONS_FLAGS_HAS_BACKGROUND_COLOR: u8 = 0x1;
|
|
||||||
|
|
||||||
// Types
|
|
||||||
|
|
||||||
// External: `font-kit`
|
|
||||||
pub type FKHandleRef = *mut Handle;
|
|
||||||
|
|
||||||
// `canvas`
|
|
||||||
pub type PFCanvasRef = *mut CanvasRenderingContext2D;
|
|
||||||
pub type PFPathRef = *mut Path2D;
|
|
||||||
pub type PFCanvasFontContextRef = *mut CanvasFontContext;
|
|
||||||
pub type PFFillStyleRef = *mut FillStyle;
|
|
||||||
pub type PFLineCap = u8;
|
|
||||||
pub type PFLineJoin = u8;
|
|
||||||
pub type PFArcDirection = u8;
|
|
||||||
pub type PFTextAlign = u8;
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct PFTextMetrics {
|
|
||||||
pub width: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
// `content`
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct PFColorF {
|
|
||||||
pub r: f32,
|
|
||||||
pub g: f32,
|
|
||||||
pub b: f32,
|
|
||||||
pub a: f32,
|
|
||||||
}
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct PFColorU {
|
|
||||||
pub r: u8,
|
|
||||||
pub g: u8,
|
|
||||||
pub b: u8,
|
|
||||||
pub a: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
// `geometry`
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct PFVector2F {
|
|
||||||
pub x: f32,
|
|
||||||
pub y: f32,
|
|
||||||
}
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct PFVector2I {
|
|
||||||
pub x: i32,
|
|
||||||
pub y: i32,
|
|
||||||
}
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct PFRectF {
|
|
||||||
pub origin: PFVector2F,
|
|
||||||
pub lower_right: PFVector2F,
|
|
||||||
}
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct PFRectI {
|
|
||||||
pub origin: PFVector2I,
|
|
||||||
pub lower_right: PFVector2I,
|
|
||||||
}
|
|
||||||
/// Row-major order.
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct PFMatrix2x2F {
|
|
||||||
pub m00: f32, pub m01: f32,
|
|
||||||
pub m10: f32, pub m11: f32,
|
|
||||||
}
|
|
||||||
/// Row-major order.
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct PFTransform2F {
|
|
||||||
pub matrix: PFMatrix2x2F,
|
|
||||||
pub vector: PFVector2F,
|
|
||||||
}
|
|
||||||
/// Row-major order.
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct PFTransform4F {
|
|
||||||
pub m00: f32, pub m01: f32, pub m02: f32, pub m03: f32,
|
|
||||||
pub m10: f32, pub m11: f32, pub m12: f32, pub m13: f32,
|
|
||||||
pub m20: f32, pub m21: f32, pub m22: f32, pub m23: f32,
|
|
||||||
pub m30: f32, pub m31: f32, pub m32: f32, pub m33: f32,
|
|
||||||
}
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct PFPerspective {
|
|
||||||
pub transform: PFTransform4F,
|
|
||||||
pub window_size: PFVector2I,
|
|
||||||
}
|
|
||||||
|
|
||||||
// `gl`
|
|
||||||
pub type PFGLDeviceRef = *mut GLDevice;
|
|
||||||
pub type PFGLVersion = u8;
|
|
||||||
pub type PFGLFunctionLoader = extern "C" fn(name: *const c_char, userdata: *mut c_void)
|
|
||||||
-> *const c_void;
|
|
||||||
// `gpu`
|
|
||||||
pub type PFGLDestFramebufferRef = *mut DestFramebuffer<GLDevice>;
|
|
||||||
pub type PFGLRendererRef = *mut Renderer<GLDevice>;
|
|
||||||
#[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
|
|
||||||
pub type PFMetalDestFramebufferRef = *mut DestFramebuffer<MetalDevice>;
|
|
||||||
#[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
|
|
||||||
pub type PFMetalRendererRef = *mut Renderer<MetalDevice>;
|
|
||||||
// FIXME(pcwalton): Double-boxing is unfortunate. Remove this when `std::raw::TraitObject` is
|
|
||||||
// stable?
|
|
||||||
pub type PFResourceLoaderRef = *mut ResourceLoaderWrapper;
|
|
||||||
pub struct ResourceLoaderWrapper(Box<dyn ResourceLoader>);
|
|
||||||
|
|
||||||
// `metal`
|
|
||||||
#[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
|
|
||||||
pub type PFMetalDeviceRef = *mut MetalDevice;
|
|
||||||
|
|
||||||
// `renderer`
|
|
||||||
pub type PFSceneRef = *mut Scene;
|
|
||||||
pub type PFSceneProxyRef = *mut SceneProxy;
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct PFRendererOptions {
|
|
||||||
pub background_color: PFColorF,
|
|
||||||
pub flags: PFRendererOptionsFlags,
|
|
||||||
}
|
|
||||||
pub type PFRendererOptionsFlags = u8;
|
|
||||||
pub type PFBuildOptionsRef = *mut BuildOptions;
|
|
||||||
pub type PFRenderTransformRef = *mut RenderTransform;
|
|
||||||
|
|
||||||
// `canvas`
|
|
||||||
|
|
||||||
/// This function internally adds a reference to the font context. Therefore, if you created the
|
|
||||||
/// font context, you must release it yourself to avoid a leak.
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFCanvasCreate(font_context: PFCanvasFontContextRef,
|
|
||||||
size: *const PFVector2F)
|
|
||||||
-> PFCanvasRef {
|
|
||||||
Box::into_raw(Box::new(Canvas::new((*size).to_rust()).get_context_2d((*font_context).clone())))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFCanvasDestroy(canvas: PFCanvasRef) {
|
|
||||||
drop(Box::from_raw(canvas))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFCanvasFontContextCreateWithSystemSource() -> PFCanvasFontContextRef {
|
|
||||||
Box::into_raw(Box::new(CanvasFontContext::from_system_source()))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFCanvasFontContextCreateWithFonts(fonts: *const FKHandleRef,
|
|
||||||
font_count: usize)
|
|
||||||
-> PFCanvasFontContextRef {
|
|
||||||
let fonts = slice::from_raw_parts(fonts, font_count);
|
|
||||||
Box::into_raw(Box::new(CanvasFontContext::from_fonts(fonts.into_iter().map(|font| {
|
|
||||||
(**font).clone()
|
|
||||||
}))))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFCanvasFontContextAddRef(font_context: PFCanvasFontContextRef)
|
|
||||||
-> PFCanvasFontContextRef {
|
|
||||||
Box::into_raw(Box::new((*font_context).clone()))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFCanvasFontContextRelease(font_context: PFCanvasFontContextRef) {
|
|
||||||
drop(Box::from_raw(font_context))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This function takes ownership of the supplied canvas and will automatically destroy it when
|
|
||||||
/// the scene is destroyed.
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFCanvasCreateScene(canvas: PFCanvasRef) -> PFSceneRef {
|
|
||||||
Box::into_raw(Box::new(Box::from_raw(canvas).into_canvas().into_scene()))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Drawing rectangles
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFCanvasFillRect(canvas: PFCanvasRef, rect: *const PFRectF) {
|
|
||||||
(*canvas).fill_rect((*rect).to_rust())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFCanvasStrokeRect(canvas: PFCanvasRef, rect: *const PFRectF) {
|
|
||||||
(*canvas).stroke_rect((*rect).to_rust())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Drawing text
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFCanvasFillText(canvas: PFCanvasRef,
|
|
||||||
string: *const c_char,
|
|
||||||
string_len: usize,
|
|
||||||
position: *const PFVector2F) {
|
|
||||||
(*canvas).fill_text(to_rust_string(&string, string_len), (*position).to_rust())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFCanvasStrokeText(canvas: PFCanvasRef,
|
|
||||||
string: *const c_char,
|
|
||||||
string_len: usize,
|
|
||||||
position: *const PFVector2F) {
|
|
||||||
(*canvas).stroke_text(to_rust_string(&string, string_len), (*position).to_rust())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFCanvasMeasureText(canvas: PFCanvasRef,
|
|
||||||
string: *const c_char,
|
|
||||||
string_len: usize,
|
|
||||||
out_text_metrics: *mut PFTextMetrics) {
|
|
||||||
debug_assert!(!out_text_metrics.is_null());
|
|
||||||
*out_text_metrics = (*canvas).measure_text(to_rust_string(&string, string_len)).to_c()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFCanvasSetLineWidth(canvas: PFCanvasRef, new_line_width: f32) {
|
|
||||||
(*canvas).set_line_width(new_line_width)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFCanvasSetLineCap(canvas: PFCanvasRef, new_line_cap: PFLineCap) {
|
|
||||||
(*canvas).set_line_cap(match new_line_cap {
|
|
||||||
PF_LINE_CAP_SQUARE => LineCap::Square,
|
|
||||||
PF_LINE_CAP_ROUND => LineCap::Round,
|
|
||||||
_ => LineCap::Butt,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFCanvasSetLineJoin(canvas: PFCanvasRef, new_line_join: PFLineJoin) {
|
|
||||||
(*canvas).set_line_join(match new_line_join {
|
|
||||||
PF_LINE_JOIN_BEVEL => LineJoin::Bevel,
|
|
||||||
PF_LINE_JOIN_ROUND => LineJoin::Round,
|
|
||||||
_ => LineJoin::Miter,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFCanvasSetMiterLimit(canvas: PFCanvasRef, new_miter_limit: f32) {
|
|
||||||
(*canvas).set_miter_limit(new_miter_limit);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFCanvasSetLineDash(canvas: PFCanvasRef,
|
|
||||||
new_line_dashes: *const f32,
|
|
||||||
new_line_dash_count: usize) {
|
|
||||||
(*canvas).set_line_dash(slice::from_raw_parts(new_line_dashes, new_line_dash_count).to_vec())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFCanvasSetTransform(canvas: PFCanvasRef,
|
|
||||||
transform: *const PFTransform2F) {
|
|
||||||
(*canvas).set_transform(&(*transform).to_rust());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFCanvasResetTransform(canvas: PFCanvasRef) {
|
|
||||||
(*canvas).reset_transform();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFCanvasSave(canvas: PFCanvasRef) {
|
|
||||||
(*canvas).save();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFCanvasRestore(canvas: PFCanvasRef) {
|
|
||||||
(*canvas).restore();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFCanvasSetLineDashOffset(canvas: PFCanvasRef, new_offset: f32) {
|
|
||||||
(*canvas).set_line_dash_offset(new_offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFCanvasSetFontByPostScriptName(canvas: PFCanvasRef,
|
|
||||||
postscript_name: *const c_char,
|
|
||||||
postscript_name_len: usize) {
|
|
||||||
(*canvas).set_font(to_rust_string(&postscript_name, postscript_name_len))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFCanvasSetFontSize(canvas: PFCanvasRef, new_font_size: f32) {
|
|
||||||
(*canvas).set_font_size(new_font_size)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFCanvasSetTextAlign(canvas: PFCanvasRef, new_text_align: PFTextAlign) {
|
|
||||||
(*canvas).set_text_align(match new_text_align {
|
|
||||||
PF_TEXT_ALIGN_CENTER => TextAlign::Center,
|
|
||||||
PF_TEXT_ALIGN_RIGHT => TextAlign::Right,
|
|
||||||
_ => TextAlign::Left,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFCanvasSetFillStyle(canvas: PFCanvasRef, fill_style: PFFillStyleRef) {
|
|
||||||
// FIXME(pcwalton): Avoid the copy?
|
|
||||||
(*canvas).set_fill_style((*fill_style).clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFCanvasSetStrokeStyle(canvas: PFCanvasRef,
|
|
||||||
stroke_style: PFFillStyleRef) {
|
|
||||||
// FIXME(pcwalton): Avoid the copy?
|
|
||||||
(*canvas).set_stroke_style((*stroke_style).clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This function automatically destroys the path. If you wish to use the path again, clone it
|
|
||||||
/// first.
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFCanvasFillPath(canvas: PFCanvasRef, path: PFPathRef) {
|
|
||||||
// TODO(pcwalton): Expose fill rules to the C API.
|
|
||||||
(*canvas).fill_path(*Box::from_raw(path), FillRule::Winding)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This function automatically destroys the path. If you wish to use the path again, clone it
|
|
||||||
/// first.
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFCanvasStrokePath(canvas: PFCanvasRef, path: PFPathRef) {
|
|
||||||
(*canvas).stroke_path(*Box::from_raw(path))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFPathCreate() -> PFPathRef {
|
|
||||||
Box::into_raw(Box::new(Path2D::new()))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFPathDestroy(path: PFPathRef) {
|
|
||||||
drop(Box::from_raw(path))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFPathClone(path: PFPathRef) -> PFPathRef {
|
|
||||||
Box::into_raw(Box::new((*path).clone()))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFPathMoveTo(path: PFPathRef, to: *const PFVector2F) {
|
|
||||||
(*path).move_to((*to).to_rust())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFPathLineTo(path: PFPathRef, to: *const PFVector2F) {
|
|
||||||
(*path).line_to((*to).to_rust())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFPathQuadraticCurveTo(path: PFPathRef,
|
|
||||||
ctrl: *const PFVector2F,
|
|
||||||
to: *const PFVector2F) {
|
|
||||||
(*path).quadratic_curve_to((*ctrl).to_rust(), (*to).to_rust())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFPathBezierCurveTo(path: PFPathRef,
|
|
||||||
ctrl0: *const PFVector2F,
|
|
||||||
ctrl1: *const PFVector2F,
|
|
||||||
to: *const PFVector2F) {
|
|
||||||
(*path).bezier_curve_to((*ctrl0).to_rust(), (*ctrl1).to_rust(), (*to).to_rust())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFPathArc(path: PFPathRef,
|
|
||||||
center: *const PFVector2F,
|
|
||||||
radius: f32,
|
|
||||||
start_angle: f32,
|
|
||||||
end_angle: f32,
|
|
||||||
direction: PFArcDirection) {
|
|
||||||
let direction = if direction == 0 { ArcDirection::CW } else { ArcDirection::CCW };
|
|
||||||
(*path).arc((*center).to_rust(), radius, start_angle, end_angle, direction)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFPathArcTo(path: PFPathRef,
|
|
||||||
ctrl: *const PFVector2F,
|
|
||||||
to: *const PFVector2F,
|
|
||||||
radius: f32) {
|
|
||||||
(*path).arc_to((*ctrl).to_rust(), (*to).to_rust(), radius)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFPathRect(path: PFPathRef, rect: *const PFRectF) {
|
|
||||||
(*path).rect((*rect).to_rust())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFPathEllipse(path: PFPathRef,
|
|
||||||
center: *const PFVector2F,
|
|
||||||
axes: *const PFVector2F,
|
|
||||||
rotation: f32,
|
|
||||||
start_angle: f32,
|
|
||||||
end_angle: f32) {
|
|
||||||
(*path).ellipse((*center).to_rust(), (*axes).to_rust(), rotation, start_angle, end_angle)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFPathClosePath(path: PFPathRef) {
|
|
||||||
(*path).close_path()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFFillStyleCreateColor(color: *const PFColorU) -> PFFillStyleRef {
|
|
||||||
Box::into_raw(Box::new(FillStyle::Color((*color).to_rust())))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFFillStyleDestroy(fill_style: PFFillStyleRef) {
|
|
||||||
drop(Box::from_raw(fill_style))
|
|
||||||
}
|
|
||||||
|
|
||||||
// `gl`
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFFilesystemResourceLoaderLocate() -> PFResourceLoaderRef {
|
|
||||||
let loader = Box::new(FilesystemResourceLoader::locate());
|
|
||||||
Box::into_raw(Box::new(ResourceLoaderWrapper(loader as Box<dyn ResourceLoader>)))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFGLLoadWith(loader: PFGLFunctionLoader, userdata: *mut c_void) {
|
|
||||||
gl::load_with(|name| {
|
|
||||||
let name = CString::new(name).unwrap();
|
|
||||||
loader(name.as_ptr(), userdata)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFGLDeviceCreate(version: PFGLVersion, default_framebuffer: u32)
|
|
||||||
-> PFGLDeviceRef {
|
|
||||||
let version = match version { PF_GL_VERSION_GLES3 => GLVersion::GLES3, _ => GLVersion::GL3 };
|
|
||||||
Box::into_raw(Box::new(GLDevice::new(version, default_framebuffer)))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFGLDeviceDestroy(device: PFGLDeviceRef) {
|
|
||||||
drop(Box::from_raw(device))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFResourceLoaderDestroy(loader: PFResourceLoaderRef) {
|
|
||||||
drop(Box::from_raw(loader))
|
|
||||||
}
|
|
||||||
|
|
||||||
// `gpu`
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFGLDestFramebufferCreateFullWindow(window_size: *const PFVector2I)
|
|
||||||
-> PFGLDestFramebufferRef {
|
|
||||||
Box::into_raw(Box::new(DestFramebuffer::full_window((*window_size).to_rust())))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFGLDestFramebufferDestroy(dest_framebuffer: PFGLDestFramebufferRef) {
|
|
||||||
drop(Box::from_raw(dest_framebuffer))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This function takes ownership of and automatically takes responsibility for destroying `device`
|
|
||||||
/// and `dest_framebuffer`. However, it does not take ownership of `resources`; therefore, if you
|
|
||||||
/// created the resource loader, you must destroy it yourself to avoid a memory leak.
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFGLRendererCreate(device: PFGLDeviceRef,
|
|
||||||
resources: PFResourceLoaderRef,
|
|
||||||
dest_framebuffer: PFGLDestFramebufferRef,
|
|
||||||
options: *const PFRendererOptions)
|
|
||||||
-> PFGLRendererRef {
|
|
||||||
Box::into_raw(Box::new(Renderer::new(*Box::from_raw(device),
|
|
||||||
&*((*resources).0),
|
|
||||||
*Box::from_raw(dest_framebuffer),
|
|
||||||
(*options).to_rust())))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFGLRendererDestroy(renderer: PFGLRendererRef) {
|
|
||||||
drop(Box::from_raw(renderer))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFGLRendererGetDevice(renderer: PFGLRendererRef) -> PFGLDeviceRef {
|
|
||||||
&mut (*renderer).device
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFMetalDestFramebufferCreateFullWindow(window_size: *const PFVector2I)
|
|
||||||
-> PFMetalDestFramebufferRef {
|
|
||||||
Box::into_raw(Box::new(DestFramebuffer::full_window((*window_size).to_rust())))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFMetalDestFramebufferDestroy(dest_framebuffer:
|
|
||||||
PFMetalDestFramebufferRef) {
|
|
||||||
drop(Box::from_raw(dest_framebuffer))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This function takes ownership of and automatically takes responsibility for destroying `device`
|
|
||||||
/// and `dest_framebuffer`. However, it does not take ownership of `resources`; therefore, if you
|
|
||||||
/// created the resource loader, you must destroy it yourself to avoid a memory leak.
|
|
||||||
#[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFMetalRendererCreate(device: PFMetalDeviceRef,
|
|
||||||
resources: PFResourceLoaderRef,
|
|
||||||
dest_framebuffer: PFMetalDestFramebufferRef,
|
|
||||||
options: *const PFRendererOptions)
|
|
||||||
-> PFMetalRendererRef {
|
|
||||||
Box::into_raw(Box::new(Renderer::new(*Box::from_raw(device),
|
|
||||||
&*((*resources).0),
|
|
||||||
*Box::from_raw(dest_framebuffer),
|
|
||||||
(*options).to_rust())))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFMetalRendererDestroy(renderer: PFMetalRendererRef) {
|
|
||||||
drop(Box::from_raw(renderer))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a reference to the Metal device in the renderer.
|
|
||||||
///
|
|
||||||
/// This reference remains valid as long as the device is alive.
|
|
||||||
#[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFMetalRendererGetDevice(renderer: PFMetalRendererRef)
|
|
||||||
-> PFMetalDeviceRef {
|
|
||||||
&mut (*renderer).device
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This function does not take ownership of `renderer` or `build_options`. Therefore, if you
|
|
||||||
/// created the renderer and/or options, you must destroy them yourself to avoid a leak.
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFSceneProxyBuildAndRenderGL(scene_proxy: PFSceneProxyRef,
|
|
||||||
renderer: PFGLRendererRef,
|
|
||||||
build_options: PFBuildOptionsRef) {
|
|
||||||
(*scene_proxy).build_and_render(&mut *renderer, (*build_options).clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This function does not take ownership of `renderer` or `build_options`. Therefore, if you
|
|
||||||
/// created the renderer and/or options, you must destroy them yourself to avoid a leak.
|
|
||||||
#[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFSceneProxyBuildAndRenderMetal(scene_proxy: PFSceneProxyRef,
|
|
||||||
renderer: PFMetalRendererRef,
|
|
||||||
build_options: PFBuildOptionsRef) {
|
|
||||||
(*scene_proxy).build_and_render(&mut *renderer, (*build_options).clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
// `metal`
|
|
||||||
|
|
||||||
#[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFMetalDeviceCreate(layer: *mut CAMetalLayer)
|
|
||||||
-> PFMetalDeviceRef {
|
|
||||||
Box::into_raw(Box::new(MetalDevice::new(CoreAnimationLayerRef::from_ptr(layer))))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFMetalDeviceDestroy(device: PFMetalDeviceRef) {
|
|
||||||
drop(Box::from_raw(device))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFMetalDevicePresentDrawable(device: PFMetalDeviceRef) {
|
|
||||||
(*device).present_drawable()
|
|
||||||
}
|
|
||||||
|
|
||||||
// `renderer`
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFRenderTransformCreate2D(transform: *const PFTransform2F)
|
|
||||||
-> PFRenderTransformRef {
|
|
||||||
Box::into_raw(Box::new(RenderTransform::Transform2D((*transform).to_rust())))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFRenderTransformCreatePerspective(perspective: *const PFPerspective)
|
|
||||||
-> PFRenderTransformRef {
|
|
||||||
Box::into_raw(Box::new(RenderTransform::Perspective((*perspective).to_rust())))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFRenderTransformDestroy(transform: PFRenderTransformRef) {
|
|
||||||
drop(Box::from_raw(transform))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFBuildOptionsCreate() -> PFBuildOptionsRef {
|
|
||||||
Box::into_raw(Box::new(BuildOptions::default()))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFBuildOptionsDestroy(options: PFBuildOptionsRef) {
|
|
||||||
drop(Box::from_raw(options))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Consumes the transform.
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFBuildOptionsSetTransform(options: PFBuildOptionsRef,
|
|
||||||
transform: PFRenderTransformRef) {
|
|
||||||
(*options).transform = *Box::from_raw(transform)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFBuildOptionsSetDilation(options: PFBuildOptionsRef,
|
|
||||||
dilation: *const PFVector2F) {
|
|
||||||
(*options).dilation = (*dilation).to_rust()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFBuildOptionsSetSubpixelAAEnabled(options: PFBuildOptionsRef,
|
|
||||||
subpixel_aa_enabled: bool) {
|
|
||||||
(*options).subpixel_aa_enabled = subpixel_aa_enabled
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFSceneDestroy(scene: PFSceneRef) {
|
|
||||||
drop(Box::from_raw(scene))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFSceneProxyCreateFromSceneAndRayonExecutor(scene: PFSceneRef)
|
|
||||||
-> PFSceneProxyRef {
|
|
||||||
Box::into_raw(Box::new(SceneProxy::from_scene(*Box::from_raw(scene), RayonExecutor)))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn PFSceneProxyDestroy(scene_proxy: PFSceneProxyRef) {
|
|
||||||
drop(Box::from_raw(scene_proxy))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helpers for `canvas`
|
|
||||||
|
|
||||||
unsafe fn to_rust_string(ptr: &*const c_char, mut len: usize) -> &str {
|
|
||||||
if len == 0 {
|
|
||||||
len = libc::strlen(*ptr);
|
|
||||||
}
|
|
||||||
str::from_utf8(slice::from_raw_parts(*ptr as *const u8, len)).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
trait TextMetricsExt {
|
|
||||||
fn to_c(&self) -> PFTextMetrics;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TextMetricsExt for TextMetrics {
|
|
||||||
fn to_c(&self) -> PFTextMetrics {
|
|
||||||
PFTextMetrics { width: self.width }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helpers for `content`
|
|
||||||
|
|
||||||
impl PFColorF {
|
|
||||||
#[inline]
|
|
||||||
pub fn to_rust(&self) -> ColorF {
|
|
||||||
ColorF(F32x4::new(self.r, self.g, self.b, self.a))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PFColorU {
|
|
||||||
#[inline]
|
|
||||||
pub fn to_rust(&self) -> ColorU {
|
|
||||||
ColorU { r: self.r, g: self.g, b: self.b, a: self.a }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helpers for `geometry`
|
|
||||||
|
|
||||||
impl PFRectF {
|
|
||||||
#[inline]
|
|
||||||
pub fn to_rust(&self) -> RectF {
|
|
||||||
RectF::from_points(self.origin.to_rust(), self.lower_right.to_rust())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PFRectI {
|
|
||||||
#[inline]
|
|
||||||
pub fn to_rust(&self) -> RectI {
|
|
||||||
RectI::from_points(self.origin.to_rust(), self.lower_right.to_rust())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PFVector2F {
|
|
||||||
#[inline]
|
|
||||||
pub fn to_rust(&self) -> Vector2F {
|
|
||||||
Vector2F::new(self.x, self.y)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PFVector2I {
|
|
||||||
#[inline]
|
|
||||||
pub fn to_rust(&self) -> Vector2I {
|
|
||||||
Vector2I::new(self.x, self.y)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PFMatrix2x2F {
|
|
||||||
#[inline]
|
|
||||||
pub fn to_rust(&self) -> Matrix2x2F {
|
|
||||||
Matrix2x2F::row_major(self.m00, self.m01, self.m10, self.m11)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PFTransform2F {
|
|
||||||
#[inline]
|
|
||||||
pub fn to_rust(&self) -> Transform2F {
|
|
||||||
Transform2F { matrix: self.matrix.to_rust(), vector: self.vector.to_rust() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PFTransform4F {
|
|
||||||
#[inline]
|
|
||||||
pub fn to_rust(&self) -> Transform4F {
|
|
||||||
Transform4F::row_major(self.m00, self.m01, self.m02, self.m03,
|
|
||||||
self.m10, self.m11, self.m12, self.m13,
|
|
||||||
self.m20, self.m21, self.m22, self.m23,
|
|
||||||
self.m30, self.m31, self.m32, self.m33)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PFPerspective {
|
|
||||||
#[inline]
|
|
||||||
pub fn to_rust(&self) -> Perspective {
|
|
||||||
Perspective {
|
|
||||||
transform: self.transform.to_rust(),
|
|
||||||
window_size: self.window_size.to_rust(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helpers for `renderer`
|
|
||||||
|
|
||||||
impl PFRendererOptions {
|
|
||||||
pub fn to_rust(&self) -> RendererOptions {
|
|
||||||
let has_background_color = self.flags & PF_RENDERER_OPTIONS_FLAGS_HAS_BACKGROUND_COLOR;
|
|
||||||
RendererOptions {
|
|
||||||
background_color: if has_background_color != 0 {
|
|
||||||
Some(self.background_color.to_rust())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
10
crates/pathfinder/demo/android/.gitignore
vendored
|
@ -1,10 +0,0 @@
|
||||||
*.iml
|
|
||||||
.gradle
|
|
||||||
/local.properties
|
|
||||||
/.idea/libraries
|
|
||||||
/.idea/modules.xml
|
|
||||||
/.idea/workspace.xml
|
|
||||||
.DS_Store
|
|
||||||
/build
|
|
||||||
/captures
|
|
||||||
.externalNativeBuild
|
|
|
@ -1 +0,0 @@
|
||||||
/build
|
|
|
@ -1,34 +0,0 @@
|
||||||
apply plugin: 'com.android.application'
|
|
||||||
|
|
||||||
android {
|
|
||||||
compileSdkVersion 27
|
|
||||||
defaultConfig {
|
|
||||||
applicationId "graphics.pathfinder.pathfinderdemo"
|
|
||||||
minSdkVersion 24
|
|
||||||
targetSdkVersion 27
|
|
||||||
versionCode 1
|
|
||||||
versionName "1.0"
|
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
|
||||||
ndk {
|
|
||||||
abiFilters "arm64-v8a"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buildTypes {
|
|
||||||
release {
|
|
||||||
minifyEnabled false
|
|
||||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
|
|
||||||
compile 'com.google.vr:sdk-base:1.160.0'
|
|
||||||
|
|
||||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
|
||||||
implementation 'com.android.support:appcompat-v7:27.1.1'
|
|
||||||
implementation 'com.android.support:support-v4:27.1.1'
|
|
||||||
testImplementation 'junit:junit:4.12'
|
|
||||||
androidTestImplementation 'com.android.support.test:runner:1.0.2'
|
|
||||||
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
# Add project specific ProGuard rules here.
|
|
||||||
# You can control the set of applied configuration files using the
|
|
||||||
# proguardFiles setting in build.gradle.
|
|
||||||
#
|
|
||||||
# For more details, see
|
|
||||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
|
||||||
|
|
||||||
# If your project uses WebView with JS, uncomment the following
|
|
||||||
# and specify the fully qualified class name to the JavaScript interface
|
|
||||||
# class:
|
|
||||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
|
||||||
# public *;
|
|
||||||
#}
|
|
||||||
|
|
||||||
# Uncomment this to preserve the line number information for
|
|
||||||
# debugging stack traces.
|
|
||||||
#-keepattributes SourceFile,LineNumberTable
|
|
||||||
|
|
||||||
# If you keep the line number information, uncomment this to
|
|
||||||
# hide the original source file name.
|
|
||||||
#-renamesourcefileattribute SourceFile
|
|
|
@ -1,67 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
package="graphics.pathfinder.pathfinderdemo">
|
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
|
||||||
|
|
||||||
<uses-feature
|
|
||||||
android:name="android.software.vr.mode"
|
|
||||||
android:required="false" />
|
|
||||||
<uses-feature
|
|
||||||
android:name="android.hardware.vr.high_performance"
|
|
||||||
android:required="false" />
|
|
||||||
|
|
||||||
<application
|
|
||||||
android:allowBackup="true"
|
|
||||||
android:icon="@mipmap/ic_launcher"
|
|
||||||
android:label="@string/app_name"
|
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
|
||||||
android:supportsRtl="true"
|
|
||||||
android:theme="@style/AppTheme">
|
|
||||||
<activity
|
|
||||||
android:name=".PathfinderDemoActivity"
|
|
||||||
android:configChanges="density|navigation|orientation|keyboardHidden|screenSize|uiMode"
|
|
||||||
android:label="@string/app_name"
|
|
||||||
android:resizeableActivity="false"
|
|
||||||
android:theme="@style/AppTheme">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.MAIN" />
|
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
|
||||||
|
|
||||||
<!--
|
|
||||||
This marks the Activity as a Daydream Activity and allows it
|
|
||||||
to be launched from the Daydream Home.
|
|
||||||
-->
|
|
||||||
<category android:name="com.google.intent.category.DAYDREAM" />
|
|
||||||
|
|
||||||
<!--
|
|
||||||
This marks the Activity as a Cardboard Activity and allows it
|
|
||||||
to be launched from the Cardboard app.
|
|
||||||
-->
|
|
||||||
<category android:name="com.google.intent.category.CARDBOARD" />
|
|
||||||
|
|
||||||
<!--
|
|
||||||
This allows this Activity to be launched from the traditional
|
|
||||||
Android 2D launcher as well. Remove it if you do not want
|
|
||||||
this Activity to be launched directly from the 2D launcher.
|
|
||||||
-->
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
|
||||||
</intent-filter>
|
|
||||||
</activity>
|
|
||||||
|
|
||||||
<service
|
|
||||||
android:name=".PathfinderDemoVRListenerService"
|
|
||||||
android:label="@string/service_name"
|
|
||||||
android:permission="android.permission.BIND_VR_LISTENER_SERVICE">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.service.vr.VrListenerService" />
|
|
||||||
</intent-filter>
|
|
||||||
</service>
|
|
||||||
|
|
||||||
<activity
|
|
||||||
android:name=".PathfinderDemoFileBrowserActivity"
|
|
||||||
android:label="@string/title_activity_pathfinder_demo_file_browser"></activity>
|
|
||||||
</application>
|
|
||||||
|
|
||||||
</manifest>
|
|
|
@ -1 +0,0 @@
|
||||||
../../../../../../resources
|
|
|
@ -1,178 +0,0 @@
|
||||||
package graphics.pathfinder.pathfinderdemo;
|
|
||||||
|
|
||||||
import android.Manifest;
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.ComponentName;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.hardware.Sensor;
|
|
||||||
import android.hardware.SensorEvent;
|
|
||||||
import android.hardware.SensorEventListener;
|
|
||||||
import android.hardware.SensorManager;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.annotation.RequiresApi;
|
|
||||||
import android.support.v4.app.ActivityCompat;
|
|
||||||
import android.support.v4.content.ContextCompat;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.GestureDetector;
|
|
||||||
import android.view.MotionEvent;
|
|
||||||
import android.view.ScaleGestureDetector;
|
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An example full-screen activity that shows and hides the system UI (i.e.
|
|
||||||
* status bar and navigation/system bar) with user interaction.
|
|
||||||
*/
|
|
||||||
public class PathfinderDemoActivity extends Activity {
|
|
||||||
private PathfinderDemoRenderer mRenderer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Some older devices needs a small delay between UI widget updates
|
|
||||||
* and a change of the status and navigation bar.
|
|
||||||
*/
|
|
||||||
private PathfinderDemoSurfaceView mContentView;
|
|
||||||
|
|
||||||
private GestureDetector mGestureDetector;
|
|
||||||
private ScaleGestureDetector mScaleGestureDetector;
|
|
||||||
|
|
||||||
ComponentName mVRListenerComponentName;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
if (ContextCompat.checkSelfPermission(this,
|
|
||||||
Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
|
||||||
String[] perms = new String[1];
|
|
||||||
perms[0] = Manifest.permission.READ_EXTERNAL_STORAGE;
|
|
||||||
ActivityCompat.requestPermissions(this, perms,
|
|
||||||
1);
|
|
||||||
} else {
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
|
||||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
|
||||||
|
|
||||||
if (permissions[0] == Manifest.permission.READ_EXTERNAL_STORAGE)
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
|
||||||
void setVRMode(boolean enabled) {
|
|
||||||
mContentView.setStereoModeEnabled(enabled);
|
|
||||||
mContentView.setDistortionCorrectionEnabled(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
|
||||||
private void init() {
|
|
||||||
mVRListenerComponentName = new ComponentName("graphics.pathfinder.pathfinderdemo",
|
|
||||||
"graphics.pathfinder.pathfinderdemo.PathfinderDemoVRListenerService");
|
|
||||||
|
|
||||||
setContentView(R.layout.activity_pathfinder);
|
|
||||||
|
|
||||||
mContentView = findViewById(R.id.fullscreen_content);
|
|
||||||
setVRMode(false);
|
|
||||||
|
|
||||||
mContentView.setEGLContextClientVersion(3);
|
|
||||||
mRenderer = new PathfinderDemoRenderer(this);
|
|
||||||
mContentView.setRenderer(mRenderer);
|
|
||||||
|
|
||||||
GestureDetector.SimpleOnGestureListener gestureListener =
|
|
||||||
new GestureDetector.SimpleOnGestureListener() {
|
|
||||||
public boolean onScroll(final MotionEvent from,
|
|
||||||
final MotionEvent to,
|
|
||||||
final float deltaX,
|
|
||||||
final float deltaY) {
|
|
||||||
final int x = Math.round(to.getX());
|
|
||||||
final int y = Math.round(to.getY());
|
|
||||||
PathfinderDemoRenderer.pushMouseDraggedEvent(x, y);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean onDown(final MotionEvent event) {
|
|
||||||
final int x = Math.round(event.getX());
|
|
||||||
final int y = Math.round(event.getY());
|
|
||||||
PathfinderDemoRenderer.pushMouseDownEvent(x, y);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
mGestureDetector = new GestureDetector(getApplicationContext(), gestureListener);
|
|
||||||
|
|
||||||
ScaleGestureDetector.SimpleOnScaleGestureListener scaleGestureListener =
|
|
||||||
new ScaleGestureDetector.SimpleOnScaleGestureListener() {
|
|
||||||
public boolean onScale(final ScaleGestureDetector detector) {
|
|
||||||
int focusX = Math.round(detector.getFocusX());
|
|
||||||
int focusY = Math.round(detector.getFocusY());
|
|
||||||
float factor = (detector.getScaleFactor() - 1.0f) * 0.5f;
|
|
||||||
PathfinderDemoRenderer.pushZoomEvent(factor, focusX, focusY);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
mScaleGestureDetector = new ScaleGestureDetector(getApplicationContext(),
|
|
||||||
scaleGestureListener);
|
|
||||||
|
|
||||||
mContentView.setOnTouchListener(new View.OnTouchListener() {
|
|
||||||
@Override
|
|
||||||
public boolean onTouch(final View view, final MotionEvent event) {
|
|
||||||
boolean result = mScaleGestureDetector.onTouchEvent(event);
|
|
||||||
if (!mScaleGestureDetector.isInProgress())
|
|
||||||
result = mGestureDetector.onTouchEvent(event) || result;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
final SensorManager sensorManager = (SensorManager)
|
|
||||||
getSystemService(Context.SENSOR_SERVICE);
|
|
||||||
final Sensor rotationSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);
|
|
||||||
sensorManager.registerListener(new SensorEventListener() {
|
|
||||||
private boolean mInitialized;
|
|
||||||
private float mPitch;
|
|
||||||
private float mYaw;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSensorChanged(SensorEvent event) {
|
|
||||||
// https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles#Quaternion_to_Euler_Angles_Conversion
|
|
||||||
final float[] q = event.values;
|
|
||||||
final float pitch = (float)Math.asin(2.0 * (q[0] * q[2] - q[3] * q[1]));
|
|
||||||
final float yaw = (float)Math.atan2(2.0 * (q[0] * q[3] + q[1] * q[2]),
|
|
||||||
1.0 - 2.0 * (q[2] * q[2] + q[3] * q[3]));
|
|
||||||
|
|
||||||
final float deltaPitch = pitch - mPitch;
|
|
||||||
final float deltaYaw = yaw - mYaw;
|
|
||||||
|
|
||||||
mPitch = pitch;
|
|
||||||
mYaw = yaw;
|
|
||||||
|
|
||||||
if (!mInitialized) {
|
|
||||||
mInitialized = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
PathfinderDemoRenderer.pushLookEvent(-deltaPitch, deltaYaw);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}, rotationSensor, 5000);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostCreate(Bundle savedInstanceState) {
|
|
||||||
super.onPostCreate(savedInstanceState);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void presentOpenSVGDialog() {
|
|
||||||
final Intent intent = new Intent(this, PathfinderDemoFileBrowserActivity.class);
|
|
||||||
startActivity(intent);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
package graphics.pathfinder.pathfinderdemo;
|
|
||||||
|
|
||||||
import android.content.res.AssetManager;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.AdapterView;
|
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
import android.widget.ListView;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public class PathfinderDemoFileBrowserActivity extends Activity {
|
|
||||||
private ListView mBrowserView;
|
|
||||||
|
|
||||||
private static String SVG_RESOURCE_PATH = "svg/";
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
setContentView(R.layout.activity_pathfinder_demo_file_browser);
|
|
||||||
|
|
||||||
mBrowserView = findViewById(R.id.fileBrowserBrowser);
|
|
||||||
|
|
||||||
try {
|
|
||||||
final AssetManager assetManager = getAssets();
|
|
||||||
final String[] svgFilenames = assetManager.list("resources/" + SVG_RESOURCE_PATH);
|
|
||||||
final ArrayAdapter<String> adapter = new ArrayAdapter<String>(
|
|
||||||
this,
|
|
||||||
R.layout.layout_pathfinder_demo_file_browser_list_item,
|
|
||||||
svgFilenames);
|
|
||||||
mBrowserView.setAdapter(adapter);
|
|
||||||
} catch (IOException exception) {
|
|
||||||
throw new RuntimeException(exception);
|
|
||||||
}
|
|
||||||
|
|
||||||
mBrowserView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
|
||||||
TextView textView = (TextView)view;
|
|
||||||
PathfinderDemoRenderer.pushOpenSVGEvent(SVG_RESOURCE_PATH + textView.getText());
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,96 +0,0 @@
|
||||||
package graphics.pathfinder.pathfinderdemo;
|
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.google.vr.sdk.base.Eye;
|
|
||||||
import com.google.vr.sdk.base.GvrView;
|
|
||||||
import com.google.vr.sdk.base.HeadTransform;
|
|
||||||
import com.google.vr.sdk.base.Viewport;
|
|
||||||
import javax.microedition.khronos.egl.EGLConfig;
|
|
||||||
|
|
||||||
public class PathfinderDemoRenderer extends Object implements GvrView.Renderer {
|
|
||||||
private final PathfinderDemoActivity mActivity;
|
|
||||||
private boolean mInitialized;
|
|
||||||
private boolean mInVRMode;
|
|
||||||
|
|
||||||
private static native void init(PathfinderDemoActivity activity,
|
|
||||||
PathfinderDemoResourceLoader resourceLoader,
|
|
||||||
int width,
|
|
||||||
int height);
|
|
||||||
|
|
||||||
private static native int prepareFrame();
|
|
||||||
|
|
||||||
private static native void drawScene();
|
|
||||||
|
|
||||||
private static native void finishDrawingFrame();
|
|
||||||
|
|
||||||
public static native void pushWindowResizedEvent(int width, int height);
|
|
||||||
|
|
||||||
public static native void pushMouseDownEvent(int x, int y);
|
|
||||||
|
|
||||||
public static native void pushMouseDraggedEvent(int x, int y);
|
|
||||||
|
|
||||||
public static native void pushZoomEvent(float scale, int centerX, int centerY);
|
|
||||||
|
|
||||||
public static native void pushLookEvent(float pitch, float yaw);
|
|
||||||
|
|
||||||
public static native void pushOpenSVGEvent(String path);
|
|
||||||
|
|
||||||
static {
|
|
||||||
System.loadLibrary("pathfinder_android_demo");
|
|
||||||
}
|
|
||||||
|
|
||||||
PathfinderDemoRenderer(PathfinderDemoActivity activity) {
|
|
||||||
super();
|
|
||||||
mActivity = activity;
|
|
||||||
mInitialized = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDrawFrame(HeadTransform headTransform, Eye leftEye, Eye rightEye) {
|
|
||||||
final boolean inVR = prepareFrame() > 1;
|
|
||||||
if (inVR != mInVRMode) {
|
|
||||||
mInVRMode = inVR;
|
|
||||||
try {
|
|
||||||
mActivity.setVrModeEnabled(mInVRMode, mActivity.mVRListenerComponentName);
|
|
||||||
mActivity.setVRMode(inVR);
|
|
||||||
} catch (PackageManager.NameNotFoundException exception) {
|
|
||||||
throw new RuntimeException(exception);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
drawScene();
|
|
||||||
finishDrawingFrame();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFinishFrame(Viewport viewport) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSurfaceChanged(int width, int height) {
|
|
||||||
if (!mInitialized) {
|
|
||||||
init(mActivity,
|
|
||||||
new PathfinderDemoResourceLoader(mActivity.getAssets()),
|
|
||||||
width,
|
|
||||||
height);
|
|
||||||
mInitialized = true;
|
|
||||||
} else {
|
|
||||||
pushWindowResizedEvent(width, height);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSurfaceCreated(EGLConfig config) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRendererShutdown() {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
package graphics.pathfinder.pathfinderdemo;
|
|
||||||
|
|
||||||
import android.content.res.AssetManager;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
|
|
||||||
public class PathfinderDemoResourceLoader {
|
|
||||||
private AssetManager m_assetManager;
|
|
||||||
|
|
||||||
PathfinderDemoResourceLoader(AssetManager assetManager) {
|
|
||||||
m_assetManager = assetManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteBuffer slurp(String path) {
|
|
||||||
try {
|
|
||||||
InputStream inputStream = m_assetManager.open("resources/" + path);
|
|
||||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
|
||||||
|
|
||||||
byte[] buffer = new byte[4096];
|
|
||||||
while (true) {
|
|
||||||
int nRead = inputStream.read(buffer, 0, buffer.length);
|
|
||||||
if (nRead == -1)
|
|
||||||
break;
|
|
||||||
outputStream.write(buffer, 0, nRead);
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] outputBytes = outputStream.toByteArray();
|
|
||||||
ByteBuffer resultBuffer = ByteBuffer.allocateDirect(outputStream.size());
|
|
||||||
resultBuffer.put(outputBytes);
|
|
||||||
return resultBuffer;
|
|
||||||
} catch (IOException exception) {
|
|
||||||
Log.e("Pathfinder", "Resource not found: " + path);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
package graphics.pathfinder.pathfinderdemo;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import com.google.vr.sdk.base.GvrView;
|
|
||||||
|
|
||||||
public class PathfinderDemoSurfaceView extends GvrView {
|
|
||||||
public PathfinderDemoSurfaceView(Context context) {
|
|
||||||
super(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public PathfinderDemoSurfaceView(Context context, AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
package graphics.pathfinder.pathfinderdemo;
|
|
||||||
|
|
||||||
import android.os.Build;
|
|
||||||
import android.service.vr.VrListenerService;
|
|
||||||
import android.support.annotation.RequiresApi;
|
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
|
||||||
public class PathfinderDemoVRListenerService extends VrListenerService {
|
|
||||||
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
../../../../../../../target/aarch64-linux-android/release/libpathfinder_android_demo.so
|
|
|
@ -1,34 +0,0 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:aapt="http://schemas.android.com/aapt"
|
|
||||||
android:width="108dp"
|
|
||||||
android:height="108dp"
|
|
||||||
android:viewportHeight="108"
|
|
||||||
android:viewportWidth="108">
|
|
||||||
<path
|
|
||||||
android:fillType="evenOdd"
|
|
||||||
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
|
|
||||||
android:strokeColor="#00000000"
|
|
||||||
android:strokeWidth="1">
|
|
||||||
<aapt:attr name="android:fillColor">
|
|
||||||
<gradient
|
|
||||||
android:endX="78.5885"
|
|
||||||
android:endY="90.9159"
|
|
||||||
android:startX="48.7653"
|
|
||||||
android:startY="61.0927"
|
|
||||||
android:type="linear">
|
|
||||||
<item
|
|
||||||
android:color="#44000000"
|
|
||||||
android:offset="0.0" />
|
|
||||||
<item
|
|
||||||
android:color="#00000000"
|
|
||||||
android:offset="1.0" />
|
|
||||||
</gradient>
|
|
||||||
</aapt:attr>
|
|
||||||
</path>
|
|
||||||
<path
|
|
||||||
android:fillColor="#FFFFFF"
|
|
||||||
android:fillType="nonZero"
|
|
||||||
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
|
|
||||||
android:strokeColor="#00000000"
|
|
||||||
android:strokeWidth="1" />
|
|
||||||
</vector>
|
|
|
@ -1,170 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="108dp"
|
|
||||||
android:height="108dp"
|
|
||||||
android:viewportHeight="108"
|
|
||||||
android:viewportWidth="108">
|
|
||||||
<path
|
|
||||||
android:fillColor="#26A69A"
|
|
||||||
android:pathData="M0,0h108v108h-108z" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M9,0L9,108"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M19,0L19,108"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M29,0L29,108"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M39,0L39,108"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M49,0L49,108"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M59,0L59,108"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M69,0L69,108"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M79,0L79,108"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M89,0L89,108"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M99,0L99,108"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,9L108,9"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,19L108,19"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,29L108,29"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,39L108,39"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,49L108,49"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,59L108,59"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,69L108,69"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,79L108,79"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,89L108,89"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M0,99L108,99"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M19,29L89,29"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M19,39L89,39"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M19,49L89,49"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M19,59L89,59"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M19,69L89,69"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M19,79L89,79"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M29,19L29,89"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M39,19L39,89"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M49,19L49,89"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M59,19L59,89"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M69,19L69,89"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
<path
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:pathData="M79,19L79,89"
|
|
||||||
android:strokeColor="#33FFFFFF"
|
|
||||||
android:strokeWidth="0.8" />
|
|
||||||
</vector>
|
|
|
@ -1,38 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:background="#0099cc"
|
|
||||||
tools:context=".PathfinderDemoActivity">
|
|
||||||
|
|
||||||
<!-- The primary full-screen view. This can be replaced with whatever view
|
|
||||||
is needed to present your content, e.g. VideoView, SurfaceView,
|
|
||||||
TextureView, etc. -->
|
|
||||||
<graphics.pathfinder.pathfinderdemo.PathfinderDemoSurfaceView
|
|
||||||
android:id="@+id/fullscreen_content"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:keepScreenOn="true" />
|
|
||||||
|
|
||||||
<!-- This FrameLayout insets its children based on system windows using
|
|
||||||
android:fitsSystemWindows. -->
|
|
||||||
<FrameLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:fitsSystemWindows="true">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/fullscreen_content_controls"
|
|
||||||
style="?metaButtonBarStyle"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="bottom|center_horizontal"
|
|
||||||
android:background="@color/black_overlay"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
tools:ignore="UselessParent">
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
</FrameLayout>
|
|
||||||
|
|
||||||
</FrameLayout>
|
|
|
@ -1,18 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:orientation="vertical"
|
|
||||||
tools:context=".PathfinderDemoFileBrowserActivity">
|
|
||||||
|
|
||||||
<ListView
|
|
||||||
android:id="@+id/fileBrowserBrowser"
|
|
||||||
style="@android:style/Widget.Material.ListView"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
</ListView>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
|
@ -1,9 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:lineSpacingExtra="14sp"
|
|
||||||
android:textAppearance="@android:style/TextAppearance"
|
|
||||||
android:textSize="18sp">
|
|
||||||
|
|
||||||
</TextView>
|
|
|
@ -1,5 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<background android:drawable="@drawable/ic_launcher_background" />
|
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
|
||||||
</adaptive-icon>
|
|
|
@ -1,5 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<background android:drawable="@drawable/ic_launcher_background" />
|
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
|
||||||
</adaptive-icon>
|
|
Before Width: | Height: | Size: 3 KiB |
Before Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 6.9 KiB |
Before Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 9 KiB |
Before Width: | Height: | Size: 15 KiB |
|
@ -1,12 +0,0 @@
|
||||||
<resources>
|
|
||||||
|
|
||||||
<!-- Declare custom theme attributes that allow changing which styles are
|
|
||||||
used for button bars depending on the API level.
|
|
||||||
?android:attr/buttonBarStyle is new as of API 11 so this is
|
|
||||||
necessary to support previous API levels. -->
|
|
||||||
<declare-styleable name="ButtonBarContainerTheme">
|
|
||||||
<attr name="metaButtonBarStyle" format="reference" />
|
|
||||||
<attr name="metaButtonBarButtonStyle" format="reference" />
|
|
||||||
</declare-styleable>
|
|
||||||
|
|
||||||
</resources>
|
|
|
@ -1,8 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
<color name="colorPrimary">#3F51B5</color>
|
|
||||||
<color name="colorPrimaryDark">#303F9F</color>
|
|
||||||
<color name="colorAccent">#FF4081</color>
|
|
||||||
|
|
||||||
<color name="black_overlay">#66000000</color>
|
|
||||||
</resources>
|
|
|
@ -1,9 +0,0 @@
|
||||||
<resources>
|
|
||||||
<string name="app_name">Pathfinder Demo</string>
|
|
||||||
|
|
||||||
<string name="dummy_button">Dummy Button</string>
|
|
||||||
<string name="dummy_content">DUMMY\nCONTENT</string>
|
|
||||||
<string name="service_name">PathfinderVRListenerService</string>
|
|
||||||
<string name="title_activity_pathfinder_demo_file_browser">PathfinderDemoFileBrowserActivity
|
|
||||||
</string>
|
|
||||||
</resources>
|
|
|
@ -1,23 +0,0 @@
|
||||||
<resources>
|
|
||||||
|
|
||||||
<!-- Base application theme. -->
|
|
||||||
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
|
||||||
<!-- Customize your theme here. -->
|
|
||||||
<item name="colorPrimary">@color/colorPrimary</item>
|
|
||||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
|
||||||
<item name="colorAccent">@color/colorAccent</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="FullscreenTheme" parent="AppTheme">
|
|
||||||
<item name="android:actionBarStyle">@style/FullscreenActionBarStyle</item>
|
|
||||||
<item name="android:windowActionBarOverlay">true</item>
|
|
||||||
<item name="android:windowBackground">@null</item>
|
|
||||||
<item name="metaButtonBarStyle">?android:attr/buttonBarStyle</item>
|
|
||||||
<item name="metaButtonBarButtonStyle">?android:attr/buttonBarButtonStyle</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="FullscreenActionBarStyle" parent="Widget.AppCompat.ActionBar">
|
|
||||||
<item name="android:background">@color/black_overlay</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
</resources>
|
|
|
@ -1,27 +0,0 @@
|
||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
|
||||||
|
|
||||||
buildscript {
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
google()
|
|
||||||
jcenter()
|
|
||||||
}
|
|
||||||
dependencies {
|
|
||||||
classpath 'com.android.tools.build:gradle:3.1.2'
|
|
||||||
|
|
||||||
|
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
|
||||||
// in the individual module build.gradle files
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
allprojects {
|
|
||||||
repositories {
|
|
||||||
google()
|
|
||||||
jcenter()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
task clean(type: Delete) {
|
|
||||||
delete rootProject.buildDir
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
# Project-wide Gradle settings.
|
|
||||||
# IDE (e.g. Android Studio) users:
|
|
||||||
# Gradle settings configured through the IDE *will override*
|
|
||||||
# any settings specified in this file.
|
|
||||||
# For more details on how to configure your build environment visit
|
|
||||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
|
||||||
# Specifies the JVM arguments used for the daemon process.
|
|
||||||
# The setting is particularly useful for tweaking memory settings.
|
|
||||||
org.gradle.jvmargs=-Xmx1536m
|
|
||||||
# When configured, Gradle will run in incubating parallel mode.
|
|
||||||
# This option should only be used with decoupled projects. More details, visit
|
|
||||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
|
||||||
# org.gradle.parallel=true
|
|
||||||
android.useDeprecatedNdk=true
|
|
|
@ -1,6 +0,0 @@
|
||||||
#Fri Mar 08 12:48:37 PST 2019
|
|
||||||
distributionBase=GRADLE_USER_HOME
|
|
||||||
distributionPath=wrapper/dists
|
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
|
||||||
zipStorePath=wrapper/dists
|
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
|
|
172
crates/pathfinder/demo/android/gradlew
vendored
|
@ -1,172 +0,0 @@
|
||||||
#!/usr/bin/env sh
|
|
||||||
|
|
||||||
##############################################################################
|
|
||||||
##
|
|
||||||
## Gradle start up script for UN*X
|
|
||||||
##
|
|
||||||
##############################################################################
|
|
||||||
|
|
||||||
# Attempt to set APP_HOME
|
|
||||||
# Resolve links: $0 may be a link
|
|
||||||
PRG="$0"
|
|
||||||
# Need this for relative symlinks.
|
|
||||||
while [ -h "$PRG" ] ; do
|
|
||||||
ls=`ls -ld "$PRG"`
|
|
||||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
|
||||||
if expr "$link" : '/.*' > /dev/null; then
|
|
||||||
PRG="$link"
|
|
||||||
else
|
|
||||||
PRG=`dirname "$PRG"`"/$link"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
SAVED="`pwd`"
|
|
||||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
|
||||||
APP_HOME="`pwd -P`"
|
|
||||||
cd "$SAVED" >/dev/null
|
|
||||||
|
|
||||||
APP_NAME="Gradle"
|
|
||||||
APP_BASE_NAME=`basename "$0"`
|
|
||||||
|
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
|
||||||
DEFAULT_JVM_OPTS=""
|
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
|
||||||
MAX_FD="maximum"
|
|
||||||
|
|
||||||
warn () {
|
|
||||||
echo "$*"
|
|
||||||
}
|
|
||||||
|
|
||||||
die () {
|
|
||||||
echo
|
|
||||||
echo "$*"
|
|
||||||
echo
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# OS specific support (must be 'true' or 'false').
|
|
||||||
cygwin=false
|
|
||||||
msys=false
|
|
||||||
darwin=false
|
|
||||||
nonstop=false
|
|
||||||
case "`uname`" in
|
|
||||||
CYGWIN* )
|
|
||||||
cygwin=true
|
|
||||||
;;
|
|
||||||
Darwin* )
|
|
||||||
darwin=true
|
|
||||||
;;
|
|
||||||
MINGW* )
|
|
||||||
msys=true
|
|
||||||
;;
|
|
||||||
NONSTOP* )
|
|
||||||
nonstop=true
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
|
||||||
|
|
||||||
# Determine the Java command to use to start the JVM.
|
|
||||||
if [ -n "$JAVA_HOME" ] ; then
|
|
||||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
|
||||||
# IBM's JDK on AIX uses strange locations for the executables
|
|
||||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
|
||||||
else
|
|
||||||
JAVACMD="$JAVA_HOME/bin/java"
|
|
||||||
fi
|
|
||||||
if [ ! -x "$JAVACMD" ] ; then
|
|
||||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
|
||||||
|
|
||||||
Please set the JAVA_HOME variable in your environment to match the
|
|
||||||
location of your Java installation."
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
JAVACMD="java"
|
|
||||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
|
||||||
|
|
||||||
Please set the JAVA_HOME variable in your environment to match the
|
|
||||||
location of your Java installation."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Increase the maximum file descriptors if we can.
|
|
||||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
|
||||||
MAX_FD_LIMIT=`ulimit -H -n`
|
|
||||||
if [ $? -eq 0 ] ; then
|
|
||||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
|
||||||
MAX_FD="$MAX_FD_LIMIT"
|
|
||||||
fi
|
|
||||||
ulimit -n $MAX_FD
|
|
||||||
if [ $? -ne 0 ] ; then
|
|
||||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# For Darwin, add options to specify how the application appears in the dock
|
|
||||||
if $darwin; then
|
|
||||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
|
||||||
fi
|
|
||||||
|
|
||||||
# For Cygwin, switch paths to Windows format before running java
|
|
||||||
if $cygwin ; then
|
|
||||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
|
||||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
|
||||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
|
||||||
|
|
||||||
# We build the pattern for arguments to be converted via cygpath
|
|
||||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
|
||||||
SEP=""
|
|
||||||
for dir in $ROOTDIRSRAW ; do
|
|
||||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
|
||||||
SEP="|"
|
|
||||||
done
|
|
||||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
|
||||||
# Add a user-defined pattern to the cygpath arguments
|
|
||||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
|
||||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
|
||||||
fi
|
|
||||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
|
||||||
i=0
|
|
||||||
for arg in "$@" ; do
|
|
||||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
|
||||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
|
||||||
|
|
||||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
|
||||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
|
||||||
else
|
|
||||||
eval `echo args$i`="\"$arg\""
|
|
||||||
fi
|
|
||||||
i=$((i+1))
|
|
||||||
done
|
|
||||||
case $i in
|
|
||||||
(0) set -- ;;
|
|
||||||
(1) set -- "$args0" ;;
|
|
||||||
(2) set -- "$args0" "$args1" ;;
|
|
||||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
|
||||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
|
||||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
|
||||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
|
||||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
|
||||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
|
||||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
|
||||||
esac
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Escape application args
|
|
||||||
save () {
|
|
||||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
|
||||||
echo " "
|
|
||||||
}
|
|
||||||
APP_ARGS=$(save "$@")
|
|
||||||
|
|
||||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
|
||||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
|
||||||
|
|
||||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
|
||||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
|
||||||
cd "$(dirname "$0")"
|
|
||||||
fi
|
|
||||||
|
|
||||||
exec "$JAVACMD" "$@"
|
|
84
crates/pathfinder/demo/android/gradlew.bat
vendored
|
@ -1,84 +0,0 @@
|
||||||
@if "%DEBUG%" == "" @echo off
|
|
||||||
@rem ##########################################################################
|
|
||||||
@rem
|
|
||||||
@rem Gradle startup script for Windows
|
|
||||||
@rem
|
|
||||||
@rem ##########################################################################
|
|
||||||
|
|
||||||
@rem Set local scope for the variables with windows NT shell
|
|
||||||
if "%OS%"=="Windows_NT" setlocal
|
|
||||||
|
|
||||||
set DIRNAME=%~dp0
|
|
||||||
if "%DIRNAME%" == "" set DIRNAME=.
|
|
||||||
set APP_BASE_NAME=%~n0
|
|
||||||
set APP_HOME=%DIRNAME%
|
|
||||||
|
|
||||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
|
||||||
set DEFAULT_JVM_OPTS=
|
|
||||||
|
|
||||||
@rem Find java.exe
|
|
||||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
|
||||||
|
|
||||||
set JAVA_EXE=java.exe
|
|
||||||
%JAVA_EXE% -version >NUL 2>&1
|
|
||||||
if "%ERRORLEVEL%" == "0" goto init
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
|
||||||
echo.
|
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
|
||||||
echo location of your Java installation.
|
|
||||||
|
|
||||||
goto fail
|
|
||||||
|
|
||||||
:findJavaFromJavaHome
|
|
||||||
set JAVA_HOME=%JAVA_HOME:"=%
|
|
||||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
|
||||||
|
|
||||||
if exist "%JAVA_EXE%" goto init
|
|
||||||
|
|
||||||
echo.
|
|
||||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
|
||||||
echo.
|
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
|
||||||
echo location of your Java installation.
|
|
||||||
|
|
||||||
goto fail
|
|
||||||
|
|
||||||
:init
|
|
||||||
@rem Get command-line arguments, handling Windows variants
|
|
||||||
|
|
||||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
|
||||||
|
|
||||||
:win9xME_args
|
|
||||||
@rem Slurp the command line arguments.
|
|
||||||
set CMD_LINE_ARGS=
|
|
||||||
set _SKIP=2
|
|
||||||
|
|
||||||
:win9xME_args_slurp
|
|
||||||
if "x%~1" == "x" goto execute
|
|
||||||
|
|
||||||
set CMD_LINE_ARGS=%*
|
|
||||||
|
|
||||||
:execute
|
|
||||||
@rem Setup the command line
|
|
||||||
|
|
||||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
|
||||||
|
|
||||||
@rem Execute Gradle
|
|
||||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
|
||||||
|
|
||||||
:end
|
|
||||||
@rem End local scope for the variables with windows NT shell
|
|
||||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
|
||||||
|
|
||||||
:fail
|
|
||||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
|
||||||
rem the _cmd.exe /c_ return code!
|
|
||||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
|
||||||
exit /b 1
|
|
||||||
|
|
||||||
:mainEnd
|
|
||||||
if "%OS%"=="Windows_NT" endlocal
|
|
||||||
|
|
||||||
:omega
|
|
|
@ -1,26 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "pathfinder_android_demo"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["Patrick Walton <pcwalton@mimiga.net>"]
|
|
||||||
edition = "2018"
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
crate_type = ["cdylib"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
egl = "0.2"
|
|
||||||
gl = "0.14"
|
|
||||||
jni = "0.15"
|
|
||||||
lazy_static = "1.3"
|
|
||||||
|
|
||||||
[dependencies.pathfinder_demo]
|
|
||||||
path = "../../common"
|
|
||||||
|
|
||||||
[dependencies.pathfinder_geometry]
|
|
||||||
path = "../../../geometry"
|
|
||||||
|
|
||||||
[dependencies.pathfinder_gl]
|
|
||||||
path = "../../../gl"
|
|
||||||
|
|
||||||
[dependencies.pathfinder_gpu]
|
|
||||||
path = "../../../gpu"
|
|
|
@ -1,294 +0,0 @@
|
||||||
// pathfinder/demo/android/rust/src/main.rs
|
|
||||||
//
|
|
||||||
// Copyright © 2019 The Pathfinder Project Developers.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate lazy_static;
|
|
||||||
|
|
||||||
use jni::objects::{GlobalRef, JByteBuffer, JClass, JObject, JString, JValue};
|
|
||||||
use jni::{JNIEnv, JavaVM};
|
|
||||||
use pathfinder_demo::window::{Event, SVGPath, View, Window, WindowSize};
|
|
||||||
use pathfinder_demo::DemoApp;
|
|
||||||
use pathfinder_demo::Options;
|
|
||||||
use pathfinder_geometry::vector::{Vector2I, vec2i};
|
|
||||||
use pathfinder_geometry::rect::RectI;
|
|
||||||
use pathfinder_gl::GLVersion;
|
|
||||||
use pathfinder_resources::ResourceLoader;
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::io::Error as IOError;
|
|
||||||
use std::mem;
|
|
||||||
use std::os::raw::c_void;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::sync::Mutex;
|
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
static ref EVENT_QUEUE: Mutex<Vec<Event>> = Mutex::new(vec![]);
|
|
||||||
}
|
|
||||||
|
|
||||||
thread_local! {
|
|
||||||
static DEMO_APP: RefCell<Option<DemoApp<WindowImpl>>> = RefCell::new(None);
|
|
||||||
static JAVA_ACTIVITY: RefCell<Option<JavaActivity>> = RefCell::new(None);
|
|
||||||
static JAVA_RESOURCE_LOADER: RefCell<Option<JavaResourceLoader>> = RefCell::new(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
static RESOURCE_LOADER: AndroidResourceLoader = AndroidResourceLoader;
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "system" fn Java_graphics_pathfinder_pathfinderdemo_PathfinderDemoRenderer_init(
|
|
||||||
env: JNIEnv,
|
|
||||||
class: JClass,
|
|
||||||
activity: JObject,
|
|
||||||
loader: JObject,
|
|
||||||
width: i32,
|
|
||||||
height: i32,
|
|
||||||
) {
|
|
||||||
let logical_size = vec2i(width, height);
|
|
||||||
let window_size = WindowSize {
|
|
||||||
logical_size,
|
|
||||||
backing_scale_factor: 1.0,
|
|
||||||
};
|
|
||||||
let window = WindowImpl { size: logical_size };
|
|
||||||
let options = Options::default();
|
|
||||||
|
|
||||||
JAVA_ACTIVITY.with(|java_activity| {
|
|
||||||
*java_activity.borrow_mut() = Some(JavaActivity::new(env.clone(), activity));
|
|
||||||
});
|
|
||||||
JAVA_RESOURCE_LOADER.with(|java_resource_loader| {
|
|
||||||
*java_resource_loader.borrow_mut() = Some(JavaResourceLoader::new(env, loader));
|
|
||||||
});
|
|
||||||
DEMO_APP.with(|demo_app| {
|
|
||||||
gl::load_with(|name| egl::get_proc_address(name) as *const c_void);
|
|
||||||
*demo_app.borrow_mut() = Some(DemoApp::new(window, window_size, options));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "system" fn Java_graphics_pathfinder_pathfinderdemo_PathfinderDemoRenderer_prepareFrame(
|
|
||||||
env: JNIEnv,
|
|
||||||
class: JClass,
|
|
||||||
) -> i32 {
|
|
||||||
DEMO_APP.with(|demo_app| {
|
|
||||||
let mut event_queue = EVENT_QUEUE.lock().unwrap();
|
|
||||||
match *demo_app.borrow_mut() {
|
|
||||||
Some(ref mut demo_app) => {
|
|
||||||
demo_app.prepare_frame(mem::replace(&mut *event_queue, vec![])) as i32
|
|
||||||
}
|
|
||||||
None => 0,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "system" fn Java_graphics_pathfinder_pathfinderdemo_PathfinderDemoRenderer_drawScene(
|
|
||||||
env: JNIEnv,
|
|
||||||
class: JClass,
|
|
||||||
) {
|
|
||||||
DEMO_APP.with(|demo_app| {
|
|
||||||
if let Some(ref mut demo_app) = *demo_app.borrow_mut() {
|
|
||||||
demo_app.draw_scene()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "system" fn Java_graphics_pathfinder_pathfinderdemo_PathfinderDemoRenderer_finishDrawingFrame(
|
|
||||||
env: JNIEnv,
|
|
||||||
class: JClass,
|
|
||||||
) {
|
|
||||||
DEMO_APP.with(|demo_app| {
|
|
||||||
if let Some(ref mut demo_app) = *demo_app.borrow_mut() {
|
|
||||||
demo_app.finish_drawing_frame()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "system" fn Java_graphics_pathfinder_pathfinderdemo_PathfinderDemoRenderer_pushWindowResizedEvent(
|
|
||||||
env: JNIEnv,
|
|
||||||
class: JClass,
|
|
||||||
width: i32,
|
|
||||||
height: i32,
|
|
||||||
) {
|
|
||||||
EVENT_QUEUE
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.push(Event::WindowResized(WindowSize {
|
|
||||||
logical_size: vec2i(width, height),
|
|
||||||
backing_scale_factor: 1.0,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "system" fn Java_graphics_pathfinder_pathfinderdemo_PathfinderDemoRenderer_pushMouseDownEvent(
|
|
||||||
_: JNIEnv,
|
|
||||||
_: JClass,
|
|
||||||
x: i32,
|
|
||||||
y: i32,
|
|
||||||
) {
|
|
||||||
EVENT_QUEUE.lock().unwrap().push(Event::MouseDown(vec2i(x, y)))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "system" fn Java_graphics_pathfinder_pathfinderdemo_PathfinderDemoRenderer_pushMouseDraggedEvent(
|
|
||||||
_: JNIEnv,
|
|
||||||
_: JClass,
|
|
||||||
x: i32,
|
|
||||||
y: i32,
|
|
||||||
) {
|
|
||||||
EVENT_QUEUE.lock().unwrap().push(Event::MouseDragged(vec2i(x, y)))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "system" fn Java_graphics_pathfinder_pathfinderdemo_PathfinderDemoRenderer_pushZoomEvent(
|
|
||||||
_: JNIEnv,
|
|
||||||
_: JClass,
|
|
||||||
factor: f32,
|
|
||||||
center_x: i32,
|
|
||||||
center_y: i32,
|
|
||||||
) {
|
|
||||||
EVENT_QUEUE.lock().unwrap().push(Event::Zoom(factor, vec2i(center_x, center_y)))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "system" fn Java_graphics_pathfinder_pathfinderdemo_PathfinderDemoRenderer_pushLookEvent(
|
|
||||||
_: JNIEnv,
|
|
||||||
_: JClass,
|
|
||||||
pitch: f32,
|
|
||||||
yaw: f32,
|
|
||||||
) {
|
|
||||||
EVENT_QUEUE.lock().unwrap().push(Event::Look { pitch, yaw })
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "system" fn Java_graphics_pathfinder_pathfinderdemo_PathfinderDemoRenderer_pushOpenSVGEvent(
|
|
||||||
env: JNIEnv,
|
|
||||||
_: JClass,
|
|
||||||
string: JObject,
|
|
||||||
) {
|
|
||||||
let string: String = env.get_string(JString::from(string)).unwrap().into();
|
|
||||||
EVENT_QUEUE
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.push(Event::OpenSVG(SVGPath::Resource(string)))
|
|
||||||
}
|
|
||||||
|
|
||||||
struct WindowImpl {
|
|
||||||
size: Vector2I,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Window for WindowImpl {
|
|
||||||
fn gl_version(&self) -> GLVersion {
|
|
||||||
GLVersion::GLES3
|
|
||||||
}
|
|
||||||
|
|
||||||
fn viewport(&self, view: View) -> RectI {
|
|
||||||
let mut width = self.size.x();
|
|
||||||
let mut offset_x = 0;
|
|
||||||
let height = self.size.y();
|
|
||||||
if let View::Stereo(index) = view {
|
|
||||||
width = width / 2;
|
|
||||||
offset_x = (index as i32) * width;
|
|
||||||
}
|
|
||||||
let size = vec2i(width, height);
|
|
||||||
let offset = vec2i(offset_x, 0);
|
|
||||||
RectI::new(offset, size)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_current(&mut self, _view: View) {}
|
|
||||||
|
|
||||||
fn present(&mut self) {}
|
|
||||||
|
|
||||||
fn resource_loader(&self) -> &dyn ResourceLoader {
|
|
||||||
&RESOURCE_LOADER
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_user_event_id(&self) -> u32 {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
|
|
||||||
fn push_user_event(message_type: u32, message_data: u32) {}
|
|
||||||
|
|
||||||
fn present_open_svg_dialog(&mut self) {
|
|
||||||
JAVA_ACTIVITY.with(|java_activity| {
|
|
||||||
let mut java_activity = java_activity.borrow_mut();
|
|
||||||
let java_activity = java_activity.as_mut().unwrap();
|
|
||||||
let env = java_activity.vm.get_env().unwrap();
|
|
||||||
env.call_method(
|
|
||||||
java_activity.activity.as_obj(),
|
|
||||||
"presentOpenSVGDialog",
|
|
||||||
"()V",
|
|
||||||
&[],
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_save_dialog(&self, extension: &str) -> Result<PathBuf, ()> {
|
|
||||||
// TODO(pcwalton)
|
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct AndroidResourceLoader;
|
|
||||||
|
|
||||||
impl ResourceLoader for AndroidResourceLoader {
|
|
||||||
fn slurp(&self, path: &str) -> Result<Vec<u8>, IOError> {
|
|
||||||
JAVA_RESOURCE_LOADER.with(|java_resource_loader| {
|
|
||||||
let java_resource_loader = java_resource_loader.borrow();
|
|
||||||
let java_resource_loader = java_resource_loader.as_ref().unwrap();
|
|
||||||
let loader = java_resource_loader.loader.as_obj();
|
|
||||||
let env = java_resource_loader.vm.get_env().unwrap();
|
|
||||||
match env
|
|
||||||
.call_method(
|
|
||||||
loader,
|
|
||||||
"slurp",
|
|
||||||
"(Ljava/lang/String;)Ljava/nio/ByteBuffer;",
|
|
||||||
&[JValue::Object(*env.new_string(path).unwrap())],
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
{
|
|
||||||
JValue::Object(object) => {
|
|
||||||
let byte_buffer = JByteBuffer::from(object);
|
|
||||||
Ok(Vec::from(
|
|
||||||
env.get_direct_buffer_address(byte_buffer).unwrap(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
_ => panic!("Unexpected return value!"),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct JavaActivity {
|
|
||||||
activity: GlobalRef,
|
|
||||||
vm: JavaVM,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl JavaActivity {
|
|
||||||
fn new(env: JNIEnv, activity: JObject) -> JavaActivity {
|
|
||||||
JavaActivity {
|
|
||||||
activity: env.new_global_ref(activity).unwrap(),
|
|
||||||
vm: env.get_java_vm().unwrap(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct JavaResourceLoader {
|
|
||||||
loader: GlobalRef,
|
|
||||||
vm: JavaVM,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl JavaResourceLoader {
|
|
||||||
fn new(env: JNIEnv, loader: JObject) -> JavaResourceLoader {
|
|
||||||
JavaResourceLoader {
|
|
||||||
loader: env.new_global_ref(loader).unwrap(),
|
|
||||||
vm: env.get_java_vm().unwrap(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
include ':app'
|
|
|
@ -1,61 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "pathfinder_demo"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2018"
|
|
||||||
authors = ["Patrick Walton <pcwalton@mimiga.net>"]
|
|
||||||
|
|
||||||
[features]
|
|
||||||
pf-gl = []
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
clap = "2.32"
|
|
||||||
gl = "0.14"
|
|
||||||
rayon = "1.0"
|
|
||||||
usvg = "0.9"
|
|
||||||
|
|
||||||
[dependencies.image]
|
|
||||||
version = "0.23"
|
|
||||||
default-features = false
|
|
||||||
features = ["png"]
|
|
||||||
|
|
||||||
[dependencies.log]
|
|
||||||
version = "0.4"
|
|
||||||
|
|
||||||
[dependencies.pathfinder_color]
|
|
||||||
path = "../../color"
|
|
||||||
|
|
||||||
[dependencies.pathfinder_content]
|
|
||||||
path = "../../content"
|
|
||||||
|
|
||||||
[dependencies.pathfinder_export]
|
|
||||||
path = "../../export"
|
|
||||||
|
|
||||||
[dependencies.pathfinder_geometry]
|
|
||||||
path = "../../geometry"
|
|
||||||
|
|
||||||
[dependencies.pathfinder_gl]
|
|
||||||
path = "../../gl"
|
|
||||||
|
|
||||||
[dependencies.pathfinder_gpu]
|
|
||||||
path = "../../gpu"
|
|
||||||
|
|
||||||
[dependencies.pathfinder_renderer]
|
|
||||||
path = "../../renderer"
|
|
||||||
|
|
||||||
[dependencies.pathfinder_resources]
|
|
||||||
path = "../../resources"
|
|
||||||
|
|
||||||
[dependencies.pathfinder_simd]
|
|
||||||
path = "../../simd"
|
|
||||||
|
|
||||||
[dependencies.pathfinder_svg]
|
|
||||||
path = "../../svg"
|
|
||||||
|
|
||||||
[dependencies.pathfinder_ui]
|
|
||||||
path = "../../ui"
|
|
||||||
|
|
||||||
[target.'cfg(target_os = "macos")'.dependencies]
|
|
||||||
metal = "0.17"
|
|
||||||
|
|
||||||
[target.'cfg(target_os = "macos")'.dependencies.pathfinder_metal]
|
|
||||||
path = "../../metal"
|
|
|
@ -1,188 +0,0 @@
|
||||||
// pathfinder/demo/common/src/camera.rs
|
|
||||||
//
|
|
||||||
// Copyright © 2019 The Pathfinder Project Developers.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
|
|
||||||
//! Camera management code for the demo.
|
|
||||||
|
|
||||||
// TODO(#140, pcwalton): Move some of this out of the demo and into the library
|
|
||||||
// proper.
|
|
||||||
|
|
||||||
use crate::window::{OcularTransform, View};
|
|
||||||
use pathfinder_geometry::vector::{Vector2I, Vector4F};
|
|
||||||
use pathfinder_geometry::rect::RectF;
|
|
||||||
use pathfinder_geometry::transform2d::Transform2F;
|
|
||||||
use pathfinder_geometry::transform3d::{Perspective, Transform4F};
|
|
||||||
use std::f32::consts::FRAC_PI_4;
|
|
||||||
|
|
||||||
const NEAR_CLIP_PLANE: f32 = 0.01;
|
|
||||||
const FAR_CLIP_PLANE: f32 = 10.0;
|
|
||||||
|
|
||||||
// Half of the eye separation distance.
|
|
||||||
const DEFAULT_EYE_OFFSET: f32 = 0.025;
|
|
||||||
|
|
||||||
pub enum Camera {
|
|
||||||
TwoD(Transform2F),
|
|
||||||
ThreeD {
|
|
||||||
// The ocular transform used for rendering of the scene to the scene framebuffer. If we are
|
|
||||||
// performing stereoscopic rendering, this is then reprojected according to the eye
|
|
||||||
// transforms below.
|
|
||||||
scene_transform: OcularTransform,
|
|
||||||
// For each eye, the perspective from camera coordinates to display coordinates,
|
|
||||||
// and the view transform from world coordinates to camera coordinates.
|
|
||||||
eye_transforms: Vec<OcularTransform>,
|
|
||||||
// The modelview transform from world coordinates to SVG coordinates
|
|
||||||
modelview_transform: CameraTransform3D,
|
|
||||||
// The camera's velocity (in world coordinates)
|
|
||||||
velocity: Vector4F,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Camera {
|
|
||||||
pub fn new(mode: Mode, view_box: RectF, viewport_size: Vector2I) -> Camera {
|
|
||||||
if mode == Mode::TwoD {
|
|
||||||
Camera::new_2d(view_box, viewport_size)
|
|
||||||
} else {
|
|
||||||
Camera::new_3d(mode, view_box, viewport_size)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_2d(view_box: RectF, viewport_size: Vector2I) -> Camera {
|
|
||||||
let scale = i32::min(viewport_size.x(), viewport_size.y()) as f32 *
|
|
||||||
scale_factor_for_view_box(view_box);
|
|
||||||
let origin = viewport_size.to_f32() * 0.5 - view_box.size() * (scale * 0.5);
|
|
||||||
Camera::TwoD(Transform2F::from_scale(scale).translate(origin))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_3d(mode: Mode, view_box: RectF, viewport_size: Vector2I) -> Camera {
|
|
||||||
let viewport_count = mode.viewport_count();
|
|
||||||
|
|
||||||
let fov_y = FRAC_PI_4;
|
|
||||||
let aspect = viewport_size.x() as f32 / viewport_size.y() as f32;
|
|
||||||
let projection =
|
|
||||||
Transform4F::from_perspective(fov_y, aspect, NEAR_CLIP_PLANE, FAR_CLIP_PLANE);
|
|
||||||
let perspective = Perspective::new(&projection, viewport_size);
|
|
||||||
|
|
||||||
// Create a scene transform by moving the camera back from the center of the eyes so that
|
|
||||||
// its field of view encompasses the field of view of both eyes.
|
|
||||||
let z_offset = Vector4F::new(0.0, 0.0, -DEFAULT_EYE_OFFSET * projection.c0.x(), 1.0);
|
|
||||||
let scene_transform = OcularTransform {
|
|
||||||
perspective,
|
|
||||||
modelview_to_eye: Transform4F::from_translation(z_offset),
|
|
||||||
};
|
|
||||||
|
|
||||||
// For now, initialize the eye transforms as copies of the scene transform.
|
|
||||||
let eye_offset = DEFAULT_EYE_OFFSET;
|
|
||||||
let eye_transforms = (0..viewport_count)
|
|
||||||
.map(|viewport_index| {
|
|
||||||
let this_eye_offset = if viewport_index == 0 {
|
|
||||||
eye_offset
|
|
||||||
} else {
|
|
||||||
-eye_offset
|
|
||||||
};
|
|
||||||
let this_eye_offset = Vector4F::new(this_eye_offset, 0.0, 0.0, 1.0);
|
|
||||||
OcularTransform {
|
|
||||||
perspective,
|
|
||||||
modelview_to_eye: Transform4F::from_translation(this_eye_offset),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
Camera::ThreeD {
|
|
||||||
scene_transform,
|
|
||||||
eye_transforms,
|
|
||||||
modelview_transform: CameraTransform3D::new(view_box),
|
|
||||||
velocity: Vector4F::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_3d(&self) -> bool {
|
|
||||||
match *self {
|
|
||||||
Camera::ThreeD { .. } => true,
|
|
||||||
Camera::TwoD { .. } => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn mode(&self) -> Mode {
|
|
||||||
match *self {
|
|
||||||
Camera::ThreeD {
|
|
||||||
ref eye_transforms, ..
|
|
||||||
} if eye_transforms.len() >= 2 => Mode::VR,
|
|
||||||
Camera::ThreeD { .. } => Mode::ThreeD,
|
|
||||||
Camera::TwoD { .. } => Mode::TwoD,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub struct CameraTransform3D {
|
|
||||||
position: Vector4F,
|
|
||||||
pub yaw: f32,
|
|
||||||
pub pitch: f32,
|
|
||||||
scale: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CameraTransform3D {
|
|
||||||
fn new(view_box: RectF) -> CameraTransform3D {
|
|
||||||
let scale = scale_factor_for_view_box(view_box);
|
|
||||||
CameraTransform3D {
|
|
||||||
position: Vector4F::new(
|
|
||||||
0.5 * view_box.max_x(),
|
|
||||||
-0.5 * view_box.max_y(),
|
|
||||||
1.5 / scale,
|
|
||||||
1.0,
|
|
||||||
),
|
|
||||||
yaw: 0.0,
|
|
||||||
pitch: 0.0,
|
|
||||||
scale,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn offset(&mut self, vector: Vector4F) -> bool {
|
|
||||||
let update = !vector.is_zero();
|
|
||||||
if update {
|
|
||||||
let rotation = Transform4F::from_rotation(-self.yaw, -self.pitch, 0.0);
|
|
||||||
self.position = self.position + rotation * vector;
|
|
||||||
}
|
|
||||||
update
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_transform(&self) -> Transform4F {
|
|
||||||
let flip = Vector4F::new(1.0, -1.0, 1.0, 1.0);
|
|
||||||
Transform4F::from_scale(flip).translate(-self.position)
|
|
||||||
.uniform_scale(2.0 * self.scale)
|
|
||||||
.rotate(self.yaw, self.pitch, 0.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq)]
|
|
||||||
pub enum Mode {
|
|
||||||
TwoD = 0,
|
|
||||||
ThreeD = 1,
|
|
||||||
VR = 2,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mode {
|
|
||||||
pub fn viewport_count(self) -> usize {
|
|
||||||
match self {
|
|
||||||
Mode::TwoD | Mode::ThreeD => 1,
|
|
||||||
Mode::VR => 2,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn view(self, viewport: u32) -> View {
|
|
||||||
match self {
|
|
||||||
Mode::TwoD | Mode::ThreeD => View::Mono,
|
|
||||||
Mode::VR => View::Stereo(viewport),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn scale_factor_for_view_box(view_box: RectF) -> f32 {
|
|
||||||
1.0 / f32::min(view_box.size().x(), view_box.size().y())
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
// pathfinder/demo/common/src/concurrent.rs
|
|
||||||
//
|
|
||||||
// Copyright © 2019 The Pathfinder Project Developers.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
|
|
||||||
//! Concurrency implementation for the demo.
|
|
||||||
|
|
||||||
use pathfinder_renderer::concurrent::executor::{Executor, SequentialExecutor};
|
|
||||||
use pathfinder_renderer::concurrent::rayon::RayonExecutor;
|
|
||||||
use rayon::ThreadPoolBuilder;
|
|
||||||
|
|
||||||
pub struct DemoExecutor {
|
|
||||||
sequential_mode: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DemoExecutor {
|
|
||||||
pub fn new(thread_count: Option<usize>) -> DemoExecutor {
|
|
||||||
let sequential_mode = thread_count == Some(1);
|
|
||||||
if !sequential_mode {
|
|
||||||
let mut thread_pool_builder = ThreadPoolBuilder::new();
|
|
||||||
if let Some(thread_count) = thread_count {
|
|
||||||
thread_pool_builder = thread_pool_builder.num_threads(thread_count);
|
|
||||||
}
|
|
||||||
thread_pool_builder.build_global().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
DemoExecutor { sequential_mode }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Executor for DemoExecutor {
|
|
||||||
fn build_vector<T, F>(&self, length: usize, builder: F) -> Vec<T>
|
|
||||||
where T: Send, F: Fn(usize) -> T + Send + Sync {
|
|
||||||
if self.sequential_mode {
|
|
||||||
SequentialExecutor.build_vector(length, builder)
|
|
||||||
} else {
|
|
||||||
RayonExecutor.build_vector(length, builder)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,82 +0,0 @@
|
||||||
// pathfinder/demo/common/src/device.rs
|
|
||||||
//
|
|
||||||
// Copyright © 2019 The Pathfinder Project Developers.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
|
|
||||||
//! GPU rendering code specifically for the demo.
|
|
||||||
|
|
||||||
use pathfinder_gpu::{BufferTarget, Device, VertexAttrClass, VertexAttrDescriptor, VertexAttrType};
|
|
||||||
use pathfinder_resources::ResourceLoader;
|
|
||||||
|
|
||||||
pub struct GroundProgram<D>
|
|
||||||
where
|
|
||||||
D: Device,
|
|
||||||
{
|
|
||||||
pub program: D::Program,
|
|
||||||
pub transform_uniform: D::Uniform,
|
|
||||||
pub gridline_count_uniform: D::Uniform,
|
|
||||||
pub ground_color_uniform: D::Uniform,
|
|
||||||
pub gridline_color_uniform: D::Uniform,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<D> GroundProgram<D>
|
|
||||||
where
|
|
||||||
D: Device,
|
|
||||||
{
|
|
||||||
pub fn new(device: &D, resources: &dyn ResourceLoader) -> GroundProgram<D> {
|
|
||||||
let program = device.create_program(resources, "demo_ground");
|
|
||||||
let transform_uniform = device.get_uniform(&program, "Transform");
|
|
||||||
let gridline_count_uniform = device.get_uniform(&program, "GridlineCount");
|
|
||||||
let ground_color_uniform = device.get_uniform(&program, "GroundColor");
|
|
||||||
let gridline_color_uniform = device.get_uniform(&program, "GridlineColor");
|
|
||||||
GroundProgram {
|
|
||||||
program,
|
|
||||||
transform_uniform,
|
|
||||||
gridline_count_uniform,
|
|
||||||
ground_color_uniform,
|
|
||||||
gridline_color_uniform,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct GroundVertexArray<D>
|
|
||||||
where
|
|
||||||
D: Device,
|
|
||||||
{
|
|
||||||
pub vertex_array: D::VertexArray,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<D> GroundVertexArray<D>
|
|
||||||
where
|
|
||||||
D: Device,
|
|
||||||
{
|
|
||||||
pub fn new(
|
|
||||||
device: &D,
|
|
||||||
ground_program: &GroundProgram<D>,
|
|
||||||
quad_vertex_positions_buffer: &D::Buffer,
|
|
||||||
quad_vertex_indices_buffer: &D::Buffer,
|
|
||||||
) -> GroundVertexArray<D> {
|
|
||||||
let vertex_array = device.create_vertex_array();
|
|
||||||
|
|
||||||
let position_attr = device.get_vertex_attr(&ground_program.program, "Position").unwrap();
|
|
||||||
|
|
||||||
device.bind_buffer(&vertex_array, quad_vertex_positions_buffer, BufferTarget::Vertex);
|
|
||||||
device.configure_vertex_attr(&vertex_array, &position_attr, &VertexAttrDescriptor {
|
|
||||||
size: 2,
|
|
||||||
class: VertexAttrClass::Int,
|
|
||||||
attr_type: VertexAttrType::I16,
|
|
||||||
stride: 4,
|
|
||||||
offset: 0,
|
|
||||||
divisor: 0,
|
|
||||||
buffer_index: 0,
|
|
||||||
});
|
|
||||||
device.bind_buffer(&vertex_array, quad_vertex_indices_buffer, BufferTarget::Index);
|
|
||||||
|
|
||||||
GroundVertexArray { vertex_array }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,889 +0,0 @@
|
||||||
// pathfinder/demo/common/src/lib.rs
|
|
||||||
//
|
|
||||||
// Copyright © 2019 The Pathfinder Project Developers.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
|
|
||||||
//! A demo app for Pathfinder.
|
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate log;
|
|
||||||
|
|
||||||
// Mode is used in Options, so has to be public
|
|
||||||
pub use crate::camera::Mode;
|
|
||||||
|
|
||||||
use crate::camera::Camera;
|
|
||||||
use crate::concurrent::DemoExecutor;
|
|
||||||
use crate::device::{GroundProgram, GroundVertexArray};
|
|
||||||
use crate::ui::{DemoUIModel, DemoUIPresenter, ScreenshotInfo, ScreenshotType, UIAction};
|
|
||||||
use crate::window::{Event, Keycode, SVGPath, Window, WindowSize};
|
|
||||||
use clap::{App, Arg};
|
|
||||||
use pathfinder_content::effects::DEFRINGING_KERNEL_CORE_GRAPHICS;
|
|
||||||
use pathfinder_content::effects::PatternFilter;
|
|
||||||
use pathfinder_content::effects::STEM_DARKENING_FACTORS;
|
|
||||||
use pathfinder_content::outline::Outline;
|
|
||||||
use pathfinder_content::pattern::Pattern;
|
|
||||||
use pathfinder_content::render_target::RenderTargetId;
|
|
||||||
use pathfinder_export::{Export, FileFormat};
|
|
||||||
use pathfinder_geometry::rect::{RectF, RectI};
|
|
||||||
use pathfinder_geometry::transform2d::Transform2F;
|
|
||||||
use pathfinder_geometry::transform3d::Transform4F;
|
|
||||||
use pathfinder_geometry::vector::{Vector2F, Vector2I, Vector4F, vec2f, vec2i};
|
|
||||||
use pathfinder_gpu::Device;
|
|
||||||
use pathfinder_renderer::concurrent::scene_proxy::{RenderCommandStream, SceneProxy};
|
|
||||||
use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererOptions};
|
|
||||||
use pathfinder_renderer::gpu::renderer::{RenderStats, RenderTime, Renderer};
|
|
||||||
use pathfinder_renderer::options::{BuildOptions, RenderTransform};
|
|
||||||
use pathfinder_renderer::paint::Paint;
|
|
||||||
use pathfinder_renderer::scene::{DrawPath, RenderTarget, Scene};
|
|
||||||
use pathfinder_resources::ResourceLoader;
|
|
||||||
use pathfinder_svg::BuiltSVG;
|
|
||||||
use pathfinder_ui::{MousePosition, UIEvent};
|
|
||||||
use std::fs::File;
|
|
||||||
use std::io::{BufWriter, Read};
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::thread;
|
|
||||||
use std::time::Duration;
|
|
||||||
use usvg::{Options as UsvgOptions, Tree};
|
|
||||||
|
|
||||||
#[cfg(any(not(target_os = "macos"), feature = "pf-gl"))]
|
|
||||||
use pathfinder_gl::GLDevice as DeviceImpl;
|
|
||||||
#[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
|
|
||||||
use pathfinder_metal::MetalDevice as DeviceImpl;
|
|
||||||
|
|
||||||
static DEFAULT_SVG_VIRTUAL_PATH: &'static str = "svg/Ghostscript_Tiger.svg";
|
|
||||||
|
|
||||||
const MOUSELOOK_ROTATION_SPEED: f32 = 0.007;
|
|
||||||
const CAMERA_VELOCITY: f32 = 0.02;
|
|
||||||
|
|
||||||
// How much the scene is scaled when a scale gesture is performed.
|
|
||||||
const CAMERA_SCALE_SPEED_2D: f32 = 6.0;
|
|
||||||
// How much the scene is scaled when a zoom button is clicked.
|
|
||||||
const CAMERA_ZOOM_AMOUNT_2D: f32 = 0.1;
|
|
||||||
|
|
||||||
// Half of the eye separation distance.
|
|
||||||
const DEFAULT_EYE_OFFSET: f32 = 0.025;
|
|
||||||
|
|
||||||
const APPROX_FONT_SIZE: f32 = 16.0;
|
|
||||||
|
|
||||||
const MESSAGE_TIMEOUT_SECS: u64 = 5;
|
|
||||||
|
|
||||||
pub mod window;
|
|
||||||
|
|
||||||
mod camera;
|
|
||||||
mod concurrent;
|
|
||||||
mod device;
|
|
||||||
mod renderer;
|
|
||||||
mod ui;
|
|
||||||
|
|
||||||
pub struct DemoApp<W> where W: Window {
|
|
||||||
pub window: W,
|
|
||||||
pub should_exit: bool,
|
|
||||||
pub options: Options,
|
|
||||||
|
|
||||||
window_size: WindowSize,
|
|
||||||
|
|
||||||
svg_tree: Tree,
|
|
||||||
scene_metadata: SceneMetadata,
|
|
||||||
render_transform: Option<RenderTransform>,
|
|
||||||
render_command_stream: Option<RenderCommandStream>,
|
|
||||||
|
|
||||||
camera: Camera,
|
|
||||||
frame_counter: u32,
|
|
||||||
pending_screenshot_info: Option<ScreenshotInfo>,
|
|
||||||
mouselook_enabled: bool,
|
|
||||||
pub dirty: bool,
|
|
||||||
expire_message_event_id: u32,
|
|
||||||
message_epoch: u32,
|
|
||||||
last_mouse_position: Vector2I,
|
|
||||||
|
|
||||||
current_frame: Option<Frame>,
|
|
||||||
|
|
||||||
ui_model: DemoUIModel,
|
|
||||||
ui_presenter: DemoUIPresenter<DeviceImpl>,
|
|
||||||
|
|
||||||
scene_proxy: SceneProxy,
|
|
||||||
renderer: Renderer<DeviceImpl>,
|
|
||||||
|
|
||||||
scene_framebuffer: Option<<DeviceImpl as Device>::Framebuffer>,
|
|
||||||
|
|
||||||
ground_program: GroundProgram<DeviceImpl>,
|
|
||||||
ground_vertex_array: GroundVertexArray<DeviceImpl>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<W> DemoApp<W> where W: Window {
|
|
||||||
pub fn new(window: W, window_size: WindowSize, mut options: Options) -> DemoApp<W> {
|
|
||||||
let expire_message_event_id = window.create_user_event_id();
|
|
||||||
|
|
||||||
let device;
|
|
||||||
#[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
|
|
||||||
{
|
|
||||||
device = DeviceImpl::new(window.metal_layer());
|
|
||||||
}
|
|
||||||
#[cfg(any(not(target_os = "macos"), feature = "pf-gl"))]
|
|
||||||
{
|
|
||||||
device = DeviceImpl::new(window.gl_version(), window.gl_default_framebuffer());
|
|
||||||
}
|
|
||||||
|
|
||||||
let resources = window.resource_loader();
|
|
||||||
|
|
||||||
// Read command line options.
|
|
||||||
options.command_line_overrides();
|
|
||||||
|
|
||||||
// Set up the executor.
|
|
||||||
let executor = DemoExecutor::new(options.jobs);
|
|
||||||
|
|
||||||
let mut ui_model = DemoUIModel::new(&options);
|
|
||||||
let render_options = RendererOptions { background_color: None };
|
|
||||||
|
|
||||||
let filter = build_filter(&ui_model);
|
|
||||||
|
|
||||||
let viewport = window.viewport(options.mode.view(0));
|
|
||||||
let (mut built_svg, svg_tree) = load_scene(resources,
|
|
||||||
&options.input_path,
|
|
||||||
viewport.size(),
|
|
||||||
filter);
|
|
||||||
|
|
||||||
let message = get_svg_building_message(&built_svg);
|
|
||||||
|
|
||||||
let dest_framebuffer = DestFramebuffer::Default {
|
|
||||||
viewport,
|
|
||||||
window_size: window_size.device_size(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let renderer = Renderer::new(device, resources, dest_framebuffer, render_options);
|
|
||||||
|
|
||||||
let scene_metadata = SceneMetadata::new_clipping_view_box(&mut built_svg.scene,
|
|
||||||
viewport.size());
|
|
||||||
let camera = Camera::new(options.mode, scene_metadata.view_box, viewport.size());
|
|
||||||
|
|
||||||
let scene_proxy = SceneProxy::from_scene(built_svg.scene, executor);
|
|
||||||
|
|
||||||
let ground_program = GroundProgram::new(&renderer.device, resources);
|
|
||||||
let ground_vertex_array = GroundVertexArray::new(&renderer.device,
|
|
||||||
&ground_program,
|
|
||||||
&renderer.quad_vertex_positions_buffer(),
|
|
||||||
&renderer.quad_vertex_indices_buffer());
|
|
||||||
|
|
||||||
let mut message_epoch = 0;
|
|
||||||
emit_message::<W>(
|
|
||||||
&mut ui_model,
|
|
||||||
&mut message_epoch,
|
|
||||||
expire_message_event_id,
|
|
||||||
message,
|
|
||||||
);
|
|
||||||
|
|
||||||
let ui_presenter = DemoUIPresenter::new(&renderer.device, resources);
|
|
||||||
|
|
||||||
DemoApp {
|
|
||||||
window,
|
|
||||||
should_exit: false,
|
|
||||||
options,
|
|
||||||
|
|
||||||
window_size,
|
|
||||||
|
|
||||||
svg_tree,
|
|
||||||
scene_metadata,
|
|
||||||
render_transform: None,
|
|
||||||
render_command_stream: None,
|
|
||||||
|
|
||||||
camera,
|
|
||||||
frame_counter: 0,
|
|
||||||
pending_screenshot_info: None,
|
|
||||||
mouselook_enabled: false,
|
|
||||||
dirty: true,
|
|
||||||
expire_message_event_id,
|
|
||||||
message_epoch,
|
|
||||||
last_mouse_position: Vector2I::default(),
|
|
||||||
|
|
||||||
current_frame: None,
|
|
||||||
|
|
||||||
ui_presenter,
|
|
||||||
ui_model,
|
|
||||||
|
|
||||||
scene_proxy,
|
|
||||||
renderer,
|
|
||||||
|
|
||||||
scene_framebuffer: None,
|
|
||||||
|
|
||||||
ground_program,
|
|
||||||
ground_vertex_array,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn prepare_frame(&mut self, events: Vec<Event>) -> u32 {
|
|
||||||
// Clear dirty flag.
|
|
||||||
self.dirty = false;
|
|
||||||
|
|
||||||
// Handle events.
|
|
||||||
let ui_events = self.handle_events(events);
|
|
||||||
|
|
||||||
// Update the scene.
|
|
||||||
self.build_scene();
|
|
||||||
|
|
||||||
// Save the frame.
|
|
||||||
//
|
|
||||||
// FIXME(pcwalton): This is super ugly.
|
|
||||||
let transform = self.render_transform.clone().unwrap();
|
|
||||||
self.current_frame = Some(Frame::new(transform, ui_events));
|
|
||||||
|
|
||||||
// Prepare to render the frame.
|
|
||||||
self.prepare_frame_rendering()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_scene(&mut self) {
|
|
||||||
self.render_transform = match self.camera {
|
|
||||||
Camera::ThreeD {
|
|
||||||
ref scene_transform,
|
|
||||||
ref mut modelview_transform,
|
|
||||||
ref mut velocity,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
if modelview_transform.offset(*velocity) {
|
|
||||||
self.dirty = true;
|
|
||||||
}
|
|
||||||
let perspective = scene_transform.perspective *
|
|
||||||
scene_transform.modelview_to_eye *
|
|
||||||
modelview_transform.to_transform();
|
|
||||||
Some(RenderTransform::Perspective(perspective))
|
|
||||||
}
|
|
||||||
Camera::TwoD(transform) => Some(RenderTransform::Transform2D(transform)),
|
|
||||||
};
|
|
||||||
|
|
||||||
let build_options = BuildOptions {
|
|
||||||
transform: self.render_transform.clone().unwrap(),
|
|
||||||
dilation: if self.ui_model.stem_darkening_effect_enabled {
|
|
||||||
let font_size = APPROX_FONT_SIZE * self.window_size.backing_scale_factor;
|
|
||||||
vec2f(STEM_DARKENING_FACTORS[0], STEM_DARKENING_FACTORS[1]) * font_size
|
|
||||||
} else {
|
|
||||||
Vector2F::zero()
|
|
||||||
},
|
|
||||||
subpixel_aa_enabled: self.ui_model.subpixel_aa_effect_enabled,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.render_command_stream = Some(self.scene_proxy.build_with_stream(build_options));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_events(&mut self, events: Vec<Event>) -> Vec<UIEvent> {
|
|
||||||
let mut ui_events = vec![];
|
|
||||||
self.dirty = false;
|
|
||||||
|
|
||||||
for event in events {
|
|
||||||
match event {
|
|
||||||
Event::Quit { .. } | Event::KeyDown(Keycode::Escape) => {
|
|
||||||
self.should_exit = true;
|
|
||||||
self.dirty = true;
|
|
||||||
}
|
|
||||||
Event::WindowResized(new_size) => {
|
|
||||||
self.window_size = new_size;
|
|
||||||
let viewport = self.window.viewport(self.ui_model.mode.view(0));
|
|
||||||
self.scene_proxy.set_view_box(RectF::new(Vector2F::zero(),
|
|
||||||
viewport.size().to_f32()));
|
|
||||||
self.renderer.set_main_framebuffer_size(self.window_size.device_size());
|
|
||||||
self.dirty = true;
|
|
||||||
}
|
|
||||||
Event::MouseDown(new_position) => {
|
|
||||||
let mouse_position = self.process_mouse_position(new_position);
|
|
||||||
ui_events.push(UIEvent::MouseDown(mouse_position));
|
|
||||||
}
|
|
||||||
Event::MouseMoved(new_position) if self.mouselook_enabled => {
|
|
||||||
let mouse_position = self.process_mouse_position(new_position);
|
|
||||||
if let Camera::ThreeD { ref mut modelview_transform, .. } = self.camera {
|
|
||||||
let rotation = mouse_position.relative.to_f32() * MOUSELOOK_ROTATION_SPEED;
|
|
||||||
modelview_transform.yaw += rotation.x();
|
|
||||||
modelview_transform.pitch += rotation.y();
|
|
||||||
self.dirty = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Event::MouseDragged(new_position) => {
|
|
||||||
let mouse_position = self.process_mouse_position(new_position);
|
|
||||||
ui_events.push(UIEvent::MouseDragged(mouse_position));
|
|
||||||
self.dirty = true;
|
|
||||||
}
|
|
||||||
Event::Zoom(d_dist, position) => {
|
|
||||||
if let Camera::TwoD(ref mut transform) = self.camera {
|
|
||||||
let backing_scale_factor = self.window_size.backing_scale_factor;
|
|
||||||
let position = position.to_f32() * backing_scale_factor;
|
|
||||||
let scale_delta = 1.0 + d_dist * CAMERA_SCALE_SPEED_2D;
|
|
||||||
*transform = transform.translate(-position)
|
|
||||||
.scale(scale_delta)
|
|
||||||
.translate(position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Event::Look { pitch, yaw } => {
|
|
||||||
if let Camera::ThreeD { ref mut modelview_transform, .. } = self.camera {
|
|
||||||
modelview_transform.pitch += pitch;
|
|
||||||
modelview_transform.yaw += yaw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Event::SetEyeTransforms(new_eye_transforms) => {
|
|
||||||
if let Camera::ThreeD {
|
|
||||||
ref mut scene_transform,
|
|
||||||
ref mut eye_transforms,
|
|
||||||
..
|
|
||||||
} = self.camera
|
|
||||||
{
|
|
||||||
*eye_transforms = new_eye_transforms;
|
|
||||||
// Calculate the new scene transform by lerp'ing the eye transforms.
|
|
||||||
*scene_transform = eye_transforms[0];
|
|
||||||
for (index, eye_transform) in eye_transforms.iter().enumerate().skip(1) {
|
|
||||||
let weight = 1.0 / (index + 1) as f32;
|
|
||||||
scene_transform.perspective.transform =
|
|
||||||
scene_transform.perspective
|
|
||||||
.transform
|
|
||||||
.lerp(weight, &eye_transform.perspective.transform);
|
|
||||||
scene_transform.modelview_to_eye =
|
|
||||||
scene_transform.modelview_to_eye
|
|
||||||
.lerp(weight, &eye_transform.modelview_to_eye);
|
|
||||||
}
|
|
||||||
// TODO: calculate the eye offset from the eye transforms?
|
|
||||||
let z_offset = -DEFAULT_EYE_OFFSET *
|
|
||||||
scene_transform.perspective.transform.c0.x();
|
|
||||||
let z_offset = Vector4F::new(0.0, 0.0, z_offset, 1.0);
|
|
||||||
scene_transform.modelview_to_eye =
|
|
||||||
Transform4F::from_translation(z_offset) *
|
|
||||||
scene_transform.modelview_to_eye;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Event::KeyDown(Keycode::Alphanumeric(b'w')) => {
|
|
||||||
if let Camera::ThreeD {
|
|
||||||
ref mut velocity, ..
|
|
||||||
} = self.camera
|
|
||||||
{
|
|
||||||
let scale_factor =
|
|
||||||
camera::scale_factor_for_view_box(self.scene_metadata.view_box);
|
|
||||||
velocity.set_z(-CAMERA_VELOCITY / scale_factor);
|
|
||||||
self.dirty = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Event::KeyDown(Keycode::Alphanumeric(b's')) => {
|
|
||||||
if let Camera::ThreeD {
|
|
||||||
ref mut velocity, ..
|
|
||||||
} = self.camera
|
|
||||||
{
|
|
||||||
let scale_factor =
|
|
||||||
camera::scale_factor_for_view_box(self.scene_metadata.view_box);
|
|
||||||
velocity.set_z(CAMERA_VELOCITY / scale_factor);
|
|
||||||
self.dirty = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Event::KeyDown(Keycode::Alphanumeric(b'a')) => {
|
|
||||||
if let Camera::ThreeD {
|
|
||||||
ref mut velocity, ..
|
|
||||||
} = self.camera
|
|
||||||
{
|
|
||||||
let scale_factor =
|
|
||||||
camera::scale_factor_for_view_box(self.scene_metadata.view_box);
|
|
||||||
velocity.set_x(-CAMERA_VELOCITY / scale_factor);
|
|
||||||
self.dirty = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Event::KeyDown(Keycode::Alphanumeric(b'd')) => {
|
|
||||||
if let Camera::ThreeD {
|
|
||||||
ref mut velocity, ..
|
|
||||||
} = self.camera
|
|
||||||
{
|
|
||||||
let scale_factor =
|
|
||||||
camera::scale_factor_for_view_box(self.scene_metadata.view_box);
|
|
||||||
velocity.set_x(CAMERA_VELOCITY / scale_factor);
|
|
||||||
self.dirty = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Event::KeyUp(Keycode::Alphanumeric(b'w'))
|
|
||||||
| Event::KeyUp(Keycode::Alphanumeric(b's')) => {
|
|
||||||
if let Camera::ThreeD {
|
|
||||||
ref mut velocity, ..
|
|
||||||
} = self.camera
|
|
||||||
{
|
|
||||||
velocity.set_z(0.0);
|
|
||||||
self.dirty = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Event::KeyUp(Keycode::Alphanumeric(b'a'))
|
|
||||||
| Event::KeyUp(Keycode::Alphanumeric(b'd')) => {
|
|
||||||
if let Camera::ThreeD {
|
|
||||||
ref mut velocity, ..
|
|
||||||
} = self.camera
|
|
||||||
{
|
|
||||||
velocity.set_x(0.0);
|
|
||||||
self.dirty = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Event::KeyDown(Keycode::Tab) => {
|
|
||||||
self.options.ui = match self.options.ui {
|
|
||||||
UIVisibility::None => UIVisibility::Stats,
|
|
||||||
UIVisibility::Stats => UIVisibility::All,
|
|
||||||
UIVisibility::All => UIVisibility::None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Event::OpenSVG(ref svg_path) => {
|
|
||||||
let viewport = self.window.viewport(self.ui_model.mode.view(0));
|
|
||||||
let filter = build_filter(&self.ui_model);
|
|
||||||
let (mut built_svg, svg_tree) = load_scene(self.window.resource_loader(),
|
|
||||||
svg_path,
|
|
||||||
viewport.size(),
|
|
||||||
filter);
|
|
||||||
|
|
||||||
self.ui_model.message = get_svg_building_message(&built_svg);
|
|
||||||
|
|
||||||
let viewport_size = self.window.viewport(self.ui_model.mode.view(0)).size();
|
|
||||||
self.scene_metadata =
|
|
||||||
SceneMetadata::new_clipping_view_box(&mut built_svg.scene, viewport_size);
|
|
||||||
self.camera = Camera::new(self.ui_model.mode,
|
|
||||||
self.scene_metadata.view_box,
|
|
||||||
viewport_size);
|
|
||||||
|
|
||||||
self.scene_proxy.replace_scene(built_svg.scene);
|
|
||||||
self.svg_tree = svg_tree;
|
|
||||||
|
|
||||||
self.dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Event::User {
|
|
||||||
message_type: event_id,
|
|
||||||
message_data: expected_epoch,
|
|
||||||
} if event_id == self.expire_message_event_id
|
|
||||||
&& expected_epoch as u32 == self.message_epoch =>
|
|
||||||
{
|
|
||||||
self.ui_model.message = String::new();
|
|
||||||
self.dirty = true;
|
|
||||||
}
|
|
||||||
_ => continue,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ui_events
|
|
||||||
}
|
|
||||||
|
|
||||||
fn process_mouse_position(&mut self, new_position: Vector2I) -> MousePosition {
|
|
||||||
let absolute = new_position * self.window_size.backing_scale_factor as i32;
|
|
||||||
let relative = absolute - self.last_mouse_position;
|
|
||||||
self.last_mouse_position = absolute;
|
|
||||||
MousePosition { absolute, relative }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn finish_drawing_frame(&mut self) {
|
|
||||||
self.maybe_take_screenshot();
|
|
||||||
self.update_stats();
|
|
||||||
self.draw_debug_ui();
|
|
||||||
|
|
||||||
let frame = self.current_frame.take().unwrap();
|
|
||||||
for ui_event in &frame.ui_events {
|
|
||||||
self.dirty = true;
|
|
||||||
self.renderer.debug_ui_presenter.ui_presenter.event_queue.push(*ui_event);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.renderer.debug_ui_presenter.ui_presenter.mouse_position =
|
|
||||||
self.last_mouse_position.to_f32() * self.window_size.backing_scale_factor;
|
|
||||||
|
|
||||||
let mut ui_action = UIAction::None;
|
|
||||||
if self.options.ui == UIVisibility::All {
|
|
||||||
self.ui_presenter.update(
|
|
||||||
&self.renderer.device,
|
|
||||||
&mut self.window,
|
|
||||||
&mut self.renderer.debug_ui_presenter,
|
|
||||||
&mut ui_action,
|
|
||||||
&mut self.ui_model,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.handle_ui_events(frame, &mut ui_action);
|
|
||||||
|
|
||||||
self.renderer.device.end_commands();
|
|
||||||
|
|
||||||
self.window.present(&mut self.renderer.device);
|
|
||||||
self.frame_counter += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_stats(&mut self) {
|
|
||||||
let frame = self.current_frame.as_mut().unwrap();
|
|
||||||
if let Some(rendering_time) = self.renderer.shift_rendering_time() {
|
|
||||||
frame.scene_rendering_times.push(rendering_time);
|
|
||||||
}
|
|
||||||
|
|
||||||
if frame.scene_stats.is_empty() && frame.scene_rendering_times.is_empty() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let zero = RenderStats::default();
|
|
||||||
let aggregate_stats = frame.scene_stats.iter().fold(zero, |sum, item| sum + *item);
|
|
||||||
if !frame.scene_rendering_times.is_empty() {
|
|
||||||
let total_rendering_time = frame.scene_rendering_times
|
|
||||||
.iter()
|
|
||||||
.fold(RenderTime::default(), |sum, item| sum + *item);
|
|
||||||
self.renderer.debug_ui_presenter.add_sample(aggregate_stats, total_rendering_time);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn maybe_take_screenshot(&mut self) {
|
|
||||||
match self.pending_screenshot_info.take() {
|
|
||||||
None => {}
|
|
||||||
Some(ScreenshotInfo { kind: ScreenshotType::PNG, path }) => {
|
|
||||||
self.take_raster_screenshot(path)
|
|
||||||
}
|
|
||||||
Some(ScreenshotInfo { kind: ScreenshotType::SVG, path }) => {
|
|
||||||
// FIXME(pcwalton): This won't work on Android.
|
|
||||||
let mut writer = BufWriter::new(File::create(path).unwrap());
|
|
||||||
self.scene_proxy.copy_scene().export(&mut writer, FileFormat::SVG).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_ui_events(&mut self, mut frame: Frame, ui_action: &mut UIAction) {
|
|
||||||
frame.ui_events = self.renderer.debug_ui_presenter.ui_presenter.event_queue.drain();
|
|
||||||
|
|
||||||
self.handle_ui_action(ui_action);
|
|
||||||
|
|
||||||
// Switch camera mode (2D/3D) if requested.
|
|
||||||
//
|
|
||||||
// FIXME(pcwalton): This should really be an MVC setup.
|
|
||||||
if self.camera.mode() != self.ui_model.mode {
|
|
||||||
let viewport_size = self.window.viewport(self.ui_model.mode.view(0)).size();
|
|
||||||
self.camera = Camera::new(self.ui_model.mode,
|
|
||||||
self.scene_metadata.view_box,
|
|
||||||
viewport_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
for ui_event in frame.ui_events {
|
|
||||||
match ui_event {
|
|
||||||
UIEvent::MouseDown(_) if self.camera.is_3d() => {
|
|
||||||
// If nothing handled the mouse-down event, toggle mouselook.
|
|
||||||
self.mouselook_enabled = !self.mouselook_enabled;
|
|
||||||
}
|
|
||||||
UIEvent::MouseDragged(position) => {
|
|
||||||
if let Camera::TwoD(ref mut transform) = self.camera {
|
|
||||||
*transform = transform.translate(position.relative.to_f32());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_ui_action(&mut self, ui_action: &mut UIAction) {
|
|
||||||
match ui_action {
|
|
||||||
UIAction::None => {}
|
|
||||||
UIAction::ModelChanged => self.dirty = true,
|
|
||||||
UIAction::EffectsChanged => {
|
|
||||||
let viewport_size = self.window.viewport(self.ui_model.mode.view(0)).size();
|
|
||||||
let filter = build_filter(&self.ui_model);
|
|
||||||
let mut built_svg = build_svg_tree(&self.svg_tree, viewport_size, filter);
|
|
||||||
self.scene_metadata =
|
|
||||||
SceneMetadata::new_clipping_view_box(&mut built_svg.scene, viewport_size);
|
|
||||||
self.scene_proxy.replace_scene(built_svg.scene);
|
|
||||||
self.dirty = true;
|
|
||||||
}
|
|
||||||
UIAction::TakeScreenshot(ref info) => {
|
|
||||||
self.pending_screenshot_info = Some((*info).clone());
|
|
||||||
self.dirty = true;
|
|
||||||
}
|
|
||||||
UIAction::ZoomIn => {
|
|
||||||
if let Camera::TwoD(ref mut transform) = self.camera {
|
|
||||||
let scale = 1.0 + CAMERA_ZOOM_AMOUNT_2D;
|
|
||||||
let center = center_of_window(&self.window_size);
|
|
||||||
*transform = transform.translate(-center).scale(scale).translate(center);
|
|
||||||
self.dirty = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
UIAction::ZoomOut => {
|
|
||||||
if let Camera::TwoD(ref mut transform) = self.camera {
|
|
||||||
let scale = 1.0 - CAMERA_ZOOM_AMOUNT_2D;
|
|
||||||
let center = center_of_window(&self.window_size);
|
|
||||||
*transform = transform.translate(-center).scale(scale).translate(center);
|
|
||||||
self.dirty = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
UIAction::ZoomActualSize => {
|
|
||||||
if let Camera::TwoD(ref mut transform) = self.camera {
|
|
||||||
*transform = Transform2F::default();
|
|
||||||
self.dirty = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
UIAction::Rotate(theta) => {
|
|
||||||
if let Camera::TwoD(ref mut transform) = self.camera {
|
|
||||||
let old_rotation = transform.rotation();
|
|
||||||
let center = center_of_window(&self.window_size);
|
|
||||||
*transform = transform.translate(-center)
|
|
||||||
.rotate(*theta - old_rotation)
|
|
||||||
.translate(center);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Options {
|
|
||||||
pub jobs: Option<usize>,
|
|
||||||
pub mode: Mode,
|
|
||||||
pub input_path: SVGPath,
|
|
||||||
pub ui: UIVisibility,
|
|
||||||
pub background_color: BackgroundColor,
|
|
||||||
hidden_field_for_future_proofing: (),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Options {
|
|
||||||
fn default() -> Self {
|
|
||||||
Options {
|
|
||||||
jobs: None,
|
|
||||||
mode: Mode::TwoD,
|
|
||||||
input_path: SVGPath::Default,
|
|
||||||
ui: UIVisibility::All,
|
|
||||||
background_color: BackgroundColor::Light,
|
|
||||||
hidden_field_for_future_proofing: (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Options {
|
|
||||||
fn command_line_overrides(&mut self) {
|
|
||||||
let matches = App::new("tile-svg")
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("jobs")
|
|
||||||
.short("j")
|
|
||||||
.long("jobs")
|
|
||||||
.value_name("THREADS")
|
|
||||||
.takes_value(true)
|
|
||||||
.help("Number of threads to use"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("3d")
|
|
||||||
.short("3")
|
|
||||||
.long("3d")
|
|
||||||
.help("Run in 3D")
|
|
||||||
.conflicts_with("vr"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("vr")
|
|
||||||
.short("V")
|
|
||||||
.long("vr")
|
|
||||||
.help("Run in VR")
|
|
||||||
.conflicts_with("3d"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("ui")
|
|
||||||
.short("u")
|
|
||||||
.long("ui")
|
|
||||||
.takes_value(true)
|
|
||||||
.possible_values(&["none", "stats", "all"])
|
|
||||||
.help("How much UI to show"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("background")
|
|
||||||
.short("b")
|
|
||||||
.long("background")
|
|
||||||
.takes_value(true)
|
|
||||||
.possible_values(&["light", "dark", "transparent"])
|
|
||||||
.help("The background color to use"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("INPUT")
|
|
||||||
.help("Path to the SVG file to render")
|
|
||||||
.index(1),
|
|
||||||
)
|
|
||||||
.get_matches();
|
|
||||||
|
|
||||||
if let Some(jobs) = matches.value_of("jobs") {
|
|
||||||
self.jobs = jobs.parse().ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
if matches.is_present("3d") {
|
|
||||||
self.mode = Mode::ThreeD;
|
|
||||||
} else if matches.is_present("vr") {
|
|
||||||
self.mode = Mode::VR;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(ui) = matches.value_of("ui") {
|
|
||||||
self.ui = match ui {
|
|
||||||
"none" => UIVisibility::None,
|
|
||||||
"stats" => UIVisibility::Stats,
|
|
||||||
_ => UIVisibility::All,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(background_color) = matches.value_of("background") {
|
|
||||||
self.background_color = match background_color {
|
|
||||||
"light" => BackgroundColor::Light,
|
|
||||||
"dark" => BackgroundColor::Dark,
|
|
||||||
_ => BackgroundColor::Transparent,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(path) = matches.value_of("INPUT") {
|
|
||||||
self.input_path = SVGPath::Path(PathBuf::from(path));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq)]
|
|
||||||
pub enum UIVisibility {
|
|
||||||
None,
|
|
||||||
Stats,
|
|
||||||
All,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_scene(resource_loader: &dyn ResourceLoader,
|
|
||||||
input_path: &SVGPath,
|
|
||||||
viewport_size: Vector2I,
|
|
||||||
filter: Option<PatternFilter>)
|
|
||||||
-> (BuiltSVG, Tree) {
|
|
||||||
let mut data;
|
|
||||||
match *input_path {
|
|
||||||
SVGPath::Default => data = resource_loader.slurp(DEFAULT_SVG_VIRTUAL_PATH).unwrap(),
|
|
||||||
SVGPath::Resource(ref name) => data = resource_loader.slurp(name).unwrap(),
|
|
||||||
SVGPath::Path(ref path) => {
|
|
||||||
data = vec![];
|
|
||||||
File::open(path).unwrap().read_to_end(&mut data).unwrap();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let tree = Tree::from_data(&data, &UsvgOptions::default()).expect("Failed to parse the SVG!");
|
|
||||||
let built_svg = build_svg_tree(&tree, viewport_size, filter);
|
|
||||||
(built_svg, tree)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME(pcwalton): Rework how transforms work in the demo. The transform affects the final
|
|
||||||
// composite steps, breaking this approach.
|
|
||||||
fn build_svg_tree(tree: &Tree, viewport_size: Vector2I, filter: Option<PatternFilter>)
|
|
||||||
-> BuiltSVG {
|
|
||||||
let mut scene = Scene::new();
|
|
||||||
let filter_info = filter.map(|filter| {
|
|
||||||
let scale = match filter {
|
|
||||||
PatternFilter::Text { defringing_kernel: Some(_), .. } => vec2i(3, 1),
|
|
||||||
_ => vec2i(1, 1),
|
|
||||||
};
|
|
||||||
let name = "Text".to_owned();
|
|
||||||
let render_target_size = viewport_size * scale;
|
|
||||||
let render_target = RenderTarget::new(render_target_size, name);
|
|
||||||
let render_target_id = scene.push_render_target(render_target);
|
|
||||||
FilterInfo { filter, render_target_id, render_target_size }
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut built_svg = BuiltSVG::from_tree_and_scene(&tree, scene);
|
|
||||||
if let Some(FilterInfo { filter, render_target_id, render_target_size }) = filter_info {
|
|
||||||
let mut pattern = Pattern::from_render_target(render_target_id, render_target_size);
|
|
||||||
pattern.set_filter(Some(filter));
|
|
||||||
let paint_id = built_svg.scene.push_paint(&Paint::from_pattern(pattern));
|
|
||||||
|
|
||||||
let outline = Outline::from_rect(RectI::new(vec2i(0, 0), viewport_size).to_f32());
|
|
||||||
let path = DrawPath::new(outline, paint_id);
|
|
||||||
|
|
||||||
built_svg.scene.pop_render_target();
|
|
||||||
built_svg.scene.push_path(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
return built_svg;
|
|
||||||
|
|
||||||
struct FilterInfo {
|
|
||||||
filter: PatternFilter,
|
|
||||||
render_target_id: RenderTargetId,
|
|
||||||
render_target_size: Vector2I,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn center_of_window(window_size: &WindowSize) -> Vector2F {
|
|
||||||
window_size.device_size().to_f32() * 0.5
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_svg_building_message(built_svg: &BuiltSVG) -> String {
|
|
||||||
if built_svg.result_flags.is_empty() {
|
|
||||||
return String::new();
|
|
||||||
}
|
|
||||||
format!(
|
|
||||||
"Warning: These features in the SVG are unsupported: {}.",
|
|
||||||
built_svg.result_flags
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn emit_message<W>(
|
|
||||||
ui_model: &mut DemoUIModel,
|
|
||||||
message_epoch: &mut u32,
|
|
||||||
expire_message_event_id: u32,
|
|
||||||
message: String,
|
|
||||||
) where
|
|
||||||
W: Window,
|
|
||||||
{
|
|
||||||
if message.is_empty() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ui_model.message = message;
|
|
||||||
let expected_epoch = *message_epoch + 1;
|
|
||||||
*message_epoch = expected_epoch;
|
|
||||||
thread::spawn(move || {
|
|
||||||
thread::sleep(Duration::from_secs(MESSAGE_TIMEOUT_SECS));
|
|
||||||
W::push_user_event(expire_message_event_id, expected_epoch);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Frame {
|
|
||||||
transform: RenderTransform,
|
|
||||||
ui_events: Vec<UIEvent>,
|
|
||||||
scene_rendering_times: Vec<RenderTime>,
|
|
||||||
scene_stats: Vec<RenderStats>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Frame {
|
|
||||||
fn new(transform: RenderTransform, ui_events: Vec<UIEvent>) -> Frame {
|
|
||||||
Frame {
|
|
||||||
transform,
|
|
||||||
ui_events,
|
|
||||||
scene_rendering_times: vec![],
|
|
||||||
scene_stats: vec![],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
||||||
pub enum BackgroundColor {
|
|
||||||
Light = 0,
|
|
||||||
Dark = 1,
|
|
||||||
Transparent = 2,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BackgroundColor {
|
|
||||||
fn as_str(&self) -> &'static str {
|
|
||||||
match *self {
|
|
||||||
BackgroundColor::Light => "Light",
|
|
||||||
BackgroundColor::Dark => "Dark",
|
|
||||||
BackgroundColor::Transparent => "Transparent",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SceneMetadata {
|
|
||||||
view_box: RectF,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SceneMetadata {
|
|
||||||
// FIXME(pcwalton): The fact that this mutates the scene is really ugly!
|
|
||||||
// Can we simplify this?
|
|
||||||
fn new_clipping_view_box(scene: &mut Scene, viewport_size: Vector2I) -> SceneMetadata {
|
|
||||||
let view_box = scene.view_box();
|
|
||||||
scene.set_view_box(RectF::new(Vector2F::zero(), viewport_size.to_f32()));
|
|
||||||
SceneMetadata { view_box }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_filter(ui_model: &DemoUIModel) -> Option<PatternFilter> {
|
|
||||||
if !ui_model.gamma_correction_effect_enabled && !ui_model.subpixel_aa_effect_enabled {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(PatternFilter::Text {
|
|
||||||
fg_color: ui_model.foreground_color().to_f32(),
|
|
||||||
bg_color: ui_model.background_color().to_f32(),
|
|
||||||
gamma_correction: ui_model.gamma_correction_effect_enabled,
|
|
||||||
defringing_kernel: if ui_model.subpixel_aa_effect_enabled {
|
|
||||||
// TODO(pcwalton): Select FreeType defringing kernel as necessary.
|
|
||||||
Some(DEFRINGING_KERNEL_CORE_GRAPHICS)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,305 +0,0 @@
|
||||||
// pathfinder/demo/common/src/renderer.rs
|
|
||||||
//
|
|
||||||
// Copyright © 2019 The Pathfinder Project Developers.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
|
|
||||||
//! Rendering functionality for the demo.
|
|
||||||
|
|
||||||
use crate::camera::{Camera, Mode};
|
|
||||||
use crate::window::{View, Window};
|
|
||||||
use crate::{BackgroundColor, DemoApp, UIVisibility};
|
|
||||||
use image::ColorType;
|
|
||||||
use pathfinder_color::{ColorF, ColorU};
|
|
||||||
use pathfinder_gpu::{ClearOps, DepthFunc, DepthState, Device, Primitive, RenderOptions};
|
|
||||||
use pathfinder_gpu::{RenderState, RenderTarget, TextureData, TextureFormat, UniformData};
|
|
||||||
use pathfinder_geometry::rect::RectI;
|
|
||||||
use pathfinder_geometry::transform3d::Transform4F;
|
|
||||||
use pathfinder_geometry::vector::{Vector2I, Vector4F};
|
|
||||||
use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererOptions};
|
|
||||||
use pathfinder_renderer::options::RenderTransform;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
const GROUND_SOLID_COLOR: ColorU = ColorU {
|
|
||||||
r: 80,
|
|
||||||
g: 80,
|
|
||||||
b: 80,
|
|
||||||
a: 255,
|
|
||||||
};
|
|
||||||
|
|
||||||
const GROUND_LINE_COLOR: ColorU = ColorU {
|
|
||||||
r: 127,
|
|
||||||
g: 127,
|
|
||||||
b: 127,
|
|
||||||
a: 255,
|
|
||||||
};
|
|
||||||
|
|
||||||
const GRIDLINE_COUNT: i32 = 10;
|
|
||||||
|
|
||||||
impl<W> DemoApp<W> where W: Window {
|
|
||||||
pub fn prepare_frame_rendering(&mut self) -> u32 {
|
|
||||||
// Make the context current.
|
|
||||||
let view = self.ui_model.mode.view(0);
|
|
||||||
self.window.make_current(view);
|
|
||||||
|
|
||||||
// Set up framebuffers.
|
|
||||||
let window_size = self.window_size.device_size();
|
|
||||||
let mode = self.camera.mode();
|
|
||||||
let scene_count = match mode {
|
|
||||||
Mode::VR => {
|
|
||||||
let viewport = self.window.viewport(View::Stereo(0));
|
|
||||||
if self.scene_framebuffer.is_none()
|
|
||||||
|| self.renderer.device.texture_size(
|
|
||||||
&self
|
|
||||||
.renderer
|
|
||||||
.device
|
|
||||||
.framebuffer_texture(self.scene_framebuffer.as_ref().unwrap()),
|
|
||||||
) != viewport.size()
|
|
||||||
{
|
|
||||||
let scene_texture = self
|
|
||||||
.renderer
|
|
||||||
.device
|
|
||||||
.create_texture(TextureFormat::RGBA8, viewport.size());
|
|
||||||
self.scene_framebuffer =
|
|
||||||
Some(self.renderer.device.create_framebuffer(scene_texture));
|
|
||||||
}
|
|
||||||
self.renderer
|
|
||||||
.replace_dest_framebuffer(DestFramebuffer::Other(
|
|
||||||
self.scene_framebuffer.take().unwrap(),
|
|
||||||
));
|
|
||||||
2
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
self.renderer
|
|
||||||
.replace_dest_framebuffer(DestFramebuffer::Default {
|
|
||||||
viewport: self.window.viewport(View::Mono),
|
|
||||||
window_size,
|
|
||||||
});
|
|
||||||
1
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Clear to the appropriate color.
|
|
||||||
let clear_color = match mode {
|
|
||||||
Mode::TwoD => Some(self.ui_model.background_color().to_f32()),
|
|
||||||
Mode::ThreeD => None,
|
|
||||||
Mode::VR => Some(ColorF::transparent_black()),
|
|
||||||
};
|
|
||||||
self.renderer.set_options(RendererOptions { background_color: clear_color });
|
|
||||||
|
|
||||||
scene_count
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn draw_scene(&mut self) {
|
|
||||||
self.renderer.device.begin_commands();
|
|
||||||
|
|
||||||
let view = self.ui_model.mode.view(0);
|
|
||||||
self.window.make_current(view);
|
|
||||||
|
|
||||||
if self.camera.mode() != Mode::VR {
|
|
||||||
self.draw_environment(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.renderer.device.end_commands();
|
|
||||||
|
|
||||||
self.render_vector_scene();
|
|
||||||
|
|
||||||
// Reattach default framebuffer.
|
|
||||||
if self.camera.mode() == Mode::VR {
|
|
||||||
if let DestFramebuffer::Other(scene_framebuffer) =
|
|
||||||
self.renderer
|
|
||||||
.replace_dest_framebuffer(DestFramebuffer::Default {
|
|
||||||
viewport: self.window.viewport(View::Mono),
|
|
||||||
window_size: self.window_size.device_size(),
|
|
||||||
})
|
|
||||||
{
|
|
||||||
self.scene_framebuffer = Some(scene_framebuffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn begin_compositing(&mut self) {
|
|
||||||
self.renderer.device.begin_commands();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn composite_scene(&mut self, render_scene_index: u32) {
|
|
||||||
let (eye_transforms, scene_transform, modelview_transform) = match self.camera {
|
|
||||||
Camera::ThreeD {
|
|
||||||
ref eye_transforms,
|
|
||||||
ref scene_transform,
|
|
||||||
ref modelview_transform,
|
|
||||||
..
|
|
||||||
} if eye_transforms.len() > 1 => (eye_transforms, scene_transform, modelview_transform),
|
|
||||||
_ => return,
|
|
||||||
};
|
|
||||||
|
|
||||||
debug!(
|
|
||||||
"scene_transform.perspective={:?}",
|
|
||||||
scene_transform.perspective
|
|
||||||
);
|
|
||||||
debug!(
|
|
||||||
"scene_transform.modelview_to_eye={:?}",
|
|
||||||
scene_transform.modelview_to_eye
|
|
||||||
);
|
|
||||||
debug!("modelview transform={:?}", modelview_transform);
|
|
||||||
|
|
||||||
let viewport = self.window.viewport(View::Stereo(render_scene_index));
|
|
||||||
self.window.make_current(View::Stereo(render_scene_index));
|
|
||||||
|
|
||||||
self.renderer.replace_dest_framebuffer(DestFramebuffer::Default {
|
|
||||||
viewport,
|
|
||||||
window_size: self.window_size.device_size(),
|
|
||||||
});
|
|
||||||
|
|
||||||
self.draw_environment(render_scene_index);
|
|
||||||
|
|
||||||
let scene_framebuffer = self.scene_framebuffer.as_ref().unwrap();
|
|
||||||
let scene_texture = self.renderer.device.framebuffer_texture(scene_framebuffer);
|
|
||||||
|
|
||||||
let mut quad_scale = self.scene_metadata.view_box.size().to_4d();
|
|
||||||
quad_scale.set_z(1.0);
|
|
||||||
let quad_scale_transform = Transform4F::from_scale(quad_scale);
|
|
||||||
|
|
||||||
let scene_transform_matrix = scene_transform.perspective *
|
|
||||||
scene_transform.modelview_to_eye *
|
|
||||||
modelview_transform.to_transform() *
|
|
||||||
quad_scale_transform;
|
|
||||||
|
|
||||||
let eye_transform = &eye_transforms[render_scene_index as usize];
|
|
||||||
let eye_transform_matrix = eye_transform.perspective *
|
|
||||||
eye_transform.modelview_to_eye *
|
|
||||||
modelview_transform.to_transform() *
|
|
||||||
quad_scale_transform;
|
|
||||||
|
|
||||||
debug!(
|
|
||||||
"eye transform({}).modelview_to_eye={:?}",
|
|
||||||
render_scene_index, eye_transform.modelview_to_eye
|
|
||||||
);
|
|
||||||
debug!(
|
|
||||||
"eye transform_matrix({})={:?}",
|
|
||||||
render_scene_index, eye_transform_matrix
|
|
||||||
);
|
|
||||||
debug!("---");
|
|
||||||
|
|
||||||
self.renderer.reproject_texture(
|
|
||||||
scene_texture,
|
|
||||||
&scene_transform_matrix.transform,
|
|
||||||
&eye_transform_matrix.transform,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draws the ground, if applicable.
|
|
||||||
fn draw_environment(&self, render_scene_index: u32) {
|
|
||||||
let frame = &self.current_frame.as_ref().unwrap();
|
|
||||||
|
|
||||||
let perspective = match frame.transform {
|
|
||||||
RenderTransform::Transform2D(..) => return,
|
|
||||||
RenderTransform::Perspective(perspective) => perspective,
|
|
||||||
};
|
|
||||||
|
|
||||||
if self.ui_model.background_color == BackgroundColor::Transparent {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let ground_scale = self.scene_metadata.view_box.max_x() * 2.0;
|
|
||||||
|
|
||||||
let mut offset = self.scene_metadata.view_box.lower_right().to_4d();
|
|
||||||
offset.set_z(ground_scale);
|
|
||||||
offset = offset * Vector4F::new(-0.5, 1.0, -0.5, 1.0);
|
|
||||||
let base_transform = perspective.transform * Transform4F::from_translation(offset);
|
|
||||||
|
|
||||||
// Fill ground.
|
|
||||||
let transform = base_transform *
|
|
||||||
Transform4F::from_scale(Vector4F::new(ground_scale, 1.0, ground_scale, 1.0));
|
|
||||||
|
|
||||||
// Don't clear the first scene after drawing it.
|
|
||||||
let clear_color = if render_scene_index == 0 {
|
|
||||||
Some(self.ui_model.background_color().to_f32())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
self.renderer.device.draw_elements(6, &RenderState {
|
|
||||||
target: &self.renderer.draw_render_target(),
|
|
||||||
program: &self.ground_program.program,
|
|
||||||
vertex_array: &self.ground_vertex_array.vertex_array,
|
|
||||||
primitive: Primitive::Triangles,
|
|
||||||
textures: &[],
|
|
||||||
uniforms: &[
|
|
||||||
(&self.ground_program.transform_uniform,
|
|
||||||
UniformData::from_transform_3d(&transform)),
|
|
||||||
(&self.ground_program.ground_color_uniform,
|
|
||||||
UniformData::Vec4(GROUND_SOLID_COLOR.to_f32().0)),
|
|
||||||
(&self.ground_program.gridline_color_uniform,
|
|
||||||
UniformData::Vec4(GROUND_LINE_COLOR.to_f32().0)),
|
|
||||||
(&self.ground_program.gridline_count_uniform, UniformData::Int(GRIDLINE_COUNT)),
|
|
||||||
],
|
|
||||||
viewport: self.renderer.draw_viewport(),
|
|
||||||
options: RenderOptions {
|
|
||||||
depth: Some(DepthState { func: DepthFunc::Less, write: true }),
|
|
||||||
clear_ops: ClearOps { color: clear_color, depth: Some(1.0), stencil: Some(0) },
|
|
||||||
..RenderOptions::default()
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_vector_scene(&mut self) {
|
|
||||||
if self.ui_model.mode == Mode::TwoD {
|
|
||||||
self.renderer.disable_depth();
|
|
||||||
} else {
|
|
||||||
self.renderer.enable_depth();
|
|
||||||
}
|
|
||||||
|
|
||||||
self.renderer.begin_scene();
|
|
||||||
|
|
||||||
// Issue render commands!
|
|
||||||
for command in self.render_command_stream.as_mut().unwrap() {
|
|
||||||
self.renderer.render_command(&command);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.current_frame
|
|
||||||
.as_mut()
|
|
||||||
.unwrap()
|
|
||||||
.scene_stats
|
|
||||||
.push(self.renderer.stats);
|
|
||||||
self.renderer.end_scene();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn take_raster_screenshot(&mut self, path: PathBuf) {
|
|
||||||
let drawable_size = self.window_size.device_size();
|
|
||||||
let viewport = RectI::new(Vector2I::default(), drawable_size);
|
|
||||||
let texture_data_receiver =
|
|
||||||
self.renderer.device.read_pixels(&RenderTarget::Default, viewport);
|
|
||||||
let pixels = match self.renderer.device.recv_texture_data(&texture_data_receiver) {
|
|
||||||
TextureData::U8(pixels) => pixels,
|
|
||||||
_ => panic!("Unexpected pixel format for default framebuffer!"),
|
|
||||||
};
|
|
||||||
image::save_buffer(
|
|
||||||
path,
|
|
||||||
&pixels,
|
|
||||||
drawable_size.x() as u32,
|
|
||||||
drawable_size.y() as u32,
|
|
||||||
ColorType::Rgba8,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn draw_debug_ui(&mut self) {
|
|
||||||
if self.options.ui == UIVisibility::None {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let viewport = self.window.viewport(View::Mono);
|
|
||||||
self.window.make_current(View::Mono);
|
|
||||||
self.renderer.replace_dest_framebuffer(DestFramebuffer::Default {
|
|
||||||
viewport,
|
|
||||||
window_size: self.window_size.device_size(),
|
|
||||||
});
|
|
||||||
|
|
||||||
self.renderer.draw_debug_ui();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,656 +0,0 @@
|
||||||
// pathfinder/demo/src/ui.rs
|
|
||||||
//
|
|
||||||
// Copyright © 2019 The Pathfinder Project Developers.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
|
|
||||||
use crate::camera::Mode;
|
|
||||||
use crate::window::Window;
|
|
||||||
use crate::{BackgroundColor, Options};
|
|
||||||
use pathfinder_color::ColorU;
|
|
||||||
use pathfinder_geometry::rect::RectI;
|
|
||||||
use pathfinder_geometry::vector::{Vector2I, vec2i};
|
|
||||||
use pathfinder_gpu::Device;
|
|
||||||
use pathfinder_renderer::gpu::debug::DebugUIPresenter;
|
|
||||||
use pathfinder_resources::ResourceLoader;
|
|
||||||
use pathfinder_ui::{BUTTON_HEIGHT, BUTTON_TEXT_OFFSET, BUTTON_WIDTH, FONT_ASCENT, PADDING};
|
|
||||||
use pathfinder_ui::{TEXT_COLOR, TOOLTIP_HEIGHT, WINDOW_COLOR};
|
|
||||||
use std::f32::consts::PI;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
const SLIDER_WIDTH: i32 = 360;
|
|
||||||
const SLIDER_HEIGHT: i32 = 48;
|
|
||||||
const SLIDER_TRACK_HEIGHT: i32 = 24;
|
|
||||||
const SLIDER_KNOB_WIDTH: i32 = 12;
|
|
||||||
const SLIDER_KNOB_HEIGHT: i32 = 48;
|
|
||||||
|
|
||||||
const EFFECTS_PANEL_WIDTH: i32 = 550;
|
|
||||||
const EFFECTS_PANEL_HEIGHT: i32 = BUTTON_HEIGHT * 3 + PADDING * 4;
|
|
||||||
|
|
||||||
const BACKGROUND_PANEL_WIDTH: i32 = 250;
|
|
||||||
const BACKGROUND_PANEL_HEIGHT: i32 = BUTTON_HEIGHT * 3;
|
|
||||||
|
|
||||||
const SCREENSHOT_PANEL_WIDTH: i32 = 275;
|
|
||||||
const SCREENSHOT_PANEL_HEIGHT: i32 = BUTTON_HEIGHT * 2;
|
|
||||||
|
|
||||||
const ROTATE_PANEL_WIDTH: i32 = SLIDER_WIDTH + PADDING * 2;
|
|
||||||
const ROTATE_PANEL_HEIGHT: i32 = PADDING * 2 + SLIDER_HEIGHT;
|
|
||||||
|
|
||||||
const LIGHT_BG_COLOR: ColorU = ColorU { r: 248, g: 248, b: 248, a: 255, };
|
|
||||||
const DARK_BG_COLOR: ColorU = ColorU { r: 32, g: 32, b: 32, a: 255, };
|
|
||||||
const TRANSPARENT_BG_COLOR: ColorU = ColorU { r: 0, g: 0, b: 0, a: 0, };
|
|
||||||
|
|
||||||
static EFFECTS_PNG_NAME: &'static str = "demo-effects";
|
|
||||||
static OPEN_PNG_NAME: &'static str = "demo-open";
|
|
||||||
static ROTATE_PNG_NAME: &'static str = "demo-rotate";
|
|
||||||
static ZOOM_IN_PNG_NAME: &'static str = "demo-zoom-in";
|
|
||||||
static ZOOM_ACTUAL_SIZE_PNG_NAME: &'static str = "demo-zoom-actual-size";
|
|
||||||
static ZOOM_OUT_PNG_NAME: &'static str = "demo-zoom-out";
|
|
||||||
static BACKGROUND_PNG_NAME: &'static str = "demo-background";
|
|
||||||
static SCREENSHOT_PNG_NAME: &'static str = "demo-screenshot";
|
|
||||||
|
|
||||||
pub struct DemoUIModel {
|
|
||||||
pub mode: Mode,
|
|
||||||
pub background_color: BackgroundColor,
|
|
||||||
pub gamma_correction_effect_enabled: bool,
|
|
||||||
pub stem_darkening_effect_enabled: bool,
|
|
||||||
pub subpixel_aa_effect_enabled: bool,
|
|
||||||
pub rotation: i32,
|
|
||||||
pub message: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DemoUIModel {
|
|
||||||
pub fn new(options: &Options) -> DemoUIModel {
|
|
||||||
DemoUIModel {
|
|
||||||
mode: options.mode,
|
|
||||||
background_color: options.background_color,
|
|
||||||
gamma_correction_effect_enabled: false,
|
|
||||||
stem_darkening_effect_enabled: false,
|
|
||||||
subpixel_aa_effect_enabled: false,
|
|
||||||
rotation: SLIDER_WIDTH / 2,
|
|
||||||
message: String::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rotation(&self) -> f32 {
|
|
||||||
(self.rotation as f32 / SLIDER_WIDTH as f32 * 2.0 - 1.0) * PI
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only relevant if in monochrome mode.
|
|
||||||
pub fn foreground_color(&self) -> ColorU {
|
|
||||||
match self.background_color {
|
|
||||||
BackgroundColor::Light | BackgroundColor::Transparent => ColorU::black(),
|
|
||||||
BackgroundColor::Dark => ColorU::white(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn background_color(&self) -> ColorU {
|
|
||||||
match self.background_color {
|
|
||||||
BackgroundColor::Light => LIGHT_BG_COLOR,
|
|
||||||
BackgroundColor::Dark => DARK_BG_COLOR,
|
|
||||||
BackgroundColor::Transparent => TRANSPARENT_BG_COLOR,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct DemoUIPresenter<D>
|
|
||||||
where
|
|
||||||
D: Device,
|
|
||||||
{
|
|
||||||
effects_texture: D::Texture,
|
|
||||||
open_texture: D::Texture,
|
|
||||||
rotate_texture: D::Texture,
|
|
||||||
zoom_in_texture: D::Texture,
|
|
||||||
zoom_actual_size_texture: D::Texture,
|
|
||||||
zoom_out_texture: D::Texture,
|
|
||||||
background_texture: D::Texture,
|
|
||||||
screenshot_texture: D::Texture,
|
|
||||||
|
|
||||||
effects_panel_visible: bool,
|
|
||||||
background_panel_visible: bool,
|
|
||||||
screenshot_panel_visible: bool,
|
|
||||||
rotate_panel_visible: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<D> DemoUIPresenter<D>
|
|
||||||
where
|
|
||||||
D: Device,
|
|
||||||
{
|
|
||||||
pub fn new(device: &D, resources: &dyn ResourceLoader) -> DemoUIPresenter<D> {
|
|
||||||
let effects_texture = device.create_texture_from_png(resources, EFFECTS_PNG_NAME);
|
|
||||||
let open_texture = device.create_texture_from_png(resources, OPEN_PNG_NAME);
|
|
||||||
let rotate_texture = device.create_texture_from_png(resources, ROTATE_PNG_NAME);
|
|
||||||
let zoom_in_texture = device.create_texture_from_png(resources, ZOOM_IN_PNG_NAME);
|
|
||||||
let zoom_actual_size_texture = device.create_texture_from_png(resources,
|
|
||||||
ZOOM_ACTUAL_SIZE_PNG_NAME);
|
|
||||||
let zoom_out_texture = device.create_texture_from_png(resources, ZOOM_OUT_PNG_NAME);
|
|
||||||
let background_texture = device.create_texture_from_png(resources, BACKGROUND_PNG_NAME);
|
|
||||||
let screenshot_texture = device.create_texture_from_png(resources, SCREENSHOT_PNG_NAME);
|
|
||||||
|
|
||||||
DemoUIPresenter {
|
|
||||||
effects_texture,
|
|
||||||
open_texture,
|
|
||||||
rotate_texture,
|
|
||||||
zoom_in_texture,
|
|
||||||
zoom_actual_size_texture,
|
|
||||||
zoom_out_texture,
|
|
||||||
background_texture,
|
|
||||||
screenshot_texture,
|
|
||||||
|
|
||||||
effects_panel_visible: false,
|
|
||||||
background_panel_visible: false,
|
|
||||||
screenshot_panel_visible: false,
|
|
||||||
rotate_panel_visible: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update<W>(
|
|
||||||
&mut self,
|
|
||||||
device: &D,
|
|
||||||
window: &mut W,
|
|
||||||
debug_ui_presenter: &mut DebugUIPresenter<D>,
|
|
||||||
action: &mut UIAction,
|
|
||||||
model: &mut DemoUIModel
|
|
||||||
) where
|
|
||||||
W: Window,
|
|
||||||
{
|
|
||||||
// Draw message text.
|
|
||||||
|
|
||||||
self.draw_message_text(device, debug_ui_presenter, model);
|
|
||||||
|
|
||||||
// Draw button strip.
|
|
||||||
|
|
||||||
let bottom = debug_ui_presenter.ui_presenter.framebuffer_size().y() - PADDING;
|
|
||||||
let mut position = vec2i(PADDING, bottom - BUTTON_HEIGHT);
|
|
||||||
|
|
||||||
let button_size = vec2i(BUTTON_WIDTH, BUTTON_HEIGHT);
|
|
||||||
|
|
||||||
// Draw effects button.
|
|
||||||
if debug_ui_presenter.ui_presenter.draw_button(device, position, &self.effects_texture) {
|
|
||||||
self.effects_panel_visible = !self.effects_panel_visible;
|
|
||||||
}
|
|
||||||
if !self.effects_panel_visible {
|
|
||||||
debug_ui_presenter.ui_presenter.draw_tooltip(
|
|
||||||
device,
|
|
||||||
"Effects",
|
|
||||||
RectI::new(position, button_size),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
position += vec2i(button_size.x() + PADDING, 0);
|
|
||||||
|
|
||||||
// Draw open button.
|
|
||||||
if debug_ui_presenter.ui_presenter.draw_button(device, position, &self.open_texture) {
|
|
||||||
// FIXME(pcwalton): This is not sufficient for Android, where we will need to take in
|
|
||||||
// the contents of the file.
|
|
||||||
window.present_open_svg_dialog();
|
|
||||||
}
|
|
||||||
debug_ui_presenter.ui_presenter.draw_tooltip(device,
|
|
||||||
"Open SVG",
|
|
||||||
RectI::new(position, button_size));
|
|
||||||
position += vec2i(BUTTON_WIDTH + PADDING, 0);
|
|
||||||
|
|
||||||
// Draw screenshot button.
|
|
||||||
if debug_ui_presenter.ui_presenter.draw_button(device,
|
|
||||||
position,
|
|
||||||
&self.screenshot_texture) {
|
|
||||||
self.screenshot_panel_visible = !self.screenshot_panel_visible;
|
|
||||||
}
|
|
||||||
if !self.screenshot_panel_visible {
|
|
||||||
debug_ui_presenter.ui_presenter.draw_tooltip(
|
|
||||||
device,
|
|
||||||
"Take Screenshot",
|
|
||||||
RectI::new(position, button_size),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw screenshot panel, if necessary.
|
|
||||||
self.draw_screenshot_panel(device, window, debug_ui_presenter, position.x(), action);
|
|
||||||
position += vec2i(button_size.x() + PADDING, 0);
|
|
||||||
|
|
||||||
// Draw mode switch.
|
|
||||||
let new_mode = debug_ui_presenter.ui_presenter.draw_text_switch(
|
|
||||||
device,
|
|
||||||
position,
|
|
||||||
&["2D", "3D", "VR"],
|
|
||||||
model.mode as u8);
|
|
||||||
if new_mode != model.mode as u8 {
|
|
||||||
model.mode = match new_mode {
|
|
||||||
0 => Mode::TwoD,
|
|
||||||
1 => Mode::ThreeD,
|
|
||||||
_ => Mode::VR,
|
|
||||||
};
|
|
||||||
*action = UIAction::ModelChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mode_switch_width = debug_ui_presenter.ui_presenter.measure_segmented_control(3);
|
|
||||||
let mode_switch_size = vec2i(mode_switch_width, BUTTON_HEIGHT);
|
|
||||||
debug_ui_presenter.ui_presenter.draw_tooltip(
|
|
||||||
device,
|
|
||||||
"2D/3D/VR Mode",
|
|
||||||
RectI::new(position, mode_switch_size),
|
|
||||||
);
|
|
||||||
position += vec2i(mode_switch_width + PADDING, 0);
|
|
||||||
|
|
||||||
// Draw background switch.
|
|
||||||
if debug_ui_presenter.ui_presenter.draw_button(device,
|
|
||||||
position,
|
|
||||||
&self.background_texture) {
|
|
||||||
self.background_panel_visible = !self.background_panel_visible;
|
|
||||||
}
|
|
||||||
if !self.background_panel_visible {
|
|
||||||
debug_ui_presenter.ui_presenter.draw_tooltip(
|
|
||||||
device,
|
|
||||||
"Background Color",
|
|
||||||
RectI::new(position, button_size),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw background panel, if necessary.
|
|
||||||
self.draw_background_panel(device, debug_ui_presenter, position.x(), action, model);
|
|
||||||
position += vec2i(button_size.x() + PADDING, 0);
|
|
||||||
|
|
||||||
// Draw effects panel, if necessary.
|
|
||||||
self.draw_effects_panel(device, debug_ui_presenter, model, action);
|
|
||||||
|
|
||||||
// Draw rotate and zoom buttons, if applicable.
|
|
||||||
if model.mode != Mode::TwoD {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if debug_ui_presenter.ui_presenter.draw_button(device, position, &self.rotate_texture) {
|
|
||||||
self.rotate_panel_visible = !self.rotate_panel_visible;
|
|
||||||
}
|
|
||||||
if !self.rotate_panel_visible {
|
|
||||||
debug_ui_presenter.ui_presenter.draw_tooltip(device,
|
|
||||||
"Rotate",
|
|
||||||
RectI::new(position, button_size));
|
|
||||||
}
|
|
||||||
self.draw_rotate_panel(device, debug_ui_presenter, position.x(), action, model);
|
|
||||||
position += vec2i(BUTTON_WIDTH + PADDING, 0);
|
|
||||||
|
|
||||||
// Draw zoom control.
|
|
||||||
self.draw_zoom_control(device, debug_ui_presenter, position, action);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw_zoom_control(
|
|
||||||
&mut self,
|
|
||||||
device: &D,
|
|
||||||
debug_ui_presenter: &mut DebugUIPresenter<D>,
|
|
||||||
position: Vector2I,
|
|
||||||
action: &mut UIAction,
|
|
||||||
) {
|
|
||||||
let zoom_segmented_control_width =
|
|
||||||
debug_ui_presenter.ui_presenter.measure_segmented_control(3);
|
|
||||||
let zoom_segmented_control_rect =
|
|
||||||
RectI::new(position, vec2i(zoom_segmented_control_width, BUTTON_HEIGHT));
|
|
||||||
debug_ui_presenter.ui_presenter.draw_tooltip(device, "Zoom", zoom_segmented_control_rect);
|
|
||||||
|
|
||||||
let zoom_textures = &[
|
|
||||||
&self.zoom_in_texture,
|
|
||||||
&self.zoom_actual_size_texture,
|
|
||||||
&self.zoom_out_texture
|
|
||||||
];
|
|
||||||
|
|
||||||
match debug_ui_presenter.ui_presenter.draw_image_segmented_control(device,
|
|
||||||
position,
|
|
||||||
zoom_textures,
|
|
||||||
None) {
|
|
||||||
Some(0) => *action = UIAction::ZoomIn,
|
|
||||||
Some(1) => *action = UIAction::ZoomActualSize,
|
|
||||||
Some(2) => *action = UIAction::ZoomOut,
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw_message_text(&mut self,
|
|
||||||
device: &D,
|
|
||||||
debug_ui_presenter: &mut DebugUIPresenter<D>,
|
|
||||||
model: &mut DemoUIModel) {
|
|
||||||
if model.message.is_empty() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let message_size = debug_ui_presenter.ui_presenter.measure_text(&model.message);
|
|
||||||
let window_origin = vec2i(PADDING, PADDING);
|
|
||||||
let window_size = vec2i(PADDING * 2 + message_size, TOOLTIP_HEIGHT);
|
|
||||||
debug_ui_presenter.ui_presenter.draw_solid_rounded_rect(
|
|
||||||
device,
|
|
||||||
RectI::new(window_origin, window_size),
|
|
||||||
WINDOW_COLOR,
|
|
||||||
);
|
|
||||||
debug_ui_presenter.ui_presenter.draw_text(
|
|
||||||
device,
|
|
||||||
&model.message,
|
|
||||||
window_origin + vec2i(PADDING, PADDING + FONT_ASCENT),
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw_effects_panel(&mut self,
|
|
||||||
device: &D,
|
|
||||||
debug_ui_presenter: &mut DebugUIPresenter<D>,
|
|
||||||
model: &mut DemoUIModel,
|
|
||||||
action: &mut UIAction) {
|
|
||||||
if !self.effects_panel_visible {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let bottom = debug_ui_presenter.ui_presenter.framebuffer_size().y() - PADDING;
|
|
||||||
let effects_panel_y = bottom - (BUTTON_HEIGHT + PADDING + EFFECTS_PANEL_HEIGHT);
|
|
||||||
debug_ui_presenter.ui_presenter.draw_solid_rounded_rect(
|
|
||||||
device,
|
|
||||||
RectI::new(vec2i(PADDING, effects_panel_y),
|
|
||||||
vec2i(EFFECTS_PANEL_WIDTH, EFFECTS_PANEL_HEIGHT)),
|
|
||||||
WINDOW_COLOR,
|
|
||||||
);
|
|
||||||
|
|
||||||
self.draw_effects_switch(
|
|
||||||
device,
|
|
||||||
action,
|
|
||||||
debug_ui_presenter,
|
|
||||||
"Gamma Correction",
|
|
||||||
0,
|
|
||||||
effects_panel_y,
|
|
||||||
&mut model.gamma_correction_effect_enabled);
|
|
||||||
self.draw_effects_switch(
|
|
||||||
device,
|
|
||||||
action,
|
|
||||||
debug_ui_presenter,
|
|
||||||
"Stem Darkening",
|
|
||||||
1,
|
|
||||||
effects_panel_y,
|
|
||||||
&mut model.stem_darkening_effect_enabled);
|
|
||||||
self.draw_effects_switch(
|
|
||||||
device,
|
|
||||||
action,
|
|
||||||
debug_ui_presenter,
|
|
||||||
"Subpixel AA",
|
|
||||||
2,
|
|
||||||
effects_panel_y,
|
|
||||||
&mut model.subpixel_aa_effect_enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw_screenshot_panel<W>(
|
|
||||||
&mut self,
|
|
||||||
device: &D,
|
|
||||||
window: &mut W,
|
|
||||||
debug_ui_presenter: &mut DebugUIPresenter<D>,
|
|
||||||
panel_x: i32,
|
|
||||||
action: &mut UIAction,
|
|
||||||
) where W: Window {
|
|
||||||
if !self.screenshot_panel_visible {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let bottom = debug_ui_presenter.ui_presenter.framebuffer_size().y() - PADDING;
|
|
||||||
let panel_y = bottom - (BUTTON_HEIGHT + PADDING + SCREENSHOT_PANEL_HEIGHT);
|
|
||||||
let panel_position = vec2i(panel_x, panel_y);
|
|
||||||
debug_ui_presenter.ui_presenter.draw_solid_rounded_rect(
|
|
||||||
device,
|
|
||||||
RectI::new(panel_position, vec2i(SCREENSHOT_PANEL_WIDTH, SCREENSHOT_PANEL_HEIGHT)),
|
|
||||||
WINDOW_COLOR,
|
|
||||||
);
|
|
||||||
|
|
||||||
self.draw_screenshot_menu_item(
|
|
||||||
device,
|
|
||||||
window,
|
|
||||||
debug_ui_presenter,
|
|
||||||
ScreenshotType::PNG,
|
|
||||||
panel_position,
|
|
||||||
action,
|
|
||||||
);
|
|
||||||
self.draw_screenshot_menu_item(
|
|
||||||
device,
|
|
||||||
window,
|
|
||||||
debug_ui_presenter,
|
|
||||||
ScreenshotType::SVG,
|
|
||||||
panel_position,
|
|
||||||
action,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw_background_panel(
|
|
||||||
&mut self,
|
|
||||||
device: &D,
|
|
||||||
debug_ui_presenter: &mut DebugUIPresenter<D>,
|
|
||||||
panel_x: i32,
|
|
||||||
action: &mut UIAction,
|
|
||||||
model: &mut DemoUIModel,
|
|
||||||
) {
|
|
||||||
if !self.background_panel_visible {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let bottom = debug_ui_presenter.ui_presenter.framebuffer_size().y() - PADDING;
|
|
||||||
let panel_y = bottom - (BUTTON_HEIGHT + PADDING + BACKGROUND_PANEL_HEIGHT);
|
|
||||||
let panel_position = vec2i(panel_x, panel_y);
|
|
||||||
debug_ui_presenter.ui_presenter.draw_solid_rounded_rect(
|
|
||||||
device,
|
|
||||||
RectI::new(panel_position, vec2i(BACKGROUND_PANEL_WIDTH, BACKGROUND_PANEL_HEIGHT)),
|
|
||||||
WINDOW_COLOR,
|
|
||||||
);
|
|
||||||
|
|
||||||
self.draw_background_menu_item(
|
|
||||||
device,
|
|
||||||
debug_ui_presenter,
|
|
||||||
BackgroundColor::Light,
|
|
||||||
panel_position,
|
|
||||||
action,
|
|
||||||
model,
|
|
||||||
);
|
|
||||||
self.draw_background_menu_item(
|
|
||||||
device,
|
|
||||||
debug_ui_presenter,
|
|
||||||
BackgroundColor::Dark,
|
|
||||||
panel_position,
|
|
||||||
action,
|
|
||||||
model,
|
|
||||||
);
|
|
||||||
self.draw_background_menu_item(
|
|
||||||
device,
|
|
||||||
debug_ui_presenter,
|
|
||||||
BackgroundColor::Transparent,
|
|
||||||
panel_position,
|
|
||||||
action,
|
|
||||||
model,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw_rotate_panel(
|
|
||||||
&mut self,
|
|
||||||
device: &D,
|
|
||||||
debug_ui_presenter: &mut DebugUIPresenter<D>,
|
|
||||||
rotate_panel_x: i32,
|
|
||||||
action: &mut UIAction,
|
|
||||||
model: &mut DemoUIModel
|
|
||||||
) {
|
|
||||||
if !self.rotate_panel_visible {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let bottom = debug_ui_presenter.ui_presenter.framebuffer_size().y() - PADDING;
|
|
||||||
let rotate_panel_y = bottom - (BUTTON_HEIGHT + PADDING + ROTATE_PANEL_HEIGHT);
|
|
||||||
let rotate_panel_origin = vec2i(rotate_panel_x, rotate_panel_y);
|
|
||||||
let rotate_panel_size = vec2i(ROTATE_PANEL_WIDTH, ROTATE_PANEL_HEIGHT);
|
|
||||||
debug_ui_presenter.ui_presenter.draw_solid_rounded_rect(
|
|
||||||
device,
|
|
||||||
RectI::new(rotate_panel_origin, rotate_panel_size),
|
|
||||||
WINDOW_COLOR,
|
|
||||||
);
|
|
||||||
|
|
||||||
let (widget_x, widget_y) = (rotate_panel_x + PADDING, rotate_panel_y + PADDING);
|
|
||||||
let widget_rect = RectI::new(vec2i(widget_x, widget_y),
|
|
||||||
vec2i(SLIDER_WIDTH, SLIDER_KNOB_HEIGHT));
|
|
||||||
if let Some(position) = debug_ui_presenter
|
|
||||||
.ui_presenter
|
|
||||||
.event_queue
|
|
||||||
.handle_mouse_down_or_dragged_in_rect(widget_rect)
|
|
||||||
{
|
|
||||||
model.rotation = position.x();
|
|
||||||
*action = UIAction::Rotate(model.rotation());
|
|
||||||
}
|
|
||||||
|
|
||||||
let slider_track_y =
|
|
||||||
rotate_panel_y + PADDING + SLIDER_KNOB_HEIGHT / 2 - SLIDER_TRACK_HEIGHT / 2;
|
|
||||||
let slider_track_rect = RectI::new(vec2i(widget_x, slider_track_y),
|
|
||||||
vec2i(SLIDER_WIDTH, SLIDER_TRACK_HEIGHT));
|
|
||||||
debug_ui_presenter.ui_presenter.draw_rect_outline(device, slider_track_rect, TEXT_COLOR);
|
|
||||||
|
|
||||||
let slider_knob_x = widget_x + model.rotation - SLIDER_KNOB_WIDTH / 2;
|
|
||||||
let slider_knob_rect = RectI::new(vec2i(slider_knob_x, widget_y),
|
|
||||||
vec2i(SLIDER_KNOB_WIDTH, SLIDER_KNOB_HEIGHT));
|
|
||||||
debug_ui_presenter.ui_presenter.draw_solid_rect(device, slider_knob_rect, TEXT_COLOR);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw_screenshot_menu_item<W>(
|
|
||||||
&mut self,
|
|
||||||
device: &D,
|
|
||||||
window: &mut W,
|
|
||||||
debug_ui_presenter: &mut DebugUIPresenter<D>,
|
|
||||||
screenshot_type: ScreenshotType,
|
|
||||||
panel_position: Vector2I,
|
|
||||||
action: &mut UIAction,
|
|
||||||
) where W: Window {
|
|
||||||
let index = screenshot_type as i32;
|
|
||||||
let text = format!("Save as {}...", screenshot_type.as_str());
|
|
||||||
|
|
||||||
let widget_size = vec2i(BACKGROUND_PANEL_WIDTH, BUTTON_HEIGHT);
|
|
||||||
let widget_origin = panel_position + vec2i(0, widget_size.y() * index);
|
|
||||||
let widget_rect = RectI::new(widget_origin, widget_size);
|
|
||||||
|
|
||||||
if self.draw_menu_item(device,
|
|
||||||
debug_ui_presenter,
|
|
||||||
&text,
|
|
||||||
widget_rect,
|
|
||||||
false) {
|
|
||||||
// FIXME(pcwalton): This is not sufficient for Android, where we will need to take in
|
|
||||||
// the contents of the file.
|
|
||||||
if let Ok(path) = window.run_save_dialog(screenshot_type.extension()) {
|
|
||||||
self.screenshot_panel_visible = false;
|
|
||||||
*action = UIAction::TakeScreenshot(ScreenshotInfo { kind: screenshot_type, path });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw_background_menu_item(
|
|
||||||
&mut self,
|
|
||||||
device: &D,
|
|
||||||
debug_ui_presenter: &mut DebugUIPresenter<D>,
|
|
||||||
color: BackgroundColor,
|
|
||||||
panel_position: Vector2I,
|
|
||||||
action: &mut UIAction,
|
|
||||||
model: &mut DemoUIModel,
|
|
||||||
) {
|
|
||||||
let (text, index) = (color.as_str(), color as i32);
|
|
||||||
|
|
||||||
let widget_size = vec2i(BACKGROUND_PANEL_WIDTH, BUTTON_HEIGHT);
|
|
||||||
let widget_origin = panel_position + vec2i(0, widget_size.y() * index);
|
|
||||||
let widget_rect = RectI::new(widget_origin, widget_size);
|
|
||||||
|
|
||||||
let selected = color == model.background_color;
|
|
||||||
if self.draw_menu_item(device,
|
|
||||||
debug_ui_presenter,
|
|
||||||
text,
|
|
||||||
widget_rect,
|
|
||||||
selected) {
|
|
||||||
model.background_color = color;
|
|
||||||
*action = UIAction::ModelChanged;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw_menu_item(&self,
|
|
||||||
device: &D,
|
|
||||||
debug_ui_presenter: &mut DebugUIPresenter<D>,
|
|
||||||
text: &str,
|
|
||||||
widget_rect: RectI,
|
|
||||||
selected: bool)
|
|
||||||
-> bool {
|
|
||||||
if selected {
|
|
||||||
debug_ui_presenter.ui_presenter.draw_solid_rounded_rect(device,
|
|
||||||
widget_rect,
|
|
||||||
TEXT_COLOR);
|
|
||||||
}
|
|
||||||
|
|
||||||
let (text_x, text_y) = (PADDING * 2, BUTTON_TEXT_OFFSET);
|
|
||||||
let text_position = widget_rect.origin() + vec2i(text_x, text_y);
|
|
||||||
debug_ui_presenter.ui_presenter.draw_text(device, text, text_position, selected);
|
|
||||||
|
|
||||||
debug_ui_presenter.ui_presenter
|
|
||||||
.event_queue
|
|
||||||
.handle_mouse_down_in_rect(widget_rect)
|
|
||||||
.is_some()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw_effects_switch(
|
|
||||||
&self,
|
|
||||||
device: &D,
|
|
||||||
action: &mut UIAction,
|
|
||||||
debug_ui_presenter: &mut DebugUIPresenter<D>,
|
|
||||||
text: &str,
|
|
||||||
index: i32,
|
|
||||||
window_y: i32,
|
|
||||||
value: &mut bool) {
|
|
||||||
let text_x = PADDING * 2;
|
|
||||||
let text_y = window_y + PADDING + BUTTON_TEXT_OFFSET + (BUTTON_HEIGHT + PADDING) * index;
|
|
||||||
debug_ui_presenter.ui_presenter.draw_text(device, text, vec2i(text_x, text_y), false);
|
|
||||||
|
|
||||||
let switch_width = debug_ui_presenter.ui_presenter.measure_segmented_control(2);
|
|
||||||
let switch_x = PADDING + EFFECTS_PANEL_WIDTH - (switch_width + PADDING);
|
|
||||||
let switch_y = window_y + PADDING + (BUTTON_HEIGHT + PADDING) * index;
|
|
||||||
let switch_position = vec2i(switch_x, switch_y);
|
|
||||||
|
|
||||||
let new_value =
|
|
||||||
debug_ui_presenter
|
|
||||||
.ui_presenter
|
|
||||||
.draw_text_switch(device, switch_position, &["Off", "On"], *value as u8) != 0;
|
|
||||||
|
|
||||||
if new_value != *value {
|
|
||||||
*action = UIAction::EffectsChanged;
|
|
||||||
*value = new_value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub enum UIAction {
|
|
||||||
None,
|
|
||||||
ModelChanged,
|
|
||||||
EffectsChanged,
|
|
||||||
TakeScreenshot(ScreenshotInfo),
|
|
||||||
ZoomIn,
|
|
||||||
ZoomActualSize,
|
|
||||||
ZoomOut,
|
|
||||||
Rotate(f32),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub struct ScreenshotInfo {
|
|
||||||
pub kind: ScreenshotType,
|
|
||||||
pub path: PathBuf,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
||||||
pub enum ScreenshotType {
|
|
||||||
PNG = 0,
|
|
||||||
SVG = 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ScreenshotType {
|
|
||||||
fn extension(&self) -> &'static str {
|
|
||||||
match *self {
|
|
||||||
ScreenshotType::PNG => "png",
|
|
||||||
ScreenshotType::SVG => "svg",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_str(&self) -> &'static str {
|
|
||||||
match *self {
|
|
||||||
ScreenshotType::PNG => "PNG",
|
|
||||||
ScreenshotType::SVG => "SVG",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,117 +0,0 @@
|
||||||
// pathfinder/demo/common/src/window.rs
|
|
||||||
//
|
|
||||||
// Copyright © 2019 The Pathfinder Project Developers.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
|
|
||||||
//! A minimal cross-platform windowing layer.
|
|
||||||
|
|
||||||
use pathfinder_geometry::rect::RectI;
|
|
||||||
use pathfinder_geometry::transform3d::{Perspective, Transform4F};
|
|
||||||
use pathfinder_geometry::vector::Vector2I;
|
|
||||||
use pathfinder_resources::ResourceLoader;
|
|
||||||
use rayon::ThreadPoolBuilder;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
#[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
|
|
||||||
use metal::CoreAnimationLayerRef;
|
|
||||||
#[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
|
|
||||||
use pathfinder_metal::MetalDevice;
|
|
||||||
|
|
||||||
#[cfg(any(not(target_os = "macos"), feature = "pf-gl"))]
|
|
||||||
use gl::types::GLuint;
|
|
||||||
#[cfg(any(not(target_os = "macos"), feature = "pf-gl"))]
|
|
||||||
use pathfinder_gl::{GLDevice, GLVersion};
|
|
||||||
|
|
||||||
pub trait Window {
|
|
||||||
#[cfg(any(not(target_os = "macos"), feature = "pf-gl"))]
|
|
||||||
fn gl_version(&self) -> GLVersion;
|
|
||||||
#[cfg(any(not(target_os = "macos"), feature = "pf-gl"))]
|
|
||||||
fn gl_default_framebuffer(&self) -> GLuint { 0 }
|
|
||||||
#[cfg(any(not(target_os = "macos"), feature = "pf-gl"))]
|
|
||||||
fn present(&mut self, device: &mut GLDevice);
|
|
||||||
|
|
||||||
#[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
|
|
||||||
fn metal_layer(&self) -> &CoreAnimationLayerRef;
|
|
||||||
#[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
|
|
||||||
fn present(&mut self, device: &mut MetalDevice);
|
|
||||||
|
|
||||||
fn make_current(&mut self, view: View);
|
|
||||||
fn viewport(&self, view: View) -> RectI;
|
|
||||||
fn resource_loader(&self) -> &dyn ResourceLoader;
|
|
||||||
fn create_user_event_id(&self) -> u32;
|
|
||||||
fn push_user_event(message_type: u32, message_data: u32);
|
|
||||||
fn present_open_svg_dialog(&mut self);
|
|
||||||
fn run_save_dialog(&self, extension: &str) -> Result<PathBuf, ()>;
|
|
||||||
|
|
||||||
fn adjust_thread_pool_settings(&self, builder: ThreadPoolBuilder) -> ThreadPoolBuilder {
|
|
||||||
builder
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum Event {
|
|
||||||
Quit,
|
|
||||||
WindowResized(WindowSize),
|
|
||||||
KeyDown(Keycode),
|
|
||||||
KeyUp(Keycode),
|
|
||||||
MouseDown(Vector2I),
|
|
||||||
MouseMoved(Vector2I),
|
|
||||||
MouseDragged(Vector2I),
|
|
||||||
Zoom(f32, Vector2I),
|
|
||||||
Look {
|
|
||||||
pitch: f32,
|
|
||||||
yaw: f32,
|
|
||||||
},
|
|
||||||
SetEyeTransforms(Vec<OcularTransform>),
|
|
||||||
OpenSVG(SVGPath),
|
|
||||||
User {
|
|
||||||
message_type: u32,
|
|
||||||
message_data: u32,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum Keycode {
|
|
||||||
Alphanumeric(u8),
|
|
||||||
Escape,
|
|
||||||
Tab,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub struct WindowSize {
|
|
||||||
pub logical_size: Vector2I,
|
|
||||||
pub backing_scale_factor: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WindowSize {
|
|
||||||
#[inline]
|
|
||||||
pub fn device_size(&self) -> Vector2I {
|
|
||||||
(self.logical_size.to_f32() * self.backing_scale_factor).to_i32()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub enum View {
|
|
||||||
Mono,
|
|
||||||
Stereo(u32),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub struct OcularTransform {
|
|
||||||
// The perspective which converts from camera coordinates to display coordinates
|
|
||||||
pub perspective: Perspective,
|
|
||||||
|
|
||||||
// The view transform which converts from world coordinates to camera coordinates
|
|
||||||
pub modelview_to_eye: Transform4F,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub enum SVGPath {
|
|
||||||
Default,
|
|
||||||
Resource(String),
|
|
||||||
Path(PathBuf),
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
[target.aarch64-linux-android]
|
|
||||||
linker = "./fake-ld.sh"
|
|
||||||
ar = "aarch64-linux-android-ar"
|
|
||||||
rustflags = "-Clinker-flavor=ld"
|
|
1
crates/pathfinder/demo/magicleap/.gitignore
vendored
|
@ -1 +0,0 @@
|
||||||
.out/
|
|
|
@ -1,51 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "pathfinder_magicleap_demo"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2018"
|
|
||||||
authors = ["Alan Jeffrey <ajeffrey@mozilla.com>"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
gl = "0.14"
|
|
||||||
rayon = "1.0"
|
|
||||||
usvg = "0.9"
|
|
||||||
egl = "0.2"
|
|
||||||
log = "0.4"
|
|
||||||
smallvec = "1.2"
|
|
||||||
glutin = { version = "0.23", optional = true }
|
|
||||||
crossbeam-channel = "0.4"
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
crate-type = ["cdylib"]
|
|
||||||
|
|
||||||
[features]
|
|
||||||
mocked = ["glutin"]
|
|
||||||
|
|
||||||
[dependencies.pathfinder_color]
|
|
||||||
path = "../../color"
|
|
||||||
|
|
||||||
[dependencies.pathfinder_content]
|
|
||||||
path = "../../content"
|
|
||||||
|
|
||||||
[dependencies.pathfinder_demo]
|
|
||||||
path = "../common"
|
|
||||||
|
|
||||||
[dependencies.pathfinder_geometry]
|
|
||||||
path = "../../geometry"
|
|
||||||
|
|
||||||
[dependencies.pathfinder_gl]
|
|
||||||
path = "../../gl"
|
|
||||||
|
|
||||||
[dependencies.pathfinder_gpu]
|
|
||||||
path = "../../gpu"
|
|
||||||
|
|
||||||
[dependencies.pathfinder_renderer]
|
|
||||||
path = "../../renderer"
|
|
||||||
|
|
||||||
[dependencies.pathfinder_simd]
|
|
||||||
path = "../../simd"
|
|
||||||
|
|
||||||
[dependencies.pathfinder_svg]
|
|
||||||
path = "../../svg"
|
|
||||||
|
|
||||||
[dependencies.pathfinder_ui]
|
|
||||||
path = "../../ui"
|
|
|
@ -1,15 +0,0 @@
|
||||||
.PHONY: debug release install
|
|
||||||
|
|
||||||
debug:
|
|
||||||
cargo build --target aarch64-linux-android
|
|
||||||
$(MAGICLEAP_SDK)/mabu -t debug_device PathfinderDemo.package -s $(MLCERT)
|
|
||||||
|
|
||||||
release:
|
|
||||||
cargo build --release --target aarch64-linux-android
|
|
||||||
$(MAGICLEAP_SDK)/mabu -t release_device PathfinderDemo.package -s $(MLCERT)
|
|
||||||
|
|
||||||
install: .out/PathfinderDemo/PathfinderDemo.mpk
|
|
||||||
$(MAGICLEAP_SDK)/tools/mldb/mldb install -u .out/PathfinderDemo/PathfinderDemo.mpk
|
|
||||||
|
|
||||||
run:
|
|
||||||
${MAGICLEAP_SDK}/tools/mldb/mldb launch -w com.mozilla.pathfinder.demo
|
|
|
@ -1,19 +0,0 @@
|
||||||
KIND = program
|
|
||||||
SRCS = src/main.cpp
|
|
||||||
|
|
||||||
LIBPATHS.debug = \
|
|
||||||
../../target/aarch64-linux-android/debug
|
|
||||||
|
|
||||||
LIBPATHS.release = \
|
|
||||||
../../target/aarch64-linux-android/release
|
|
||||||
|
|
||||||
USES = ml_sdk OpenGL stdc++
|
|
||||||
|
|
||||||
STLIBS = \
|
|
||||||
pathfinder_immersive_demo
|
|
||||||
|
|
||||||
SHLIBS = \
|
|
||||||
ml_privileges
|
|
||||||
|
|
||||||
DATAS = \
|
|
||||||
../../resources/** : resources/
|
|
|
@ -1,8 +0,0 @@
|
||||||
USES = "lre/scenes"
|
|
||||||
|
|
||||||
REFS = PathfinderImmersiveDemo PathfinderLandscapeDemo
|
|
||||||
|
|
||||||
OPTIONS=package/debuggable/on
|
|
||||||
|
|
||||||
DATAS = \
|
|
||||||
../../target/aarch64-linux-android/release/*.so : bin/
|
|
|
@ -1,17 +0,0 @@
|
||||||
KIND = program
|
|
||||||
SRCS = src/main.cpp
|
|
||||||
|
|
||||||
LIBPATHS.debug = \
|
|
||||||
../../target/aarch64-linux-android/debug
|
|
||||||
|
|
||||||
LIBPATHS.release = \
|
|
||||||
../../target/aarch64-linux-android/release
|
|
||||||
|
|
||||||
USES = ml_sdk OpenGL stdc++
|
|
||||||
|
|
||||||
SHLIBS = \
|
|
||||||
pathfinder_magicleap_demo \
|
|
||||||
ml_privileges
|
|
||||||
|
|
||||||
DATAS = \
|
|
||||||
../../resources/** : resources/
|
|
|
@ -1,19 +0,0 @@
|
||||||
KIND = program
|
|
||||||
SRCS = src/landscape.cpp
|
|
||||||
|
|
||||||
LIBPATHS.debug = \
|
|
||||||
../../target/aarch64-linux-android/debug
|
|
||||||
|
|
||||||
LIBPATHS.release = \
|
|
||||||
../../target/aarch64-linux-android/release
|
|
||||||
|
|
||||||
INCS = \
|
|
||||||
src/ \
|
|
||||||
lre/code/inc/gen/
|
|
||||||
|
|
||||||
USES = \
|
|
||||||
lumin_runtime \
|
|
||||||
lre/code/srcs
|
|
||||||
|
|
||||||
SHLIBS = \
|
|
||||||
pathfinder_magicleap_demo
|
|
|
@ -1,25 +0,0 @@
|
||||||
# Magic Leap demo
|
|
||||||
|
|
||||||
First, install v0.20.0 or later of the Magic Leap SDK. By default this is installed in `MagicLeap/mlsdk/<version>`, for example:
|
|
||||||
```
|
|
||||||
export MAGICLEAP_SDK=~/MagicLeap/mlsdk/v0.20.0
|
|
||||||
```
|
|
||||||
You will also need a signing certificate.
|
|
||||||
```
|
|
||||||
export MLCERT=~/MagicLeap/cert/mycert.cert
|
|
||||||
```
|
|
||||||
|
|
||||||
Now build the pathfinder demo library and `.mpk` archive:
|
|
||||||
```
|
|
||||||
cd demo/pathfinder
|
|
||||||
make release
|
|
||||||
```
|
|
||||||
|
|
||||||
The `.mpk` can be installed:
|
|
||||||
```
|
|
||||||
make install
|
|
||||||
```
|
|
||||||
and run:
|
|
||||||
```
|
|
||||||
make run
|
|
||||||
```
|
|
|
@ -1,18 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# This shell script strips out the -landroid that is passed by default by rustc to
|
|
||||||
# the linker on aarch64-linux-android, and adds some entries to the ld search path.
|
|
||||||
|
|
||||||
set -o errexit
|
|
||||||
set -o nounset
|
|
||||||
set -o pipefail
|
|
||||||
|
|
||||||
TARGET=${TARGET:-"aarch64-linux-android"}
|
|
||||||
LD=${LD:-"${MAGICLEAP_SDK}/tools/toolchains/bin/${TARGET}-ld"}
|
|
||||||
LDFLAGS=${LDFLAGS:-"--sysroot=${MAGICLEAP_SDK}/lumin -L${MAGICLEAP_SDK}/lumin/usr/lib -L${MAGICLEAP_SDK}/tools/toolchains/lib/gcc/${TARGET}/4.9.x ${MAGICLEAP_SDK}/lumin/usr/lib/crtbegin_so.o"}
|
|
||||||
|
|
||||||
# Remove the -landroid flag, grr
|
|
||||||
ARGS=("$@")
|
|
||||||
ARGS=${ARGS[@]/-landroid}
|
|
||||||
|
|
||||||
${LD} ${LDFLAGS} ${ARGS}
|
|
12
crates/pathfinder/demo/magicleap/lre/.gitignore
vendored
|
@ -1,12 +0,0 @@
|
||||||
.DS_Store
|
|
||||||
*.log
|
|
||||||
*.json.dirty
|
|
||||||
*.json.lock
|
|
||||||
*.pyc
|
|
||||||
*.sln
|
|
||||||
*.vcxproj*
|
|
||||||
|
|
||||||
pipeline/cache/intermediate/
|
|
||||||
.out/
|
|
||||||
.vscode/
|
|
||||||
.vs/
|
|
|
@ -1,13 +0,0 @@
|
||||||
KIND = program
|
|
||||||
|
|
||||||
INCS = \
|
|
||||||
code/inc/ \
|
|
||||||
code/inc/gen/
|
|
||||||
|
|
||||||
SRCS = \
|
|
||||||
code/src/main.cpp \
|
|
||||||
code/src/PathfinderDemo.cpp
|
|
||||||
|
|
||||||
USES = \
|
|
||||||
lumin_runtime \
|
|
||||||
code/srcs
|
|
|
@ -1,10 +0,0 @@
|
||||||
<?xml version="1.0" encoding="ASCII"?>
|
|
||||||
<mlproject:mlproject xmlns:mlproject="http://www.magicleap.com/uidesigner/mlproject" generated="true" srcGenVersion="1">
|
|
||||||
<designFile path="scenes/PathfinderDemo.design"/>
|
|
||||||
<pipelineDirectory path="pipeline"/>
|
|
||||||
<preferences key="srcgen.directories.incgen" value="inc/gen"/>
|
|
||||||
<preferences key="srcgen.directories.src" value="src"/>
|
|
||||||
<preferences key="srcgen.directories.srcgen" value="src/gen"/>
|
|
||||||
<preferences key="srcgen.directories.basedir" value="code"/>
|
|
||||||
<preferences key="srcgen.directories.inc" value="inc"/>
|
|
||||||
</mlproject:mlproject>
|
|
|
@ -1,3 +0,0 @@
|
||||||
|
|
||||||
USES = "scenes" "pipeline/cache/AssetManifest"
|
|
||||||
REFS = PathfinderDemo
|
|
|
@ -1,99 +0,0 @@
|
||||||
// %BANNER_BEGIN%
|
|
||||||
// ---------------------------------------------------------------------
|
|
||||||
// %COPYRIGHT_BEGIN%
|
|
||||||
//
|
|
||||||
// Copyright (c) 2018 Magic Leap, Inc. All Rights Reserved.
|
|
||||||
// Use of this file is governed by the Creator Agreement, located
|
|
||||||
// here: https://id.magicleap.com/creator-terms
|
|
||||||
//
|
|
||||||
// %COPYRIGHT_END%
|
|
||||||
// ---------------------------------------------------------------------
|
|
||||||
// %BANNER_END%
|
|
||||||
|
|
||||||
// %SRC_VERSION%: 1
|
|
||||||
|
|
||||||
#include <lumin/LandscapeApp.h>
|
|
||||||
#include <lumin/Prism.h>
|
|
||||||
#include <lumin/event/ServerEvent.h>
|
|
||||||
#include <SceneDescriptor.h>
|
|
||||||
#include <PrismSceneManager.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* PathfinderDemo Landscape Application
|
|
||||||
*/
|
|
||||||
class PathfinderDemo : public lumin::LandscapeApp {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* Constructs the Landscape Application.
|
|
||||||
*/
|
|
||||||
PathfinderDemo();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroys the Landscape Application.
|
|
||||||
*/
|
|
||||||
virtual ~PathfinderDemo();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disallows the copy constructor.
|
|
||||||
*/
|
|
||||||
PathfinderDemo(const PathfinderDemo&) = delete;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disallows the move constructor.
|
|
||||||
*/
|
|
||||||
PathfinderDemo(PathfinderDemo&&) = delete;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disallows the copy assignment operator.
|
|
||||||
*/
|
|
||||||
PathfinderDemo& operator=(const PathfinderDemo&) = delete;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disallows the move assignment operator.
|
|
||||||
*/
|
|
||||||
PathfinderDemo& operator=(PathfinderDemo&&) = delete;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
/**
|
|
||||||
* Initializes the Landscape Application.
|
|
||||||
* @return - 0 on success, error code on failure.
|
|
||||||
*/
|
|
||||||
int init() override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deinitializes the Landscape Application.
|
|
||||||
* @return - 0 on success, error code on failure.
|
|
||||||
*/
|
|
||||||
int deInit() override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the initial size of the Prism
|
|
||||||
* Used in createPrism().
|
|
||||||
*/
|
|
||||||
const glm::vec3 getInitialPrismSize() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates the prism, updates the private variable prism_ with the created prism.
|
|
||||||
*/
|
|
||||||
void createInitialPrism();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes and creates the scene of all scenes marked as initially instanced
|
|
||||||
*/
|
|
||||||
void spawnInitialScenes();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run application login
|
|
||||||
*/
|
|
||||||
virtual bool updateLoop(float fDelta) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle events from the server
|
|
||||||
*/
|
|
||||||
virtual bool eventListener(lumin::ServerEvent* event) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
lumin::Prism* prism_ = nullptr; // represents the bounded space where the App renders.
|
|
||||||
PrismSceneManager* prismSceneManager_ = nullptr;
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
// -- WARNING -- WARNING -- WARNING -- WARNING -- WARNING -- WARNING --
|
|
||||||
//
|
|
||||||
// THE CONTENTS OF THIS FILE IS GENERATED BY CODE AND
|
|
||||||
// ANY MODIFICATIONS WILL BE OVERWRITTEN
|
|
||||||
//
|
|
||||||
// -- WARNING -- WARNING -- WARNING -- WARNING -- WARNING -- WARNING --
|
|
||||||
|
|
||||||
// %BANNER_BEGIN%
|
|
||||||
// ---------------------------------------------------------------------
|
|
||||||
// %COPYRIGHT_BEGIN%
|
|
||||||
//
|
|
||||||
// Copyright (c) 2018 Magic Leap, Inc. All Rights Reserved.
|
|
||||||
// Use of this file is governed by the Creator Agreement, located
|
|
||||||
// here: https://id.magicleap.com/creator-terms
|
|
||||||
//
|
|
||||||
// %COPYRIGHT_END%
|
|
||||||
// ---------------------------------------------------------------------
|
|
||||||
// %BANNER_END%
|
|
||||||
|
|
||||||
// %SRC_VERSION%: 1
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <SpawnedSceneBase.h>
|
|
||||||
#include <SpawnedSceneHandlers.h>
|
|
||||||
|
|
||||||
namespace scenes {
|
|
||||||
|
|
||||||
namespace PathfinderDemo {
|
|
||||||
|
|
||||||
namespace externalNodes {
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SpawnedScene : public SpawnedSceneBase {
|
|
||||||
SpawnedScene(const SceneDescriptor& sceneDescriptor, lumin::Node* root);
|
|
||||||
~SpawnedScene();
|
|
||||||
};
|
|
||||||
|
|
||||||
SpawnedSceneBase* createSpawnedScene(const SceneDescriptor& sceneDescriptor, lumin::Node* root);
|
|
||||||
SpawnedSceneHandlers* createSpawnedSceneHandlers(SpawnedSceneBase& spawnedScene);
|
|
||||||
|
|
||||||
extern const SceneDescriptor descriptor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,74 +0,0 @@
|
||||||
// -- WARNING -- WARNING -- WARNING -- WARNING -- WARNING -- WARNING --
|
|
||||||
//
|
|
||||||
// THE CONTENTS OF THIS FILE IS GENERATED BY CODE AND
|
|
||||||
// ANY MODIFICATIONS WILL BE OVERWRITTEN
|
|
||||||
//
|
|
||||||
// -- WARNING -- WARNING -- WARNING -- WARNING -- WARNING -- WARNING --
|
|
||||||
|
|
||||||
// %BANNER_BEGIN%
|
|
||||||
// ---------------------------------------------------------------------
|
|
||||||
// %COPYRIGHT_BEGIN%
|
|
||||||
//
|
|
||||||
// Copyright (c) 2018 Magic Leap, Inc. All Rights Reserved.
|
|
||||||
// Use of this file is governed by the Creator Agreement, located
|
|
||||||
// here: https://id.magicleap.com/creator-terms
|
|
||||||
//
|
|
||||||
// %COPYRIGHT_END%
|
|
||||||
// ---------------------------------------------------------------------
|
|
||||||
// %BANNER_END%
|
|
||||||
|
|
||||||
// %SRC_VERSION%: 1
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <lumin/Prism.h>
|
|
||||||
#include <lumin/node/Node.h>
|
|
||||||
|
|
||||||
#include <SceneDescriptor.h>
|
|
||||||
#include <SpawnedSceneBase.h>
|
|
||||||
#include <SpawnedSceneUserData.h>
|
|
||||||
#include <scenes.h>
|
|
||||||
|
|
||||||
class PrismSceneManager {
|
|
||||||
public:
|
|
||||||
|
|
||||||
typedef std::function<SpawnedSceneUserData*(SpawnedSceneBase&)> (*CreateSpawnedSceneUserData);
|
|
||||||
static void setUserDataCreator(const SceneDescriptor & sceneDescriptor, CreateSpawnedSceneUserData createSpawnedSceneUserData);
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
PrismSceneManager(lumin::Prism* prism);
|
|
||||||
|
|
||||||
enum class SceneState {
|
|
||||||
Unloaded,
|
|
||||||
ResourceModelLoaded,
|
|
||||||
ResourceAndObjectModelLoaded,
|
|
||||||
};
|
|
||||||
|
|
||||||
void setSceneState(const SceneDescriptor & sceneDescriptor, SceneState sceneState);
|
|
||||||
SceneState getSceneState(const SceneDescriptor & sceneDescriptor, SceneState sceneState) const;
|
|
||||||
|
|
||||||
SpawnedSceneBase* spawnScene(const SceneDescriptor & sceneDescriptor);
|
|
||||||
lumin::Node* spawn(const SceneDescriptor & sceneDescriptor);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
typedef SpawnedSceneBase* (*CreateSpawnedScene)(const SceneDescriptor& sceneDescriptor, lumin::Node* root);
|
|
||||||
static const CreateSpawnedScene createSpawnedScene[scenes::numberOfExternalScenes];
|
|
||||||
|
|
||||||
typedef SpawnedSceneHandlers* (*CreateSpawnedSceneHandlers)(SpawnedSceneBase& spawnedScene);
|
|
||||||
static const CreateSpawnedSceneHandlers createSpawnedSceneHandlers[scenes::numberOfExternalScenes];
|
|
||||||
|
|
||||||
static CreateSpawnedSceneUserData createSpawnedSceneUserData[scenes::numberOfExternalScenes];
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
lumin::Node* createNodeTree(const SceneDescriptor & sceneDescriptor);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
lumin::Prism* prism_;
|
|
||||||
SceneState sceneStates_[scenes::numberOfExternalScenes];
|
|
||||||
std::string objectModelNames_[scenes::numberOfExternalScenes];
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,58 +0,0 @@
|
||||||
// -- WARNING -- WARNING -- WARNING -- WARNING -- WARNING -- WARNING --
|
|
||||||
//
|
|
||||||
// THE CONTENTS OF THIS FILE IS GENERATED BY CODE AND
|
|
||||||
// ANY MODIFICATIONS WILL BE OVERWRITTEN
|
|
||||||
//
|
|
||||||
// -- WARNING -- WARNING -- WARNING -- WARNING -- WARNING -- WARNING --
|
|
||||||
|
|
||||||
// %BANNER_BEGIN%
|
|
||||||
// ---------------------------------------------------------------------
|
|
||||||
// %COPYRIGHT_BEGIN%
|
|
||||||
//
|
|
||||||
// Copyright (c) 2018 Magic Leap, Inc. All Rights Reserved.
|
|
||||||
// Use of this file is governed by the Creator Agreement, located
|
|
||||||
// here: https://id.magicleap.com/creator-terms
|
|
||||||
//
|
|
||||||
// %COPYRIGHT_END%
|
|
||||||
// ---------------------------------------------------------------------
|
|
||||||
// %BANNER_END%
|
|
||||||
|
|
||||||
// %SRC_VERSION%: 1
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
// data class
|
|
||||||
class SceneDescriptor {
|
|
||||||
public:
|
|
||||||
|
|
||||||
typedef std::map<std::string /* externalNodeName */, const std::string& /* externalNodeId */> ExternalNodeReferences;
|
|
||||||
|
|
||||||
SceneDescriptor(int index, const char* externalName, const char* id, const char* sceneGraphFilePath, const char* resourceModelFilePath, const ExternalNodeReferences& externalNodeReferences, bool initiallySpawned);
|
|
||||||
const std::string& getExternalName() const;
|
|
||||||
const std::string& getId() const;
|
|
||||||
const std::string& getSceneGraphPath() const;
|
|
||||||
const std::string& getResourceModelPath() const;
|
|
||||||
const ExternalNodeReferences & getExternalNodeReferences() const;
|
|
||||||
bool getInitiallySpawned() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
friend class PrismSceneManager;
|
|
||||||
int getIndex() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
int index_;
|
|
||||||
std::string externalName_;
|
|
||||||
std::string id_;
|
|
||||||
std::string sceneGraphPath_;
|
|
||||||
std::string resourceModelPath_;
|
|
||||||
const ExternalNodeReferences& externalNodeReferences_;
|
|
||||||
bool initiallySpawned_;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool operator<(const SceneDescriptor& a, const SceneDescriptor& b);
|
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
// -- WARNING -- WARNING -- WARNING -- WARNING -- WARNING -- WARNING --
|
|
||||||
//
|
|
||||||
// THE CONTENTS OF THIS FILE IS GENERATED BY CODE AND
|
|
||||||
// ANY MODIFICATIONS WILL BE OVERWRITTEN
|
|
||||||
//
|
|
||||||
// -- WARNING -- WARNING -- WARNING -- WARNING -- WARNING -- WARNING --
|
|
||||||
|
|
||||||
// %BANNER_BEGIN%
|
|
||||||
// ---------------------------------------------------------------------
|
|
||||||
// %COPYRIGHT_BEGIN%
|
|
||||||
//
|
|
||||||
// Copyright (c) 2018 Magic Leap, Inc. All Rights Reserved.
|
|
||||||
// Use of this file is governed by the Creator Agreement, located
|
|
||||||
// here: https://id.magicleap.com/creator-terms
|
|
||||||
//
|
|
||||||
// %COPYRIGHT_END%
|
|
||||||
// ---------------------------------------------------------------------
|
|
||||||
// %BANNER_END%
|
|
||||||
|
|
||||||
// %SRC_VERSION%: 1
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <lumin/node/Node.h>
|
|
||||||
|
|
||||||
class SceneDescriptor;
|
|
||||||
class SpawnedSceneHandlers;
|
|
||||||
struct SpawnedSceneUserData;
|
|
||||||
|
|
||||||
struct SpawnedSceneBase {
|
|
||||||
SpawnedSceneBase(const SceneDescriptor &sd, lumin::Node* rt);
|
|
||||||
virtual ~SpawnedSceneBase();
|
|
||||||
|
|
||||||
SpawnedSceneBase() = delete;
|
|
||||||
SpawnedSceneBase(const SpawnedSceneBase&) = delete;
|
|
||||||
SpawnedSceneBase(const SpawnedSceneBase&&) = delete;
|
|
||||||
|
|
||||||
const SceneDescriptor& sceneDescriptor;
|
|
||||||
lumin::Node* root = nullptr;
|
|
||||||
SpawnedSceneHandlers* handlers = nullptr;
|
|
||||||
SpawnedSceneUserData* userData = nullptr;
|
|
||||||
};
|
|
||||||
|
|