Initial work toward VR support
This commit is contained in:
parent
9c404dfdc1
commit
bb32777101
|
@ -186,7 +186,7 @@ dependencies = [
|
|||
"arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -473,7 +473,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.2.0"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
|
@ -633,6 +633,7 @@ dependencies = [
|
|||
"egl 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gl 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jni 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pathfinder_demo 0.1.0",
|
||||
"pathfinder_geometry 0.3.0",
|
||||
"pathfinder_gl 0.1.0",
|
||||
|
@ -939,7 +940,7 @@ version = "1.4.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
@ -1073,7 +1074,7 @@ version = "0.32.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1224,7 +1225,7 @@ name = "thread_local"
|
|||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1412,7 +1413,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||
"checksum khronos 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c0711aaa80e6ba6eb1fa8978f1f46bfcb38ceb2f3f33f3736efbff39dac89f50"
|
||||
"checksum khronos_api 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "037ab472c33f67b5fbd3e9163a2645319e5356fcd355efa6d4eb7fff4bbcb554"
|
||||
"checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1"
|
||||
"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14"
|
||||
"checksum libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "e32a70cf75e5846d53a673923498228bbec6a8624708a9ea5645f075d6276122"
|
||||
"checksum libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)" = "e962c7641008ac010fa60a7dfdc1712449f29c44ef2d4702394aea943ee75047"
|
||||
"checksum libflate 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "54d1ddf9c52870243c5689d7638d888331c1116aa5b398f3ba1acfa7d8758ca1"
|
||||
|
|
|
@ -10,6 +10,7 @@ import android.support.v7.app.ActionBar;
|
|||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
|
@ -18,6 +19,8 @@ import android.view.View;
|
|||
* status bar and navigation/system bar) with user interaction.
|
||||
*/
|
||||
public class PathfinderActivity extends AppCompatActivity {
|
||||
private PathfinderDemoRenderer mRenderer;
|
||||
|
||||
/**
|
||||
* Whether or not the system UI should be auto-hidden after
|
||||
* {@link #AUTO_HIDE_DELAY_MILLIS} milliseconds.
|
||||
|
@ -111,6 +114,7 @@ public class PathfinderActivity extends AppCompatActivity {
|
|||
init();
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
private void init() {
|
||||
setContentView(R.layout.activity_pathfinder);
|
||||
|
||||
|
@ -118,6 +122,7 @@ public class PathfinderActivity extends AppCompatActivity {
|
|||
mControlsView = findViewById(R.id.fullscreen_content_controls);
|
||||
mContentView = findViewById(R.id.fullscreen_content);
|
||||
|
||||
/*
|
||||
// Set up the user interaction to manually show or hide the system UI.
|
||||
mContentView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
|
@ -125,9 +130,30 @@ public class PathfinderActivity extends AppCompatActivity {
|
|||
toggle();
|
||||
}
|
||||
});
|
||||
*/
|
||||
|
||||
mContentView.setEGLContextClientVersion(3);
|
||||
mContentView.setRenderer(new PathfinderDemoRenderer(getAssets()));
|
||||
mRenderer = new PathfinderDemoRenderer(getAssets());
|
||||
mContentView.setRenderer(mRenderer);
|
||||
|
||||
mContentView.setOnTouchListener(new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View view, MotionEvent event) {
|
||||
int x = Math.round(event.getX());
|
||||
int y = Math.round(event.getY());
|
||||
switch (event.getActionMasked()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
Log.i("Pathfinder", "DOWN " + x + " " + y);
|
||||
PathfinderDemoRenderer.pushMouseDownEvent(x, y);
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
Log.i("Pathfinder", "MOVE " + x + " " + y);
|
||||
PathfinderDemoRenderer.pushMouseDraggedEvent(x, y);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -12,6 +12,9 @@ public class PathfinderDemoRenderer extends Object implements GLSurfaceView.Rend
|
|||
private static native void init(PathfinderDemoResourceLoader resourceLoader);
|
||||
private static native void runOnce();
|
||||
|
||||
public static native void pushMouseDownEvent(int x, int y);
|
||||
public static native void pushMouseDraggedEvent(int x, int y);
|
||||
|
||||
static {
|
||||
System.loadLibrary("pathfinder_android_demo");
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ package graphics.pathfinder.pathfinderdemo;
|
|||
import android.content.Context;
|
||||
import android.opengl.GLSurfaceView;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
public class PathfinderDemoSurfaceView extends GLSurfaceView {
|
||||
public PathfinderDemoSurfaceView(Context context) {
|
||||
|
|
|
@ -11,6 +11,7 @@ crate_type = ["cdylib"]
|
|||
egl = "0.2"
|
||||
gl = "0.6"
|
||||
jni = "0.11"
|
||||
lazy_static = "1.3"
|
||||
|
||||
[dependencies.pathfinder_demo]
|
||||
path = "../../common"
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
use jni::{JNIEnv, JavaVM};
|
||||
use jni::objects::{GlobalRef, JByteBuffer, JClass, JObject, JValue};
|
||||
use pathfinder_demo::DemoApp;
|
||||
|
@ -16,9 +19,16 @@ use pathfinder_geometry::basic::point::Point2DI32;
|
|||
use pathfinder_gl::GLVersion;
|
||||
use pathfinder_gpu::resources::ResourceLoader;
|
||||
use std::cell::RefCell;
|
||||
use std::ffi::CString;
|
||||
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);
|
||||
|
@ -43,18 +53,31 @@ pub unsafe extern "system" fn
|
|||
Java_graphics_pathfinder_pathfinderdemo_PathfinderDemoRenderer_runOnce(env: JNIEnv,
|
||||
class: JClass) {
|
||||
DEMO_APP.with(|demo_app| {
|
||||
let mut event_queue = EVENT_QUEUE.lock().unwrap();
|
||||
if let Some(ref mut demo_app) = *demo_app.borrow_mut() {
|
||||
demo_app.run_once(vec![]);
|
||||
demo_app.run_once(mem::replace(&mut *event_queue, vec![]));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "system" fn
|
||||
Java_graphics_pathfinder_pathfinderdemo_PathfinderDemoRenderer_pushMouseDown(env: JNIEnv,
|
||||
Java_graphics_pathfinder_pathfinderdemo_PathfinderDemoRenderer_pushMouseDownEvent(
|
||||
env: JNIEnv,
|
||||
class: JClass,
|
||||
x: i32,
|
||||
y: i32) {
|
||||
EVENT_QUEUE.lock().unwrap().push(Event::MouseDown(Point2DI32::new(x, y)))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "system" fn
|
||||
Java_graphics_pathfinder_pathfinderdemo_PathfinderDemoRenderer_pushMouseDraggedEvent(
|
||||
env: JNIEnv,
|
||||
class: JClass,
|
||||
x: i32,
|
||||
y: i32) {
|
||||
EVENT_QUEUE.lock().unwrap().push(Event::MouseDragged(Point2DI32::new(x, y)))
|
||||
}
|
||||
|
||||
struct WindowImpl;
|
||||
|
@ -70,11 +93,11 @@ impl Window for WindowImpl {
|
|||
}
|
||||
|
||||
fn size(&self) -> Point2DI32 {
|
||||
Point2DI32::new(1080, 1920)
|
||||
Point2DI32::new(1920, 1080)
|
||||
}
|
||||
|
||||
fn drawable_size(&self) -> Point2DI32 {
|
||||
Point2DI32::new(1080, 1920)
|
||||
Point2DI32::new(1920, 1080)
|
||||
}
|
||||
|
||||
fn mouse_position(&self) -> Point2DI32 {
|
||||
|
|
|
@ -17,7 +17,7 @@ use clap::{App, Arg};
|
|||
use image::ColorType;
|
||||
use jemallocator;
|
||||
use pathfinder_geometry::basic::point::{Point2DF32, Point2DI32, Point3DF32};
|
||||
use pathfinder_geometry::basic::rect::RectF32;
|
||||
use pathfinder_geometry::basic::rect::{RectF32, RectI32};
|
||||
use pathfinder_geometry::basic::transform2d::Transform2DF32;
|
||||
use pathfinder_geometry::basic::transform3d::{Perspective, Transform3DF32};
|
||||
use pathfinder_geometry::color::ColorU;
|
||||
|
@ -27,17 +27,18 @@ use pathfinder_gpu::{DepthFunc, DepthState, Device, Primitive, RenderState, Sten
|
|||
use pathfinder_gpu::{StencilState, UniformData};
|
||||
use pathfinder_renderer::builder::{RenderOptions, RenderTransform, SceneBuilder};
|
||||
use pathfinder_renderer::gpu::renderer::Renderer;
|
||||
use pathfinder_renderer::gpu_data::BuiltScene;
|
||||
use pathfinder_renderer::gpu_data::{BuiltScene, Stats};
|
||||
use pathfinder_renderer::post::{DEFRINGING_KERNEL_CORE_GRAPHICS, STEM_DARKENING_FACTORS};
|
||||
use pathfinder_renderer::scene::Scene;
|
||||
use pathfinder_renderer::z_buffer::ZBuffer;
|
||||
use pathfinder_svg::BuiltSVG;
|
||||
use pathfinder_ui::UIEvent;
|
||||
use pathfinder_ui::{MousePosition, UIEvent};
|
||||
use rayon::ThreadPoolBuilder;
|
||||
use std::f32::consts::FRAC_PI_4;
|
||||
use std::ffi::CString;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::iter;
|
||||
use std::mem;
|
||||
use std::panic;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
@ -98,6 +99,7 @@ pub struct DemoApp<W> where W: Window {
|
|||
dirty: bool,
|
||||
expire_message_event_id: u32,
|
||||
message_epoch: u32,
|
||||
last_mouse_position: Point2DI32,
|
||||
|
||||
ui: DemoUI<GLDevice>,
|
||||
scene_thread_proxy: SceneThreadProxy,
|
||||
|
@ -120,20 +122,24 @@ impl<W> DemoApp<W> where W: Window {
|
|||
let options = Options::get(resources);
|
||||
|
||||
let (window_size, drawable_size) = (window.size(), window.drawable_size());
|
||||
let view_box_size = view_box_size(options.mode, &window);
|
||||
|
||||
let built_svg = load_scene(resources, &options.input_path);
|
||||
let message = get_svg_building_message(&built_svg);
|
||||
let scene_view_box = built_svg.scene.view_box;
|
||||
let scene_is_monochrome = built_svg.scene.is_monochrome();
|
||||
|
||||
let renderer = Renderer::new(device, resources, drawable_size);
|
||||
let renderer = Renderer::new(device,
|
||||
resources,
|
||||
RectI32::new(Point2DI32::default(), view_box_size),
|
||||
drawable_size);
|
||||
let scene_thread_proxy = SceneThreadProxy::new(built_svg.scene, options.clone());
|
||||
scene_thread_proxy.set_drawable_size(window.drawable_size());
|
||||
scene_thread_proxy.set_drawable_size(view_box_size);
|
||||
|
||||
let camera = if options.three_d {
|
||||
Camera::new_3d(scene_view_box)
|
||||
let camera = if options.mode == Mode::TwoD {
|
||||
Camera::new_2d(scene_view_box, view_box_size)
|
||||
} else {
|
||||
Camera::new_2d(scene_view_box, drawable_size)
|
||||
Camera::new_3d(scene_view_box)
|
||||
};
|
||||
|
||||
let ground_program = GroundProgram::new(&renderer.device, resources);
|
||||
|
@ -164,6 +170,7 @@ impl<W> DemoApp<W> where W: Window {
|
|||
dirty: true,
|
||||
expire_message_event_id,
|
||||
message_epoch,
|
||||
last_mouse_position: Point2DI32::default(),
|
||||
|
||||
ui,
|
||||
scene_thread_proxy,
|
||||
|
@ -180,31 +187,35 @@ impl<W> DemoApp<W> where W: Window {
|
|||
self.build_scene();
|
||||
|
||||
// Handle events.
|
||||
let ui_event = self.handle_events(events);
|
||||
let ui_events = self.handle_events(events);
|
||||
|
||||
// Draw the scene.
|
||||
let render_msg = self.scene_thread_proxy.receiver.recv().unwrap();
|
||||
self.draw_scene(render_msg, ui_event);
|
||||
self.draw_scene(render_msg, ui_events);
|
||||
}
|
||||
|
||||
fn build_scene(&mut self) {
|
||||
let drawable_size = self.window.drawable_size();
|
||||
let view_box_size = view_box_size(self.ui.mode, &self.window);
|
||||
|
||||
let render_transform = match self.camera {
|
||||
Camera::ThreeD { ref mut transform, ref mut velocity } => {
|
||||
if transform.offset(*velocity) {
|
||||
self.dirty = true;
|
||||
}
|
||||
let perspective = transform.to_perspective(drawable_size);
|
||||
let perspective = transform.to_perspective(view_box_size);
|
||||
RenderTransform::Perspective(perspective)
|
||||
}
|
||||
Camera::TwoD(transform) => RenderTransform::Transform2D(transform),
|
||||
};
|
||||
|
||||
let count = if self.frame_counter == 0 { 2 } else { 1 };
|
||||
for _ in 0..count {
|
||||
let is_first_frame = self.frame_counter == 0;
|
||||
let frame_count = if is_first_frame { 2 } else { 1 };
|
||||
for _ in 0..frame_count {
|
||||
let viewport_count = self.ui.mode.viewport_count();
|
||||
let render_transforms = iter::repeat(render_transform.clone()).take(viewport_count)
|
||||
.collect();
|
||||
self.scene_thread_proxy.sender.send(MainToSceneMsg::Build(BuildOptions {
|
||||
render_transform: render_transform.clone(),
|
||||
render_transforms,
|
||||
stem_darkening_font_size: if self.ui.stem_darkening_effect_enabled {
|
||||
Some(APPROX_FONT_SIZE * self.scale_factor)
|
||||
} else {
|
||||
|
@ -213,13 +224,13 @@ impl<W> DemoApp<W> where W: Window {
|
|||
})).unwrap();
|
||||
}
|
||||
|
||||
if count == 2 {
|
||||
if is_first_frame {
|
||||
self.dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_events(&mut self, events: Vec<Event>) -> UIEvent {
|
||||
let mut ui_event = UIEvent::None;
|
||||
fn handle_events(&mut self, events: Vec<Event>) -> Vec<UIEvent> {
|
||||
let mut ui_events = vec![];
|
||||
self.dirty = false;
|
||||
|
||||
for event in events {
|
||||
|
@ -230,25 +241,29 @@ impl<W> DemoApp<W> where W: Window {
|
|||
self.dirty = true;
|
||||
}
|
||||
Event::WindowResized => {
|
||||
self.scene_thread_proxy.set_drawable_size(self.window.drawable_size());
|
||||
let view_box_size = view_box_size(self.ui.mode, &self.window);
|
||||
self.scene_thread_proxy.set_drawable_size(view_box_size);
|
||||
self.renderer.set_main_framebuffer_size(self.window.drawable_size());
|
||||
self.dirty = true;
|
||||
}
|
||||
Event::MouseDown(position) => {
|
||||
ui_event = UIEvent::MouseDown(position.scale(self.scale_factor as i32));
|
||||
Event::MouseDown(new_position) => {
|
||||
let mouse_position = self.process_mouse_position(new_position);
|
||||
ui_events.push(UIEvent::MouseDown(mouse_position));
|
||||
}
|
||||
Event::MouseMoved { relative_position, .. } if self.mouselook_enabled => {
|
||||
Event::MouseMoved(new_position) if self.mouselook_enabled => {
|
||||
let mouse_position = self.process_mouse_position(new_position);
|
||||
if let Camera::ThreeD { ref mut transform, .. } = self.camera {
|
||||
let rotation = relative_position.to_f32().scale(MOUSELOOK_ROTATION_SPEED);
|
||||
let rotation = mouse_position.relative
|
||||
.to_f32()
|
||||
.scale(MOUSELOOK_ROTATION_SPEED);
|
||||
transform.yaw += rotation.x();
|
||||
transform.pitch += rotation.y();
|
||||
self.dirty = true;
|
||||
}
|
||||
}
|
||||
Event::MouseDragged { position, relative_position } => {
|
||||
let absolute_position = position.scale(self.scale_factor as i32);
|
||||
let relative_position = relative_position.scale(self.scale_factor as i32);
|
||||
ui_event = UIEvent::MouseDragged { absolute_position, relative_position };
|
||||
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) => {
|
||||
|
@ -312,34 +327,58 @@ impl<W> DemoApp<W> where W: Window {
|
|||
}
|
||||
}
|
||||
|
||||
ui_event
|
||||
ui_events
|
||||
}
|
||||
|
||||
fn draw_scene(&mut self, render_msg: SceneToMainMsg, mut ui_event: UIEvent) {
|
||||
let SceneToMainMsg::Render {
|
||||
built_scene,
|
||||
transform: render_transform,
|
||||
tile_time,
|
||||
} = render_msg;
|
||||
fn process_mouse_position(&mut self, new_position: Point2DI32) -> MousePosition {
|
||||
let absolute = new_position.scale(self.scale_factor as i32);
|
||||
let relative = absolute - self.last_mouse_position;
|
||||
self.last_mouse_position = absolute;
|
||||
MousePosition { absolute, relative }
|
||||
}
|
||||
|
||||
fn draw_scene(&mut self, render_msg: SceneToMainMsg, mut ui_events: Vec<UIEvent>) {
|
||||
self.renderer.device.clear(Some(self.background_color().to_f32().0), Some(1.0), Some(0));
|
||||
self.draw_environment(&render_transform);
|
||||
self.render_vector_scene(&built_scene);
|
||||
|
||||
let SceneToMainMsg::Render { render_scenes, tile_time } = render_msg;
|
||||
let mut render_stats = None;
|
||||
|
||||
for (viewport_index, render_scene) in render_scenes.iter().enumerate() {
|
||||
self.draw_environment(viewport_index, &render_scene.transform);
|
||||
self.render_vector_scene(viewport_index, &render_scene.built_scene);
|
||||
|
||||
match render_stats {
|
||||
None => {
|
||||
render_stats = Some(RenderStats {
|
||||
rendering_time: self.renderer.shift_timer_query(),
|
||||
stats: render_scene.built_scene.stats(),
|
||||
})
|
||||
}
|
||||
Some(ref mut render_stats) => {
|
||||
render_stats.stats = render_stats.stats + render_scene.built_scene.stats()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let drawable_size = self.window.drawable_size();
|
||||
self.renderer.set_viewport(RectI32::new(Point2DI32::default(), drawable_size));
|
||||
|
||||
if self.pending_screenshot_path.is_some() {
|
||||
self.take_screenshot();
|
||||
}
|
||||
|
||||
let rendering_time = self.renderer.shift_timer_query();
|
||||
let stats = built_scene.stats();
|
||||
self.renderer.debug_ui.add_sample(stats, tile_time, rendering_time);
|
||||
self.renderer.debug_ui.draw(&self.renderer.device);
|
||||
|
||||
if !ui_event.is_none() {
|
||||
self.dirty = true;
|
||||
if let Some(render_stats) = render_stats {
|
||||
self.renderer.debug_ui.add_sample(render_stats.stats,
|
||||
tile_time,
|
||||
render_stats.rendering_time);
|
||||
self.renderer.draw_debug_ui();
|
||||
}
|
||||
|
||||
for ui_event in &ui_events {
|
||||
self.dirty = true;
|
||||
self.renderer.debug_ui.ui.event_queue.push(*ui_event);
|
||||
}
|
||||
|
||||
self.renderer.debug_ui.ui.event = ui_event;
|
||||
self.renderer.debug_ui.ui.mouse_position = get_mouse_position(&self.window,
|
||||
self.scale_factor);
|
||||
self.ui.show_text_effects = self.scene_is_monochrome;
|
||||
|
@ -350,39 +389,43 @@ impl<W> DemoApp<W> where W: Window {
|
|||
&mut self.renderer.debug_ui,
|
||||
&mut ui_action);
|
||||
|
||||
ui_event = mem::replace(&mut self.renderer.debug_ui.ui.event, UIEvent::None);
|
||||
ui_events = self.renderer.debug_ui.ui.event_queue.drain();
|
||||
self.handle_ui_action(&mut ui_action);
|
||||
|
||||
// Switch camera mode (2D/3D) if requested.
|
||||
//
|
||||
// FIXME(pcwalton): This mess should really be an MVC setup.
|
||||
match (&self.camera, self.ui.three_d_enabled) {
|
||||
(&Camera::TwoD { .. }, true) => self.camera = Camera::new_3d(self.scene_view_box),
|
||||
(&Camera::ThreeD { .. }, false) => {
|
||||
match (&self.camera, self.ui.mode) {
|
||||
(&Camera::TwoD { .. }, Mode::ThreeD) | (&Camera::TwoD { .. }, Mode::VR) => {
|
||||
self.camera = Camera::new_3d(self.scene_view_box);
|
||||
}
|
||||
(&Camera::ThreeD { .. }, Mode::TwoD) => {
|
||||
let drawable_size = self.window.drawable_size();
|
||||
self.camera = Camera::new_2d(self.scene_view_box, drawable_size);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
for ui_event in 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 { relative_position, .. } => {
|
||||
UIEvent::MouseDragged(position) => {
|
||||
if let Camera::TwoD(ref mut transform) = self.camera {
|
||||
*transform = transform.post_translate(relative_position.to_f32());
|
||||
*transform = transform.post_translate(position.relative.to_f32());
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
self.window.present();
|
||||
self.frame_counter += 1;
|
||||
}
|
||||
|
||||
fn draw_environment(&self, render_transform: &RenderTransform) {
|
||||
fn draw_environment(&self, viewport_index: usize, render_transform: &RenderTransform) {
|
||||
let perspective = match *render_transform {
|
||||
RenderTransform::Transform2D(..) => return,
|
||||
RenderTransform::Perspective(perspective) => perspective,
|
||||
|
@ -449,7 +492,12 @@ impl<W> DemoApp<W> where W: Window {
|
|||
});
|
||||
}
|
||||
|
||||
fn render_vector_scene(&mut self, built_scene: &BuiltScene) {
|
||||
fn render_vector_scene(&mut self, viewport_index: usize, built_scene: &BuiltScene) {
|
||||
let view_box_size = view_box_size(self.ui.mode, &self.window);
|
||||
let viewport_origin_x = viewport_index as i32 * view_box_size.x();
|
||||
let viewport = RectI32::new(Point2DI32::new(viewport_origin_x, 0), view_box_size);
|
||||
self.renderer.set_viewport(viewport);
|
||||
|
||||
if self.ui.gamma_correction_effect_enabled {
|
||||
self.renderer.enable_gamma_correction(self.background_color());
|
||||
} else {
|
||||
|
@ -462,10 +510,10 @@ impl<W> DemoApp<W> where W: Window {
|
|||
self.renderer.disable_subpixel_aa();
|
||||
}
|
||||
|
||||
if self.ui.three_d_enabled {
|
||||
self.renderer.enable_depth();
|
||||
} else {
|
||||
if self.ui.mode == Mode::TwoD {
|
||||
self.renderer.disable_depth();
|
||||
} else {
|
||||
self.renderer.enable_depth();
|
||||
}
|
||||
|
||||
self.renderer.render_scene(&built_scene);
|
||||
|
@ -485,10 +533,10 @@ impl<W> DemoApp<W> where W: Window {
|
|||
self.scene_thread_proxy.set_drawable_size(self.window.drawable_size());
|
||||
let drawable_size = self.window.drawable_size();
|
||||
|
||||
self.camera = if self.ui.three_d_enabled {
|
||||
Camera::new_3d(built_svg.scene.view_box)
|
||||
} else {
|
||||
self.camera = if self.ui.mode == Mode::TwoD {
|
||||
Camera::new_2d(built_svg.scene.view_box, drawable_size)
|
||||
} else {
|
||||
Camera::new_3d(built_svg.scene.view_box)
|
||||
};
|
||||
|
||||
self.scene_thread_proxy.load_scene(built_svg.scene);
|
||||
|
@ -546,6 +594,7 @@ impl<W> DemoApp<W> where W: Window {
|
|||
fn background_color(&self) -> ColorU {
|
||||
if self.ui.dark_background_enabled { DARK_BG_COLOR } else { LIGHT_BG_COLOR }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct SceneThreadProxy {
|
||||
|
@ -593,15 +642,18 @@ impl SceneThread {
|
|||
self.scene.view_box = RectF32::new(Point2DF32::default(), size.to_f32());
|
||||
}
|
||||
MainToSceneMsg::Build(build_options) => {
|
||||
let render_transform = build_options.render_transform.clone();
|
||||
let start_time = Instant::now();
|
||||
let built_scene = build_scene(&self.scene, build_options, self.options.jobs);
|
||||
let render_scenes = build_options.render_transforms
|
||||
.iter()
|
||||
.map(|render_transform| {
|
||||
let built_scene = build_scene(&self.scene,
|
||||
&build_options,
|
||||
(*render_transform).clone(),
|
||||
self.options.jobs);
|
||||
RenderScene { built_scene, transform: (*render_transform).clone() }
|
||||
}).collect();
|
||||
let tile_time = Instant::now() - start_time;
|
||||
self.sender.send(SceneToMainMsg::Render {
|
||||
built_scene,
|
||||
transform: render_transform,
|
||||
tile_time,
|
||||
}).unwrap();
|
||||
self.sender.send(SceneToMainMsg::Render { render_scenes, tile_time }).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -615,18 +667,23 @@ enum MainToSceneMsg {
|
|||
}
|
||||
|
||||
struct BuildOptions {
|
||||
render_transform: RenderTransform,
|
||||
render_transforms: Vec<RenderTransform>,
|
||||
stem_darkening_font_size: Option<f32>,
|
||||
}
|
||||
|
||||
enum SceneToMainMsg {
|
||||
Render { built_scene: BuiltScene, transform: RenderTransform, tile_time: Duration }
|
||||
Render { render_scenes: Vec<RenderScene>, tile_time: Duration }
|
||||
}
|
||||
|
||||
pub struct RenderScene {
|
||||
built_scene: BuiltScene,
|
||||
transform: RenderTransform,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Options {
|
||||
jobs: Option<usize>,
|
||||
three_d: bool,
|
||||
mode: Mode,
|
||||
input_path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
|
@ -641,14 +698,22 @@ impl Options {
|
|||
.takes_value(true)
|
||||
.help("Number of threads to use"),
|
||||
)
|
||||
.arg(Arg::with_name("3d").short("3").long("3d").help("Run in 3D"))
|
||||
.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("INPUT").help("Path to the SVG file to render").index(1))
|
||||
.get_matches();
|
||||
|
||||
let jobs: Option<usize> = matches
|
||||
.value_of("jobs")
|
||||
.map(|string| string.parse().unwrap());
|
||||
let three_d = matches.is_present("3d");
|
||||
|
||||
let mode = if matches.is_present("3d") {
|
||||
Mode::ThreeD
|
||||
} else if matches.is_present("vr") {
|
||||
Mode::VR
|
||||
} else {
|
||||
Mode::TwoD
|
||||
};
|
||||
|
||||
let input_path = matches.value_of("INPUT").map(PathBuf::from);
|
||||
|
||||
|
@ -659,10 +724,29 @@ impl Options {
|
|||
}
|
||||
thread_pool_builder.build_global().unwrap();
|
||||
|
||||
Options { jobs, three_d, input_path }
|
||||
Options { jobs, mode, input_path }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub enum Mode {
|
||||
TwoD = 0,
|
||||
ThreeD = 1,
|
||||
VR = 2,
|
||||
}
|
||||
|
||||
impl Mode {
|
||||
fn viewport_count(self) -> usize {
|
||||
match self { Mode::TwoD | Mode::ThreeD => 1, Mode::VR => 2 }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct RenderStats {
|
||||
rendering_time: Option<Duration>,
|
||||
stats: Stats,
|
||||
}
|
||||
|
||||
fn load_scene(resource_loader: &dyn ResourceLoader, input_path: &Option<PathBuf>) -> BuiltSVG {
|
||||
let mut data;
|
||||
match *input_path {
|
||||
|
@ -680,11 +764,15 @@ fn load_scene(resource_loader: &dyn ResourceLoader, input_path: &Option<PathBuf>
|
|||
BuiltSVG::from_tree(Tree::from_data(&data, &UsvgOptions::default()).unwrap())
|
||||
}
|
||||
|
||||
fn build_scene(scene: &Scene, build_options: BuildOptions, jobs: Option<usize>) -> BuiltScene {
|
||||
fn build_scene(scene: &Scene,
|
||||
build_options: &BuildOptions,
|
||||
render_transform: RenderTransform,
|
||||
jobs: Option<usize>)
|
||||
-> BuiltScene {
|
||||
let z_buffer = ZBuffer::new(scene.view_box);
|
||||
|
||||
let render_options = RenderOptions {
|
||||
transform: build_options.render_transform,
|
||||
transform: render_transform,
|
||||
dilation: match build_options.stem_darkening_font_size {
|
||||
None => Point2DF32::default(),
|
||||
Some(font_size) => {
|
||||
|
@ -835,3 +923,11 @@ fn emit_message<W>(ui: &mut DemoUI<GLDevice>,
|
|||
W::push_user_event(expire_message_event_id, expected_epoch);
|
||||
});
|
||||
}
|
||||
|
||||
fn view_box_size<W>(mode: Mode, window: &W) -> Point2DI32 where W: Window {
|
||||
let window_drawable_size = window.drawable_size();
|
||||
match mode {
|
||||
Mode::TwoD | Mode::ThreeD => window_drawable_size,
|
||||
Mode::VR => Point2DI32::new(window_drawable_size.x() / 2, window_drawable_size.y()),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use crate::Options;
|
||||
use crate::{Mode, Options};
|
||||
use crate::window::Window;
|
||||
use pathfinder_geometry::basic::point::Point2DI32;
|
||||
use pathfinder_geometry::basic::rect::RectI32;
|
||||
|
@ -16,7 +16,7 @@ use pathfinder_gpu::Device;
|
|||
use pathfinder_gpu::resources::ResourceLoader;
|
||||
use pathfinder_renderer::gpu::debug::DebugUI;
|
||||
use pathfinder_ui::{BUTTON_HEIGHT, BUTTON_TEXT_OFFSET, BUTTON_WIDTH, FONT_ASCENT, PADDING};
|
||||
use pathfinder_ui::{SWITCH_SIZE, TEXT_COLOR, TOOLTIP_HEIGHT, WINDOW_COLOR};
|
||||
use pathfinder_ui::{TEXT_COLOR, TOOLTIP_HEIGHT, WINDOW_COLOR};
|
||||
use std::f32::consts::PI;
|
||||
use std::path::PathBuf;
|
||||
|
||||
|
@ -56,7 +56,7 @@ pub struct DemoUI<D> where D: Device {
|
|||
|
||||
// FIXME(pcwalton): Factor the below out into a model class.
|
||||
|
||||
pub three_d_enabled: bool,
|
||||
pub mode: Mode,
|
||||
pub dark_background_enabled: bool,
|
||||
pub gamma_correction_effect_enabled: bool,
|
||||
pub stem_darkening_effect_enabled: bool,
|
||||
|
@ -90,7 +90,7 @@ impl<D> DemoUI<D> where D: Device {
|
|||
effects_panel_visible: false,
|
||||
rotate_panel_visible: false,
|
||||
|
||||
three_d_enabled: options.three_d,
|
||||
mode: options.mode,
|
||||
dark_background_enabled: false,
|
||||
gamma_correction_effect_enabled: false,
|
||||
stem_darkening_effect_enabled: false,
|
||||
|
@ -121,7 +121,6 @@ impl<D> DemoUI<D> where D: Device {
|
|||
let mut position = Point2DI32::new(PADDING, bottom - BUTTON_HEIGHT);
|
||||
|
||||
let button_size = Point2DI32::new(BUTTON_WIDTH, BUTTON_HEIGHT);
|
||||
let switch_size = Point2DI32::new(SWITCH_SIZE, BUTTON_HEIGHT);
|
||||
|
||||
// Draw text effects button.
|
||||
if self.show_text_effects {
|
||||
|
@ -158,14 +157,24 @@ impl<D> DemoUI<D> where D: Device {
|
|||
debug_ui.ui.draw_tooltip(device, "Take Screenshot", RectI32::new(position, button_size));
|
||||
position += Point2DI32::new(BUTTON_WIDTH + PADDING, 0);
|
||||
|
||||
// Draw 3D switch.
|
||||
self.three_d_enabled = debug_ui.ui.draw_text_switch(device,
|
||||
// Draw mode switch.
|
||||
let new_mode = debug_ui.ui.draw_text_switch(device,
|
||||
position,
|
||||
"2D",
|
||||
"3D",
|
||||
self.three_d_enabled);
|
||||
debug_ui.ui.draw_tooltip(device, "2D/3D Mode", RectI32::new(position, switch_size));
|
||||
position += Point2DI32::new(SWITCH_SIZE + PADDING, 0);
|
||||
&["2D", "3D", "VR"],
|
||||
self.mode as u8);
|
||||
if new_mode != self.mode as u8 {
|
||||
self.mode = match new_mode {
|
||||
0 => Mode::TwoD,
|
||||
1 => Mode::ThreeD,
|
||||
_ => Mode::VR,
|
||||
};
|
||||
}
|
||||
let mode_switch_width = debug_ui.ui.measure_switch(3);
|
||||
let mode_switch_size = Point2DI32::new(mode_switch_width, BUTTON_HEIGHT);
|
||||
debug_ui.ui.draw_tooltip(device,
|
||||
"2D/3D/VR Mode",
|
||||
RectI32::new(position, mode_switch_size));
|
||||
position += Point2DI32::new(mode_switch_width + PADDING, 0);
|
||||
|
||||
// Draw background switch.
|
||||
self.dark_background_enabled = debug_ui.ui.draw_image_switch(device,
|
||||
|
@ -173,14 +182,18 @@ impl<D> DemoUI<D> where D: Device {
|
|||
&self.bg_light_texture,
|
||||
&self.bg_dark_texture,
|
||||
self.dark_background_enabled);
|
||||
debug_ui.ui.draw_tooltip(device, "Background Color", RectI32::new(position, switch_size));
|
||||
position += Point2DI32::new(SWITCH_SIZE + PADDING, 0);
|
||||
let background_color_switch_width = debug_ui.ui.measure_switch(2);
|
||||
let background_color_switch_size = Point2DI32::new(background_color_switch_width,
|
||||
BUTTON_HEIGHT);
|
||||
let background_color_switch_rect = RectI32::new(position, background_color_switch_size);
|
||||
debug_ui.ui.draw_tooltip(device, "Background Color", background_color_switch_rect);
|
||||
position += Point2DI32::new(background_color_switch_width + PADDING, 0);
|
||||
|
||||
// Draw effects panel, if necessary.
|
||||
self.draw_effects_panel(device, debug_ui);
|
||||
|
||||
// Draw rotate and zoom buttons, if applicable.
|
||||
if self.three_d_enabled {
|
||||
if self.mode != Mode::TwoD {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -281,7 +294,7 @@ impl<D> DemoUI<D> where D: Device {
|
|||
let widget_rect = RectI32::new(Point2DI32::new(widget_x, widget_y),
|
||||
Point2DI32::new(SLIDER_WIDTH, SLIDER_KNOB_HEIGHT));
|
||||
if let Some(position) = debug_ui.ui
|
||||
.event
|
||||
.event_queue
|
||||
.handle_mouse_down_or_dragged_in_rect(widget_rect) {
|
||||
self.rotation = position.x();
|
||||
*action = UIAction::Rotate(self.rotation());
|
||||
|
@ -313,10 +326,11 @@ impl<D> DemoUI<D> where D: Device {
|
|||
let text_y = window_y + PADDING + BUTTON_TEXT_OFFSET + (BUTTON_HEIGHT + PADDING) * index;
|
||||
debug_ui.ui.draw_text(device, text, Point2DI32::new(text_x, text_y), false);
|
||||
|
||||
let switch_x = PADDING + EFFECTS_PANEL_WIDTH - (SWITCH_SIZE + PADDING);
|
||||
let switch_width = debug_ui.ui.measure_switch(2);
|
||||
let switch_x = PADDING + EFFECTS_PANEL_WIDTH - (switch_width + PADDING);
|
||||
let switch_y = window_y + PADDING + (BUTTON_HEIGHT + PADDING) * index;
|
||||
let switch_position = Point2DI32::new(switch_x, switch_y);
|
||||
debug_ui.ui.draw_text_switch(device, switch_position, "Off", "On", value)
|
||||
debug_ui.ui.draw_text_switch(device, switch_position, &["Off", "On"], value as u8) != 0
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,8 +36,8 @@ pub enum Event {
|
|||
KeyDown(Keycode),
|
||||
KeyUp(Keycode),
|
||||
MouseDown(Point2DI32),
|
||||
MouseMoved { position: Point2DI32, relative_position: Point2DI32 },
|
||||
MouseDragged { position: Point2DI32, relative_position: Point2DI32 },
|
||||
MouseMoved(Point2DI32),
|
||||
MouseDragged(Point2DI32),
|
||||
Zoom(f32),
|
||||
User { message_type: u32, message_data: u32 },
|
||||
}
|
||||
|
|
|
@ -167,13 +167,12 @@ impl WindowImpl {
|
|||
SDLEvent::MouseButtonDown { x, y, .. } => {
|
||||
Some(Event::MouseDown(Point2DI32::new(x, y)))
|
||||
}
|
||||
SDLEvent::MouseMotion { x, y, xrel, yrel, mousestate, .. } => {
|
||||
SDLEvent::MouseMotion { x, y, mousestate, .. } => {
|
||||
let position = Point2DI32::new(x, y);
|
||||
let relative_position = Point2DI32::new(xrel, yrel);
|
||||
if mousestate.left() {
|
||||
Some(Event::MouseDragged { position, relative_position })
|
||||
Some(Event::MouseDragged(position))
|
||||
} else {
|
||||
Some(Event::MouseMoved { position, relative_position })
|
||||
Some(Event::MouseMoved(position))
|
||||
}
|
||||
}
|
||||
SDLEvent::Quit { .. } => Some(Event::Quit),
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
use gl::types::{GLboolean, GLchar, GLenum, GLfloat, GLint, GLsizei, GLsizeiptr, GLuint, GLvoid};
|
||||
use pathfinder_geometry::basic::point::Point2DI32;
|
||||
use pathfinder_geometry::basic::rect::RectI32;
|
||||
use pathfinder_gpu::{BlendState, BufferTarget, BufferUploadMode, DepthFunc, Device, Primitive};
|
||||
use pathfinder_gpu::{RenderState, ShaderKind, StencilFunc, TextureFormat};
|
||||
use pathfinder_gpu::{UniformData, VertexAttrType};
|
||||
|
@ -584,10 +585,13 @@ impl Device for GLDevice {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn bind_default_framebuffer(&self, size: Point2DI32) {
|
||||
fn bind_default_framebuffer(&self, viewport: RectI32) {
|
||||
unsafe {
|
||||
gl::BindFramebuffer(gl::FRAMEBUFFER, 0); ck();
|
||||
gl::Viewport(0, 0, size.x(), size.y()); ck();
|
||||
gl::Viewport(viewport.origin().x(),
|
||||
viewport.origin().y(),
|
||||
viewport.size().x(),
|
||||
viewport.size().y()); ck();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
use crate::resources::ResourceLoader;
|
||||
use image::ImageFormat;
|
||||
use pathfinder_geometry::basic::point::Point2DI32;
|
||||
use pathfinder_geometry::basic::rect::RectI32;
|
||||
use pathfinder_simd::default::F32x4;
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
|
@ -91,7 +92,7 @@ pub trait Device {
|
|||
// TODO(pcwalton): Go bindless...
|
||||
fn bind_vertex_array(&self, vertex_array: &Self::VertexArray);
|
||||
fn bind_buffer(&self, buffer: &Self::Buffer, target: BufferTarget);
|
||||
fn bind_default_framebuffer(&self, size: Point2DI32);
|
||||
fn bind_default_framebuffer(&self, viewport: RectI32);
|
||||
fn bind_framebuffer(&self, framebuffer: &Self::Framebuffer);
|
||||
fn bind_texture(&self, texture: &Self::Texture, unit: u32);
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ use crate::post::DefringingKernel;
|
|||
use crate::scene::ObjectShader;
|
||||
use crate::tiles::{TILE_HEIGHT, TILE_WIDTH};
|
||||
use pathfinder_geometry::basic::point::{Point2DI32, Point3DF32};
|
||||
use pathfinder_geometry::basic::rect::RectI32;
|
||||
use pathfinder_geometry::color::ColorU;
|
||||
use pathfinder_gpu::resources::ResourceLoader;
|
||||
use pathfinder_gpu::{BlendState, BufferTarget, BufferUploadMode, DepthFunc, DepthState, Device};
|
||||
|
@ -68,13 +69,17 @@ pub struct Renderer<D> where D: Device {
|
|||
pub debug_ui: DebugUI<D>,
|
||||
|
||||
// Extra info
|
||||
viewport: RectI32,
|
||||
main_framebuffer_size: Point2DI32,
|
||||
postprocess_options: PostprocessOptions,
|
||||
use_depth: bool,
|
||||
}
|
||||
|
||||
impl<D> Renderer<D> where D: Device {
|
||||
pub fn new(device: D, resources: &dyn ResourceLoader, main_framebuffer_size: Point2DI32)
|
||||
pub fn new(device: D,
|
||||
resources: &dyn ResourceLoader,
|
||||
viewport: RectI32,
|
||||
main_framebuffer_size: Point2DI32)
|
||||
-> Renderer<D> {
|
||||
let fill_program = FillProgram::new(&device, resources);
|
||||
let solid_tile_program = SolidTileProgram::new(&device, resources);
|
||||
|
@ -144,6 +149,7 @@ impl<D> Renderer<D> where D: Device {
|
|||
|
||||
debug_ui,
|
||||
|
||||
viewport,
|
||||
main_framebuffer_size,
|
||||
postprocess_options: PostprocessOptions::default(),
|
||||
use_depth: false,
|
||||
|
@ -181,6 +187,11 @@ impl<D> Renderer<D> where D: Device {
|
|||
self.pending_timer_queries.push_back(timer_query);
|
||||
}
|
||||
|
||||
pub fn draw_debug_ui(&self) {
|
||||
self.device.bind_default_framebuffer(self.viewport);
|
||||
self.debug_ui.draw(&self.device);
|
||||
}
|
||||
|
||||
pub fn shift_timer_query(&mut self) -> Option<Duration> {
|
||||
let query = self.pending_timer_queries.front()?;
|
||||
if !self.device.timer_query_is_available(&query) {
|
||||
|
@ -192,9 +203,13 @@ impl<D> Renderer<D> where D: Device {
|
|||
Some(result)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_viewport(&mut self, new_viewport: RectI32) {
|
||||
self.viewport = new_viewport;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_main_framebuffer_size(&mut self, new_framebuffer_size: Point2DI32) {
|
||||
self.main_framebuffer_size = new_framebuffer_size;
|
||||
self.debug_ui.ui.set_framebuffer_size(new_framebuffer_size);
|
||||
}
|
||||
|
||||
|
@ -299,7 +314,7 @@ impl<D> Renderer<D> where D: Device {
|
|||
self.device.bind_vertex_array(&self.mask_tile_vertex_array.vertex_array);
|
||||
self.device.use_program(&self.mask_tile_program.program);
|
||||
self.device.set_uniform(&self.mask_tile_program.framebuffer_size_uniform,
|
||||
UniformData::Vec2(self.main_framebuffer_size.0.to_f32x4()));
|
||||
UniformData::Vec2(self.viewport.size().to_f32().0));
|
||||
self.device.set_uniform(&self.mask_tile_program.tile_size_uniform,
|
||||
UniformData::Vec2(I32x4::new(TILE_WIDTH as i32,
|
||||
TILE_HEIGHT as i32,
|
||||
|
@ -339,7 +354,7 @@ impl<D> Renderer<D> where D: Device {
|
|||
self.device.bind_vertex_array(&self.solid_tile_vertex_array.vertex_array);
|
||||
self.device.use_program(&self.solid_tile_program.program);
|
||||
self.device.set_uniform(&self.solid_tile_program.framebuffer_size_uniform,
|
||||
UniformData::Vec2(self.main_framebuffer_size.0.to_f32x4()));
|
||||
UniformData::Vec2(self.viewport.size().0.to_f32x4()));
|
||||
self.device.set_uniform(&self.solid_tile_program.tile_size_uniform,
|
||||
UniformData::Vec2(I32x4::new(TILE_WIDTH as i32,
|
||||
TILE_HEIGHT as i32,
|
||||
|
@ -365,12 +380,12 @@ impl<D> Renderer<D> where D: Device {
|
|||
}
|
||||
|
||||
fn postprocess(&mut self) {
|
||||
self.device.bind_default_framebuffer(self.main_framebuffer_size);
|
||||
self.device.bind_default_framebuffer(self.viewport);
|
||||
|
||||
self.device.bind_vertex_array(&self.postprocess_vertex_array.vertex_array);
|
||||
self.device.use_program(&self.postprocess_program.program);
|
||||
self.device.set_uniform(&self.postprocess_program.framebuffer_size_uniform,
|
||||
UniformData::Vec2(self.main_framebuffer_size.to_f32().0));
|
||||
UniformData::Vec2(self.viewport.size().to_f32().0));
|
||||
match self.postprocess_options.defringing_kernel {
|
||||
Some(ref kernel) => {
|
||||
self.device.set_uniform(&self.postprocess_program.kernel_uniform,
|
||||
|
@ -434,7 +449,7 @@ impl<D> Renderer<D> where D: Device {
|
|||
if self.postprocessing_needed() {
|
||||
self.device.bind_framebuffer(self.postprocess_source_framebuffer.as_ref().unwrap());
|
||||
} else {
|
||||
self.device.bind_default_framebuffer(self.main_framebuffer_size);
|
||||
self.device.bind_default_framebuffer(self.viewport);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -447,10 +462,10 @@ impl<D> Renderer<D> where D: Device {
|
|||
match self.postprocess_source_framebuffer {
|
||||
Some(ref framebuffer) if
|
||||
self.device.texture_size(self.device.framebuffer_texture(framebuffer)) ==
|
||||
self.main_framebuffer_size => {}
|
||||
self.viewport.size() => {}
|
||||
_ => {
|
||||
let texture = self.device.create_texture(TextureFormat::RGBA8,
|
||||
self.main_framebuffer_size);
|
||||
self.viewport.size());
|
||||
self.postprocess_source_framebuffer = Some(self.device.create_framebuffer(texture))
|
||||
}
|
||||
};
|
||||
|
|
|
@ -18,6 +18,7 @@ use pathfinder_geometry::basic::point::{Point2DF32, Point3DF32};
|
|||
use pathfinder_geometry::basic::rect::{RectF32, RectI32};
|
||||
use pathfinder_geometry::util;
|
||||
use pathfinder_simd::default::{F32x4, I32x4};
|
||||
use std::ops::Add;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BuiltObject {
|
||||
|
@ -306,3 +307,15 @@ impl TileObjectPrimitive {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<Stats> for Stats {
|
||||
type Output = Stats;
|
||||
fn add(self, other: Stats) -> Stats {
|
||||
Stats {
|
||||
object_count: other.object_count,
|
||||
solid_tile_count: other.solid_tile_count,
|
||||
mask_tile_count: other.mask_tile_count,
|
||||
fill_count: other.fill_count,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
153
ui/src/lib.rs
153
ui/src/lib.rs
|
@ -25,8 +25,7 @@ use pathfinder_gpu::{BlendState, BufferTarget, BufferUploadMode, Device, Primiti
|
|||
use pathfinder_gpu::{UniformData, VertexAttrType};
|
||||
use pathfinder_simd::default::F32x4;
|
||||
use serde_json;
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
use std::mem;
|
||||
|
||||
pub const PADDING: i32 = 12;
|
||||
|
||||
|
@ -37,8 +36,6 @@ pub const BUTTON_WIDTH: i32 = PADDING * 2 + ICON_SIZE;
|
|||
pub const BUTTON_HEIGHT: i32 = PADDING * 2 + ICON_SIZE;
|
||||
pub const BUTTON_TEXT_OFFSET: i32 = PADDING + 36;
|
||||
|
||||
pub const SWITCH_SIZE: i32 = SWITCH_HALF_SIZE * 2 + 1;
|
||||
|
||||
pub const TOOLTIP_HEIGHT: i32 = FONT_ASCENT + PADDING * 2;
|
||||
|
||||
const DEBUG_TEXTURE_VERTEX_SIZE: usize = 8;
|
||||
|
@ -46,7 +43,7 @@ const DEBUG_SOLID_VERTEX_SIZE: usize = 4;
|
|||
|
||||
const ICON_SIZE: i32 = 48;
|
||||
|
||||
const SWITCH_HALF_SIZE: i32 = 96;
|
||||
const SWITCH_SEGMENT_SIZE: i32 = 96;
|
||||
|
||||
pub static TEXT_COLOR: ColorU = ColorU { r: 255, g: 255, b: 255, a: 255 };
|
||||
pub static WINDOW_COLOR: ColorU = ColorU { r: 0, g: 0, b: 0, a: 255 - 90 };
|
||||
|
@ -67,7 +64,7 @@ static RECT_LINE_INDICES: [u32; 8] = [0, 1, 1, 2, 2, 3, 3, 0];
|
|||
static OUTLINE_RECT_LINE_INDICES: [u32; 8] = [0, 1, 2, 3, 4, 5, 6, 7];
|
||||
|
||||
pub struct UI<D> where D: Device {
|
||||
pub event: UIEvent,
|
||||
pub event_queue: UIEventQueue,
|
||||
pub mouse_position: Point2DF32,
|
||||
|
||||
framebuffer_size: Point2DI32,
|
||||
|
@ -98,7 +95,7 @@ impl<D> UI<D> where D: Device {
|
|||
CORNER_OUTLINE_PNG_NAME);
|
||||
|
||||
UI {
|
||||
event: UIEvent::None,
|
||||
event_queue: UIEventQueue::new(),
|
||||
mouse_position: Point2DF32::default(),
|
||||
|
||||
framebuffer_size,
|
||||
|
@ -251,6 +248,11 @@ impl<D> UI<D> where D: Device {
|
|||
next
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn measure_switch(&self, segment_count: u8) -> i32 {
|
||||
SWITCH_SEGMENT_SIZE * segment_count as i32 + (segment_count - 1) as i32
|
||||
}
|
||||
|
||||
pub fn draw_solid_rounded_rect(&self, device: &D, rect: RectI32, color: ColorU) {
|
||||
let corner_texture = self.corner_texture(true);
|
||||
let corner_rects = CornerRects::new(device, rect, corner_texture);
|
||||
|
@ -311,6 +313,13 @@ impl<D> UI<D> where D: Device {
|
|||
self.draw_solid_rects_with_vertex_data(device, &vertex_data, index_data, color, false);
|
||||
}
|
||||
|
||||
// TODO(pcwalton): `LineSegmentI32`.
|
||||
fn draw_line(&self, device: &D, from: Point2DI32, to: Point2DI32, color: ColorU) {
|
||||
let vertex_data = vec![DebugSolidVertex::new(from), DebugSolidVertex::new(to)];
|
||||
self.draw_solid_rects_with_vertex_data(device, &vertex_data, &[0, 1], color, false);
|
||||
|
||||
}
|
||||
|
||||
fn draw_rounded_rect_corners(&self,
|
||||
device: &D,
|
||||
color: ColorU,
|
||||
|
@ -409,30 +418,32 @@ impl<D> UI<D> where D: Device {
|
|||
origin + Point2DI32::new(PADDING, PADDING),
|
||||
texture,
|
||||
BUTTON_ICON_COLOR);
|
||||
self.event.handle_mouse_down_in_rect(button_rect).is_some()
|
||||
self.event_queue.handle_mouse_down_in_rect(button_rect).is_some()
|
||||
}
|
||||
|
||||
pub fn draw_text_switch(&mut self,
|
||||
device: &D,
|
||||
origin: Point2DI32,
|
||||
off_text: &str,
|
||||
on_text: &str,
|
||||
mut value: bool)
|
||||
-> bool {
|
||||
value = self.draw_switch(device, origin, value);
|
||||
mut origin: Point2DI32,
|
||||
segment_labels: &[&str],
|
||||
mut value: u8)
|
||||
-> u8 {
|
||||
value = self.draw_switch(device, origin, value, segment_labels.len() as u8);
|
||||
|
||||
let off_size = self.measure_text(off_text);
|
||||
let on_size = self.measure_text(on_text);
|
||||
let off_offset = SWITCH_HALF_SIZE / 2 - off_size / 2;
|
||||
let on_offset = SWITCH_HALF_SIZE + SWITCH_HALF_SIZE / 2 - on_size / 2;
|
||||
let text_top = BUTTON_TEXT_OFFSET;
|
||||
|
||||
self.draw_text(device, off_text, origin + Point2DI32::new(off_offset, text_top), !value);
|
||||
self.draw_text(device, on_text, origin + Point2DI32::new(on_offset, text_top), value);
|
||||
origin = origin + Point2DI32::new(0, BUTTON_TEXT_OFFSET);
|
||||
for (segment_index, segment_label) in segment_labels.iter().enumerate() {
|
||||
let label_width = self.measure_text(segment_label);
|
||||
let offset = SWITCH_SEGMENT_SIZE / 2 - label_width / 2;
|
||||
self.draw_text(device,
|
||||
segment_label,
|
||||
origin + Point2DI32::new(offset, 0),
|
||||
segment_index as u8 == value);
|
||||
origin += Point2DI32::new(SWITCH_SEGMENT_SIZE + 1, 0);
|
||||
}
|
||||
|
||||
value
|
||||
}
|
||||
|
||||
/// TODO(pcwalton): Support switches with more than two segments.
|
||||
pub fn draw_image_switch(&mut self,
|
||||
device: &D,
|
||||
origin: Point2DI32,
|
||||
|
@ -440,10 +451,10 @@ impl<D> UI<D> where D: Device {
|
|||
on_texture: &D::Texture,
|
||||
mut value: bool)
|
||||
-> bool {
|
||||
value = self.draw_switch(device, origin, value);
|
||||
value = self.draw_switch(device, origin, value as u8, 2) != 0;
|
||||
|
||||
let off_offset = SWITCH_HALF_SIZE / 2 - device.texture_size(off_texture).x() / 2;
|
||||
let on_offset = SWITCH_HALF_SIZE + SWITCH_HALF_SIZE / 2 -
|
||||
let off_offset = SWITCH_SEGMENT_SIZE / 2 - device.texture_size(off_texture).x() / 2;
|
||||
let on_offset = SWITCH_SEGMENT_SIZE + SWITCH_SEGMENT_SIZE / 2 -
|
||||
device.texture_size(on_texture).x() / 2;
|
||||
|
||||
let off_color = if !value { WINDOW_COLOR } else { TEXT_COLOR };
|
||||
|
@ -461,24 +472,35 @@ impl<D> UI<D> where D: Device {
|
|||
value
|
||||
}
|
||||
|
||||
fn draw_switch(&mut self, device: &D, origin: Point2DI32, mut value: bool) -> bool {
|
||||
let widget_rect = RectI32::new(origin, Point2DI32::new(SWITCH_SIZE, BUTTON_HEIGHT));
|
||||
if self.event.handle_mouse_down_in_rect(widget_rect).is_some() {
|
||||
value = !value;
|
||||
fn draw_switch(&mut self, device: &D, origin: Point2DI32, mut value: u8, segment_count: u8)
|
||||
-> u8 {
|
||||
let widget_width = self.measure_switch(segment_count);
|
||||
let widget_rect = RectI32::new(origin, Point2DI32::new(widget_width, BUTTON_HEIGHT));
|
||||
if let Some(position) = self.event_queue.handle_mouse_down_in_rect(widget_rect) {
|
||||
let segment = (position.x() / (SWITCH_SEGMENT_SIZE + 1)) as u8;
|
||||
value = segment.min(segment_count - 1);
|
||||
}
|
||||
|
||||
self.draw_solid_rounded_rect(device, widget_rect, WINDOW_COLOR);
|
||||
self.draw_rounded_rect_outline(device, widget_rect, OUTLINE_COLOR);
|
||||
|
||||
let highlight_size = Point2DI32::new(SWITCH_HALF_SIZE, BUTTON_HEIGHT);
|
||||
if !value {
|
||||
self.draw_solid_rounded_rect(device, RectI32::new(origin, highlight_size), TEXT_COLOR);
|
||||
} else {
|
||||
let x_offset = SWITCH_HALF_SIZE + 1;
|
||||
let highlight_size = Point2DI32::new(SWITCH_SEGMENT_SIZE, BUTTON_HEIGHT);
|
||||
let x_offset = value as i32 * SWITCH_SEGMENT_SIZE + (value as i32 - 1);
|
||||
self.draw_solid_rounded_rect(device,
|
||||
RectI32::new(origin + Point2DI32::new(x_offset, 0),
|
||||
highlight_size),
|
||||
TEXT_COLOR);
|
||||
|
||||
let mut segment_origin = origin + Point2DI32::new(SWITCH_SEGMENT_SIZE + 1, 0);
|
||||
for next_segment_index in 1..segment_count {
|
||||
let prev_segment_index = next_segment_index - 1;
|
||||
if value != prev_segment_index && value != next_segment_index {
|
||||
self.draw_line(device,
|
||||
segment_origin,
|
||||
segment_origin + Point2DI32::new(0, BUTTON_HEIGHT),
|
||||
TEXT_COLOR);
|
||||
}
|
||||
segment_origin = segment_origin + Point2DI32::new(SWITCH_SEGMENT_SIZE + 1, 0);
|
||||
}
|
||||
|
||||
value
|
||||
|
@ -666,40 +688,63 @@ fn set_color_uniform<D>(device: &D, uniform: &D::Uniform, color: ColorU) where D
|
|||
device.set_uniform(uniform, UniformData::Vec4(color * F32x4::splat(1.0 / 255.0)));
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum UIEvent {
|
||||
None,
|
||||
MouseDown(Point2DI32),
|
||||
MouseDragged {
|
||||
absolute_position: Point2DI32,
|
||||
relative_position: Point2DI32,
|
||||
}
|
||||
MouseDown(MousePosition),
|
||||
MouseDragged(MousePosition),
|
||||
}
|
||||
|
||||
impl UIEvent {
|
||||
pub fn is_none(&self) -> bool {
|
||||
match *self { UIEvent::None => true, _ => false }
|
||||
pub struct UIEventQueue {
|
||||
events: Vec<UIEvent>,
|
||||
}
|
||||
|
||||
impl UIEventQueue {
|
||||
fn new() -> UIEventQueue {
|
||||
UIEventQueue { events: vec![] }
|
||||
}
|
||||
|
||||
pub fn push(&mut self, event: UIEvent) {
|
||||
self.events.push(event);
|
||||
}
|
||||
|
||||
pub fn drain(&mut self) -> Vec<UIEvent> {
|
||||
mem::replace(&mut self.events, vec![])
|
||||
}
|
||||
|
||||
fn handle_mouse_down_in_rect(&mut self, rect: RectI32) -> Option<Point2DI32> {
|
||||
if let UIEvent::MouseDown(point) = *self {
|
||||
if rect.contains_point(point) {
|
||||
*self = UIEvent::None;
|
||||
return Some(point - rect.origin());
|
||||
let (mut remaining_events, mut result) = (vec![], None);
|
||||
for event in self.events.drain(..) {
|
||||
match event {
|
||||
UIEvent::MouseDown(position) if rect.contains_point(position.absolute) => {
|
||||
result = Some(position.absolute - rect.origin());
|
||||
}
|
||||
event => remaining_events.push(event),
|
||||
}
|
||||
}
|
||||
None
|
||||
self.events = remaining_events;
|
||||
result
|
||||
}
|
||||
|
||||
pub fn handle_mouse_down_or_dragged_in_rect(&mut self, rect: RectI32) -> Option<Point2DI32> {
|
||||
match *self {
|
||||
UIEvent::MouseDown(point) | UIEvent::MouseDragged { absolute_position: point, .. }
|
||||
if rect.contains_point(point) => {
|
||||
*self = UIEvent::None;
|
||||
Some(point - rect.origin())
|
||||
let (mut remaining_events, mut result) = (vec![], None);
|
||||
for event in self.events.drain(..) {
|
||||
match event {
|
||||
UIEvent::MouseDown(position) | UIEvent::MouseDragged(position) if
|
||||
rect.contains_point(position.absolute) => {
|
||||
result = Some(position.absolute - rect.origin());
|
||||
}
|
||||
_ => None,
|
||||
event => remaining_events.push(event),
|
||||
}
|
||||
}
|
||||
self.events = remaining_events;
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct MousePosition {
|
||||
pub absolute: Point2DI32,
|
||||
pub relative: Point2DI32,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
|
|
Loading…
Reference in New Issue