use "32 bit alignment" and "explicit shaders" pathfinder branches
|
@ -12,8 +12,8 @@ bevy_asset = { path = "../bevy_asset" }
|
|||
bevy_render = { path = "../bevy_render" }
|
||||
legion = { path = "../bevy_legion" }
|
||||
|
||||
pathfinder_geometry = { path = "../pathfinder/geometry" }
|
||||
pathfinder_gpu = { path = "../pathfinder/gpu" }
|
||||
pathfinder_geometry = { path = "../pathfinder/geometry", features = ["shader_alignment_32_bits"] }
|
||||
pathfinder_gpu = { path = "../pathfinder/gpu", features = ["shader_alignment_32_bits"] }
|
||||
pathfinder_renderer = { path = "../pathfinder/renderer" }
|
||||
pathfinder_simd = { path = "../pathfinder/simd" }
|
||||
pathfinder_resources = { path = "../pathfinder/resources" }
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
use crate::shaders;
|
||||
use bevy_asset::{AssetStorage, Handle};
|
||||
use bevy_render::{
|
||||
pipeline::{
|
||||
|
@ -6,7 +5,7 @@ use bevy_render::{
|
|||
VertexAttributeDescriptor, VertexBufferDescriptor, VertexFormat,
|
||||
},
|
||||
render_resource::{BufferInfo, BufferUsage, RenderResource},
|
||||
renderer::{RenderContext, RenderResourceContext},
|
||||
renderer::RenderContext,
|
||||
shader::{Shader, ShaderSource, ShaderStage, ShaderStages},
|
||||
texture::{
|
||||
AddressMode, Extent3d, FilterMode, SamplerDescriptor, TextureDescriptor, TextureDimension,
|
||||
|
@ -16,9 +15,9 @@ use bevy_render::{
|
|||
use pathfinder_canvas::vec2i;
|
||||
use pathfinder_geometry::{rect::RectI, vector::Vector2I};
|
||||
use pathfinder_gpu::{
|
||||
BufferData, BufferTarget, BufferUploadMode, Device, RenderState, RenderTarget, ShaderKind,
|
||||
TextureData, TextureDataRef, TextureSamplingFlags, VertexAttrClass, VertexAttrDescriptor,
|
||||
VertexAttrType,
|
||||
BufferData, BufferTarget, BufferUploadMode, Device, ProgramKind, RenderState, RenderTarget,
|
||||
ShaderKind, TextureData, TextureDataRef, TextureSamplingFlags, VertexAttrClass,
|
||||
VertexAttrDescriptor, VertexAttrType, FeatureLevel,
|
||||
};
|
||||
use pathfinder_resources::ResourceLoader;
|
||||
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 BevyTextureDataReceiver {}
|
||||
pub struct BevyUniform {
|
||||
name: String,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
pub struct BevyVertexAttr {
|
||||
|
@ -56,8 +55,8 @@ pub struct BevyVertexAttr {
|
|||
#[derive(Debug)]
|
||||
pub struct BevyVertexArray {
|
||||
requested_descriptors: RefCell<HashMap<u32, VertexBufferDescriptor>>,
|
||||
vertex_buffers: RefCell<Vec<RenderResource>>,
|
||||
index_buffer: RefCell<Option<RenderResource>>,
|
||||
vertex_buffers: RefCell<Vec<BevyBuffer>>,
|
||||
index_buffer: RefCell<Option<BevyBuffer>>,
|
||||
}
|
||||
|
||||
pub struct BevyTexture {
|
||||
|
@ -66,15 +65,19 @@ pub struct BevyTexture {
|
|||
sampler_resource: RefCell<Option<RenderResource>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BevyBuffer {
|
||||
handle: Rc<RefCell<Option<RenderResource>>>,
|
||||
mode: BufferUploadMode,
|
||||
}
|
||||
|
||||
impl<'a> Device for BevyPathfinderDevice<'a> {
|
||||
type Buffer = BevyBuffer;
|
||||
type Fence = ();
|
||||
type Framebuffer = BevyTexture;
|
||||
type Program = PipelineDescriptor;
|
||||
type Shader = Handle<Shader>;
|
||||
type StorageBuffer = ();
|
||||
type Texture = BevyTexture;
|
||||
type TextureDataReceiver = BevyTextureDataReceiver;
|
||||
type TimerQuery = BevyTimerQuery;
|
||||
|
@ -127,31 +130,38 @@ impl<'a> Device for BevyPathfinderDevice<'a> {
|
|||
}
|
||||
fn create_shader(
|
||||
&self,
|
||||
_resources: &dyn ResourceLoader,
|
||||
resources: &dyn ResourceLoader,
|
||||
name: &str,
|
||||
kind: ShaderKind,
|
||||
) -> Self::Shader {
|
||||
let shader_bytes = match (name, kind) {
|
||||
("blit", ShaderKind::Fragment) => shaders::BLIT_FS,
|
||||
("blit", ShaderKind::Vertex) => shaders::BLIT_VS,
|
||||
// ("debug_solid", ShaderKind::Fragment) => shaders::DEMO_GROUND_FS,
|
||||
// ("demo_ground", ShaderKind::Vertex) => shaders::DEMO_GROUND_VS,
|
||||
("fill", ShaderKind::Fragment) => shaders::FILL_FS,
|
||||
("fill", ShaderKind::Vertex) => shaders::FILL_VS,
|
||||
("reproject", ShaderKind::Fragment) => shaders::REPROJECT_FS,
|
||||
("reproject", ShaderKind::Vertex) => shaders::REPROJECT_VS,
|
||||
("stencil", ShaderKind::Fragment) => shaders::STENCIL_FS,
|
||||
("stencil", ShaderKind::Vertex) => shaders::STENCIL_VS,
|
||||
("tile_clip", ShaderKind::Fragment) => shaders::TILE_CLIP_FS,
|
||||
("tile_clip", ShaderKind::Vertex) => shaders::TILE_CLIP_VS,
|
||||
("tile_copy", ShaderKind::Fragment) => shaders::TILE_COPY_FS,
|
||||
("tile_copy", ShaderKind::Vertex) => shaders::TILE_COPY_VS,
|
||||
("tile", ShaderKind::Fragment) => shaders::TILE_FS,
|
||||
("tile", ShaderKind::Vertex) => shaders::TILE_VS,
|
||||
_ => panic!("encountered unexpected shader {} {:?}", name, kind),
|
||||
// let shader_bytes = match (name, kind) {
|
||||
// ("blit", ShaderKind::Fragment) => shaders::BLIT_FS,
|
||||
// ("blit", ShaderKind::Vertex) => shaders::BLIT_VS,
|
||||
// // ("debug_solid", ShaderKind::Fragment) => shaders::DEMO_GROUND_FS,
|
||||
// // ("demo_ground", ShaderKind::Vertex) => shaders::DEMO_GROUND_VS,
|
||||
// ("fill", ShaderKind::Fragment) => shaders::FILL_FS,
|
||||
// ("fill", ShaderKind::Vertex) => shaders::FILL_VS,
|
||||
// ("reproject", ShaderKind::Fragment) => shaders::REPROJECT_FS,
|
||||
// ("reproject", ShaderKind::Vertex) => shaders::REPROJECT_VS,
|
||||
// ("stencil", ShaderKind::Fragment) => shaders::STENCIL_FS,
|
||||
// ("stencil", ShaderKind::Vertex) => shaders::STENCIL_VS,
|
||||
// ("tile_clip", ShaderKind::Fragment) => shaders::TILE_CLIP_FS,
|
||||
// ("tile_clip", ShaderKind::Vertex) => shaders::TILE_CLIP_VS,
|
||||
// ("tile_copy", ShaderKind::Fragment) => shaders::TILE_COPY_FS,
|
||||
// ("tile_copy", ShaderKind::Vertex) => shaders::TILE_COPY_VS,
|
||||
// ("tile", ShaderKind::Fragment) => shaders::TILE_FS,
|
||||
// ("tile", ShaderKind::Vertex) => shaders::TILE_VS,
|
||||
// _ => 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(
|
||||
&self,
|
||||
|
@ -162,6 +172,7 @@ impl<'a> Device for BevyPathfinderDevice<'a> {
|
|||
let stage = match kind {
|
||||
ShaderKind::Fragment => ShaderStage::Fragment,
|
||||
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 mut shaders = self.shaders.borrow_mut();
|
||||
|
@ -183,17 +194,20 @@ impl<'a> Device for BevyPathfinderDevice<'a> {
|
|||
fn create_program_from_shaders(
|
||||
&self,
|
||||
_resources: &dyn ResourceLoader,
|
||||
name: &str,
|
||||
vertex_shader: Self::Shader,
|
||||
fragment_shader: Self::Shader,
|
||||
_name: &str,
|
||||
shaders: ProgramKind<Handle<Shader>>,
|
||||
) -> Self::Program {
|
||||
println!("{}", name);
|
||||
let mut descriptor = PipelineDescriptor::new(ShaderStages {
|
||||
vertex: vertex_shader,
|
||||
fragment: Some(fragment_shader),
|
||||
});
|
||||
descriptor.reflect_layout(&self.shaders.borrow(), false, None, None);
|
||||
descriptor
|
||||
match shaders {
|
||||
ProgramKind::Compute(_) => panic!("bevy does not currently support compute shaders"),
|
||||
ProgramKind::Raster { vertex, fragment } => {
|
||||
let mut descriptor = PipelineDescriptor::new(ShaderStages {
|
||||
vertex,
|
||||
fragment: Some(fragment),
|
||||
});
|
||||
descriptor.reflect_layout(&self.shaders.borrow(), false, None, None);
|
||||
descriptor
|
||||
}
|
||||
}
|
||||
}
|
||||
fn get_vertex_attr(
|
||||
&self,
|
||||
|
@ -228,19 +242,20 @@ impl<'a> Device for BevyPathfinderDevice<'a> {
|
|||
}
|
||||
fn bind_buffer(
|
||||
&self,
|
||||
vertex_array: &Self::VertexArray,
|
||||
buffer: &Self::Buffer,
|
||||
vertex_array: &BevyVertexArray,
|
||||
buffer: &BevyBuffer,
|
||||
target: BufferTarget,
|
||||
) {
|
||||
match target {
|
||||
BufferTarget::Vertex => vertex_array
|
||||
.vertex_buffers
|
||||
.borrow_mut()
|
||||
.push(buffer.handle.borrow().unwrap().clone()),
|
||||
.push(buffer.clone()),
|
||||
BufferTarget::Index => {
|
||||
*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(
|
||||
|
@ -282,6 +297,14 @@ impl<'a> Device for BevyPathfinderDevice<'a> {
|
|||
// VertexFormat::Short3Normalized
|
||||
// }
|
||||
(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, 2) => VertexFormat::Float2,
|
||||
(VertexAttrClass::Float, VertexAttrType::F32, 3) => VertexFormat::Float3,
|
||||
|
@ -340,20 +363,15 @@ impl<'a> Device for BevyPathfinderDevice<'a> {
|
|||
texture
|
||||
}
|
||||
|
||||
fn create_buffer(&self) -> Self::Buffer {
|
||||
fn create_buffer(&self, mode: BufferUploadMode) -> Self::Buffer {
|
||||
BevyBuffer {
|
||||
handle: Rc::new(RefCell::new(None)),
|
||||
mode,
|
||||
}
|
||||
}
|
||||
|
||||
fn allocate_buffer<T>(
|
||||
&self,
|
||||
buffer: &BevyBuffer,
|
||||
data: BufferData<T>,
|
||||
_target: BufferTarget,
|
||||
mode: BufferUploadMode,
|
||||
) {
|
||||
let buffer_usage = match mode {
|
||||
fn allocate_buffer<T>(&self, buffer: &BevyBuffer, data: BufferData<T>, _target: BufferTarget) {
|
||||
let buffer_usage = match buffer.mode {
|
||||
BufferUploadMode::Dynamic => BufferUsage::WRITE_ALL,
|
||||
BufferUploadMode::Static => BufferUsage::COPY_DST,
|
||||
};
|
||||
|
@ -504,8 +522,8 @@ impl<'a> Device for BevyPathfinderDevice<'a> {
|
|||
}
|
||||
fn read_pixels(
|
||||
&self,
|
||||
target: &RenderTarget<Self>,
|
||||
viewport: RectI,
|
||||
_target: &RenderTarget<Self>,
|
||||
_viewport: RectI,
|
||||
) -> Self::TextureDataReceiver {
|
||||
// TODO: this might actually be optional, which is great because otherwise this requires a command buffer sync
|
||||
todo!()
|
||||
|
@ -516,17 +534,17 @@ impl<'a> Device for BevyPathfinderDevice<'a> {
|
|||
fn end_commands(&self) {
|
||||
// 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!()
|
||||
}
|
||||
fn draw_elements(&self, index_count: u32, render_state: &RenderState<Self>) {
|
||||
fn draw_elements(&self, _index_count: u32, _render_state: &RenderState<Self>) {
|
||||
todo!()
|
||||
}
|
||||
fn draw_elements_instanced(
|
||||
&self,
|
||||
index_count: u32,
|
||||
instance_count: u32,
|
||||
render_state: &RenderState<Self>,
|
||||
_index_count: u32,
|
||||
_instance_count: u32,
|
||||
_render_state: &RenderState<Self>,
|
||||
) {
|
||||
todo!()
|
||||
}
|
||||
|
@ -534,27 +552,82 @@ impl<'a> Device for BevyPathfinderDevice<'a> {
|
|||
// TODO: maybe not needed
|
||||
BevyTimerQuery {}
|
||||
}
|
||||
fn begin_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 begin_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> {
|
||||
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)
|
||||
}
|
||||
fn try_recv_texture_data(&self, receiver: &Self::TextureDataReceiver) -> Option<TextureData> {
|
||||
fn try_recv_texture_data(&self, _receiver: &Self::TextureDataReceiver) -> Option<TextureData> {
|
||||
None
|
||||
}
|
||||
fn recv_texture_data(&self, receiver: &Self::TextureDataReceiver) -> TextureData {
|
||||
fn recv_texture_data(&self, _receiver: &Self::TextureDataReceiver) -> TextureData {
|
||||
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] {
|
||||
match data_ref {
|
||||
TextureDataRef::U8(data) => data,
|
||||
TextureDataRef::F16(data) => {
|
||||
panic!("we dont do half measures");
|
||||
slice_to_u8(data)
|
||||
}
|
||||
TextureDataRef::F32(data) => data.as_bytes(),
|
||||
}
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
mod device;
|
||||
mod resource_loader;
|
||||
mod pathfinder_node;
|
||||
mod shaders;
|
||||
use bevy_app::{AppBuilder, AppPlugin};
|
||||
pub use device::*;
|
||||
pub use resource_loader::*;
|
||||
|
||||
use bevy_render::render_graph::RenderGraph;
|
||||
use pathfinder_node::PathfinderNode;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{BevyPathfinderDevice, BevyResourceLoader};
|
||||
use crate::BevyPathfinderDevice;
|
||||
use bevy_asset::AssetStorage;
|
||||
use bevy_render::{
|
||||
render_graph::{Node, ResourceSlots},
|
||||
|
@ -15,6 +15,7 @@ use pathfinder_renderer::{
|
|||
},
|
||||
options::BuildOptions,
|
||||
};
|
||||
use pathfinder_resources::embedded::EmbeddedResourceLoader;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct PathfinderNode;
|
||||
|
@ -33,10 +34,11 @@ impl Node for PathfinderNode {
|
|||
let window_size = Vector2I::new(640 as i32, 480 as i32);
|
||||
let mut renderer = Renderer::new(
|
||||
device,
|
||||
&BevyResourceLoader::new(),
|
||||
&EmbeddedResourceLoader::new(),
|
||||
DestFramebuffer::full_window(window_size),
|
||||
RendererOptions {
|
||||
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_nanovg",
|
||||
"examples/canvas_text",
|
||||
"examples/canvas_webgpu_minimal",
|
||||
"examples/lottie_basic",
|
||||
"examples/swf_basic",
|
||||
"geometry",
|
||||
|
@ -35,7 +34,6 @@ members = [
|
|||
"utils/svg-to-skia",
|
||||
"utils/convert",
|
||||
"webgl",
|
||||
"webgpu",
|
||||
]
|
||||
|
||||
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;
|
||||
};
|
||||
|