From 140d58a16ce3c67c0eafce530e459fdec71a458b Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Thu, 24 Oct 2013 14:53:39 +0200 Subject: [PATCH 001/146] renamed GroupNode to TransformNode and added eyes --- .../gua/physics/CollisionShapeNodeVisitor.hpp | 7 +- include/gua/renderer/Camera.hpp | 14 ++- include/gua/renderer/Frustum.hpp | 2 + include/gua/renderer/SerializedScene.hpp | 11 +-- include/gua/renderer/Serializer.hpp | 27 +----- include/gua/scenegraph.hpp | 3 +- include/gua/scenegraph/NodeVisitor.hpp | 26 ++---- .../{GroupNode.hpp => TransformNode.hpp} | 8 +- include/gua/scenegraph/ViewNode.hpp | 76 ---------------- include/gua/utils/DotGenerator.hpp | 4 +- src/gua/physics/CollisionShapeNode.cpp | 4 +- src/gua/physics/RigidBodyNode.cpp | 4 +- src/gua/renderer/GeometryLoader.cpp | 4 +- src/gua/renderer/LightingPass.cpp | 2 +- src/gua/renderer/MeshLoader.cpp | 4 +- src/gua/renderer/Pass.cpp | 23 +---- src/gua/renderer/Pipeline.cpp | 86 ++++++++++++------- src/gua/renderer/Serializer.cpp | 32 +------ src/gua/scenegraph/SceneGraph.cpp | 4 +- .../{GroupNode.cpp => TransformNode.cpp} | 10 +-- src/gua/scenegraph/ViewNode.cpp | 45 ---------- src/gua/utils/DotGenerator.cpp | 19 +--- 22 files changed, 113 insertions(+), 302 deletions(-) rename include/gua/scenegraph/{GroupNode.hpp => TransformNode.hpp} (93%) delete mode 100644 include/gua/scenegraph/ViewNode.hpp rename src/gua/scenegraph/{GroupNode.cpp => TransformNode.cpp} (84%) delete mode 100644 src/gua/scenegraph/ViewNode.cpp diff --git a/include/gua/physics/CollisionShapeNodeVisitor.hpp b/include/gua/physics/CollisionShapeNodeVisitor.hpp index 9e9d62ba9..a86b3683b 100644 --- a/include/gua/physics/CollisionShapeNodeVisitor.hpp +++ b/include/gua/physics/CollisionShapeNodeVisitor.hpp @@ -28,9 +28,8 @@ #include #include -#include +#include #include -#include #include #include #include @@ -100,9 +99,7 @@ class CollisionShapeNodeVisitor : public NodeVisitor { * \param node Pointer to Node */ - /* virtual */ void visit(GroupNode* node) { generic_visit(node); } - - /* virtual */ void visit(ViewNode* node) { generic_visit(node); } + /* virtual */ void visit(TransformNode* node) { generic_visit(node); } /* virtual */ void visit(GeometryNode* node) { generic_visit(node); } diff --git a/include/gua/renderer/Camera.hpp b/include/gua/renderer/Camera.hpp index 309b14e17..64846e619 100644 --- a/include/gua/renderer/Camera.hpp +++ b/include/gua/renderer/Camera.hpp @@ -34,12 +34,18 @@ namespace gua { */ struct Camera { - Camera(std::string const& v = "unknown_view", std::string const& s = "unknown_screen", + Camera(std::string const& eye_l = "unknown_left_eye", + std::string const& eye_r = "unknown_right_eye", + std::string const& screen_l = "unknown_left_screen", + std::string const& screen_r = "unknown_right_screen", std::string const& g = "scene_graph", std::string const& m = "") - : view(v), screen(s), scene_graph(g), render_mask(m) {} + : eye_l(eye_l), eye_r(eye_r), screen_l(screen_l), screen_r(screen_r), + scene_graph(g), render_mask(m) {} - std::string view; - std::string screen; + std::string eye_l; + std::string eye_r; + std::string screen_l; + std::string screen_r; std::string scene_graph; std::string render_mask; }; diff --git a/include/gua/renderer/Frustum.hpp b/include/gua/renderer/Frustum.hpp index 4af7891da..1d90a4bf1 100644 --- a/include/gua/renderer/Frustum.hpp +++ b/include/gua/renderer/Frustum.hpp @@ -35,6 +35,8 @@ class Frustum { public: + Frustum() {} + Frustum(math::mat4 const& camera_transform, math::mat4 const& screen_transform, float clip_near, diff --git a/include/gua/renderer/SerializedScene.hpp b/include/gua/renderer/SerializedScene.hpp index c94b7251e..d03cf8c12 100644 --- a/include/gua/renderer/SerializedScene.hpp +++ b/include/gua/renderer/SerializedScene.hpp @@ -23,7 +23,6 @@ #define GUA_SERIALIZED_SCENE_HPP // guacamole headers -#include #include #include #include @@ -32,6 +31,7 @@ #include #include #include +#include // external headers #include @@ -70,14 +70,9 @@ struct SerializedScene { std::vector > spot_lights_; /** - * All cameras. + * The frustum. */ - SerializedNode view_; - - /** - * All screens. - */ - SerializedNode screen_; + Frustum frustum; /** * All used materials. diff --git a/include/gua/renderer/Serializer.hpp b/include/gua/renderer/Serializer.hpp index 927ef4ccd..bf9b03ffa 100644 --- a/include/gua/renderer/Serializer.hpp +++ b/include/gua/renderer/Serializer.hpp @@ -62,28 +62,18 @@ class Serializer : public NodeVisitor { void check(SerializedScene* output, SceneGraph const* scene_graph, Camera const& camera, - Frustum const& frustum, bool draw_bounding_boxes, bool draw_rays, bool enable_frustum_culling); /** - * Visits a GroupNode + * Visits a TransformNode * - * This function provides the interface to visit a GroupNode + * This function provides the interface to visit a TransformNode * - * \param cam Pointer to GroupNode + * \param cam Pointer to TransformNode */ - /* virtual */ void visit(GroupNode* cam); - - /** - * Visits a ViewNode - * - * This function provides the interface to visit a ViewNode - * - * \param cam Pointer to ViewNode - */ - /* virtual */ void visit(ViewNode* cam); + /* virtual */ void visit(TransformNode* cam); /** * Visits a GeometryNode @@ -103,15 +93,6 @@ class Serializer : public NodeVisitor { */ /* virtual */ void visit(PointLightNode* pointlight); - /** - * Visits a ScreenLightNode - * - * This function provides the interface to visit a ScreenLightNode - * - * \param screen Pointer to ScreenLightNode - */ - /* virtual */ void visit(ScreenNode* screen); - /** * Visits a SpotLightNode * diff --git a/include/gua/scenegraph.hpp b/include/gua/scenegraph.hpp index 6f9c173f0..937359718 100644 --- a/include/gua/scenegraph.hpp +++ b/include/gua/scenegraph.hpp @@ -27,11 +27,10 @@ // node headers #include -#include +#include #include #include #include #include -#include #endif // GUA_INCLUDE_SCENEGRAPH_HPP diff --git a/include/gua/scenegraph/NodeVisitor.hpp b/include/gua/scenegraph/NodeVisitor.hpp index c32003388..5aab79741 100644 --- a/include/gua/scenegraph/NodeVisitor.hpp +++ b/include/gua/scenegraph/NodeVisitor.hpp @@ -29,8 +29,7 @@ namespace gua { class Node; -class GroupNode; -class ViewNode; +class TransformNode; class GeometryNode; class PointLightNode; class ScreenNode; @@ -67,31 +66,22 @@ class NodeVisitor { virtual ~NodeVisitor(); /** - * Visits a GroupNode + * Visits a TransformNode * - * This function provides the interface to visit a GroupNode + * This function provides the interface to visit a TransformNode * - * \param cam Pointer to GroupNode + * \param cam Pointer to TransformNode */ virtual void visit(Node* node) {}; /** - * Visits a GroupNode + * Visits a TransformNode * - * This function provides the interface to visit a GroupNode + * This function provides the interface to visit a TransformNode * - * \param cam Pointer to GroupNode + * \param cam Pointer to TransformNode */ - virtual void visit(GroupNode* node) { visit(reinterpret_cast(node)); } - - /** - * Visits a ViewNode - * - * This function provides the interface to visit a ViewNode - * - * \param cam Pointer to ViewNode - */ - virtual void visit(ViewNode* node) { visit(reinterpret_cast(node)); } + virtual void visit(TransformNode* node) { visit(reinterpret_cast(node)); } /** * Visits a GeometryNode diff --git a/include/gua/scenegraph/GroupNode.hpp b/include/gua/scenegraph/TransformNode.hpp similarity index 93% rename from include/gua/scenegraph/GroupNode.hpp rename to include/gua/scenegraph/TransformNode.hpp index 11d64813f..b6e9713a3 100644 --- a/include/gua/scenegraph/GroupNode.hpp +++ b/include/gua/scenegraph/TransformNode.hpp @@ -31,20 +31,20 @@ namespace gua { -class GroupNode : public Node { +class TransformNode : public Node { public: - GroupNode() {}; + TransformNode() {}; /** * Constructor. * - * This constructs a GroupNode with the given parameters. + * This constructs a TransformNode with the given parameters. * * \param name The Node's name * \param transform The transformation of the object the Node contains. */ - GroupNode(std::string const& name, + TransformNode(std::string const& name, math::mat4 const& transform = math::mat4::identity()); /** diff --git a/include/gua/scenegraph/ViewNode.hpp b/include/gua/scenegraph/ViewNode.hpp deleted file mode 100644 index 9a74a159c..000000000 --- a/include/gua/scenegraph/ViewNode.hpp +++ /dev/null @@ -1,76 +0,0 @@ -/****************************************************************************** - * guacamole - delicious VR * - * * - * Copyright: (c) 2011-2013 Bauhaus-Universität Weimar * - * Contact: felix.lauer@uni-weimar.de / simon.schneegans@uni-weimar.de * - * * - * This program is free software: you can redistribute it and/or modify it * - * under the terms of the GNU General Public License as published by the Free * - * Software Foundation, either version 3 of the License, or (at your option) * - * any later version. * - * * - * This program is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * - * for more details. * - * * - * You should have received a copy of the GNU General Public License along * - * with this program. If not, see . * - * * - ******************************************************************************/ - -#ifndef GUA_VIEW_NODE_HPP -#define GUA_VIEW_NODE_HPP - -#include -#include - -/** - * This class is used to represent a camera in the SceneGraph. - * - */ - -namespace gua { - -class ViewNode : public Node { - public: - - struct Configuration { - GUA_ADD_PROPERTY(float, stereo_width, 0.07f); - }; - - Configuration data; - - ViewNode() {} - - /** - * Constructor. - * - * This constructs a ViewNode with the given parameters and calls - * the constructor of base class Core with the type CAMERA. - * - * \param stereo_width The gap between the eyes. - */ - ViewNode(std::string const& name, - Configuration const& configuration = Configuration(), - math::mat4 const& transform = math::mat4::identity()); - - /** - * Accepts a visitor and calls concrete visit method - * - * This method implements the visitor pattern for Nodes - * - */ - /* virtual */ void accept(NodeVisitor&); - - private: - - /** - * - */ - std::shared_ptr copy() const; -}; - -} - -#endif // GUA_VIEW_NODE_HPP diff --git a/include/gua/utils/DotGenerator.hpp b/include/gua/utils/DotGenerator.hpp index c91ea0328..52fa4b235 100644 --- a/include/gua/utils/DotGenerator.hpp +++ b/include/gua/utils/DotGenerator.hpp @@ -34,7 +34,6 @@ namespace gua { class SceneGraph; class Node; -class ViewNode; class GeometryNode; class PointLightNode; class ScreenNode; @@ -65,8 +64,7 @@ class DotGenerator : public NodeVisitor { * Visiters for each Node type */ /*virtual*/ void visit(Node* node); - /*virtual*/ void visit(GroupNode* cam); - /*virtual*/ void visit(ViewNode* cam); + /*virtual*/ void visit(TransformNode* cam); /*virtual*/ void visit(GeometryNode* geometry); /*virtual*/ void visit(PointLightNode* pointlight); /*virtual*/ void visit(ScreenNode* screen); diff --git a/src/gua/physics/CollisionShapeNode.cpp b/src/gua/physics/CollisionShapeNode.cpp index 13db87202..64bca182d 100644 --- a/src/gua/physics/CollisionShapeNode.cpp +++ b/src/gua/physics/CollisionShapeNode.cpp @@ -23,7 +23,7 @@ #include // guacamole headers -#include +#include #include namespace gua { @@ -49,7 +49,7 @@ CollisionShapeNode::~CollisionShapeNode() {} //////////////////////////////////////////////////////////////////////////////// std::shared_ptr CollisionShapeNode::copy() const { - return std::make_shared(get_name(), get_transform()); + return std::make_shared(get_name(), get_transform()); } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/gua/physics/RigidBodyNode.cpp b/src/gua/physics/RigidBodyNode.cpp index d8c03bdab..01b0349e6 100644 --- a/src/gua/physics/RigidBodyNode.cpp +++ b/src/gua/physics/RigidBodyNode.cpp @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include // external headers @@ -391,7 +391,7 @@ void RigidBodyNode::sync_shapes(bool do_not_lock) { //////////////////////////////////////////////////////////////////////////////// std::shared_ptr RigidBodyNode::copy() const { - return std::make_shared(get_name(), get_transform()); + return std::make_shared(get_name(), get_transform()); } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/gua/renderer/GeometryLoader.cpp b/src/gua/renderer/GeometryLoader.cpp index 105068a59..0cc6fd672 100644 --- a/src/gua/renderer/GeometryLoader.cpp +++ b/src/gua/renderer/GeometryLoader.cpp @@ -24,7 +24,7 @@ // guacamole headers #include -#include +#include #include #include #include @@ -119,7 +119,7 @@ std::shared_ptr GeometryLoader::create_geometry_from_file return copy; } - return std::make_shared(node_name); + return std::make_shared(node_name); } diff --git a/src/gua/renderer/LightingPass.cpp b/src/gua/renderer/LightingPass.cpp index d4ac88d02..befefaf6d 100644 --- a/src/gua/renderer/LightingPass.cpp +++ b/src/gua/renderer/LightingPass.cpp @@ -265,10 +265,10 @@ void LightingPass::render_shadow_map(RenderContext const & ctx, pipeline_->config.far_clip()); SerializedScene scene; + scene.frustum = shadow_frustum; serializer_->check(&scene, pipeline_->get_current_graph(), Camera("", "", scene_camera.render_mask), - shadow_frustum, false, false, true); diff --git a/src/gua/renderer/MeshLoader.cpp b/src/gua/renderer/MeshLoader.cpp index 6d2b856b2..15a3fb52c 100644 --- a/src/gua/renderer/MeshLoader.cpp +++ b/src/gua/renderer/MeshLoader.cpp @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include #include #include @@ -180,7 +180,7 @@ std::shared_ptr MeshLoader::get_tree(std::shared_ptr con } // else: there are multiple children and meshes - auto group(std::make_shared()); + auto group(std::make_shared()); for (unsigned i(0); i < ai_root->mNumMeshes; ++i) { group->add_child(load_geometry(i)); diff --git a/src/gua/renderer/Pass.cpp b/src/gua/renderer/Pass.cpp index 1e7c66a33..efc0588bf 100644 --- a/src/gua/renderer/Pass.cpp +++ b/src/gua/renderer/Pass.cpp @@ -107,26 +107,9 @@ void Pass::set_camera_matrices(ShaderProgram const& shader, CameraMode eye, RenderContext const& ctx) const { - auto view(scene.view_); - auto screen(scene.screen_); - - math::mat4 view_transform(view.transform); - if (eye == CameraMode::LEFT) { - scm::math::translate( - view_transform, -view.data.get_stereo_width() * 0.5f, 0.f, 0.f); - } else if (eye == CameraMode::RIGHT) { - scm::math::translate( - view_transform, view.data.get_stereo_width() * 0.5f, 0.f, 0.f); - } - - Frustum frustum(view_transform, - screen.transform, - pipeline_->config.near_clip(), - pipeline_->config.far_clip()); - - auto camera_position(frustum.get_camera_position()); - auto projection(frustum.get_projection()); - auto view_matrix(frustum.get_view()); + auto camera_position(scene.frustum.get_camera_position()); + auto projection(scene.frustum.get_projection()); + auto view_matrix(scene.frustum.get_view()); shader.set_uniform(ctx, camera_position, "gua_camera_position"); shader.set_uniform(ctx, projection, "gua_projection_matrix"); diff --git a/src/gua/renderer/Pipeline.cpp b/src/gua/renderer/Pipeline.cpp index b4e34d775..1a07cb198 100644 --- a/src/gua/renderer/Pipeline.cpp +++ b/src/gua/renderer/Pipeline.cpp @@ -186,63 +186,85 @@ void Pipeline::process(std::vector> const& sce create_buffers(); } - auto view_it((*current_graph_)[config.camera().view]); - if (!view_it) { - WARNING("Cannot render scene: No valid view specified"); - return; - } - - auto screen_it((*current_graph_)[config.camera().screen]); - if (!screen_it) { - WARNING("Cannot render scene: No valid screen specified"); - return; - } - auto view(std::dynamic_pointer_cast(view_it)); - auto screen(std::dynamic_pointer_cast(screen_it)); if (!config.get_enable_stereo()) { + auto eye((*current_graph_)[config.camera().eye_l]); + if (!eye) { + WARNING("Cannot render scene: No valid eye specified"); + return; + } + + auto screen_it((*current_graph_)[config.camera().screen_l]); + auto screen(std::dynamic_pointer_cast(screen_it)); + if (!screen) { + WARNING("Cannot render scene: No valid screen specified"); + return; + } + + current_scenes_[0].frustum = Frustum(eye->get_world_transform(), + screen->get_scaled_world_transform(), + config.near_clip(), + config.far_clip()); + serializer_->check(¤t_scenes_[0], current_graph_, config.camera(), - Frustum(view->get_world_transform(), - screen->get_scaled_world_transform(), - config.near_clip(), - config.far_clip()), config.enable_bbox_display(), config.enable_ray_display(), config.enable_frustum_culling()); } else { - math::mat4 camera_transform(view->get_world_transform()); - scm::math::translate( - camera_transform, -view->data.get_stereo_width() * 0.5f, 0.f, 0.f); + + auto eye_l((*current_graph_)[config.camera().eye_l]); + if (!eye_l) { + WARNING("Cannot render scene: No valid left eye specified"); + return; + } + + auto eye_r((*current_graph_)[config.camera().eye_r]); + if (!eye_r) { + WARNING("Cannot render scene: No valid right eye specified"); + return; + } + + auto screen_it_l((*current_graph_)[config.camera().screen_l]); + auto screen_l(std::dynamic_pointer_cast(screen_it_l)); + if (!screen_l) { + WARNING("Cannot render scene: No valid left screen specified"); + return; + } + + auto screen_it_r((*current_graph_)[config.camera().screen_r]); + auto screen_r(std::dynamic_pointer_cast(screen_it_r)); + if (!screen_r) { + WARNING("Cannot render scene: No valid right screen specified"); + return; + } + + + current_scenes_[0].frustum = Frustum(eye_l->get_world_transform(), + screen_l->get_scaled_world_transform(), + config.near_clip(), + config.far_clip()); + current_scenes_[1].frustum = Frustum(eye_r->get_world_transform(), + screen_r->get_scaled_world_transform(), + config.near_clip(), + config.far_clip()); serializer_->check(¤t_scenes_[0], current_graph_, config.camera(), - Frustum(camera_transform, - screen->get_scaled_world_transform(), - config.near_clip(), - config.far_clip()), config.enable_bbox_display(), config.enable_ray_display(), config.enable_frustum_culling()); - camera_transform = view->get_world_transform(); - scm::math::translate( - camera_transform, view->data.get_stereo_width() * 0.5f, 0.f, 0.f); - serializer_->check(¤t_scenes_[1], current_graph_, config.camera(), - Frustum(camera_transform, - screen->get_scaled_world_transform(), - config.near_clip(), - config.far_clip()), config.enable_bbox_display(), config.enable_ray_display(), config.enable_frustum_culling()); diff --git a/src/gua/renderer/Serializer.cpp b/src/gua/renderer/Serializer.cpp index 8ecaa0ac3..4dcbf6b0c 100644 --- a/src/gua/renderer/Serializer.cpp +++ b/src/gua/renderer/Serializer.cpp @@ -31,9 +31,8 @@ #include #include -#include +#include #include -#include #include #include #include @@ -64,7 +63,6 @@ Serializer::Serializer() void Serializer::check(SerializedScene* output, SceneGraph const* scene_graph, Camera const& camera, - Frustum const& frustum, bool draw_bounding_boxes, bool draw_rays, bool enable_frustum_culling) { @@ -114,14 +112,14 @@ void Serializer::check(SerializedScene* output, current_camera_ = camera; current_render_mask_ = Mask(current_camera_.render_mask); - current_frustum_ = frustum; + current_frustum_ = output->frustum; scene_graph->accept(*this); } //////////////////////////////////////////////////////////////////////// -/* virtual */ void Serializer::visit(GroupNode* node) { +/* virtual */ void Serializer::visit(TransformNode* node) { if (is_visible(node)) { visit_children(node); } @@ -129,18 +127,6 @@ void Serializer::check(SerializedScene* output, //////////////////////////////////////////////////////////////////////// -/* virtual */ void Serializer::visit(ViewNode* node) { - if (is_visible(node)) { - if (node->get_path() == current_camera_.view) { - data_->view_ = make_serialized_node(node->get_world_transform(), node->data); - } - - visit_children(node); - } -} - -//////////////////////////////////////////////////////////////////////// - /* virtual */ void Serializer::visit(GeometryNode* node) { if (is_visible(node)) { @@ -203,18 +189,6 @@ void Serializer::check(SerializedScene* output, //////////////////////////////////////////////////////////////////////// -/* virtual */ void Serializer::visit(ScreenNode* node) { - if (is_visible(node)) { - if (node->get_path() == current_camera_.screen) { - data_->screen_ = make_serialized_node(node->get_scaled_world_transform(), node->data); - } - - visit_children(node); - } -} - -//////////////////////////////////////////////////////////////////////// - /* virtual */ void Serializer::visit(RayNode* node) { if (is_visible(node)) { diff --git a/src/gua/scenegraph/SceneGraph.cpp b/src/gua/scenegraph/SceneGraph.cpp index e104b627c..1f9ffe18c 100644 --- a/src/gua/scenegraph/SceneGraph.cpp +++ b/src/gua/scenegraph/SceneGraph.cpp @@ -24,7 +24,7 @@ // guacamole headers #include -#include +#include #include #include #include @@ -36,7 +36,7 @@ namespace gua { SceneGraph::SceneGraph(std::string const& name) - : root_(new GroupNode("/", math::mat4::identity())), + : root_(new TransformNode("/", math::mat4::identity())), name_(name) {} SceneGraph::SceneGraph(SceneGraph const& graph) diff --git a/src/gua/scenegraph/GroupNode.cpp b/src/gua/scenegraph/TransformNode.cpp similarity index 84% rename from src/gua/scenegraph/GroupNode.cpp rename to src/gua/scenegraph/TransformNode.cpp index 96e076abb..d45af39a0 100644 --- a/src/gua/scenegraph/GroupNode.cpp +++ b/src/gua/scenegraph/TransformNode.cpp @@ -20,23 +20,23 @@ ******************************************************************************/ // class header -#include +#include // guacamole headers #include namespace gua { -GroupNode::GroupNode(std::string const& name, math::mat4 const& transform) +TransformNode::TransformNode(std::string const& name, math::mat4 const& transform) : Node(name, transform) {} -/* virtual */ void GroupNode::accept(NodeVisitor& visitor) { +/* virtual */ void TransformNode::accept(NodeVisitor& visitor) { visitor.visit(this); } -std::shared_ptr GroupNode::copy() const { - return std::make_shared(get_name(), get_transform()); +std::shared_ptr TransformNode::copy() const { + return std::make_shared(get_name(), get_transform()); } } diff --git a/src/gua/scenegraph/ViewNode.cpp b/src/gua/scenegraph/ViewNode.cpp deleted file mode 100644 index b6b31a319..000000000 --- a/src/gua/scenegraph/ViewNode.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/****************************************************************************** - * guacamole - delicious VR * - * * - * Copyright: (c) 2011-2013 Bauhaus-Universität Weimar * - * Contact: felix.lauer@uni-weimar.de / simon.schneegans@uni-weimar.de * - * * - * This program is free software: you can redistribute it and/or modify it * - * under the terms of the GNU General Public License as published by the Free * - * Software Foundation, either version 3 of the License, or (at your option) * - * any later version. * - * * - * This program is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * - * for more details. * - * * - * You should have received a copy of the GNU General Public License along * - * with this program. If not, see . * - * * - ******************************************************************************/ - -// class header -#include - -// guacamole headers -#include -#include - -namespace gua { - -ViewNode::ViewNode(std::string const& name, - Configuration const& configuration, - math::mat4 const& transform) - : Node(name, transform), data(configuration) {} - -/* virtual */ void ViewNode::accept(NodeVisitor& visitor) { - - visitor.visit(this); -} - -std::shared_ptr ViewNode::copy() const { - return std::make_shared(get_name(), data, get_transform()); -} - -} diff --git a/src/gua/utils/DotGenerator.cpp b/src/gua/utils/DotGenerator.cpp index 072025dd9..3d2d42198 100644 --- a/src/gua/utils/DotGenerator.cpp +++ b/src/gua/utils/DotGenerator.cpp @@ -24,8 +24,7 @@ // guacamole headers #include -#include -#include +#include #include #include #include @@ -82,7 +81,7 @@ void DotGenerator::parse_graph(SceneGraph const* graph) { } //////////////////////////////////////////////////////////////////////////////// -/* virtual */ void DotGenerator::visit(GroupNode* cam) { +/* virtual */ void DotGenerator::visit(TransformNode* cam) { pre_node_info(cam); std::string fillcolor("[fillcolor ="); @@ -95,20 +94,6 @@ void DotGenerator::parse_graph(SceneGraph const* graph) { child->accept(*this); } -//////////////////////////////////////////////////////////////////////////////// -/* virtual */ void DotGenerator::visit(ViewNode* cam) { - pre_node_info(cam); - - std::string fillcolor("[fillcolor ="); - fillcolor += " \"#AAFFAA\""; - fillcolor += "]"; - - post_node_info(cam, fillcolor); - - for (auto child : cam->children_) - child->accept(*this); -} - //////////////////////////////////////////////////////////////////////////////// /* virtual */ void DotGenerator::visit(GeometryNode* geometry) { pre_node_info(geometry); From cd8f02ca3569a09ee57af7e0a33b6c2d028e7e27 Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Tue, 29 Oct 2013 11:47:29 +0100 Subject: [PATCH 002/146] renamed Texture to Texture2D --- include/gua/databases/TextureDatabase.hpp | 4 +-- include/gua/renderer/BuiltInTextures.hpp | 6 ++-- include/gua/renderer/FrameBufferObject.hpp | 8 +++--- include/gua/renderer/GBuffer.hpp | 8 +++--- include/gua/renderer/NURBS.hpp | 8 +++--- include/gua/renderer/Pass.hpp | 2 +- include/gua/renderer/PostFXPass.hpp | 2 +- .../renderer/{Texture.hpp => Texture2D.hpp} | 12 ++++---- include/gua/renderer/Uniform.hpp | 22 +++++++-------- include/gua/renderer/WarpMatrix.hpp | 4 +-- include/gua/renderer/Window.hpp | 10 +++---- src/gua/databases/TextureDatabase.cpp | 2 +- src/gua/guacamole.cpp | 2 +- src/gua/renderer/BuiltInTextures.cpp | 6 ++-- src/gua/renderer/FrameBufferObject.cpp | 6 ++-- src/gua/renderer/GBuffer.cpp | 18 ++++++------ src/gua/renderer/GBufferPass.cpp | 2 +- src/gua/renderer/NURBS.cpp | 2 +- src/gua/renderer/PostFXPass.cpp | 4 +-- .../renderer/{Texture.cpp => Texture2D.cpp} | 28 +++++++++---------- src/gua/renderer/WarpMatrix.cpp | 4 +-- src/gua/renderer/Window.cpp | 20 ++++++------- 22 files changed, 90 insertions(+), 90 deletions(-) rename include/gua/renderer/{Texture.hpp => Texture2D.hpp} (97%) rename src/gua/renderer/{Texture.cpp => Texture2D.cpp} (89%) diff --git a/include/gua/databases/TextureDatabase.hpp b/include/gua/databases/TextureDatabase.hpp index 8248cbf69..2c45c7856 100644 --- a/include/gua/databases/TextureDatabase.hpp +++ b/include/gua/databases/TextureDatabase.hpp @@ -25,7 +25,7 @@ // guacamole headers #include #include -#include +#include namespace gua { @@ -35,7 +35,7 @@ namespace gua { * This Database stores texture data. It can be accessed via string * identifiers. */ -class TextureDatabase : public Database, +class TextureDatabase : public Database, public Singleton { public: diff --git a/include/gua/renderer/BuiltInTextures.hpp b/include/gua/renderer/BuiltInTextures.hpp index d11df1bd7..013c36bc1 100644 --- a/include/gua/renderer/BuiltInTextures.hpp +++ b/include/gua/renderer/BuiltInTextures.hpp @@ -23,11 +23,11 @@ #define GUA_NOISE_TEXTURE_HPP // guacamole headers -#include +#include namespace gua { -class NoiseTexture : public Texture { +class NoiseTexture : public Texture2D { public: NoiseTexture(); @@ -36,7 +36,7 @@ class NoiseTexture : public Texture { static unsigned char pixel_data[64 * 64 * 3 + 1]; }; -class DefaultTexture : public Texture { +class DefaultTexture : public Texture2D { public: DefaultTexture(); diff --git a/include/gua/renderer/FrameBufferObject.hpp b/include/gua/renderer/FrameBufferObject.hpp index 29e66c516..b92a7ec75 100644 --- a/include/gua/renderer/FrameBufferObject.hpp +++ b/include/gua/renderer/FrameBufferObject.hpp @@ -24,7 +24,7 @@ // guacamole headers #include -#include +#include #include // external headers @@ -72,7 +72,7 @@ class FrameBufferObject { */ void attach_color_buffer(RenderContext const& context, unsigned in_color_attachment, - std::shared_ptr const& buffer, + std::shared_ptr const& buffer, int mip_level = 0, int z_slice = 0); @@ -87,7 +87,7 @@ class FrameBufferObject { * \param z_slice The buffer's z_slice. */ void attach_depth_stencil_buffer(RenderContext const& context, - std::shared_ptr const& buffer, + std::shared_ptr const& buffer, int mip_level = 0, int z_slice = 0); @@ -147,7 +147,7 @@ class FrameBufferObject { ///@} private: - bool set_size(std::shared_ptr const& buffer); + bool set_size(std::shared_ptr const& buffer); unsigned width_, height_; mutable std::vector fbos_; diff --git a/include/gua/renderer/GBuffer.hpp b/include/gua/renderer/GBuffer.hpp index a3784ebc2..12ac806d4 100644 --- a/include/gua/renderer/GBuffer.hpp +++ b/include/gua/renderer/GBuffer.hpp @@ -58,10 +58,10 @@ class GBuffer : public FrameBufferObject { /** * */ - std::vector > const& get_color_buffers( + std::vector > const& get_color_buffers( BufferComponentType type) const; - inline std::shared_ptr const& get_depth_buffer() const { + inline std::shared_ptr const& get_depth_buffer() const { return depth_buffer_; } @@ -70,9 +70,9 @@ class GBuffer : public FrameBufferObject { layer_types_; unsigned width_, height_, mipmap_layers_; - std::map > > + std::map > > color_buffers_; - std::shared_ptr depth_buffer_; + std::shared_ptr depth_buffer_; }; } diff --git a/include/gua/renderer/NURBS.hpp b/include/gua/renderer/NURBS.hpp index 8349e939e..7d183d43f 100644 --- a/include/gua/renderer/NURBS.hpp +++ b/include/gua/renderer/NURBS.hpp @@ -71,16 +71,16 @@ class NURBS : public Geometry { NURBSData* _data; scm::gl::fill_mode _fill_mode; - //Texture Buffer for Parametric Data + //Texture2D Buffer for Parametric Data mutable std::vector _parametric_texture_buffer; - //Texture Buffer for Attributes + //Texture2D Buffer for Attributes mutable std::vector _attribute_texture_buffer; - //Texture Buffer for Domain + //Texture2D Buffer for Domain mutable std::vector _domain_texture_buffer; - //Texture Buffers for Trim Data + //Texture2D Buffers for Trim Data mutable std::vector _trim_partition_texture_buffer; mutable std::vector diff --git a/include/gua/renderer/Pass.hpp b/include/gua/renderer/Pass.hpp index 0cdc2d0d9..f9d60d3be 100644 --- a/include/gua/renderer/Pass.hpp +++ b/include/gua/renderer/Pass.hpp @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include diff --git a/include/gua/renderer/PostFXPass.hpp b/include/gua/renderer/PostFXPass.hpp index e1c07bff7..c8e08bed0 100644 --- a/include/gua/renderer/PostFXPass.hpp +++ b/include/gua/renderer/PostFXPass.hpp @@ -81,7 +81,7 @@ class PostFXPass : public Pass { CameraMode eye, RenderContext const& ctx); void render_ssao(RenderContext const& ctx); - void render_hdr(RenderContext const& ctx, std::shared_ptr const& texture); + void render_hdr(RenderContext const& ctx, std::shared_ptr const& texture); // postfx_shaders 0: SSAO, Fog, God Rays, // postfx_shaders 1: Glow, diff --git a/include/gua/renderer/Texture.hpp b/include/gua/renderer/Texture2D.hpp similarity index 97% rename from include/gua/renderer/Texture.hpp rename to include/gua/renderer/Texture2D.hpp index 395365ee1..01ff97944 100644 --- a/include/gua/renderer/Texture.hpp +++ b/include/gua/renderer/Texture2D.hpp @@ -47,7 +47,7 @@ namespace gua { * This class allows to load texture data from a file and bind the * texture to an OpenGL context. */ -class Texture { +class Texture2D { public: /** @@ -61,7 +61,7 @@ class Texture { * texture. * \param state_descripton The sampler state for the loaded texture. */ - Texture(unsigned width, + Texture2D(unsigned width, unsigned height, scm::gl::data_format color_format, std::vector const& data, @@ -82,7 +82,7 @@ class Texture { * texture. * \param state_descripton The sampler state for the loaded texture. */ - Texture(unsigned width, + Texture2D(unsigned width, unsigned height, scm::gl::data_format color_format = scm::gl::FORMAT_RGB_32F, unsigned mipmap_layers = 1, @@ -99,14 +99,14 @@ class Texture { * \param file The file which contains the texture data. * \param state_descripton The sampler state for the loaded texture. */ - Texture(std::string const& file, + Texture2D(std::string const& file, bool generate_mipmaps = false, scm::gl::sampler_state_desc const& state_descripton = scm::gl::sampler_state_desc(scm::gl::FILTER_ANISOTROPIC, scm::gl::WRAP_REPEAT, scm::gl::WRAP_REPEAT)); - virtual ~Texture(); + virtual ~Texture2D(); void generate_mipmaps(RenderContext const& context); @@ -133,7 +133,7 @@ class Texture { /** * Gets the size. * - * Returns the size of the Texture. + * Returns the size of the Texture2D. */ unsigned width() const; unsigned height() const; diff --git a/include/gua/renderer/Uniform.hpp b/include/gua/renderer/Uniform.hpp index 5bf1a4b26..ae6e44cc2 100644 --- a/include/gua/renderer/Uniform.hpp +++ b/include/gua/renderer/Uniform.hpp @@ -23,7 +23,7 @@ #define GUA_UNIFORM_HPP // guacamole headers -#include +#include #include #include #include @@ -108,9 +108,9 @@ template class UniformValue : public UniformValueBase { template <> -class UniformValue > : public UniformValueBase { +class UniformValue > : public UniformValueBase { public: - UniformValue(std::shared_ptr const& value) + UniformValue(std::shared_ptr const& value) : UniformValueBase(), value_(value) {} void apply(RenderContext const& context, @@ -121,18 +121,18 @@ class UniformValue > : public UniformValueBase { program->uniform(name, position, value_->get_handle(context)); } - std::shared_ptr const& value() const { return value_; } + std::shared_ptr const& value() const { return value_; } - void value(std::shared_ptr const& value) { value_ = value; } + void value(std::shared_ptr const& value) { value_ = value; } private: - std::shared_ptr value_; + std::shared_ptr value_; }; template <> -class UniformValue : public UniformValueBase { +class UniformValue : public UniformValueBase { public: - UniformValue(Texture* value) + UniformValue(Texture2D* value) : UniformValueBase(), value_(value) {} void apply(RenderContext const& context, @@ -143,12 +143,12 @@ class UniformValue : public UniformValueBase { program->uniform(name, position, value_->get_handle(context)); } - Texture* value() const { return value_; } + Texture2D* value() const { return value_; } - void value(Texture* value) { value_ = value; } + void value(Texture2D* value) { value_ = value; } private: - Texture* value_; + Texture2D* value_; }; diff --git a/include/gua/renderer/WarpMatrix.hpp b/include/gua/renderer/WarpMatrix.hpp index a34339d73..bf2add5dc 100644 --- a/include/gua/renderer/WarpMatrix.hpp +++ b/include/gua/renderer/WarpMatrix.hpp @@ -23,7 +23,7 @@ #define GUA_WARP_MATRIX_HPP // guacamole headers -#include +#include namespace gua { @@ -34,7 +34,7 @@ namespace gua { * when the projecting beamers for the colors red, green and blue don't * overlap exactly. */ -class WarpMatrix : public Texture { +class WarpMatrix : public Texture2D { public: /** diff --git a/include/gua/renderer/Window.hpp b/include/gua/renderer/Window.hpp index f55bb11e7..e9e92190b 100644 --- a/include/gua/renderer/Window.hpp +++ b/include/gua/renderer/Window.hpp @@ -38,7 +38,7 @@ namespace gua { class Geometry; -class Texture; +class Texture2D; class StereoBuffer; /** @@ -131,10 +131,10 @@ class Window { /** * */ - void display(std::shared_ptr const& center_texture); + void display(std::shared_ptr const& center_texture); - void display(std::shared_ptr const& left_texture, - std::shared_ptr const& right_texture); + void display(std::shared_ptr const& left_texture, + std::shared_ptr const& right_texture); /** * Get the RenderContext of this window. @@ -147,7 +147,7 @@ class Window { RenderContext* get_context(); private: - void display(std::shared_ptr const& texture, + void display(std::shared_ptr const& texture, math::vec2ui const& size, math::vec2ui const& position, TextureDisplayMode mode = FULL, diff --git a/src/gua/databases/TextureDatabase.cpp b/src/gua/databases/TextureDatabase.cpp index 7903faecb..43d6c1446 100644 --- a/src/gua/databases/TextureDatabase.cpp +++ b/src/gua/databases/TextureDatabase.cpp @@ -33,7 +33,7 @@ namespace gua { void TextureDatabase::load(std::string const& id) { - instance()->add(id, std::make_shared(id, true)); + instance()->add(id, std::make_shared(id, true)); } } diff --git a/src/gua/guacamole.cpp b/src/gua/guacamole.cpp index 8dc915dc9..1a7e17745 100644 --- a/src/gua/guacamole.cpp +++ b/src/gua/guacamole.cpp @@ -42,7 +42,7 @@ void init(int argc, char** argv) { Resources::materials_gua_textured_quad_gsd, Resources::materials_gua_textured_quad_gmd); - gua::TextureDatabase::instance()->add("gua_default_texture", std::shared_ptr(new DefaultTexture())); + gua::TextureDatabase::instance()->add("gua_default_texture", std::shared_ptr(new DefaultTexture())); MeshLoader mesh_loader; diff --git a/src/gua/renderer/BuiltInTextures.cpp b/src/gua/renderer/BuiltInTextures.cpp index dc8a1793f..2db42e548 100644 --- a/src/gua/renderer/BuiltInTextures.cpp +++ b/src/gua/renderer/BuiltInTextures.cpp @@ -456,7 +456,7 @@ k\204\205ke\217\211p\\~Z\303\224{(s\224Jxs\256s\245\30{\204|vn\241\214s<\ NoiseTexture:: NoiseTexture() - : Texture(64, 64, scm::gl::FORMAT_RGB_8, {pixel_data}) + : Texture2D(64, 64, scm::gl::FORMAT_RGB_8, {pixel_data}) {} //////////////////////////////////////////////////////////////////////////////// @@ -7738,9 +7738,9 @@ I?????????????????????????????????????????????\225\225\225\225\225\225\225\ DefaultTexture:: DefaultTexture() #if WIN32 - : Texture(64, 64, scm::gl::FORMAT_RGB_8, {pixel_data}) + : Texture2D(64, 64, scm::gl::FORMAT_RGB_8, {pixel_data}) #else - : Texture(256, 256, scm::gl::FORMAT_RGB_8, {pixel_data}) + : Texture2D(256, 256, scm::gl::FORMAT_RGB_8, {pixel_data}) #endif {} diff --git a/src/gua/renderer/FrameBufferObject.cpp b/src/gua/renderer/FrameBufferObject.cpp index 2fc141307..5f3ba892b 100644 --- a/src/gua/renderer/FrameBufferObject.cpp +++ b/src/gua/renderer/FrameBufferObject.cpp @@ -55,7 +55,7 @@ void FrameBufferObject::remove_attachments() { void FrameBufferObject::attach_color_buffer(RenderContext const& ctx, unsigned in_color_attachment, - std::shared_ptr const& buffer, + std::shared_ptr const& buffer, int mip_level, int z_slice) { @@ -79,7 +79,7 @@ void FrameBufferObject::attach_color_buffer(RenderContext const& ctx, void FrameBufferObject::attach_depth_stencil_buffer( RenderContext const& ctx, - std::shared_ptr const& buffer, + std::shared_ptr const& buffer, int mip_level, int z_slice) { @@ -163,7 +163,7 @@ void FrameBufferObject::set_viewport(RenderContext const& ctx) const { //////////////////////////////////////////////////////////////////////////////// -bool FrameBufferObject::set_size(std::shared_ptr const& buffer) { +bool FrameBufferObject::set_size(std::shared_ptr const& buffer) { if (width_ == 0 && height_ == 0) { width_ = buffer->width(); diff --git a/src/gua/renderer/GBuffer.cpp b/src/gua/renderer/GBuffer.cpp index 527ac4fbb..cd8387996 100644 --- a/src/gua/renderer/GBuffer.cpp +++ b/src/gua/renderer/GBuffer.cpp @@ -167,10 +167,10 @@ GBuffer::GBuffer( height_(height), mipmap_layers_(mipmap_layers) { - color_buffers_[TYPE_INTEGER] = std::vector >(); - color_buffers_[TYPE_UNSIGNED] = std::vector >(); - color_buffers_[TYPE_HALF] = std::vector >(); - color_buffers_[TYPE_FLOAT] = std::vector >(); + color_buffers_[TYPE_INTEGER] = std::vector >(); + color_buffers_[TYPE_UNSIGNED] = std::vector >(); + color_buffers_[TYPE_HALF] = std::vector >(); + color_buffers_[TYPE_FLOAT] = std::vector >(); } void GBuffer::remove_buffers(RenderContext const& ctx) { @@ -194,12 +194,12 @@ void GBuffer::create(RenderContext const& ctx) { auto format = to_scm_data_format(type); if (format) { if (type == BufferComponent::DEPTH_16 || type == BufferComponent::DEPTH_24) { - depth_buffer_ = std::make_shared( + depth_buffer_ = std::make_shared( width_, height_, *format, mipmap_layers_, state); attach_depth_stencil_buffer(ctx, depth_buffer_); } else if (type != BufferComponent::NONE) { color_buffers_[enums::get_type(type)].push_back( - std::make_shared( width_, + std::make_shared( width_, height_, *format, mipmap_layers_, @@ -221,12 +221,12 @@ void GBuffer::create_UGLY(RenderContext const & ctx) { auto format = to_scm_data_format(type); if (format) { if (type == BufferComponent::DEPTH_16 || type == BufferComponent::DEPTH_24) { - depth_buffer_ = std::make_shared( + depth_buffer_ = std::make_shared( width_, height_, *format, mipmap_layers_, state); attach_depth_stencil_buffer(ctx, depth_buffer_); } else if (type != BufferComponent::NONE) { color_buffers_[enums::get_type(type)].push_back( - std::make_shared( width_, + std::make_shared( width_, height_, *format, mipmap_layers_, @@ -240,7 +240,7 @@ void GBuffer::create_UGLY(RenderContext const & ctx) { } } -std::vector > const& GBuffer::get_color_buffers( +std::vector > const& GBuffer::get_color_buffers( BufferComponentType type) const { return color_buffers_.find(type)->second; } diff --git a/src/gua/renderer/GBufferPass.cpp b/src/gua/renderer/GBufferPass.cpp index debf7f46e..b4e0e8582 100644 --- a/src/gua/renderer/GBufferPass.cpp +++ b/src/gua/renderer/GBufferPass.cpp @@ -220,7 +220,7 @@ void GBufferPass::rendering(SerializedScene const& scene, geometry->draw(ctx); } } else { - WARNING("Failed to render TexturedQuad: Texture \"%s\" not found!", texture_name.c_str()); + WARNING("Failed to render TexturedQuad: Texture2D \"%s\" not found!", texture_name.c_str()); } } } diff --git a/src/gua/renderer/NURBS.cpp b/src/gua/renderer/NURBS.cpp index 63b5e1fde..c49026f69 100644 --- a/src/gua/renderer/NURBS.cpp +++ b/src/gua/renderer/NURBS.cpp @@ -118,7 +118,7 @@ void NURBS::upload_to(RenderContext const& context) const { scm::gl::FUNC_ONE, scm::gl::FUNC_ZERO); - //Initialize Texture Buffers + //Initialize Texture2D Buffers initialize_texture_buffers(context); //Initialize Vertex Data diff --git a/src/gua/renderer/PostFXPass.cpp b/src/gua/renderer/PostFXPass.cpp index 8fee034f3..849dd200c 100644 --- a/src/gua/renderer/PostFXPass.cpp +++ b/src/gua/renderer/PostFXPass.cpp @@ -611,7 +611,7 @@ void PostFXPass::render_ssao(RenderContext const& ctx) { //////////////////////////////////////////////////////////////////////////////// void PostFXPass:: -render_hdr(RenderContext const& ctx, std::shared_ptr const& texture) { +render_hdr(RenderContext const& ctx, std::shared_ptr const& texture) { ctx.render_context->set_viewport(scm::gl::viewport( math::vec2(0,0), math::vec2(float(luminance_buffer_->width()), @@ -673,7 +673,7 @@ void PostFXPass::render_previews(CameraMode eye, RenderContext const& ctx) { preview_text_renderer_ = new TextRenderer(ctx, 12, font); } - std::vector>> previews; + std::vector>> previews; for (unsigned input(0); input < inputs_.size(); ++input) { diff --git a/src/gua/renderer/Texture.cpp b/src/gua/renderer/Texture2D.cpp similarity index 89% rename from src/gua/renderer/Texture.cpp rename to src/gua/renderer/Texture2D.cpp index e13e5982d..15276a880 100644 --- a/src/gua/renderer/Texture.cpp +++ b/src/gua/renderer/Texture2D.cpp @@ -20,7 +20,7 @@ ******************************************************************************/ // class header -#include +#include // guacamole headers #include @@ -35,7 +35,7 @@ namespace gua { //////////////////////////////////////////////////////////////////////////////// -Texture::Texture(unsigned width, +Texture2D::Texture2D(unsigned width, unsigned height, scm::gl::data_format color_format, std::vector const& data, @@ -54,7 +54,7 @@ Texture::Texture(unsigned width, //////////////////////////////////////////////////////////////////////////////// -Texture::Texture(unsigned width, +Texture2D::Texture2D(unsigned width, unsigned height, scm::gl::data_format color_format, unsigned mipmap_layers, @@ -71,7 +71,7 @@ Texture::Texture(unsigned width, //////////////////////////////////////////////////////////////////////////////// -Texture::Texture(std::string const& file, +Texture2D::Texture2D(std::string const& file, bool generate_mipmaps, scm::gl::sampler_state_desc const& state_descripton) : width_(0), @@ -86,13 +86,13 @@ Texture::Texture(std::string const& file, //////////////////////////////////////////////////////////////////////////////// -Texture::~Texture() { +Texture2D::~Texture2D() { make_non_resident(); } //////////////////////////////////////////////////////////////////////////////// -void Texture::generate_mipmaps(RenderContext const& context) { +void Texture2D::generate_mipmaps(RenderContext const& context) { if (textures_.size() <= context.id || textures_[context.id] == 0) upload_to(context); @@ -102,7 +102,7 @@ void Texture::generate_mipmaps(RenderContext const& context) { //////////////////////////////////////////////////////////////////////////////// -math::vec2ui const Texture::get_handle(RenderContext const& context) const { +math::vec2ui const Texture2D::get_handle(RenderContext const& context) const { if (textures_.size() <= context.id || textures_[context.id] == 0) upload_to(context); @@ -114,7 +114,7 @@ math::vec2ui const Texture::get_handle(RenderContext const& context) const { //////////////////////////////////////////////////////////////////////////////// -scm::gl::texture_2d_ptr const& Texture::get_buffer( +scm::gl::texture_2d_ptr const& Texture2D::get_buffer( RenderContext const& context) const { if (textures_.size() <= context.id || textures_[context.id] == 0) @@ -125,7 +125,7 @@ scm::gl::texture_2d_ptr const& Texture::get_buffer( //////////////////////////////////////////////////////////////////////////////// -void Texture::make_resident(RenderContext const& context) const { +void Texture2D::make_resident(RenderContext const& context) const { context.render_context ->make_resident(textures_[context.id], sampler_states_[context.id]); @@ -133,14 +133,14 @@ void Texture::make_resident(RenderContext const& context) const { //////////////////////////////////////////////////////////////////////////////// -void Texture::make_non_resident(RenderContext const& context) const { +void Texture2D::make_non_resident(RenderContext const& context) const { context.render_context->make_non_resident(textures_[context.id]); } //////////////////////////////////////////////////////////////////////////////// -void Texture::make_non_resident() const { +void Texture2D::make_non_resident() const { for (int i(0); imake_non_resident(textures_[i]); @@ -149,15 +149,15 @@ void Texture::make_non_resident() const { //////////////////////////////////////////////////////////////////////////////// -unsigned Texture::width() const { return width_; } +unsigned Texture2D::width() const { return width_; } //////////////////////////////////////////////////////////////////////////////// -unsigned Texture::height() const { return height_; } +unsigned Texture2D::height() const { return height_; } //////////////////////////////////////////////////////////////////////////////// -void Texture::upload_to(RenderContext const& context) const { +void Texture2D::upload_to(RenderContext const& context) const { std::unique_lock lock(upload_mutex_); diff --git a/src/gua/renderer/WarpMatrix.cpp b/src/gua/renderer/WarpMatrix.cpp index af6e7876c..2f2d9e620 100644 --- a/src/gua/renderer/WarpMatrix.cpp +++ b/src/gua/renderer/WarpMatrix.cpp @@ -33,12 +33,12 @@ namespace gua { //////////////////////////////////////////////////////////////////////////////// -WarpMatrix::WarpMatrix() : Texture(0, 0), data_() {} +WarpMatrix::WarpMatrix() : Texture2D(0, 0), data_() {} //////////////////////////////////////////////////////////////////////////////// WarpMatrix::WarpMatrix(std::string const& file_name) - : Texture(0, + : Texture2D(0, 0, scm::gl::FORMAT_RGBA_16F, 1, diff --git a/src/gua/renderer/Window.cpp b/src/gua/renderer/Window.cpp index bfef9c319..1a5b1e11a 100644 --- a/src/gua/renderer/Window.cpp +++ b/src/gua/renderer/Window.cpp @@ -205,7 +205,7 @@ void Window::finish_frame() const { //////////////////////////////////////////////////////////////////////////////// -void Window::display(std::shared_ptr const& center_texture) { +void Window::display(std::shared_ptr const& center_texture) { display(center_texture, config.get_left_resolution(), config.get_left_position(), Window::FULL, true, true); @@ -214,8 +214,8 @@ void Window::display(std::shared_ptr const& center_texture) { //////////////////////////////////////////////////////////////////////////////// -void Window::display(std::shared_ptr const& left_texture, - std::shared_ptr const& right_texture) { +void Window::display(std::shared_ptr const& left_texture, + std::shared_ptr const& right_texture) { switch (config.get_stereo_mode()) { case StereoMode::MONO: @@ -249,7 +249,7 @@ RenderContext* Window::get_context() { return &ctx_; } //////////////////////////////////////////////////////////////////////////////// -void Window::display(std::shared_ptr const& texture, +void Window::display(std::shared_ptr const& texture, math::vec2ui const& size, math::vec2ui const& position, TextureDisplayMode mode, @@ -260,13 +260,13 @@ void Window::display(std::shared_ptr const& texture, fullscreen_shader_.set_uniform(ctx_, texture, "sampler"); if (is_left) { - if (warpRL_) fullscreen_shader_.set_uniform(ctx_, std::dynamic_pointer_cast(warpRL_), "warpR"); - if (warpGL_) fullscreen_shader_.set_uniform(ctx_, std::dynamic_pointer_cast(warpGL_), "warpG"); - if (warpBL_) fullscreen_shader_.set_uniform(ctx_, std::dynamic_pointer_cast(warpBL_), "warpB"); + if (warpRL_) fullscreen_shader_.set_uniform(ctx_, std::dynamic_pointer_cast(warpRL_), "warpR"); + if (warpGL_) fullscreen_shader_.set_uniform(ctx_, std::dynamic_pointer_cast(warpGL_), "warpG"); + if (warpBL_) fullscreen_shader_.set_uniform(ctx_, std::dynamic_pointer_cast(warpBL_), "warpB"); } else { - if (warpRR_) fullscreen_shader_.set_uniform(ctx_, std::dynamic_pointer_cast(warpRR_), "warpR"); - if (warpGR_) fullscreen_shader_.set_uniform(ctx_, std::dynamic_pointer_cast(warpGR_), "warpG"); - if (warpBR_) fullscreen_shader_.set_uniform(ctx_, std::dynamic_pointer_cast(warpBR_), "warpB"); + if (warpRR_) fullscreen_shader_.set_uniform(ctx_, std::dynamic_pointer_cast(warpRR_), "warpR"); + if (warpGR_) fullscreen_shader_.set_uniform(ctx_, std::dynamic_pointer_cast(warpGR_), "warpG"); + if (warpBR_) fullscreen_shader_.set_uniform(ctx_, std::dynamic_pointer_cast(warpBR_), "warpB"); } std::string subroutine = subroutine_from_mode(mode); From fdbc9803a4f02a9c08107d7d303b61f0792abeee Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Tue, 29 Oct 2013 12:42:50 +0100 Subject: [PATCH 003/146] change include guard --- include/gua/renderer/Texture2D.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/gua/renderer/Texture2D.hpp b/include/gua/renderer/Texture2D.hpp index 01ff97944..23d49b9a9 100644 --- a/include/gua/renderer/Texture2D.hpp +++ b/include/gua/renderer/Texture2D.hpp @@ -19,8 +19,8 @@ * * ******************************************************************************/ -#ifndef GUA_TEXTURE_HPP -#define GUA_TEXTURE_HPP +#ifndef GUA_TEXTURE2D_HPP +#define GUA_TEXTURE2D_HPP // guacamole headers #include @@ -162,4 +162,4 @@ class Texture2D { }; } -#endif // GUA_TEXTURE_HPP +#endif // GUA_TEXTURE2D_HPP From c56725690e18ecfc05ffd62d49cc2d274a9d2efb Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Tue, 29 Oct 2013 13:03:49 +0100 Subject: [PATCH 004/146] change SAMPLER to SAMPLER2D --- include/gua/renderer/enums.hpp | 2 +- src/gua/renderer/Material.cpp | 2 +- src/gua/renderer/MaterialLoader.cpp | 6 +++--- src/gua/renderer/UberShaderFactory.cpp | 2 +- src/gua/renderer/UniformMapping.cpp | 2 +- src/gua/renderer/enums.cpp | 8 ++++---- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/include/gua/renderer/enums.hpp b/include/gua/renderer/enums.hpp index f37f2c464..3de736f0a 100644 --- a/include/gua/renderer/enums.hpp +++ b/include/gua/renderer/enums.hpp @@ -66,7 +66,7 @@ enum class UniformType { VEC4, MAT3, MAT4, - SAMPLER, + SAMPLER2D, CUBEMAP, NONE }; diff --git a/src/gua/renderer/Material.cpp b/src/gua/renderer/Material.cpp index 61d1657df..650d7243c 100644 --- a/src/gua/renderer/Material.cpp +++ b/src/gua/renderer/Material.cpp @@ -79,7 +79,7 @@ std::unique_ptr create_from_string_and_type( string_utils::from_string(value)); break; - case UniformType::SAMPLER: + case UniformType::SAMPLER2D: return gua::make_unique >(value); break; diff --git a/src/gua/renderer/MaterialLoader.cpp b/src/gua/renderer/MaterialLoader.cpp index 6e915809a..2e0a1b6fe 100644 --- a/src/gua/renderer/MaterialLoader.cpp +++ b/src/gua/renderer/MaterialLoader.cpp @@ -265,7 +265,7 @@ std::string const MaterialLoader::load_shading_model( if (capabilities & OPACITY_MAP) { model->get_gbuffer_fragment_stage().get_uniforms()["opacity_map"] = - UniformType::SAMPLER; + UniformType::SAMPLER2D; gbuffer_fragment_body += std::string( " \n\ @@ -284,7 +284,7 @@ std::string const MaterialLoader::load_shading_model( BufferComponent::F3; model->get_gbuffer_fragment_stage().get_uniforms()["normal_map"] = - UniformType::SAMPLER; + UniformType::SAMPLER2D; gbuffer_vertex_body += std::string( " \n\ @@ -326,7 +326,7 @@ std::string const MaterialLoader::load_shading_model( std::string layer("gua_" + name); model->get_gbuffer_fragment_stage().get_uniforms()[texture] = - UniformType::SAMPLER; + UniformType::SAMPLER2D; model->get_gbuffer_fragment_stage().get_outputs()[layer] = layer_type; gbuffer_fragment_body += diff --git a/src/gua/renderer/UberShaderFactory.cpp b/src/gua/renderer/UberShaderFactory.cpp index 5b722c33c..6946f68ae 100644 --- a/src/gua/renderer/UberShaderFactory.cpp +++ b/src/gua/renderer/UberShaderFactory.cpp @@ -75,7 +75,7 @@ UberShaderFactory::UberShaderFactory( auto mapped( uniform_mapping_.get_mapping(mat->get_name(), uniform->first)); - if (uniform->second == UniformType::SAMPLER) + if (uniform->second == UniformType::SAMPLER2D) call << "gua_get_float_sampler(" << mapped.first << "[" << mapped.second << "])"; else diff --git a/src/gua/renderer/UniformMapping.cpp b/src/gua/renderer/UniformMapping.cpp index 259178937..5f1561aa5 100644 --- a/src/gua/renderer/UniformMapping.cpp +++ b/src/gua/renderer/UniformMapping.cpp @@ -89,7 +89,7 @@ std::string const UniformMapping::get_uniform_definition() const { int count(get_uniform_count(UniformType(t))); if (count > 0) { - if (UniformType(t) == UniformType::SAMPLER) { + if (UniformType(t) == UniformType::SAMPLER2D) { std::string type(enums::uniform_type_to_string(UniformType(t))); result << "uniform uvec2" << " gua_" << type << "s[" << count << "];" << std::endl; diff --git a/src/gua/renderer/enums.cpp b/src/gua/renderer/enums.cpp index 328f12a17..aeb211bcc 100644 --- a/src/gua/renderer/enums.cpp +++ b/src/gua/renderer/enums.cpp @@ -138,7 +138,7 @@ std::string uniform_type_to_string(UniformType type) { return "mat3"; case UniformType::MAT4: return "mat4"; - case UniformType::SAMPLER: + case UniformType::SAMPLER2D: return "sampler2D"; case UniformType::CUBEMAP: return "samplerCube"; @@ -167,7 +167,7 @@ boost::optional parse_uniform_type(std::string const& type) { if (type == "mat4") return boost::make_optional(UniformType::MAT4); if (type == "sampler2D") - return boost::make_optional(UniformType::SAMPLER); + return boost::make_optional(UniformType::SAMPLER2D); if (type == "samplerCube") return boost::make_optional(UniformType::CUBEMAP); @@ -232,7 +232,7 @@ std::string get_default_value(UniformType type) { string_utils::replace(s, "\n", ";"); return s; } - case UniformType::SAMPLER: + case UniformType::SAMPLER2D: return "path/to/texture.png"; case UniformType::CUBEMAP: return "path/to/cubemap.png"; @@ -271,7 +271,7 @@ bool is_valid_value(UniformType type, std::string& value) { return true; } return false; - case UniformType::SAMPLER: + case UniformType::SAMPLER2D: return true; case UniformType::CUBEMAP: return true; From 2b43eb3b83f31d0975fdf0ff3b29c2047ecf2654 Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Tue, 29 Oct 2013 13:07:47 +0100 Subject: [PATCH 005/146] add SAMPLER1D and SAMPLER2D --- include/gua/renderer/enums.hpp | 2 ++ src/gua/renderer/enums.cpp | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/include/gua/renderer/enums.hpp b/include/gua/renderer/enums.hpp index 3de736f0a..1836c0691 100644 --- a/include/gua/renderer/enums.hpp +++ b/include/gua/renderer/enums.hpp @@ -66,7 +66,9 @@ enum class UniformType { VEC4, MAT3, MAT4, + SAMPLER1D, SAMPLER2D, + SAMPLER3D, CUBEMAP, NONE }; diff --git a/src/gua/renderer/enums.cpp b/src/gua/renderer/enums.cpp index aeb211bcc..85d812b25 100644 --- a/src/gua/renderer/enums.cpp +++ b/src/gua/renderer/enums.cpp @@ -138,8 +138,12 @@ std::string uniform_type_to_string(UniformType type) { return "mat3"; case UniformType::MAT4: return "mat4"; + case UniformType::SAMPLER1D: + return "sampler1D"; case UniformType::SAMPLER2D: return "sampler2D"; + case UniformType::SAMPLER3D: + return "sampler3D"; case UniformType::CUBEMAP: return "samplerCube"; default: @@ -166,8 +170,12 @@ boost::optional parse_uniform_type(std::string const& type) { return boost::make_optional(UniformType::MAT3); if (type == "mat4") return boost::make_optional(UniformType::MAT4); + if (type == "sampler1D") + return boost::make_optional(UniformType::SAMPLER1D); if (type == "sampler2D") return boost::make_optional(UniformType::SAMPLER2D); + if (type == "sampler3D") + return boost::make_optional(UniformType::SAMPLER3D); if (type == "samplerCube") return boost::make_optional(UniformType::CUBEMAP); @@ -232,8 +240,12 @@ std::string get_default_value(UniformType type) { string_utils::replace(s, "\n", ";"); return s; } + case UniformType::SAMPLER1D: + return "path/to/texture.png"; case UniformType::SAMPLER2D: return "path/to/texture.png"; + case UniformType::SAMPLER3D: + return "path/to/volume.raw"; case UniformType::CUBEMAP: return "path/to/cubemap.png"; default: @@ -271,8 +283,12 @@ bool is_valid_value(UniformType type, std::string& value) { return true; } return false; + case UniformType::SAMPLER1D: + return true; case UniformType::SAMPLER2D: return true; + case UniformType::SAMPLER3D: + return true; case UniformType::CUBEMAP: return true; default: From 35a678426fc339846e8bef5c8dbbfe3e2271b540 Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Tue, 29 Oct 2013 13:09:47 +0100 Subject: [PATCH 006/146] rename CUBEMAP to SAMPLECUBE --- include/gua/renderer/enums.hpp | 2 +- src/gua/renderer/enums.cpp | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/gua/renderer/enums.hpp b/include/gua/renderer/enums.hpp index 1836c0691..93305fec6 100644 --- a/include/gua/renderer/enums.hpp +++ b/include/gua/renderer/enums.hpp @@ -69,7 +69,7 @@ enum class UniformType { SAMPLER1D, SAMPLER2D, SAMPLER3D, - CUBEMAP, + SAMPLERCUBE, NONE }; diff --git a/src/gua/renderer/enums.cpp b/src/gua/renderer/enums.cpp index 85d812b25..dd055cc7e 100644 --- a/src/gua/renderer/enums.cpp +++ b/src/gua/renderer/enums.cpp @@ -144,7 +144,7 @@ std::string uniform_type_to_string(UniformType type) { return "sampler2D"; case UniformType::SAMPLER3D: return "sampler3D"; - case UniformType::CUBEMAP: + case UniformType::SAMPLERCUBE: return "samplerCube"; default: return "undefined"; @@ -177,7 +177,7 @@ boost::optional parse_uniform_type(std::string const& type) { if (type == "sampler3D") return boost::make_optional(UniformType::SAMPLER3D); if (type == "samplerCube") - return boost::make_optional(UniformType::CUBEMAP); + return boost::make_optional(UniformType::SAMPLERCUBE); return boost::optional(); } @@ -246,7 +246,7 @@ std::string get_default_value(UniformType type) { return "path/to/texture.png"; case UniformType::SAMPLER3D: return "path/to/volume.raw"; - case UniformType::CUBEMAP: + case UniformType::SAMPLERCUBE: return "path/to/cubemap.png"; default: return "undefined"; @@ -289,7 +289,7 @@ bool is_valid_value(UniformType type, std::string& value) { return true; case UniformType::SAMPLER3D: return true; - case UniformType::CUBEMAP: + case UniformType::SAMPLERCUBE: return true; default: return false; @@ -419,7 +419,7 @@ std::set list_uniform_types() { std::set result; for (int t(0); t < static_cast(UniformType::NONE); ++t) { - if (t != static_cast(UniformType::CUBEMAP)) + if (t != static_cast(UniformType::SAMPLERCUBE)) result.insert(uniform_type_to_string(static_cast(t))); } From 10131db0e9559df6125712ef1f637741ac65c495 Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Tue, 29 Oct 2013 14:19:58 +0100 Subject: [PATCH 007/146] add Texture as base class for Texture2D --- include/gua/databases/TextureDatabase.hpp | 4 +- include/gua/renderer/Texture.hpp | 147 ++++++++++++++++++++ include/gua/renderer/Texture2D.hpp | 47 ++----- include/gua/renderer/Uniform.hpp | 47 ++++++- src/gua/databases/TextureDatabase.cpp | 2 + src/gua/renderer/Texture.cpp | 162 ++++++++++++++++++++++ src/gua/renderer/Texture2D.cpp | 118 ++-------------- 7 files changed, 376 insertions(+), 151 deletions(-) create mode 100644 include/gua/renderer/Texture.hpp create mode 100644 src/gua/renderer/Texture.cpp diff --git a/include/gua/databases/TextureDatabase.hpp b/include/gua/databases/TextureDatabase.hpp index 2c45c7856..8248cbf69 100644 --- a/include/gua/databases/TextureDatabase.hpp +++ b/include/gua/databases/TextureDatabase.hpp @@ -25,7 +25,7 @@ // guacamole headers #include #include -#include +#include namespace gua { @@ -35,7 +35,7 @@ namespace gua { * This Database stores texture data. It can be accessed via string * identifiers. */ -class TextureDatabase : public Database, +class TextureDatabase : public Database, public Singleton { public: diff --git a/include/gua/renderer/Texture.hpp b/include/gua/renderer/Texture.hpp new file mode 100644 index 000000000..fd9b8556b --- /dev/null +++ b/include/gua/renderer/Texture.hpp @@ -0,0 +1,147 @@ +/****************************************************************************** + * guacamole - delicious VR * + * * + * Copyright: (c) 2011-2013 Bauhaus-Universität Weimar * + * Contact: felix.lauer@uni-weimar.de / simon.schneegans@uni-weimar.de * + * * + * This program is free software: you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the Free * + * Software Foundation, either version 3 of the License, or (at your option) * + * any later version. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * + * for more details. * + * * + * You should have received a copy of the GNU General Public License along * + * with this program. If not, see . * + * * + ******************************************************************************/ + +#ifndef GUA_TEXTURE_HPP +#define GUA_TEXTURE_HPP + +// guacamole headers +#include +#include +#include +#include + +// external headers +#include +#include + +#if GUA_COMPILER == GUA_COMPILER_MSVC&& GUA_COMPILER_VER <= 1600 +#include +#else +#include +#include +#endif + +namespace gua { + +/** + * A class representing a texture. + * + * This class allows to load texture data from a file and bind the + * texture to an OpenGL context. + */ +class Texture { + public: + + /** + * Constructor. + * + * This constructs a new texture with the given parameters. + * + * \param color_format The color format of the resulting + * texture. + * \param state_descripton The sampler state for the loaded texture. + */ + Texture(scm::gl::data_format color_format, + std::vector const& data, + unsigned mipmap_layers = 1, + scm::gl::sampler_state_desc const& state_descripton = + scm::gl::sampler_state_desc(scm::gl::FILTER_MIN_MAG_LINEAR, + scm::gl::WRAP_CLAMP_TO_EDGE, + scm::gl::WRAP_CLAMP_TO_EDGE)); + + /** + * Constructor. + * + * This constructs a new texture with the given parameters. + * + * \param color_format The color format of the resulting + * texture. + * \param state_descripton The sampler state for the loaded texture. + */ + Texture(scm::gl::data_format color_format = scm::gl::FORMAT_RGB_32F, + unsigned mipmap_layers = 1, + scm::gl::sampler_state_desc const& state_descripton = + scm::gl::sampler_state_desc(scm::gl::FILTER_MIN_MAG_MIP_LINEAR, + scm::gl::WRAP_CLAMP_TO_EDGE, + scm::gl::WRAP_CLAMP_TO_EDGE)); + + /** + * Constructor. + * + * This constructs a new texture from a given file. + * + * \param file The file which contains the texture data. + * \param state_descripton The sampler state for the loaded texture. + */ + Texture(std::string const& file, + bool generate_mipmaps = false, + scm::gl::sampler_state_desc const& state_descripton = + scm::gl::sampler_state_desc(scm::gl::FILTER_ANISOTROPIC, + scm::gl::WRAP_REPEAT, + scm::gl::WRAP_REPEAT)); + + virtual ~Texture(); + + void generate_mipmaps(RenderContext const& context); + + /** + * + */ + virtual math::vec2ui const get_handle(RenderContext const& context) const; + + /** + * Get the schism texture. + * + * \param context The context for which the texture should be + * returned. + * \return A pointer to the schism texture. + */ + virtual scm::gl::texture_image_ptr const& get_buffer( + RenderContext const& context) const; + + void make_resident(RenderContext const& context) const; + void make_non_resident(RenderContext const& context) const; + void make_non_resident() const; + + protected: + mutable unsigned mipmap_layers_; + scm::gl::data_format color_format_; + scm::gl::sampler_state_desc state_descripton_; + mutable std::vector textures_; + mutable std::vector sampler_states_; + mutable std::vector render_contexts_; + +#if GUA_COMPILER == GUA_COMPILER_MSVC&& GUA_COMPILER_VER <= 1600 + mutable boost::mutex upload_mutex_; +#else + mutable std::mutex upload_mutex_; +#endif + virtual void upload_to(RenderContext const& context) const = 0; + + std::vector data_; + std::string file_name_; + + private: + +}; + +} +#endif // GUA_TEXTURE2D_HPP diff --git a/include/gua/renderer/Texture2D.hpp b/include/gua/renderer/Texture2D.hpp index 23d49b9a9..c2d428ae6 100644 --- a/include/gua/renderer/Texture2D.hpp +++ b/include/gua/renderer/Texture2D.hpp @@ -25,6 +25,7 @@ // guacamole headers #include #include +#include #include #include @@ -47,7 +48,7 @@ namespace gua { * This class allows to load texture data from a file and bind the * texture to an OpenGL context. */ -class Texture2D { +class Texture2D : public Texture { public: /** @@ -106,28 +107,7 @@ class Texture2D { scm::gl::WRAP_REPEAT, scm::gl::WRAP_REPEAT)); - virtual ~Texture2D(); - - void generate_mipmaps(RenderContext const& context); - - /** - * - */ - virtual math::vec2ui const get_handle(RenderContext const& context) const; - - /** - * Get the schism texture. - * - * \param context The context for which the texture should be - * returned. - * \return A pointer to the schism texture. - */ - virtual scm::gl::texture_2d_ptr const& get_buffer( - RenderContext const& context) const; - - void make_resident(RenderContext const& context) const; - void make_non_resident(RenderContext const& context) const; - void make_non_resident() const; + virtual ~Texture2D() {} ///@{ /** @@ -135,28 +115,17 @@ class Texture2D { * * Returns the size of the Texture2D. */ - unsigned width() const; - unsigned height() const; + unsigned width() const { return width_; } + unsigned height() const { return height_; } + ///@} protected: - mutable unsigned width_, height_, mipmap_layers_; - scm::gl::data_format color_format_; - scm::gl::sampler_state_desc state_descripton_; - mutable std::vector textures_; - mutable std::vector sampler_states_; - mutable std::vector render_contexts_; + mutable unsigned width_; + mutable unsigned height_; -#if GUA_COMPILER == GUA_COMPILER_MSVC&& GUA_COMPILER_VER <= 1600 - mutable boost::mutex upload_mutex_; -#else - mutable std::mutex upload_mutex_; -#endif virtual void upload_to(RenderContext const& context) const; - std::vector data_; - std::string file_name_; - private: }; diff --git a/include/gua/renderer/Uniform.hpp b/include/gua/renderer/Uniform.hpp index ae6e44cc2..c9587ec8a 100644 --- a/include/gua/renderer/Uniform.hpp +++ b/include/gua/renderer/Uniform.hpp @@ -23,6 +23,7 @@ #define GUA_UNIFORM_HPP // guacamole headers +#include #include #include #include @@ -107,6 +108,51 @@ template class UniformValue : public UniformValueBase { +template <> +class UniformValue > : public UniformValueBase { + public: + UniformValue(std::shared_ptr const& value) + : UniformValueBase(), value_(value) {} + + void apply(RenderContext const& context, + scm::gl::program_ptr program, + std::string const& name, + unsigned position = 0) const { + + program->uniform(name, position, value_->get_handle(context)); + } + + std::shared_ptr const& value() const { return value_; } + + void value(std::shared_ptr const& value) { value_ = value; } + + private: + std::shared_ptr value_; +}; + +template <> +class UniformValue : public UniformValueBase { + public: + UniformValue(Texture* value) + : UniformValueBase(), value_(value) {} + + void apply(RenderContext const& context, + scm::gl::program_ptr program, + std::string const& name, + unsigned position = 0) const { + + program->uniform(name, position, value_->get_handle(context)); + } + + Texture* value() const { return value_; } + + void value(Texture* value) { value_ = value; } + + private: + Texture* value_; +}; + + template <> class UniformValue > : public UniformValueBase { public: @@ -152,7 +198,6 @@ class UniformValue : public UniformValueBase { }; - template <> class UniformValue : public UniformValueBase { public: UniformValue(std::string const& value) : UniformValueBase(), value_(value) {} diff --git a/src/gua/databases/TextureDatabase.cpp b/src/gua/databases/TextureDatabase.cpp index 43d6c1446..ba7da7b1f 100644 --- a/src/gua/databases/TextureDatabase.cpp +++ b/src/gua/databases/TextureDatabase.cpp @@ -21,6 +21,7 @@ // class header #include +#include // guacamole headers #include @@ -29,6 +30,7 @@ // external headers #include #include +#include namespace gua { diff --git a/src/gua/renderer/Texture.cpp b/src/gua/renderer/Texture.cpp new file mode 100644 index 000000000..3418da24b --- /dev/null +++ b/src/gua/renderer/Texture.cpp @@ -0,0 +1,162 @@ +/****************************************************************************** + * guacamole - delicious VR * + * * + * Copyright: (c) 2011-2013 Bauhaus-Universität Weimar * + * Contact: felix.lauer@uni-weimar.de / simon.schneegans@uni-weimar.de * + * * + * This program is free software: you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the Free * + * Software Foundation, either version 3 of the License, or (at your option) * + * any later version. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * + * for more details. * + * * + * You should have received a copy of the GNU General Public License along * + * with this program. If not, see . * + * * + ******************************************************************************/ + +// class header +#include + +// guacamole headers +#include +#include +#include + +// external headers +#include +#include + +namespace gua { + +Texture::Texture(scm::gl::data_format color_format, + std::vector const& data, + unsigned mipmap_layers, + scm::gl::sampler_state_desc const& state_descripton) + : mipmap_layers_(mipmap_layers), + color_format_(color_format), + file_name_(""), + data_(data), + state_descripton_(state_descripton), + textures_(), + sampler_states_(), + upload_mutex_() {} + +Texture::Texture(scm::gl::data_format color_format, + unsigned mipmap_layers, + scm::gl::sampler_state_desc const& state_descripton) + : mipmap_layers_(mipmap_layers), + color_format_(color_format), + file_name_(""), + state_descripton_(state_descripton), + textures_(), + sampler_states_(), + upload_mutex_() {} + +Texture::Texture(std::string const& file, + bool generate_mipmaps, + scm::gl::sampler_state_desc const& state_descripton) + : + mipmap_layers_(generate_mipmaps ? 1 : 0), + color_format_(scm::gl::FORMAT_NULL), + file_name_(file), + state_descripton_(state_descripton), + textures_(), + sampler_states_(), + upload_mutex_() {} + +Texture::~Texture() { + make_non_resident(); +} + +void Texture::generate_mipmaps(RenderContext const& context) { + + if (textures_.size() <= context.id || textures_[context.id] == 0) + upload_to(context); + + context.render_context->generate_mipmaps(textures_[context.id]); +} + +math::vec2ui const Texture::get_handle(RenderContext const& context) const { + + if (textures_.size() <= context.id || textures_[context.id] == 0) + upload_to(context); + + uint64_t handle(textures_[context.id]->native_handle()); + + return math::vec2ui(handle & 0x00000000ffffffff, handle & 0xffffffff00000000); +} + +scm::gl::texture_image_ptr const& Texture::get_buffer( + RenderContext const& context) const { + + if (textures_.size() <= context.id || textures_[context.id] == 0) + upload_to(context); + + return textures_[context.id]; +} + +void Texture::make_resident(RenderContext const& context) const { + context.render_context + ->make_resident(textures_[context.id], sampler_states_[context.id]); + +} + +void Texture::make_non_resident(RenderContext const& context) const { + context.render_context->make_non_resident(textures_[context.id]); +} + +void Texture::make_non_resident() const { + for (int i(0); imake_non_resident(textures_[i]); + } +} + +#if 0 +void Texture::upload_to(RenderContext const& context) const { + std::unique_lock lock(upload_mutex_); + + if (textures_.size() <= context.id) { + textures_.resize(context.id + 1); + sampler_states_.resize(context.id + 1); + render_contexts_.resize(context.id + 1); + } + + if (file_name_ == "") { + + + if (data_.size() == 0) + textures_[context.id] = context.render_device->create_texture_2d( + math::vec2ui(width_, height_), color_format_, mipmap_layers_); + else + textures_[context.id] = context.render_device->create_texture_2d( + scm::gl::texture_2d_desc( + math::vec2ui(width_, height_), color_format_, mipmap_layers_), + color_format_, + data_); + } else { + MESSAGE("Uploading texture file %s", file_name_.c_str()); + scm::gl::texture_loader loader; + textures_[context.id] = loader.load_texture_2d( + *context.render_device, file_name_, mipmap_layers_ > 0); + + if (textures_[context.id]) { + width_ = textures_[context.id]->dimensions()[0]; + height_ = textures_[context.id]->dimensions()[1]; + } + } + + sampler_states_[context.id] = + context.render_device->create_sampler_state(state_descripton_); + + render_contexts_[context.id] = context.render_context; + + make_resident(context); +} +#endif + +} diff --git a/src/gua/renderer/Texture2D.cpp b/src/gua/renderer/Texture2D.cpp index 15276a880..f1e0d323f 100644 --- a/src/gua/renderer/Texture2D.cpp +++ b/src/gua/renderer/Texture2D.cpp @@ -33,129 +33,31 @@ namespace gua { -//////////////////////////////////////////////////////////////////////////////// - Texture2D::Texture2D(unsigned width, unsigned height, scm::gl::data_format color_format, std::vector const& data, unsigned mipmap_layers, scm::gl::sampler_state_desc const& state_descripton) - : width_(width), - height_(height), - mipmap_layers_(mipmap_layers), - color_format_(color_format), - file_name_(""), - data_(data), - state_descripton_(state_descripton), - textures_(), - sampler_states_(), - upload_mutex_() {} - -//////////////////////////////////////////////////////////////////////////////// + : Texture(color_format, data, mipmap_layers, state_descripton), + width_(width), + height_(height) {} Texture2D::Texture2D(unsigned width, unsigned height, scm::gl::data_format color_format, unsigned mipmap_layers, scm::gl::sampler_state_desc const& state_descripton) - : width_(width), - height_(height), - mipmap_layers_(mipmap_layers), - color_format_(color_format), - file_name_(""), - state_descripton_(state_descripton), - textures_(), - sampler_states_(), - upload_mutex_() {} - -//////////////////////////////////////////////////////////////////////////////// + : Texture(color_format, mipmap_layers, state_descripton), + width_(width), + height_(height) {} Texture2D::Texture2D(std::string const& file, bool generate_mipmaps, scm::gl::sampler_state_desc const& state_descripton) - : width_(0), - height_(0), - mipmap_layers_(generate_mipmaps ? 1 : 0), - color_format_(scm::gl::FORMAT_NULL), - file_name_(file), - state_descripton_(state_descripton), - textures_(), - sampler_states_(), - upload_mutex_() {} - -//////////////////////////////////////////////////////////////////////////////// - -Texture2D::~Texture2D() { - make_non_resident(); -} - -//////////////////////////////////////////////////////////////////////////////// - -void Texture2D::generate_mipmaps(RenderContext const& context) { - - if (textures_.size() <= context.id || textures_[context.id] == 0) - upload_to(context); - - context.render_context->generate_mipmaps(textures_[context.id]); -} - -//////////////////////////////////////////////////////////////////////////////// - -math::vec2ui const Texture2D::get_handle(RenderContext const& context) const { - - if (textures_.size() <= context.id || textures_[context.id] == 0) - upload_to(context); - - uint64_t handle(textures_[context.id]->native_handle()); - - return math::vec2ui(handle & 0x00000000ffffffff, handle & 0xffffffff00000000); -} - -//////////////////////////////////////////////////////////////////////////////// - -scm::gl::texture_2d_ptr const& Texture2D::get_buffer( - RenderContext const& context) const { - - if (textures_.size() <= context.id || textures_[context.id] == 0) - upload_to(context); - - return textures_[context.id]; -} - -//////////////////////////////////////////////////////////////////////////////// - -void Texture2D::make_resident(RenderContext const& context) const { - context.render_context - ->make_resident(textures_[context.id], sampler_states_[context.id]); - -} - -//////////////////////////////////////////////////////////////////////////////// - -void Texture2D::make_non_resident(RenderContext const& context) const { - - context.render_context->make_non_resident(textures_[context.id]); -} - -//////////////////////////////////////////////////////////////////////////////// - -void Texture2D::make_non_resident() const { - - for (int i(0); imake_non_resident(textures_[i]); - } -} - -//////////////////////////////////////////////////////////////////////////////// - -unsigned Texture2D::width() const { return width_; } - -//////////////////////////////////////////////////////////////////////////////// - -unsigned Texture2D::height() const { return height_; } - -//////////////////////////////////////////////////////////////////////////////// + : Texture(file, generate_mipmaps, state_descripton), + width_(0), + height_(0) {} void Texture2D::upload_to(RenderContext const& context) const { @@ -199,6 +101,4 @@ void Texture2D::upload_to(RenderContext const& context) const { make_resident(context); } -//////////////////////////////////////////////////////////////////////////////// - } From 7946b67f8bcb3c499622a38b896573be78115086 Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Tue, 29 Oct 2013 15:13:37 +0100 Subject: [PATCH 008/146] add Texture3D --- include/gua/renderer/Texture3D.hpp | 140 +++++++++++++++++++++++++++++ src/gua/renderer/Texture3D.cpp | 110 +++++++++++++++++++++++ 2 files changed, 250 insertions(+) create mode 100644 include/gua/renderer/Texture3D.hpp create mode 100644 src/gua/renderer/Texture3D.cpp diff --git a/include/gua/renderer/Texture3D.hpp b/include/gua/renderer/Texture3D.hpp new file mode 100644 index 000000000..205c7e439 --- /dev/null +++ b/include/gua/renderer/Texture3D.hpp @@ -0,0 +1,140 @@ +/****************************************************************************** + * guacamole - delicious VR * + * * + * Copyright: (c) 2011-2013 Bauhaus-Universität Weimar * + * Contact: felix.lauer@uni-weimar.de / simon.schneegans@uni-weimar.de * + * * + * This program is free software: you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the Free * + * Software Foundation, either version 3 of the License, or (at your option) * + * any later version. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * + * for more details. * + * * + * You should have received a copy of the GNU General Public License along * + * with this program. If not, see . * + * * + ******************************************************************************/ + +#ifndef GUA_TEXTURE3D_HPP +#define GUA_TEXTURE3D_HPP + +// guacamole headers +#include +#include +#include +#include +#include + +// external headers +#include +#include + +#if GUA_COMPILER == GUA_COMPILER_MSVC&& GUA_COMPILER_VER <= 1600 +#include +#else +#include +#include +#endif + +namespace gua { + +/** + * A class representing a texture. + * + * This class allows to load texture data from a file and bind the + * texture to an OpenGL context. + */ +class Texture3D : public Texture { + public: + + /** + * Constructor. + * + * This constructs a new texture with the given parameters. + * + * \param width The width of the resulting 3D texture. + * \param height The height of the resulting 3D texture. + * \param depth The depth of the resulting 3D texture. + * \param color_format The color format of the resulting 3D + * texture. + * \param state_descripton The sampler state for the loaded 3D texture. + */ + Texture3D(unsigned width, + unsigned height, + unsigned depth, + scm::gl::data_format color_format, + std::vector const& data, + unsigned mipmap_layers = 1, + scm::gl::sampler_state_desc const& state_descripton = + scm::gl::sampler_state_desc(scm::gl::FILTER_MIN_MAG_LINEAR, + scm::gl::WRAP_CLAMP_TO_EDGE, + scm::gl::WRAP_CLAMP_TO_EDGE)); + + /** + * Constructor. + * + * This constructs a new texture with the given parameters. + * + * \param width The width of the resulting texture. + * \param height The height of the resulting texture. + * \param depth The depth of the resulting 3D texture. + * \param color_format The color format of the resulting + * texture. + * \param state_descripton The sampler state for the loaded texture. + */ + Texture3D(unsigned width, + unsigned height, + unsigned depth, + scm::gl::data_format color_format = scm::gl::FORMAT_RGB_32F, + unsigned mipmap_layers = 1, + scm::gl::sampler_state_desc const& state_descripton = + scm::gl::sampler_state_desc(scm::gl::FILTER_MIN_MAG_MIP_LINEAR, + scm::gl::WRAP_CLAMP_TO_EDGE, + scm::gl::WRAP_CLAMP_TO_EDGE)); + + /** + * Constructor. + * + * This constructs a new texture from a given file. + * + * \param file The file which contains the texture data. + * \param state_descripton The sampler state for the loaded texture. + */ + Texture3D(std::string const& file, + bool generate_mipmaps = false, + scm::gl::sampler_state_desc const& state_descripton = + scm::gl::sampler_state_desc(scm::gl::FILTER_ANISOTROPIC, + scm::gl::WRAP_REPEAT, + scm::gl::WRAP_REPEAT)); + + virtual ~Texture3D() {} + + ///@{ + /** + * Gets the size. + * + * Returns the size of the Texture3D. + */ + inline unsigned width() const { return width_; } + inline unsigned height() const { return height_; } + inline unsigned depth() const { return depth_; } + + ///@} + + protected: + mutable unsigned width_; + mutable unsigned height_; + mutable unsigned depth_; + + virtual void upload_to(RenderContext const& context) const; + + private: + +}; + +} +#endif // GUA_TEXTURE3D_HPP diff --git a/src/gua/renderer/Texture3D.cpp b/src/gua/renderer/Texture3D.cpp new file mode 100644 index 000000000..a74e2c340 --- /dev/null +++ b/src/gua/renderer/Texture3D.cpp @@ -0,0 +1,110 @@ +/****************************************************************************** + * guacamole - delicious VR * + * * + * Copyright: (c) 2011-2013 Bauhaus-Universität Weimar * + * Contact: felix.lauer@uni-weimar.de / simon.schneegans@uni-weimar.de * + * * + * This program is free software: you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the Free * + * Software Foundation, either version 3 of the License, or (at your option) * + * any later version. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * + * for more details. * + * * + * You should have received a copy of the GNU General Public License along * + * with this program. If not, see . * + * * + ******************************************************************************/ + +// class header +#include + +// guacamole headers +#include +#include +#include + +// external headers +#include +#include + +namespace gua { + +Texture3D::Texture3D(unsigned width, + unsigned height, + unsigned depth, + scm::gl::data_format color_format, + std::vector const& data, + unsigned mipmap_layers, + scm::gl::sampler_state_desc const& state_descripton) + : Texture(color_format, data, mipmap_layers, state_descripton), + width_(width), + height_(height), + depth_(depth) {} + +Texture3D::Texture3D(unsigned width, + unsigned height, + unsigned depth, + scm::gl::data_format color_format, + unsigned mipmap_layers, + scm::gl::sampler_state_desc const& state_descripton) + : Texture(color_format, mipmap_layers, state_descripton), + width_(width), + height_(height), + depth_(depth) {} + +Texture3D::Texture3D(std::string const& file, + bool generate_mipmaps, + scm::gl::sampler_state_desc const& state_descripton) + : Texture(file, generate_mipmaps, state_descripton), + width_(0), + height_(0), + depth_(0) {} + +void Texture3D::upload_to(RenderContext const& context) const { + + std::unique_lock lock(upload_mutex_); + + if (textures_.size() <= context.id) { + textures_.resize(context.id + 1); + sampler_states_.resize(context.id + 1); + render_contexts_.resize(context.id + 1); + } + + if (file_name_ == "") { + + + if (data_.size() == 0) + textures_[context.id] = context.render_device->create_texture_3d( + math::vec3ui(width_, height_, depth_), color_format_, mipmap_layers_); + else + textures_[context.id] = context.render_device->create_texture_3d( + scm::gl::texture_3d_desc( + math::vec3ui(width_, height_, depth_), color_format_, mipmap_layers_), + color_format_, + data_); + } else { + MESSAGE("Uploading texture file %s", file_name_.c_str()); + scm::gl::volume_loader loader; + textures_[context.id] = loader.load_texture_3d( + *context.render_device, file_name_, mipmap_layers_ > 0); + + if (textures_[context.id]) { + width_ = textures_[context.id]->dimensions()[0]; + height_ = textures_[context.id]->dimensions()[1]; + depth_ = textures_[context.id]->dimensions()[2]; + } + } + + sampler_states_[context.id] = + context.render_device->create_sampler_state(state_descripton_); + + render_contexts_[context.id] = context.render_context; + + make_resident(context); +} + +} From a462fa0cbb2a10fd8acfe02450b17e376b3dbdec Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Wed, 30 Oct 2013 10:26:22 +0100 Subject: [PATCH 009/146] added dedicated create_shaders method to pipeline --- include/gua/renderer/Window.hpp | 23 ++++++++++---------- src/gua/renderer/Pipeline.cpp | 1 + src/gua/renderer/Window.cpp | 38 +++++++++++++++++++-------------- 3 files changed, 35 insertions(+), 27 deletions(-) diff --git a/include/gua/renderer/Window.hpp b/include/gua/renderer/Window.hpp index f6e6719db..4a972142c 100644 --- a/include/gua/renderer/Window.hpp +++ b/include/gua/renderer/Window.hpp @@ -100,6 +100,8 @@ class Window { void open(); bool get_is_open() const; + virtual void create_shader(); + void close(); /** @@ -127,9 +129,9 @@ class Window { /** * */ - void display(std::shared_ptr const& center_texture); + virtual void display(std::shared_ptr const& center_texture); - void display(std::shared_ptr const& left_texture, + virtual void display(std::shared_ptr const& left_texture, std::shared_ptr const& right_texture); /** @@ -142,6 +144,14 @@ class Window { */ RenderContext* get_context(); +protected: + ShaderProgram fullscreen_shader_; + scm::gl::quad_geometry_ptr fullscreen_quad_; + + scm::gl::depth_stencil_state_ptr depth_stencil_state_; + scm::gl::blend_state_ptr blend_state_; + RenderContext ctx_; + private: void display(std::shared_ptr const& texture, math::vec2ui const& size, @@ -153,15 +163,6 @@ class Window { static unsigned last_context_id_; - - RenderContext ctx_; - - ShaderProgram fullscreen_shader_; - scm::gl::quad_geometry_ptr fullscreen_quad_; - - scm::gl::depth_stencil_state_ptr depth_stencil_state_; - scm::gl::blend_state_ptr blend_state_; - std::shared_ptr warpRR_, warpGR_, warpBR_, warpRL_, warpGL_, warpBL_; }; diff --git a/src/gua/renderer/Pipeline.cpp b/src/gua/renderer/Pipeline.cpp index 1a07cb198..1dcbbe8d8 100644 --- a/src/gua/renderer/Pipeline.cpp +++ b/src/gua/renderer/Pipeline.cpp @@ -145,6 +145,7 @@ void Pipeline::process(std::vector> const& sce if (window_) { if (!window_->get_is_open()) { window_->open(); + window_->create_shader(); } set_context(window_->get_context()); diff --git a/src/gua/renderer/Window.cpp b/src/gua/renderer/Window.cpp index 628dd184a..7914ca6a6 100644 --- a/src/gua/renderer/Window.cpp +++ b/src/gua/renderer/Window.cpp @@ -108,6 +108,28 @@ void Window::open() { ctx_.id = last_context_id_++; + + + fullscreen_quad_ = scm::gl::quad_geometry_ptr(new scm::gl::quad_geometry( + ctx_.render_device, math::vec2(-1.f, -1.f), math::vec2(1.f, 1.f))); + + depth_stencil_state_ = ctx_.render_device + ->create_depth_stencil_state(false, false, scm::gl::COMPARISON_NEVER); + + blend_state_ = ctx_.render_device->create_blend_state(true, + scm::gl::FUNC_ONE, + scm::gl::FUNC_ONE, + scm::gl::FUNC_ONE, + scm::gl::FUNC_ONE); +} + +//////////////////////////////////////////////////////////////////////////////// + +bool Window::get_is_open() const { return ctx_.window != nullptr; } + +//////////////////////////////////////////////////////////////////////////////// + +void Window::create_shader() { if (config.get_warp_matrix_red_right() == "" || config.get_warp_matrix_green_right() == "" || config.get_warp_matrix_blue_right() == "" || @@ -136,26 +158,10 @@ void Window::open() { Resources::lookup_shader(Resources::shaders_display_shader_warped_frag) ); } - - fullscreen_quad_ = scm::gl::quad_geometry_ptr(new scm::gl::quad_geometry( - ctx_.render_device, math::vec2(-1.f, -1.f), math::vec2(1.f, 1.f))); - - depth_stencil_state_ = ctx_.render_device - ->create_depth_stencil_state(false, false, scm::gl::COMPARISON_NEVER); - - blend_state_ = ctx_.render_device->create_blend_state(true, - scm::gl::FUNC_ONE, - scm::gl::FUNC_ONE, - scm::gl::FUNC_ONE, - scm::gl::FUNC_ONE); } //////////////////////////////////////////////////////////////////////////////// -bool Window::get_is_open() const { return ctx_.window != nullptr; } - -//////////////////////////////////////////////////////////////////////////////// - void Window::close() { if (get_is_open()) { From ffc55ff4315501ed62e769fb96d2d987eee3f63d Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Wed, 30 Oct 2013 11:48:27 +0100 Subject: [PATCH 010/146] fixed bug which rendered it impossible to load same geometry with different materials --- include/gua/renderer/GeometryLoader.hpp | 2 ++ include/gua/renderer/LoaderBase.hpp | 1 - include/gua/renderer/MeshLoader.hpp | 2 -- include/gua/renderer/NURBSLoader.hpp | 1 - src/gua/renderer/GeometryLoader.cpp | 22 +++++++++++++++++++++- src/gua/renderer/MeshLoader.cpp | 10 ++++------ src/gua/renderer/NURBSLoader.cpp | 5 ++--- 7 files changed, 29 insertions(+), 14 deletions(-) diff --git a/include/gua/renderer/GeometryLoader.hpp b/include/gua/renderer/GeometryLoader.hpp index d08654993..2e065f470 100644 --- a/include/gua/renderer/GeometryLoader.hpp +++ b/include/gua/renderer/GeometryLoader.hpp @@ -62,6 +62,8 @@ class GeometryLoader { private: + void apply_fallback_material(std::shared_ptr const& root, std::string const& fallback_material) const; + static std::unordered_map> loaded_files_; std::string parent_material_name_; diff --git a/include/gua/renderer/LoaderBase.hpp b/include/gua/renderer/LoaderBase.hpp index a2c8cec3f..bde9f3f86 100644 --- a/include/gua/renderer/LoaderBase.hpp +++ b/include/gua/renderer/LoaderBase.hpp @@ -63,7 +63,6 @@ class LoaderBase { * \param flags TODO: what does flags? */ virtual std::shared_ptr load(std::string const& file_name, - std::string const& fallback_material, unsigned flags) = 0; /** diff --git a/include/gua/renderer/MeshLoader.hpp b/include/gua/renderer/MeshLoader.hpp index 906e5f741..19f282f87 100644 --- a/include/gua/renderer/MeshLoader.hpp +++ b/include/gua/renderer/MeshLoader.hpp @@ -75,7 +75,6 @@ class MeshLoader : public LoaderBase { * \param material_name The material name that was set to the parent node */ std::shared_ptr load(std::string const& file_name, - std::string const& fallback_material, unsigned flags); /** @@ -98,7 +97,6 @@ class MeshLoader : public LoaderBase { aiScene const* ai_scene, aiNode* ai_root, std::string const& file_name, - std::string const& fallback_material, unsigned flags); unsigned node_counter_; diff --git a/include/gua/renderer/NURBSLoader.hpp b/include/gua/renderer/NURBSLoader.hpp index fc4a531a2..c64a090aa 100644 --- a/include/gua/renderer/NURBSLoader.hpp +++ b/include/gua/renderer/NURBSLoader.hpp @@ -57,7 +57,6 @@ class NURBSLoader : public LoaderBase { * \param unsigned Special flag */ /* virtual */ std::shared_ptr load(std::string const& file_name, - std::string const& fallback_material, unsigned flags); /* virtual */ bool is_supported(std::string const& file_name) const; diff --git a/src/gua/renderer/GeometryLoader.cpp b/src/gua/renderer/GeometryLoader.cpp index 105068a59..a17acb364 100644 --- a/src/gua/renderer/GeometryLoader.cpp +++ b/src/gua/renderer/GeometryLoader.cpp @@ -80,7 +80,7 @@ std::shared_ptr GeometryLoader::create_geometry_from_file bool fileload_succeed = false; for (auto f : fileloaders_) { if (f->is_supported(file_name)) { - cached_node = f->load(file_name, fallback_material, flags); + cached_node = f->load(file_name, flags); cached_node->update_cache(); loaded_files_.insert(std::make_pair(key, cached_node)); @@ -115,6 +115,9 @@ std::shared_ptr GeometryLoader::create_geometry_from_file if (cached_node) { auto copy(cached_node->deep_copy()); + + apply_fallback_material(copy, fallback_material); + copy->set_name(node_name); return copy; } @@ -122,6 +125,23 @@ std::shared_ptr GeometryLoader::create_geometry_from_file return std::make_shared(node_name); } +//////////////////////////////////////////////////////////////////////////////// + +void GeometryLoader::apply_fallback_material(std::shared_ptr const& root, std::string const& fallback_material) const { + + auto g_node(std::dynamic_pointer_cast(root)); + + if (g_node) { + if (g_node->data.get_material().empty()) { + g_node->data.set_material(fallback_material); + } + + for(auto& child: root->get_children()) { + apply_fallback_material(child, fallback_material); + } + } + +} //////////////////////////////////////////////////////////////////////////////// diff --git a/src/gua/renderer/MeshLoader.cpp b/src/gua/renderer/MeshLoader.cpp index 6d2b856b2..833e33892 100644 --- a/src/gua/renderer/MeshLoader.cpp +++ b/src/gua/renderer/MeshLoader.cpp @@ -44,7 +44,6 @@ MeshLoader::MeshLoader() : node_counter_(0) {} std::shared_ptr MeshLoader::load(std::string const& file_name, - std::string const& fallback_material, unsigned flags) { node_counter_ = 0; @@ -93,7 +92,7 @@ std::shared_ptr MeshLoader::load(std::string const& file_name, // new_node = std::make_shared(new GeometryNode("unnamed", // GeometryNode::Configuration("", ""), // math::mat4::identity())); - new_node = get_tree(importer, scene, scene->mRootNode, file_name, fallback_material, flags); + new_node = get_tree(importer, scene, scene->mRootNode, file_name, flags); } else { WARNING("Failed to load object \"%s\": No valid root node contained!", @@ -140,7 +139,6 @@ std::shared_ptr MeshLoader::get_tree(std::shared_ptr con aiScene const* ai_scene, aiNode* ai_root, std::string const& file_name, - std::string const& fallback_material, unsigned flags) { // creates a geometry node and returns it @@ -150,7 +148,7 @@ std::shared_ptr MeshLoader::get_tree(std::shared_ptr con GeometryDatabase::instance()->add(mesh_name, std::make_shared(ai_scene->mMeshes[ai_root->mMeshes[i]], importer, flags & GeometryLoader::MAKE_PICKABLE)); // load material - std::string material_name(fallback_material); + std::string material_name(""); unsigned material_index(ai_scene->mMeshes[ai_root->mMeshes[i]]->mMaterialIndex); if (material_index != 0 && flags & GeometryLoader::LOAD_MATERIALS) { @@ -170,7 +168,7 @@ std::shared_ptr MeshLoader::get_tree(std::shared_ptr con if (ai_root->mNumChildren == 1 && ai_root->mNumMeshes == 0) { return get_tree( importer, ai_scene, ai_root->mChildren[0], - file_name, fallback_material, flags + file_name, flags ); } @@ -190,7 +188,7 @@ std::shared_ptr MeshLoader::get_tree(std::shared_ptr con group->add_child( get_tree( importer, ai_scene, ai_root->mChildren[i], - file_name, fallback_material, flags + file_name, flags ) ); } diff --git a/src/gua/renderer/NURBSLoader.cpp b/src/gua/renderer/NURBSLoader.cpp index 73ef235c2..7303c397a 100644 --- a/src/gua/renderer/NURBSLoader.cpp +++ b/src/gua/renderer/NURBSLoader.cpp @@ -47,7 +47,6 @@ NURBSLoader::NURBSLoader() : LoaderBase(), _supported_file_extensions() { //////////////////////////////////////////////////////////////////////////////// /* virtual */ std::shared_ptr NURBSLoader::load(std::string const& file_name, - std::string const& fallback_material, unsigned flags) { try { igs_loader igsloader; @@ -65,8 +64,8 @@ std::shared_ptr NURBSLoader::load(std::string const& file_name, file_name, std::shared_ptr(new NURBS(bezier_object))); auto result = std::make_shared("unnamed_nurbs"); - result->data.geometry = file_name; - result->data.material = fallback_material; + result->data.set_geometry(file_name); + result->data.set_material(""); return result; From 3f7f9cceef3ca90fa550168635ff6f56eb9b81e5 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Wed, 30 Oct 2013 13:04:14 +0100 Subject: [PATCH 011/146] fixed not-working ticker --- src/gua/events/Ticker.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/gua/events/Ticker.cpp b/src/gua/events/Ticker.cpp index b486dbc97..7d180f1e3 100644 --- a/src/gua/events/Ticker.cpp +++ b/src/gua/events/Ticker.cpp @@ -26,7 +26,7 @@ namespace gua { namespace events { - Ticker::Ticker(MainLoop& mainloop, double tick_time) + Ticker::Ticker(MainLoop& mainloop, double tick_time) : timer_(new boost::asio::deadline_timer(mainloop.io_service, boost::posix_time::microseconds(1000000.0*tick_time))), tick_time_(tick_time) { @@ -38,16 +38,17 @@ namespace gua { delete timer_; } - void Ticker::self_callback(int revents) - { - on_tick.emit(); + void Ticker::self_callback(int revents) + { async_wait(); + on_tick.emit(); } void Ticker::async_wait() { + timer_->expires_from_now(boost::posix_time::microseconds(1000000.0*tick_time_)); timer_->async_wait(boost::bind(&Ticker::self_callback, this, 0)); } - + } } From 2c0a7b3e3b7dfa89fbe87ca4f66a9fc8930424b3 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Wed, 30 Oct 2013 13:11:30 +0100 Subject: [PATCH 012/146] prevented useless recompilation of resources under linux --- CMakeLists.txt | 61 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3467d78ad..ae4fb3f53 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,7 +35,7 @@ if (UNIX) pkg_check_modules(GL REQUIRED gl) pkg_check_modules(GLEW REQUIRED glew) elseif (WIN32) - include(find_assimp) + include(find_assimp) endif (UNIX) include(find_compiler) @@ -112,8 +112,6 @@ file(GLOB_RECURSE RESOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} resources/* ) -MESSAGE(${RESOURCES}) - if (NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/include/gua/generated/) file(MAKE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/gua/generated) endif() @@ -122,31 +120,46 @@ if (NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/src/gua/generated/) file(MAKE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src/gua/generated) endif() -# ADD_CUSTOM_COMMAND(OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/src/gua/generated/R.inl - # COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/guarc/guarc - # ${CMAKE_CURRENT_SOURCE_DIR}/resources - # ${CMAKE_CURRENT_SOURCE_DIR}/include/gua/generated/R.inl - # ${CMAKE_CURRENT_SOURCE_DIR}/src/gua/generated/R.inl - # DEPENDS ${RESOURCES} guarc -# ) - -ADD_CUSTOM_TARGET(CompileResources ALL - DEPENDS ${RESOURCES} guarc -) -SET (_GUARC_EXECUTABLE "") IF (UNIX) - SET (_GUARC_EXECUTABLE "/guarc/guarc") -ELSEIF(WIN32) - SET (_GUARC_EXECUTABLE "/guarc/$(Configuration)/guarc.exe") + ADD_CUSTOM_COMMAND(OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/src/gua/generated/R.inl + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/guarc/guarc + ${CMAKE_CURRENT_SOURCE_DIR}/resources + ${CMAKE_CURRENT_SOURCE_DIR}/include/gua/generated/R.inl + ${CMAKE_CURRENT_SOURCE_DIR}/src/gua/generated/R.inl + DEPENDS ${RESOURCES} guarc + ) + + ADD_CUSTOM_TARGET(CompileResources ALL + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/src/gua/generated/R.inl + ) + +ELSEIF(MSVC) + ADD_CUSTOM_TARGET(CompileResources ALL + DEPENDS ${RESOURCES} guarc + ) + + SET (_GUARC_EXECUTABLE "") + SET (_GUARC_EXECUTABLE "/guarc/$(Configuration)/guarc.exe") + + ADD_CUSTOM_COMMAND(TARGET CompileResources POST_BUILD + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}${_GUARC_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/resources + ${CMAKE_CURRENT_SOURCE_DIR}/include/gua/generated/R.inl + ${CMAKE_CURRENT_SOURCE_DIR}/src/gua/generated/R.inl + ) ENDIF(UNIX) -ADD_CUSTOM_COMMAND(TARGET CompileResources POST_BUILD - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}${_GUARC_EXECUTABLE} - ${CMAKE_CURRENT_SOURCE_DIR}/resources - ${CMAKE_CURRENT_SOURCE_DIR}/include/gua/generated/R.inl - ${CMAKE_CURRENT_SOURCE_DIR}/src/gua/generated/R.inl -) + + + + + + + + + + ################################################################ # Create libraries From 6e5d6278c2cb1e00702472ccc94e73fc5a603b88 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Wed, 30 Oct 2013 15:35:45 +0100 Subject: [PATCH 013/146] fixed missing propagation of fall_back_material --- src/gua/renderer/GeometryLoader.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/gua/renderer/GeometryLoader.cpp b/src/gua/renderer/GeometryLoader.cpp index dde995892..30017f330 100644 --- a/src/gua/renderer/GeometryLoader.cpp +++ b/src/gua/renderer/GeometryLoader.cpp @@ -133,12 +133,13 @@ void GeometryLoader::apply_fallback_material(std::shared_ptr const& root, if (g_node) { if (g_node->data.get_material().empty()) { + std::cout << fallback_material << std::endl; g_node->data.set_material(fallback_material); } + } - for(auto& child: root->get_children()) { - apply_fallback_material(child, fallback_material); - } + for(auto& child: root->get_children()) { + apply_fallback_material(child, fallback_material); } } From ee121666792f28463c11211c022e043adbc4214d Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Wed, 30 Oct 2013 15:47:56 +0100 Subject: [PATCH 014/146] removed faulty output message --- src/gua/renderer/GeometryLoader.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/gua/renderer/GeometryLoader.cpp b/src/gua/renderer/GeometryLoader.cpp index 30017f330..15a84da51 100644 --- a/src/gua/renderer/GeometryLoader.cpp +++ b/src/gua/renderer/GeometryLoader.cpp @@ -133,7 +133,6 @@ void GeometryLoader::apply_fallback_material(std::shared_ptr const& root, if (g_node) { if (g_node->data.get_material().empty()) { - std::cout << fallback_material << std::endl; g_node->data.set_material(fallback_material); } } From 62007c867f23156900b974ea8e666a3912f3e724 Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Wed, 30 Oct 2013 16:05:16 +0100 Subject: [PATCH 015/146] add Texture3D --- include/gua/renderer/Uniform.hpp | 44 ++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/include/gua/renderer/Uniform.hpp b/include/gua/renderer/Uniform.hpp index c9587ec8a..4fd54e178 100644 --- a/include/gua/renderer/Uniform.hpp +++ b/include/gua/renderer/Uniform.hpp @@ -25,6 +25,7 @@ // guacamole headers #include #include +#include #include #include #include @@ -197,6 +198,49 @@ class UniformValue : public UniformValueBase { Texture2D* value_; }; +template <> +class UniformValue > : public UniformValueBase { + public: + UniformValue(std::shared_ptr const& value) + : UniformValueBase(), value_(value) {} + + void apply(RenderContext const& context, + scm::gl::program_ptr program, + std::string const& name, + unsigned position = 0) const { + + program->uniform(name, position, value_->get_handle(context)); + } + + std::shared_ptr const& value() const { return value_; } + + void value(std::shared_ptr const& value) { value_ = value; } + + private: + std::shared_ptr value_; +}; + +template <> +class UniformValue : public UniformValueBase { + public: + UniformValue(Texture3D* value) + : UniformValueBase(), value_(value) {} + + void apply(RenderContext const& context, + scm::gl::program_ptr program, + std::string const& name, + unsigned position = 0) const { + + program->uniform(name, position, value_->get_handle(context)); + } + + Texture3D* value() const { return value_; } + + void value(Texture3D* value) { value_ = value; } + + private: + Texture3D* value_; +}; template <> class UniformValue : public UniformValueBase { public: From 51d70d8b557abd438bd677633edaeeeac99c04e9 Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Wed, 30 Oct 2013 16:59:06 +0100 Subject: [PATCH 016/146] extend load function for sebastian volumes --- src/gua/databases/TextureDatabase.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/gua/databases/TextureDatabase.cpp b/src/gua/databases/TextureDatabase.cpp index ba7da7b1f..da11fe04b 100644 --- a/src/gua/databases/TextureDatabase.cpp +++ b/src/gua/databases/TextureDatabase.cpp @@ -22,6 +22,7 @@ // class header #include #include +#include // guacamole headers #include @@ -31,11 +32,21 @@ #include #include #include +#include namespace gua { void TextureDatabase::load(std::string const& id) { - instance()->add(id, std::make_shared(id, true)); + boost::filesystem::path fp(id); + std::string extension(fp.extension().string()); + boost::algorithm::to_lower(extension); + + if (extension == ".png" || extension == ".jpg" || extension == ".bmp" || + extension == ".tif" || extension == ".tga") { + instance()->add(id, std::make_shared(id, true)); + } else if (extension == ".vol") { + instance()->add(id, std::make_shared(id, true)); + } } } From 4bc856d47bd2ed3204bf901349f16c343de2af99 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Mon, 4 Nov 2013 10:30:00 +0100 Subject: [PATCH 017/146] added possibility to change fragment position in materials --- include/gua/renderer/Frustum.hpp | 4 ++++ resources/shaders/uber_shaders/gbuffer/mesh/mesh.frag | 8 ++++++++ src/gua/renderer/Frustum.cpp | 4 +++- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/include/gua/renderer/Frustum.hpp b/include/gua/renderer/Frustum.hpp index f8dced0ef..4dcfa39ad 100644 --- a/include/gua/renderer/Frustum.hpp +++ b/include/gua/renderer/Frustum.hpp @@ -47,6 +47,8 @@ class Frustum { } inline math::mat4 const& get_projection() const { return projection_; } inline math::mat4 const& get_view() const { return view_; } + inline float get_clip_near() const { return clip_near_; } + inline float get_clip_far() const { return clip_far_; } bool is_inside(math::BoundingBox const& bbox) const; @@ -56,6 +58,8 @@ class Frustum { math::mat4 projection_; math::mat4 view_; std::vector planes_; + float clip_near_; + float clip_far_; }; diff --git a/resources/shaders/uber_shaders/gbuffer/mesh/mesh.frag b/resources/shaders/uber_shaders/gbuffer/mesh/mesh.frag index 25feffa4e..219b1e706 100644 --- a/resources/shaders/uber_shaders/gbuffer/mesh/mesh.frag +++ b/resources/shaders/uber_shaders/gbuffer/mesh/mesh.frag @@ -51,12 +51,20 @@ vec3 gua_get_position() { return gua_position_varying; } +void gua_set_position(vec3 world_position) { + vec4 pos = gua_projection_matrix * gua_view_matrix * vec4(world_position, 1.0); + float ndc = pos.z/pos.w; + gl_FragDepth = (((gl_DepthRange.diff) * ndc) + gl_DepthRange.near + gl_DepthRange.far) / 2.0; +} + // material specific methods @material_methods // main ------------------------------------------------------------------------ void main() { + gl_FragDepth = gl_FragCoord.z; + // big switch, one case for each material @material_switch diff --git a/src/gua/renderer/Frustum.cpp b/src/gua/renderer/Frustum.cpp index 0b2585fef..25cd3eddb 100644 --- a/src/gua/renderer/Frustum.cpp +++ b/src/gua/renderer/Frustum.cpp @@ -33,7 +33,9 @@ Frustum::Frustum(math::mat4 const& camera_transform, : camera_position_(), projection_(math::mat4::identity()), view_(math::mat4::identity()), - planes_(6) { + planes_(6), + clip_near_(clip_near), + clip_far_(clip_far) { projection_ = math::compute_frustum( camera_transform.column(3), screen_transform, clip_near, clip_far); From 71ac3afe57ca4064fa95af6b0c440ef638ab65c1 Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Mon, 4 Nov 2013 13:13:49 +0100 Subject: [PATCH 018/146] remove -fpermissive --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ae4fb3f53..782f8d4d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -90,8 +90,8 @@ if (NOT CMAKE_BUILD_TYPE) endif() IF (UNIX) - set(CMAKE_CXX_FLAGS_RELEASE "-s -O4 --std=c++0x -fpermissive") - set(CMAKE_CXX_FLAGS_DEBUG "-g -Wall --std=c++0x -fpermissive") + set(CMAKE_CXX_FLAGS_RELEASE "-s -O4 --std=c++0x") + set(CMAKE_CXX_FLAGS_DEBUG "-g -Wall --std=c++0x") ELSEIF(MSVC) set(CMAKE_CXX_FLAGS_RELEASE "-D _SECURE_SCL=0 -D _SCL_SECURE_NO_WARNINGS -D _CRT_SECURE_NO_DEPRECATE /MD /MP") set(CMAKE_CXX_FLAGS_DEBUG "-D_DEBUG /MDd /Zi") From 7313c176dc2c89001bad9a66c09156449e458261 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Mon, 4 Nov 2013 17:20:59 +0100 Subject: [PATCH 019/146] added interpolated texture-coordinate picking --- include/gua/math/math.hpp | 21 ++++++++++++++++++++ src/gua/utils/KDTreeUtils.cpp | 36 +++++++++++++++++++++++++++-------- 2 files changed, 49 insertions(+), 8 deletions(-) diff --git a/include/gua/math/math.hpp b/include/gua/math/math.hpp index b9ca4c913..e5becc15e 100644 --- a/include/gua/math/math.hpp +++ b/include/gua/math/math.hpp @@ -97,6 +97,27 @@ inline math::vec3 get_translation(math::mat4 const& m) return math::vec3(m[12], m[13], m[14]); } + +template +ValueType interpolate(PosType const& position, + std::pair const& a, + std::pair const& b, + std::pair const& c) { + + // calculate vectors from position to vertices a, b and c: + auto f1 = a.first-position; + auto f2 = b.first-position; + auto f3 = c.first-position; + + // calculate the areas and factors (order of parameters doesn't matter): + auto area = scm::math::length(scm::math::cross(a.first-b.first, a.first-c.first)); + auto a1 = scm::math::length(scm::math::cross(f2, f3)) / area; + auto a2 = scm::math::length(scm::math::cross(f3, f1)) / area; + auto a3 = scm::math::length(scm::math::cross(f1, f2)) / area; + + return a.second * a1 + b.second * a2 + c.second * a3; +} + } } diff --git a/src/gua/utils/KDTreeUtils.cpp b/src/gua/utils/KDTreeUtils.cpp index 55d8afb26..91d0f060c 100644 --- a/src/gua/utils/KDTreeUtils.cpp +++ b/src/gua/utils/KDTreeUtils.cpp @@ -193,17 +193,37 @@ math::vec3 Triangle::get_normal_interpolated(aiMesh* mesh, math::vec3 const& pos } math::vec2 Triangle::get_texture_coords_interpolated(aiMesh* mesh, math::vec3 const& position) const { - /// TODO: actually interpolate - math::vec2 tex_coords; + + math::vec2 tex_coords(0, 0); + if (mesh->HasTextureCoords(0)) { - for (unsigned i = 0; i < 3; ++i) { - tex_coords += math::vec2( - mesh->mTextureCoords[0][mesh->mFaces[face_id_].mIndices[i]].x, - mesh->mTextureCoords[0][mesh->mFaces[face_id_].mIndices[i]].y); - } + + tex_coords = math::interpolate(position, + + std::make_pair( + math::vec3(mesh->mVertices[mesh->mFaces[face_id_].mIndices[0]].x, + mesh->mVertices[mesh->mFaces[face_id_].mIndices[0]].y, + mesh->mVertices[mesh->mFaces[face_id_].mIndices[0]].z), + math::vec2(mesh->mTextureCoords[0][mesh->mFaces[face_id_].mIndices[0]].x, + mesh->mTextureCoords[0][mesh->mFaces[face_id_].mIndices[0]].y)), + + std::make_pair( + math::vec3(mesh->mVertices[mesh->mFaces[face_id_].mIndices[1]].x, + mesh->mVertices[mesh->mFaces[face_id_].mIndices[1]].y, + mesh->mVertices[mesh->mFaces[face_id_].mIndices[1]].z), + math::vec2(mesh->mTextureCoords[0][mesh->mFaces[face_id_].mIndices[1]].x, + mesh->mTextureCoords[0][mesh->mFaces[face_id_].mIndices[1]].y)), + + std::make_pair( + math::vec3(mesh->mVertices[mesh->mFaces[face_id_].mIndices[2]].x, + mesh->mVertices[mesh->mFaces[face_id_].mIndices[2]].y, + mesh->mVertices[mesh->mFaces[face_id_].mIndices[2]].z), + math::vec2(mesh->mTextureCoords[0][mesh->mFaces[face_id_].mIndices[2]].x, + mesh->mTextureCoords[0][mesh->mFaces[face_id_].mIndices[2]].y)) + ); } - return tex_coords/3; + return tex_coords; } From a73000e0172e9da384591cf77f6539b7306cd12c Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Tue, 5 Nov 2013 10:09:31 +0100 Subject: [PATCH 020/146] Pickresult's normals are now really interpolated if requested --- include/gua/math/math.hpp | 6 +- src/gua/scenegraph/GeometryNode.cpp | 232 ++++++++++++++-------------- src/gua/utils/KDTree.cpp | 13 +- src/gua/utils/KDTreeUtils.cpp | 66 ++++++-- 4 files changed, 182 insertions(+), 135 deletions(-) diff --git a/include/gua/math/math.hpp b/include/gua/math/math.hpp index e5becc15e..13aa46cf3 100644 --- a/include/gua/math/math.hpp +++ b/include/gua/math/math.hpp @@ -24,6 +24,7 @@ #include #include +#include #if ASSIMP_VERSION == 3 #include @@ -92,8 +93,7 @@ math::mat4 const mat_ai_to_scm(aiMatrix4x4 const& ai_mat); } #endif -inline math::vec3 get_translation(math::mat4 const& m) -{ +inline math::vec3 get_translation(math::mat4 const& m) { return math::vec3(m[12], m[13], m[14]); } @@ -104,6 +104,8 @@ ValueType interpolate(PosType const& position, std::pair const& b, std::pair const& c) { + // TODO: Is there a more efficient way to interpolate? + // calculate vectors from position to vertices a, b and c: auto f1 = a.first-position; auto f2 = b.first-position; diff --git a/src/gua/scenegraph/GeometryNode.cpp b/src/gua/scenegraph/GeometryNode.cpp index 1b1acffed..c709977ce 100644 --- a/src/gua/scenegraph/GeometryNode.cpp +++ b/src/gua/scenegraph/GeometryNode.cpp @@ -39,151 +39,151 @@ GeometryNode::GeometryNode(std::string const& name, /* virtual */ void GeometryNode::accept(NodeVisitor& visitor) { - visitor.visit(this); + visitor.visit(this); } void GeometryNode::update_bounding_box() const { - if (data.get_geometry() != "") { - auto geometry_bbox(GeometryDatabase::instance()->lookup(data.get_geometry())->get_bounding_box()); - bounding_box_ = transform(geometry_bbox, world_transform_); + if (data.get_geometry() != "") { + auto geometry_bbox(GeometryDatabase::instance()->lookup(data.get_geometry())->get_bounding_box()); + bounding_box_ = transform(geometry_bbox, world_transform_); - for (auto child : get_children()) { - bounding_box_.expandBy(child->get_bounding_box()); - } - } - else { - Node::update_bounding_box(); + for (auto child : get_children()) { + bounding_box_.expandBy(child->get_bounding_box()); } + } + else { + Node::update_bounding_box(); + } } void GeometryNode::ray_test_impl(RayNode const& ray, PickResult::Options options, Mask const& mask, std::set& hits) { - // first of all, check bbox - auto box_hits(ray.intersect(bounding_box_)); - - // ray did not intersect bbox -- therefore it wont intersect - if (box_hits.first == RayNode::END && box_hits.second == RayNode::END) { - return; - } - - // return if only first object shall be returned and the current first hit - // is in front of the bbox entry point and the ray does not start inside - // the bbox - if (options & PickResult::PICK_ONLY_FIRST_OBJECT - && hits.size() > 0 && hits.begin()->distance < box_hits.first - && box_hits.first != Ray::END) { - - return; - } - - // bbox is intersected, but check geometry only if mask tells us to check - if (data.get_geometry() != "" && mask.check(get_groups())) { - - auto geometry(GeometryDatabase::instance()->lookup(data.get_geometry())); - - if (geometry) { - - bool check_kd_tree(true); - - math::mat4 world_transform(get_world_transform()); - - // check for bounding box intersection of contained geometry if node - // has children (in this case, the bbox might be larger - // than the actual geometry) - if (has_children()) { - auto geometry_bbox(geometry->get_bounding_box()); - - math::BoundingBox inner_bbox; - inner_bbox.expandBy(world_transform * geometry_bbox.min); - inner_bbox.expandBy(world_transform * geometry_bbox.max); - inner_bbox.expandBy(world_transform * - math::vec3(geometry_bbox.min.x, - geometry_bbox.min.y, - geometry_bbox.max.z)); - inner_bbox.expandBy(world_transform * - math::vec3(geometry_bbox.min.x, - geometry_bbox.max.y, - geometry_bbox.min.z)); - inner_bbox.expandBy(world_transform * - math::vec3(geometry_bbox.min.x, - geometry_bbox.max.y, - geometry_bbox.max.z)); - inner_bbox.expandBy(world_transform * - math::vec3(geometry_bbox.max.x, - geometry_bbox.min.y, - geometry_bbox.max.z)); - inner_bbox.expandBy(world_transform * - math::vec3(geometry_bbox.max.x, - geometry_bbox.max.y, - geometry_bbox.min.z)); - inner_bbox.expandBy(world_transform * - math::vec3(geometry_bbox.max.x, - geometry_bbox.min.y, - geometry_bbox.min.z)); - - auto inner_hits(ray.intersect(inner_bbox)); - if (inner_hits.first == RayNode::END && - inner_hits.second == RayNode::END) - check_kd_tree = false; - } + // first of all, check bbox + auto box_hits(ray.intersect(bounding_box_)); + + // ray did not intersect bbox -- therefore it wont intersect + if (box_hits.first == RayNode::END && box_hits.second == RayNode::END) { + return; + } + + // return if only first object shall be returned and the current first hit + // is in front of the bbox entry point and the ray does not start inside + // the bbox + if (options & PickResult::PICK_ONLY_FIRST_OBJECT + && hits.size() > 0 && hits.begin()->distance < box_hits.first + && box_hits.first != Ray::END) { + + return; + } + + // bbox is intersected, but check geometry only if mask tells us to check + if (data.get_geometry() != "" && mask.check(get_groups())) { + + auto geometry(GeometryDatabase::instance()->lookup(data.get_geometry())); + + if (geometry) { + + bool check_kd_tree(true); + + math::mat4 world_transform(get_world_transform()); + + // check for bounding box intersection of contained geometry if node + // has children (in this case, the bbox might be larger + // than the actual geometry) + if (has_children()) { + auto geometry_bbox(geometry->get_bounding_box()); + + math::BoundingBox inner_bbox; + inner_bbox.expandBy(world_transform * geometry_bbox.min); + inner_bbox.expandBy(world_transform * geometry_bbox.max); + inner_bbox.expandBy(world_transform * + math::vec3(geometry_bbox.min.x, + geometry_bbox.min.y, + geometry_bbox.max.z)); + inner_bbox.expandBy(world_transform * + math::vec3(geometry_bbox.min.x, + geometry_bbox.max.y, + geometry_bbox.min.z)); + inner_bbox.expandBy(world_transform * + math::vec3(geometry_bbox.min.x, + geometry_bbox.max.y, + geometry_bbox.max.z)); + inner_bbox.expandBy(world_transform * + math::vec3(geometry_bbox.max.x, + geometry_bbox.min.y, + geometry_bbox.max.z)); + inner_bbox.expandBy(world_transform * + math::vec3(geometry_bbox.max.x, + geometry_bbox.max.y, + geometry_bbox.min.z)); + inner_bbox.expandBy(world_transform * + math::vec3(geometry_bbox.max.x, + geometry_bbox.min.y, + geometry_bbox.min.z)); + + auto inner_hits(ray.intersect(inner_bbox)); + if (inner_hits.first == RayNode::END && + inner_hits.second == RayNode::END) + check_kd_tree = false; + } - if (check_kd_tree) { - Ray world_ray(ray.get_world_ray()); + if (check_kd_tree) { + Ray world_ray(ray.get_world_ray()); - math::mat4 ori_transform(scm::math::inverse(world_transform)); + math::mat4 ori_transform(scm::math::inverse(world_transform)); - math::vec4 ori(world_ray.origin_[0], - world_ray.origin_[1], - world_ray.origin_[2], - 1.0); - math::vec4 dir(world_ray.direction_[0], - world_ray.direction_[1], - world_ray.direction_[2], - 0.0); + math::vec4 ori(world_ray.origin_[0], + world_ray.origin_[1], + world_ray.origin_[2], + 1.0); + math::vec4 dir(world_ray.direction_[0], + world_ray.direction_[1], + world_ray.direction_[2], + 0.0); - ori = ori_transform * ori; - dir = ori_transform * dir; + ori = ori_transform * ori; + dir = ori_transform * dir; - Ray object_ray(ori, dir, world_ray.t_max_); - geometry->ray_test(object_ray, options, this, hits); + Ray object_ray(ori, dir, world_ray.t_max_); + geometry->ray_test(object_ray, options, this, hits); - float const inf(std::numeric_limits::max()); + float const inf(std::numeric_limits::max()); - if (options & PickResult::GET_WORLD_POSITIONS) { + if (options & PickResult::GET_WORLD_POSITIONS) { - for (auto& hit: hits) { - if (hit.world_position == math::vec3(inf, inf, inf)) { - auto transformed(world_transform * math::vec4(hit.position.x, hit.position.y, hit.position.z, 0.0)); - hit.world_position = scm::math::vec3(transformed.x, transformed.y, transformed.z); - } - } - } + for (auto& hit: hits) { + if (hit.world_position == math::vec3(inf, inf, inf)) { + auto transformed(world_transform * math::vec4(hit.position.x, hit.position.y, hit.position.z, 0.0)); + hit.world_position = scm::math::vec3(transformed.x, transformed.y, transformed.z); + } + } + } - if (options & PickResult::GET_WORLD_NORMALS) { + if (options & PickResult::GET_WORLD_NORMALS) { - math::mat4 normal_matrix(scm::math::inverse(scm::math::transpose(world_transform))); - for (auto& hit: hits) { - if (hit.world_normal == math::vec3(inf, inf, inf)) { - auto transformed(normal_matrix * math::vec4(hit.normal.x, hit.normal.y, hit.normal.z, 0.0)); - hit.world_normal = scm::math::normalize(scm::math::vec3(transformed.x, transformed.y, transformed.z)); - } - } - } + math::mat4 normal_matrix(scm::math::inverse(scm::math::transpose(world_transform))); + for (auto& hit: hits) { + if (hit.world_normal == math::vec3(inf, inf, inf)) { + auto transformed(normal_matrix * math::vec4(hit.normal.x, hit.normal.y, hit.normal.z, 0.0)); + hit.world_normal = scm::math::normalize(scm::math::vec3(transformed.x, transformed.y, transformed.z)); + } } + } } } + } - for (auto child : get_children()) { - // test for intersection with each child - child->ray_test_impl(ray, options, mask, hits); - } + for (auto child : get_children()) { + // test for intersection with each child + child->ray_test_impl(ray, options, mask, hits); + } } std::shared_ptr GeometryNode::copy() const { - return std::make_shared(get_name(), data, get_transform()); + return std::make_shared(get_name(), data, get_transform()); } } diff --git a/src/gua/utils/KDTree.cpp b/src/gua/utils/KDTree.cpp index 56311b327..926cde07b 100644 --- a/src/gua/utils/KDTree.cpp +++ b/src/gua/utils/KDTree.cpp @@ -340,16 +340,21 @@ void KDTree::intersect_all(KDNode* node, if (intersection < Ray::END) { float const inf(std::numeric_limits::max()); - math::vec3 position(inf, inf, inf), normal(inf, inf, inf); + math::vec3 position(inf, inf, inf), + world_position(inf, inf, inf), + normal(inf, inf, inf), + world_normal(inf, inf, inf); math::vec2 tex_coords; if (options & PickResult::GET_POSITIONS + || options & PickResult::GET_WORLD_POSITIONS || options & PickResult::INTERPOLATE_NORMALS || options & PickResult::GET_TEXTURE_COORDS) { position = ray.origin_ + intersection * ray.direction_; } - if (options & PickResult::GET_NORMALS) { + if (options & PickResult::GET_NORMALS + || options & PickResult::GET_WORLD_NORMALS) { if (options & PickResult::INTERPOLATE_NORMALS) { normal = triangles[triangle.id_].get_normal_interpolated(mesh, position); @@ -364,8 +369,8 @@ void KDTree::intersect_all(KDNode* node, } hits.insert(PickResult(intersection, current_owner_, - position, position, - normal, normal, + position, world_position, + normal, world_normal, tex_coords)); } diff --git a/src/gua/utils/KDTreeUtils.cpp b/src/gua/utils/KDTreeUtils.cpp index 91d0f060c..78a0f9486 100644 --- a/src/gua/utils/KDTreeUtils.cpp +++ b/src/gua/utils/KDTreeUtils.cpp @@ -174,7 +174,9 @@ math::vec3 Triangle::get_vertex(aiMesh* mesh, unsigned vertex_id) const { } math::vec3 Triangle::get_normal(aiMesh* mesh) const { - math::vec3 normal; + + math::vec3 normal(0, 0, 0); + if (mesh->HasNormals()) { for (unsigned i = 0; i < 3; ++i) { normal += math::vec3( @@ -182,14 +184,52 @@ math::vec3 Triangle::get_normal(aiMesh* mesh) const { mesh->mNormals[mesh->mFaces[face_id_].mIndices[i]].y, mesh->mNormals[mesh->mFaces[face_id_].mIndices[i]].z); } + + normal = scm::math::normalize(normal); } - return normal/3; + return normal; } math::vec3 Triangle::get_normal_interpolated(aiMesh* mesh, math::vec3 const& position) const { - /// TODO: actually interpolate - return get_normal(mesh); + + math::vec3 normal(0, 0, 0); + + + if (mesh->HasNormals()) { + + normal = math::interpolate(position, + + std::make_pair( + math::vec3(mesh->mVertices[mesh->mFaces[face_id_].mIndices[0]].x, + mesh->mVertices[mesh->mFaces[face_id_].mIndices[0]].y, + mesh->mVertices[mesh->mFaces[face_id_].mIndices[0]].z), + math::vec3(mesh->mNormals[mesh->mFaces[face_id_].mIndices[0]].x, + mesh->mNormals[mesh->mFaces[face_id_].mIndices[0]].y, + mesh->mNormals[mesh->mFaces[face_id_].mIndices[0]].z)), + + std::make_pair( + math::vec3(mesh->mVertices[mesh->mFaces[face_id_].mIndices[1]].x, + mesh->mVertices[mesh->mFaces[face_id_].mIndices[1]].y, + mesh->mVertices[mesh->mFaces[face_id_].mIndices[1]].z), + math::vec3(mesh->mNormals[mesh->mFaces[face_id_].mIndices[1]].x, + mesh->mNormals[mesh->mFaces[face_id_].mIndices[1]].y, + mesh->mNormals[mesh->mFaces[face_id_].mIndices[1]].z)), + + std::make_pair( + math::vec3(mesh->mVertices[mesh->mFaces[face_id_].mIndices[2]].x, + mesh->mVertices[mesh->mFaces[face_id_].mIndices[2]].y, + mesh->mVertices[mesh->mFaces[face_id_].mIndices[2]].z), + math::vec3(mesh->mNormals[mesh->mFaces[face_id_].mIndices[2]].x, + mesh->mNormals[mesh->mFaces[face_id_].mIndices[2]].y, + mesh->mNormals[mesh->mFaces[face_id_].mIndices[2]].z)) + ); + + normal = scm::math::normalize(normal); + } + + return normal; + } math::vec2 Triangle::get_texture_coords_interpolated(aiMesh* mesh, math::vec3 const& position) const { @@ -202,24 +242,24 @@ math::vec2 Triangle::get_texture_coords_interpolated(aiMesh* mesh, math::vec3 co std::make_pair( math::vec3(mesh->mVertices[mesh->mFaces[face_id_].mIndices[0]].x, - mesh->mVertices[mesh->mFaces[face_id_].mIndices[0]].y, - mesh->mVertices[mesh->mFaces[face_id_].mIndices[0]].z), + mesh->mVertices[mesh->mFaces[face_id_].mIndices[0]].y, + mesh->mVertices[mesh->mFaces[face_id_].mIndices[0]].z), math::vec2(mesh->mTextureCoords[0][mesh->mFaces[face_id_].mIndices[0]].x, - mesh->mTextureCoords[0][mesh->mFaces[face_id_].mIndices[0]].y)), + mesh->mTextureCoords[0][mesh->mFaces[face_id_].mIndices[0]].y)), std::make_pair( math::vec3(mesh->mVertices[mesh->mFaces[face_id_].mIndices[1]].x, - mesh->mVertices[mesh->mFaces[face_id_].mIndices[1]].y, - mesh->mVertices[mesh->mFaces[face_id_].mIndices[1]].z), + mesh->mVertices[mesh->mFaces[face_id_].mIndices[1]].y, + mesh->mVertices[mesh->mFaces[face_id_].mIndices[1]].z), math::vec2(mesh->mTextureCoords[0][mesh->mFaces[face_id_].mIndices[1]].x, - mesh->mTextureCoords[0][mesh->mFaces[face_id_].mIndices[1]].y)), + mesh->mTextureCoords[0][mesh->mFaces[face_id_].mIndices[1]].y)), std::make_pair( math::vec3(mesh->mVertices[mesh->mFaces[face_id_].mIndices[2]].x, - mesh->mVertices[mesh->mFaces[face_id_].mIndices[2]].y, - mesh->mVertices[mesh->mFaces[face_id_].mIndices[2]].z), + mesh->mVertices[mesh->mFaces[face_id_].mIndices[2]].y, + mesh->mVertices[mesh->mFaces[face_id_].mIndices[2]].z), math::vec2(mesh->mTextureCoords[0][mesh->mFaces[face_id_].mIndices[2]].x, - mesh->mTextureCoords[0][mesh->mFaces[face_id_].mIndices[2]].y)) + mesh->mTextureCoords[0][mesh->mFaces[face_id_].mIndices[2]].y)) ); } From 39aa50d9d414eaa843bbb3291b31a7d1fdffd5bb Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Tue, 5 Nov 2013 17:56:58 +0100 Subject: [PATCH 021/146] change intersect to a free function --- include/gua/utils/KDTreeUtils.hpp | 5 +++-- src/gua/scenegraph/RayNode.cpp | 3 +-- src/gua/utils/KDTreeUtils.cpp | 18 +++++++++--------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/include/gua/utils/KDTreeUtils.hpp b/include/gua/utils/KDTreeUtils.hpp index 008af0b34..81fa1a531 100644 --- a/include/gua/utils/KDTreeUtils.hpp +++ b/include/gua/utils/KDTreeUtils.hpp @@ -41,8 +41,6 @@ struct Ray { Ray(); Ray(math::vec3 const& origin, math::vec3 const& direction, float t_max); - std::pair intersect( - math::BoundingBox const& box) const; Ray const intersection(math::BoundingBox const& box) const; math::vec3 origin_; @@ -52,6 +50,9 @@ struct Ray { static const float END; }; +std::pair intersect(Ray const& ray, + math::BoundingBox const& box); + /** * This helper class represents a triangle. * diff --git a/src/gua/scenegraph/RayNode.cpp b/src/gua/scenegraph/RayNode.cpp index cc892c32b..e1b5fac2f 100644 --- a/src/gua/scenegraph/RayNode.cpp +++ b/src/gua/scenegraph/RayNode.cpp @@ -43,8 +43,7 @@ RayNode::RayNode(std::string const& name, math::mat4 const& transform) std::pair RayNode::intersect( math::BoundingBox const& box) const { - - return get_world_ray().intersect(box); + return ::gua::intersect(get_world_ray(),box); } Ray const RayNode::get_world_ray() const { diff --git a/src/gua/utils/KDTreeUtils.cpp b/src/gua/utils/KDTreeUtils.cpp index 78a0f9486..e5380acbf 100644 --- a/src/gua/utils/KDTreeUtils.cpp +++ b/src/gua/utils/KDTreeUtils.cpp @@ -45,11 +45,11 @@ Ray::Ray() : origin_(), direction_(), t_max_(-1.f) {} Ray::Ray(math::vec3 const& origin, math::vec3 const& direction, float t_max) : origin_(origin), direction_(direction), t_max_(t_max) {} -std::pair Ray::intersect( - math::BoundingBox const& box) const { +std::pair intersect(Ray const& ray, + math::BoundingBox const& box) { - math::vec3 t1((box.min - origin_) / direction_); - math::vec3 t2((box.max - origin_) / direction_); + math::vec3 t1((box.min - ray.origin_) / ray.direction_); + math::vec3 t2((box.max - ray.origin_) / ray.direction_); math::vec3 tmin1( std::min(t1[0], t2[0]), std::min(t1[1], t2[1]), std::min(t1[2], t2[2])); @@ -61,25 +61,25 @@ std::pair Ray::intersect( if (tmax >= tmin) { // there are two intersections - if (tmin > 0.0 && tmax < t_max_) + if (tmin > 0.0 && tmax < ray.t_max_) return std::make_pair(tmin, tmax); // there is only one intersection, the ray ends inside the box else if (tmin > 0.0) - return std::make_pair(tmin, END); + return std::make_pair(tmin, Ray::END); // there is only one intersection, the ray starts inside the box else - return std::make_pair(END, tmax); + return std::make_pair(Ray::END, tmax); } // there is no intersection - return std::make_pair(END, END); + return std::make_pair(Ray::END, Ray::END); } Ray const Ray::intersection(math::BoundingBox const& box) const { - auto hits(intersect(box)); + auto hits(intersect(*this,box)); // there are to hits -> clamp ray on both sides if (hits.first != END && hits.first != END) From a02eb89b689e3f0b6e160b14a7b2d5f9c951931e Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Wed, 6 Nov 2013 15:41:11 +0100 Subject: [PATCH 022/146] add function to calculate barycentric coordinates --- include/gua/math/math.hpp | 2 ++ src/gua/math/math.cpp | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/include/gua/math/math.hpp b/include/gua/math/math.hpp index 13aa46cf3..fe1c72cca 100644 --- a/include/gua/math/math.hpp +++ b/include/gua/math/math.hpp @@ -25,6 +25,7 @@ #include #include #include +#include #if ASSIMP_VERSION == 3 #include @@ -97,6 +98,7 @@ inline math::vec3 get_translation(math::mat4 const& m) { return math::vec3(m[12], m[13], m[14]); } +std::tuple barycentric(math::vec3 const& a, math::vec3 const& b, math::vec3 const& c, math::vec3 const& p); template ValueType interpolate(PosType const& position, diff --git a/src/gua/math/math.cpp b/src/gua/math/math.cpp index be46db045..aece811d7 100644 --- a/src/gua/math/math.cpp +++ b/src/gua/math/math.cpp @@ -78,4 +78,20 @@ math::mat4 const math::mat_ai_to_scm(aiMatrix4x4 const& ai_mat) { //////////////////////////////////////////////////////////////////////////////// +std::tuple barycentric(math::vec3 const& a, + math::vec3 const& b, + math::vec3 const& c, + math::vec3 const& p) { + auto ap = a-p; + auto bp = b-p; + auto cp = c-p; + + auto area = scm::math::length(scm::math::cross(a-b, a-c)); + auto a1 = scm::math::length(scm::math::cross(bp, cp)) / area; + auto a2 = scm::math::length(scm::math::cross(cp, ap)) / area; + auto a3 = 1.0 - a1 - a2; + + return std::make_tuple(a1,a2,a3); +} + } From 8b83fbcf2b77db6b38b8a1c72888ecf461cf3dad Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Wed, 6 Nov 2013 15:41:44 +0100 Subject: [PATCH 023/146] use barycentric function for interpolation --- include/gua/math/math.hpp | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/include/gua/math/math.hpp b/include/gua/math/math.hpp index fe1c72cca..99f8e3583 100644 --- a/include/gua/math/math.hpp +++ b/include/gua/math/math.hpp @@ -100,26 +100,14 @@ inline math::vec3 get_translation(math::mat4 const& m) { std::tuple barycentric(math::vec3 const& a, math::vec3 const& b, math::vec3 const& c, math::vec3 const& p); -template -ValueType interpolate(PosType const& position, - std::pair const& a, - std::pair const& b, - std::pair const& c) { - - // TODO: Is there a more efficient way to interpolate? - - // calculate vectors from position to vertices a, b and c: - auto f1 = a.first-position; - auto f2 = b.first-position; - auto f3 = c.first-position; - - // calculate the areas and factors (order of parameters doesn't matter): - auto area = scm::math::length(scm::math::cross(a.first-b.first, a.first-c.first)); - auto a1 = scm::math::length(scm::math::cross(f2, f3)) / area; - auto a2 = scm::math::length(scm::math::cross(f3, f1)) / area; - auto a3 = scm::math::length(scm::math::cross(f1, f2)) / area; - - return a.second * a1 + b.second * a2 + c.second * a3; +template +ValueType interpolate(math::vec3 const& position, + std::pair const& a, + std::pair const& b, + std::pair const& c) { + float u, v, w; + std::tie(u,v,w) = barycentric(a.first,b.first,c.first,position); + return u * a.second + v * b.second + w * c.second; } } From 12586074038b6b6cdaf75c14bd4820c2eaba7ae0 Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Wed, 6 Nov 2013 15:42:18 +0100 Subject: [PATCH 024/146] indentation --- include/gua/math/math.hpp | 5 ++++- src/gua/utils/KDTree.cpp | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/include/gua/math/math.hpp b/include/gua/math/math.hpp index 99f8e3583..145f5a0b6 100644 --- a/include/gua/math/math.hpp +++ b/include/gua/math/math.hpp @@ -98,7 +98,10 @@ inline math::vec3 get_translation(math::mat4 const& m) { return math::vec3(m[12], m[13], m[14]); } -std::tuple barycentric(math::vec3 const& a, math::vec3 const& b, math::vec3 const& c, math::vec3 const& p); +std::tuple barycentric(math::vec3 const& a, + math::vec3 const& b, + math::vec3 const& c, + math::vec3 const& p); template ValueType interpolate(math::vec3 const& position, diff --git a/src/gua/utils/KDTree.cpp b/src/gua/utils/KDTree.cpp index 926cde07b..7525afcd8 100644 --- a/src/gua/utils/KDTree.cpp +++ b/src/gua/utils/KDTree.cpp @@ -429,10 +429,10 @@ KDTree::LeafData::LeafData() : id_(-1), bbox_() {} KDTree::LeafData::LeafData(aiMesh* mesh, Triangle const & triangle, unsigned id) : id_(id), bbox_() { - for (auto i(0); i < 3; ++i) { - bbox_.expandBy(triangle.get_vertex(mesh, i)); - } + for (auto i(0); i < 3; ++i) { + bbox_.expandBy(triangle.get_vertex(mesh, i)); } +} KDTree::LeafData::LeafData(math::BoundingBox const & bbox, unsigned id) From 21fcad2bf1396b8eee80fc9432ce6693fcb0fcd3 Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Wed, 6 Nov 2013 15:47:30 +0100 Subject: [PATCH 025/146] fix naming --- src/gua/math/math.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/gua/math/math.cpp b/src/gua/math/math.cpp index aece811d7..ca537e893 100644 --- a/src/gua/math/math.cpp +++ b/src/gua/math/math.cpp @@ -76,19 +76,17 @@ math::mat4 const math::mat_ai_to_scm(aiMatrix4x4 const& ai_mat) { return scm_mat; } -//////////////////////////////////////////////////////////////////////////////// - std::tuple barycentric(math::vec3 const& a, math::vec3 const& b, math::vec3 const& c, math::vec3 const& p) { - auto ap = a-p; - auto bp = b-p; - auto cp = c-p; + auto pa = a-p; + auto pb = b-p; + auto pc = c-p; - auto area = scm::math::length(scm::math::cross(a-b, a-c)); - auto a1 = scm::math::length(scm::math::cross(bp, cp)) / area; - auto a2 = scm::math::length(scm::math::cross(cp, ap)) / area; + auto inv2area = 1.0 / scm::math::length(scm::math::cross(a-b, a-c)); + auto a1 = inv2area * scm::math::length(scm::math::cross(pb, pc)); + auto a2 = inv2area * scm::math::length(scm::math::cross(pc, pa)); auto a3 = 1.0 - a1 - a2; return std::make_tuple(a1,a2,a3); From 156579f79cd2f30a6e26968a5d3e71ae30ccd552 Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Wed, 6 Nov 2013 16:01:36 +0100 Subject: [PATCH 026/146] use get_vertex --- src/gua/utils/KDTreeUtils.cpp | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/src/gua/utils/KDTreeUtils.cpp b/src/gua/utils/KDTreeUtils.cpp index e5380acbf..7ecbe5bba 100644 --- a/src/gua/utils/KDTreeUtils.cpp +++ b/src/gua/utils/KDTreeUtils.cpp @@ -201,25 +201,19 @@ math::vec3 Triangle::get_normal_interpolated(aiMesh* mesh, math::vec3 const& pos normal = math::interpolate(position, std::make_pair( - math::vec3(mesh->mVertices[mesh->mFaces[face_id_].mIndices[0]].x, - mesh->mVertices[mesh->mFaces[face_id_].mIndices[0]].y, - mesh->mVertices[mesh->mFaces[face_id_].mIndices[0]].z), + get_vertex(mesh,0), math::vec3(mesh->mNormals[mesh->mFaces[face_id_].mIndices[0]].x, mesh->mNormals[mesh->mFaces[face_id_].mIndices[0]].y, mesh->mNormals[mesh->mFaces[face_id_].mIndices[0]].z)), std::make_pair( - math::vec3(mesh->mVertices[mesh->mFaces[face_id_].mIndices[1]].x, - mesh->mVertices[mesh->mFaces[face_id_].mIndices[1]].y, - mesh->mVertices[mesh->mFaces[face_id_].mIndices[1]].z), + get_vertex(mesh,1), math::vec3(mesh->mNormals[mesh->mFaces[face_id_].mIndices[1]].x, mesh->mNormals[mesh->mFaces[face_id_].mIndices[1]].y, mesh->mNormals[mesh->mFaces[face_id_].mIndices[1]].z)), std::make_pair( - math::vec3(mesh->mVertices[mesh->mFaces[face_id_].mIndices[2]].x, - mesh->mVertices[mesh->mFaces[face_id_].mIndices[2]].y, - mesh->mVertices[mesh->mFaces[face_id_].mIndices[2]].z), + get_vertex(mesh,2), math::vec3(mesh->mNormals[mesh->mFaces[face_id_].mIndices[2]].x, mesh->mNormals[mesh->mFaces[face_id_].mIndices[2]].y, mesh->mNormals[mesh->mFaces[face_id_].mIndices[2]].z)) @@ -241,23 +235,17 @@ math::vec2 Triangle::get_texture_coords_interpolated(aiMesh* mesh, math::vec3 co tex_coords = math::interpolate(position, std::make_pair( - math::vec3(mesh->mVertices[mesh->mFaces[face_id_].mIndices[0]].x, - mesh->mVertices[mesh->mFaces[face_id_].mIndices[0]].y, - mesh->mVertices[mesh->mFaces[face_id_].mIndices[0]].z), + get_vertex(mesh,0), math::vec2(mesh->mTextureCoords[0][mesh->mFaces[face_id_].mIndices[0]].x, mesh->mTextureCoords[0][mesh->mFaces[face_id_].mIndices[0]].y)), std::make_pair( - math::vec3(mesh->mVertices[mesh->mFaces[face_id_].mIndices[1]].x, - mesh->mVertices[mesh->mFaces[face_id_].mIndices[1]].y, - mesh->mVertices[mesh->mFaces[face_id_].mIndices[1]].z), + get_vertex(mesh,1), math::vec2(mesh->mTextureCoords[0][mesh->mFaces[face_id_].mIndices[1]].x, mesh->mTextureCoords[0][mesh->mFaces[face_id_].mIndices[1]].y)), std::make_pair( - math::vec3(mesh->mVertices[mesh->mFaces[face_id_].mIndices[2]].x, - mesh->mVertices[mesh->mFaces[face_id_].mIndices[2]].y, - mesh->mVertices[mesh->mFaces[face_id_].mIndices[2]].z), + get_vertex(mesh,2), math::vec2(mesh->mTextureCoords[0][mesh->mFaces[face_id_].mIndices[2]].x, mesh->mTextureCoords[0][mesh->mFaces[face_id_].mIndices[2]].y)) ); From 00dc5429df3d5ee10c502bf9236840c3a7770c57 Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Wed, 6 Nov 2013 16:20:39 +0100 Subject: [PATCH 027/146] add helper functions for aiMesh access --- src/gua/utils/KDTreeUtils.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/gua/utils/KDTreeUtils.cpp b/src/gua/utils/KDTreeUtils.cpp index 7ecbe5bba..03f902a8c 100644 --- a/src/gua/utils/KDTreeUtils.cpp +++ b/src/gua/utils/KDTreeUtils.cpp @@ -36,6 +36,30 @@ namespace gua { +namespace { + +// helper function to access mesh attributes +// no bounds checking! +// local to this translation unit +inline math::vec3 aiMesh_vertex(aiMesh* mesh, unsigned face, unsigned i) { + return math::vec3(mesh->mVertices[mesh->mFaces[face].mIndices[i]].x, + mesh->mVertices[mesh->mFaces[face].mIndices[i]].y, + mesh->mVertices[mesh->mFaces[face].mIndices[i]].z); +} + +inline math::vec3 aiMesh_normal(aiMesh* mesh, unsigned face, unsigned i) { + return math::vec3(mesh->mNormals[mesh->mFaces[face].mIndices[i]].x, + mesh->mNormals[mesh->mFaces[face].mIndices[i]].y, + mesh->mNormals[mesh->mFaces[face].mIndices[i]].z); +} + +inline math::vec2 aiMesh_texcoord(aiMesh* mesh, unsigned face, unsigned i, unsigned j) { + return math::vec2(mesh->mTextureCoords[i][mesh->mFaces[face].mIndices[j]].x, + mesh->mTextureCoords[i][mesh->mFaces[face].mIndices[j]].y); +} + +} + // Ray ------------------------------------------------------------------------- const float Ray::END(std::numeric_limits::max()); From 2a9f2ccaf4c010c4c31a57c4d1a2c8e30686fad4 Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Wed, 6 Nov 2013 16:21:57 +0100 Subject: [PATCH 028/146] simplify code by using inlined aiMesh accessors --- src/gua/utils/KDTreeUtils.cpp | 55 +++++------------------------------ 1 file changed, 8 insertions(+), 47 deletions(-) diff --git a/src/gua/utils/KDTreeUtils.cpp b/src/gua/utils/KDTreeUtils.cpp index 03f902a8c..1c2e25731 100644 --- a/src/gua/utils/KDTreeUtils.cpp +++ b/src/gua/utils/KDTreeUtils.cpp @@ -135,7 +135,7 @@ float Triangle::intersect(aiMesh* mesh, Ray const& ray) const { std::vector points(3); // math::vec3 normal(0, 0, 0); for (unsigned i = 0; i < 3; ++i) { - points[i] = get_vertex(mesh, i); + points[i] = aiMesh_vertex(mesh, face_id_, i); } // Find Triangle Normal @@ -188,10 +188,7 @@ float Triangle::intersect(aiMesh* mesh, Ray const& ray) const { math::vec3 Triangle::get_vertex(aiMesh* mesh, unsigned vertex_id) const { math::vec3 vertex; if (vertex_id < 3) { - vertex = math::vec3( - mesh->mVertices[mesh->mFaces[face_id_].mIndices[vertex_id]].x, - mesh->mVertices[mesh->mFaces[face_id_].mIndices[vertex_id]].y, - mesh->mVertices[mesh->mFaces[face_id_].mIndices[vertex_id]].z); + vertex = aiMesh_vertex(mesh,face_id_,vertex_id); } return vertex; @@ -216,67 +213,31 @@ math::vec3 Triangle::get_normal(aiMesh* mesh) const { } math::vec3 Triangle::get_normal_interpolated(aiMesh* mesh, math::vec3 const& position) const { - math::vec3 normal(0, 0, 0); - if (mesh->HasNormals()) { - normal = math::interpolate(position, - - std::make_pair( - get_vertex(mesh,0), - math::vec3(mesh->mNormals[mesh->mFaces[face_id_].mIndices[0]].x, - mesh->mNormals[mesh->mFaces[face_id_].mIndices[0]].y, - mesh->mNormals[mesh->mFaces[face_id_].mIndices[0]].z)), - - std::make_pair( - get_vertex(mesh,1), - math::vec3(mesh->mNormals[mesh->mFaces[face_id_].mIndices[1]].x, - mesh->mNormals[mesh->mFaces[face_id_].mIndices[1]].y, - mesh->mNormals[mesh->mFaces[face_id_].mIndices[1]].z)), - - std::make_pair( - get_vertex(mesh,2), - math::vec3(mesh->mNormals[mesh->mFaces[face_id_].mIndices[2]].x, - mesh->mNormals[mesh->mFaces[face_id_].mIndices[2]].y, - mesh->mNormals[mesh->mFaces[face_id_].mIndices[2]].z)) - ); - + std::make_pair(aiMesh_vertex(mesh,face_id_,0), aiMesh_normal(mesh,face_id_,0)), + std::make_pair(aiMesh_vertex(mesh,face_id_,1), aiMesh_normal(mesh,face_id_,1)), + std::make_pair(aiMesh_vertex(mesh,face_id_,2), aiMesh_normal(mesh,face_id_,2))); normal = scm::math::normalize(normal); } return normal; - } math::vec2 Triangle::get_texture_coords_interpolated(aiMesh* mesh, math::vec3 const& position) const { - math::vec2 tex_coords(0, 0); if (mesh->HasTextureCoords(0)) { - tex_coords = math::interpolate(position, - - std::make_pair( - get_vertex(mesh,0), - math::vec2(mesh->mTextureCoords[0][mesh->mFaces[face_id_].mIndices[0]].x, - mesh->mTextureCoords[0][mesh->mFaces[face_id_].mIndices[0]].y)), - - std::make_pair( - get_vertex(mesh,1), - math::vec2(mesh->mTextureCoords[0][mesh->mFaces[face_id_].mIndices[1]].x, - mesh->mTextureCoords[0][mesh->mFaces[face_id_].mIndices[1]].y)), - - std::make_pair( - get_vertex(mesh,2), - math::vec2(mesh->mTextureCoords[0][mesh->mFaces[face_id_].mIndices[2]].x, - mesh->mTextureCoords[0][mesh->mFaces[face_id_].mIndices[2]].y)) + std::make_pair(aiMesh_vertex(mesh,face_id_,0), aiMesh_texcoord(mesh,face_id_,0,0)), + std::make_pair(aiMesh_vertex(mesh,face_id_,1), aiMesh_texcoord(mesh,face_id_,0,1)), + std::make_pair(aiMesh_vertex(mesh,face_id_,2), aiMesh_texcoord(mesh,face_id_,0,2)) ); } return tex_coords; } - } From cf783742ead77f0696ea1f3a5a69e4b50525dcca Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Wed, 6 Nov 2013 16:51:29 +0100 Subject: [PATCH 029/146] apply clang-format --- src/gua/utils/KDTreeUtils.cpp | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/src/gua/utils/KDTreeUtils.cpp b/src/gua/utils/KDTreeUtils.cpp index 1c2e25731..3d9e33768 100644 --- a/src/gua/utils/KDTreeUtils.cpp +++ b/src/gua/utils/KDTreeUtils.cpp @@ -53,7 +53,10 @@ inline math::vec3 aiMesh_normal(aiMesh* mesh, unsigned face, unsigned i) { mesh->mNormals[mesh->mFaces[face].mIndices[i]].z); } -inline math::vec2 aiMesh_texcoord(aiMesh* mesh, unsigned face, unsigned i, unsigned j) { +inline math::vec2 aiMesh_texcoord(aiMesh* mesh, + unsigned face, + unsigned i, + unsigned j) { return math::vec2(mesh->mTextureCoords[i][mesh->mFaces[face].mIndices[j]].x, mesh->mTextureCoords[i][mesh->mFaces[face].mIndices[j]].y); } @@ -195,7 +198,6 @@ math::vec3 Triangle::get_vertex(aiMesh* mesh, unsigned vertex_id) const { } math::vec3 Triangle::get_normal(aiMesh* mesh) const { - math::vec3 normal(0, 0, 0); if (mesh->HasNormals()) { @@ -212,28 +214,37 @@ math::vec3 Triangle::get_normal(aiMesh* mesh) const { return normal; } -math::vec3 Triangle::get_normal_interpolated(aiMesh* mesh, math::vec3 const& position) const { +math::vec3 Triangle::get_normal_interpolated(aiMesh* mesh, + math::vec3 const& position) const { math::vec3 normal(0, 0, 0); if (mesh->HasNormals()) { normal = math::interpolate(position, - std::make_pair(aiMesh_vertex(mesh,face_id_,0), aiMesh_normal(mesh,face_id_,0)), - std::make_pair(aiMesh_vertex(mesh,face_id_,1), aiMesh_normal(mesh,face_id_,1)), - std::make_pair(aiMesh_vertex(mesh,face_id_,2), aiMesh_normal(mesh,face_id_,2))); + std::make_pair(aiMesh_vertex(mesh,face_id_,0), + aiMesh_normal(mesh,face_id_,0)), + std::make_pair(aiMesh_vertex(mesh,face_id_,1), + aiMesh_normal(mesh,face_id_,1)), + std::make_pair(aiMesh_vertex(mesh,face_id_,2), + aiMesh_normal(mesh,face_id_,2))); normal = scm::math::normalize(normal); } return normal; } -math::vec2 Triangle::get_texture_coords_interpolated(aiMesh* mesh, math::vec3 const& position) const { +math::vec2 Triangle::get_texture_coords_interpolated( + aiMesh* mesh, + math::vec3 const& position) const { math::vec2 tex_coords(0, 0); if (mesh->HasTextureCoords(0)) { tex_coords = math::interpolate(position, - std::make_pair(aiMesh_vertex(mesh,face_id_,0), aiMesh_texcoord(mesh,face_id_,0,0)), - std::make_pair(aiMesh_vertex(mesh,face_id_,1), aiMesh_texcoord(mesh,face_id_,0,1)), - std::make_pair(aiMesh_vertex(mesh,face_id_,2), aiMesh_texcoord(mesh,face_id_,0,2)) + std::make_pair(aiMesh_vertex(mesh,face_id_,0), + aiMesh_texcoord(mesh,face_id_,0,0)), + std::make_pair(aiMesh_vertex(mesh,face_id_,1), + aiMesh_texcoord(mesh,face_id_,0,1)), + std::make_pair(aiMesh_vertex(mesh,face_id_,2), + aiMesh_texcoord(mesh,face_id_,0,2)) ); } From f5d54634b153d5926760f67e53429eafa6326f5a Mon Sep 17 00:00:00 2001 From: thelaui Date: Fri, 8 Nov 2013 10:13:22 +0100 Subject: [PATCH 030/146] removed uploading/generating messages from Textures and Mesh --- src/gua/renderer/MeshLoader.cpp | 2 +- src/gua/renderer/Texture2D.cpp | 2 +- src/gua/renderer/Texture3D.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/gua/renderer/MeshLoader.cpp b/src/gua/renderer/MeshLoader.cpp index 6696c928c..ad6bb10b7 100644 --- a/src/gua/renderer/MeshLoader.cpp +++ b/src/gua/renderer/MeshLoader.cpp @@ -49,7 +49,7 @@ std::shared_ptr MeshLoader::load(std::string const& file_name, node_counter_ = 0; TextFile file(file_name); - MESSAGE("Loading mesh file %s", file_name.c_str()); + // MESSAGE("Loading mesh file %s", file_name.c_str()); if (file.is_valid()) { auto importer = std::make_shared(); diff --git a/src/gua/renderer/Texture2D.cpp b/src/gua/renderer/Texture2D.cpp index f1e0d323f..4d8a34350 100644 --- a/src/gua/renderer/Texture2D.cpp +++ b/src/gua/renderer/Texture2D.cpp @@ -82,7 +82,7 @@ void Texture2D::upload_to(RenderContext const& context) const { color_format_, data_); } else { - MESSAGE("Uploading texture file %s", file_name_.c_str()); + // MESSAGE("Uploading texture file %s", file_name_.c_str()); scm::gl::texture_loader loader; textures_[context.id] = loader.load_texture_2d( *context.render_device, file_name_, mipmap_layers_ > 0); diff --git a/src/gua/renderer/Texture3D.cpp b/src/gua/renderer/Texture3D.cpp index a74e2c340..7c0b1b1e8 100644 --- a/src/gua/renderer/Texture3D.cpp +++ b/src/gua/renderer/Texture3D.cpp @@ -87,7 +87,7 @@ void Texture3D::upload_to(RenderContext const& context) const { color_format_, data_); } else { - MESSAGE("Uploading texture file %s", file_name_.c_str()); + // MESSAGE("Uploading texture file %s", file_name_.c_str()); scm::gl::volume_loader loader; textures_[context.id] = loader.load_texture_3d( *context.render_device, file_name_, mipmap_layers_ > 0); From 4eb23bd1e721b9784d8829e13bac21dfbeef3bc9 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Fri, 8 Nov 2013 10:53:47 +0100 Subject: [PATCH 031/146] Fixed regression introduced by Andreas which made EVEN THE EXAMPLES UNCOMPILABLE --- PLEASE TEST WHAT YOU DO BEFORE COMMITTING TO DEVELOP --- src/gua/math/math.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/gua/math/math.cpp b/src/gua/math/math.cpp index ca537e893..a8fbc2a54 100644 --- a/src/gua/math/math.cpp +++ b/src/gua/math/math.cpp @@ -76,10 +76,10 @@ math::mat4 const math::mat_ai_to_scm(aiMatrix4x4 const& ai_mat) { return scm_mat; } -std::tuple barycentric(math::vec3 const& a, - math::vec3 const& b, - math::vec3 const& c, - math::vec3 const& p) { +std::tuple math::barycentric(math::vec3 const& a, + math::vec3 const& b, + math::vec3 const& c, + math::vec3 const& p) { auto pa = a-p; auto pb = b-p; auto pc = c-p; From 3970fd03a30144bd3ae5fdd435f1262adc8d52e2 Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Tue, 12 Nov 2013 09:48:33 +0100 Subject: [PATCH 032/146] add typdefs for quaternions --- include/gua/math/math.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/gua/math/math.hpp b/include/gua/math/math.hpp index 145f5a0b6..ab189df70 100644 --- a/include/gua/math/math.hpp +++ b/include/gua/math/math.hpp @@ -56,6 +56,9 @@ typedef scm::math::vec vec2i; typedef scm::math::vec vec4ui; typedef scm::math::vec vec3ui; typedef scm::math::vec vec2ui; + +typedef scm::math::quat quatf; +typedef scm::math::quat quatd; ///@} /** From a3d9c708d4e1a2f489a1e0f16ac0a95828448e2e Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Tue, 12 Nov 2013 11:36:30 +0100 Subject: [PATCH 033/146] rename quatf to quat --- include/gua/math/math.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/gua/math/math.hpp b/include/gua/math/math.hpp index ab189df70..8ae4e04b8 100644 --- a/include/gua/math/math.hpp +++ b/include/gua/math/math.hpp @@ -57,8 +57,7 @@ typedef scm::math::vec vec4ui; typedef scm::math::vec vec3ui; typedef scm::math::vec vec2ui; -typedef scm::math::quat quatf; -typedef scm::math::quat quatd; +typedef scm::math::quat quat; ///@} /** From 11818b105ce7046ae7c010b541674773bfac712c Mon Sep 17 00:00:00 2001 From: Andre Schollmeyer Date: Tue, 12 Nov 2013 17:37:42 +0100 Subject: [PATCH 034/146] assimp forward declaration to remove dependency propagation --- include/gua/math/math.hpp | 6 +----- src/gua/renderer/FullscreenPass.cpp | 2 +- src/gua/renderer/GeometryPass.cpp | 2 +- src/gua/renderer/LightingPass.cpp | 2 +- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/include/gua/math/math.hpp b/include/gua/math/math.hpp index 8ae4e04b8..a086dbfe6 100644 --- a/include/gua/math/math.hpp +++ b/include/gua/math/math.hpp @@ -27,11 +27,7 @@ #include #include -#if ASSIMP_VERSION == 3 -#include -#else -#include -#endif +class aiMatrix4x4; #include diff --git a/src/gua/renderer/FullscreenPass.cpp b/src/gua/renderer/FullscreenPass.cpp index 46d3fa00f..21dbe443c 100644 --- a/src/gua/renderer/FullscreenPass.cpp +++ b/src/gua/renderer/FullscreenPass.cpp @@ -67,7 +67,7 @@ void FullscreenPass::render_scene(Camera const& camera, RenderContext const& ctx fbo->bind(ctx); ctx.render_context->set_viewport(scm::gl::viewport( - math::vec2(0, 0), math::vec2(fbo->width(), fbo->height()))); + math::vec2(0, 0), math::vec2(float(fbo->width()), float(fbo->height())))); ctx.render_context->set_depth_stencil_state(depth_stencil_state_); rendering(camera, pipeline_->get_current_scene(eye), eye, ctx); diff --git a/src/gua/renderer/GeometryPass.cpp b/src/gua/renderer/GeometryPass.cpp index c17866da7..0c983c503 100644 --- a/src/gua/renderer/GeometryPass.cpp +++ b/src/gua/renderer/GeometryPass.cpp @@ -53,7 +53,7 @@ void GeometryPass::render_scene(Camera const& camera, RenderContext const& ctx) fbo->bind(ctx); ctx.render_context->set_viewport(scm::gl::viewport( - math::vec2(0, 0), math::vec2(fbo->width(), fbo->height()))); + math::vec2(0, 0), ::scm::math::vec2f(fbo->width(), fbo->height()))); rendering(pipeline_->get_current_scene(eye), ctx, eye, camera, fbo); diff --git a/src/gua/renderer/LightingPass.cpp b/src/gua/renderer/LightingPass.cpp index 052de888d..1a2c3b56d 100644 --- a/src/gua/renderer/LightingPass.cpp +++ b/src/gua/renderer/LightingPass.cpp @@ -176,7 +176,7 @@ void LightingPass::rendering(SerializedScene const& scene, ctx.render_context->set_viewport(scm::gl::viewport( math::vec2(0, 0), - math::vec2(target->width(), target->height()))); + math::vec2(float(target->width()), float(target->height())))); ctx.render_context->set_depth_stencil_state(depth_stencil_state_); ctx.render_context->set_rasterizer_state(rasterizer_state_); From 1b968789d8a9b1a0cd5319aa1545f357f3d0f6ed Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Tue, 12 Nov 2013 17:47:15 +0100 Subject: [PATCH 035/146] add missing assimp header --- src/gua/math/math.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/gua/math/math.cpp b/src/gua/math/math.cpp index a8fbc2a54..0a378dd30 100644 --- a/src/gua/math/math.cpp +++ b/src/gua/math/math.cpp @@ -26,6 +26,13 @@ #include #include +#if ASSIMP_VERSION == 3 +#include +#else +#include +#endif + + namespace gua { //////////////////////////////////////////////////////////////////////////////// From 22aaf8a2146641c43a43ebd9c895fac181d62726 Mon Sep 17 00:00:00 2001 From: Andre Schollmeyer Date: Wed, 13 Nov 2013 10:01:49 +0100 Subject: [PATCH 036/146] - fixes for windows port of avango-gua --- CMakeLists.txt | 3 +-- include/gua/math/math.hpp | 2 +- include/gua/renderer/RenderClient.hpp | 12 ++++++------ include/gua/renderer/Renderer.hpp | 1 + include/gua/renderer/Texture.hpp | 5 ----- include/gua/utils/Color3f.hpp | 2 ++ src/gua/math/math.cpp | 6 ++++++ src/gua/utils/Color3f.cpp | 7 +++++++ 8 files changed, 24 insertions(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 782f8d4d7..6c44aecb6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,7 +42,6 @@ include(find_compiler) include(find_schism) include(find_boost) include(find_cuda) -#include(find_ev) include(find_bullet) include(find_json) @@ -93,7 +92,7 @@ IF (UNIX) set(CMAKE_CXX_FLAGS_RELEASE "-s -O4 --std=c++0x") set(CMAKE_CXX_FLAGS_DEBUG "-g -Wall --std=c++0x") ELSEIF(MSVC) - set(CMAKE_CXX_FLAGS_RELEASE "-D _SECURE_SCL=0 -D _SCL_SECURE_NO_WARNINGS -D _CRT_SECURE_NO_DEPRECATE /MD /MP") + set(CMAKE_CXX_FLAGS_RELEASE "-D NDEBUG -D _SECURE_SCL=0 -D _SCL_SECURE_NO_WARNINGS -D _CRT_SECURE_NO_DEPRECATE /MD /MP") set(CMAKE_CXX_FLAGS_DEBUG "-D_DEBUG /MDd /Zi") ENDIF(UNIX) diff --git a/include/gua/math/math.hpp b/include/gua/math/math.hpp index a086dbfe6..f58f3e71f 100644 --- a/include/gua/math/math.hpp +++ b/include/gua/math/math.hpp @@ -27,7 +27,7 @@ #include #include -class aiMatrix4x4; +struct aiMatrix4x4; #include diff --git a/include/gua/renderer/RenderClient.hpp b/include/gua/renderer/RenderClient.hpp index 817cef1f9..1c83cc72f 100644 --- a/include/gua/renderer/RenderClient.hpp +++ b/include/gua/renderer/RenderClient.hpp @@ -22,16 +22,16 @@ #ifndef GUA_RENDERCLIENT_HPP #define GUA_RENDERCLIENT_HPP -// guacamole headers -#include -#include -#include -#include - // external headers #include #include +// guacamole headers +//#include +//#include +#include +//#include + namespace gua { /** diff --git a/include/gua/renderer/Renderer.hpp b/include/gua/renderer/Renderer.hpp index 0fda04029..5a0edda31 100644 --- a/include/gua/renderer/Renderer.hpp +++ b/include/gua/renderer/Renderer.hpp @@ -28,6 +28,7 @@ #include #include #include + #include #include diff --git a/include/gua/renderer/Texture.hpp b/include/gua/renderer/Texture.hpp index fd9b8556b..cfadf013c 100644 --- a/include/gua/renderer/Texture.hpp +++ b/include/gua/renderer/Texture.hpp @@ -31,13 +31,8 @@ // external headers #include #include - -#if GUA_COMPILER == GUA_COMPILER_MSVC&& GUA_COMPILER_VER <= 1600 -#include -#else #include #include -#endif namespace gua { diff --git a/include/gua/utils/Color3f.hpp b/include/gua/utils/Color3f.hpp index be75b0358..969034d2a 100644 --- a/include/gua/utils/Color3f.hpp +++ b/include/gua/utils/Color3f.hpp @@ -104,6 +104,8 @@ struct Color3f { */ static const Color3f random(); + friend bool operator==(Color3f const& lhs, Color3f const& rhs); + private: void set_hsv(float hue, float saturation, float value); diff --git a/src/gua/math/math.cpp b/src/gua/math/math.cpp index a8fbc2a54..737a40ecb 100644 --- a/src/gua/math/math.cpp +++ b/src/gua/math/math.cpp @@ -26,6 +26,12 @@ #include #include +#if ASSIMP_VERSION == 3 +#include +#else +#include +#endif + namespace gua { //////////////////////////////////////////////////////////////////////////////// diff --git a/src/gua/utils/Color3f.cpp b/src/gua/utils/Color3f.cpp index 0aec2f4cc..50ba297cb 100644 --- a/src/gua/utils/Color3f.cpp +++ b/src/gua/utils/Color3f.cpp @@ -179,6 +179,13 @@ Color3f operator-(Color3f const& lhs, Color3f const& rhs) { return result; } +bool operator==(Color3f const& lhs, Color3f const& rhs) { + return lhs.r() == rhs.r() && + lhs.g() == rhs.g() && + lhs.b() == rhs.b(); + +} + std::ostream& operator<<(std::ostream& os, Color3f const& color) { os << color.r() << " " << color.g() << " " << color.b() << std::endl; return os; From e29e401a00af6c617bf2fb630876f823406c37c4 Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Wed, 13 Nov 2013 10:29:41 +0100 Subject: [PATCH 037/146] add missing FpsCounter header --- include/gua/renderer/RenderClient.hpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/include/gua/renderer/RenderClient.hpp b/include/gua/renderer/RenderClient.hpp index 1c83cc72f..006dbde3b 100644 --- a/include/gua/renderer/RenderClient.hpp +++ b/include/gua/renderer/RenderClient.hpp @@ -27,10 +27,8 @@ #include // guacamole headers -//#include -//#include #include -//#include +#include namespace gua { From cbf09bdfc2ae5a167cbe790a83574c6ddb624edb Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Wed, 20 Nov 2013 14:01:15 +0100 Subject: [PATCH 038/146] build script is now a little more sophisticated --- scripts/make.sh | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/scripts/make.sh b/scripts/make.sh index 0aff1ef5a..1dfbbee04 100755 --- a/scripts/make.sh +++ b/scripts/make.sh @@ -1,5 +1,17 @@ #!/bin/sh -mkdir build +# get directory of script and cd to its parent +DIR="$( cd "$( dirname "$0" )" && pwd )" +cd $DIR/.. -cd build; cmake -DCMAKE_BUILD_TYPE=release ..; make -j16 && ( cd .. ) +# run initial cmake if neccessary +if [ ! -d "build" ]; then + mkdir build + cd build + cmake -DCMAKE_BUILD_TYPE=release .. + cd .. +fi + +# buil it! +cd build +make -j16 From cb80c055c7aa1779089514bfcf889d0ce5d8bab5 Mon Sep 17 00:00:00 2001 From: Andre Schollmeyer Date: Fri, 22 Nov 2013 17:24:31 +0100 Subject: [PATCH 039/146] - updated platform.hpp to allow DLL_EXPORT - necessary to change unique_ptr<> to raw pointer in renderer.hpp/cpp - cmake improvements for windows --- cmake/modules/find_boost.cmake | 227 +++++++++++------- cmake/modules/find_compiler.cmake | 10 + .../gua/databases/CollisionShapeDatabase.hpp | 3 +- include/gua/databases/GeometryDatabase.hpp | 4 +- include/gua/databases/MaterialDatabase.hpp | 5 +- .../gua/databases/ShadingModelDatabase.hpp | 4 +- include/gua/databases/TextureDatabase.hpp | 5 +- include/gua/events/MainLoop.hpp | 4 +- include/gua/events/Scheduler.hpp | 3 +- include/gua/events/Ticker.hpp | 3 +- include/gua/guacamole.hpp | 3 +- include/gua/math/math.hpp | 13 +- include/gua/math/random.hpp | 10 +- include/gua/physics/BoxShape.hpp | 3 +- include/gua/physics/CollisionShape.hpp | 4 +- include/gua/physics/CollisionShapeNode.hpp | 3 +- .../gua/physics/CollisionShapeNodeVisitor.hpp | 1 + include/gua/physics/Constraint.hpp | 2 +- include/gua/physics/ConvexHullShape.hpp | 3 +- include/gua/physics/CylinderShape.hpp | 3 +- include/gua/physics/FixedConstraint.hpp | 3 +- include/gua/physics/HingeConstraint.hpp | 3 +- include/gua/physics/Physics.hpp | 2 +- include/gua/physics/PlaneShape.hpp | 3 +- include/gua/physics/Point2PointConstraint.hpp | 3 +- include/gua/physics/RigidBodyNode.hpp | 2 +- include/gua/physics/SliderConstraint.hpp | 3 +- include/gua/physics/SphereShape.hpp | 3 +- include/gua/physics/TriangleMeshShape.hpp | 3 +- include/gua/platform.hpp | 110 ++++----- include/gua/renderer/BufferDescriptions.hpp | 4 +- include/gua/renderer/BuiltInTextures.hpp | 5 +- include/gua/renderer/Camera.hpp | 4 +- include/gua/renderer/DisplayData.hpp | 3 +- include/gua/renderer/FrameBufferObject.hpp | 7 +- include/gua/renderer/Geometry.hpp | 3 +- include/gua/renderer/GeometryLoader.hpp | 2 +- include/gua/renderer/Material.hpp | 1 + include/gua/renderer/MaterialLoader.hpp | 3 +- include/gua/renderer/NURBS.hpp | 3 +- include/gua/renderer/Pipeline.hpp | 8 +- include/gua/renderer/Renderer.hpp | 12 +- include/gua/renderer/ShaderProgram.hpp | 4 - include/gua/renderer/TextRenderer.hpp | 3 +- include/gua/renderer/Texture.hpp | 2 +- include/gua/renderer/Texture2D.hpp | 6 +- include/gua/renderer/Texture3D.hpp | 7 +- include/gua/renderer/WarpMatrix.hpp | 3 +- include/gua/renderer/Window.hpp | 3 +- include/gua/scenegraph/GeometryNode.hpp | 2 +- include/gua/scenegraph/Node.hpp | 3 +- include/gua/scenegraph/PickResult.hpp | 3 +- include/gua/scenegraph/PointLightNode.hpp | 3 +- include/gua/scenegraph/SceneGraph.hpp | 3 +- include/gua/scenegraph/ScreenNode.hpp | 3 +- include/gua/scenegraph/SpotLightNode.hpp | 3 +- include/gua/scenegraph/TexturedQuadNode.hpp | 2 +- include/gua/scenegraph/TransformNode.hpp | 3 +- include/gua/utils/AnimatedValue.hpp | 4 +- include/gua/utils/Color3f.hpp | 3 +- include/gua/utils/Directory.hpp | 4 +- include/gua/utils/DotGenerator.hpp | 3 +- include/gua/utils/FpsCounter.hpp | 3 +- include/gua/utils/Mask.hpp | 4 +- include/gua/utils/Profiler.hpp | 3 +- include/gua/utils/Timer.hpp | 4 +- src/CMakeLists.txt | 15 +- src/gua/renderer/Renderer.cpp | 8 +- 68 files changed, 341 insertions(+), 266 deletions(-) diff --git a/cmake/modules/find_boost.cmake b/cmake/modules/find_boost.cmake index 9b989eb7e..7608b00c7 100644 --- a/cmake/modules/find_boost.cmake +++ b/cmake/modules/find_boost.cmake @@ -1,58 +1,98 @@ -SET(GUA_BOOST_MIN_VERSION_MAJOR 1) -SET(GUA_BOOST_MIN_VERSION_MINOR 46) -SET(GUA_BOOST_MIN_VERSION_SUBMINOR 0) -SET(GUA_BOOST_MIN_VERSION "${GUA_BOOST_MIN_VERSION_MAJOR}.${GUA_BOOST_MIN_VERSION_MINOR}.${GUA_BOOST_MIN_VERSION_SUBMINOR}") -MATH(EXPR GUA_BOOST_MIN_VERSION_NUM "${GUA_BOOST_MIN_VERSION_MAJOR}*10000 + ${GUA_BOOST_MIN_VERSION_MINOR}*100 + ${GUA_BOOST_MIN_VERSION_SUBMINOR}") - -SET(GUA_BOOST_INCLUDE_SEARCH_DIRS +############################################################################## +# set search directories +############################################################################## +SET(AVANGO_BOOST_MIN_VERSION_MAJOR 1) +SET(AVANGO_BOOST_MIN_VERSION_MINOR 46) +SET(AVANGO_BOOST_MIN_VERSION_SUBMINOR 0) +SET(AVANGO_BOOST_MIN_VERSION "${AVANGO_BOOST_MIN_VERSION_MAJOR}.${AVANGO_BOOST_MIN_VERSION_MINOR}.${AVANGO_BOOST_MIN_VERSION_SUBMINOR}") +MATH(EXPR AVANGO_BOOST_MIN_VERSION_NUM "${AVANGO_BOOST_MIN_VERSION_MAJOR}*10000 + ${AVANGO_BOOST_MIN_VERSION_MINOR}*100 + ${AVANGO_BOOST_MIN_VERSION_SUBMINOR}") + +SET(AVANGO_BOOST_INCLUDE_SEARCH_DIRS ${GLOBAL_EXT_DIR}/inc/boost + ${GUACAMOLE_EXT_DIR}/inc/boost + ${BOOST_INCLUDE_SEARCH_DIR} + ${BOOST_INCLUDE_DIRS} /opt/boost/latest/include ${CMAKE_SYSTEM_INCLUDE_PATH} ${CMAKE_INCLUDE_PATH} /usr/include ) -SET(GUA_BOOST_LIBRARY_SEARCH_DIRS +SET(AVANGO_BOOST_LIBRARY_SEARCH_DIRS ${GLOBAL_EXT_DIR}/lib + ${GUACAMOLE_EXT_DIR}/lib + ${BOOST_LIBRARY_SEARCH_DIR} + ${BOOST_LIBRARY_DIRS} /opt/boost/latest/lib ${CMAKE_SYSTEM_LIBRARY_PATH} ${CMAKE_LIBRARY_PATH} /usr/lib64 ) +############################################################################## +# feedback to provide user-defined paths to search for boost +############################################################################## +MACRO (request_boost_search_directories) + + IF ( NOT BOOST_INCLUDE_DIRS AND NOT BOOST_LIBRARY_DIRS ) + SET(BOOST_INCLUDE_SEARCH_DIR "Please provide boost include path." CACHE PATH "path to boost headers.") + SET(BOOST_LIBRARY_SEARCH_DIR "Please provide boost library path." CACHE PATH "path to boost libraries.") + MESSAGE(FATAL_ERROR "find_boost.cmake: unable to find boost.") + ENDIF ( NOT BOOST_INCLUDE_DIRS AND NOT BOOST_LIBRARY_DIRS ) + + IF ( NOT BOOST_INCLUDE_DIRS ) + SET(BOOST_INCLUDE_SEARCH_DIR "Please provide boost include path." CACHE PATH "path to boost headers.") + MESSAGE(FATAL_ERROR "find_boost.cmake: unable to find boost headers.") + ELSE ( NOT BOOST_INCLUDE_DIRS ) + UNSET(BOOST_INCLUDE_SEARCH_DIR CACHE) + ENDIF ( NOT BOOST_INCLUDE_DIRS ) + + IF ( NOT BOOST_LIBRARY_DIRS ) + SET(BOOST_LIBRARY_SEARCH_DIR "Please provide boost library path." CACHE PATH "path to boost libraries.") + MESSAGE(FATAL_ERROR "find_boost.cmake: unable to find boost libraries.") + ELSE ( NOT BOOST_LIBRARY_DIRS ) + UNSET(BOOST_LIBRARY_SEARCH_DIR CACHE) + ENDIF ( NOT BOOST_LIBRARY_DIRS ) + +ENDMACRO (request_boost_search_directories) + +############################################################################## +# start search +############################################################################## + message("-- checking for BOOST") -IF (NOT BOOST_INCLUDE_DIRS) +IF ( NOT BOOST_INCLUDE_DIRS ) - SET(_GUA_BOOST_FOUND_INC_DIRS "") - FOREACH(_SEARCH_DIR ${GUA_BOOST_INCLUDE_SEARCH_DIRS}) + SET(_AVANGO_BOOST_FOUND_INC_DIRS "") + FOREACH(_SEARCH_DIR ${AVANGO_BOOST_INCLUDE_SEARCH_DIRS}) FIND_PATH(_CUR_SEARCH NAMES boost/config.hpp PATHS ${_SEARCH_DIR} NO_DEFAULT_PATH) IF (_CUR_SEARCH) - LIST(APPEND _GUA_BOOST_FOUND_INC_DIRS ${_CUR_SEARCH}) + LIST(APPEND _AVANGO_BOOST_FOUND_INC_DIRS ${_CUR_SEARCH}) ENDIF(_CUR_SEARCH) SET(_CUR_SEARCH _CUR_SEARCH-NOTFOUND CACHE INTERNAL "internal use") - ENDFOREACH(_SEARCH_DIR ${GUA_BOOST_INCLUDE_SEARCH_DIRS}) + ENDFOREACH(_SEARCH_DIR ${AVANGO_BOOST_INCLUDE_SEARCH_DIRS}) - IF (NOT _GUA_BOOST_FOUND_INC_DIRS) - MESSAGE(FATAL_ERROR "guacamole_boost.cmake: unable to find boost library") - ELSE (NOT _GUA_BOOST_FOUND_INC_DIRS) - SET(BOOST_INCLUDE_DIRS ${_GUA_BOOST_FOUND_INC_DIRS}) - ENDIF (NOT _GUA_BOOST_FOUND_INC_DIRS) + IF (NOT _AVANGO_BOOST_FOUND_INC_DIRS) + request_boost_search_directories() + ELSE (NOT _AVANGO_BOOST_FOUND_INC_DIRS) + SET(BOOST_INCLUDE_DIRS ${_AVANGO_BOOST_FOUND_INC_DIRS}) + ENDIF (NOT _AVANGO_BOOST_FOUND_INC_DIRS) - SET(GUA_BOOST_VERSION 0) - SET(GUA_BOOST_LIB_VERSION "") - SET(GUA_BOOST_LIB_SUFFIX "") + SET(AVANGO_BOOST_VERSION 0) + SET(AVANGO_BOOST_LIB_VERSION "") + SET(AVANGO_BOOST_LIB_SUFFIX "") - SET(_GUA_BOOST_CUR_VERSION ${GUA_BOOST_MIN_VERSION_NUM}) + SET(_AVANGO_BOOST_CUR_VERSION ${AVANGO_BOOST_MIN_VERSION_NUM}) - FOREACH(_INC_DIR ${_GUA_BOOST_FOUND_INC_DIRS}) - FILE(READ "${_INC_DIR}/boost/version.hpp" _GUA_BOOST_VERSION_CONTENTS) + FOREACH(_INC_DIR ${_AVANGO_BOOST_FOUND_INC_DIRS}) + FILE(READ "${_INC_DIR}/boost/version.hpp" _AVANGO_BOOST_VERSION_CONTENTS) - STRING(REGEX REPLACE ".*#define BOOST_VERSION ([0-9]+).*" "\\1" _BOOST_VERSION "${_GUA_BOOST_VERSION_CONTENTS}") - STRING(REGEX REPLACE ".*#define BOOST_LIB_VERSION \"([0-9_]+)\".*" "\\1" _BOOST_LIB_VERSION "${_GUA_BOOST_VERSION_CONTENTS}") + STRING(REGEX REPLACE ".*#define BOOST_VERSION ([0-9]+).*" "\\1" _BOOST_VERSION "${_AVANGO_BOOST_VERSION_CONTENTS}") + STRING(REGEX REPLACE ".*#define BOOST_LIB_VERSION \"([0-9_]+)\".*" "\\1" _BOOST_LIB_VERSION "${_AVANGO_BOOST_VERSION_CONTENTS}") IF(NOT "${_BOOST_VERSION}" STREQUAL "0") MATH(EXPR _BOOST_MAJOR_VERSION "${_BOOST_VERSION} / 100000") @@ -64,103 +104,104 @@ IF (NOT BOOST_INCLUDE_DIRS) MATH(EXPR _BOOST_VERSION_NUM "${_BOOST_MAJOR_VERSION}*10000 + ${_BOOST_MINOR_VERSION}*100 + ${_BOOST_SUBMINOR_VERSION}") - IF ( _GUA_BOOST_CUR_VERSION LESS _BOOST_VERSION_NUM - OR _GUA_BOOST_CUR_VERSION EQUAL _BOOST_VERSION_NUM) - SET(GUA_BOOST_VERSION ${_BOOST_VERSION}) - SET(GUA_BOOST_LIB_VERSION ${_BOOST_LIB_VERSION}) - SET(GUA_BOOST_LIB_SUFFIX ".${_BOOST_MAJOR_VERSION}.${_BOOST_MINOR_VERSION}.${_BOOST_SUBMINOR_VERSION}") + IF ( _AVANGO_BOOST_CUR_VERSION LESS _BOOST_VERSION_NUM + OR _AVANGO_BOOST_CUR_VERSION EQUAL _BOOST_VERSION_NUM) + SET(AVANGO_BOOST_VERSION ${_BOOST_VERSION}) + SET(AVANGO_BOOST_LIB_VERSION ${_BOOST_LIB_VERSION}) + SET(AVANGO_BOOST_LIB_SUFFIX ".${_BOOST_MAJOR_VERSION}.${_BOOST_MINOR_VERSION}.${_BOOST_SUBMINOR_VERSION}") SET(BOOST_INCLUDE_DIRS ${_INC_DIR}) - SET(_GUA_BOOST_CUR_VERSION ${_BOOST_VERSION_NUM}) - ENDIF ( _GUA_BOOST_CUR_VERSION LESS _BOOST_VERSION_NUM - OR _GUA_BOOST_CUR_VERSION EQUAL _BOOST_VERSION_NUM) + SET(_AVANGO_BOOST_CUR_VERSION ${_BOOST_VERSION_NUM}) + ENDIF ( _AVANGO_BOOST_CUR_VERSION LESS _BOOST_VERSION_NUM + OR _AVANGO_BOOST_CUR_VERSION EQUAL _BOOST_VERSION_NUM) - ENDFOREACH(_INC_DIR ${_GUA_BOOST_FOUND_INC_DIRS}) + ENDFOREACH(_INC_DIR ${_AVANGO_BOOST_FOUND_INC_DIRS}) - IF (GUA_BOOST_VERSION EQUAL 0) - MESSAGE(FATAL_ERROR "found boost versions ${_BOOST_VERSION} to old (min. version ${GUA_BOOST_MIN_VERSION} required)") - ELSE (GUA_BOOST_VERSION EQUAL 0) - #SET(BOOST_INCLUDE_DIRS ${BOOST_INCLUDE_DIRS} CACHE STRING "The boost include directory") - SET(BOOST_INCLUDE_DIRS ${BOOST_INCLUDE_DIRS}) - SET(GUA_BOOST_VERSION ${GUA_BOOST_VERSION} CACHE STRING "The boost version number") - SET(GUA_BOOST_LIB_SUFFIX ${GUA_BOOST_LIB_SUFFIX} CACHE STRING "The boost library suffix") - SET(GUA_BOOST_LIB_VERSION ${GUA_BOOST_LIB_VERSION} CACHE STRING "The boost library version string") - ENDIF (GUA_BOOST_VERSION EQUAL 0) + IF (AVANGO_BOOST_VERSION EQUAL 0) + MESSAGE(FATAL_ERROR "found boost versions ${_BOOST_VERSION} to old (min. version ${AVANGO_BOOST_MIN_VERSION} required)") + request_boost_search_directories() + ELSE (AVANGO_BOOST_VERSION EQUAL 0) + SET(BOOST_INCLUDE_DIRS ${BOOST_INCLUDE_DIRS} CACHE STRING "The boost include directory") + SET(AVANGO_BOOST_VERSION ${AVANGO_BOOST_VERSION} CACHE STRING "The boost version number") + SET(AVANGO_BOOST_LIB_SUFFIX ${AVANGO_BOOST_LIB_SUFFIX} CACHE STRING "The boost library suffix") + SET(AVANGO_BOOST_LIB_VERSION ${AVANGO_BOOST_LIB_VERSION} CACHE STRING "The boost library version string") + ENDIF (AVANGO_BOOST_VERSION EQUAL 0) -ENDIF(NOT BOOST_INCLUDE_DIRS) +ENDIF ( NOT BOOST_INCLUDE_DIRS ) -IF ( BOOST_INCLUDE_DIRS - AND NOT BOOST_LIBRARIES) - #AND NOT BOOST_LIBRARY_DIRS) +IF ( BOOST_INCLUDE_DIRS AND NOT BOOST_LIBRARY_DIRS ) - SET(_GUA_BOOST_FILESYSTEM_LIB "") - SET(_GUA_BOOST_FOUND_LIB_DIR "") - SET(_GUA_BOOST_POSTFIX "") + SET(_AVANGO_BOOST_FILESYSTEM_LIB "") + SET(_AVANGO_BOOST_FOUND_LIB_DIR "") + SET(_AVANGO_BOOST_POSTFIX "") if (UNIX) - LIST(APPEND _GUA_BOOST_FILESYSTEM_LIB "${CMAKE_SHARED_LIBRARY_PREFIX}boost_filesystem${GUA_COMPILER_SUFFIX}${GUA_BOOST_LIB_VERSION}${CMAKE_SHARED_LIBRARY_SUFFIX}${GUA_BOOST_LIB_SUFFIX}" - "${CMAKE_SHARED_LIBRARY_PREFIX}boost_filesystem${CMAKE_SHARED_LIBRARY_SUFFIX}${GUA_BOOST_LIB_SUFFIX}" - "${CMAKE_SHARED_LIBRARY_PREFIX}boost_filesystem${CMAKE_SHARED_LIBRARY_SUFFIX}${GUA_BOOST_LIB_SUFFIX}") + LIST(APPEND _AVANGO_BOOST_FILESYSTEM_LIB "${CMAKE_SHARED_LIBRARY_PREFIX}boost_filesystem${COMPILER_SUFFIX}${AVANGO_BOOST_LIB_VERSION}${CMAKE_SHARED_LIBRARY_SUFFIX}${AVANGO_BOOST_LIB_SUFFIX}" + "${CMAKE_SHARED_LIBRARY_PREFIX}boost_filesystem${CMAKE_SHARED_LIBRARY_SUFFIX}${AVANGO_BOOST_LIB_SUFFIX}" + "${CMAKE_SHARED_LIBRARY_PREFIX}boost_filesystem${CMAKE_SHARED_LIBRARY_SUFFIX}${AVANGO_BOOST_LIB_SUFFIX}") elseif (WIN32) - LIST(APPEND _GUA_BOOST_FILESYSTEM_LIB "${CMAKE_STATIC_LIBRARY_PREFIX}libboost_filesystem-${GUA_COMPILER_SUFFIX}-mt-${GUA_BOOST_LIB_VERSION}${CMAKE_STATIC_LIBRARY_SUFFIX}" + LIST(APPEND _AVANGO_BOOST_FILESYSTEM_LIB "${CMAKE_STATIC_LIBRARY_PREFIX}libboost_filesystem-${COMPILER_SUFFIX}-mt-${AVANGO_BOOST_LIB_VERSION}${CMAKE_STATIC_LIBRARY_SUFFIX}" "${CMAKE_STATIC_LIBRARY_PREFIX}libboost_filesystem-mt${CMAKE_STATIC_LIBRARY_SUFFIX}") endif (UNIX) - FOREACH(_SEARCH_DIR ${GUA_BOOST_LIBRARY_SEARCH_DIRS}) + FOREACH(_SEARCH_DIR ${AVANGO_BOOST_LIBRARY_SEARCH_DIRS}) + FIND_PATH(_CUR_SEARCH - NAMES ${_GUA_BOOST_FILESYSTEM_LIB} + NAMES ${_AVANGO_BOOST_FILESYSTEM_LIB} PATHS ${_SEARCH_DIR} PATH_SUFFIXES debug release . NO_DEFAULT_PATH) IF (_CUR_SEARCH) - LIST(APPEND _GUA_BOOST_FOUND_LIB_DIR ${_SEARCH_DIR}) + LIST(APPEND _AVANGO_BOOST_FOUND_LIB_DIR ${_SEARCH_DIR}) if (UNIX) - FIND_FILE(_CUR_SEARCH_FILE NAMES ${_GUA_BOOST_FILESYSTEM_LIB} PATHS ${_CUR_SEARCH}) + FIND_FILE(_CUR_SEARCH_FILE NAMES ${_AVANGO_BOOST_FILESYSTEM_LIB} PATHS ${_CUR_SEARCH}) if (_CUR_SEARCH_FILE) LIST(APPEND BOOST_LIBRARIES ${_CUR_SEARCH_FILE}) - STRING(REGEX REPLACE "${_CUR_SEARCH}/${CMAKE_SHARED_LIBRARY_PREFIX}boost_filesystem(.*).so(.*)" "\\2" _GUA_BOOST_UNIX_LIB_SUF ${_CUR_SEARCH_FILE}) - if (${_GUA_BOOST_UNIX_LIB_SUF} STREQUAL ${GUA_BOOST_LIB_SUFFIX}) - message("found matching version") - list(APPEND BOOST_LIBRARIES _GUA_BOOST_FILESYSTEM_LIB) - STRING(REGEX REPLACE "${_CUR_SEARCH}/${CMAKE_SHARED_LIBRARY_PREFIX}boost_filesystem(.*).so(.*)" "\\1" _GUA_BOOST_POSTFIX ${_CUR_SEARCH_FILE}) - endif (${_GUA_BOOST_UNIX_LIB_SUF} STREQUAL ${GUA_BOOST_LIB_SUFFIX}) + STRING(REGEX REPLACE "${_CUR_SEARCH}/${CMAKE_SHARED_LIBRARY_PREFIX}boost_filesystem(.*).so(.*)" "\\2" _AVANGO_BOOST_UNIX_LIB_SUF ${_CUR_SEARCH_FILE}) + if (${_AVANGO_BOOST_UNIX_LIB_SUF} STREQUAL ${AVANGO_BOOST_LIB_SUFFIX}) + list(APPEND BOOST_LIBRARIES _AVANGO_BOOST_FILESYSTEM_LIB) + STRING(REGEX REPLACE "${_CUR_SEARCH}/${CMAKE_SHARED_LIBRARY_PREFIX}boost_filesystem(.*).so(.*)" "\\1" _AVANGO_BOOST_POSTFIX ${_CUR_SEARCH_FILE}) + endif (${_AVANGO_BOOST_UNIX_LIB_SUF} STREQUAL ${AVANGO_BOOST_LIB_SUFFIX}) endif (_CUR_SEARCH_FILE) SET(_CUR_SEARCH_FILE _CUR_SEARCH_FILE-NOTFOUND CACHE INTERNAL "internal use") endif (UNIX) ENDIF(_CUR_SEARCH) SET(_CUR_SEARCH _CUR_SEARCH-NOTFOUND CACHE INTERNAL "internal use") - ENDFOREACH(_SEARCH_DIR ${GUA_BOOST_LIBRARY_SEARCH_DIRS}) - IF (NOT _GUA_BOOST_FOUND_LIB_DIR) + ENDFOREACH(_SEARCH_DIR ${AVANGO_BOOST_LIBRARY_SEARCH_DIRS}) + + IF (NOT _AVANGO_BOOST_FOUND_LIB_DIR) MESSAGE(FATAL_ERROR "guacamole_boost.cmake: unable to find boost library") - ELSE (NOT _GUA_BOOST_FOUND_LIB_DIR) - SET(BOOST_LIBRARY_DIRS ${_GUA_BOOST_FOUND_LIB_DIR} CACHE STRING "The boost library directory") - message("-- found matching version") - FILE(GLOB BOOST_LIBRARIES ${_GUA_BOOST_FOUND_LIB_DIR}/*.so) - ENDIF (NOT _GUA_BOOST_FOUND_LIB_DIR) + ELSE (NOT _AVANGO_BOOST_FOUND_LIB_DIR) + SET(BOOST_LIBRARY_DIRS ${_AVANGO_BOOST_FOUND_LIB_DIR} CACHE PATH "boost library path.") + IF (UNIX) + FILE(GLOB BOOST_LIBRARIES ${_AVANGO_BOOST_FOUND_LIB_DIR}/*.so) + ELSEIF (MSVC) + # use boost auto link + ENDIF (UNIX) + ENDIF (NOT _AVANGO_BOOST_FOUND_LIB_DIR) if (UNIX) -# SET(SCHISM_BOOST_LIB_POSTFIX_REL "${_GUA_BOOST_POSTFIX}" CACHE STRING "(deprecated) boost library release postfix") -# SET(SCHISM_BOOST_LIB_POSTFIX_DBG "${_GUA_BOOST_POSTFIX}" CACHE STRING "(deprecated) boost library debug postfix") -# IF (NOT _GUA_BOOST_POSTFIX) -# MESSAGE(FATAL_ERROR "guacamole_boost.cmake: unable to determine boost library suffix") -# ELSE (NOT _GUA_BOOST_POSTFIX) -# SET(SCHISM_BOOST_LIB_POSTFIX_REL "${_GUA_BOOST_POSTFIX}" CACHE STRING "boost library release postfix") -# SET(SCHISM_BOOST_LIB_POSTFIX_DBG "${_GUA_BOOST_POSTFIX}" CACHE STRING "boost library debug postfix") -# ENDIF (NOT _GUA_BOOST_POSTFIX) - set(GUA_BOOST_MT_REL "${_GUA_BOOST_POSTFIX}" CACHE STRING "boost dynamic library release postfix") - set(GUA_BOOST_MT_DBG "${_GUA_BOOST_POSTFIX}" CACHE STRING "boost dynamic library debug postfix") - set(GUA_BOOST_MT_S_REL "${_GUA_BOOST_POSTFIX}" CACHE STRING "boost static library release postfix") - set(GUA_BOOST_MT_S_DBG "${_GUA_BOOST_POSTFIX}" CACHE STRING "boost static library debug postfix") + set(AVANGO_BOOST_MT_REL "${_AVANGO_BOOST_POSTFIX}" CACHE STRING "boost dynamic library release postfix") + set(AVANGO_BOOST_MT_DBG "${_AVANGO_BOOST_POSTFIX}" CACHE STRING "boost dynamic library debug postfix") + set(AVANGO_BOOST_MT_S_REL "${_AVANGO_BOOST_POSTFIX}" CACHE STRING "boost static library release postfix") + set(AVANGO_BOOST_MT_S_DBG "${_AVANGO_BOOST_POSTFIX}" CACHE STRING "boost static library debug postfix") elseif (WIN32) -# SET(SCHISM_BOOST_LIB_POSTFIX_REL "${GUA_COMPILER_SUFFIX}-mt-${GUA_BOOST_LIB_VERSION}" CACHE STRING "(deprecated) boost library release postfix") -# SET(SCHISM_BOOST_LIB_POSTFIX_DBG "${GUA_COMPILER_SUFFIX}-mt-gd-${GUA_BOOST_LIB_VERSION}" CACHE STRING "(deprecated) boost library debug postfix") - - set(GUA_BOOST_MT_REL "${GUA_COMPILER_SUFFIX}-mt-${GUA_BOOST_LIB_VERSION}" CACHE STRING "boost dynamic library release postfix") - set(GUA_BOOST_MT_DBG "${GUA_COMPILER_SUFFIX}-mt-gd-${GUA_BOOST_LIB_VERSION}" CACHE STRING "boost dynamic library debug postfix") - set(GUA_BOOST_MT_S_REL "${GUA_COMPILER_SUFFIX}-mt-s-${GUA_BOOST_LIB_VERSION}" CACHE STRING "boost static library release postfix") - set(GUA_BOOST_MT_S_DBG "${GUA_COMPILER_SUFFIX}-mt-sgd-${GUA_BOOST_LIB_VERSION}" CACHE STRING "boost static library debug postfix") + set(AVANGO_BOOST_MT_REL "${COMPILER_SUFFIX}-mt-${AVANGO_BOOST_LIB_VERSION}" CACHE STRING "boost dynamic library release postfix") + set(AVANGO_BOOST_MT_DBG "${COMPILER_SUFFIX}-mt-gd-${AVANGO_BOOST_LIB_VERSION}" CACHE STRING "boost dynamic library debug postfix") + set(AVANGO_BOOST_MT_S_REL "${COMPILER_SUFFIX}-mt-s-${AVANGO_BOOST_LIB_VERSION}" CACHE STRING "boost static library release postfix") + set(AVANGO_BOOST_MT_S_DBG "${COMPILER_SUFFIX}-mt-sgd-${AVANGO_BOOST_LIB_VERSION}" CACHE STRING "boost static library debug postfix") endif (UNIX) -ENDIF(BOOST_INCLUDE_DIRS AND NOT BOOST_LIBRARIES) -# AND NOT BOOST_LIBRARY_DIRS) +ENDIF ( BOOST_INCLUDE_DIRS AND NOT BOOST_LIBRARY_DIRS ) + +############################################################################## +# verify +############################################################################## +IF ( NOT BOOST_INCLUDE_DIRS OR NOT BOOST_LIBRARY_DIRS ) + request_boost_search_directories() +ELSE ( NOT BOOST_INCLUDE_DIRS OR NOT BOOST_LIBRARY_DIRS ) + UNSET(BOOST_INCLUDE_SEARCH_DIR CACHE) + UNSET(BOOST_LIBRARY_SEARCH_DIR CACHE) + MESSAGE("-- found matching boost version") +ENDIF ( NOT BOOST_INCLUDE_DIRS OR NOT BOOST_LIBRARY_DIRS ) \ No newline at end of file diff --git a/cmake/modules/find_compiler.cmake b/cmake/modules/find_compiler.cmake index 011ea8a71..41bb52082 100644 --- a/cmake/modules/find_compiler.cmake +++ b/cmake/modules/find_compiler.cmake @@ -24,6 +24,16 @@ if (WIN32) endif(MSVC12) endif (WIN32) +macro(get_WIN32_WINNT version) + if (WIN32 AND CMAKE_SYSTEM_VERSION) + set(ver ${CMAKE_SYSTEM_VERSION}) + string(REPLACE "." "" ver ${ver}) + string(REGEX REPLACE "([0-9])" "0\\1" ver ${ver}) + + set(${version} "0x${ver}") + endif() +endmacro() + if (UNIX) if (CMAKE_COMPILER_IS_GNUCXX) #find out the version of gcc being used. diff --git a/include/gua/databases/CollisionShapeDatabase.hpp b/include/gua/databases/CollisionShapeDatabase.hpp index c8131fd2f..a27359959 100644 --- a/include/gua/databases/CollisionShapeDatabase.hpp +++ b/include/gua/databases/CollisionShapeDatabase.hpp @@ -23,6 +23,7 @@ #define GUA_COLLISION_SHAPE_DATABASE_HPP // guacamole headers +#include #include #include #include @@ -36,7 +37,7 @@ namespace physics { * This Database stores collision shapes that can be shared among rigid bodies. * It can be accessed via string identifiers. */ -class CollisionShapeDatabase : public Database, +class GUA_DLL CollisionShapeDatabase : public Database, public Singleton { public: diff --git a/include/gua/databases/GeometryDatabase.hpp b/include/gua/databases/GeometryDatabase.hpp index ea7efeabd..8a8e1c70c 100644 --- a/include/gua/databases/GeometryDatabase.hpp +++ b/include/gua/databases/GeometryDatabase.hpp @@ -35,8 +35,8 @@ namespace gua { * This Database stores geometry data. It can be accessed via string * identifiers. */ -class GeometryDatabase : public Database, - public Singleton { +class GUA_DLL GeometryDatabase : public Database, + public Singleton { public: /** diff --git a/include/gua/databases/MaterialDatabase.hpp b/include/gua/databases/MaterialDatabase.hpp index 727ab09ea..af83dd605 100644 --- a/include/gua/databases/MaterialDatabase.hpp +++ b/include/gua/databases/MaterialDatabase.hpp @@ -23,6 +23,7 @@ #define GUA_MATERIAL_DATABASE_HPP // guacamole headers +#include #include #include #include @@ -35,8 +36,8 @@ namespace gua { * This Database stores material data. It can be accessed via string * identifiers. */ -class MaterialDatabase : public Database, - public Singleton { +class GUA_DLL MaterialDatabase : public Database, + public Singleton { public: /** diff --git a/include/gua/databases/ShadingModelDatabase.hpp b/include/gua/databases/ShadingModelDatabase.hpp index dfcd04148..7ac1438a2 100644 --- a/include/gua/databases/ShadingModelDatabase.hpp +++ b/include/gua/databases/ShadingModelDatabase.hpp @@ -35,8 +35,8 @@ namespace gua { * This Database stores shading model data. It can be accessed via string * identifiers. */ -class ShadingModelDatabase : public Database, - public Singleton { +class GUA_DLL ShadingModelDatabase : public Database, + public Singleton { public: /** diff --git a/include/gua/databases/TextureDatabase.hpp b/include/gua/databases/TextureDatabase.hpp index 8248cbf69..b27362bfc 100644 --- a/include/gua/databases/TextureDatabase.hpp +++ b/include/gua/databases/TextureDatabase.hpp @@ -23,6 +23,7 @@ #define GUA_TEXTURE_DATABASE_HPP // guacamole headers +#include #include #include #include @@ -35,8 +36,8 @@ namespace gua { * This Database stores texture data. It can be accessed via string * identifiers. */ -class TextureDatabase : public Database, - public Singleton { +class GUA_DLL TextureDatabase : public Database, + public Singleton { public: /** diff --git a/include/gua/events/MainLoop.hpp b/include/gua/events/MainLoop.hpp index 04914c42f..1d7df81d4 100644 --- a/include/gua/events/MainLoop.hpp +++ b/include/gua/events/MainLoop.hpp @@ -26,10 +26,12 @@ #include +#include + namespace gua { namespace events { - class MainLoop + class GUA_DLL MainLoop { public: diff --git a/include/gua/events/Scheduler.hpp b/include/gua/events/Scheduler.hpp index a3f6b235a..599cd032b 100644 --- a/include/gua/events/Scheduler.hpp +++ b/include/gua/events/Scheduler.hpp @@ -27,12 +27,13 @@ #include +#include #include namespace gua { namespace events { -class Scheduler { +class GUA_DLL Scheduler { public: Scheduler(); ~Scheduler(); diff --git a/include/gua/events/Ticker.hpp b/include/gua/events/Ticker.hpp index ad9ea5e29..bca76e40a 100644 --- a/include/gua/events/Ticker.hpp +++ b/include/gua/events/Ticker.hpp @@ -24,6 +24,7 @@ #include +#include #include #include #include @@ -31,7 +32,7 @@ namespace gua { namespace events { - class Ticker { + class GUA_DLL Ticker { public: Ticker(MainLoop& mainloop, double tick_time); diff --git a/include/gua/guacamole.hpp b/include/gua/guacamole.hpp index 76d42e06e..c8deb9d00 100644 --- a/include/gua/guacamole.hpp +++ b/include/gua/guacamole.hpp @@ -40,7 +40,8 @@ namespace gua { * using guacamole. */ -void init(int argc, char** argv); + void GUA_DLL init(int argc, char** argv); + } #endif // GUA_GUACAMOLE_HPP diff --git a/include/gua/math/math.hpp b/include/gua/math/math.hpp index f58f3e71f..1f95e3d04 100644 --- a/include/gua/math/math.hpp +++ b/include/gua/math/math.hpp @@ -29,6 +29,7 @@ struct aiMatrix4x4; +#include #include namespace gua { @@ -66,7 +67,7 @@ typedef scm::math::quat quat; * * \return A frustum matrix. */ -math::mat4 const compute_frustum(math::vec4 const& eye_position, +math::mat4 const GUA_DLL compute_frustum(math::vec4 const& eye_position, math::mat4 const& screen_transform, float near_plane, float far_plane); @@ -78,7 +79,7 @@ math::mat4 const compute_frustum(math::vec4 const& eye_position, * * \return A schism matrix. */ -math::mat4 const mat_ai_to_scm(aiMatrix4x4 const& ai_mat); +math::mat4 const GUA_DLL mat_ai_to_scm(aiMatrix4x4 const& ai_mat); #if WIN32 template @@ -96,10 +97,10 @@ inline math::vec3 get_translation(math::mat4 const& m) { return math::vec3(m[12], m[13], m[14]); } -std::tuple barycentric(math::vec3 const& a, - math::vec3 const& b, - math::vec3 const& c, - math::vec3 const& p); +std::tuple GUA_DLL barycentric(math::vec3 const& a, + math::vec3 const& b, + math::vec3 const& c, + math::vec3 const& p); template ValueType interpolate(math::vec3 const& position, diff --git a/include/gua/math/random.hpp b/include/gua/math/random.hpp index 9185ccbb6..f37d7d916 100644 --- a/include/gua/math/random.hpp +++ b/include/gua/math/random.hpp @@ -19,6 +19,8 @@ * * ******************************************************************************/ +#include + namespace gua { namespace math { namespace random { @@ -28,14 +30,14 @@ namespace random { * * \param seed The new seed. */ -void set_seed(unsigned int seed); +void GUA_DLL set_seed(unsigned int seed); /** * Gets the current seed. * * \return The current seed. */ -unsigned int get_seed(); +unsigned int GUA_DLL get_seed(); /** * Returns a random floating point value. @@ -45,7 +47,7 @@ unsigned int get_seed(); * * \return A random floating point value. */ -float get(float begin, float end); +float GUA_DLL get(float begin, float end); /** * Returns a random integer value. @@ -55,7 +57,7 @@ float get(float begin, float end); * * \return A random integer value. */ -int get(int begin, int end); +int GUA_DLL get(int begin, int end); } } } diff --git a/include/gua/physics/BoxShape.hpp b/include/gua/physics/BoxShape.hpp index f35952d04..4588c0010 100644 --- a/include/gua/physics/BoxShape.hpp +++ b/include/gua/physics/BoxShape.hpp @@ -23,6 +23,7 @@ #define GUA_BOX_SHAPE_HPP // guacamole headers +#include #include #include @@ -38,7 +39,7 @@ namespace physics { * length specified by half extents, in local shape coordinates. * The box shape can be used for both static and dynamic rigid bodies. */ -class BoxShape : public CollisionShape { +class GUA_DLL BoxShape : public CollisionShape { public: /** diff --git a/include/gua/physics/CollisionShape.hpp b/include/gua/physics/CollisionShape.hpp index 310338c54..6f5581aa7 100644 --- a/include/gua/physics/CollisionShape.hpp +++ b/include/gua/physics/CollisionShape.hpp @@ -22,6 +22,8 @@ #ifndef GUA_COLLISION_SHAPE_HPP #define GUA_COLLISION_SHAPE_HPP +#include + class btCollisionShape; class btCompoundShape; class btTransform; @@ -35,7 +37,7 @@ namespace physics { * This abstract class provides an interface for collision shapes that can be * shared among multiple rigid bodies. */ -class CollisionShape { +class GUA_DLL CollisionShape { friend class RigidBodyNode; public: diff --git a/include/gua/physics/CollisionShapeNode.hpp b/include/gua/physics/CollisionShapeNode.hpp index e10c3461a..ad2292d16 100644 --- a/include/gua/physics/CollisionShapeNode.hpp +++ b/include/gua/physics/CollisionShapeNode.hpp @@ -22,6 +22,7 @@ #ifndef GUA_COLLISION_SHAPE_NODE_HPP #define GUA_COLLISION_SHAPE_NODE_HPP +#include #include #include @@ -33,7 +34,7 @@ namespace gua { namespace physics { -class CollisionShapeNode : public Node { +class GUA_DLL CollisionShapeNode : public Node { public: struct Configuration { diff --git a/include/gua/physics/CollisionShapeNodeVisitor.hpp b/include/gua/physics/CollisionShapeNodeVisitor.hpp index a86b3683b..015e862ae 100644 --- a/include/gua/physics/CollisionShapeNodeVisitor.hpp +++ b/include/gua/physics/CollisionShapeNodeVisitor.hpp @@ -23,6 +23,7 @@ #define GUA_COLLISION_SHAPE_NODE_VISITOR_HPP // guacamole headers +#include #include #include #include diff --git a/include/gua/physics/Constraint.hpp b/include/gua/physics/Constraint.hpp index 6e3ae8031..9056ec7ff 100644 --- a/include/gua/physics/Constraint.hpp +++ b/include/gua/physics/Constraint.hpp @@ -49,7 +49,7 @@ class RigidBodyNode; * This abstract class is the base class for rigid body constraints. * */ -class Constraint { +class GUA_DLL Constraint { friend class Physics; public: diff --git a/include/gua/physics/ConvexHullShape.hpp b/include/gua/physics/ConvexHullShape.hpp index 5b8020c87..026947ab3 100644 --- a/include/gua/physics/ConvexHullShape.hpp +++ b/include/gua/physics/ConvexHullShape.hpp @@ -23,6 +23,7 @@ #define GUA_CONVEX_HULL_SHAPE_HPP // guacamole headers +#include #include #include #include @@ -38,7 +39,7 @@ namespace physics { * This class implements an implicit convex hull of an array of vertices. * The convex hull shape can be used for both static and dynamic rigid bodies. */ -class ConvexHullShape : public CollisionShape { +class GUA_DLL ConvexHullShape : public CollisionShape { public: /** diff --git a/include/gua/physics/CylinderShape.hpp b/include/gua/physics/CylinderShape.hpp index 32e96d741..b06f8132e 100644 --- a/include/gua/physics/CylinderShape.hpp +++ b/include/gua/physics/CylinderShape.hpp @@ -23,6 +23,7 @@ #define GUA_CYLINDER_SHAPE_HPP // guacamole headers +#include #include #include @@ -38,7 +39,7 @@ namespace physics { * aligned with Y axis of given half-extents vector. * The cylinder shape can be used for both static and dynamic rigid bodies. */ -class CylinderShape : public CollisionShape { +class GUA_DLL CylinderShape : public CollisionShape { public: /** diff --git a/include/gua/physics/FixedConstraint.hpp b/include/gua/physics/FixedConstraint.hpp index 8931bf4bf..3cfa8a433 100644 --- a/include/gua/physics/FixedConstraint.hpp +++ b/include/gua/physics/FixedConstraint.hpp @@ -22,6 +22,7 @@ #define FIXED_CONSTRAINT_HPP // guacamole headers +#include #include namespace gua { @@ -34,7 +35,7 @@ namespace physics { * The constraint restricts mutual rotations and translations between rigid * bodies or between a rigid body and static environment. */ -class FixedConstraint : public Constraint { +class GUA_DLL FixedConstraint : public Constraint { public: /** diff --git a/include/gua/physics/HingeConstraint.hpp b/include/gua/physics/HingeConstraint.hpp index 0be2dd49d..621734f28 100644 --- a/include/gua/physics/HingeConstraint.hpp +++ b/include/gua/physics/HingeConstraint.hpp @@ -23,6 +23,7 @@ #define HINGE_CONSTRAINT_HPP // guacamole headers +#include #include namespace gua { @@ -36,7 +37,7 @@ namespace physics { * a given axis. This constraint can be useful to represent doors or wheels * rotating around one axis. */ -class HingeConstraint : public Constraint { +class GUA_DLL HingeConstraint : public Constraint { public: /** diff --git a/include/gua/physics/Physics.hpp b/include/gua/physics/Physics.hpp index a7bae2a56..497308447 100644 --- a/include/gua/physics/Physics.hpp +++ b/include/gua/physics/Physics.hpp @@ -74,7 +74,7 @@ class Constraint; * rigid bodies and constraints as well as provides scene graph traversal * capability and methods to tune simulation parameters. */ -class Physics { +class GUA_DLL Physics { public: /** diff --git a/include/gua/physics/PlaneShape.hpp b/include/gua/physics/PlaneShape.hpp index 9f4806647..f811b68cb 100644 --- a/include/gua/physics/PlaneShape.hpp +++ b/include/gua/physics/PlaneShape.hpp @@ -23,6 +23,7 @@ #define GUA_PLANE_SHAPE_HPP // guacamole headers +#include #include #include @@ -36,7 +37,7 @@ namespace physics { * * The plane shape can only be used for static rigid bodies. */ -class PlaneShape : public CollisionShape { +class GUA_DLL PlaneShape : public CollisionShape { public: /** diff --git a/include/gua/physics/Point2PointConstraint.hpp b/include/gua/physics/Point2PointConstraint.hpp index 7196cf741..8366659f1 100644 --- a/include/gua/physics/Point2PointConstraint.hpp +++ b/include/gua/physics/Point2PointConstraint.hpp @@ -23,6 +23,7 @@ #define POINT_2_POINT_CONSTRAINT_HPP // guacamole headers +#include #include namespace gua { @@ -34,7 +35,7 @@ namespace physics { * * This constraint represents a ball socket joint. */ -class Point2PointConstraint : public Constraint { +class GUA_DLL Point2PointConstraint : public Constraint { public: /** diff --git a/include/gua/physics/RigidBodyNode.hpp b/include/gua/physics/RigidBodyNode.hpp index f7f9ee02a..8688c25a9 100644 --- a/include/gua/physics/RigidBodyNode.hpp +++ b/include/gua/physics/RigidBodyNode.hpp @@ -43,7 +43,7 @@ class CollisionShape; * * */ -class RigidBodyNode : public Node { +class GUA_DLL RigidBodyNode : public Node { friend class Physics; friend class CollisionShapeNodeVisitor; diff --git a/include/gua/physics/SliderConstraint.hpp b/include/gua/physics/SliderConstraint.hpp index ad1566f01..362c1e0e8 100644 --- a/include/gua/physics/SliderConstraint.hpp +++ b/include/gua/physics/SliderConstraint.hpp @@ -23,6 +23,7 @@ #define SLIDER_CONSTRAINT_HPP // guacamole headers +#include #include namespace gua { @@ -35,7 +36,7 @@ namespace physics { * The constraint allows the body to rotate around one axis and translate * along this axis. */ -class SliderConstraint : public Constraint { +class GUA_DLL SliderConstraint : public Constraint { public: /** diff --git a/include/gua/physics/SphereShape.hpp b/include/gua/physics/SphereShape.hpp index d4ddea0fa..5759304e6 100644 --- a/include/gua/physics/SphereShape.hpp +++ b/include/gua/physics/SphereShape.hpp @@ -23,6 +23,7 @@ #define GUA_SPHERE_SHAPE_HPP // guacamole headers +#include #include class btSphereShape; @@ -36,7 +37,7 @@ namespace physics { * This class is a sphere primitive around the origin with the given radius. * The sphere shape can be used for both static and dynamic rigid bodies. */ -class SphereShape : public CollisionShape { +class GUA_DLL SphereShape : public CollisionShape { public: /** diff --git a/include/gua/physics/TriangleMeshShape.hpp b/include/gua/physics/TriangleMeshShape.hpp index af8512488..da5dd0baa 100644 --- a/include/gua/physics/TriangleMeshShape.hpp +++ b/include/gua/physics/TriangleMeshShape.hpp @@ -23,6 +23,7 @@ #define GUA_TRIANGLE_SHAPE_HPP // guacamole headers +#include #include #include #include @@ -48,7 +49,7 @@ namespace physics { * For dynamic rigid bodies this class uses Hierarchical Approximate Convex * Decomposition (HACD). */ -class TriangleMeshShape : public CollisionShape { +class GUA_DLL TriangleMeshShape : public CollisionShape { public: /** diff --git a/include/gua/platform.hpp b/include/gua/platform.hpp index 101100e61..aef1ed873 100644 --- a/include/gua/platform.hpp +++ b/include/gua/platform.hpp @@ -37,34 +37,35 @@ // compiler #if defined(_MSC_VER) -#define GUA_COMPILER GUA_COMPILER_MSVC -#define GUA_COMPILER_VER _MSC_VER -#define gua_force_inline __force_inline -#define gua_align(border) __declspec(align(border)) + #define GUA_COMPILER GUA_COMPILER_MSVC + #define GUA_COMPILER_VER _MSC_VER + #define gua_force_inline __force_inline + #define gua_align(border) __declspec(align(border)) + #elif defined(__GNUC__) -#define GUA_COMPILER GUA_COMPILER_GNUC -#define GUA_COMPILER_VER \ - (((__GNUC__) * 100) + (__GNUC_MINOR__ * 10) + __GNUC_PATCHLEVEL__) -#define gua_force_inline __attribute__((always_inline)) -#define gua_align(border) __attribute__((aligned(border))) + #define GUA_COMPILER GUA_COMPILER_GNUC + #define GUA_COMPILER_VER \ + (((__GNUC__) * 100) + (__GNUC_MINOR__ * 10) + __GNUC_PATCHLEVEL__) + #define gua_force_inline __attribute__((always_inline)) + #define gua_align(border) __attribute__((aligned(border))) #else -#error "unknown compiler" + #error "unknown compiler" #endif // platform #if defined(__WIN32__) || defined(_WIN32) || defined(_WIN64) -#define GUA_PLATFORM GUA_PLATFORM_WINDOWS + #define GUA_PLATFORM GUA_PLATFORM_WINDOWS #elif defined(__APPLE_CC__) -#define GUA_PLATFORM GUA_PLATFORM_APPLE + #define GUA_PLATFORM GUA_PLATFORM_APPLE #else -#define GUA_PLATFORM GUA_PLATFORM_LINUX + #define GUA_PLATFORM GUA_PLATFORM_LINUX #endif // architecture #if defined(__x86_64__) || defined(_M_X64) -#define GUA_ARCHITECTURE_TYPE GUA_ARCHITECTURE_64 + #define GUA_ARCHITECTURE_TYPE GUA_ARCHITECTURE_64 #else -#define GUA_ARCHITECTURE_TYPE GUA_ARCHITECTURE_32 + #define GUA_ARCHITECTURE_TYPE GUA_ARCHITECTURE_32 #endif // compiler messages @@ -81,71 +82,42 @@ // windows related #ifndef GUA_STATIC_BUILD -#if GUA_PLATFORM == GUA_PLATFORM_WINDOWS - -#if GUA_COMPILER == GUA_COMPILER_MSVC - -#define __gua_export(lib) \ - export_(BOOST_PP_EXPAND(BOOST_PP_CAT(GUA_BUILD_LIBRARY_, lib))) -#ifndef export_ - #define export_(lib) \ - BOOST_PP_IF( \ - BOOST_PP_IS_NULLARY(lib), __declspec(dllexport), __declspec(dllimport)) -#endif -#define __gua_private(lib) -#else -#error "unsupported windows platform compiler" -#endif - -#ifndef NDEBUG -#define GUA_DEBUG 1 -#else -#define GUA_DEBUG 0 -#endif + #if GUA_PLATFORM == GUA_PLATFORM_WINDOWS + #if GUA_COMPILER == GUA_COMPILER_MSVC + #pragma warning(disable: 4251) // needs to have dll-interface to be used by clients of class + #pragma warning(disable: 4275) // non dll-interface class used as base for dll-interface class + + #if defined(GUA_LIBRARY) + #define GUA_DLL __declspec( dllexport ) + #else + #define GUA_DLL __declspec( dllimport ) + #endif + #endif + #endif +#else + #define GUA_DLL #endif // Linux, Apple #if GUA_PLATFORM == GUA_PLATFORM_LINUX || GUA_PLATFORM == GUA_PLATFORM_APPLE -#if GUA_COMPILER_VER >= 400 -// gcc 4.x attribute visibility -#define __gua_export(lib) __attribute__((visibility("default"))) -#define __gua_private(lib) __attribute__((visibility("hidden"))) -#else -#define __gua_export(lib) -#define __gua_private(lib) -#endif - -#ifndef NDEBUG -#define GUA_DEBUG 1 -#else -#define GUA_DEBUG 0 -#endif + #define GUA_DLL #endif -#else // GUA_STATIC_BUILD -#define __gua_export(lib) -#define __gua_private(lib) -#endif //GUA_STATIC_BUILD - #if GUA_PLATFORM == GUA_PLATFORM_WINDOWS - -#ifndef NDEBUG -#define GUA_DEBUG 1 -#else -#define GUA_DEBUG 0 -#endif - + #ifndef NDEBUG + #define GUA_DEBUG 1 + #else + #define GUA_DEBUG 0 + #endif #endif // Linux, Apple #if GUA_PLATFORM == GUA_PLATFORM_LINUX || GUA_PLATFORM == GUA_PLATFORM_APPLE - -#ifndef NDEBUG -#define GUA_DEBUG 1 -#else -#define GUA_DEBUG 0 -#endif - + #ifndef NDEBUG + #define GUA_DEBUG 1 + #else + #define GUA_DEBUG 0 + #endif #endif //#endif // namespace GUA_PLATFORM_HPP diff --git a/include/gua/renderer/BufferDescriptions.hpp b/include/gua/renderer/BufferDescriptions.hpp index af79364f8..07c0b54a5 100644 --- a/include/gua/renderer/BufferDescriptions.hpp +++ b/include/gua/renderer/BufferDescriptions.hpp @@ -22,6 +22,8 @@ #ifndef GUA_BUFFER_DESCRIPTIONS_HPP #define GUA_BUFFER_DESCRIPTIONS_HPP +#include + // external headers #include @@ -32,7 +34,7 @@ namespace gua { * * This struct is used to create new color buffers in render passes. */ -struct ColorBufferDescription { +struct GUA_DLL ColorBufferDescription { ColorBufferDescription(std::string const& name, unsigned location, scm::gl::data_format format = diff --git a/include/gua/renderer/BuiltInTextures.hpp b/include/gua/renderer/BuiltInTextures.hpp index 013c36bc1..0846f0c89 100644 --- a/include/gua/renderer/BuiltInTextures.hpp +++ b/include/gua/renderer/BuiltInTextures.hpp @@ -23,11 +23,12 @@ #define GUA_NOISE_TEXTURE_HPP // guacamole headers +#include #include namespace gua { -class NoiseTexture : public Texture2D { +class GUA_DLL NoiseTexture : public Texture2D { public: NoiseTexture(); @@ -36,7 +37,7 @@ class NoiseTexture : public Texture2D { static unsigned char pixel_data[64 * 64 * 3 + 1]; }; -class DefaultTexture : public Texture2D { +class GUA_DLL DefaultTexture : public Texture2D { public: DefaultTexture(); diff --git a/include/gua/renderer/Camera.hpp b/include/gua/renderer/Camera.hpp index 64846e619..7636ebe1c 100644 --- a/include/gua/renderer/Camera.hpp +++ b/include/gua/renderer/Camera.hpp @@ -22,6 +22,8 @@ #ifndef GUA_CAMERA_HPP #define GUA_CAMERA_HPP +#include + // external headers #include @@ -33,7 +35,7 @@ namespace gua { * It is defined by a screen, a view point a a render mask. */ -struct Camera { +struct GUA_DLL Camera { Camera(std::string const& eye_l = "unknown_left_eye", std::string const& eye_r = "unknown_right_eye", std::string const& screen_l = "unknown_left_screen", diff --git a/include/gua/renderer/DisplayData.hpp b/include/gua/renderer/DisplayData.hpp index 17b14f913..5c763d66e 100644 --- a/include/gua/renderer/DisplayData.hpp +++ b/include/gua/renderer/DisplayData.hpp @@ -22,6 +22,7 @@ #ifndef GUA_DISPLAY_DATA_HPP #define GUA_DISPLAY_DATA_HPP +#include #include namespace gua { @@ -30,7 +31,7 @@ namespace gua { * Temporary class that holds data for the PostFXPass. */ -class DisplayData { +class GUA_DLL DisplayData { public: void set_physics_fps(float fps); diff --git a/include/gua/renderer/FrameBufferObject.hpp b/include/gua/renderer/FrameBufferObject.hpp index b92a7ec75..3587c02c4 100644 --- a/include/gua/renderer/FrameBufferObject.hpp +++ b/include/gua/renderer/FrameBufferObject.hpp @@ -28,11 +28,8 @@ #include // external headers -#if GUA_COMPILER == GUA_COMPILER_MSVC&& SCM_COMPILER_VER <= 1700 -#include -#else #include -#endif + #include @@ -46,7 +43,7 @@ struct RenderContext; * This class allows to bind textures to a FBO. This FBO * can be used as drawing target for an rendering context. */ -class FrameBufferObject { +class GUA_DLL FrameBufferObject { public: /** diff --git a/include/gua/renderer/Geometry.hpp b/include/gua/renderer/Geometry.hpp index 2fc714d2c..754f01a89 100644 --- a/include/gua/renderer/Geometry.hpp +++ b/include/gua/renderer/Geometry.hpp @@ -23,6 +23,7 @@ #define GUA_GEOMETRY_HPP // guacamole_headers +#include #include #include #include @@ -39,7 +40,7 @@ struct Ray; /** * Base class for different geometries. */ -class Geometry { +class GUA_DLL Geometry { public: /** diff --git a/include/gua/renderer/GeometryLoader.hpp b/include/gua/renderer/GeometryLoader.hpp index 2e065f470..74c4e5b95 100644 --- a/include/gua/renderer/GeometryLoader.hpp +++ b/include/gua/renderer/GeometryLoader.hpp @@ -38,7 +38,7 @@ class Node; * This DataBase stores geometry data. It can be accessed via string * identifiers. */ -class GeometryLoader { +class GUA_DLL GeometryLoader { public: enum Flags { diff --git a/include/gua/renderer/Material.hpp b/include/gua/renderer/Material.hpp index 160c497cd..7a238100d 100644 --- a/include/gua/renderer/Material.hpp +++ b/include/gua/renderer/Material.hpp @@ -25,6 +25,7 @@ // guacamole headers #include #include +#include // external headers #include diff --git a/include/gua/renderer/MaterialLoader.hpp b/include/gua/renderer/MaterialLoader.hpp index 306861093..e19abfcc1 100644 --- a/include/gua/renderer/MaterialLoader.hpp +++ b/include/gua/renderer/MaterialLoader.hpp @@ -23,6 +23,7 @@ #define GUA_MATERIAL_LOADER_HPP // guacamole headers +#include #include // external headers @@ -52,7 +53,7 @@ class GeometryNode; * This class can load mesh data from files and display them in multiple * contexts. A MaterialLoader object is made of several Mesh objects. */ -class MaterialLoader { +class GUA_DLL MaterialLoader { public: enum ShadingCapabilities { diff --git a/include/gua/renderer/NURBS.hpp b/include/gua/renderer/NURBS.hpp index 7d183d43f..7e5610f96 100644 --- a/include/gua/renderer/NURBS.hpp +++ b/include/gua/renderer/NURBS.hpp @@ -23,6 +23,7 @@ #define GUA_NURBS_HPP_INCLUDED // guacamole headers +#include #include #include #include @@ -46,7 +47,7 @@ namespace gua { -class NURBS : public Geometry { +class GUA_DLL NURBS : public Geometry { public: NURBS(std::shared_ptr const& object, diff --git a/include/gua/renderer/Pipeline.hpp b/include/gua/renderer/Pipeline.hpp index bb64670c5..8fb1f6791 100644 --- a/include/gua/renderer/Pipeline.hpp +++ b/include/gua/renderer/Pipeline.hpp @@ -23,6 +23,7 @@ #define GUA_PIPELINE_HPP // guacamole headers +#include #include #include #include @@ -125,7 +126,7 @@ struct PipelineConfiguration { * of (or the entire) SceneGraph to buffers. These buffers may be used as input * for other passes. One final buffer of a final pass is shown on the screen. */ -class Pipeline { +class GUA_DLL Pipeline { public: /** @@ -162,6 +163,7 @@ class Pipeline { friend class GBufferPass; friend class LightingPass; friend class FinalPass; + friend class CompositePass; friend class PostFXPass; friend class GeometryPass; friend class FullscreenPass; @@ -177,11 +179,7 @@ class Pipeline { SerializedScene const& get_current_scene(CameraMode mode) const; inline SceneGraph const* get_current_graph() const { return current_graph_; } -#if GUA_COMPILER == GUA_COMPILER_MSVC&& GUA_COMPILER_VER <= 1600 - mutable boost::mutex upload_mutex_; -#else mutable std::mutex upload_mutex_; -#endif Window* window_; RenderContext* context_; diff --git a/include/gua/renderer/Renderer.hpp b/include/gua/renderer/Renderer.hpp index 5a0edda31..65e8d8db5 100644 --- a/include/gua/renderer/Renderer.hpp +++ b/include/gua/renderer/Renderer.hpp @@ -22,13 +22,12 @@ #ifndef GUA_RENDERER_HPP #define GUA_RENDERER_HPP -#include - // external headers #include #include #include +#include #include #include @@ -42,7 +41,7 @@ class Pipeline; * * This class is used to provide a renderer frontend interface to the user. */ -class Renderer { +class GUA_DLL Renderer { public: typedef std::vector > render_vec_t; typedef render_vec_t const const_render_vec_t; @@ -57,6 +56,11 @@ class Renderer { */ Renderer(std::vector const& pipelines); + /** + * + */ + ~Renderer(); + /** * Request a redraw of all RenderClients. * @@ -68,7 +72,7 @@ class Renderer { private: typedef RenderClient > renderclient_t; - std::vector > render_clients_; + std::vector render_clients_; FpsCounter application_fps_; }; diff --git a/include/gua/renderer/ShaderProgram.hpp b/include/gua/renderer/ShaderProgram.hpp index e489c41a2..facd5e601 100644 --- a/include/gua/renderer/ShaderProgram.hpp +++ b/include/gua/renderer/ShaderProgram.hpp @@ -28,11 +28,7 @@ #include // external headers -#if GUA_COMPILER == GUA_COMPILER_MSVC&& SCM_COMPILER_VER <= 1700 -#include -#else #include -#endif #include #include diff --git a/include/gua/renderer/TextRenderer.hpp b/include/gua/renderer/TextRenderer.hpp index b39e30c06..364a86c41 100644 --- a/include/gua/renderer/TextRenderer.hpp +++ b/include/gua/renderer/TextRenderer.hpp @@ -23,6 +23,7 @@ #define GUA_TEXT_RENDERER_HPP // guacamole headers +#include #include // external headers @@ -39,7 +40,7 @@ class StereoBuffer; * * It can write some predefined strings and information on a given FBO. */ -class TextRenderer { +class GUA_DLL TextRenderer { public: /** diff --git a/include/gua/renderer/Texture.hpp b/include/gua/renderer/Texture.hpp index cfadf013c..1ace0dfbd 100644 --- a/include/gua/renderer/Texture.hpp +++ b/include/gua/renderer/Texture.hpp @@ -42,7 +42,7 @@ namespace gua { * This class allows to load texture data from a file and bind the * texture to an OpenGL context. */ -class Texture { +class GUA_DLL Texture { public: /** diff --git a/include/gua/renderer/Texture2D.hpp b/include/gua/renderer/Texture2D.hpp index c2d428ae6..b98c578e3 100644 --- a/include/gua/renderer/Texture2D.hpp +++ b/include/gua/renderer/Texture2D.hpp @@ -33,12 +33,8 @@ #include #include -#if GUA_COMPILER == GUA_COMPILER_MSVC&& GUA_COMPILER_VER <= 1600 -#include -#else #include #include -#endif namespace gua { @@ -48,7 +44,7 @@ namespace gua { * This class allows to load texture data from a file and bind the * texture to an OpenGL context. */ -class Texture2D : public Texture { + class GUA_DLL Texture2D : public Texture { public: /** diff --git a/include/gua/renderer/Texture3D.hpp b/include/gua/renderer/Texture3D.hpp index 205c7e439..e774a106d 100644 --- a/include/gua/renderer/Texture3D.hpp +++ b/include/gua/renderer/Texture3D.hpp @@ -33,12 +33,9 @@ #include #include -#if GUA_COMPILER == GUA_COMPILER_MSVC&& GUA_COMPILER_VER <= 1600 -#include -#else #include #include -#endif + namespace gua { @@ -48,7 +45,7 @@ namespace gua { * This class allows to load texture data from a file and bind the * texture to an OpenGL context. */ -class Texture3D : public Texture { +class GUA_DLL Texture3D : public Texture { public: /** diff --git a/include/gua/renderer/WarpMatrix.hpp b/include/gua/renderer/WarpMatrix.hpp index bf2add5dc..7dcc3a03a 100644 --- a/include/gua/renderer/WarpMatrix.hpp +++ b/include/gua/renderer/WarpMatrix.hpp @@ -23,6 +23,7 @@ #define GUA_WARP_MATRIX_HPP // guacamole headers +#include #include namespace gua { @@ -34,7 +35,7 @@ namespace gua { * when the projecting beamers for the colors red, green and blue don't * overlap exactly. */ -class WarpMatrix : public Texture2D { +class GUA_DLL WarpMatrix : public Texture2D { public: /** diff --git a/include/gua/renderer/Window.hpp b/include/gua/renderer/Window.hpp index 3bb944756..52d6d8178 100644 --- a/include/gua/renderer/Window.hpp +++ b/include/gua/renderer/Window.hpp @@ -23,6 +23,7 @@ #define GUA_WINDOW_HPP // guacamole headers +#include #include #include #include @@ -46,7 +47,7 @@ class StereoBuffer; * * It's a window which can display OpenGL stuff. */ -class Window { +class GUA_DLL Window { public: enum TextureDisplayMode { diff --git a/include/gua/scenegraph/GeometryNode.hpp b/include/gua/scenegraph/GeometryNode.hpp index 1e1717c61..41e253f8f 100644 --- a/include/gua/scenegraph/GeometryNode.hpp +++ b/include/gua/scenegraph/GeometryNode.hpp @@ -36,7 +36,7 @@ namespace gua { -class GeometryNode : public Node { +class GUA_DLL GeometryNode : public Node { public: struct Configuration { diff --git a/include/gua/scenegraph/Node.hpp b/include/gua/scenegraph/Node.hpp index 7f0383463..8284ad970 100644 --- a/include/gua/scenegraph/Node.hpp +++ b/include/gua/scenegraph/Node.hpp @@ -23,6 +23,7 @@ #define GUA_NODE_HPP // guacamole headers +#include #include #include #include @@ -56,7 +57,7 @@ class RayNode; namespace physics { class CollisionShapeNodeVisitor; } -class Node { +class GUA_DLL Node { public: /** diff --git a/include/gua/scenegraph/PickResult.hpp b/include/gua/scenegraph/PickResult.hpp index b37baa6db..d73fe960c 100644 --- a/include/gua/scenegraph/PickResult.hpp +++ b/include/gua/scenegraph/PickResult.hpp @@ -23,6 +23,7 @@ #define GUA_PICK_RESULT_HPP // guacamole headers +#include #include @@ -30,7 +31,7 @@ namespace gua { class Node; -struct PickResult { +struct GUA_DLL PickResult { enum Options { PICK_ALL = 0, PICK_ONLY_FIRST_OBJECT = 1<<1, diff --git a/include/gua/scenegraph/PointLightNode.hpp b/include/gua/scenegraph/PointLightNode.hpp index d40203be7..b1b7dff95 100644 --- a/include/gua/scenegraph/PointLightNode.hpp +++ b/include/gua/scenegraph/PointLightNode.hpp @@ -22,6 +22,7 @@ #ifndef GUA_POINT_LIGHT_NODE_HPP #define GUA_POINT_LIGHT_NODE_HPP +#include #include #include @@ -36,7 +37,7 @@ namespace gua { -class PointLightNode : public Node { +class GUA_DLL PointLightNode : public Node { public: struct Configuration { diff --git a/include/gua/scenegraph/SceneGraph.hpp b/include/gua/scenegraph/SceneGraph.hpp index 083d5d0a0..56a90bcc8 100644 --- a/include/gua/scenegraph/SceneGraph.hpp +++ b/include/gua/scenegraph/SceneGraph.hpp @@ -22,6 +22,7 @@ #ifndef GUA_SCENE_GRAPH_HPP #define GUA_SCENE_GRAPH_HPP +#include #include #include #include @@ -42,7 +43,7 @@ class RayNode; * all its contents. It provides an interface to set up and have access to * a graph consisting of several Nodes in order to build a scene abstraction. */ -class SceneGraph { +class GUA_DLL SceneGraph { public: diff --git a/include/gua/scenegraph/ScreenNode.hpp b/include/gua/scenegraph/ScreenNode.hpp index ca68aaa50..198020eac 100644 --- a/include/gua/scenegraph/ScreenNode.hpp +++ b/include/gua/scenegraph/ScreenNode.hpp @@ -22,6 +22,7 @@ #ifndef GUA_SCREEN_CORE_HPP #define GUA_SCREEN_CORE_HPP +#include #include #include @@ -32,7 +33,7 @@ namespace gua { -class ScreenNode : public Node { +class GUA_DLL ScreenNode : public Node { public: struct Configuration { diff --git a/include/gua/scenegraph/SpotLightNode.hpp b/include/gua/scenegraph/SpotLightNode.hpp index 3b498acf2..1b25510ee 100644 --- a/include/gua/scenegraph/SpotLightNode.hpp +++ b/include/gua/scenegraph/SpotLightNode.hpp @@ -22,6 +22,7 @@ #ifndef GUA_SPOT_LIGHT_NODE_HPP #define GUA_SPOT_LIGHT_NODE_HPP +#include #include #include @@ -36,7 +37,7 @@ namespace gua { -class SpotLightNode : public Node { +class GUA_DLL SpotLightNode : public Node { public: struct Configuration { diff --git a/include/gua/scenegraph/TexturedQuadNode.hpp b/include/gua/scenegraph/TexturedQuadNode.hpp index cc58e1ece..9b5fb5748 100644 --- a/include/gua/scenegraph/TexturedQuadNode.hpp +++ b/include/gua/scenegraph/TexturedQuadNode.hpp @@ -32,7 +32,7 @@ namespace gua { -class TexturedQuadNode : public Node { +class GUA_DLL TexturedQuadNode : public Node { public: struct Configuration { diff --git a/include/gua/scenegraph/TransformNode.hpp b/include/gua/scenegraph/TransformNode.hpp index b6e9713a3..ebdd566ed 100644 --- a/include/gua/scenegraph/TransformNode.hpp +++ b/include/gua/scenegraph/TransformNode.hpp @@ -22,6 +22,7 @@ #ifndef GUA_GROUP_NODE_HPP #define GUA_GROUP_NODE_HPP +#include #include /** @@ -31,7 +32,7 @@ namespace gua { -class TransformNode : public Node { +class GUA_DLL TransformNode : public Node { public: TransformNode() {}; diff --git a/include/gua/utils/AnimatedValue.hpp b/include/gua/utils/AnimatedValue.hpp index f5a10815f..044ce15ef 100644 --- a/include/gua/utils/AnimatedValue.hpp +++ b/include/gua/utils/AnimatedValue.hpp @@ -22,13 +22,15 @@ #ifndef GUA_ANIMATEDVALUE_HPP #define GUA_ANIMATEDVALUE_HPP +#include + namespace gua { namespace utils { /** * A class for smooth value interpolation. */ -class AnimatedValue { +class GUA_DLL AnimatedValue { public: enum Direction { In, diff --git a/include/gua/utils/Color3f.hpp b/include/gua/utils/Color3f.hpp index 969034d2a..b51f48fb4 100644 --- a/include/gua/utils/Color3f.hpp +++ b/include/gua/utils/Color3f.hpp @@ -22,6 +22,7 @@ #ifndef COLOR_3F_HPP #define COLOR_3F_HPP +#include #include namespace gua { @@ -34,7 +35,7 @@ namespace utils { * interface as well. */ -struct Color3f { +struct GUA_DLL Color3f { public: /** diff --git a/include/gua/utils/Directory.hpp b/include/gua/utils/Directory.hpp index b15cc3a7f..d92c8db1c 100644 --- a/include/gua/utils/Directory.hpp +++ b/include/gua/utils/Directory.hpp @@ -22,6 +22,8 @@ #ifndef GUA_DIRECTORY_HPP #define GUA_DIRECTORY_HPP +#include + #include /** @@ -30,7 +32,7 @@ namespace gua { -class Directory { +class GUA_DLL Directory { public: diff --git a/include/gua/utils/DotGenerator.hpp b/include/gua/utils/DotGenerator.hpp index 52fa4b235..ad55b2136 100644 --- a/include/gua/utils/DotGenerator.hpp +++ b/include/gua/utils/DotGenerator.hpp @@ -27,6 +27,7 @@ #include // guacamole header +#include #include namespace gua { @@ -42,7 +43,7 @@ class SpotLightNode; /** * This class may be used to parse a path. */ -class DotGenerator : public NodeVisitor { +class GUA_DLL DotGenerator : public NodeVisitor { public: DotGenerator(); diff --git a/include/gua/utils/FpsCounter.hpp b/include/gua/utils/FpsCounter.hpp index 841a48b1d..94609dde4 100644 --- a/include/gua/utils/FpsCounter.hpp +++ b/include/gua/utils/FpsCounter.hpp @@ -22,11 +22,12 @@ #ifndef GUA_FPS_COUNTER_HPP #define GUA_FPS_COUNTER_HPP +#include #include namespace gua { -struct FpsCounter +struct GUA_DLL FpsCounter { FpsCounter(unsigned t) : fps(0.0f), frame_count(0), timer(), delay(t) {} void step() { diff --git a/include/gua/utils/Mask.hpp b/include/gua/utils/Mask.hpp index 020926822..8e4f8e20b 100644 --- a/include/gua/utils/Mask.hpp +++ b/include/gua/utils/Mask.hpp @@ -27,6 +27,8 @@ #include #include +#include + namespace gua { /** @@ -43,7 +45,7 @@ namespace gua { * ) --- closing parenthesis * Whitespaces are ignored and all other characters are treated as group names. */ -class Mask { +class GUA_DLL Mask { public: /** diff --git a/include/gua/utils/Profiler.hpp b/include/gua/utils/Profiler.hpp index 6598d621f..63681a6df 100644 --- a/include/gua/utils/Profiler.hpp +++ b/include/gua/utils/Profiler.hpp @@ -23,6 +23,7 @@ #define GUA_PROFILER_HPP // guacamole headers +#include #include // external headers @@ -37,7 +38,7 @@ namespace gua { * * */ -class Profiler { +class GUA_DLL Profiler { public: class Timer { diff --git a/include/gua/utils/Timer.hpp b/include/gua/utils/Timer.hpp index 0edd52c4f..f092f5d55 100644 --- a/include/gua/utils/Timer.hpp +++ b/include/gua/utils/Timer.hpp @@ -24,13 +24,15 @@ #include +#include + /** * */ namespace gua { -class Timer { +class GUA_DLL Timer { public: void start(); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d1ce0dc64..854b1ee17 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -8,17 +8,18 @@ file(GLOB_RECURSE GUACAMOLE_SRC RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} LINK_DIRECTORIES(${LIB_PATHS}) -IF (UNIX) +INCLUDE_DIRECTORIES(guacamole ${INCLUDE_PATHS}) + ADD_LIBRARY( guacamole SHARED ${GUACAMOLE_SRC} ) -ELSEIF (WIN32) -ADD_LIBRARY( guacamole STATIC - ${GUACAMOLE_SRC} -) -ENDIF(UNIX) -INCLUDE_DIRECTORIES(guacamole ${INCLUDE_PATHS}) +MESSAGE(${CMAKE_SYSTEM_VERSION}) + +IF (MSVC) + GET_WIN32_WINNT(WIN_VERSION) + SET_TARGET_PROPERTIES(guacamole PROPERTIES COMPILE_FLAGS "-D GUA_LIBRARY -D _WIN32_WINNT=${WIN_VERSION}") +ENDIF(MSVC) TARGET_LINK_LIBRARIES( guacamole debug ${LIBS} optimized ${LIBS}) diff --git a/src/gua/renderer/Renderer.cpp b/src/gua/renderer/Renderer.cpp index aff23f56d..623c19638 100644 --- a/src/gua/renderer/Renderer.cpp +++ b/src/gua/renderer/Renderer.cpp @@ -42,6 +42,12 @@ std::shared_ptr garbage_collected_copy( return sgs; } +Renderer::~Renderer() { + for (auto rc : render_clients_) { + if (rc) delete rc; + } +} + Renderer::Renderer(std::vector const& pipelines) : render_clients_(), application_fps_(20) { @@ -52,7 +58,7 @@ Renderer::Renderer(std::vector const& pipelines) pipeline->process(*sg, this->application_fps_.fps, render_fps); }; - render_clients_.push_back(gua::make_unique(fun)); + render_clients_.push_back(new renderclient_t(fun)); } } From 5ab3c05cc0c0f3aca45521023e9e47d51d39aee5 Mon Sep 17 00:00:00 2001 From: Andre Schollmeyer Date: Mon, 25 Nov 2013 17:22:43 +0100 Subject: [PATCH 040/146] - added composite stage - introduced enums for pipeline stages --- cmake/modules/find_compiler.cmake | 5 +- include/gua/renderer/CompositePass.hpp | 76 +++++++++++++++++++++ include/gua/renderer/Pipeline.hpp | 9 +++ include/gua/renderer/Renderer.hpp | 7 +- src/gua/renderer/Pipeline.cpp | 93 ++++++++++++-------------- src/gua/renderer/PostFXPass.cpp | 10 +-- src/gua/renderer/Renderer.cpp | 7 +- 7 files changed, 147 insertions(+), 60 deletions(-) create mode 100644 include/gua/renderer/CompositePass.hpp diff --git a/cmake/modules/find_compiler.cmake b/cmake/modules/find_compiler.cmake index 41bb52082..d8d0e3c65 100644 --- a/cmake/modules/find_compiler.cmake +++ b/cmake/modules/find_compiler.cmake @@ -24,15 +24,14 @@ if (WIN32) endif(MSVC12) endif (WIN32) -macro(get_WIN32_WINNT version) +macro(GET_WIN32_WINNT version) if (WIN32 AND CMAKE_SYSTEM_VERSION) set(ver ${CMAKE_SYSTEM_VERSION}) string(REPLACE "." "" ver ${ver}) string(REGEX REPLACE "([0-9])" "0\\1" ver ${ver}) - set(${version} "0x${ver}") endif() -endmacro() +endmacro(GET_WIN32_WINNT) if (UNIX) if (CMAKE_COMPILER_IS_GNUCXX) diff --git a/include/gua/renderer/CompositePass.hpp b/include/gua/renderer/CompositePass.hpp new file mode 100644 index 000000000..f28529985 --- /dev/null +++ b/include/gua/renderer/CompositePass.hpp @@ -0,0 +1,76 @@ +/****************************************************************************** + * guacamole - delicious VR * + * * + * Copyright: (c) 2011-2013 Bauhaus-Universität Weimar * + * Contact: felix.lauer@uni-weimar.de / simon.schneegans@uni-weimar.de * + * * + * This program is free software: you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the Free * + * Software Foundation, either version 3 of the License, or (at your option) * + * any later version. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * + * for more details. * + * * + * You should have received a copy of the GNU General Public License along * + * with this program. If not, see . * + * * + ******************************************************************************/ + +#ifndef GUA_COMPOSITE_PASS_HPP +#define GUA_COMPOSITE_PASS_HPP + +// guacamole headers +#include +#include + +namespace gua { + +class GBuffer; +struct PipelineConfiguration; + +/** + * + */ +class CompositePass : public Pass { + public: + + /** + * + */ + CompositePass(Pipeline* pipeline); + + /** + * + */ + virtual ~CompositePass(); + + void create( + RenderContext const& ctx, + PipelineConfiguration const& config, + std::vector > const& layers); + + void render_scene(Camera const& camera, RenderContext const& ctx); + + /* virtual */ LayerMapping const* get_gbuffer_mapping() const; + + void print_shaders(std::string const& directory, + std::string const& name) const; + + bool pre_compile_shaders(RenderContext const& ctx); + + private: + + void apply_material_mapping(); + + scm::gl::depth_stencil_state_ptr depth_stencil_state_; + scm::gl::quad_geometry_ptr fullscreen_quad_; + ShaderProgram* composite_shader_; +}; + +} + +#endif // GUA_COMPOSITE_PASS_HPP diff --git a/include/gua/renderer/Pipeline.hpp b/include/gua/renderer/Pipeline.hpp index 8fb1f6791..b01ecfc6b 100644 --- a/include/gua/renderer/Pipeline.hpp +++ b/include/gua/renderer/Pipeline.hpp @@ -127,6 +127,15 @@ struct PipelineConfiguration { * for other passes. One final buffer of a final pass is shown on the screen. */ class GUA_DLL Pipeline { + public: + + enum PipelineStage { geometry = 0, + lighting = 1, + shading = 2, + compositing = 3, + postfx = 4 + }; + public: /** diff --git a/include/gua/renderer/Renderer.hpp b/include/gua/renderer/Renderer.hpp index 65e8d8db5..d8e1ecb17 100644 --- a/include/gua/renderer/Renderer.hpp +++ b/include/gua/renderer/Renderer.hpp @@ -31,6 +31,8 @@ #include #include +#define USE_RAW_POINTER_RENDER_CLIENTS 1 + namespace gua { class SceneGraph; @@ -72,8 +74,11 @@ class GUA_DLL Renderer { private: typedef RenderClient > renderclient_t; +#if USE_RAW_POINTER_RENDER_CLIENTS std::vector render_clients_; - +#else + std::vector > render_clients_; +#endif FpsCounter application_fps_; }; diff --git a/src/gua/renderer/Pipeline.cpp b/src/gua/renderer/Pipeline.cpp index 637119d5c..d4f0bb7fa 100644 --- a/src/gua/renderer/Pipeline.cpp +++ b/src/gua/renderer/Pipeline.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -76,10 +77,11 @@ void Pipeline::print_shaders(std::string const& directory) const { std::unique_lock lock(upload_mutex_); - passes_[0]->print_shaders(directory, "/0_gbuffer"); - passes_[1]->print_shaders(directory, "/1_lighting"); - passes_[2]->print_shaders(directory, "/2_final"); - passes_[3]->print_shaders(directory, "/3_postFX"); + passes_[PipelineStage::geometry]->print_shaders(directory, "/0_gbuffer"); + passes_[PipelineStage::lighting]->print_shaders(directory, "/1_lighting"); + passes_[PipelineStage::shading]->print_shaders(directory, "/2_final"); + passes_[PipelineStage::compositing]->print_shaders(directory, "/3_composite"); + passes_[PipelineStage::postfx]->print_shaders(directory, "/4_postFX"); } //////////////////////////////////////////////////////////////////////////////// @@ -247,18 +249,18 @@ void Pipeline::process(std::vector> const& sce config.enable_frustum_culling()); } - for (int i(0); i < passes_.size(); ++i) { - passes_[i]->render_scene(config.camera(), *context_); + for (auto pass : passes_) { + pass->render_scene(config.camera(), *context_); } if (window_) { if (config.get_enable_stereo()) { - window_->display(passes_[3]->get_gbuffer()->get_eye_buffers()[0] + window_->display(passes_[PipelineStage::postfx]->get_gbuffer()->get_eye_buffers()[0] ->get_color_buffers(TYPE_FLOAT)[0], - passes_[3]->get_gbuffer()->get_eye_buffers()[1] + passes_[PipelineStage::postfx]->get_gbuffer()->get_eye_buffers()[1] ->get_color_buffers(TYPE_FLOAT)[0]); } else { - window_->display(passes_[3]->get_gbuffer()->get_eye_buffers()[0] + window_->display(passes_[PipelineStage::postfx]->get_gbuffer()->get_eye_buffers()[0] ->get_color_buffers(TYPE_FLOAT)[0]); } @@ -282,7 +284,6 @@ void Pipeline::create_passes() { if (passes_need_reload_) { - auto materials(MaterialDatabase::instance()->list_all()); auto pre_pass = new GBufferPass(this); @@ -299,37 +300,40 @@ void Pipeline::create_passes() { auto final_pass = new FinalPass(this); final_pass->apply_material_mapping(materials, layer_mapping); + auto composite_pass = new CompositePass(this); + auto post_fx_pass = new PostFXPass(this); - bool compilation_succeeded = false; + bool compilation_succeeded = true; passes_need_reload_ = false; // try compilation if context is already present if (context_) { - if (pre_pass->pre_compile_shaders(*context_)) - if (light_pass->pre_compile_shaders(*context_)) - if (final_pass->pre_compile_shaders(*context_)) - if (post_fx_pass->pre_compile_shaders(*context_)) + for (auto pass : passes_) { - compilation_succeeded = true; + if (!pass->pre_compile_shaders(*context_)) { + compilation_succeeded = false; + } + } } else { compilation_succeeded = true; } if (compilation_succeeded) { - for (int i(0); i < passes_.size(); ++i) { - delete passes_[i]; + for (auto pass : passes_) { + delete pass; } - + passes_.clear(); passes_.push_back(pre_pass); passes_.push_back(light_pass); passes_.push_back(final_pass); + passes_.push_back(composite_pass); passes_.push_back(post_fx_pass); buffers_need_reload_ = true; @@ -339,6 +343,7 @@ void Pipeline::create_passes() { delete pre_pass; delete light_pass; delete final_pass; + delete composite_pass; delete post_fx_pass; } } @@ -349,48 +354,36 @@ void Pipeline::create_passes() { void Pipeline::create_buffers() { if (buffers_need_reload_) { - passes_[0]->create(*context_, config, - passes_[0]->get_gbuffer_mapping()->get_layers()); - - passes_[1]->create(*context_, config, - passes_[1]->get_gbuffer_mapping()->get_layers()); - + std::vector> stereobuffers; - stereobuffers.push_back(passes_[0]->get_gbuffer()); - - passes_[1]->set_inputs(stereobuffers); - stereobuffers.push_back(passes_[1]->get_gbuffer()); + passes_[PipelineStage::geometry]->create(*context_, config, passes_[PipelineStage::geometry]->get_gbuffer_mapping()->get_layers()); + stereobuffers.push_back(passes_[PipelineStage::geometry]->get_gbuffer()); - passes_[2]->create(*context_, - config, - passes_[2]->get_gbuffer_mapping()->get_layers()); + passes_[PipelineStage::lighting]->create(*context_, config, passes_[PipelineStage::lighting]->get_gbuffer_mapping()->get_layers()); + passes_[PipelineStage::lighting]->set_inputs(stereobuffers); + stereobuffers.push_back(passes_[PipelineStage::lighting]->get_gbuffer()); - passes_[2]->set_inputs(stereobuffers); + passes_[PipelineStage::shading]->create(*context_, config, passes_[PipelineStage::shading]->get_gbuffer_mapping()->get_layers()); + passes_[PipelineStage::shading]->set_inputs(stereobuffers); + stereobuffers.push_back(passes_[PipelineStage::shading]->get_gbuffer()); - scm::gl::sampler_state_desc state(scm::gl::FILTER_MIN_MAG_LINEAR, + scm::gl::sampler_state_desc state(scm::gl::FILTER_MIN_MAG_LINEAR, scm::gl::WRAP_REPEAT, scm::gl::WRAP_REPEAT); - #if GUA_COMPILER == GUA_COMPILER_MSVC&& SCM_COMPILER_VER <= 1700 - std::vector > - layer_desc; - layer_desc.push_back(std::make_pair(BufferComponent::F3, state)); - passes_[3]->create(*context_, config, layer_desc); - #else - passes_[3]->create(*context_, config, - { - { BufferComponent::F3, state } - }); - #endif - stereobuffers.push_back(passes_[2]->get_gbuffer()); - passes_[3]->set_inputs(stereobuffers); + passes_[PipelineStage::compositing]->create(*context_, config, { { BufferComponent::F3, state } }); + passes_[PipelineStage::compositing]->set_inputs(stereobuffers); + stereobuffers.push_back(passes_[PipelineStage::compositing]->get_gbuffer()); + + passes_[PipelineStage::postfx]->create(*context_, config, { { BufferComponent::F3, state } }); + passes_[PipelineStage::postfx]->set_inputs(stereobuffers); if (!config.get_enable_stereo()) { - TextureDatabase::instance()->add(config.output_texture_name(), passes_[3]->get_gbuffer()->get_eye_buffers()[0]->get_color_buffers(TYPE_FLOAT)[0]); + TextureDatabase::instance()->add(config.output_texture_name(), passes_[PipelineStage::postfx]->get_gbuffer()->get_eye_buffers()[0]->get_color_buffers(TYPE_FLOAT)[0]); } else { - TextureDatabase::instance()->add(config.output_texture_name() + "_left", passes_[3]->get_gbuffer()->get_eye_buffers()[0]->get_color_buffers(TYPE_FLOAT)[0]); - TextureDatabase::instance()->add(config.output_texture_name() + "_right", passes_[3]->get_gbuffer()->get_eye_buffers()[1]->get_color_buffers(TYPE_FLOAT)[0]); + TextureDatabase::instance()->add(config.output_texture_name() + "_left", passes_[PipelineStage::postfx]->get_gbuffer()->get_eye_buffers()[0]->get_color_buffers(TYPE_FLOAT)[0]); + TextureDatabase::instance()->add(config.output_texture_name() + "_right", passes_[PipelineStage::postfx]->get_gbuffer()->get_eye_buffers()[1]->get_color_buffers(TYPE_FLOAT)[0]); } diff --git a/src/gua/renderer/PostFXPass.cpp b/src/gua/renderer/PostFXPass.cpp index 849dd200c..b967d424f 100644 --- a/src/gua/renderer/PostFXPass.cpp +++ b/src/gua/renderer/PostFXPass.cpp @@ -288,11 +288,11 @@ void PostFXPass::render_scene(Camera const& camera, RenderContext const& ctx) { any_godrays = render_godrays(camera, pipeline_->get_current_scene(eye), eye, ctx); render_glow(eye, ctx); - auto input_tex(inputs_[2]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); + auto input_tex(inputs_[Pipeline::compositing]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); auto ping_tex(ping_buffer_->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); auto pong_tex(pong_buffer_->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); - auto normal_tex(inputs_[0]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); - auto depth_tex(inputs_[0]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_depth_buffer()); + auto normal_tex(inputs_[Pipeline::geometry]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); + auto depth_tex(inputs_[Pipeline::geometry]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_depth_buffer()); Pass::set_camera_matrices(*postfx_shaders_[0], camera, pipeline_->get_current_scene(eye), eye, ctx); @@ -449,7 +449,7 @@ bool PostFXPass::render_godrays(Camera const& camera, if (any_godrays) { Pass::set_camera_matrices(*god_ray_shader_, camera, scene, eye, ctx); - auto depth_buffer(inputs_[0]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_depth_buffer()); + auto depth_buffer(inputs_[Pipeline::geometry]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_depth_buffer()); god_ray_shader_->set_uniform(ctx, 1.0f * godray_buffers_[0]->width() / godray_buffers_[0]->height(), "gua_aspect_ratio"); ctx.render_context->set_viewport(scm::gl::viewport( math::vec2(0.0f,0.0f), math::vec2(float(godray_buffers_[0]->width()), float(godray_buffers_[0]->height())))); @@ -533,7 +533,7 @@ void PostFXPass::render_glow(CameraMode eye, RenderContext const& ctx) { glow_shader_->set_uniform(ctx, pipeline_->config.bloom_threshold(), "gua_glow_threshold"); - auto color_buffer(inputs_[2]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); + auto color_buffer(inputs_[Pipeline::compositing]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); ctx.render_context->set_viewport(scm::gl::viewport( math::vec2(0,0), math::vec2(float(glow_buffers_[0]->width()), float(glow_buffers_[0]->height())))); diff --git a/src/gua/renderer/Renderer.cpp b/src/gua/renderer/Renderer.cpp index 623c19638..f5f1f787c 100644 --- a/src/gua/renderer/Renderer.cpp +++ b/src/gua/renderer/Renderer.cpp @@ -43,9 +43,11 @@ std::shared_ptr garbage_collected_copy( } Renderer::~Renderer() { +#if USE_RAW_POINTER_RENDER_CLIENTS for (auto rc : render_clients_) { if (rc) delete rc; } +#endif } Renderer::Renderer(std::vector const& pipelines) @@ -57,8 +59,11 @@ Renderer::Renderer(std::vector const& pipelines) std::shared_ptr const & sg, float render_fps) { pipeline->process(*sg, this->application_fps_.fps, render_fps); }; - +#if USE_RAW_POINTER_RENDER_CLIENTS render_clients_.push_back(new renderclient_t(fun)); +#else + render_clients_.push_back(gua::make_unique(fun)); +#endif } } From 07b4d2064891614f5a820dc56cedc7e051e861a3 Mon Sep 17 00:00:00 2001 From: Andre Schollmeyer Date: Tue, 26 Nov 2013 10:31:08 +0100 Subject: [PATCH 041/146] - composite pass now inherits from geometrypass to render volumeproxygeometries --- include/gua/renderer/CompositePass.hpp | 27 ++-- .../uber_shaders/composite/compose.frag | 49 ++++++ .../uber_shaders/composite/compose.vert | 33 +++++ src/gua/renderer/CompositePass.cpp | 140 ++++++++++++++++++ 4 files changed, 238 insertions(+), 11 deletions(-) create mode 100644 resources/shaders/uber_shaders/composite/compose.frag create mode 100644 resources/shaders/uber_shaders/composite/compose.vert create mode 100644 src/gua/renderer/CompositePass.cpp diff --git a/include/gua/renderer/CompositePass.hpp b/include/gua/renderer/CompositePass.hpp index f28529985..793996014 100644 --- a/include/gua/renderer/CompositePass.hpp +++ b/include/gua/renderer/CompositePass.hpp @@ -24,7 +24,7 @@ // guacamole headers #include -#include +#include namespace gua { @@ -34,7 +34,7 @@ struct PipelineConfiguration; /** * */ -class CompositePass : public Pass { +class CompositePass : public GeometryPass { public: /** @@ -47,13 +47,10 @@ class CompositePass : public Pass { */ virtual ~CompositePass(); - void create( - RenderContext const& ctx, - PipelineConfiguration const& config, - std::vector > const& layers); - - void render_scene(Camera const& camera, RenderContext const& ctx); + void create( RenderContext const& ctx, + PipelineConfiguration const& config, + std::vector > const& layers); /* virtual */ LayerMapping const* get_gbuffer_mapping() const; @@ -62,9 +59,17 @@ class CompositePass : public Pass { bool pre_compile_shaders(RenderContext const& ctx); - private: +protected : + + /* virtual */ void rendering( SerializedScene const& scene, + RenderContext const& ctx, + CameraMode eye, + Camera const& camera, + FrameBufferObject* target); - void apply_material_mapping(); + void init_ressources (RenderContext const& ctx); + + private: scm::gl::depth_stencil_state_ptr depth_stencil_state_; scm::gl::quad_geometry_ptr fullscreen_quad_; diff --git a/resources/shaders/uber_shaders/composite/compose.frag b/resources/shaders/uber_shaders/composite/compose.frag new file mode 100644 index 000000000..121a56cca --- /dev/null +++ b/resources/shaders/uber_shaders/composite/compose.frag @@ -0,0 +1,49 @@ +/****************************************************************************** + * guacamole - delicious VR * + * * + * Copyright: (c) 2011-2013 Bauhaus-Universität Weimar * + * Contact: felix.lauer@uni-weimar.de / simon.schneegans@uni-weimar.de * + * * + * This program is free software: you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the Free * + * Software Foundation, either version 3 of the License, or (at your option) * + * any later version. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * + * for more details. * + * * + * You should have received a copy of the GNU General Public License along * + * with this program. If not, see . * + * * + ******************************************************************************/ + +@include "shaders/common/header.glsl" + +// input from gbuffer ---------------------------------------------------- +uniform uvec2 gua_depth_gbuffer_in; +uniform uvec2 gua_color_gbuffer_in; +uniform uvec2 gua_normal_gbuffer_in; + +// uniforms +@include "shaders/uber_shaders/common/get_sampler_casts.glsl" +@include "shaders/uber_shaders/common/gua_camera_uniforms.glsl" + +// methods --------------------------------------------------------------------- + +// global gua_* methods +vec2 gua_get_quad_coords() { + return vec2(gl_FragCoord.x * gua_texel_width, gl_FragCoord.y * gua_texel_height); +} + +// write outputs --------------------------------------------------------------------- +layout(location=0) out vec3 gua_out_color; + +// main ------------------------------------------------------------------------ +void main() { + // compose + gua_out_color = texture2D(gua_get_float_sampler(gua_color_gbuffer_in), gua_get_quad_coords()).xyz + + texture2D(gua_get_float_sampler(gua_normal_gbuffer_in), gua_get_quad_coords()).xyz; +} + diff --git a/resources/shaders/uber_shaders/composite/compose.vert b/resources/shaders/uber_shaders/composite/compose.vert new file mode 100644 index 000000000..11b51ce7d --- /dev/null +++ b/resources/shaders/uber_shaders/composite/compose.vert @@ -0,0 +1,33 @@ +/****************************************************************************** + * guacamole - delicious VR * + * * + * Copyright: (c) 2011-2013 Bauhaus-Universität Weimar * + * Contact: felix.lauer@uni-weimar.de / simon.schneegans@uni-weimar.de * + * * + * This program is free software: you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the Free * + * Software Foundation, either version 3 of the License, or (at your option) * + * any later version. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * + * for more details. * + * * + * You should have received a copy of the GNU General Public License along * + * with this program. If not, see . * + * * + ******************************************************************************/ + +@include "shaders/common/header.glsl" + +// uniforms +@include "shaders/uber_shaders/common/gua_camera_uniforms.glsl" + +// input +layout(location=0) in vec3 gua_in_position; + +void main() { + gl_Position = vec4(gua_in_position, 1.0); +} + diff --git a/src/gua/renderer/CompositePass.cpp b/src/gua/renderer/CompositePass.cpp new file mode 100644 index 000000000..444d534a3 --- /dev/null +++ b/src/gua/renderer/CompositePass.cpp @@ -0,0 +1,140 @@ +/****************************************************************************** + * guacamole - delicious VR * + * * + * Copyright: (c) 2011-2013 Bauhaus-Universität Weimar * + * Contact: felix.lauer@uni-weimar.de / simon.schneegans@uni-weimar.de * + * * + * This program is free software: you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the Free * + * Software Foundation, either version 3 of the License, or (at your option) * + * any later version. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * + * for more details. * + * * + * You should have received a copy of the GNU General Public License along * + * with this program. If not, see . * + * * + ******************************************************************************/ + +// class header +#include + +// guacamole headers +#include +#include +#include +#include +#include +#include + +namespace gua { + +//////////////////////////////////////////////////////////////////////////////// + +CompositePass::CompositePass(Pipeline* pipeline) : + GeometryPass(pipeline), + composite_shader_(new ShaderProgram) +{ + std::string vertex_shader (Resources::lookup_shader(Resources::shaders_uber_shaders_composite_compose_vert)); + std::string fragment_shader(Resources::lookup_shader(Resources::shaders_uber_shaders_composite_compose_frag)); + + composite_shader_->create_from_sources(vertex_shader, fragment_shader); + + print_shaders("debug", "composite.txt"); +} + +//////////////////////////////////////////////////////////////////////////////// + +CompositePass::~CompositePass() { + + if (composite_shader_) { + delete composite_shader_; + } + +} + +//////////////////////////////////////////////////////////////////////////////// + +void CompositePass::create(RenderContext const& ctx, + PipelineConfiguration const& config, std::vector> const& layers) { + + Pass::create(ctx, config, layers); + +} + +//////////////////////////////////////////////////////////////////////////////// + +/* virtual */ void CompositePass::rendering(SerializedScene const& scene, + RenderContext const& ctx, + CameraMode eye, + Camera const& camera, + FrameBufferObject* target) { + + init_ressources(ctx); + + ctx.render_context->set_depth_stencil_state(depth_stencil_state_); + { + // gather input textures and set uniforms + Pass::set_camera_matrices(*composite_shader_, camera, pipeline_->get_current_scene(eye), eye, ctx); + + auto input_tex(inputs_[Pipeline::shading]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); + auto normal_tex(inputs_[Pipeline::geometry]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); + auto depth_tex(inputs_[Pipeline::geometry]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_depth_buffer()); + + composite_shader_->set_uniform(ctx, input_tex, "gua_color_gbuffer_in"); + composite_shader_->set_uniform(ctx, normal_tex, "gua_normal_gbuffer_in"); + composite_shader_->set_uniform(ctx, depth_tex, "gua_depth_gbuffer_in"); + + composite_shader_->set_uniform(ctx, 1.f / gbuffer_->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->width(), "gua_texel_width"); + composite_shader_->set_uniform(ctx, 1.f / gbuffer_->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->height(), "gua_texel_height"); + + composite_shader_->use(ctx); + { + fullscreen_quad_->draw(ctx.render_context); + } + composite_shader_->unuse(ctx); + } + + ctx.render_context->reset_state_objects(); +} + +//////////////////////////////////////////////////////////////////////////////// + +void CompositePass::init_ressources(RenderContext const& ctx) { + + if (!depth_stencil_state_) { + depth_stencil_state_ = ctx.render_device->create_depth_stencil_state(false, false, scm::gl::COMPARISON_NEVER); + } + + if (!fullscreen_quad_) { + fullscreen_quad_ = scm::gl::quad_geometry_ptr(new scm::gl::quad_geometry(ctx.render_device, math::vec2(-1.f, -1.f), math::vec2(1.f, 1.f))); + } +} + +//////////////////////////////////////////////////////////////////////////////// + +/* virtual */ LayerMapping const* CompositePass::get_gbuffer_mapping() const { + throw std::runtime_error("no gbuffer mapping available for composite pass"); +} + +//////////////////////////////////////////////////////////////////////////////// + +void CompositePass::print_shaders(std::string const& directory, + std::string const& name) const { + composite_shader_->save_to_file(directory, name + "/composite_shader"); +} + +//////////////////////////////////////////////////////////////////////////////// + +bool CompositePass::pre_compile_shaders(RenderContext const& ctx) { + + if (composite_shader_) return composite_shader_->upload_to(ctx); + + return false; +} + +} From 669c4507bdcaff923624d45d49bc0d49843c3a9a Mon Sep 17 00:00:00 2001 From: thelaui Date: Tue, 26 Nov 2013 13:03:21 +0100 Subject: [PATCH 042/146] implemented checkerboard stereo mode --- include/gua/renderer/Window.hpp | 4 +- include/gua/renderer/enums.hpp | 3 +- resources/shaders/display_shader.frag | 14 +++++++ src/gua/renderer/Window.cpp | 57 ++++++++++++++++----------- 4 files changed, 54 insertions(+), 24 deletions(-) diff --git a/include/gua/renderer/Window.hpp b/include/gua/renderer/Window.hpp index 3bb944756..ebfae0a26 100644 --- a/include/gua/renderer/Window.hpp +++ b/include/gua/renderer/Window.hpp @@ -53,7 +53,9 @@ class Window { FULL, RED, GREEN, - CYAN + CYAN, + CHECKER_EVEN, + CHECKER_ODD }; /** diff --git a/include/gua/renderer/enums.hpp b/include/gua/renderer/enums.hpp index 93305fec6..b1064143a 100644 --- a/include/gua/renderer/enums.hpp +++ b/include/gua/renderer/enums.hpp @@ -51,7 +51,8 @@ enum class StereoMode { MONO = 0, SIDE_BY_SIDE, ANAGLYPH_RED_GREEN, - ANAGLYPH_RED_CYAN + ANAGLYPH_RED_CYAN, + CHECKERBOARD }; /** diff --git a/resources/shaders/display_shader.frag b/resources/shaders/display_shader.frag index 80b8d9f04..97670d1f5 100644 --- a/resources/shaders/display_shader.frag +++ b/resources/shaders/display_shader.frag @@ -49,6 +49,20 @@ vec3 get_cyan() { return vec3(0.0, texture2D( get_tex(sampler), tex_coord).gb); } +subroutine( GetColorType ) +vec3 get_checker_even() { + if (mod(gl_FragCoord.x + gl_FragCoord.y, 2.0) == 0.0) + return vec3(texture2D( get_tex(sampler), tex_coord).rgb); + else discard; +} + +subroutine( GetColorType ) +vec3 get_checker_odd() { + if (mod(gl_FragCoord.x + gl_FragCoord.y, 2.0) == 1.0) + return vec3(texture2D( get_tex(sampler), tex_coord).rgb); + else discard; +} + subroutine( GetColorType ) vec3 get_full() { return vec3(texture2D( get_tex(sampler), tex_coord).rgb); diff --git a/src/gua/renderer/Window.cpp b/src/gua/renderer/Window.cpp index 8d536d8bd..8f642ca4c 100644 --- a/src/gua/renderer/Window.cpp +++ b/src/gua/renderer/Window.cpp @@ -46,6 +46,12 @@ std::string subroutine_from_mode(Window::TextureDisplayMode mode) { case Window::CYAN: return "get_cyan"; break; + case Window::CHECKER_EVEN: + return "get_checker_even"; + break; + case Window::CHECKER_ODD: + return "get_checker_odd"; + break; default: return "get_full"; } @@ -146,6 +152,7 @@ bool Window::get_is_open() const { return ctx_.window != nullptr; } //////////////////////////////////////////////////////////////////////////////// void Window::create_shader() { + if (config.get_warp_matrix_red_right() == "" || config.get_warp_matrix_green_right() == "" || config.get_warp_matrix_blue_right() == "" || @@ -224,28 +231,34 @@ void Window::display(std::shared_ptr const& left_texture, std::shared_ptr const& right_texture) { switch (config.get_stereo_mode()) { - case StereoMode::MONO: - display(left_texture); - break; - case StereoMode::SIDE_BY_SIDE: - display(left_texture, config.get_left_resolution(), - config.get_left_position(), Window::FULL, true, true); - display(right_texture, config.get_right_resolution(), - config.get_right_position(), Window::FULL, false, true); - break; - case StereoMode::ANAGLYPH_RED_CYAN: - display(left_texture, config.get_left_resolution(), - config.get_left_position(), Window::RED, true, true); - display(right_texture, config.get_right_resolution(), - config.get_right_position(), Window::CYAN, false, false); - break; - case StereoMode::ANAGLYPH_RED_GREEN: - display(left_texture, config.get_left_resolution(), - config.get_left_position(), Window::RED, true, true); - display(right_texture, config.get_right_resolution(), - config.get_right_position(), Window::GREEN, false, false); - break; - } + case StereoMode::MONO: + display(left_texture); + break; + case StereoMode::SIDE_BY_SIDE: + display(left_texture, config.get_left_resolution(), + config.get_left_position(), Window::FULL, true, true); + display(right_texture, config.get_right_resolution(), + config.get_right_position(), Window::FULL, false, true); + break; + case StereoMode::ANAGLYPH_RED_CYAN: + display(left_texture, config.get_left_resolution(), + config.get_left_position(), Window::RED, true, true); + display(right_texture, config.get_right_resolution(), + config.get_right_position(), Window::CYAN, false, false); + break; + case StereoMode::ANAGLYPH_RED_GREEN: + display(left_texture, config.get_left_resolution(), + config.get_left_position(), Window::RED, true, true); + display(right_texture, config.get_right_resolution(), + config.get_right_position(), Window::GREEN, false, false); + break; + case StereoMode::CHECKERBOARD: + display(left_texture, config.get_left_resolution(), + config.get_left_position(), Window::CHECKER_EVEN, true, true); + display(right_texture, config.get_right_resolution(), + config.get_right_position(), Window::CHECKER_ODD, false, true); + break; + } } From 99a46736ed925891592f344e511351b18c96a986 Mon Sep 17 00:00:00 2001 From: Andre Schollmeyer Date: Tue, 26 Nov 2013 17:32:13 +0100 Subject: [PATCH 043/146] extended scenegraph with volumenodes extended compositePass with volume proxy rendering --- include/gua/renderer/CompositePass.hpp | 5 + include/gua/renderer/SerializedScene.hpp | 6 + include/gua/renderer/Serializer.hpp | 9 + include/gua/scenegraph/VolumeNode.hpp | 70 +++++++ .../uber_shaders/composite/compose.frag | 7 +- .../composite/ray_generation.frag | 34 +++ .../composite/ray_generation.vert | 42 ++++ src/gua/renderer/CompositePass.cpp | 99 +++++++-- src/gua/renderer/GeometryPass.cpp | 1 + src/gua/renderer/Serializer.cpp | 15 ++ src/gua/scenegraph/VolumeNode.cpp | 198 ++++++++++++++++++ 11 files changed, 465 insertions(+), 21 deletions(-) create mode 100644 include/gua/scenegraph/VolumeNode.hpp create mode 100644 resources/shaders/uber_shaders/composite/ray_generation.frag create mode 100644 resources/shaders/uber_shaders/composite/ray_generation.vert create mode 100644 src/gua/scenegraph/VolumeNode.cpp diff --git a/include/gua/renderer/CompositePass.hpp b/include/gua/renderer/CompositePass.hpp index 793996014..9a2f50836 100644 --- a/include/gua/renderer/CompositePass.hpp +++ b/include/gua/renderer/CompositePass.hpp @@ -25,6 +25,7 @@ // guacamole headers #include #include +#include namespace gua { @@ -71,9 +72,13 @@ protected : private: + GBuffer* volume_raygeneration_; + scm::gl::depth_stencil_state_ptr depth_stencil_state_; scm::gl::quad_geometry_ptr fullscreen_quad_; + ShaderProgram* composite_shader_; + ShaderProgram* ray_generation_shader_; }; } diff --git a/include/gua/renderer/SerializedScene.hpp b/include/gua/renderer/SerializedScene.hpp index d03cf8c12..6d5c8830f 100644 --- a/include/gua/renderer/SerializedScene.hpp +++ b/include/gua/renderer/SerializedScene.hpp @@ -24,6 +24,7 @@ // guacamole headers #include +#include #include #include #include @@ -59,6 +60,11 @@ struct SerializedScene { */ std::vector > nurbsnodes_; + /** + * All Volume nodes. + */ + std::vector > volumenodes_; + /** * All point light nodes. */ diff --git a/include/gua/renderer/Serializer.hpp b/include/gua/renderer/Serializer.hpp index bf9b03ffa..1c9a4f860 100644 --- a/include/gua/renderer/Serializer.hpp +++ b/include/gua/renderer/Serializer.hpp @@ -84,6 +84,15 @@ class Serializer : public NodeVisitor { */ /* virtual */ void visit(GeometryNode* geometry); + /** + * Visits a VolumeNode + * + * This function provides the interface to visit a VolumeNode + * + * \param volume Pointer to VolumeNode + */ + /* virtual */ void visit(VolumeNode* volume); + /** * Visits a PointLightNode * diff --git a/include/gua/scenegraph/VolumeNode.hpp b/include/gua/scenegraph/VolumeNode.hpp new file mode 100644 index 000000000..81d58e2cd --- /dev/null +++ b/include/gua/scenegraph/VolumeNode.hpp @@ -0,0 +1,70 @@ +/****************************************************************************** + * guacamole - delicious VR * + * * + * Copyright: (c) 2011-2013 Bauhaus-Universität Weimar * + * Contact: felix.lauer@uni-weimar.de / simon.schneegans@uni-weimar.de * + * * + * This program is free software: you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the Free * + * Software Foundation, either version 3 of the License, or (at your option) * + * any later version. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * + * for more details. * + * * + * You should have received a copy of the GNU General Public License along * + * with this program. If not, see . * + * * + ******************************************************************************/ + +#ifndef GUA_VOLUME_NODE_HPP +#define GUA_VOLUME_NODE_HPP + +// guacamole headers +#include +#include + +// external headers +#include + +/** + * This class is used to represent a volume in the SceneGraph. + * + */ + +namespace gua { + +class GUA_DLL VolumeNode : public Node { + public: + + struct Configuration { + GUA_ADD_PROPERTY(std::string, volume, "gua_volume_default"); + }; + + Configuration data; + + VolumeNode() {}; + + VolumeNode(std::string const& name, + Configuration const& configuration = Configuration(), + math::mat4 const& transform = math::mat4::identity()); + + /*virtual*/ void accept(NodeVisitor&); + + /*virtual*/ void update_bounding_box() const; + + /*virtual*/ void ray_test_impl(RayNode const& ray, + PickResult::Options options, + Mask const& mask, + std::set& hits); + + private: + + std::shared_ptr copy() const; +}; + +} + +#endif // GUA_VOLUME_NODE_HPP diff --git a/resources/shaders/uber_shaders/composite/compose.frag b/resources/shaders/uber_shaders/composite/compose.frag index 121a56cca..cbc851142 100644 --- a/resources/shaders/uber_shaders/composite/compose.frag +++ b/resources/shaders/uber_shaders/composite/compose.frag @@ -25,6 +25,7 @@ uniform uvec2 gua_depth_gbuffer_in; uniform uvec2 gua_color_gbuffer_in; uniform uvec2 gua_normal_gbuffer_in; +uniform uvec2 gua_ray_entry_in; // uniforms @include "shaders/uber_shaders/common/get_sampler_casts.glsl" @@ -43,7 +44,9 @@ layout(location=0) out vec3 gua_out_color; // main ------------------------------------------------------------------------ void main() { // compose - gua_out_color = texture2D(gua_get_float_sampler(gua_color_gbuffer_in), gua_get_quad_coords()).xyz + - texture2D(gua_get_float_sampler(gua_normal_gbuffer_in), gua_get_quad_coords()).xyz; + if ( length ( texture2D(gua_get_float_sampler(gua_color_gbuffer_in), gua_get_quad_coords()).xyz) > 0.0 ) + { + gua_out_color = texture2D(gua_get_float_sampler(gua_ray_entry_in), gua_get_quad_coords()).xyz; + } } diff --git a/resources/shaders/uber_shaders/composite/ray_generation.frag b/resources/shaders/uber_shaders/composite/ray_generation.frag new file mode 100644 index 000000000..f4aa41e03 --- /dev/null +++ b/resources/shaders/uber_shaders/composite/ray_generation.frag @@ -0,0 +1,34 @@ +/****************************************************************************** + * guacamole - delicious VR * + * * + * Copyright: (c) 2011-2013 Bauhaus-Universität Weimar * + * Contact: felix.lauer@uni-weimar.de / simon.schneegans@uni-weimar.de * + * * + * This program is free software: you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the Free * + * Software Foundation, either version 3 of the License, or (at your option) * + * any later version. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * + * for more details. * + * * + * You should have received a copy of the GNU General Public License along * + * with this program. If not, see . * + * * + ******************************************************************************/ + +@include "shaders/common/header.glsl" + +// input +in vec3 gua_position_varying; + +// write outputs +layout(location=0) out vec3 gua_out_color; + +// main ------------------------------------------------------------------------ +void main() { + gua_out_color = gua_position_varying; +} + diff --git a/resources/shaders/uber_shaders/composite/ray_generation.vert b/resources/shaders/uber_shaders/composite/ray_generation.vert new file mode 100644 index 000000000..db1800245 --- /dev/null +++ b/resources/shaders/uber_shaders/composite/ray_generation.vert @@ -0,0 +1,42 @@ +/****************************************************************************** + * guacamole - delicious VR * + * * + * Copyright: (c) 2011-2013 Bauhaus-Universität Weimar * + * Contact: felix.lauer@uni-weimar.de / simon.schneegans@uni-weimar.de * + * * + * This program is free software: you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the Free * + * Software Foundation, either version 3 of the License, or (at your option) * + * any later version. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * + * for more details. * + * * + * You should have received a copy of the GNU General Public License along * + * with this program. If not, see . * + * * + ******************************************************************************/ + +@include "shaders/common/header.glsl" + +// uniforms +@include "shaders/uber_shaders/common/gua_camera_uniforms.glsl" + +// input +layout(location=0) in vec3 gua_in_position; + +// output +out vec3 gua_position_varying; + +void main() { + + gua_position_varying = gua_in_position; + + vec3 gua_world_position = (gua_model_matrix * vec4(gua_in_position, 1.0)).xyz; + + //gl_Position = gua_projection_matrix * gua_view_matrix * vec4(gua_world_position.xyz, 1.0); + gl_Position = vec4(gua_in_position, 1.0); +} + diff --git a/src/gua/renderer/CompositePass.cpp b/src/gua/renderer/CompositePass.cpp index 444d534a3..b68b7b5cc 100644 --- a/src/gua/renderer/CompositePass.cpp +++ b/src/gua/renderer/CompositePass.cpp @@ -36,13 +36,20 @@ namespace gua { CompositePass::CompositePass(Pipeline* pipeline) : GeometryPass(pipeline), - composite_shader_(new ShaderProgram) + composite_shader_(new ShaderProgram), + ray_generation_shader_(new ShaderProgram), + volume_raygeneration_(nullptr) { std::string vertex_shader (Resources::lookup_shader(Resources::shaders_uber_shaders_composite_compose_vert)); std::string fragment_shader(Resources::lookup_shader(Resources::shaders_uber_shaders_composite_compose_frag)); composite_shader_->create_from_sources(vertex_shader, fragment_shader); + std::string ray_generation_vertex_shader(Resources::lookup_shader(Resources::shaders_uber_shaders_composite_ray_generation_vert)); + std::string ray_generation_fragment_shader(Resources::lookup_shader(Resources::shaders_uber_shaders_composite_ray_generation_frag)); + + ray_generation_shader_->create_from_sources(ray_generation_vertex_shader, ray_generation_fragment_shader); + print_shaders("debug", "composite.txt"); } @@ -54,6 +61,13 @@ CompositePass::~CompositePass() { delete composite_shader_; } + if (volume_raygeneration_) { + delete volume_raygeneration_; + } + + if (ray_generation_shader_) { + delete ray_generation_shader_; + } } //////////////////////////////////////////////////////////////////////////////// @@ -64,40 +78,85 @@ void CompositePass::create(RenderContext const& ctx, Pass::create(ctx, config, layers); + if (volume_raygeneration_) { + volume_raygeneration_->remove_buffers(ctx); + delete volume_raygeneration_; + } + + scm::gl::sampler_state_desc state(scm::gl::FILTER_MIN_MAG_LINEAR, + scm::gl::WRAP_CLAMP_TO_EDGE, + scm::gl::WRAP_CLAMP_TO_EDGE); + + std::vector> layer_3f_desc; + layer_3f_desc.push_back(std::make_pair(BufferComponent::F3, state)); + + volume_raygeneration_ = new GBuffer(layer_3f_desc, + config.get_left_resolution()[0], + config.get_left_resolution()[1]); + volume_raygeneration_->create(ctx); } //////////////////////////////////////////////////////////////////////////////// /* virtual */ void CompositePass::rendering(SerializedScene const& scene, - RenderContext const& ctx, - CameraMode eye, - Camera const& camera, - FrameBufferObject* target) { - + RenderContext const& ctx, + CameraMode eye, + Camera const& camera, + FrameBufferObject* target) { + init_ressources(ctx); ctx.render_context->set_depth_stencil_state(depth_stencil_state_); - { - // gather input textures and set uniforms - Pass::set_camera_matrices(*composite_shader_, camera, pipeline_->get_current_scene(eye), eye, ctx); - auto input_tex(inputs_[Pipeline::shading]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); - auto normal_tex(inputs_[Pipeline::geometry]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); - auto depth_tex(inputs_[Pipeline::geometry]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_depth_buffer()); + // 1. render proxy geometry into fbo + volume_raygeneration_->bind(ctx); + { + scm::math::vec2f resolution(volume_raygeneration_->width(), volume_raygeneration_->height()); + ctx.render_context->set_viewport(scm::gl::viewport(math::vec2(0, 0), resolution)); - composite_shader_->set_uniform(ctx, input_tex, "gua_color_gbuffer_in"); - composite_shader_->set_uniform(ctx, normal_tex, "gua_normal_gbuffer_in"); - composite_shader_->set_uniform(ctx, depth_tex, "gua_depth_gbuffer_in"); + // gather input textures and set uniforms + Pass::set_camera_matrices(*ray_generation_shader_, camera, pipeline_->get_current_scene(eye), eye, ctx); - composite_shader_->set_uniform(ctx, 1.f / gbuffer_->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->width(), "gua_texel_width"); - composite_shader_->set_uniform(ctx, 1.f / gbuffer_->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->height(), "gua_texel_height"); + ray_generation_shader_->set_uniform(ctx, 1.f / gbuffer_->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->width(), "gua_texel_width"); + ray_generation_shader_->set_uniform(ctx, 1.f / gbuffer_->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->height(), "gua_texel_height"); - composite_shader_->use(ctx); + ray_generation_shader_->use(ctx); { fullscreen_quad_->draw(ctx.render_context); } - composite_shader_->unuse(ctx); + ray_generation_shader_->unuse(ctx); + } + volume_raygeneration_->unbind(ctx); + + // 2. render fullscreen quad for compositing and volume ray castinG + Pass::set_camera_matrices(*composite_shader_, camera, pipeline_->get_current_scene(eye), eye, ctx); + + auto input_tex(inputs_[Pipeline::shading]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); + auto normal_tex(inputs_[Pipeline::geometry]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); + auto depth_tex(inputs_[Pipeline::geometry]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_depth_buffer()); + auto raygen_tex(volume_raygeneration_->get_color_buffers(TYPE_FLOAT)[0]); + + composite_shader_->set_uniform(ctx, input_tex, "gua_color_gbuffer_in"); + composite_shader_->set_uniform(ctx, normal_tex, "gua_normal_gbuffer_in"); + composite_shader_->set_uniform(ctx, depth_tex, "gua_depth_gbuffer_in"); + composite_shader_->set_uniform(ctx, raygen_tex, "gua_ray_entry_in"); + + composite_shader_->set_uniform(ctx, 1.f / gbuffer_->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->width(), "gua_texel_width"); + composite_shader_->set_uniform(ctx, 1.f / gbuffer_->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->height(), "gua_texel_height"); + + // bind target fbo and set viewport + target->bind(ctx); + ctx.render_context->set_viewport(scm::gl::viewport( + math::vec2(0, 0), + ::scm::math::vec2f(target->width(), target->height()))); + + composite_shader_->use(ctx); + { + fullscreen_quad_->draw(ctx.render_context); } + composite_shader_->unuse(ctx); + + target->unbind(ctx); ctx.render_context->reset_state_objects(); } @@ -126,6 +185,7 @@ void CompositePass::init_ressources(RenderContext const& ctx) { void CompositePass::print_shaders(std::string const& directory, std::string const& name) const { composite_shader_->save_to_file(directory, name + "/composite_shader"); + ray_generation_shader_->save_to_file(directory, name + "/ray_generation_shader"); } //////////////////////////////////////////////////////////////////////////////// @@ -133,6 +193,7 @@ void CompositePass::print_shaders(std::string const& directory, bool CompositePass::pre_compile_shaders(RenderContext const& ctx) { if (composite_shader_) return composite_shader_->upload_to(ctx); + if (ray_generation_shader_) return ray_generation_shader_->upload_to(ctx); return false; } diff --git a/src/gua/renderer/GeometryPass.cpp b/src/gua/renderer/GeometryPass.cpp index 0c983c503..ee61384c4 100644 --- a/src/gua/renderer/GeometryPass.cpp +++ b/src/gua/renderer/GeometryPass.cpp @@ -42,6 +42,7 @@ void GeometryPass::render_scene(Camera const& camera, RenderContext const& ctx) gbuffer_->clear(ctx); for (int i(0); i < gbuffer_->get_eye_buffers().size(); ++i) { + FrameBufferObject* fbo(gbuffer_->get_eye_buffers()[i]); CameraMode eye(CameraMode::CENTER); diff --git a/src/gua/renderer/Serializer.cpp b/src/gua/renderer/Serializer.cpp index 4dcbf6b0c..a32dd8f2d 100644 --- a/src/gua/renderer/Serializer.cpp +++ b/src/gua/renderer/Serializer.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -160,6 +161,20 @@ void Serializer::check(SerializedScene* output, //////////////////////////////////////////////////////////////////////// +/* virtual */ void Serializer::visit(VolumeNode* node) { + + if ( is_visible(node) ) { + if ( !node->data.get_volume().empty() ) { + add_bbox(node); + data_->volumenodes_.push_back(make_serialized_node(node->get_world_transform(), node->data)); + } + + visit_children(node); + } +} + +//////////////////////////////////////////////////////////////////////// + /* virtual */ void Serializer::visit(PointLightNode* node) { if (is_visible(node)) { diff --git a/src/gua/scenegraph/VolumeNode.cpp b/src/gua/scenegraph/VolumeNode.cpp new file mode 100644 index 000000000..db51d76df --- /dev/null +++ b/src/gua/scenegraph/VolumeNode.cpp @@ -0,0 +1,198 @@ +/****************************************************************************** + * guacamole - delicious VR * + * * + * Copyright: (c) 2011-2013 Bauhaus-Universität Weimar * + * Contact: felix.lauer@uni-weimar.de / simon.schneegans@uni-weimar.de * + * * + * This program is free software: you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the Free * + * Software Foundation, either version 3 of the License, or (at your option) * + * any later version. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * + * for more details. * + * * + * You should have received a copy of the GNU General Public License along * + * with this program. If not, see . * + * * + ******************************************************************************/ + +// class header +#include + +// guacamole headers +#include +#include +#include +#include +#include +#include + +namespace gua { + + ///////////////////////////////////////////////////////////////////////////// + + VolumeNode::VolumeNode(std::string const& name, + Configuration const& configuration, + math::mat4 const& transform) + : Node(name, transform), data(configuration) {} + + ///////////////////////////////////////////////////////////////////////////// + + /* virtual */ void VolumeNode::accept(NodeVisitor& visitor) { + visitor.visit(this); + } + + ///////////////////////////////////////////////////////////////////////////// + + void VolumeNode::update_bounding_box() const { + + if (data.get_volume() != "") { + + auto geometry_bbox(GeometryDatabase::instance()->lookup(data.get_volume())->get_bounding_box()); + bounding_box_ = transform(geometry_bbox, world_transform_); + + for (auto child : get_children()) { + bounding_box_.expandBy(child->get_bounding_box()); + } + } else { + Node::update_bounding_box(); + } + } + + ///////////////////////////////////////////////////////////////////////////// + + void VolumeNode::ray_test_impl(RayNode const& ray, PickResult::Options options, + Mask const& mask, std::set& hits) { + + // first of all, check bbox + auto box_hits(ray.intersect(bounding_box_)); + + // ray did not intersect bbox -- therefore it wont intersect + if (box_hits.first == RayNode::END && box_hits.second == RayNode::END) { + return; + } + + // return if only first object shall be returned and the current first hit + // is in front of the bbox entry point and the ray does not start inside + // the bbox + if (options & PickResult::PICK_ONLY_FIRST_OBJECT + && hits.size() > 0 && hits.begin()->distance < box_hits.first + && box_hits.first != Ray::END) { + + return; + } + + // bbox is intersected, but check geometry only if mask tells us to check + if (data.get_volume() != "" && mask.check(get_groups())) { + + auto geometry(GeometryDatabase::instance()->lookup(data.get_volume())); + + if (geometry) { + + bool check_kd_tree(true); + + math::mat4 world_transform(get_world_transform()); + + // check for bounding box intersection of contained geometry if node + // has children (in this case, the bbox might be larger + // than the actual geometry) + if (has_children()) { + auto geometry_bbox(geometry->get_bounding_box()); + + math::BoundingBox inner_bbox; + inner_bbox.expandBy(world_transform * geometry_bbox.min); + inner_bbox.expandBy(world_transform * geometry_bbox.max); + inner_bbox.expandBy(world_transform * + math::vec3(geometry_bbox.min.x, + geometry_bbox.min.y, + geometry_bbox.max.z)); + inner_bbox.expandBy(world_transform * + math::vec3(geometry_bbox.min.x, + geometry_bbox.max.y, + geometry_bbox.min.z)); + inner_bbox.expandBy(world_transform * + math::vec3(geometry_bbox.min.x, + geometry_bbox.max.y, + geometry_bbox.max.z)); + inner_bbox.expandBy(world_transform * + math::vec3(geometry_bbox.max.x, + geometry_bbox.min.y, + geometry_bbox.max.z)); + inner_bbox.expandBy(world_transform * + math::vec3(geometry_bbox.max.x, + geometry_bbox.max.y, + geometry_bbox.min.z)); + inner_bbox.expandBy(world_transform * + math::vec3(geometry_bbox.max.x, + geometry_bbox.min.y, + geometry_bbox.min.z)); + + auto inner_hits(ray.intersect(inner_bbox)); + if (inner_hits.first == RayNode::END && + inner_hits.second == RayNode::END) + check_kd_tree = false; + } + + if (check_kd_tree) { + Ray world_ray(ray.get_world_ray()); + + math::mat4 ori_transform(scm::math::inverse(world_transform)); + + math::vec4 ori(world_ray.origin_[0], + world_ray.origin_[1], + world_ray.origin_[2], + 1.0); + math::vec4 dir(world_ray.direction_[0], + world_ray.direction_[1], + world_ray.direction_[2], + 0.0); + + ori = ori_transform * ori; + dir = ori_transform * dir; + + Ray object_ray(ori, dir, world_ray.t_max_); + geometry->ray_test(object_ray, options, this, hits); + + float const inf(std::numeric_limits::max()); + + if (options & PickResult::GET_WORLD_POSITIONS) { + + for (auto& hit: hits) { + if (hit.world_position == math::vec3(inf, inf, inf)) { + auto transformed(world_transform * math::vec4(hit.position.x, hit.position.y, hit.position.z, 0.0)); + hit.world_position = scm::math::vec3(transformed.x, transformed.y, transformed.z); + } + } + } + + if (options & PickResult::GET_WORLD_NORMALS) { + + math::mat4 normal_matrix(scm::math::inverse(scm::math::transpose(world_transform))); + for (auto& hit: hits) { + if (hit.world_normal == math::vec3(inf, inf, inf)) { + auto transformed(normal_matrix * math::vec4(hit.normal.x, hit.normal.y, hit.normal.z, 0.0)); + hit.world_normal = scm::math::normalize(scm::math::vec3(transformed.x, transformed.y, transformed.z)); + } + } + } + } + } + } + + for (auto child : get_children()) { + // test for intersection with each child + child->ray_test_impl(ray, options, mask, hits); + } + + } + + ///////////////////////////////////////////////////////////////////////////// + + std::shared_ptr VolumeNode::copy() const { + return std::make_shared(get_name(), data, get_transform()); + } + +} // namespace gua From 3004bf45754877c91209f316b9a5cdc641e770ea Mon Sep 17 00:00:00 2001 From: Andre Schollmeyer Date: Tue, 26 Nov 2013 17:49:02 +0100 Subject: [PATCH 044/146] fixed bug in find_compiler.cmake and find_boost.cmake --- cmake/modules/find_boost.cmake | 3 +-- cmake/modules/find_compiler.cmake | 12 ++++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/cmake/modules/find_boost.cmake b/cmake/modules/find_boost.cmake index 7608b00c7..181dbbfc2 100644 --- a/cmake/modules/find_boost.cmake +++ b/cmake/modules/find_boost.cmake @@ -11,7 +11,6 @@ SET(AVANGO_BOOST_INCLUDE_SEARCH_DIRS ${GLOBAL_EXT_DIR}/inc/boost ${GUACAMOLE_EXT_DIR}/inc/boost ${BOOST_INCLUDE_SEARCH_DIR} - ${BOOST_INCLUDE_DIRS} /opt/boost/latest/include ${CMAKE_SYSTEM_INCLUDE_PATH} ${CMAKE_INCLUDE_PATH} @@ -22,7 +21,6 @@ SET(AVANGO_BOOST_LIBRARY_SEARCH_DIRS ${GLOBAL_EXT_DIR}/lib ${GUACAMOLE_EXT_DIR}/lib ${BOOST_LIBRARY_SEARCH_DIR} - ${BOOST_LIBRARY_DIRS} /opt/boost/latest/lib ${CMAKE_SYSTEM_LIBRARY_PATH} ${CMAKE_LIBRARY_PATH} @@ -143,6 +141,7 @@ IF ( BOOST_INCLUDE_DIRS AND NOT BOOST_LIBRARY_DIRS ) "${CMAKE_STATIC_LIBRARY_PREFIX}libboost_filesystem-mt${CMAKE_STATIC_LIBRARY_SUFFIX}") endif (UNIX) + MESSAGE(${_AVANGO_BOOST_FILESYSTEM_LIB}) FOREACH(_SEARCH_DIR ${AVANGO_BOOST_LIBRARY_SEARCH_DIRS}) diff --git a/cmake/modules/find_compiler.cmake b/cmake/modules/find_compiler.cmake index d8d0e3c65..84f6c2ef9 100644 --- a/cmake/modules/find_compiler.cmake +++ b/cmake/modules/find_compiler.cmake @@ -1,26 +1,26 @@ if (WIN32) if (MSVC71) - set (GUA_COMPILER_SUFFIX "vc71") + set (COMPILER_SUFFIX "vc71") endif(MSVC71) if (MSVC80) - set (GUA_COMPILER_SUFFIX "vc80") + set (COMPILER_SUFFIX "vc80") endif(MSVC80) if (MSVC90) - set (GUA_COMPILER_SUFFIX "vc90") + set (COMPILER_SUFFIX "vc90") endif(MSVC90) if (MSVC10) - set (GUA_COMPILER_SUFFIX "vc100") + set (COMPILER_SUFFIX "vc100") endif(MSVC10) if (MSVC11) - set (GUA_COMPILER_SUFFIX "vc110") + set (COMPILER_SUFFIX "vc110") endif(MSVC11) if (MSVC12) - set (GUA_COMPILER_SUFFIX "vc120") + set (COMPILER_SUFFIX "vc120") endif(MSVC12) endif (WIN32) From c6efed83ca0c2a653533462074950b6147040997 Mon Sep 17 00:00:00 2001 From: thelaui Date: Tue, 26 Nov 2013 18:17:33 +0100 Subject: [PATCH 045/146] removed composite pass from postfx --- src/gua/renderer/PostFXPass.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gua/renderer/PostFXPass.cpp b/src/gua/renderer/PostFXPass.cpp index b967d424f..7802aae79 100644 --- a/src/gua/renderer/PostFXPass.cpp +++ b/src/gua/renderer/PostFXPass.cpp @@ -288,7 +288,7 @@ void PostFXPass::render_scene(Camera const& camera, RenderContext const& ctx) { any_godrays = render_godrays(camera, pipeline_->get_current_scene(eye), eye, ctx); render_glow(eye, ctx); - auto input_tex(inputs_[Pipeline::compositing]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); + auto input_tex(inputs_[Pipeline::shading]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); auto ping_tex(ping_buffer_->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); auto pong_tex(pong_buffer_->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); auto normal_tex(inputs_[Pipeline::geometry]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); @@ -533,7 +533,7 @@ void PostFXPass::render_glow(CameraMode eye, RenderContext const& ctx) { glow_shader_->set_uniform(ctx, pipeline_->config.bloom_threshold(), "gua_glow_threshold"); - auto color_buffer(inputs_[Pipeline::compositing]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); + auto color_buffer(inputs_[Pipeline::shading]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); ctx.render_context->set_viewport(scm::gl::viewport( math::vec2(0,0), math::vec2(float(glow_buffers_[0]->width()), float(glow_buffers_[0]->height())))); From f2ec7803dab0609b07cb1290a6ce30905d2ca65d Mon Sep 17 00:00:00 2001 From: Andre Schollmeyer Date: Thu, 28 Nov 2013 10:33:59 +0100 Subject: [PATCH 046/146] added shaderprogram to dll-interface, necessary for guacamole-oculus --- include/gua/renderer/ShaderProgram.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/gua/renderer/ShaderProgram.hpp b/include/gua/renderer/ShaderProgram.hpp index facd5e601..66cf8275a 100644 --- a/include/gua/renderer/ShaderProgram.hpp +++ b/include/gua/renderer/ShaderProgram.hpp @@ -57,7 +57,7 @@ struct ShaderProgramStage { * It combines data from a FragmentShader and a VertexShader in order to * achieve different visual appearances of the same mesh. */ -class ShaderProgram { +class GUA_DLL ShaderProgram { public: friend class GBufferNURBSUberShader; From e642bcde9c485e62c9cb3a00288eab8d345a1f41 Mon Sep 17 00:00:00 2001 From: Andre Schollmeyer Date: Thu, 28 Nov 2013 18:29:17 +0100 Subject: [PATCH 047/146] - improved cmake-build-system - added plugin-system - merged guacamole-oculus into plugin system --- CMakeLists.txt | 109 +- cmake/modules/define_macros.cmake | 37 + cmake/modules/find_assimp.cmake | 78 +- cmake/modules/find_boost.cmake | 146 +- cmake/modules/find_bullet.cmake | 69 +- cmake/modules/find_compiler.cmake | 27 +- cmake/modules/find_cuda.cmake | 293 +- cmake/modules/find_ev.cmake | 68 - cmake/modules/find_json.cmake | 79 +- cmake/modules/find_ovr.cmake | 121 + cmake/modules/find_schism.cmake | 146 +- plugins/CMakeLists.txt | 14 + plugins/guacamole-oculus/.gitignore | 9 + plugins/guacamole-oculus/CMakeLists.txt | 40 + .../example-oculus/CMakeLists.txt | 36 + .../data/materials/ComplexTexture.gsd | 55 + .../data/materials/Shadeless.gsd | 41 + .../example-oculus/data/materials/Stones.gmd | 12 + .../example-oculus/data/materials/White.gmd | 8 + .../data/objects/light_sphere.obj | 647 + .../example-oculus/data/objects/monkey.obj | 10833 ++++++++++++++++ .../data/textures/stones_diffuse.jpg | Bin 0 -> 158843 bytes .../data/textures/stones_normal.jpg | Bin 0 -> 196678 bytes .../data/textures/stones_specular.jpg | Bin 0 -> 111046 bytes .../guacamole-oculus/example-oculus/main.cpp | 257 + .../include/gua/OculusDeviceManager.hpp | 43 + .../include/gua/OculusRift.hpp | 78 + plugins/guacamole-oculus/readme.md | 1 + .../src/gua/OculusDeviceManager.cpp | 78 + .../guacamole-oculus/src/gua/OculusRift.cpp | 197 + src/CMakeLists.txt | 2 - src/gua/renderer/ShaderProgram.cpp | 16 - 32 files changed, 13044 insertions(+), 496 deletions(-) create mode 100644 cmake/modules/define_macros.cmake delete mode 100644 cmake/modules/find_ev.cmake create mode 100644 cmake/modules/find_ovr.cmake create mode 100644 plugins/CMakeLists.txt create mode 100644 plugins/guacamole-oculus/.gitignore create mode 100644 plugins/guacamole-oculus/CMakeLists.txt create mode 100644 plugins/guacamole-oculus/example-oculus/CMakeLists.txt create mode 100644 plugins/guacamole-oculus/example-oculus/data/materials/ComplexTexture.gsd create mode 100644 plugins/guacamole-oculus/example-oculus/data/materials/Shadeless.gsd create mode 100644 plugins/guacamole-oculus/example-oculus/data/materials/Stones.gmd create mode 100644 plugins/guacamole-oculus/example-oculus/data/materials/White.gmd create mode 100644 plugins/guacamole-oculus/example-oculus/data/objects/light_sphere.obj create mode 100644 plugins/guacamole-oculus/example-oculus/data/objects/monkey.obj create mode 100644 plugins/guacamole-oculus/example-oculus/data/textures/stones_diffuse.jpg create mode 100644 plugins/guacamole-oculus/example-oculus/data/textures/stones_normal.jpg create mode 100644 plugins/guacamole-oculus/example-oculus/data/textures/stones_specular.jpg create mode 100644 plugins/guacamole-oculus/example-oculus/main.cpp create mode 100644 plugins/guacamole-oculus/include/gua/OculusDeviceManager.hpp create mode 100644 plugins/guacamole-oculus/include/gua/OculusRift.hpp create mode 100644 plugins/guacamole-oculus/readme.md create mode 100644 plugins/guacamole-oculus/src/gua/OculusDeviceManager.cpp create mode 100644 plugins/guacamole-oculus/src/gua/OculusRift.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 6c44aecb6..646fa48bf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,10 +38,11 @@ elseif (WIN32) include(find_assimp) endif (UNIX) +include(define_macros) include(find_compiler) include(find_schism) include(find_boost) -include(find_cuda) +#include(find_cuda) include(find_bullet) include(find_json) @@ -52,7 +53,6 @@ set(LIBS ${JSON_LIBRARIES} ${GL_LIBRARIES} ${GLEW_LIBRARIES} - ${EV_LIBRARIES} ${BULLET_LIBRARIES} ${CUDA_LIBRARIES} #profiler @@ -79,7 +79,6 @@ set(INCLUDE_PATHS ${JSON_INCLUDE_DIRS} ${GL_INCLUDE_DIRS} ${GLEW_INCLUDE_DIRS} - ${EV_INCLUDE_DIRS} ${BULLET_INCLUDE_DIRS} ${CUDA_INCLUDE_DIRS} ) @@ -150,16 +149,6 @@ ELSEIF(MSVC) ENDIF(UNIX) - - - - - - - - - - ################################################################ # Create libraries ################################################################ @@ -168,53 +157,55 @@ file(MAKE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/lib) set(LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/lib) add_subdirectory(src) +################################################################ +# Create plugins +################################################################ + +add_subdirectory(plugins) + ################################################################ # Summary ################################################################ -message( "" ) -message( "Summary:" ) -message( " build type: ${CMAKE_BUILD_TYPE}" ) -message( "" ) -message( " schism:" ) -message( " library: ${SCHISM_LIBRARIES}" ) -message( " library path: ${SCHISM_LIBRARY_DIRS}" ) -message( " include: ${SCHISM_INCLUDE_DIRS}" ) -message( "" ) -message( " boost:" ) -message( " library: ${BOOST_LIBRARIES}" ) -message( " library path: ${BOOST_LIBRARY_DIRS}" ) -message( " include: ${BOOST_INCLUDE_DIRS}" ) -message( "" ) -message( " assimp:" ) -message( " library: ${ASSIMP_LIBRARIES}" ) -message( " library path: ${ASSIMP_LIBRARY_DIRS}" ) -message( " include: ${ASSIMP_INCLUDE_DIRS}" ) -message( "" ) -message( " json:" ) -message( " library: ${JSON_LIBRARIES}" ) -message( " library path: ${JSON_LIBRARY_DIRS}" ) -message( " include: ${JSON_INCLUDE_DIRS}" ) -message( "" ) -message( " cuda:" ) -message( " library: ${CUDA_LIBRARIES}" ) -message( " library path: ${CUDA_LIBRARY_DIRS}" ) -message( " include: ${CUDA_INCLUDE_DIRS}" ) -message( "" ) -message( " gl:" ) -message( " library: ${GL_LIBRARIES}" ) -message( " include: ${GL_INCLUDE_DIRS}" ) -message( "" ) -message( " glew:" ) -message( " library: ${GLEW_LIBRARIES}" ) -message( " include: ${GLEW_INCLUDE_DIRS}" ) -message( "" ) -message( " ev:" ) -message( " library: ${EV_LIBRARIES}" ) -message( " include: ${EV_INCLUDE_DIRS}" ) -message( "" ) -message( " bullet:" ) -message( " library: ${BULLET_LIBRARIES}" ) -message( " library path: ${BULLET_LIBRARY_DIRS}" ) -message( " include: ${BULLET_INCLUDE_DIRS}" ) -message( "" ) +message(STATUS "" ) +message(STATUS "Summary:" ) +message(STATUS " build type: ${CMAKE_BUILD_TYPE}" ) +message(STATUS "" ) +message(STATUS " schism:" ) +message(STATUS " library: ${SCHISM_LIBRARIES}" ) +message(STATUS " library path: ${SCHISM_LIBRARY_DIRS}" ) +message(STATUS " include: ${SCHISM_INCLUDE_DIRS}" ) +message(STATUS "" ) +message(STATUS " boost:" ) +message(STATUS " library: ${BOOST_LIBRARIES}" ) +message(STATUS " library path: ${BOOST_LIBRARY_DIRS}" ) +message(STATUS " include: ${BOOST_INCLUDE_DIRS}" ) +message(STATUS "" ) +message(STATUS " assimp:" ) +message(STATUS " library: ${ASSIMP_LIBRARIES}" ) +message(STATUS " library path: ${ASSIMP_LIBRARY_DIRS}" ) +message(STATUS " include: ${ASSIMP_INCLUDE_DIRS}" ) +message(STATUS "" ) +message(STATUS " json:" ) +message(STATUS " library: ${JSON_LIBRARIES}" ) +message(STATUS " library path: ${JSON_LIBRARY_DIRS}" ) +message(STATUS " include: ${JSON_INCLUDE_DIRS}" ) +message(STATUS "" ) +message(STATUS " cuda:" ) +message(STATUS " library: ${CUDA_LIBRARIES}" ) +message(STATUS " library path: ${CUDA_LIBRARY_DIRS}" ) +message(STATUS " include: ${CUDA_INCLUDE_DIRS}" ) +message(STATUS "" ) +message(STATUS " gl:" ) +message(STATUS " library: ${GL_LIBRARIES}" ) +message(STATUS " include: ${GL_INCLUDE_DIRS}" ) +message(STATUS "" ) +message(STATUS " glew:" ) +message(STATUS " library: ${GLEW_LIBRARIES}" ) +message(STATUS " include: ${GLEW_INCLUDE_DIRS}" ) +message(STATUS "" ) +message(STATUS " bullet:" ) +message(STATUS " library: ${BULLET_LIBRARIES}" ) +message(STATUS " library path: ${BULLET_LIBRARY_DIRS}" ) +message(STATUS " include: ${BULLET_INCLUDE_DIRS}" ) +message(STATUS "" ) diff --git a/cmake/modules/define_macros.cmake b/cmake/modules/define_macros.cmake new file mode 100644 index 000000000..4ea8ebaff --- /dev/null +++ b/cmake/modules/define_macros.cmake @@ -0,0 +1,37 @@ +############################################################################### + +MACRO(GET_SUBDIRECTORIES subdirectories current_directory) + + FILE(GLOB directory_content RELATIVE ${current_directory} *) + SET(dirlist "") + + FOREACH(child ${directory_content}) + + IF(IS_DIRECTORY ${current_directory}/${child}) + SET(dirlist ${dirlist} ${child}) + ENDIF() + + ENDFOREACH() + + SET(${subdirectories} ${dirlist}) + +ENDMACRO() + +############################################################################### + +# copy_runtime_dependencies [target_name] [path_to_runtime_libraries] [executable_path] +MACRO(COPY_RUNTIME_DEPENDENCIES _TARGET_NAME _RUNTIME_LIBRARY_PATH _TARGET_PATH) + + SET ( _COPY_COMMAND "copy" ) + SET ( _PUSH_DIRECTORY_COMMAND "pushd" ) + SET ( _POP_DIRECTORY_COMMAND "popd" ) + + IF (WIN32) + SET(_POST_PROCESS_COMMAND + ${_PUSH_DIRECTORY_COMMAND} ${_RUNTIME_LIBRARY_PATH} \n + ${_COPY_COMMAND} *.dll ${_TARGET_PATH} \n + ${_POP_DIRECTORY_COMMAND}) + ADD_CUSTOM_COMMAND ( TARGET ${_TARGET_NAME} POST_BUILD COMMAND ${_POST_PROCESS_COMMAND}) + ENDIF(WIN32) + +ENDMACRO(COPY_RUNTIME_DEPENDENCIES) diff --git a/cmake/modules/find_assimp.cmake b/cmake/modules/find_assimp.cmake index c61811c8b..b9284b58d 100644 --- a/cmake/modules/find_assimp.cmake +++ b/cmake/modules/find_assimp.cmake @@ -1,14 +1,49 @@ +############################################################################## +# search paths +############################################################################## SET(ASSIMP_INCLUDE_SEARCH_DIRS ${GLOBAL_EXT_DIR}/inc/assimp /opt/assimp/current + /usr/include ) SET(ASSIMP_LIBRARY_SEARCH_DIRS ${GLOBAL_EXT_DIR}/lib /opt/assimp/current/assimp-build + /usr/lib ) -message("-- checking for ASSIMP") +############################################################################## +# feedback to provide user-defined paths to search for assimp +############################################################################## +MACRO (request_assimp_search_directories) + + IF ( NOT ASSIMP_INCLUDE_DIRS AND NOT ASSIMP_LIBRARY_DIRS ) + SET(ASSIMP_INCLUDE_SEARCH_DIR "Please provide assimp include path." CACHE PATH "path to assimp headers.") + SET(ASSIMP_LIBRARY_SEARCH_DIR "Please provide assimp library path." CACHE PATH "path to assimp libraries.") + MESSAGE(FATAL_ERROR "find_assimp.cmake: unable to find assimp.") + ENDIF ( NOT ASSIMP_INCLUDE_DIRS AND NOT ASSIMP_LIBRARY_DIRS ) + + IF ( NOT ASSIMP_INCLUDE_DIRS ) + SET(ASSIMP_INCLUDE_SEARCH_DIR "Please provide assimp include path." CACHE PATH "path to assimp headers.") + MESSAGE(FATAL_ERROR "find_assimp.cmake: unable to find assimp headers.") + ELSE ( NOT ASSIMP_INCLUDE_DIRS ) + UNSET(ASSIMP_INCLUDE_SEARCH_DIR CACHE) + ENDIF ( NOT ASSIMP_INCLUDE_DIRS ) + + IF ( NOT ASSIMP_LIBRARY_DIRS ) + SET(ASSIMP_LIBRARY_SEARCH_DIR "Please provide assimp library path." CACHE PATH "path to assimp libraries.") + MESSAGE(FATAL_ERROR "find_assimp.cmake: unable to find assimp libraries.") + ELSE ( NOT ASSIMP_LIBRARY_DIRS ) + UNSET(ASSIMP_LIBRARY_SEARCH_DIR CACHE) + ENDIF ( NOT ASSIMP_LIBRARY_DIRS ) + +ENDMACRO (request_assimp_search_directories) + +############################################################################## +# check for assimp +############################################################################## +message(STATUS "-- checking for ASSIMP") IF (NOT ASSIMP_INCLUDE_DIRS) @@ -26,13 +61,17 @@ IF (NOT ASSIMP_INCLUDE_DIRS) ENDFOREACH(_SEARCH_DIR ${ASSIMP_INCLUDE_SEARCH_DIRS}) IF (NOT _ASSIMP_FOUND_INC_DIRS) - MESSAGE(FATAL_ERROR "find_assimp.cmake: unable to find assimp headers") + request_assimp_search_directories() ENDIF (NOT _ASSIMP_FOUND_INC_DIRS) - + FOREACH(_INC_DIR ${_ASSIMP_FOUND_INC_DIRS}) - LIST(APPEND ASSIMP_INCLUDE_DIRS ${_INC_DIR}) + LIST(APPEND _ASSIMP_INCLUDE_DIRS ${_INC_DIR}) ENDFOREACH(_INC_DIR ${_BOOST_FOUND_INC_DIRS}) + IF (_ASSIMP_FOUND_INC_DIRS) + SET(ASSIMP_INCLUDE_DIRS ${_ASSIMP_INCLUDE_DIRS} CACHE PATH "path to assimp headers.") + ENDIF (_ASSIMP_FOUND_INC_DIRS) + ENDIF(NOT ASSIMP_INCLUDE_DIRS) @@ -43,8 +82,7 @@ ELSEIF(WIN32) ENDIF(UNIX) -IF ( ASSIMP_INCLUDE_DIRS - AND NOT ASSIMP_LIBRARIES) +IF ( ASSIMP_INCLUDE_DIRS AND ( NOT ASSIMP_LIBRARY_DIRS OR NOT ASSIMP_LIBRARIES)) SET(_ASSIMP_FOUND_LIB_DIR "") SET(_ASSIMP_POSTFIX "") @@ -62,19 +100,29 @@ IF ( ASSIMP_INCLUDE_DIRS ENDFOREACH(_SEARCH_DIR ${ASSIMP_LIBRARY_SEARCH_DIRS}) IF (NOT _ASSIMP_FOUND_LIB_DIR) - MESSAGE(FATAL_ERROR "find_assimp.cmake: unable to find assimp libraries") + request_assimp_search_directories() ELSE (NOT _ASSIMP_FOUND_LIB_DIR) SET(ASSIMP_LIBRARY_DIRS ${_ASSIMP_FOUND_LIB_DIR} CACHE STRING "The assimp library directory") - message("-- found matching version") ENDIF (NOT _ASSIMP_FOUND_LIB_DIR) - + FOREACH(_LIB_DIR ${_ASSIMP_FOUND_LIB_DIR}) - LIST(APPEND ASSIMP_LIBRARIES ${ASSIMP_LIB_FILENAME}) + LIST(APPEND _ASSIMP_LIBRARIES ${ASSIMP_LIB_FILENAME}) ENDFOREACH(_LIB_DIR ${_ASSIMP_FOUND_INC_DIRS}) - - -ENDIF( ASSIMP_INCLUDE_DIRS - AND NOT ASSIMP_LIBRARIES) - + IF (_ASSIMP_FOUND_LIB_DIR) + SET(ASSIMP_LIBRARIES ${_ASSIMP_LIBRARIES} CACHE PATH "path to assimp library.") + ENDIF (_ASSIMP_FOUND_LIB_DIR) + +ENDIF ( ASSIMP_INCLUDE_DIRS AND ( NOT ASSIMP_LIBRARY_DIRS OR NOT ASSIMP_LIBRARIES)) + +############################################################################## +# verify +############################################################################## +IF ( NOT ASSIMP_INCLUDE_DIRS OR NOT ASSIMP_LIBRARY_DIRS ) + request_assimp_search_directories() +ELSE ( NOT ASSIMP_INCLUDE_DIRS OR NOT ASSIMP_LIBRARY_DIRS ) + UNSET(ASSIMP_INCLUDE_SEARCH_DIR CACHE) + UNSET(ASSIMP_LIBRARY_SEARCH_DIR CACHE) + MESSAGE(STATUS "-- found matching assimp version") +ENDIF ( NOT ASSIMP_INCLUDE_DIRS OR NOT ASSIMP_LIBRARY_DIRS ) diff --git a/cmake/modules/find_boost.cmake b/cmake/modules/find_boost.cmake index 7608b00c7..0406b2732 100644 --- a/cmake/modules/find_boost.cmake +++ b/cmake/modules/find_boost.cmake @@ -1,28 +1,24 @@ ############################################################################## # set search directories ############################################################################## -SET(AVANGO_BOOST_MIN_VERSION_MAJOR 1) -SET(AVANGO_BOOST_MIN_VERSION_MINOR 46) -SET(AVANGO_BOOST_MIN_VERSION_SUBMINOR 0) -SET(AVANGO_BOOST_MIN_VERSION "${AVANGO_BOOST_MIN_VERSION_MAJOR}.${AVANGO_BOOST_MIN_VERSION_MINOR}.${AVANGO_BOOST_MIN_VERSION_SUBMINOR}") -MATH(EXPR AVANGO_BOOST_MIN_VERSION_NUM "${AVANGO_BOOST_MIN_VERSION_MAJOR}*10000 + ${AVANGO_BOOST_MIN_VERSION_MINOR}*100 + ${AVANGO_BOOST_MIN_VERSION_SUBMINOR}") +SET(BOOST_MIN_VERSION_MAJOR 1) +SET(BOOST_MIN_VERSION_MINOR 46) +SET(BOOST_MIN_VERSION_SUBMINOR 0) +SET(BOOST_MIN_VERSION "${BOOST_MIN_VERSION_MAJOR}.${BOOST_MIN_VERSION_MINOR}.${BOOST_MIN_VERSION_SUBMINOR}") +MATH(EXPR BOOST_MIN_VERSION_NUM "${BOOST_MIN_VERSION_MAJOR}*10000 + ${BOOST_MIN_VERSION_MINOR}*100 + ${BOOST_MIN_VERSION_SUBMINOR}") -SET(AVANGO_BOOST_INCLUDE_SEARCH_DIRS +SET(BOOST_INCLUDE_SEARCH_DIRS ${GLOBAL_EXT_DIR}/inc/boost - ${GUACAMOLE_EXT_DIR}/inc/boost ${BOOST_INCLUDE_SEARCH_DIR} - ${BOOST_INCLUDE_DIRS} /opt/boost/latest/include ${CMAKE_SYSTEM_INCLUDE_PATH} ${CMAKE_INCLUDE_PATH} /usr/include ) -SET(AVANGO_BOOST_LIBRARY_SEARCH_DIRS +SET(BOOST_LIBRARY_SEARCH_DIRS ${GLOBAL_EXT_DIR}/lib - ${GUACAMOLE_EXT_DIR}/lib ${BOOST_LIBRARY_SEARCH_DIR} - ${BOOST_LIBRARY_DIRS} /opt/boost/latest/lib ${CMAKE_SYSTEM_LIBRARY_PATH} ${CMAKE_LIBRARY_PATH} @@ -60,39 +56,39 @@ ENDMACRO (request_boost_search_directories) # start search ############################################################################## -message("-- checking for BOOST") +message(STATUS "-- checking for BOOST") IF ( NOT BOOST_INCLUDE_DIRS ) - SET(_AVANGO_BOOST_FOUND_INC_DIRS "") - FOREACH(_SEARCH_DIR ${AVANGO_BOOST_INCLUDE_SEARCH_DIRS}) + SET(_BOOST_FOUND_INC_DIRS "") + FOREACH(_SEARCH_DIR ${BOOST_INCLUDE_SEARCH_DIRS}) FIND_PATH(_CUR_SEARCH NAMES boost/config.hpp PATHS ${_SEARCH_DIR} NO_DEFAULT_PATH) IF (_CUR_SEARCH) - LIST(APPEND _AVANGO_BOOST_FOUND_INC_DIRS ${_CUR_SEARCH}) + LIST(APPEND _BOOST_FOUND_INC_DIRS ${_CUR_SEARCH}) ENDIF(_CUR_SEARCH) SET(_CUR_SEARCH _CUR_SEARCH-NOTFOUND CACHE INTERNAL "internal use") - ENDFOREACH(_SEARCH_DIR ${AVANGO_BOOST_INCLUDE_SEARCH_DIRS}) + ENDFOREACH(_SEARCH_DIR ${BOOST_INCLUDE_SEARCH_DIRS}) - IF (NOT _AVANGO_BOOST_FOUND_INC_DIRS) + IF (NOT _BOOST_FOUND_INC_DIRS) request_boost_search_directories() - ELSE (NOT _AVANGO_BOOST_FOUND_INC_DIRS) - SET(BOOST_INCLUDE_DIRS ${_AVANGO_BOOST_FOUND_INC_DIRS}) - ENDIF (NOT _AVANGO_BOOST_FOUND_INC_DIRS) + ELSE (NOT _BOOST_FOUND_INC_DIRS) + SET(BOOST_INCLUDE_DIRS ${_BOOST_FOUND_INC_DIRS}) + ENDIF (NOT _BOOST_FOUND_INC_DIRS) - SET(AVANGO_BOOST_VERSION 0) - SET(AVANGO_BOOST_LIB_VERSION "") - SET(AVANGO_BOOST_LIB_SUFFIX "") + SET(BOOST_VERSION 0) + SET(BOOST_LIB_VERSION "") + SET(BOOST_LIB_SUFFIX "") - SET(_AVANGO_BOOST_CUR_VERSION ${AVANGO_BOOST_MIN_VERSION_NUM}) + SET(_BOOST_CUR_VERSION ${BOOST_MIN_VERSION_NUM}) - FOREACH(_INC_DIR ${_AVANGO_BOOST_FOUND_INC_DIRS}) - FILE(READ "${_INC_DIR}/boost/version.hpp" _AVANGO_BOOST_VERSION_CONTENTS) + FOREACH(_INC_DIR ${_BOOST_FOUND_INC_DIRS}) + FILE(READ "${_INC_DIR}/boost/version.hpp" _BOOST_VERSION_CONTENTS) - STRING(REGEX REPLACE ".*#define BOOST_VERSION ([0-9]+).*" "\\1" _BOOST_VERSION "${_AVANGO_BOOST_VERSION_CONTENTS}") - STRING(REGEX REPLACE ".*#define BOOST_LIB_VERSION \"([0-9_]+)\".*" "\\1" _BOOST_LIB_VERSION "${_AVANGO_BOOST_VERSION_CONTENTS}") + STRING(REGEX REPLACE ".*#define BOOST_VERSION ([0-9]+).*" "\\1" _BOOST_VERSION "${_BOOST_VERSION_CONTENTS}") + STRING(REGEX REPLACE ".*#define BOOST_LIB_VERSION \"([0-9_]+)\".*" "\\1" _BOOST_LIB_VERSION "${_BOOST_VERSION_CONTENTS}") IF(NOT "${_BOOST_VERSION}" STREQUAL "0") MATH(EXPR _BOOST_MAJOR_VERSION "${_BOOST_VERSION} / 100000") @@ -104,93 +100,93 @@ IF ( NOT BOOST_INCLUDE_DIRS ) MATH(EXPR _BOOST_VERSION_NUM "${_BOOST_MAJOR_VERSION}*10000 + ${_BOOST_MINOR_VERSION}*100 + ${_BOOST_SUBMINOR_VERSION}") - IF ( _AVANGO_BOOST_CUR_VERSION LESS _BOOST_VERSION_NUM - OR _AVANGO_BOOST_CUR_VERSION EQUAL _BOOST_VERSION_NUM) - SET(AVANGO_BOOST_VERSION ${_BOOST_VERSION}) - SET(AVANGO_BOOST_LIB_VERSION ${_BOOST_LIB_VERSION}) - SET(AVANGO_BOOST_LIB_SUFFIX ".${_BOOST_MAJOR_VERSION}.${_BOOST_MINOR_VERSION}.${_BOOST_SUBMINOR_VERSION}") + IF ( _BOOST_CUR_VERSION LESS _BOOST_VERSION_NUM + OR _BOOST_CUR_VERSION EQUAL _BOOST_VERSION_NUM) + SET(BOOST_VERSION ${_BOOST_VERSION}) + SET(BOOST_LIB_VERSION ${_BOOST_LIB_VERSION}) + SET(BOOST_LIB_SUFFIX ".${_BOOST_MAJOR_VERSION}.${_BOOST_MINOR_VERSION}.${_BOOST_SUBMINOR_VERSION}") SET(BOOST_INCLUDE_DIRS ${_INC_DIR}) - SET(_AVANGO_BOOST_CUR_VERSION ${_BOOST_VERSION_NUM}) - ENDIF ( _AVANGO_BOOST_CUR_VERSION LESS _BOOST_VERSION_NUM - OR _AVANGO_BOOST_CUR_VERSION EQUAL _BOOST_VERSION_NUM) + SET(_BOOST_CUR_VERSION ${_BOOST_VERSION_NUM}) + ENDIF ( _BOOST_CUR_VERSION LESS _BOOST_VERSION_NUM + OR _BOOST_CUR_VERSION EQUAL _BOOST_VERSION_NUM) - ENDFOREACH(_INC_DIR ${_AVANGO_BOOST_FOUND_INC_DIRS}) + ENDFOREACH(_INC_DIR ${_BOOST_FOUND_INC_DIRS}) - IF (AVANGO_BOOST_VERSION EQUAL 0) - MESSAGE(FATAL_ERROR "found boost versions ${_BOOST_VERSION} to old (min. version ${AVANGO_BOOST_MIN_VERSION} required)") + IF (BOOST_VERSION EQUAL 0) + MESSAGE(FATAL_ERROR "found boost versions ${_BOOST_VERSION} to old (min. version ${BOOST_MIN_VERSION} required)") request_boost_search_directories() - ELSE (AVANGO_BOOST_VERSION EQUAL 0) + ELSE (BOOST_VERSION EQUAL 0) SET(BOOST_INCLUDE_DIRS ${BOOST_INCLUDE_DIRS} CACHE STRING "The boost include directory") - SET(AVANGO_BOOST_VERSION ${AVANGO_BOOST_VERSION} CACHE STRING "The boost version number") - SET(AVANGO_BOOST_LIB_SUFFIX ${AVANGO_BOOST_LIB_SUFFIX} CACHE STRING "The boost library suffix") - SET(AVANGO_BOOST_LIB_VERSION ${AVANGO_BOOST_LIB_VERSION} CACHE STRING "The boost library version string") - ENDIF (AVANGO_BOOST_VERSION EQUAL 0) + SET(BOOST_VERSION ${BOOST_VERSION} CACHE STRING "The boost version number") + SET(BOOST_LIB_SUFFIX ${BOOST_LIB_SUFFIX} CACHE STRING "The boost library suffix") + SET(BOOST_LIB_VERSION ${BOOST_LIB_VERSION} CACHE STRING "The boost library version string") + ENDIF (BOOST_VERSION EQUAL 0) ENDIF ( NOT BOOST_INCLUDE_DIRS ) IF ( BOOST_INCLUDE_DIRS AND NOT BOOST_LIBRARY_DIRS ) - SET(_AVANGO_BOOST_FILESYSTEM_LIB "") - SET(_AVANGO_BOOST_FOUND_LIB_DIR "") - SET(_AVANGO_BOOST_POSTFIX "") + SET(_BOOST_FILESYSTEM_LIB "") + SET(_BOOST_FOUND_LIB_DIR "") + SET(_BOOST_POSTFIX "") if (UNIX) - LIST(APPEND _AVANGO_BOOST_FILESYSTEM_LIB "${CMAKE_SHARED_LIBRARY_PREFIX}boost_filesystem${COMPILER_SUFFIX}${AVANGO_BOOST_LIB_VERSION}${CMAKE_SHARED_LIBRARY_SUFFIX}${AVANGO_BOOST_LIB_SUFFIX}" - "${CMAKE_SHARED_LIBRARY_PREFIX}boost_filesystem${CMAKE_SHARED_LIBRARY_SUFFIX}${AVANGO_BOOST_LIB_SUFFIX}" - "${CMAKE_SHARED_LIBRARY_PREFIX}boost_filesystem${CMAKE_SHARED_LIBRARY_SUFFIX}${AVANGO_BOOST_LIB_SUFFIX}") + LIST(APPEND _BOOST_FILESYSTEM_LIB "${CMAKE_SHARED_LIBRARY_PREFIX}boost_filesystem${COMPILER_SUFFIX}${BOOST_LIB_VERSION}${CMAKE_SHARED_LIBRARY_SUFFIX}${BOOST_LIB_SUFFIX}" + "${CMAKE_SHARED_LIBRARY_PREFIX}boost_filesystem${CMAKE_SHARED_LIBRARY_SUFFIX}${BOOST_LIB_SUFFIX}" + "${CMAKE_SHARED_LIBRARY_PREFIX}boost_filesystem${CMAKE_SHARED_LIBRARY_SUFFIX}${BOOST_LIB_SUFFIX}") elseif (WIN32) - LIST(APPEND _AVANGO_BOOST_FILESYSTEM_LIB "${CMAKE_STATIC_LIBRARY_PREFIX}libboost_filesystem-${COMPILER_SUFFIX}-mt-${AVANGO_BOOST_LIB_VERSION}${CMAKE_STATIC_LIBRARY_SUFFIX}" + LIST(APPEND _BOOST_FILESYSTEM_LIB "${CMAKE_STATIC_LIBRARY_PREFIX}libboost_filesystem-${COMPILER_SUFFIX}-mt-${BOOST_LIB_VERSION}${CMAKE_STATIC_LIBRARY_SUFFIX}" "${CMAKE_STATIC_LIBRARY_PREFIX}libboost_filesystem-mt${CMAKE_STATIC_LIBRARY_SUFFIX}") endif (UNIX) - FOREACH(_SEARCH_DIR ${AVANGO_BOOST_LIBRARY_SEARCH_DIRS}) + FOREACH(_SEARCH_DIR ${BOOST_LIBRARY_SEARCH_DIRS}) FIND_PATH(_CUR_SEARCH - NAMES ${_AVANGO_BOOST_FILESYSTEM_LIB} + NAMES ${_BOOST_FILESYSTEM_LIB} PATHS ${_SEARCH_DIR} PATH_SUFFIXES debug release . NO_DEFAULT_PATH) IF (_CUR_SEARCH) - LIST(APPEND _AVANGO_BOOST_FOUND_LIB_DIR ${_SEARCH_DIR}) + LIST(APPEND _BOOST_FOUND_LIB_DIR ${_SEARCH_DIR}) if (UNIX) - FIND_FILE(_CUR_SEARCH_FILE NAMES ${_AVANGO_BOOST_FILESYSTEM_LIB} PATHS ${_CUR_SEARCH}) + FIND_FILE(_CUR_SEARCH_FILE NAMES ${_BOOST_FILESYSTEM_LIB} PATHS ${_CUR_SEARCH}) if (_CUR_SEARCH_FILE) LIST(APPEND BOOST_LIBRARIES ${_CUR_SEARCH_FILE}) - STRING(REGEX REPLACE "${_CUR_SEARCH}/${CMAKE_SHARED_LIBRARY_PREFIX}boost_filesystem(.*).so(.*)" "\\2" _AVANGO_BOOST_UNIX_LIB_SUF ${_CUR_SEARCH_FILE}) - if (${_AVANGO_BOOST_UNIX_LIB_SUF} STREQUAL ${AVANGO_BOOST_LIB_SUFFIX}) - list(APPEND BOOST_LIBRARIES _AVANGO_BOOST_FILESYSTEM_LIB) - STRING(REGEX REPLACE "${_CUR_SEARCH}/${CMAKE_SHARED_LIBRARY_PREFIX}boost_filesystem(.*).so(.*)" "\\1" _AVANGO_BOOST_POSTFIX ${_CUR_SEARCH_FILE}) - endif (${_AVANGO_BOOST_UNIX_LIB_SUF} STREQUAL ${AVANGO_BOOST_LIB_SUFFIX}) + STRING(REGEX REPLACE "${_CUR_SEARCH}/${CMAKE_SHARED_LIBRARY_PREFIX}boost_filesystem(.*).so(.*)" "\\2" _BOOST_UNIX_LIB_SUF ${_CUR_SEARCH_FILE}) + if (${_BOOST_UNIX_LIB_SUF} STREQUAL ${BOOST_LIB_SUFFIX}) + list(APPEND BOOST_LIBRARIES _BOOST_FILESYSTEM_LIB) + STRING(REGEX REPLACE "${_CUR_SEARCH}/${CMAKE_SHARED_LIBRARY_PREFIX}boost_filesystem(.*).so(.*)" "\\1" _BOOST_POSTFIX ${_CUR_SEARCH_FILE}) + endif (${_BOOST_UNIX_LIB_SUF} STREQUAL ${BOOST_LIB_SUFFIX}) endif (_CUR_SEARCH_FILE) SET(_CUR_SEARCH_FILE _CUR_SEARCH_FILE-NOTFOUND CACHE INTERNAL "internal use") endif (UNIX) ENDIF(_CUR_SEARCH) SET(_CUR_SEARCH _CUR_SEARCH-NOTFOUND CACHE INTERNAL "internal use") - ENDFOREACH(_SEARCH_DIR ${AVANGO_BOOST_LIBRARY_SEARCH_DIRS}) + ENDFOREACH(_SEARCH_DIR ${BOOST_LIBRARY_SEARCH_DIRS}) - IF (NOT _AVANGO_BOOST_FOUND_LIB_DIR) + IF (NOT _BOOST_FOUND_LIB_DIR) MESSAGE(FATAL_ERROR "guacamole_boost.cmake: unable to find boost library") - ELSE (NOT _AVANGO_BOOST_FOUND_LIB_DIR) - SET(BOOST_LIBRARY_DIRS ${_AVANGO_BOOST_FOUND_LIB_DIR} CACHE PATH "boost library path.") + ELSE (NOT _BOOST_FOUND_LIB_DIR) + SET(BOOST_LIBRARY_DIRS ${_BOOST_FOUND_LIB_DIR} CACHE PATH "boost library path.") IF (UNIX) - FILE(GLOB BOOST_LIBRARIES ${_AVANGO_BOOST_FOUND_LIB_DIR}/*.so) + FILE(GLOB BOOST_LIBRARIES ${_BOOST_FOUND_LIB_DIR}/*.so) ELSEIF (MSVC) # use boost auto link ENDIF (UNIX) - ENDIF (NOT _AVANGO_BOOST_FOUND_LIB_DIR) + ENDIF (NOT _BOOST_FOUND_LIB_DIR) if (UNIX) - set(AVANGO_BOOST_MT_REL "${_AVANGO_BOOST_POSTFIX}" CACHE STRING "boost dynamic library release postfix") - set(AVANGO_BOOST_MT_DBG "${_AVANGO_BOOST_POSTFIX}" CACHE STRING "boost dynamic library debug postfix") - set(AVANGO_BOOST_MT_S_REL "${_AVANGO_BOOST_POSTFIX}" CACHE STRING "boost static library release postfix") - set(AVANGO_BOOST_MT_S_DBG "${_AVANGO_BOOST_POSTFIX}" CACHE STRING "boost static library debug postfix") + set(BOOST_MT_REL "${_BOOST_POSTFIX}" CACHE STRING "boost dynamic library release postfix") + set(BOOST_MT_DBG "${_BOOST_POSTFIX}" CACHE STRING "boost dynamic library debug postfix") + set(BOOST_MT_S_REL "${_BOOST_POSTFIX}" CACHE STRING "boost static library release postfix") + set(BOOST_MT_S_DBG "${_BOOST_POSTFIX}" CACHE STRING "boost static library debug postfix") elseif (WIN32) - set(AVANGO_BOOST_MT_REL "${COMPILER_SUFFIX}-mt-${AVANGO_BOOST_LIB_VERSION}" CACHE STRING "boost dynamic library release postfix") - set(AVANGO_BOOST_MT_DBG "${COMPILER_SUFFIX}-mt-gd-${AVANGO_BOOST_LIB_VERSION}" CACHE STRING "boost dynamic library debug postfix") - set(AVANGO_BOOST_MT_S_REL "${COMPILER_SUFFIX}-mt-s-${AVANGO_BOOST_LIB_VERSION}" CACHE STRING "boost static library release postfix") - set(AVANGO_BOOST_MT_S_DBG "${COMPILER_SUFFIX}-mt-sgd-${AVANGO_BOOST_LIB_VERSION}" CACHE STRING "boost static library debug postfix") + set(BOOST_MT_REL "${COMPILER_SUFFIX}-mt-${BOOST_LIB_VERSION}" CACHE STRING "boost dynamic library release postfix") + set(BOOST_MT_DBG "${COMPILER_SUFFIX}-mt-gd-${BOOST_LIB_VERSION}" CACHE STRING "boost dynamic library debug postfix") + set(BOOST_MT_S_REL "${COMPILER_SUFFIX}-mt-s-${BOOST_LIB_VERSION}" CACHE STRING "boost static library release postfix") + set(BOOST_MT_S_DBG "${COMPILER_SUFFIX}-mt-sgd-${BOOST_LIB_VERSION}" CACHE STRING "boost static library debug postfix") endif (UNIX) ENDIF ( BOOST_INCLUDE_DIRS AND NOT BOOST_LIBRARY_DIRS ) @@ -203,5 +199,5 @@ IF ( NOT BOOST_INCLUDE_DIRS OR NOT BOOST_LIBRARY_DIRS ) ELSE ( NOT BOOST_INCLUDE_DIRS OR NOT BOOST_LIBRARY_DIRS ) UNSET(BOOST_INCLUDE_SEARCH_DIR CACHE) UNSET(BOOST_LIBRARY_SEARCH_DIR CACHE) - MESSAGE("-- found matching boost version") + MESSAGE(STATUS "-- found matching boost version") ENDIF ( NOT BOOST_INCLUDE_DIRS OR NOT BOOST_LIBRARY_DIRS ) \ No newline at end of file diff --git a/cmake/modules/find_bullet.cmake b/cmake/modules/find_bullet.cmake index 6ae7ee356..4971205ed 100644 --- a/cmake/modules/find_bullet.cmake +++ b/cmake/modules/find_bullet.cmake @@ -1,14 +1,48 @@ SET(BULLET_INCLUDE_SEARCH_DIRS - ${GLOBAL_EXT_DIR}/inc/bullet - /opt/bullet/current + ${GLOBAL_EXT_DIR}/inc/bullet + ${BULLET_INCLUDE_SEARCH_DIR} + ${BULLET_INCLUDE_DIRS} + /opt/bullet/current ) SET(BULLET_LIBRARY_SEARCH_DIRS - ${GLOBAL_EXT_DIR}/lib + ${GLOBAL_EXT_DIR}/lib + ${BULLET_LIBRARY_SEARCH_DIR} + ${BULLET_LIBRARY_DIRS} /opt/bullet/current/bullet-build ) -message("-- checking for BULLET") +############################################################################## +# feedback to provide user-defined paths to search for bullet +############################################################################## +MACRO (request_bullet_search_directories) + + IF ( NOT BULLET_INCLUDE_DIRS AND NOT BULLET_LIBRARY_DIRS ) + SET(BULLET_INCLUDE_SEARCH_DIR "Please provide bullet include path." CACHE PATH "path to bullet headers.") + SET(BULLET_LIBRARY_SEARCH_DIR "Please provide bullet library path." CACHE PATH "path to bullet libraries.") + MESSAGE(FATAL_ERROR "find_bullet.cmake: unable to find bullet.") + ENDIF ( NOT BULLET_INCLUDE_DIRS AND NOT BULLET_LIBRARY_DIRS ) + + IF ( NOT BULLET_INCLUDE_DIRS ) + SET(BULLET_INCLUDE_SEARCH_DIR "Please provide bullet include path." CACHE PATH "path to bullet headers.") + MESSAGE(FATAL_ERROR "find_bullet.cmake: unable to find bullet headers.") + ELSE ( NOT BULLET_INCLUDE_DIRS ) + UNSET(BULLET_INCLUDE_SEARCH_DIR CACHE) + ENDIF ( NOT BULLET_INCLUDE_DIRS ) + + IF ( NOT BULLET_LIBRARY_DIRS ) + SET(BULLET_LIBRARY_SEARCH_DIR "Please provide bullet library path." CACHE PATH "path to bullet libraries.") + MESSAGE(FATAL_ERROR "find_bullet.cmake: unable to find bullet libraries.") + ELSE ( NOT BULLET_LIBRARY_DIRS ) + UNSET(BULLET_LIBRARY_SEARCH_DIR CACHE) + ENDIF ( NOT BULLET_LIBRARY_DIRS ) + +ENDMACRO (request_bullet_search_directories) + +############################################################################## +# search +############################################################################## +message(STATUS "-- checking for BULLET") IF (NOT BULLET_INCLUDE_DIRS) @@ -26,14 +60,18 @@ IF (NOT BULLET_INCLUDE_DIRS) ENDFOREACH(_SEARCH_DIR ${BULLET_INCLUDE_SEARCH_DIRS}) IF (NOT _BULLET_FOUND_INC_DIRS) - MESSAGE(FATAL_ERROR "find_bullet.cmake: unable to find bullet headers") + request_bullet_search_directories() ENDIF (NOT _BULLET_FOUND_INC_DIRS) FOREACH(_INC_DIR ${_BULLET_FOUND_INC_DIRS}) - LIST(APPEND BULLET_INCLUDE_DIRS ${_INC_DIR}/src) - LIST(APPEND BULLET_INCLUDE_DIRS ${_INC_DIR}/Extras/HACD) + LIST(APPEND _BULLET_INCLUDE_DIRS ${_INC_DIR}/src) + LIST(APPEND _BULLET_INCLUDE_DIRS ${_INC_DIR}/Extras/HACD) ENDFOREACH(_INC_DIR ${_BOOST_FOUND_INC_DIRS}) + IF (_BULLET_FOUND_INC_DIRS) + SET(BULLET_INCLUDE_DIRS ${_BULLET_INCLUDE_DIRS} CACHE PATH "paths to bullet headers.") + ENDIF (_BULLET_FOUND_INC_DIRS) + ENDIF(NOT BULLET_INCLUDE_DIRS) @@ -70,7 +108,6 @@ IF ( BULLET_INCLUDE_DIRS PATHS ${_SEARCH_DIR} PATH_SUFFIXES debug release ${BULLET_DYNAMICS_LIB_SUFFIX} NO_DEFAULT_PATH) - message(${_SEARCH_DIR}) IF (_CUR_SEARCH) LIST(APPEND _BULLET_FOUND_LIB_DIR ${_SEARCH_DIR}) ENDIF(_CUR_SEARCH) @@ -78,10 +115,9 @@ IF ( BULLET_INCLUDE_DIRS ENDFOREACH(_SEARCH_DIR ${BULLET_LIBRARY_SEARCH_DIRS}) IF (NOT _BULLET_FOUND_LIB_DIR) - MESSAGE(FATAL_ERROR "find_bullet.cmake: unable to find bullet libraries") + request_bullet_search_directories() ELSE (NOT _BULLET_FOUND_LIB_DIR) - SET(BULLET_LIBRARY_DIRS ${_BULLET_FOUND_LIB_DIR} CACHE STRING "The bullet library directory") - message("-- found matching version") + SET(BULLET_LIBRARY_DIRS ${_BULLET_FOUND_LIB_DIR} CACHE STRING "The bullet library directory") ENDIF (NOT _BULLET_FOUND_LIB_DIR) FOREACH(_LIB_DIR ${_BULLET_FOUND_LIB_DIR}) @@ -99,5 +135,14 @@ IF ( BULLET_INCLUDE_DIRS ENDIF( BULLET_INCLUDE_DIRS AND NOT BULLET_LIBRARIES) - +############################################################################## +# verify +############################################################################## +IF ( NOT BULLET_INCLUDE_DIRS OR NOT BULLET_LIBRARY_DIRS ) + request_bullet_search_directories() +ELSE ( NOT BULLET_INCLUDE_DIRS OR NOT BULLET_LIBRARY_DIRS ) + UNSET(BULLET_INCLUDE_SEARCH_DIR CACHE) + UNSET(BULLET_LIBRARY_SEARCH_DIR CACHE) + MESSAGE(STATUS "-- found matching bullet version") +ENDIF ( NOT BULLET_INCLUDE_DIRS OR NOT BULLET_LIBRARY_DIRS ) diff --git a/cmake/modules/find_compiler.cmake b/cmake/modules/find_compiler.cmake index d8d0e3c65..e6ebced9b 100644 --- a/cmake/modules/find_compiler.cmake +++ b/cmake/modules/find_compiler.cmake @@ -1,26 +1,26 @@ if (WIN32) if (MSVC71) - set (GUA_COMPILER_SUFFIX "vc71") + set (_COMPILER_SUFFIX "vc71") endif(MSVC71) if (MSVC80) - set (GUA_COMPILER_SUFFIX "vc80") + set (_COMPILER_SUFFIX "vc80") endif(MSVC80) if (MSVC90) - set (GUA_COMPILER_SUFFIX "vc90") + set (_COMPILER_SUFFIX "vc90") endif(MSVC90) if (MSVC10) - set (GUA_COMPILER_SUFFIX "vc100") + set (_COMPILER_SUFFIX "vc100") endif(MSVC10) if (MSVC11) - set (GUA_COMPILER_SUFFIX "vc110") + set (_COMPILER_SUFFIX "vc110") endif(MSVC11) if (MSVC12) - set (GUA_COMPILER_SUFFIX "vc120") + set (_COMPILER_SUFFIX "vc120") endif(MSVC12) endif (WIN32) @@ -43,14 +43,13 @@ if (UNIX) message(${_COMPILER_VERSION}) string(REGEX REPLACE ".* ([0-9])\\.([0-9])\\.[0-9].*" "\\1\\2" _COMPILER_VERSION ${_COMPILER_VERSION}) message(${_COMPILER_VERSION}) - set (GUA_COMPILER_SUFFIX "gcc${_COMPILER_VERSION}") - #set (GUA_COMPILER_SUFFIX "") - message(${GUA_COMPILER_SUFFIX}) + set (_COMPILER_SUFFIX "gcc${_COMPILER_VERSION}") + message(${COMPILER_SUFFIX}) endif (CMAKE_COMPILER_IS_GNUCXX) endif(UNIX) -if (GUA_COMPILER_SUFFIX STREQUAL "") - message(FATAL_ERROR "schism_compiler.cmake: unable to identify supported compiler") -else (GUA_COMPILER_SUFFIX STREQUAL "") - set(GUA_COMPILER_SUFFIX ${GUA_COMPILER_SUFFIX} CACHE STRING "The boost style compiler suffix") -endif (GUA_COMPILER_SUFFIX STREQUAL "") +if (_COMPILER_SUFFIX STREQUAL "") + message(FATAL_ERROR "find_compiler.cmake: unable to identify supported compiler") +else (_COMPILER_SUFFIX STREQUAL "") + set(COMPILER_SUFFIX ${_COMPILER_SUFFIX} CACHE STRING "The boost style compiler suffix") +endif (_COMPILER_SUFFIX STREQUAL "") diff --git a/cmake/modules/find_cuda.cmake b/cmake/modules/find_cuda.cmake index fc7b8fa0d..c232d98c6 100644 --- a/cmake/modules/find_cuda.cmake +++ b/cmake/modules/find_cuda.cmake @@ -1,173 +1,126 @@ -#option(GUA_CUDA_BUILD_DEBUG "Build debug" OFF) -option(GUA_CUDA_BUILD_VERBOSE "Verbose Output" OFF) -option(GUA_CUDA_BUILD_COMPUTE20 "Enable Compute 2.0" ON) -option(GUA_CUDA_BUILD_COMPUTE30 "Enable Compute 3.0" OFF) -option(GUA_CUDA_BUILD_COMPUTE35 "Enable Compute 3.5" OFF) -option(GUA_CUDA_BUILD_USE_FAST_MATH "Use lower precision math calculations" ON) -option(GUA_CUDA_BUILD_KEEP_INTERMEDIATE_FILES "Keep the generated intermediate files" ON) - -IF (UNIX) +############################################################################## +# pre-defined search paths +############################################################################## +SET(CUDA_INCLUDE_SEARCH_DIRS + ${GLOBAL_EXT_DIR}/inc/cuda + /opt/cuda/current/cuda/include + /usr/include + /usr/local/include +) + +SET(CUDA_LIBRARY_SEARCH_DIRS + ${GLOBAL_EXT_DIR}/lib + /opt/cuda/current/cuda/lib + /usr/lib + /usr/local/lib +) + +############################################################################## +# feedback to provide user-defined paths to search for cuda +############################################################################## +MACRO (request_cuda_search_directories) + + IF ( NOT CUDA_INCLUDE_DIRS AND NOT CUDA_LIBRARY_DIRS ) + SET(CUDA_INCLUDE_SEARCH_DIR "Please provide cuda include path." CACHE PATH "path to cuda headers.") + SET(CUDA_LIBRARY_SEARCH_DIR "Please provide cuda library path." CACHE PATH "path to cuda libraries.") + MESSAGE(FATAL_ERROR "find_cuda.cmake: unable to find cuda.") + ENDIF ( NOT CUDA_INCLUDE_DIRS AND NOT CUDA_LIBRARY_DIRS ) + + IF ( NOT CUDA_INCLUDE_DIRS ) + SET(CUDA_INCLUDE_SEARCH_DIR "Please provide cuda include path." CACHE PATH "path to cuda headers.") + MESSAGE(FATAL_ERROR "find_cuda.cmake: unable to find cuda headers.") + ELSE ( NOT CUDA_INCLUDE_DIRS ) + UNSET(CUDA_INCLUDE_SEARCH_DIR CACHE) + ENDIF ( NOT CUDA_INCLUDE_DIRS ) + + IF ( NOT CUDA_LIBRARY_DIRS ) + SET(CUDA_LIBRARY_SEARCH_DIR "Please provide cuda library path." CACHE PATH "path to cuda libraries.") + MESSAGE(FATAL_ERROR "find_cuda.cmake: unable to find cuda libraries.") + ELSE ( NOT CUDA_LIBRARY_DIRS ) + UNSET(CUDA_LIBRARY_SEARCH_DIR CACHE) + ENDIF ( NOT CUDA_LIBRARY_DIRS ) + +ENDMACRO (request_cuda_search_directories) + +############################################################################## +# search +############################################################################## +message(STATUS "-- checking for CUDA") + +IF (NOT CUDA_INCLUDE_DIRS) + + SET(_CUDA_FOUND_INC_DIRS "") + + FOREACH(_SEARCH_DIR ${CUDA_INCLUDE_SEARCH_DIRS}) + FIND_PATH(_CUR_SEARCH + NAMES cuda.h + PATHS ${_SEARCH_DIR} + NO_DEFAULT_PATH) + IF (_CUR_SEARCH) + LIST(APPEND _CUDA_FOUND_INC_DIRS ${_CUR_SEARCH}) + ENDIF(_CUR_SEARCH) + SET(_CUR_SEARCH _CUR_SEARCH-NOTFOUND CACHE INTERNAL "internal use") + ENDFOREACH(_SEARCH_DIR ${CUDA_INCLUDE_SEARCH_DIRS}) + + IF (NOT _CUDA_FOUND_INC_DIRS) + request_cuda_search_directories() + ENDIF (NOT _CUDA_FOUND_INC_DIRS) - ELSEIF(WIN32) - SET(CUDA_LIBRARY_DIRS ${GLOBAL_EXT_DIR}/lib/) - SET(CUDA_LIBRARIES cuda.lib cudart.lib OpenGL32.lib OpenCL.lib ) - SET(CUDA_INCLUDE_DIRS ${GLOBAL_EXT_DIR}/inc/cuda/) + FOREACH(_INC_DIR ${_CUDA_FOUND_INC_DIRS}) + LIST(APPEND _CUDA_INCLUDE_DIRS ${_INC_DIR}) + ENDFOREACH(_INC_DIR ${_BOOST_FOUND_INC_DIRS}) + + IF (_CUDA_FOUND_INC_DIRS) + SET(CUDA_INCLUDE_DIRS ${_CUDA_INCLUDE_DIRS} CACHE PATH "path to cuda headers.") + ENDIF (_CUDA_FOUND_INC_DIRS) + +ENDIF(NOT CUDA_INCLUDE_DIRS) + +IF(UNIX) + LIST(APPEND _CUDA_LIB_FILENAMES "libcudart.so") +ELSEIF(WIN32) + LIST(APPEND _CUDA_LIB_FILENAMES "cudart.lib") + LIST(APPEND _CUDA_LIB_FILENAMES "cuda.lib") ENDIF(UNIX) -if (WIN32) - if (MSVC10) - set (GUA_CUDA_NVCC_OPTIONS --cl-version 2010 CACHE STRING "gua cuda internal" FORCE) - set(GUA_CUDA_SHARED_LIB_NAME "cudart64_50_32" CACHE STRING "gua cuda internal" FORCE) - endif(MSVC10) - - if (MSVC11) - set (GUA_CUDA_NVCC_OPTIONS --cl-version 2012 CACHE STRING "gua cuda internal" FORCE) - set(GUA_CUDA_SHARED_LIB_NAME "cudart64_55" CACHE STRING "gua cuda internal" FORCE) - endif(MSVC11) - - if (MSVC12) - set (GUA_CUDA_NVCC_OPTIONS --cl-version 2013 CACHE STRING "gua cuda internal" FORCE) - set(GUA_CUDA_SHARED_LIB_NAME "cudart64_55" CACHE STRING "gua cuda internal" FORCE) - endif(MSVC12) - - set(GUA_CUDA_NVCC_OPTIONS ${GUA_CUDA_NVCC_OPTIONS} --use-local-env --compile CACHE STRING "gua cuda internal" FORCE) - set(GUA_CUDA_NVCC_PATH ${GLOBAL_EXT_DIR}/bin/cuda/bin CACHE PATH "gua cuda internal") - if (${GUA_PLATFORM} MATCHES ${PLATFORM_WIN64}) - set(GUA_CUDA_NVCC_OPTIONS ${GUA_CUDA_NVCC_OPTIONS} --machine 64 -ccbin \"$(VCInstallDir)bin/x86_amd64/cl.exe\" CACHE STRING "gua cuda internal" FORCE) - else (${GUA_PLATFORM} MATCHES ${PLATFORM_WIN64}) - set(GUA_CUDA_NVCC_OPTIONS ${GUA_CUDA_NVCC_OPTIONS} --machine 32 -ccbin \"$(VCInstallDir)bin/x86_amd64/cl.exe\" CACHE STRING "gua cuda internal" FORCE) - endif (${GUA_PLATFORM} MATCHES ${PLATFORM_WIN64}) -elseif (UNIX) - set(GUA_CUDA_NVCC_PATH /opt/cuda/current/cuda/bin CACHE PATH "gua cuda internal") -endif (WIN32) - -set(GUA_CUDA_NVCC_COMMAND "${GUA_CUDA_NVCC_PATH}/nvcc" CACHE STRING "gua cuda internal") - -if (CMAKE_PATCH_VERSION GREATER "10") -# convert CXX compiler options into comma separated list -string(REPLACE " " "," GUA_CUDA_NVCC_CXX_FLAGS ${CMAKE_CXX_FLAGS}) -string(REPLACE " " "," GUA_CUDA_NVCC_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE}) -string(REPLACE " " "," GUA_CUDA_NVCC_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG}) - -string(REPLACE ",-std=c++0x" "," GUA_CUDA_NVCC_CXX_FLAGS ${GUA_CUDA_NVCC_CXX_FLAGS}) -string(REPLACE ",-std=c++0x" "," GUA_CUDA_NVCC_CXX_FLAGS_RELEASE ${GUA_CUDA_NVCC_CXX_FLAGS_RELEASE}) -string(REPLACE ",-std=c++0x" "," GUA_CUDA_NVCC_CXX_FLAGS_DEBUG ${GUA_CUDA_NVCC_CXX_FLAGS_DEBUG}) -endif (CMAKE_PATCH_VERSION GREATER "10") - -set(GUA_CUDA_NVCC_DEFINITIONS "") -set(GUA_CUDA_NVCC_INCLUDE_DIRECTORIES "") - -#if (GUA_CUDA_BUILD_DEBUG) -# set(GUA_CUDA_NVCC_OPTIONS ${GUA_CUDA_NVCC_OPTIONS} -Xcompiler ${GUA_CUDA_NVCC_CXX_FLAGS_DEBUG}${GUA_CUDA_NVCC_CXX_FLAGS} CACHE STRING "gua cuda internal" FORCE) -#else (GUA_CUDA_BUILD_DEBUG) -# set(GUA_CUDA_NVCC_OPTIONS ${GUA_CUDA_NVCC_OPTIONS} --optimize 3 -Xcompiler ${GUA_CUDA_NVCC_CXX_FLAGS_RELEASE}${GUA_CUDA_NVCC_CXX_FLAGS} CACHE STRING "gua cuda internal" FORCE) -#endif (GUA_CUDA_BUILD_DEBUG) - -set(GUA_CUDA_NVCC_OPTIONS ${GUA_CUDA_NVCC_OPTIONS} -Xcompiler CACHE STRING "gua cuda internal" FORCE) -set(GUA_CUDA_NVCC_OPTIONS ${GUA_CUDA_NVCC_OPTIONS} $<$:${GUA_CUDA_NVCC_CXX_FLAGS_DEBUG}${GUA_CUDA_NVCC_CXX_FLAGS}> CACHE STRING "gua cuda internal" FORCE) -set(GUA_CUDA_NVCC_OPTIONS ${GUA_CUDA_NVCC_OPTIONS} $<$:${GUA_CUDA_NVCC_CXX_FLAGS_RELEASE}${GUA_CUDA_NVCC_CXX_FLAGS}> CACHE STRING "gua cuda internal" FORCE) -set(GUA_CUDA_NVCC_OPTIONS ${GUA_CUDA_NVCC_OPTIONS} $<$:--optimize> $<$:3> CACHE STRING "gua cuda internal" FORCE) - -if (GUA_CUDA_BUILD_VERBOSE) - set(GUA_CUDA_NVCC_OPTIONS ${GUA_CUDA_NVCC_OPTIONS} --ptxas-options=-v CACHE STRING "gua cuda internal" FORCE) -endif (GUA_CUDA_BUILD_VERBOSE) - -if (GUA_CUDA_BUILD_COMPUTE20) - set(GUA_CUDA_NVCC_OPTIONS ${GUA_CUDA_NVCC_OPTIONS} -gencode arch=\"compute_20\",code=\"sm_20\" CACHE STRING "gua cuda internal" FORCE) -endif (GUA_CUDA_BUILD_COMPUTE20) - -if (GUA_CUDA_BUILD_COMPUTE30) - set(GUA_CUDA_NVCC_OPTIONS ${GUA_CUDA_NVCC_OPTIONS} -gencode arch=\"compute_30\",code=\"sm_30\" CACHE STRING "gua cuda internal" FORCE) -endif (GUA_CUDA_BUILD_COMPUTE30) - -if (GUA_CUDA_BUILD_COMPUTE35) - set(GUA_CUDA_NVCC_OPTIONS ${GUA_CUDA_NVCC_OPTIONS} -gencode arch=\"compute_35\",code=\"sm_35\" CACHE STRING "gua cuda internal" FORCE) -endif (GUA_CUDA_BUILD_COMPUTE35) - -if (GUA_CUDA_BUILD_KEEP_INTERMEDIATE_FILES) - if (WIN32) - set(GUA_CUDA_NVCC_OPTIONS "${GUA_CUDA_NVCC_OPTIONS} --keep-dir \"x64\\Release\"" CACHE STRING "gua cuda internal" FORCE) - endif (WIN32) -endif(GUA_CUDA_BUILD_KEEP_INTERMEDIATE_FILES) - -if (GUA_CUDA_BUILD_USE_FAST_MATH) - set(GUA_CUDA_NVCC_OPTIONS ${GUA_CUDA_NVCC_OPTIONS} --use_fast_math --fmad=true CACHE STRING "gua cuda internal" FORCE) -endif(GUA_CUDA_BUILD_USE_FAST_MATH) - -# add include directories to pass to the nvcc command -set(GUA_CUDA_NVCC_INCLUDE_DIRECTORIES "") -macro(scm_cuda_project_include_directories) - list(APPEND GUA_CUDA_NVCC_INCLUDE_DIRECTORIES ${ARGN}) -endmacro(scm_cuda_project_include_directories) - -# add definitions to pass to the nvcc command. -set(GUA_CUDA_NVCC_DEFINITIONS "") -macro(scm_cuda_definitions) - list(APPEND GUA_CUDA_NVCC_DEFINITIONS ${ARGN}) -endmacro(scm_cuda_definitions) - -# add the nvcc command to all .cu files -macro(scm_cuda_add_nvcc_prepass GUA_CUDA_OUTPUT_FILES) - set(input_file_list ${ARGV}) - set(output_file_list ) - - list(REMOVE_AT input_file_list 0) - - set(inc_dir_list_name "${PROJECT_NAME}_INCLUDE_DIRS") - set(GUA_CUDA_NVCC_INCLUDE_DIRECTORIES ${${inc_dir_list_name}} ${GUA_CUDA_NVCC_INCLUDE_DIRECTORIES}) - - foreach(cuda_idir ${GUA_CUDA_NVCC_INCLUDE_DIRECTORIES}) - set(GUA_CUDA_NVCC_INC_DIR_STRING ${GUA_CUDA_NVCC_INC_DIR_STRING} -I${cuda_idir}) - endforeach(cuda_idir ${GUA_CUDA_NVCC_INCLUDE_DIRECTORIES}) - - foreach(cuda_def ${GUA_CUDA_NVCC_DEFINITIONS}) - set(GUA_CUDA_NVCC_DEF_STRING ${GUA_CUDA_NVCC_DEF_STRING} -I${cuda_def}) - endforeach(cuda_def ${GUA_CUDA_NVCC_DEFINITIONS}) - - # remove the leading output variable - #LIST(REMOVE_AT input_file_list 0) - - foreach(input_file ${input_file_list}) - get_filename_component(input_file ${input_file} ABSOLUTE) - get_filename_component(input_file_name ${input_file} NAME) - get_filename_component(input_file_name_we ${input_file} NAME_WE) - get_filename_component(input_file_ext ${input_file} EXT) - get_filename_component(input_file_path ${input_file} PATH) - - if (input_file_ext STREQUAL ".cu") - - if (WIN32) - # CMake generates a directory where the intermediate files are stored - file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/${PROJECT_NAME}.dir/$(ConfigurationName)") - set(CUDA_NVCC_OUTPUT_FILE ${PROJECT_BINARY_DIR}/${PROJECT_NAME}.dir/$(ConfigurationName)/${input_file_name_we}.obj) - set(CUDA_NVCC_OUTPUT_CUBIN_FILE ${PROJECT_BINARY_DIR}/${PROJECT_NAME}.dir/$(ConfigurationName)/${input_file_name_we}.cubin) - endif (WIN32) - if (UNIX) - # CMake generates a directory where the intermediate files are stored - file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/CMakeFiles/${PROJECT_NAME}.dir${input_file_path}") - set(CUDA_NVCC_OUTPUT_FILE "${PROJECT_BINARY_DIR}/CMakeFiles/${PROJECT_NAME}.dir${input_file_path}/${input_file_name_we}.o") - set(CUDA_NVCC_OUTPUT_CUBIN_FILE "${PROJECT_BINARY_DIR}/CMakeFiles/${PROJECT_NAME}.dir${input_file_path}/${input_file_name_we}.cubin") - endif (UNIX) - - set(output_file_list ${output_file_list} ${CUDA_NVCC_OUTPUT_FILE}) - - # add the actual nvcc command to generate the .obj/.o file - add_custom_command(OUTPUT ${CUDA_NVCC_OUTPUT_FILE} - COMMAND ${GUA_CUDA_NVCC_COMMAND} - ARGS ${GUA_CUDA_NVCC_OPTIONS} - ${GUA_CUDA_NVCC_INC_DIR_STRING} - ${GUA_CUDA_NVCC_DEF_STRING} - -o \"${CUDA_NVCC_OUTPUT_FILE}\" - \"${input_file}\" - MAIN_DEPENDENCY ${input_file} - DEPENDS ${input_file} - COMMENT "NVCC compiling (${input_file_name}):") - - endif (input_file_ext STREQUAL ".cu") - endforeach(input_file) - - set_source_files_properties(${output_file_list} PROPERTIES GENERATED TRUE) - set(${GUA_CUDA_OUTPUT_FILES} ${output_file_list}) - -endmacro(scm_cuda_add_nvcc_prepass) - +IF ( CUDA_INCLUDE_DIRS AND NOT CUDA_LIBRARIES ) + + SET(_CUDA_FOUND_LIB_DIR "") + SET(_CUDA_POSTFIX "") + LIST(GET _CUDA_LIB_FILENAMES 0 _CUDA_RUNTIME_LIBRARY) + + FOREACH(_SEARCH_DIR ${CUDA_LIBRARY_SEARCH_DIRS}) + FIND_PATH(_CUR_SEARCH + NAMES ${_CUDA_RUNTIME_LIBRARY} + PATHS ${_SEARCH_DIR} + PATH_SUFFIXES debug release + NO_DEFAULT_PATH) + IF (_CUR_SEARCH) + LIST(APPEND _CUDA_FOUND_LIB_DIR ${_SEARCH_DIR}) + ENDIF(_CUR_SEARCH) + SET(_CUR_SEARCH _CUR_SEARCH-NOTFOUND CACHE INTERNAL "internal use") + ENDFOREACH(_SEARCH_DIR ${CUDA_LIBRARY_SEARCH_DIRS}) + + IF (NOT _CUDA_FOUND_LIB_DIR) + request_cuda_search_directories() + ELSE (NOT _CUDA_FOUND_LIB_DIR) + SET(CUDA_LIBRARY_DIRS ${_CUDA_FOUND_LIB_DIR} CACHE STRING "The cuda library directory.") + message(STATUS "-- found matching version") + ENDIF (NOT _CUDA_FOUND_LIB_DIR) + + IF (_CUDA_FOUND_LIB_DIR) + SET(CUDA_LIBRARIES ${_CUDA_LIB_FILENAMES} CACHE STRING "The cuda libraries.") + ENDIF (_CUDA_FOUND_LIB_DIR) + +ENDIF( CUDA_INCLUDE_DIRS AND NOT CUDA_LIBRARIES ) + +############################################################################## +# verify +############################################################################## +IF ( NOT CUDA_INCLUDE_DIRS OR NOT CUDA_LIBRARY_DIRS ) + request_cuda_search_directories() +ELSE ( NOT CUDA_INCLUDE_DIRS OR NOT CUDA_LIBRARY_DIRS ) + UNSET(CUDA_INCLUDE_SEARCH_DIR CACHE) + UNSET(CUDA_LIBRARY_SEARCH_DIR CACHE) + MESSAGE(STATUS "-- found matching cuda version") +ENDIF ( NOT CUDA_INCLUDE_DIRS OR NOT CUDA_LIBRARY_DIRS ) \ No newline at end of file diff --git a/cmake/modules/find_ev.cmake b/cmake/modules/find_ev.cmake deleted file mode 100644 index efb48b56e..000000000 --- a/cmake/modules/find_ev.cmake +++ /dev/null @@ -1,68 +0,0 @@ -SET(EV_INCLUDE_SEARCH_DIRS - /usr/include -) - -SET(EV_LIBRARY_SEARCH_DIRS - /usr/lib -) - -message("-- checking for ev") - -IF (NOT EV_INCLUDE_DIRS) - - SET(_EV_FOUND_INC_DIRS "") - - FOREACH(_SEARCH_DIR ${EV_INCLUDE_SEARCH_DIRS}) - FIND_PATH(_CUR_SEARCH - NAMES ev.h - PATHS ${_SEARCH_DIR} - NO_DEFAULT_PATH) - IF (_CUR_SEARCH) - LIST(APPEND _EV_FOUND_INC_DIRS ${_CUR_SEARCH}) - ENDIF(_CUR_SEARCH) - SET(_CUR_SEARCH _CUR_SEARCH-NOTFOUND CACHE INTERNAL "internal use") - ENDFOREACH(_SEARCH_DIR ${EV_INCLUDE_SEARCH_DIRS}) - - IF (NOT _EV_FOUND_INC_DIRS) - MESSAGE(FATAL_ERROR "find_ev.cmake: unable to find ev headers") - ENDIF (NOT _EV_FOUND_INC_DIRS) - - FOREACH(_INC_DIR ${_EV_FOUND_INC_DIRS}) - LIST(APPEND EV_INCLUDE_DIRS ${_INC_DIR}) - ENDFOREACH(_INC_DIR ${_BOOST_FOUND_INC_DIRS}) - -ENDIF(NOT EV_INCLUDE_DIRS) - -IF ( EV_INCLUDE_DIRS - AND NOT EV_LIBRARIES) - - SET(_EV_FOUND_LIB_DIR "") - SET(_EV_POSTFIX "") - - FOREACH(_SEARCH_DIR ${EV_LIBRARY_SEARCH_DIRS}) - FIND_PATH(_CUR_SEARCH - NAMES libev.so - PATHS ${_SEARCH_DIR} - NO_DEFAULT_PATH) - IF (_CUR_SEARCH) - LIST(APPEND _EV_FOUND_LIB_DIR ${_SEARCH_DIR}) - ENDIF(_CUR_SEARCH) - SET(_CUR_SEARCH _CUR_SEARCH-NOTFOUND CACHE INTERNAL "internal use") - ENDFOREACH(_SEARCH_DIR ${EV_LIBRARY_SEARCH_DIRS}) - - IF (NOT _EV_FOUND_LIB_DIR) - MESSAGE(FATAL_ERROR "find_ev.cmake: unable to find ev library") - ELSE (NOT _EV_FOUND_LIB_DIR) - message("-- found matching version") - ENDIF (NOT _EV_FOUND_LIB_DIR) - - FOREACH(_LIB_DIR ${_EV_FOUND_LIB_DIR}) - LIST(APPEND EV_LIBRARIES ${_LIB_DIR}/libev.so) - ENDFOREACH(_LIB_DIR ${_EV_FOUND_INC_DIRS}) - - -ENDIF( EV_INCLUDE_DIRS - AND NOT EV_LIBRARIES) - - - diff --git a/cmake/modules/find_json.cmake b/cmake/modules/find_json.cmake index 3cc55cc3f..a5cf1d015 100644 --- a/cmake/modules/find_json.cmake +++ b/cmake/modules/find_json.cmake @@ -1,14 +1,49 @@ +############################################################################## +# pre-defined search paths +############################################################################## SET(JSON_INCLUDE_SEARCH_DIRS ${GLOBAL_EXT_DIR}/inc/json - /opt/json/current + ${GUACAMOLE_EXT_DIR}/inc/json + /usr/include ) SET(JSON_LIBRARY_SEARCH_DIRS ${GLOBAL_EXT_DIR}/lib - /opt/json/current/json-build + ${GUACAMOLE_EXT_DIR}/lib + /usr/lib ) -message("-- checking for JSON") +############################################################################## +# feedback to provide user-defined paths to search for json +############################################################################## +MACRO (request_json_search_directories) + + IF ( NOT JSON_INCLUDE_DIRS AND NOT JSON_LIBRARY_DIRS ) + SET(JSON_INCLUDE_SEARCH_DIR "Please provide json include path." CACHE PATH "path to json headers.") + SET(JSON_LIBRARY_SEARCH_DIR "Please provide json library path." CACHE PATH "path to json libraries.") + MESSAGE(FATAL_ERROR "find_json.cmake: unable to find json.") + ENDIF ( NOT JSON_INCLUDE_DIRS AND NOT JSON_LIBRARY_DIRS ) + + IF ( NOT JSON_INCLUDE_DIRS ) + SET(JSON_INCLUDE_SEARCH_DIR "Please provide json include path." CACHE PATH "path to json headers.") + MESSAGE(FATAL_ERROR "find_json.cmake: unable to find json headers.") + ELSE ( NOT JSON_INCLUDE_DIRS ) + UNSET(JSON_INCLUDE_SEARCH_DIR CACHE) + ENDIF ( NOT JSON_INCLUDE_DIRS ) + + IF ( NOT JSON_LIBRARY_DIRS ) + SET(JSON_LIBRARY_SEARCH_DIR "Please provide json library path." CACHE PATH "path to json libraries.") + MESSAGE(FATAL_ERROR "find_json.cmake: unable to find json libraries.") + ELSE ( NOT JSON_LIBRARY_DIRS ) + UNSET(JSON_LIBRARY_SEARCH_DIR CACHE) + ENDIF ( NOT JSON_LIBRARY_DIRS ) + +ENDMACRO (request_json_search_directories) + +############################################################################## +# search +############################################################################## +message(STATUS "-- checking for JSON") IF (NOT JSON_INCLUDE_DIRS) @@ -26,13 +61,17 @@ IF (NOT JSON_INCLUDE_DIRS) ENDFOREACH(_SEARCH_DIR ${JSON_INCLUDE_SEARCH_DIRS}) IF (NOT _JSON_FOUND_INC_DIRS) - MESSAGE(FATAL_ERROR "find_json.cmake: unable to find json headers") + request_json_search_directories() ENDIF (NOT _JSON_FOUND_INC_DIRS) - FOREACH(_INC_DIR ${_JSON_FOUND_INC_DIRS}) - LIST(APPEND JSON_INCLUDE_DIRS ${_INC_DIR}) + FOREACH(_INC_DIR ${_JSON_FOUND_INC_DIRS}) + LIST(APPEND _JSON_INCLUDE_DIRS ${_INC_DIR}) ENDFOREACH(_INC_DIR ${_BOOST_FOUND_INC_DIRS}) + IF (_JSON_FOUND_INC_DIRS) + SET(JSON_INCLUDE_DIRS ${_JSON_INCLUDE_DIRS} CACHE PATH "path to json headers.") + ENDIF (_JSON_FOUND_INC_DIRS) + ENDIF(NOT JSON_INCLUDE_DIRS) IF(UNIX) @@ -41,8 +80,7 @@ ELSEIF(WIN32) SET(JSON_LIB_FILENAME "json.lib") ENDIF(UNIX) -IF ( JSON_INCLUDE_DIRS - AND NOT JSON_LIBRARIES) +IF ( JSON_INCLUDE_DIRS AND NOT JSON_LIBRARIES ) SET(_JSON_FOUND_LIB_DIR "") SET(_JSON_POSTFIX "") @@ -60,19 +98,28 @@ IF ( JSON_INCLUDE_DIRS ENDFOREACH(_SEARCH_DIR ${JSON_LIBRARY_SEARCH_DIRS}) IF (NOT _JSON_FOUND_LIB_DIR) - MESSAGE(FATAL_ERROR "find_json.cmake: unable to find json libraries") + request_json_search_directories() ELSE (NOT _JSON_FOUND_LIB_DIR) - SET(JSON_LIBRARY_DIRS ${_JSON_FOUND_LIB_DIR} CACHE STRING "The json library directory") - message("-- found matching version") + SET(JSON_LIBRARY_DIRS ${_JSON_FOUND_LIB_DIR} CACHE STRING "The json library directory.") + message(STATUS "-- found matching version") ENDIF (NOT _JSON_FOUND_LIB_DIR) - FOREACH(_LIB_DIR ${_JSON_FOUND_LIB_DIR}) - LIST(APPEND JSON_LIBRARIES ${JSON_LIB_FILENAME}) - ENDFOREACH(_LIB_DIR ${_JSON_FOUND_INC_DIRS}) + IF (_JSON_FOUND_LIB_DIR) + SET(JSON_LIBRARIES ${JSON_LIB_FILENAME} CACHE STRING "The json library.") + ENDIF (_JSON_FOUND_LIB_DIR) +ENDIF( JSON_INCLUDE_DIRS AND NOT JSON_LIBRARIES ) -ENDIF( JSON_INCLUDE_DIRS - AND NOT JSON_LIBRARIES) +############################################################################## +# verify +############################################################################## +IF ( NOT JSON_INCLUDE_DIRS OR NOT JSON_LIBRARY_DIRS ) + request_json_search_directories() +ELSE ( NOT JSON_INCLUDE_DIRS OR NOT JSON_LIBRARY_DIRS ) + UNSET(JSON_INCLUDE_SEARCH_DIR CACHE) + UNSET(JSON_LIBRARY_SEARCH_DIR CACHE) + MESSAGE(STATUS "-- found matching json version") +ENDIF ( NOT JSON_INCLUDE_DIRS OR NOT JSON_LIBRARY_DIRS ) diff --git a/cmake/modules/find_ovr.cmake b/cmake/modules/find_ovr.cmake new file mode 100644 index 000000000..a45d4b667 --- /dev/null +++ b/cmake/modules/find_ovr.cmake @@ -0,0 +1,121 @@ +############################################################################## +# search paths +############################################################################## +SET(OVR_INCLUDE_SEARCH_DIRS + ${GLOBAL_EXT_DIR}/inc/OculusSDK + ${OVR_INCLUDE_DIRS} + ${OVR_INCLUDE_SEARCH_DIR} + "/opt/OculusSDK/LibOVR/Include" +) + +SET(OVR_LIBRARY_SEARCH_DIRS + ${GLOBAL_EXT_DIR}/lib + ${OVR_LIBRARY_DIRS} + ${OVR_LIBRARY_SEARCH_DIR} + "/opt/OculusSDK/LibOVR/Lib" +) + +############################################################################## +# feedback to provide user-defined paths to search for python +############################################################################## +MACRO (request_ovr_search_directories) + + IF ( NOT OVR_INCLUDE_DIRS AND NOT OVR_LIBRARY_DIRS ) + SET(OVR_INCLUDE_SEARCH_DIR "Please provide Oculus SDK include path." CACHE PATH "path to Oculus SDK headers.") + SET(OVR_LIBRARY_SEARCH_DIR "Please provide Oculus SDK library path." CACHE PATH "path to Oculus SDK libraries.") + MESSAGE(FATAL_ERROR "find_ovr.cmake: unable to find Oculus SDK.") + ENDIF ( NOT OVR_INCLUDE_DIRS AND NOT OVR_LIBRARY_DIRS ) + + IF ( NOT OVR_INCLUDE_DIRS ) + SET(OVR_INCLUDE_SEARCH_DIR "Please provide Oculus SDK include path." CACHE PATH "path to Oculus SDK headers.") + MESSAGE(FATAL_ERROR "find_ovr.cmake: unable to find Oculus SDK headers.") + ELSE ( NOT OVR_INCLUDE_DIRS ) + UNSET(OVR_INCLUDE_SEARCH_DIR CACHE) + ENDIF ( NOT OVR_INCLUDE_DIRS ) + + IF ( NOT OVR_LIBRARY_DIRS ) + SET(OVR_LIBRARY_SEARCH_DIR "Please provide Oculus SDK library path." CACHE PATH "path to Oculus SDK libraries.") + MESSAGE(FATAL_ERROR "find_ovr.cmake: unable to find Oculus SDK libraries.") + ELSE ( NOT OVR_LIBRARY_DIRS ) + UNSET(OVR_LIBRARY_SEARCH_DIR CACHE) + ENDIF ( NOT OVR_LIBRARY_DIRS ) + +ENDMACRO (request_ovr_search_directories) + +############################################################################## +# search +############################################################################## +message(STATUS "-- checking for OVR") + +IF (NOT OVR_INCLUDE_DIRS) + + SET(_OVR_FOUND_INC_DIRS "") + FOREACH(_SEARCH_DIR ${OVR_INCLUDE_SEARCH_DIRS}) + FIND_PATH(_CUR_SEARCH + NAMES OVR.h + PATHS ${_SEARCH_DIR} + NO_DEFAULT_PATH) + IF (_CUR_SEARCH) + LIST(APPEND _OVR_FOUND_INC_DIRS ${_CUR_SEARCH}) + ENDIF(_CUR_SEARCH) + SET(_CUR_SEARCH _CUR_SEARCH-NOTFOUND CACHE INTERNAL "internal use") + ENDFOREACH(_SEARCH_DIR ${OVR_INCLUDE_SEARCH_DIRS}) + + IF (NOT _OVR_FOUND_INC_DIRS) + request_ovr_search_directories() + ENDIF (NOT _OVR_FOUND_INC_DIRS) + + FOREACH(_INC_DIR ${_OVR_FOUND_INC_DIRS}) + SET(OVR_INCLUDE_DIRS ${OVR_INCLUDE_DIRS} ${_INC_DIR} CACHE PATH "Oculus SDK include directory.") + ENDFOREACH(_INC_DIR ${_OVR_FOUND_INC_DIRS}) + +ENDIF (NOT OVR_INCLUDE_DIRS) + +IF(UNIX) + SET(OVR_LIB_FILENAME "libovr.a") +ELSEIF(WIN32) + SET(OVR_LIB_FILENAME "libovr64.lib") +ENDIF(UNIX) + +IF ( NOT OVR_LIBRARY_DIRS ) + + SET(_OVR_FOUND_LIB_DIR "") + SET(_OVR_POSTFIX "") + + FOREACH(_SEARCH_DIR ${OVR_LIBRARY_SEARCH_DIRS}) + FIND_PATH(_CUR_SEARCH + NAMES ${OVR_LIB_FILENAME} + PATHS ${_SEARCH_DIR} + NO_DEFAULT_PATH) + IF (_CUR_SEARCH) + LIST(APPEND _OVR_FOUND_LIB_DIR ${_SEARCH_DIR}) + ENDIF(_CUR_SEARCH) + SET(_CUR_SEARCH _CUR_SEARCH-NOTFOUND CACHE INTERNAL "internal use") + ENDFOREACH(_SEARCH_DIR ${OVR_LIBRARY_SEARCH_DIRS}) + + IF (NOT _OVR_FOUND_LIB_DIR) + request_ovr_search_directories() + ELSE (NOT _OVR_FOUND_LIB_DIR) + SET(OVR_LIBRARY_DIRS ${_OVR_FOUND_LIB_DIR} CACHE PATH "The Oculus SDK library directory") + ENDIF (NOT _OVR_FOUND_LIB_DIR) + + FOREACH(_LIB_DIR ${_OVR_FOUND_LIB_DIR}) + LIST(APPEND _OVR_LIBRARIES ${OVR_LIB_FILENAME}) + ENDFOREACH(_LIB_DIR ${_OVR_FOUND_INC_DIRS}) + + IF (_OVR_FOUND_LIB_DIR) + SET(OVR_LIBRARIES ${_OVR_LIBRARIES} CACHE FILEPATH "The Oculus SDK library filename.") + ENDIF (_OVR_FOUND_LIB_DIR) + +ENDIF ( NOT OVR_LIBRARY_DIRS ) + +############################################################################## +# verify +############################################################################## +IF ( NOT OVR_INCLUDE_DIRS OR NOT OVR_LIBRARY_DIRS ) + request_ovr_search_directories() +ELSE ( NOT OVR_INCLUDE_DIRS OR NOT OVR_LIBRARY_DIRS ) + UNSET(OVR_INCLUDE_SEARCH_DIR CACHE) + UNSET(OVR_LIBRARY_SEARCH_DIR CACHE) + MESSAGE(STATUS "-- found matching Oculus SDK version") +ENDIF ( NOT OVR_INCLUDE_DIRS OR NOT OVR_LIBRARY_DIRS ) diff --git a/cmake/modules/find_schism.cmake b/cmake/modules/find_schism.cmake index 0d5f8e58e..776382577 100644 --- a/cmake/modules/find_schism.cmake +++ b/cmake/modules/find_schism.cmake @@ -1,22 +1,52 @@ +############################################################################## +# search paths +############################################################################## SET(SCHISM_INCLUDE_SEARCH_DIRS - ${GLOBAL_EXT_DIR}/inc/schism - #/opt/schism/schism_debug - /opt/schism/current - #/opt/schism/schism_debug + ${GLOBAL_EXT_DIR}/inc/schism + ${SCHISM_INCLUDE_SEARCH_DIR} + /opt/schism/current ) SET(SCHISM_LIBRARY_SEARCH_DIRS - ${GLOBAL_EXT_DIR}/lib - #/opt/schism/schism_debug/lib/linux_x86 - /opt/schism/current/lib/linux_x86 - #/opt/schism/schism_debug/lib/linux_x86 + ${GLOBAL_EXT_DIR}/lib + ${SCHISM_LIBRARY_SEARCH_DIR} + ../ + /opt/schism/current/lib/linux_x86 ) -message("-- checking for schism") +############################################################################## +# feedback to provide user-defined paths to search for schism +############################################################################## +MACRO (request_schism_search_directories) + + IF ( NOT SCHISM_INCLUDE_DIRS AND NOT SCHISM_LIBRARY_DIRS ) + SET(SCHISM_INCLUDE_SEARCH_DIR "Please provide schism include path." CACHE PATH "path to schism headers.") + SET(SCHISM_LIBRARY_SEARCH_DIR "Please provide schism library path." CACHE PATH "path to schism libraries.") + MESSAGE(FATAL_ERROR "find_schism.cmake: unable to find schism.") + ENDIF ( NOT SCHISM_INCLUDE_DIRS AND NOT SCHISM_LIBRARY_DIRS ) + + IF ( NOT SCHISM_INCLUDE_DIRS ) + SET(SCHISM_INCLUDE_SEARCH_DIR "Please provide schism include path." CACHE PATH "path to schism headers.") + MESSAGE(FATAL_ERROR "find_schism.cmake: unable to find schism headers.") + ELSE ( NOT SCHISM_INCLUDE_DIRS ) + UNSET(SCHISM_INCLUDE_SEARCH_DIR CACHE) + ENDIF ( NOT SCHISM_INCLUDE_DIRS ) + + IF ( NOT SCHISM_LIBRARY_DIRS ) + SET(SCHISM_LIBRARY_SEARCH_DIR "Please provide schism library path." CACHE PATH "path to schism libraries.") + MESSAGE(FATAL_ERROR "find_schism.cmake: unable to find schism libraries.") + ELSE ( NOT SCHISM_LIBRARY_DIRS ) + UNSET(SCHISM_LIBRARY_SEARCH_DIR CACHE) + ENDIF ( NOT SCHISM_LIBRARY_DIRS ) -IF (NOT SCHISM_INCLUDE_DIRS) +ENDMACRO (request_schism_search_directories) - SET(_SCHISM_FOUND_INC_DIRS "") +############################################################################## +# check for schism +############################################################################## +message(STATUS "-- checking for schism") + +IF ( NOT SCHISM_INCLUDE_DIRS ) FOREACH(_SEARCH_DIR ${SCHISM_INCLUDE_SEARCH_DIRS}) FIND_PATH(_CUR_SEARCH @@ -30,67 +60,85 @@ IF (NOT SCHISM_INCLUDE_DIRS) ENDFOREACH(_SEARCH_DIR ${SCHISM_INCLUDE_SEARCH_DIRS}) IF (NOT _SCHISM_FOUND_INC_DIRS) - MESSAGE(FATAL_ERROR "find_schism.cmake: unable to find SCHISM headers") + request_schism_search_directories() ENDIF (NOT _SCHISM_FOUND_INC_DIRS) FOREACH(_INC_DIR ${_SCHISM_FOUND_INC_DIRS}) - LIST(APPEND SCHISM_INCLUDE_DIRS ${_INC_DIR}/scm_cl_core/src) - LIST(APPEND SCHISM_INCLUDE_DIRS ${_INC_DIR}/scm_core/src) - LIST(APPEND SCHISM_INCLUDE_DIRS ${_INC_DIR}/scm_gl_core/src) - LIST(APPEND SCHISM_INCLUDE_DIRS ${_INC_DIR}/scm_gl_util/src) - LIST(APPEND SCHISM_INCLUDE_DIRS ${_INC_DIR}/scm_input/src) + LIST(APPEND _SCHISM_INCLUDE_DIRS ${_INC_DIR}/scm_cl_core/src) + LIST(APPEND _SCHISM_INCLUDE_DIRS ${_INC_DIR}/scm_core/src) + LIST(APPEND _SCHISM_INCLUDE_DIRS ${_INC_DIR}/scm_gl_core/src) + LIST(APPEND _SCHISM_INCLUDE_DIRS ${_INC_DIR}/scm_gl_util/src) + LIST(APPEND _SCHISM_INCLUDE_DIRS ${_INC_DIR}/scm_input/src) ENDFOREACH(_INC_DIR ${_BOOST_FOUND_INC_DIRS}) -ENDIF(NOT SCHISM_INCLUDE_DIRS) + IF (_SCHISM_FOUND_INC_DIRS) + SET(SCHISM_INCLUDE_DIRS ${_SCHISM_INCLUDE_DIRS} CACHE PATH "path to schism headers.") + ENDIF (_SCHISM_FOUND_INC_DIRS) -IF ( SCHISM_INCLUDE_DIRS - AND NOT SCHISM_LIBRARIES) +ENDIF ( NOT SCHISM_INCLUDE_DIRS ) - SET(_SCHISM_FOUND_LIB_DIR "") - SET(_SCHISM_POSTFIX "") +IF ( SCHISM_INCLUDE_DIRS AND ( NOT SCHISM_LIBRARY_DIRS OR NOT SCHISM_LIBRARIES)) FOREACH(_SEARCH_DIR ${SCHISM_LIBRARY_SEARCH_DIRS}) - IF (UNIX) - FIND_PATH(_CUR_SEARCH - NAMES libscm_gl_core.so - PATHS ${_SEARCH_DIR} - NO_DEFAULT_PATH) - ELSEIF(WIN32) - FIND_PATH(_CUR_SEARCH - NAMES scm_gl_core.lib - PATHS ${_SEARCH_DIR} - PATH_SUFFIXES debug release - NO_DEFAULT_PATH) - ENDIF(UNIX) + + IF (UNIX) + FIND_PATH(_CUR_SEARCH + NAMES libscm_gl_core.so + PATHS ${_SEARCH_DIR} + NO_DEFAULT_PATH) + ELSEIF(WIN32) + FIND_PATH(_CUR_SEARCH + NAMES scm_gl_core.lib + PATHS ${_SEARCH_DIR} + PATH_SUFFIXES debug release + NO_DEFAULT_PATH) + ENDIF(UNIX) + IF (_CUR_SEARCH) LIST(APPEND _SCHISM_FOUND_LIB_DIR ${_SEARCH_DIR}) ENDIF(_CUR_SEARCH) + SET(_CUR_SEARCH _CUR_SEARCH-NOTFOUND CACHE INTERNAL "internal use") + ENDFOREACH(_SEARCH_DIR ${SCHISM_LIBRARY_SEARCH_DIRS}) IF (NOT _SCHISM_FOUND_LIB_DIR) - MESSAGE(FATAL_ERROR "find_schism.cmake: unable to find SCHISM library") + request_schism_search_directories() ELSE (NOT _SCHISM_FOUND_LIB_DIR) - SET(SCHISM_LIBRARY_DIRS ${_SCHISM_FOUND_LIB_DIR} CACHE STRING "The schism library directory") - message("-- found matching version") + SET(SCHISM_LIBRARY_DIRS ${_SCHISM_FOUND_LIB_DIR} CACHE STRING "The schism library directory.") ENDIF (NOT _SCHISM_FOUND_LIB_DIR) + SET(_SCHISM_LIBRARIES "") + FOREACH(_LIB_DIR ${_SCHISM_FOUND_LIB_DIR}) - IF (UNIX) - file(GLOB SCHISM_LIBRARIES ${_LIB_DIR}/*.so) - ELSEIF(WIN32) - file(GLOB _SCHISM_LIBRARY_ABSOLUTE_PATHS ${_LIB_DIR}/release/scm*.lib) - FOREACH (_SCHISM_LIB_PATH ${_SCHISM_LIBRARY_ABSOLUTE_PATHS}) - SET(_SCHISM_LIB_FILENAME, "") - GET_FILENAME_COMPONENT(_SCHISM_LIB_FILENAME ${_SCHISM_LIB_PATH} NAME) - LIST(APPEND SCHISM_LIBRARIES ${_SCHISM_LIB_FILENAME}) - ENDFOREACH(_SCHISM_LIB_PATH) - ENDIF(UNIX) + IF (UNIX) + file(GLOB SCHISM_LIBRARIES ${_LIB_DIR}/*.so) + ELSEIF(WIN32) + file(GLOB _SCHISM_LIBRARY_ABSOLUTE_PATHS ${_LIB_DIR}/release/scm*.lib) + FOREACH (_SCHISM_LIB_PATH ${_SCHISM_LIBRARY_ABSOLUTE_PATHS}) + SET(_SCHISM_LIB_FILENAME "") + GET_FILENAME_COMPONENT(_SCHISM_LIB_FILENAME ${_SCHISM_LIB_PATH} NAME) + LIST(APPEND _SCHISM_LIBRARIES ${_SCHISM_LIB_FILENAME}) + ENDFOREACH(_SCHISM_LIB_PATH) + ENDIF(UNIX) ENDFOREACH(_LIB_DIR ${_SCHISM_FOUND_INC_DIRS}) + + IF (_SCHISM_FOUND_LIB_DIR) + SET(SCHISM_LIBRARIES ${_SCHISM_LIBRARIES} CACHE STRING "schism libraries.") + ENDIF (_SCHISM_FOUND_LIB_DIR) +ENDIF ( SCHISM_INCLUDE_DIRS AND ( NOT SCHISM_LIBRARY_DIRS OR NOT SCHISM_LIBRARIES)) -ENDIF( SCHISM_INCLUDE_DIRS - AND NOT SCHISM_LIBRARIES) +############################################################################## +# verify +############################################################################## +IF ( NOT SCHISM_INCLUDE_DIRS OR NOT SCHISM_LIBRARY_DIRS ) + request_schism_search_directories() +ELSE ( NOT SCHISM_INCLUDE_DIRS OR NOT SCHISM_LIBRARY_DIRS ) + UNSET(SCHISM_INCLUDE_SEARCH_DIR CACHE) + UNSET(SCHISM_LIBRARY_SEARCH_DIR CACHE) + MESSAGE(STATUS "-- found matching schism version") +ENDIF ( NOT SCHISM_INCLUDE_DIRS OR NOT SCHISM_LIBRARY_DIRS ) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt new file mode 100644 index 000000000..e6d6872f7 --- /dev/null +++ b/plugins/CMakeLists.txt @@ -0,0 +1,14 @@ +GET_SUBDIRECTORIES(_LIST_OF_PLUGINS ${CMAKE_CURRENT_SOURCE_DIR}) + +FOREACH (_PLUGIN ${_LIST_OF_PLUGINS}) + SET(PLUGIN_${_PLUGIN} false CACHE BOOL "Build module ${_PLUGIN}") + MESSAGE(STATUS "Found plugin ${_PLUGIN}") +ENDFOREACH (_PLUGIN) + +FOREACH (_PLUGIN ${_LIST_OF_PLUGINS}) + IF (NOT PLUGIN_${_PLUGIN}) + SET(PLUGIN_${_PLUGIN} false CACHE BOOL "Build module ${_PLUGIN}") + ELSEIF (${PLUGIN_${_PLUGIN}}) + add_subdirectory(${_PLUGIN}) + ENDIF (NOT PLUGIN_${_PLUGIN}) +ENDFOREACH (_PLUGIN) diff --git a/plugins/guacamole-oculus/.gitignore b/plugins/guacamole-oculus/.gitignore new file mode 100644 index 000000000..e69affda6 --- /dev/null +++ b/plugins/guacamole-oculus/.gitignore @@ -0,0 +1,9 @@ +*.out +build +*.gv +*.o +*.so +guacamole_editor +tags +*.sublime-workspace +.nfs* diff --git a/plugins/guacamole-oculus/CMakeLists.txt b/plugins/guacamole-oculus/CMakeLists.txt new file mode 100644 index 000000000..bd42582d4 --- /dev/null +++ b/plugins/guacamole-oculus/CMakeLists.txt @@ -0,0 +1,40 @@ +# dependencies +include(find_ovr) + +# determine source and header files +file(GLOB_RECURSE GUACAMOLE_OCULUS_SRC RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + src/*.cpp + include/*.h + include/*.hpp +) + +SET(GUACAMOLE_OCULUS_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include) + +LINK_DIRECTORIES(${LIB_PATHS} ${OVR_LIBRARY_DIRS}) + +INCLUDE_DIRECTORIES( ${INCLUDE_PATHS} + ${GUACAMOLE_SOURCE_DIR} + ${OVR_INCLUDE_DIRS} + ${GUACAMOLE_OCULUS_SOURCE_DIR} +) + +ADD_LIBRARY( guacamole-oculus SHARED + ${GUACAMOLE_OCULUS_SRC} +) + +IF (MSVC) + set_target_properties(guacamole-oculus PROPERTIES COMPILE_FLAGS "-D GUA_OCULUS_LIBRARY") +ENDIF (MSVC) + +IF (UNIX) + LIST(APPEND LIBS udev Xinerama) +ELSEIF (MSVC) + LIST(APPEND LIBS winmm.lib guacamole ${OVR_LIBRARIES}) +ENDIF(UNIX) + + +TARGET_LINK_LIBRARIES( guacamole-oculus debug ${LIBS} optimized ${LIBS}) + +# compile examples + +ADD_SUBDIRECTORY(example-oculus) \ No newline at end of file diff --git a/plugins/guacamole-oculus/example-oculus/CMakeLists.txt b/plugins/guacamole-oculus/example-oculus/CMakeLists.txt new file mode 100644 index 000000000..62039d357 --- /dev/null +++ b/plugins/guacamole-oculus/example-oculus/CMakeLists.txt @@ -0,0 +1,36 @@ +# determine source and header files +file(GLOB_RECURSE EXAMPLE_SRC RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.c *.cpp *.cc) + +GET_FILENAME_COMPONENT(_EXE_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME) + +SET(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}) + +LIST(APPEND LIB_PATHS ${CMAKE_SOURCE_DIR}/lib) + +LINK_DIRECTORIES(${LIB_PATHS}) + +INCLUDE_DIRECTORIES( ${INCLUDE_PATHS} + ${GUACAMOLE_SOURCE_DIR} + ${OVR_INCLUDE_DIRS} + ${GUACAMOLE_OCULUS_SOURCE_DIR} +) + +ADD_EXECUTABLE( ${_EXE_NAME} + ${EXAMPLE_SRC} +) + +ADD_DEPENDENCIES(${_EXE_NAME} guacamole-oculus) + +TARGET_LINK_LIBRARIES( ${_EXE_NAME} debug ${LIBS} guacamole-oculus optimized ${LIBS} guacamole-oculus) + +# copy runtime libraries as a post-build process +IF (MSVC) + + COPY_RUNTIME_DEPENDENCIES ( ${_EXE_NAME} \"${GLOBAL_EXT_DIR}/bin/$(Configuration)/\" \"${EXECUTABLE_OUTPUT_PATH}/$(Configuration)/\") + COPY_RUNTIME_DEPENDENCIES ( ${_EXE_NAME} \"${LIBRARY_OUTPUT_PATH}/$(Configuration)/\" \"${EXECUTABLE_OUTPUT_PATH}/$(Configuration)/\") + + FOREACH (_LIB_DIR ${GUACAMOLE_LIBRARY_DIRS}) + COPY_RUNTIME_DEPENDENCIES ( ${_EXE_NAME} \"${_LIB_DIR}/$(Configuration)/\" \"${EXECUTABLE_OUTPUT_PATH}/$(Configuration)/\") + ENDFOREACH (_LIB_DIR ${GUACAMOLE_LIBRARY_DIRS}) + +ENDIF (MSVC) \ No newline at end of file diff --git a/plugins/guacamole-oculus/example-oculus/data/materials/ComplexTexture.gsd b/plugins/guacamole-oculus/example-oculus/data/materials/ComplexTexture.gsd new file mode 100644 index 000000000..853e91dc1 --- /dev/null +++ b/plugins/guacamole-oculus/example-oculus/data/materials/ComplexTexture.gsd @@ -0,0 +1,55 @@ + +{ + "final_shading_stage" : + { + "body" : "vec3 diffuse = texture2D(diffuse_map, texcoords).rgb;\n\ngua_color = diffuse * emit + (1.0 - emit) * \n my_diffuse_color * diffuse + my_specular_intensity;\n", + "functions" : "", + "outputs" : null, + "uniforms" : + { + "diffuse_map" : "sampler2D", + "emit" : "float" + } + }, + "gbuffer_fragment_stage" : + { + "body" : "vec3 ts_normal = normalize(texture2D(normal_map, my_texcoords).rgb * 2.0 - 1.0);\n\ngua_normal = normalize( my_tangent * ts_normal.x + my_bitangent * \n ts_normal.y + my_normal * ts_normal.z);\n \ntexcoords = my_texcoords;", + "functions" : "", + "outputs" : + { + "texcoords" : "vec2" + }, + "uniforms" : + { + "normal_map" : "sampler2D" + } + }, + "gbuffer_vertex_stage" : + { + "body" : "my_tangent = gua_world_tangent;\nmy_texcoords = gua_texcoords;\nmy_bitangent = gua_world_bitangent;\nmy_normal = gua_world_normal;\n\ngua_position = gua_world_position;", + "functions" : "", + "outputs" : + { + "my_bitangent" : "vec3", + "my_normal" : "vec3", + "my_tangent" : "vec3", + "my_texcoords" : "vec2" + }, + "uniforms" : null + }, + "lbuffer_stage" : + { + "body" : "my_diffuse_color = my_dot(gua_normal, gua_light_direction) * \n gua_light_intensity * gua_light_color;\n \nfloat spec = texture2D(specular_map, texcoords).x;\n\nmy_specular_intensity = spec * pow(max(0, dot(\n reflect(gua_light_direction, gua_normal), \n normalize(gua_position - gua_camera_position)\n )), shinyness) * gua_light_intensity;", + "functions" : "float my_dot(vec3 a, vec3 b) {\n return dot(a, b);\n}", + "outputs" : + { + "my_diffuse_color" : "vec3", + "my_specular_intensity" : "float" + }, + "uniforms" : + { + "shinyness" : "float", + "specular_map" : "sampler2D" + } + } +} diff --git a/plugins/guacamole-oculus/example-oculus/data/materials/Shadeless.gsd b/plugins/guacamole-oculus/example-oculus/data/materials/Shadeless.gsd new file mode 100644 index 000000000..ac17b1eda --- /dev/null +++ b/plugins/guacamole-oculus/example-oculus/data/materials/Shadeless.gsd @@ -0,0 +1,41 @@ + +{ + "final_shading_stage" : + { + "body" : "gua_color = diffuse_color;\n", + "functions" : "", + "outputs" : null, + "uniforms" : + { + "diffuse_color" : "vec3" + } + }, + "gbuffer_fragment_stage" : + { + "body" : "gua_normal = varying_normal;\n", + "functions" : "", + "outputs" : null, + "uniforms" : null + }, + "gbuffer_vertex_stage" : + { + "body" : "gua_position = gua_world_position;\nvarying_normal = gua_world_normal;\n", + "functions" : "", + "outputs" : + { + "varying_normal" : "vec3" + }, + "uniforms" : null + }, + "lbuffer_stage" : + { + "body" : "", + "functions" : "", + "outputs" : + { + "diffuse" : "vec3", + "specular" : "float" + }, + "uniforms" : null + } +} diff --git a/plugins/guacamole-oculus/example-oculus/data/materials/Stones.gmd b/plugins/guacamole-oculus/example-oculus/data/materials/Stones.gmd new file mode 100644 index 000000000..f574e692f --- /dev/null +++ b/plugins/guacamole-oculus/example-oculus/data/materials/Stones.gmd @@ -0,0 +1,12 @@ + +{ + "shading_model" : "ComplexTexture", + "uniforms" : + { + "diffuse_map" : "data/textures/stones_diffuse.jpg", + "emit" : "0.1", + "normal_map" : "data/textures/stones_normal.jpg", + "shinyness" : "50", + "specular_map" : "data/textures/stones_specular.jpg" + } +} diff --git a/plugins/guacamole-oculus/example-oculus/data/materials/White.gmd b/plugins/guacamole-oculus/example-oculus/data/materials/White.gmd new file mode 100644 index 000000000..b85505972 --- /dev/null +++ b/plugins/guacamole-oculus/example-oculus/data/materials/White.gmd @@ -0,0 +1,8 @@ + +{ + "shading_model" : "Shadeless", + "uniforms" : + { + "diffuse_color" : "(1.000 1.000 1.000)" + } +} diff --git a/plugins/guacamole-oculus/example-oculus/data/objects/light_sphere.obj b/plugins/guacamole-oculus/example-oculus/data/objects/light_sphere.obj new file mode 100644 index 000000000..4c00d38a2 --- /dev/null +++ b/plugins/guacamole-oculus/example-oculus/data/objects/light_sphere.obj @@ -0,0 +1,647 @@ +# Blender v2.62 (sub 4) OBJ File: '' +# www.blender.org +v 0.000000 -0.500000 0.000000 +v 0.361804 -0.223610 0.262863 +v -0.138194 -0.223610 0.425325 +v -0.447213 -0.223608 0.000000 +v -0.138194 -0.223610 -0.425325 +v 0.361804 -0.223610 -0.262863 +v 0.138194 0.223610 0.425325 +v -0.361804 0.223610 0.262863 +v -0.361804 0.223610 -0.262863 +v 0.138194 0.223610 -0.425325 +v 0.447213 0.223608 0.000000 +v 0.000000 0.500000 0.000000 +v 0.101590 -0.483975 0.073809 +v 0.212661 -0.425327 0.154506 +v 0.304773 -0.328759 0.221428 +v 0.265970 -0.251151 0.340856 +v 0.131434 -0.262869 0.404506 +v -0.014820 -0.251151 0.432092 +v -0.116411 -0.328760 0.358282 +v -0.081228 -0.425327 0.249998 +v -0.038803 -0.483975 0.119426 +v 0.101590 -0.483975 -0.073809 +v 0.212661 -0.425327 -0.154506 +v 0.304773 -0.328759 -0.221428 +v 0.406365 -0.251150 -0.147619 +v 0.425324 -0.262868 0.000000 +v 0.406365 -0.251150 0.147619 +v -0.241986 -0.251151 0.358282 +v -0.344095 -0.262868 0.249998 +v -0.415525 -0.251149 0.119427 +v -0.376721 -0.328757 0.000000 +v -0.262865 -0.425326 0.000000 +v -0.125573 -0.483974 0.000000 +v -0.415525 -0.251149 -0.119427 +v -0.344095 -0.262868 -0.249998 +v -0.241986 -0.251151 -0.358282 +v -0.116411 -0.328760 -0.358282 +v -0.081228 -0.425327 -0.249998 +v -0.038803 -0.483975 -0.119426 +v -0.014820 -0.251151 -0.432092 +v 0.131434 -0.262869 -0.404506 +v 0.265970 -0.251151 -0.340856 +v 0.430349 -0.125575 -0.221429 +v 0.475529 0.000000 -0.154506 +v 0.478313 0.125575 -0.073809 +v 0.478313 0.125575 0.073809 +v 0.475529 -0.000000 0.154506 +v 0.430349 -0.125575 0.221429 +v 0.343579 -0.125576 0.340858 +v 0.293893 0.000000 0.404508 +v 0.218003 0.125576 0.432094 +v 0.077608 0.125576 0.477711 +v 0.000000 -0.000000 0.500000 +v -0.077608 -0.125576 0.477711 +v -0.218003 -0.125576 0.432094 +v -0.293893 0.000000 0.404508 +v -0.343579 0.125576 0.340858 +v -0.430349 0.125575 0.221429 +v -0.475529 -0.000000 0.154506 +v -0.478313 -0.125575 0.073809 +v -0.478313 -0.125575 -0.073809 +v -0.475529 0.000000 -0.154506 +v -0.430349 0.125575 -0.221429 +v -0.343579 0.125576 -0.340858 +v -0.293893 -0.000000 -0.404508 +v -0.218003 -0.125576 -0.432094 +v -0.077608 -0.125576 -0.477711 +v 0.000000 0.000000 -0.500000 +v 0.077608 0.125576 -0.477711 +v 0.218003 0.125576 -0.432094 +v 0.293893 -0.000000 -0.404508 +v 0.343579 -0.125576 -0.340858 +v 0.415525 0.251149 0.119427 +v 0.344095 0.262868 0.249998 +v 0.241986 0.251151 0.358282 +v 0.014820 0.251151 0.432092 +v -0.131434 0.262869 0.404506 +v -0.265970 0.251151 0.340856 +v -0.406365 0.251150 0.147619 +v -0.425324 0.262868 0.000000 +v -0.406365 0.251150 -0.147619 +v -0.265970 0.251151 -0.340856 +v -0.131434 0.262869 -0.404506 +v 0.014820 0.251151 -0.432092 +v 0.241986 0.251151 -0.358282 +v 0.344095 0.262868 -0.249998 +v 0.415525 0.251149 -0.119427 +v 0.376721 0.328757 0.000000 +v 0.262865 0.425326 0.000000 +v 0.125573 0.483974 0.000000 +v 0.038803 0.483975 0.119426 +v 0.081228 0.425327 0.249998 +v 0.116411 0.328760 0.358282 +v -0.101590 0.483975 0.073809 +v -0.212661 0.425327 0.154506 +v -0.304773 0.328759 0.221428 +v -0.101590 0.483975 -0.073809 +v -0.212661 0.425327 -0.154506 +v -0.304773 0.328759 -0.221428 +v 0.038803 0.483975 -0.119426 +v 0.081228 0.425327 -0.249998 +v 0.116411 0.328760 -0.358282 +v 0.026395 -0.361806 0.344093 +v 0.069099 -0.447215 0.212660 +v 0.180902 -0.361805 0.293890 +v 0.335409 -0.361805 -0.081228 +v 0.335409 -0.361805 0.081229 +v 0.223605 -0.447214 0.000000 +v -0.319097 -0.361805 0.131432 +v -0.180901 -0.447214 0.131432 +v -0.223605 -0.361805 0.262864 +v -0.223605 -0.361806 -0.262864 +v -0.180901 -0.447214 -0.131431 +v -0.319097 -0.361805 -0.131431 +v 0.180902 -0.361806 -0.293890 +v 0.069098 -0.447215 -0.212661 +v 0.026395 -0.361805 -0.344093 +v 0.500000 0.000000 0.000000 +v 0.473607 -0.138198 0.081229 +v 0.473607 -0.138198 -0.081229 +v 0.154509 0.000000 0.475528 +v 0.069100 -0.138199 0.475527 +v 0.223608 -0.138199 0.425324 +v -0.404509 0.000000 0.293892 +v -0.430902 -0.138198 0.212662 +v -0.335410 -0.138199 0.344095 +v -0.404509 -0.000000 -0.293892 +v -0.335409 -0.138199 -0.344095 +v -0.430902 -0.138198 -0.212662 +v 0.154509 -0.000000 -0.475528 +v 0.223608 -0.138199 -0.425324 +v 0.069100 -0.138199 -0.475527 +v 0.335410 0.138198 0.344095 +v 0.404509 -0.000001 0.293891 +v 0.430902 0.138197 0.212662 +v -0.223608 0.138198 0.425324 +v -0.154509 -0.000000 0.475528 +v -0.069100 0.138198 0.475528 +v -0.473607 0.138198 -0.081229 +v -0.500000 0.000000 0.000000 +v -0.473606 0.138198 0.081229 +v -0.069099 0.138199 -0.475528 +v -0.154508 -0.000000 -0.475528 +v -0.223608 0.138199 -0.425324 +v 0.430902 0.138198 -0.212661 +v 0.404510 0.000000 -0.293891 +v 0.335410 0.138199 -0.344095 +v 0.180900 0.447215 0.131431 +v 0.223605 0.361806 0.262864 +v 0.319097 0.361805 0.131432 +v -0.069099 0.447215 0.212660 +v -0.180902 0.361806 0.293889 +v -0.026395 0.361806 0.344092 +v -0.223605 0.447215 0.000000 +v -0.335408 0.361805 -0.081229 +v -0.335408 0.361805 0.081229 +v -0.069099 0.447215 -0.212660 +v -0.026395 0.361806 -0.344092 +v -0.180902 0.361806 -0.293889 +v 0.180900 0.447215 -0.131431 +v 0.319097 0.361805 -0.131432 +v 0.223605 0.361806 -0.262864 +vn 0.000000 1.000000 0.000000 +vn 0.080569 0.965392 -0.247963 +vn -0.210944 0.965392 -0.153264 +vn -0.723594 0.447218 -0.525712 +vn -0.815180 0.503800 -0.285714 +vn -0.604205 0.664968 -0.438978 +vn 0.260750 0.965392 0.000000 +vn 0.080569 0.965392 0.247963 +vn -0.210944 0.965392 0.153264 +vn -0.864986 0.243049 -0.438978 +vn 0.276376 0.447218 -0.850642 +vn 0.150212 0.243049 -0.958281 +vn 0.019837 0.503800 -0.863552 +vn 0.894406 0.447188 0.000000 +vn 0.957823 0.243049 -0.153264 +vn 0.827448 0.503800 -0.247963 +vn 0.276376 0.447218 0.850642 +vn 0.441725 0.243049 0.863582 +vn 0.491531 0.503800 0.710288 +vn -0.723594 0.447218 0.525712 +vn -0.684805 0.243049 0.686972 +vn -0.523637 0.503800 0.686972 +vn -0.684805 0.243049 -0.686972 +vn 0.441725 0.243049 -0.863582 +vn 0.957823 0.243049 0.153264 +vn 0.150212 0.243049 0.958281 +vn -0.864986 0.243049 0.438978 +vn -0.276376 -0.447218 -0.850642 +vn -0.230781 -0.664968 -0.710288 +vn -0.491531 -0.503800 -0.710288 +vn 0.723594 -0.447218 -0.525712 +vn 0.604205 -0.664968 -0.438978 +vn 0.523637 -0.503800 -0.686972 +vn 0.723594 -0.447218 0.525712 +vn 0.604205 -0.664968 0.438978 +vn 0.815180 -0.503800 0.285714 +vn -0.276376 -0.447218 0.850642 +vn -0.230781 -0.664968 0.710288 +vn -0.019837 -0.503800 0.863552 +vn -0.894406 -0.447188 0.000000 +vn -0.746849 -0.664968 0.000000 +vn -0.827448 -0.503800 0.247963 +vn 0.230781 0.664968 -0.710288 +vn 0.162450 0.850642 -0.499985 +vn -0.059206 0.727531 -0.683493 +vn -0.140629 0.890408 -0.432844 +vn -0.262856 0.525712 -0.808985 +vn -0.353832 0.727531 -0.587756 +vn -0.523637 0.503800 -0.686972 +vn -0.425306 0.850642 -0.309000 +vn -0.815180 0.503800 0.285714 +vn -0.604205 0.664968 0.438978 +vn -0.850642 0.525712 0.000000 +vn -0.668325 0.727531 0.154881 +vn -0.668325 0.727531 -0.154881 +vn -0.425306 0.850642 0.309000 +vn -0.455123 0.890408 0.000000 +vn 0.746849 0.664968 0.000000 +vn 0.525712 0.850642 0.000000 +vn 0.631733 0.727531 -0.267495 +vn 0.368206 0.890408 -0.267495 +vn 0.688162 0.525712 -0.499985 +vn 0.449629 0.727531 -0.518143 +vn 0.491531 0.503800 -0.710288 +vn 0.230781 0.664968 0.710288 +vn 0.162450 0.850642 0.499985 +vn 0.449629 0.727531 0.518143 +vn 0.368206 0.890408 0.267495 +vn 0.688162 0.525712 0.499985 +vn 0.631733 0.727531 0.267495 +vn 0.827448 0.503800 0.247963 +vn -0.353832 0.727531 0.587756 +vn -0.140629 0.890408 0.432844 +vn -0.262856 0.525712 0.808985 +vn -0.059206 0.727531 0.683493 +vn 0.019837 0.503800 0.863552 +vn -0.957823 -0.243049 -0.153264 +vn -0.957823 -0.243049 0.153264 +vn -0.951048 0.000000 -0.309000 +vn -0.999939 0.008850 0.000000 +vn -0.949614 0.272408 -0.154881 +vn -0.951048 0.000000 0.309000 +vn -0.949614 0.272408 0.154881 +vn -0.150212 -0.243049 -0.958281 +vn -0.441725 -0.243049 -0.863582 +vn 0.000000 0.000000 -1.000000 +vn -0.309000 0.008850 -0.951018 +vn -0.146123 0.272408 -0.950987 +vn -0.587756 0.000000 -0.809015 +vn -0.440748 0.272408 -0.855251 +vn 0.864986 -0.243049 -0.438978 +vn 0.684805 -0.243049 -0.686972 +vn 0.951048 0.000000 -0.309000 +vn 0.808985 0.008850 -0.587756 +vn 0.859310 0.272408 -0.432844 +vn 0.587756 0.000000 -0.809015 +vn 0.677206 0.272408 -0.683493 +vn 0.684805 -0.243049 0.686972 +vn 0.864986 -0.243049 0.438978 +vn 0.587756 0.000000 0.809015 +vn 0.808985 0.008850 0.587756 +vn 0.677206 0.272408 0.683493 +vn 0.951048 0.000000 0.309000 +vn 0.859310 0.272408 0.432844 +vn -0.441725 -0.243049 0.863582 +vn -0.150212 -0.243049 0.958281 +vn -0.587756 0.000000 0.809015 +vn -0.309000 0.008850 0.951018 +vn -0.440748 0.272408 0.855251 +vn 0.000000 0.000000 1.000000 +vn -0.146123 0.272408 0.950987 +vn -0.677206 -0.272408 -0.683493 +vn -0.808985 -0.008850 -0.587756 +vn -0.688162 -0.525712 -0.499985 +vn -0.859310 -0.272408 -0.432844 +vn -0.827448 -0.503800 -0.247963 +vn 0.440748 -0.272408 -0.855251 +vn 0.309000 -0.008850 -0.951018 +vn 0.262856 -0.525712 -0.808985 +vn 0.146123 -0.272408 -0.950987 +vn -0.019837 -0.503800 -0.863552 +vn 0.949614 -0.272408 0.154881 +vn 0.999939 -0.008850 0.000000 +vn 0.850642 -0.525712 0.000000 +vn 0.949614 -0.272408 -0.154881 +vn 0.815180 -0.503800 -0.285714 +vn 0.146123 -0.272408 0.951018 +vn 0.309000 -0.008850 0.951018 +vn 0.262856 -0.525712 0.808985 +vn 0.440748 -0.272408 0.855251 +vn 0.523637 -0.503800 0.686972 +vn -0.859310 -0.272408 0.432844 +vn -0.808985 -0.008850 0.587756 +vn -0.688162 -0.525712 0.499985 +vn -0.677206 -0.272408 0.683493 +vn -0.491531 -0.503800 0.710288 +vn -0.080569 -0.965392 -0.247963 +vn 0.000000 -1.000000 0.000000 +vn -0.260750 -0.965392 0.000000 +vn -0.162450 -0.850642 -0.499985 +vn -0.368206 -0.890408 -0.267495 +vn -0.449629 -0.727531 -0.518143 +vn -0.525712 -0.850642 0.000000 +vn -0.631733 -0.727531 -0.267495 +vn 0.210944 -0.965392 -0.153264 +vn 0.425306 -0.850642 -0.309000 +vn 0.140629 -0.890408 -0.432844 +vn 0.353832 -0.727531 -0.587725 +vn 0.059206 -0.727531 -0.683493 +vn 0.210944 -0.965392 0.153264 +vn 0.425306 -0.850642 0.309000 +vn 0.455123 -0.890408 0.000000 +vn 0.668325 -0.727531 0.154881 +vn 0.668325 -0.727531 -0.154881 +vn -0.080569 -0.965392 0.247963 +vn -0.162450 -0.850642 0.499985 +vn 0.140629 -0.890408 0.432844 +vn 0.059206 -0.727531 0.683493 +vn 0.353832 -0.727531 0.587725 +vn -0.368206 -0.890408 0.267495 +vn -0.631733 -0.727531 0.267495 +vn -0.449629 -0.727531 0.518143 +s 1 +f 1//1 21//2 13//3 +f 2//4 27//5 15//6 +f 1//1 33//7 21//2 +f 1//1 39//8 33//7 +f 1//1 22//9 39//8 +f 2//4 48//10 27//5 +f 3//11 54//12 18//13 +f 4//14 60//15 30//16 +f 5//17 66//18 36//19 +f 6//20 72//21 42//22 +f 2//4 49//23 48//10 +f 3//11 55//24 54//12 +f 4//14 61//25 60//15 +f 5//17 67//26 66//18 +f 6//20 43//27 72//21 +f 7//28 93//29 75//30 +f 8//31 96//32 78//33 +f 9//34 99//35 81//36 +f 10//37 102//38 84//39 +f 11//40 88//41 87//42 +f 19//43 3//11 18//13 +f 20//44 19//43 103//45 +f 21//2 20//44 104//46 +f 19//43 18//13 103//45 +f 103//45 18//13 17//47 +f 20//44 103//45 104//46 +f 104//46 103//45 105//48 +f 103//45 17//47 105//48 +f 105//48 17//47 16//49 +f 21//2 104//46 13//3 +f 13//3 104//46 14//50 +f 104//46 105//48 14//50 +f 14//50 105//48 15//6 +f 105//48 16//49 15//6 +f 15//6 16//49 2//4 +f 25//51 6//20 24//52 +f 26//53 25//51 106//54 +f 27//5 26//53 107//55 +f 25//51 24//52 106//54 +f 106//54 24//52 23//56 +f 26//53 106//54 107//55 +f 107//55 106//54 108//57 +f 106//54 23//56 108//57 +f 108//57 23//56 22//9 +f 27//5 107//55 15//6 +f 15//6 107//55 14//50 +f 107//55 108//57 14//50 +f 14//50 108//57 13//3 +f 108//57 22//9 13//3 +f 22//9 1//1 13//3 +f 31//58 4//14 30//16 +f 32//59 31//58 109//60 +f 33//7 32//59 110//61 +f 31//58 30//16 109//60 +f 109//60 30//16 29//62 +f 32//59 109//60 110//61 +f 110//61 109//60 111//63 +f 109//60 29//62 111//63 +f 111//63 29//62 28//64 +f 33//7 110//61 21//2 +f 21//2 110//61 20//44 +f 110//61 111//63 20//44 +f 20//44 111//63 19//43 +f 111//63 28//64 19//43 +f 19//43 28//64 3//11 +f 37//65 5//17 36//19 +f 38//66 37//65 112//67 +f 39//8 38//66 113//68 +f 37//65 36//19 112//67 +f 112//67 36//19 35//69 +f 38//66 112//67 113//68 +f 113//68 112//67 114//70 +f 112//67 35//69 114//70 +f 114//70 35//69 34//71 +f 39//8 113//68 33//7 +f 33//7 113//68 32//59 +f 113//68 114//70 32//59 +f 32//59 114//70 31//58 +f 114//70 34//71 31//58 +f 31//58 34//71 4//14 +f 24//52 6//20 42//22 +f 23//56 24//52 115//72 +f 22//9 23//56 116//73 +f 24//52 42//22 115//72 +f 115//72 42//22 41//74 +f 23//56 115//72 116//73 +f 116//73 115//72 117//75 +f 115//72 41//74 117//75 +f 117//75 41//74 40//76 +f 22//9 116//73 39//8 +f 39//8 116//73 38//66 +f 116//73 117//75 38//66 +f 38//66 117//75 37//65 +f 117//75 40//76 37//65 +f 37//65 40//76 5//17 +f 46//77 11//40 45//78 +f 47//79 46//77 118//80 +f 48//10 47//79 119//81 +f 46//77 45//78 118//80 +f 118//80 45//78 44//82 +f 47//79 118//80 119//81 +f 119//81 118//80 120//83 +f 118//80 44//82 120//83 +f 120//83 44//82 43//27 +f 48//10 119//81 27//5 +f 27//5 119//81 26//53 +f 119//81 120//83 26//53 +f 26//53 120//83 25//51 +f 120//83 43//27 25//51 +f 25//51 43//27 6//20 +f 52//84 7//28 51//85 +f 53//86 52//84 121//87 +f 54//12 53//86 122//88 +f 52//84 51//85 121//87 +f 121//87 51//85 50//89 +f 53//86 121//87 122//88 +f 122//88 121//87 123//90 +f 121//87 50//89 123//90 +f 123//90 50//89 49//23 +f 54//12 122//88 18//13 +f 18//13 122//88 17//47 +f 122//88 123//90 17//47 +f 17//47 123//90 16//49 +f 123//90 49//23 16//49 +f 16//49 49//23 2//4 +f 58//91 8//31 57//92 +f 59//93 58//91 124//94 +f 60//15 59//93 125//95 +f 58//91 57//92 124//94 +f 124//94 57//92 56//96 +f 59//93 124//94 125//95 +f 125//95 124//94 126//97 +f 124//94 56//96 126//97 +f 126//97 56//96 55//24 +f 60//15 125//95 30//16 +f 30//16 125//95 29//62 +f 125//95 126//97 29//62 +f 29//62 126//97 28//64 +f 126//97 55//24 28//64 +f 28//64 55//24 3//11 +f 64//98 9//34 63//99 +f 65//100 64//98 127//101 +f 66//18 65//100 128//102 +f 64//98 63//99 127//101 +f 127//101 63//99 62//103 +f 65//100 127//101 128//102 +f 128//102 127//101 129//104 +f 127//101 62//103 129//104 +f 129//104 62//103 61//25 +f 66//18 128//102 36//19 +f 36//19 128//102 35//69 +f 128//102 129//104 35//69 +f 35//69 129//104 34//71 +f 129//104 61//25 34//71 +f 34//71 61//25 4//14 +f 70//105 10//37 69//106 +f 71//107 70//105 130//108 +f 72//21 71//107 131//109 +f 70//105 69//106 130//108 +f 130//108 69//106 68//110 +f 71//107 130//108 131//109 +f 131//109 130//108 132//111 +f 130//108 68//110 132//111 +f 132//111 68//110 67//26 +f 72//21 131//109 42//22 +f 42//22 131//109 41//74 +f 131//109 132//111 41//74 +f 41//74 132//111 40//76 +f 132//111 67//26 40//76 +f 40//76 67//26 5//17 +f 51//85 7//28 75//30 +f 50//89 51//85 133//112 +f 49//23 50//89 134//113 +f 51//85 75//30 133//112 +f 133//112 75//30 74//114 +f 50//89 133//112 134//113 +f 134//113 133//112 135//115 +f 133//112 74//114 135//115 +f 135//115 74//114 73//116 +f 49//23 134//113 48//10 +f 48//10 134//113 47//79 +f 134//113 135//115 47//79 +f 47//79 135//115 46//77 +f 135//115 73//116 46//77 +f 46//77 73//116 11//40 +f 57//92 8//31 78//33 +f 56//96 57//92 136//117 +f 55//24 56//96 137//118 +f 57//92 78//33 136//117 +f 136//117 78//33 77//119 +f 56//96 136//117 137//118 +f 137//118 136//117 138//120 +f 136//117 77//119 138//120 +f 138//120 77//119 76//121 +f 55//24 137//118 54//12 +f 54//12 137//118 53//86 +f 137//118 138//120 53//86 +f 53//86 138//120 52//84 +f 138//120 76//121 52//84 +f 52//84 76//121 7//28 +f 63//99 9//34 81//36 +f 62//103 63//99 139//122 +f 61//25 62//103 140//123 +f 63//99 81//36 139//122 +f 139//122 81//36 80//124 +f 62//103 139//122 140//123 +f 140//123 139//122 141//125 +f 139//122 80//124 141//125 +f 141//125 80//124 79//126 +f 61//25 140//123 60//15 +f 60//15 140//123 59//93 +f 140//123 141//125 59//93 +f 59//93 141//125 58//91 +f 141//125 79//126 58//91 +f 58//91 79//126 8//31 +f 69//106 10//37 84//39 +f 68//110 69//106 142//127 +f 67//26 68//110 143//128 +f 69//106 84//39 142//127 +f 142//127 84//39 83//129 +f 68//110 142//127 143//128 +f 143//128 142//127 144//130 +f 142//127 83//129 144//130 +f 144//130 83//129 82//131 +f 67//26 143//128 66//18 +f 66//18 143//128 65//100 +f 143//128 144//130 65//100 +f 65//100 144//130 64//98 +f 144//130 82//131 64//98 +f 64//98 82//131 9//34 +f 45//78 11//40 87//42 +f 44//82 45//78 145//132 +f 43//27 44//82 146//133 +f 45//78 87//42 145//132 +f 145//132 87//42 86//134 +f 44//82 145//132 146//133 +f 146//133 145//132 147//135 +f 145//132 86//134 147//135 +f 147//135 86//134 85//136 +f 43//27 146//133 72//21 +f 72//21 146//133 71//107 +f 146//133 147//135 71//107 +f 71//107 147//135 70//105 +f 147//135 85//136 70//105 +f 70//105 85//136 10//37 +f 91//137 12//138 90//139 +f 92//140 91//137 148//141 +f 93//29 92//140 149//142 +f 91//137 90//139 148//141 +f 148//141 90//139 89//143 +f 92//140 148//141 149//142 +f 149//142 148//141 150//144 +f 148//141 89//143 150//144 +f 150//144 89//143 88//41 +f 93//29 149//142 75//30 +f 75//30 149//142 74//114 +f 149//142 150//144 74//114 +f 74//114 150//144 73//116 +f 150//144 88//41 73//116 +f 73//116 88//41 11//40 +f 94//145 12//138 91//137 +f 95//146 94//145 151//147 +f 96//32 95//146 152//148 +f 94//145 91//137 151//147 +f 151//147 91//137 92//140 +f 95//146 151//147 152//148 +f 152//148 151//147 153//149 +f 151//147 92//140 153//149 +f 153//149 92//140 93//29 +f 96//32 152//148 78//33 +f 78//33 152//148 77//119 +f 152//148 153//149 77//119 +f 77//119 153//149 76//121 +f 153//149 93//29 76//121 +f 76//121 93//29 7//28 +f 97//150 12//138 94//145 +f 98//151 97//150 154//152 +f 99//35 98//151 155//153 +f 97//150 94//145 154//152 +f 154//152 94//145 95//146 +f 98//151 154//152 155//153 +f 155//153 154//152 156//154 +f 154//152 95//146 156//154 +f 156//154 95//146 96//32 +f 99//35 155//153 81//36 +f 81//36 155//153 80//124 +f 155//153 156//154 80//124 +f 80//124 156//154 79//126 +f 156//154 96//32 79//126 +f 79//126 96//32 8//31 +f 100//155 12//138 97//150 +f 101//156 100//155 157//157 +f 102//38 101//156 158//158 +f 100//155 97//150 157//157 +f 157//157 97//150 98//151 +f 101//156 157//157 158//158 +f 158//158 157//157 159//159 +f 157//157 98//151 159//159 +f 159//159 98//151 99//35 +f 102//38 158//158 84//39 +f 84//39 158//158 83//129 +f 158//158 159//159 83//129 +f 83//129 159//159 82//131 +f 159//159 99//35 82//131 +f 82//131 99//35 9//34 +f 90//139 12//138 100//155 +f 89//143 90//139 160//160 +f 88//41 89//143 161//161 +f 90//139 100//155 160//160 +f 160//160 100//155 101//156 +f 89//143 160//160 161//161 +f 161//161 160//160 162//162 +f 160//160 101//156 162//162 +f 162//162 101//156 102//38 +f 88//41 161//161 87//42 +f 87//42 161//161 86//134 +f 161//161 162//162 86//134 +f 86//134 162//162 85//136 +f 162//162 102//38 85//136 +f 85//136 102//38 10//37 diff --git a/plugins/guacamole-oculus/example-oculus/data/objects/monkey.obj b/plugins/guacamole-oculus/example-oculus/data/objects/monkey.obj new file mode 100644 index 000000000..9f4cdfbac --- /dev/null +++ b/plugins/guacamole-oculus/example-oculus/data/objects/monkey.obj @@ -0,0 +1,10833 @@ +# Blender v2.58 (sub 0) OBJ File: '' +# www.blender.org +o Monkey +v 0.492188 0.185547 0.720703 +v -0.492188 0.185547 0.720703 +v 0.558594 0.158203 0.625000 +v -0.558594 0.158203 0.625000 +v 0.437500 0.039063 0.650391 +v -0.437500 0.039063 0.650391 +v 0.410156 0.105469 0.738281 +v -0.410156 0.105469 0.738281 +v 0.294922 0.105469 0.759766 +v -0.294922 0.105469 0.759766 +v 0.265625 0.039063 0.681641 +v -0.265625 0.039063 0.681641 +v 0.144531 0.158203 0.697266 +v -0.144531 0.158203 0.697266 +v 0.214844 0.185547 0.769531 +v -0.214844 0.185547 0.769531 +v 0.214844 0.300781 0.769531 +v -0.214844 0.300781 0.769531 +v 0.144531 0.328125 0.697266 +v -0.144531 0.328125 0.697266 +v 0.265625 0.449219 0.681641 +v -0.265625 0.449219 0.681641 +v 0.294922 0.382813 0.759766 +v -0.294922 0.382813 0.759766 +v 0.410156 0.382813 0.738281 +v -0.410156 0.382813 0.738281 +v 0.437500 0.449219 0.650391 +v -0.437500 0.449219 0.650391 +v 0.558594 0.328125 0.625000 +v -0.558594 0.328125 0.625000 +v 0.492188 0.300781 0.720703 +v -0.492188 0.300781 0.720703 +v 0.457031 0.287109 0.769531 +v -0.457031 0.287109 0.769531 +v 0.396484 0.349609 0.783203 +v -0.396484 0.349609 0.783203 +v 0.310547 0.349609 0.800781 +v -0.310547 0.349609 0.800781 +v 0.251953 0.287109 0.808594 +v -0.251953 0.287109 0.808594 +v 0.251953 0.201172 0.808594 +v -0.251953 0.201172 0.808594 +v 0.310547 0.142578 0.800781 +v -0.310547 0.142578 0.800781 +v 0.396484 0.142578 0.783203 +v -0.396484 0.142578 0.783203 +v 0.457031 0.201172 0.769531 +v -0.457031 0.201172 0.769531 +v 0.424479 0.213542 0.794271 +v -0.424479 0.213542 0.794271 +v 0.382812 0.171875 0.804688 +v -0.382812 0.171875 0.804688 +v 0.322917 0.171875 0.817708 +v -0.322917 0.171875 0.817708 +v 0.281250 0.213542 0.822917 +v -0.281250 0.213542 0.822917 +v 0.281250 0.273438 0.822917 +v -0.281250 0.273438 0.822917 +v 0.322917 0.317708 0.817708 +v -0.322917 0.317708 0.817708 +v 0.382812 0.317708 0.804688 +v -0.382812 0.317708 0.804688 +v 0.424479 0.273438 0.794271 +v -0.424479 0.273438 0.794271 +v 0.085938 -0.957031 0.601563 +v -0.085938 -0.957031 0.601563 +v 0.226562 -0.939453 0.585938 +v -0.226562 -0.939453 0.585938 +v 0.298828 -0.892578 0.587891 +v -0.298828 -0.892578 0.587891 +v 0.308594 -0.777344 0.613281 +v -0.308594 -0.777344 0.613281 +v 0.281250 -0.570312 0.634766 +v -0.281250 -0.570312 0.634766 +v 0.291016 -0.119141 0.644531 +v -0.291016 -0.119141 0.644531 +v 0.521484 -0.042969 0.591797 +v -0.521484 -0.042969 0.591797 +v 0.701172 0.091797 0.552734 +v -0.701172 0.091797 0.552734 +v 0.789062 0.289063 0.574219 +v -0.789062 0.289063 0.574219 +v 0.750000 0.425781 0.650391 +v -0.750000 0.425781 0.650391 +v 0.582031 0.511719 0.708984 +v -0.582031 0.511719 0.708984 +v 0.390625 0.636719 0.763672 +v -0.390625 0.636719 0.763672 +v 0.248047 0.683594 0.794922 +v -0.248047 0.683594 0.794922 +v 0.130859 0.564453 0.800781 +v -0.130859 0.564453 0.800781 +v 0.041016 0.425781 0.789062 +v -0.041016 0.425781 0.789062 +v 0.179688 0.482422 0.806641 +v -0.179688 0.482422 0.806641 +v 0.273438 0.550781 0.796875 +v -0.273438 0.550781 0.796875 +v 0.376953 0.525391 0.773437 +v -0.376953 0.525391 0.773437 +v 0.539062 0.443359 0.726562 +v -0.539062 0.443359 0.726562 +v 0.667969 0.365234 0.673828 +v -0.667969 0.365234 0.673828 +v 0.683594 0.265625 0.638672 +v -0.683594 0.265625 0.638672 +v 0.615234 0.126953 0.636719 +v -0.615234 0.126953 0.636719 +v 0.470703 0.021484 0.667969 +v -0.470703 0.021484 0.667969 +v 0.275391 -0.009766 0.732422 +v -0.275391 -0.009766 0.732422 +v 0.123047 0.044922 0.757812 +v -0.123047 0.044922 0.757812 +v 0.097656 0.375000 0.800781 +v -0.097656 0.375000 0.800781 +v 0.064453 0.269531 0.777344 +v -0.064453 0.269531 0.777344 +v 0.074219 0.152344 0.750000 +v -0.074219 0.152344 0.750000 +v 0.056641 -0.912109 0.664063 +v -0.056641 -0.912109 0.664063 +v 0.144531 -0.890625 0.667969 +v -0.144531 -0.890625 0.667969 +v 0.181641 -0.822266 0.685547 +v -0.181641 -0.822266 0.685547 +v 0.164062 -0.570312 0.720703 +v -0.164062 -0.570312 0.720703 +v 0.185547 -0.732422 0.705078 +v -0.185547 -0.732422 0.705078 +v 0.041016 -0.376953 0.746094 +v -0.041016 -0.376953 0.746094 +v 0.048828 -0.564453 0.742188 +v -0.048828 -0.564453 0.742188 +v 0.056641 -0.712891 0.734375 +v -0.056641 -0.712891 0.734375 +v 0.109375 -0.253906 0.767578 +v -0.109375 -0.253906 0.767578 +v 0.117188 -0.183594 0.767578 +v -0.117188 -0.183594 0.767578 +v 0.062500 -0.136719 0.761719 +v -0.062500 -0.136719 0.761719 +v 0.009766 -0.162109 0.767578 +v -0.009766 -0.162109 0.767578 +v 0.044922 -0.302734 0.761719 +v -0.044922 -0.302734 0.761719 +v 0.042969 -0.283203 0.792969 +v -0.042969 -0.283203 0.792969 +v 0.021484 -0.166016 0.804688 +v -0.021484 -0.166016 0.804688 +v 0.072266 -0.140625 0.796875 +v -0.072266 -0.140625 0.796875 +v 0.111328 -0.185547 0.804688 +v -0.111328 -0.185547 0.804688 +v 0.103516 -0.244141 0.802734 +v -0.103516 -0.244141 0.802734 +v 0.062500 -0.183594 0.820312 +v -0.062500 -0.183594 0.820312 +v 0.046875 -0.242187 0.816406 +v -0.046875 -0.242187 0.816406 +v 0.056641 -0.085937 0.755859 +v -0.056641 -0.085937 0.755859 +v 0.128906 -0.179687 0.753906 +v -0.128906 -0.179687 0.753906 +v 0.138672 -0.267578 0.728516 +v -0.138672 -0.267578 0.728516 +v 0.138672 -0.373047 0.728516 +v -0.138672 -0.373047 0.728516 +v 0.240234 -0.376953 0.636719 +v -0.240234 -0.376953 0.636719 +v 0.208984 -0.279297 0.632812 +v -0.208984 -0.279297 0.632812 +v 0.181641 -0.195312 0.660156 +v -0.181641 -0.195312 0.660156 +v 0.050781 -0.750000 0.728516 +v -0.050781 -0.750000 0.728516 +v 0.103516 -0.779297 0.720703 +v -0.103516 -0.779297 0.720703 +v 0.080078 -0.851562 0.701172 +v -0.080078 -0.851562 0.701172 +v 0.027344 -0.878906 0.689453 +v -0.027344 -0.878906 0.689453 +v 0.023438 -0.863281 0.660156 +v -0.023438 -0.863281 0.660156 +v 0.070312 -0.837891 0.667969 +v -0.070312 -0.837891 0.667969 +v 0.093750 -0.781250 0.685547 +v -0.093750 -0.781250 0.685547 +v 0.046875 -0.761719 0.691406 +v -0.046875 -0.761719 0.691406 +v 0.035156 -0.810547 0.646484 +v -0.035156 -0.810547 0.646484 +v 0.078125 -0.804687 0.645833 +v -0.078125 -0.804687 0.645833 +v 0.164062 0.181641 0.765625 +v -0.164062 0.181641 0.765625 +v 0.152344 0.257813 0.771484 +v -0.152344 0.257813 0.771484 +v 0.169922 0.347656 0.775391 +v -0.169922 0.347656 0.775391 +v 0.195312 0.125000 0.761719 +v -0.195312 0.125000 0.761719 +v 0.294922 0.070313 0.742188 +v -0.294922 0.070313 0.742188 +v 0.429688 0.060547 0.708984 +v -0.429688 0.060547 0.708984 +v 0.542969 0.136719 0.679688 +v -0.542969 0.136719 0.679688 +v 0.607422 0.242188 0.666016 +v -0.607422 0.242188 0.666016 +v 0.597656 0.328125 0.673828 +v -0.597656 0.328125 0.673828 +v 0.503906 0.390625 0.712891 +v -0.503906 0.390625 0.712891 +v 0.378906 0.435547 0.748047 +v -0.378906 0.435547 0.748047 +v 0.296875 0.449219 0.757812 +v -0.296875 0.449219 0.757812 +v 0.224609 0.419922 0.771484 +v -0.224609 0.419922 0.771484 +v 0.250000 0.388672 0.769531 +v -0.250000 0.388672 0.769531 +v 0.306641 0.414063 0.761719 +v -0.306641 0.414063 0.761719 +v 0.376953 0.406250 0.757812 +v -0.376953 0.406250 0.757812 +v 0.482422 0.369141 0.724609 +v -0.482422 0.369141 0.724609 +v 0.558594 0.314453 0.683594 +v -0.558594 0.314453 0.683594 +v 0.566406 0.244141 0.677734 +v -0.566406 0.244141 0.677734 +v 0.515625 0.156250 0.693359 +v -0.515625 0.156250 0.693359 +v 0.421875 0.091797 0.722656 +v -0.421875 0.091797 0.722656 +v 0.304688 0.095703 0.751953 +v -0.304688 0.095703 0.751953 +v 0.214844 0.140625 0.765625 +v -0.214844 0.140625 0.765625 +v 0.205078 0.332031 0.769531 +v -0.205078 0.332031 0.769531 +v 0.185547 0.259766 0.767578 +v -0.185547 0.259766 0.767578 +v 0.189453 0.193359 0.763672 +v -0.189453 0.193359 0.763672 +v 0.042969 0.447266 0.675781 +v -0.042969 0.447266 0.675781 +v 0.130859 0.583984 0.683594 +v -0.130859 0.583984 0.683594 +v 0.251953 0.707031 0.675781 +v -0.251953 0.707031 0.675781 +v 0.408203 0.650391 0.642578 +v -0.408203 0.650391 0.642578 +v 0.591797 0.523438 0.589844 +v -0.591797 0.523438 0.589844 +v 0.761719 0.443359 0.542969 +v -0.761719 0.443359 0.542969 +v 0.814453 0.287109 0.468750 +v -0.814453 0.287109 0.468750 +v 0.708984 0.068359 0.443359 +v -0.708984 0.068359 0.443359 +v 0.527344 -0.068359 0.488281 +v -0.527344 -0.068359 0.488281 +v 0.076172 -0.501953 0.304688 +v -0.076172 -0.501953 0.304688 +v 0.066406 -0.667969 0.347656 +v -0.066406 -0.667969 0.347656 +v 0.076172 -0.871094 0.402344 +v -0.076172 -0.871094 0.402344 +v 0.085938 -0.968750 0.507813 +v -0.085938 -0.968750 0.507813 +v 0.250000 -0.943359 0.478516 +v -0.250000 -0.943359 0.478516 +v 0.328125 -0.865234 0.458984 +v -0.328125 -0.865234 0.458984 +v 0.314453 -0.699219 0.468750 +v -0.314453 -0.699219 0.468750 +v 0.201172 -0.626953 0.375000 +v -0.201172 -0.626953 0.375000 +v 0.230469 -0.832031 0.396484 +v -0.230469 -0.832031 0.396484 +v 0.197266 -0.451172 0.353516 +v -0.197266 -0.451172 0.353516 +v 0.287109 -0.496094 0.484375 +v -0.287109 -0.496094 0.484375 +v 0.230469 -0.267578 0.501953 +v -0.230469 -0.267578 0.501953 +v 0.255859 -0.345703 0.490234 +v -0.255859 -0.345703 0.490234 +v 0.212891 -0.208984 0.521484 +v -0.212891 -0.208984 0.521484 +v 0.320312 -0.148437 0.515625 +v -0.320312 -0.148437 0.515625 +v 0.169922 -0.054688 -0.675781 +v -0.169922 -0.054688 -0.675781 +v 0.160156 -0.259766 -0.457031 +v -0.160156 -0.259766 -0.457031 +v 0.126953 -0.386719 -0.066406 +v -0.126953 -0.386719 -0.066406 +v 0.097656 -0.437500 0.222656 +v -0.097656 -0.437500 0.222656 +v 0.210938 -0.359375 0.314453 +v -0.210938 -0.359375 0.314453 +v 0.740234 0.087891 0.228516 +v -0.740234 0.087891 0.228516 +v 0.199219 0.281250 -0.761719 +v -0.199219 0.281250 -0.761719 +v 0.226562 0.916016 0.093750 +v -0.226562 0.916016 0.093750 +v 0.226562 0.919922 -0.269531 +v -0.226562 0.919922 -0.269531 +v 0.228516 0.691406 -0.621094 +v -0.228516 0.691406 -0.621094 +v 0.708984 0.429688 0.392578 +v -0.708984 0.429688 0.392578 +v 0.699219 0.531250 0.199219 +v -0.699219 0.531250 0.199219 +v 0.718750 0.658203 -0.033203 +v -0.718750 0.658203 -0.033203 +v 0.718750 0.646484 -0.279297 +v -0.718750 0.646484 -0.279297 +v 0.707031 0.453125 -0.457031 +v -0.707031 0.453125 -0.457031 +v 0.542969 0.578125 -0.529297 +v -0.542969 0.578125 -0.529297 +v 0.546875 0.806641 -0.273438 +v -0.546875 0.806641 -0.273438 +v 0.546875 0.808594 0.005859 +v -0.546875 0.808594 0.005859 +v 0.546875 0.632812 0.250000 +v -0.546875 0.632812 0.250000 +v 0.564453 0.496094 0.439453 +v -0.564453 0.496094 0.439453 +v 0.228516 0.710938 0.380859 +v -0.228516 0.710938 0.380859 +v 0.281250 0.591797 0.593750 +v -0.281250 0.591797 0.593750 +v 0.263672 0.527344 0.541016 +v -0.263672 0.527344 0.541016 +v 0.036458 0.479167 0.593750 +v -0.036458 0.479167 0.593750 +v 0.787109 0.302734 0.306641 +v -0.787109 0.302734 0.306641 +v 0.808594 0.380859 0.117187 +v -0.808594 0.380859 0.117187 +v 0.818359 0.457031 -0.060547 +v -0.818359 0.457031 -0.060547 +v 0.796875 0.437500 -0.279297 +v -0.796875 0.437500 -0.279297 +v 0.335938 -0.267578 -0.041016 +v -0.335938 -0.267578 -0.041016 +v 0.541016 -0.134766 -0.039062 +v -0.541016 -0.134766 -0.039062 +v 0.544922 -0.078125 0.275391 +v -0.544922 -0.078125 0.275391 +v 0.318359 -0.193359 0.378906 +v -0.318359 -0.193359 0.378906 +v 0.278646 -0.281250 0.247396 +v -0.278646 -0.281250 0.247396 +v 0.283854 -0.164062 0.479167 +v -0.283854 -0.164062 0.479167 +v 0.628906 0.152344 -0.500000 +v -0.628906 0.152344 -0.500000 +v 0.474609 0.210937 -0.625000 +v -0.474609 0.210937 -0.625000 +v 0.537109 -0.076172 -0.337891 +v -0.537109 -0.076172 -0.337891 +v 0.388672 -0.158203 -0.390625 +v -0.388672 -0.158203 -0.390625 +v 0.388021 -0.023438 -0.583333 +v -0.388021 -0.023438 -0.583333 +v 0.962891 0.414062 -0.263672 +v -0.962891 0.414062 -0.263672 +v 1.115234 0.458984 -0.353516 +v -1.115234 0.458984 -0.353516 +v 1.259766 0.388672 -0.410156 +v -1.259766 0.388672 -0.410156 +v 1.277344 0.185547 -0.416016 +v -1.277344 0.185547 -0.416016 +v 1.140625 -0.001953 -0.367188 +v -1.140625 -0.001953 -0.367188 +v 0.917969 -0.087891 -0.222656 +v -0.917969 -0.087891 -0.222656 +v 0.945312 -0.033203 -0.253906 +v -0.945312 -0.033203 -0.253906 +v 1.117188 0.033203 -0.380859 +v -1.117188 0.033203 -0.380859 +v 1.224609 0.177734 -0.425781 +v -1.224609 0.177734 -0.425781 +v 1.214844 0.333984 -0.419922 +v -1.214844 0.333984 -0.419922 +v 1.099609 0.388672 -0.369141 +v -1.099609 0.388672 -0.369141 +v 0.976562 0.355469 -0.289063 +v -0.976562 0.355469 -0.289063 +v 0.693359 -0.048828 -0.083984 +v -0.693359 -0.048828 -0.083984 +v 0.728516 -0.089844 -0.148438 +v -0.728516 -0.089844 -0.148438 +v 0.792969 0.126953 0.001953 +v -0.792969 0.126953 0.001953 +v 0.869141 0.345703 -0.216797 +v -0.869141 0.345703 -0.216797 +v 0.810547 -0.033203 -0.197266 +v -0.810547 -0.033203 -0.197266 +v 0.773438 0.003906 -0.226562 +v -0.773438 0.003906 -0.226562 +v 0.785156 0.058594 -0.251953 +v -0.785156 0.058594 -0.251953 +v 0.777344 0.126953 -0.236328 +v -0.777344 0.126953 -0.236328 +v 0.841797 0.226562 -0.240234 +v -0.841797 0.226562 -0.240234 +v 0.900391 0.298828 -0.246094 +v -0.900391 0.298828 -0.246094 +v 0.830078 0.285156 -0.167969 +v -0.830078 0.285156 -0.167969 +v 0.775391 0.140625 -0.128906 +v -0.775391 0.140625 -0.128906 +v 0.721354 0.005208 -0.143229 +v -0.721354 0.005208 -0.143229 +v 0.919922 0.267578 -0.304688 +v -0.919922 0.267578 -0.304688 +v 0.865234 0.205078 -0.294922 +v -0.865234 0.205078 -0.294922 +v 0.800781 0.132812 -0.296875 +v -0.800781 0.132812 -0.296875 +v 0.792969 0.087891 -0.296875 +v -0.792969 0.087891 -0.296875 +v 0.835938 0.048828 -0.296875 +v -0.835938 0.048828 -0.296875 +v 0.830078 -0.000000 -0.296875 +v -0.830078 -0.000000 -0.296875 +v 0.847656 -0.017578 -0.267578 +v -0.847656 -0.017578 -0.267578 +v 0.990234 0.316406 -0.351563 +v -0.990234 0.316406 -0.351563 +v 1.105469 0.343750 -0.423828 +v -1.105469 0.343750 -0.423828 +v 1.212891 0.298828 -0.464844 +v -1.212891 0.298828 -0.464844 +v 1.222656 0.167969 -0.466797 +v -1.222656 0.167969 -0.466797 +v 1.121094 0.044922 -0.429688 +v -1.121094 0.044922 -0.429688 +v 0.962891 -0.009766 -0.316406 +v -0.962891 -0.009766 -0.316406 +v 0.876953 0.066406 -0.326172 +v -0.876953 0.066406 -0.326172 +v 0.947266 0.117187 -0.345703 +v -0.947266 0.117187 -0.345703 +v 1.007812 0.179687 -0.369141 +v -1.007812 0.179687 -0.369141 +v 1.066406 0.226562 -0.384766 +v -1.066406 0.226562 -0.384766 +v 1.023438 0.281250 -0.380859 +v -1.023438 0.281250 -0.380859 +v 0.955078 0.232422 -0.347656 +v -0.955078 0.232422 -0.347656 +v 0.896484 0.171875 -0.330078 +v -0.896484 0.171875 -0.330078 +v 0.832031 0.113281 -0.322266 +v -0.832031 0.113281 -0.322266 +v 0.871094 0.011719 -0.310547 +v -0.871094 0.011719 -0.310547 +v 0.966797 0.042969 -0.347656 +v -0.966797 0.042969 -0.347656 +v 1.078125 0.099609 -0.414062 +v -1.078125 0.099609 -0.414062 +v 1.158203 0.181641 -0.437500 +v -1.158203 0.181641 -0.437500 +v 1.160156 0.267578 -0.439453 +v -1.160156 0.267578 -0.439453 +v 1.104167 0.315104 -0.429688 +v -1.104167 0.315104 -0.429688 +v 0.910156 -0.113281 -0.318359 +v -0.910156 -0.113281 -0.318359 +v 1.167969 -0.019531 -0.445312 +v -1.167969 -0.019531 -0.445312 +v 1.328125 0.181641 -0.470703 +v -1.328125 0.181641 -0.470703 +v 1.300781 0.398437 -0.472656 +v -1.300781 0.398437 -0.472656 +v 1.132812 0.472656 -0.441406 +v -1.132812 0.472656 -0.441406 +v 0.949219 0.425781 -0.353516 +v -0.949219 0.425781 -0.353516 +v 0.927734 0.152344 -0.421875 +v -0.927734 0.152344 -0.421875 +v 1.156250 0.218750 -0.513672 +v -1.156250 0.218750 -0.513672 +v 1.309896 0.273437 -0.526042 +v -1.309896 0.273437 -0.526042 +v 0.835938 0.345703 -0.314453 +v -0.835938 0.345703 -0.314453 +v 0.765625 0.128906 -0.394531 +v -0.765625 0.128906 -0.394531 +v 0.699219 -0.099609 -0.261719 +v -0.699219 -0.099609 -0.261719 +v 0.463867 0.198242 0.753418 +v 0.459961 0.137207 0.728027 +v 0.528320 0.169922 0.676270 +v 0.503906 0.242676 0.717773 +v -0.459961 0.137207 0.728027 +v -0.463867 0.198242 0.753418 +v -0.503906 0.242676 0.717773 +v -0.528320 0.169922 0.676270 +v 0.510742 0.086426 0.635254 +v 0.585938 0.148438 0.570312 +v 0.576172 0.242676 0.621094 +v -0.510742 0.086426 0.635254 +v -0.576172 0.242676 0.621094 +v -0.585938 0.148438 0.570312 +v 0.424805 0.067383 0.698730 +v 0.351562 0.021484 0.666992 +v 0.449219 0.015625 0.597656 +v -0.351562 0.021484 0.666992 +v -0.424805 0.067383 0.698730 +v -0.449219 0.015625 0.597656 +v 0.398926 0.136231 0.767090 +v 0.352051 0.093750 0.749512 +v -0.352051 0.093750 0.749512 +v -0.398926 0.136231 0.767090 +v 0.307617 0.136231 0.784668 +v 0.246582 0.137207 0.767090 +v 0.278809 0.067383 0.725586 +v -0.246582 0.137207 0.767090 +v -0.307617 0.136231 0.784668 +v -0.278809 0.067383 0.725586 +v 0.192383 0.086426 0.692383 +v 0.253906 0.015625 0.632812 +v -0.192383 0.086426 0.692383 +v -0.253906 0.015625 0.632812 +v 0.175781 0.169922 0.737793 +v 0.126953 0.242676 0.698242 +v 0.117188 0.148438 0.652344 +v -0.126953 0.242676 0.698242 +v -0.175781 0.169922 0.737793 +v -0.117188 0.148438 0.652344 +v 0.245605 0.198242 0.792969 +v 0.203125 0.242676 0.769531 +v -0.203125 0.242676 0.769531 +v -0.245605 0.198242 0.792969 +v 0.245605 0.289551 0.792969 +v 0.246582 0.350586 0.767090 +v 0.175781 0.315430 0.737793 +v -0.246582 0.350586 0.767090 +v -0.245605 0.289551 0.792969 +v -0.175781 0.315430 0.737793 +v 0.192383 0.401367 0.692383 +v 0.117188 0.339844 0.652344 +v -0.192383 0.401367 0.692383 +v -0.117188 0.339844 0.652344 +v 0.278809 0.418945 0.725586 +v 0.351562 0.466797 0.666992 +v 0.253906 0.476563 0.632812 +v -0.351562 0.466797 0.666992 +v -0.278809 0.418945 0.725586 +v -0.253906 0.476563 0.632812 +v 0.307617 0.354981 0.784668 +v 0.352051 0.394531 0.749512 +v -0.352051 0.394531 0.749512 +v -0.307617 0.354981 0.784668 +v 0.398926 0.354981 0.767090 +v 0.459961 0.350586 0.728027 +v 0.424805 0.418945 0.698730 +v -0.459961 0.350586 0.728027 +v -0.398926 0.354981 0.767090 +v -0.424805 0.418945 0.698730 +v 0.510742 0.401367 0.635254 +v 0.449219 0.476563 0.597656 +v -0.510742 0.401367 0.635254 +v -0.449219 0.476563 0.597656 +v 0.528320 0.315430 0.676270 +v 0.585938 0.339844 0.570312 +v -0.528320 0.315430 0.676270 +v -0.585938 0.339844 0.570312 +v 0.463867 0.289551 0.753418 +v -0.463867 0.289551 0.753418 +v 0.434082 0.325195 0.774902 +v 0.450846 0.284668 0.779622 +v 0.464844 0.243164 0.767578 +v -0.434082 0.325195 0.774902 +v -0.464844 0.243164 0.767578 +v -0.450846 0.284668 0.779622 +v 0.352539 0.358398 0.792480 +v 0.394043 0.344564 0.793457 +v -0.352539 0.358398 0.792480 +v -0.394043 0.344564 0.793457 +v 0.275391 0.325195 0.806641 +v 0.312663 0.344564 0.810872 +v -0.275391 0.325195 0.806641 +v -0.312663 0.344564 0.810872 +v 0.243164 0.243164 0.808594 +v 0.256348 0.284668 0.818034 +v -0.243164 0.243164 0.808594 +v -0.256348 0.284668 0.818034 +v 0.275391 0.166016 0.806641 +v 0.256348 0.203288 0.818034 +v -0.275391 0.166016 0.806641 +v -0.256348 0.203288 0.818034 +v 0.352539 0.133789 0.792480 +v 0.312663 0.146973 0.810872 +v -0.352539 0.133789 0.792480 +v -0.312663 0.146973 0.810872 +v 0.434082 0.166016 0.774902 +v 0.394043 0.146973 0.793457 +v -0.434082 0.166016 0.774902 +v -0.394043 0.146973 0.793457 +v 0.450846 0.203288 0.779622 +v -0.450846 0.203288 0.779622 +v 0.401042 0.195964 0.802083 +v 0.419271 0.242839 0.797526 +v -0.401042 0.195964 0.802083 +v -0.419271 0.242839 0.797526 +v 0.352214 0.175781 0.813802 +v -0.352214 0.175781 0.813802 +v 0.305339 0.195964 0.822266 +v -0.305339 0.195964 0.822266 +v 0.285156 0.242839 0.823568 +v -0.285156 0.242839 0.823568 +v 0.305339 0.292318 0.822266 +v -0.305339 0.292318 0.822266 +v 0.352214 0.313151 0.813802 +v -0.352214 0.313151 0.813802 +v 0.401042 0.292318 0.802083 +v -0.401042 0.292318 0.802083 +v 0.164062 -0.948730 0.593750 +v 0.076660 -0.936035 0.634766 +v 0.000000 -0.960937 0.605469 +v 0.087891 -0.969727 0.560547 +v -0.076660 -0.936035 0.634766 +v -0.164062 -0.948730 0.593750 +v -0.087891 -0.969727 0.560547 +v 0.271973 -0.922852 0.582520 +v 0.192383 -0.918457 0.629883 +v 0.246094 -0.949219 0.535645 +v -0.192383 -0.918457 0.629883 +v -0.271973 -0.922852 0.582520 +v -0.246094 -0.949219 0.535645 +v 0.310059 -0.845215 0.599121 +v 0.245117 -0.862305 0.642578 +v 0.330566 -0.898437 0.525391 +v -0.245117 -0.862305 0.642578 +v -0.310059 -0.845215 0.599121 +v -0.330566 -0.898437 0.525391 +v 0.297852 -0.686523 0.626465 +v 0.252441 -0.758301 0.667481 +v 0.335449 -0.765625 0.545898 +v -0.252441 -0.758301 0.667481 +v -0.297852 -0.686523 0.626465 +v -0.335449 -0.765625 0.545898 +v 0.261230 -0.457519 0.638184 +v 0.226562 -0.572266 0.688477 +v 0.308105 -0.549805 0.564941 +v -0.226562 -0.572266 0.688477 +v -0.261230 -0.457519 0.638184 +v -0.308105 -0.549805 0.564941 +v 0.312988 -0.148926 0.563477 +v 0.412109 -0.087402 0.609863 +v 0.272461 -0.069336 0.715332 +v 0.200195 -0.150879 0.669922 +v -0.412109 -0.087402 0.609863 +v -0.312988 -0.148926 0.563477 +v -0.200195 -0.150879 0.669922 +v -0.272461 -0.069336 0.715332 +v 0.529785 -0.072754 0.537598 +v 0.618164 0.016113 0.577148 +v 0.501953 -0.003418 0.639160 +v -0.618164 0.016113 0.577148 +v -0.529785 -0.072754 0.537598 +v -0.501953 -0.003418 0.639160 +v 0.717773 0.067383 0.495117 +v 0.761230 0.183106 0.543457 +v 0.665039 0.119141 0.604004 +v -0.761230 0.183106 0.543457 +v -0.717773 0.067383 0.495117 +v -0.665039 0.119141 0.604004 +v 0.822754 0.288574 0.520508 +v 0.785156 0.379883 0.618652 +v 0.735352 0.283203 0.617676 +v -0.785156 0.379883 0.618652 +v -0.822754 0.288574 0.520508 +v -0.735352 0.283203 0.617676 +v 0.770508 0.445801 0.603027 +v 0.682617 0.458984 0.677734 +v 0.711914 0.395020 0.676758 +v -0.682617 0.458984 0.677734 +v -0.770508 0.445801 0.603027 +v -0.711914 0.395020 0.676758 +v 0.594238 0.530273 0.652832 +v 0.475586 0.574219 0.739258 +v 0.561523 0.479004 0.739746 +v -0.475586 0.574219 0.739258 +v -0.594238 0.530273 0.652832 +v -0.561523 0.479004 0.739746 +v 0.402832 0.661621 0.707031 +v 0.317871 0.679688 0.782227 +v 0.379395 0.587402 0.792480 +v -0.317871 0.679688 0.782227 +v -0.402832 0.661621 0.707031 +v -0.379395 0.587402 0.792480 +v 0.244141 0.716797 0.740723 +v 0.184570 0.645996 0.801269 +v 0.259277 0.623047 0.819824 +v -0.184570 0.645996 0.801269 +v -0.244141 0.716797 0.740723 +v -0.259277 0.623047 0.819824 +v 0.120117 0.589844 0.748047 +v 0.083984 0.478027 0.795898 +v 0.153809 0.523438 0.825684 +v -0.083984 0.478027 0.795898 +v -0.120117 0.589844 0.748047 +v -0.153809 0.523438 0.825684 +v 0.036621 0.448731 0.739258 +v 0.000000 0.408203 0.785156 +v 0.060059 0.395508 0.813477 +v -0.036621 0.448731 0.739258 +v -0.060059 0.395508 0.813477 +v 0.204590 0.446289 0.777344 +v 0.226562 0.529785 0.803223 +v 0.135742 0.425293 0.806152 +v -0.226562 0.529785 0.803223 +v -0.204590 0.446289 0.777344 +v -0.135742 0.425293 0.806152 +v 0.287109 0.486328 0.763672 +v 0.322754 0.548340 0.787109 +v -0.322754 0.548340 0.787109 +v -0.287109 0.486328 0.763672 +v 0.445801 0.488281 0.753906 +v 0.378418 0.468750 0.745605 +v -0.445801 0.488281 0.753906 +v -0.378418 0.468750 0.745605 +v 0.624023 0.399414 0.697754 +v 0.518555 0.411621 0.705566 +v -0.624023 0.399414 0.697754 +v -0.518555 0.411621 0.705566 +v 0.683594 0.325684 0.654297 +v 0.626953 0.341309 0.665039 +v -0.683594 0.325684 0.654297 +v -0.626953 0.341309 0.665039 +v 0.662598 0.195801 0.631348 +v 0.639160 0.248047 0.650391 +v -0.662598 0.195801 0.631348 +v -0.639160 0.248047 0.650391 +v 0.548828 0.066406 0.650391 +v 0.568848 0.128418 0.659180 +v -0.548828 0.066406 0.650391 +v -0.568848 0.128418 0.659180 +v 0.379883 -0.004883 0.693848 +v 0.441895 0.040039 0.687988 +v -0.379883 -0.004883 0.693848 +v -0.441895 0.040039 0.687988 +v 0.181641 0.006836 0.761230 +v 0.287109 0.042481 0.729980 +v -0.181641 0.006836 0.761230 +v -0.287109 0.042481 0.729980 +v 0.171387 0.101074 0.752930 +v 0.090332 0.096191 0.746094 +v 0.076172 -0.023926 0.763184 +v -0.090332 0.096191 0.746094 +v -0.171387 0.101074 0.752930 +v -0.076172 -0.023926 0.763184 +v 0.071777 0.325195 0.791016 +v 0.139160 0.360352 0.778809 +v -0.139160 0.360352 0.778809 +v -0.071777 0.325195 0.791016 +v 0.000000 0.275391 0.785156 +v 0.067871 0.210938 0.762695 +v 0.118652 0.260742 0.768066 +v -0.118652 0.260742 0.768066 +v -0.067871 0.210938 0.762695 +v 0.133789 0.171387 0.755859 +v 0.000000 0.140625 0.748047 +v -0.133789 0.171387 0.755859 +v 0.036621 -0.891113 0.684082 +v 0.000000 -0.915039 0.664063 +v 0.106934 -0.903809 0.665039 +v -0.036621 -0.891113 0.684082 +v -0.106934 -0.903809 0.665039 +v 0.101074 -0.865234 0.693848 +v 0.169434 -0.865723 0.674316 +v -0.101074 -0.865234 0.693848 +v -0.169434 -0.865723 0.674316 +v 0.127930 -0.789062 0.712891 +v 0.185547 -0.773437 0.697266 +v -0.127930 -0.789062 0.712891 +v -0.185547 -0.773437 0.697266 +v 0.147949 -0.458496 0.727539 +v 0.102051 -0.566894 0.736816 +v 0.179199 -0.673340 0.711914 +v -0.102051 -0.566894 0.736816 +v -0.147949 -0.458496 0.727539 +v -0.179199 -0.673340 0.711914 +v 0.117188 -0.712891 0.727051 +v -0.117188 -0.712891 0.727051 +v 0.042969 -0.324219 0.748047 +v 0.000000 -0.381836 0.746094 +v 0.041992 -0.458008 0.747070 +v 0.085938 -0.371094 0.741699 +v -0.042969 -0.324219 0.748047 +v -0.085938 -0.371094 0.741699 +v -0.041992 -0.458008 0.747070 +v 0.000000 -0.563477 0.742188 +v 0.055664 -0.661133 0.736328 +v -0.055664 -0.661133 0.736328 +v 0.054199 -0.736816 0.732910 +v 0.000000 -0.717773 0.734375 +v -0.054199 -0.736816 0.732910 +v 0.121094 -0.222656 0.770508 +v 0.109863 -0.249512 0.787109 +v 0.083496 -0.279785 0.763184 +v 0.114746 -0.259277 0.747070 +v -0.109863 -0.249512 0.787109 +v -0.121094 -0.222656 0.770508 +v -0.114746 -0.259277 0.747070 +v -0.083496 -0.279785 0.763184 +v 0.097656 -0.150391 0.763184 +v 0.117676 -0.182129 0.787598 +v 0.118164 -0.184570 0.753418 +v -0.117676 -0.182129 0.787598 +v -0.097656 -0.150391 0.763184 +v -0.118164 -0.184570 0.753418 +v 0.027832 -0.141113 0.763184 +v 0.070801 -0.133789 0.780273 +v 0.055176 -0.127930 0.750488 +v -0.070801 -0.133789 0.780273 +v -0.027832 -0.141113 0.763184 +v -0.055176 -0.127930 0.750488 +v 0.000000 -0.176758 0.770508 +v 0.017578 -0.160156 0.787598 +v 0.000000 -0.165039 0.756836 +v -0.017578 -0.160156 0.787598 +v 0.045410 -0.294922 0.779297 +v 0.000000 -0.313477 0.761719 +v -0.045410 -0.294922 0.779297 +v 0.079590 -0.262695 0.795410 +v 0.041992 -0.266113 0.804688 +v 0.000000 -0.293945 0.792969 +v -0.079590 -0.262695 0.795410 +v -0.041992 -0.266113 0.804688 +v 0.000000 -0.180664 0.808594 +v 0.032715 -0.175293 0.816406 +v 0.044922 -0.145019 0.798828 +v -0.032715 -0.175293 0.816406 +v -0.044922 -0.145019 0.798828 +v 0.068848 -0.157227 0.810547 +v 0.096680 -0.153809 0.798828 +v -0.068848 -0.157227 0.810547 +v -0.096680 -0.153809 0.798828 +v 0.094238 -0.187988 0.816406 +v 0.114258 -0.220703 0.808105 +v -0.094238 -0.187988 0.816406 +v -0.114258 -0.220703 0.808105 +v 0.084473 -0.240723 0.812988 +v -0.084473 -0.240723 0.812988 +v 0.054688 -0.213867 0.823242 +v -0.054688 -0.213867 0.823242 +v 0.000000 -0.244141 0.816406 +v 0.103027 -0.128906 0.766113 +v 0.000000 -0.066406 0.745117 +v -0.103027 -0.128906 0.766113 +v 0.139160 -0.229004 0.735840 +v 0.149902 -0.179687 0.734375 +v -0.139160 -0.229004 0.735840 +v -0.149902 -0.179687 0.734375 +v 0.135742 -0.310547 0.727539 +v 0.172852 -0.275391 0.695801 +v -0.135742 -0.310547 0.727539 +v -0.172852 -0.275391 0.695801 +v 0.192383 -0.376953 0.696777 +v -0.192383 -0.376953 0.696777 +v 0.266602 -0.368164 0.562988 +v 0.221680 -0.320312 0.633789 +v -0.221680 -0.320312 0.633789 +v -0.266602 -0.368164 0.562988 +v 0.232910 -0.277344 0.561035 +v 0.197266 -0.241699 0.639648 +v -0.197266 -0.241699 0.639648 +v -0.232910 -0.277344 0.561035 +v 0.208008 -0.210449 0.574707 +v -0.208008 -0.210449 0.574707 +v 0.000000 -0.759766 0.727539 +v 0.047852 -0.756836 0.716309 +v 0.089355 -0.747559 0.727539 +v -0.089355 -0.747559 0.727539 +v -0.047852 -0.756836 0.716309 +v 0.096191 -0.780762 0.710938 +v 0.098633 -0.821777 0.710938 +v -0.098633 -0.821777 0.710938 +v -0.096191 -0.780762 0.710938 +v 0.072754 -0.844238 0.691895 +v 0.054199 -0.870117 0.693359 +v -0.054199 -0.870117 0.693359 +v -0.072754 -0.844238 0.691895 +v 0.024414 -0.871094 0.681152 +v 0.000000 -0.880859 0.688477 +v -0.024414 -0.871094 0.681152 +v 0.046875 -0.854980 0.662109 +v 0.026367 -0.846191 0.643066 +v 0.000000 -0.865234 0.660156 +v -0.046875 -0.854980 0.662109 +v -0.026367 -0.846191 0.643066 +v 0.087891 -0.812988 0.676270 +v 0.072266 -0.826660 0.646810 +v -0.087891 -0.812988 0.676270 +v -0.072266 -0.826660 0.646810 +v 0.082031 -0.758789 0.691895 +v 0.089844 -0.787109 0.659017 +v -0.082031 -0.758789 0.691895 +v -0.089844 -0.787109 0.659017 +v 0.000000 -0.769531 0.689453 +v 0.043945 -0.775879 0.664551 +v -0.043945 -0.775879 0.664551 +v 0.000000 -0.815430 0.645508 +v 0.063477 -0.804199 0.647298 +v -0.063477 -0.804199 0.647298 +v 0.177734 0.150879 0.762695 +v 0.178223 0.187500 0.770996 +v 0.155273 0.217285 0.769043 +v -0.177734 0.150879 0.762695 +v -0.155273 0.217285 0.769043 +v -0.178223 0.187500 0.770996 +v 0.172363 0.258301 0.775391 +v 0.156738 0.301758 0.773438 +v -0.156738 0.301758 0.773438 +v -0.172363 0.258301 0.775391 +v 0.191406 0.337891 0.776855 +v 0.192383 0.389160 0.775391 +v -0.192383 0.389160 0.775391 +v -0.191406 0.337891 0.776855 +v 0.229980 0.099609 0.756836 +v 0.206055 0.132813 0.770508 +v -0.229980 0.099609 0.756836 +v -0.206055 0.132813 0.770508 +v 0.368652 0.052246 0.724121 +v 0.300293 0.084473 0.754395 +v -0.368652 0.052246 0.724121 +v -0.300293 0.084473 0.754395 +v 0.485352 0.090332 0.694824 +v 0.425781 0.079102 0.723145 +v -0.485352 0.090332 0.694824 +v -0.425781 0.079102 0.723145 +v 0.588379 0.190430 0.668457 +v 0.528320 0.147461 0.692871 +v -0.588379 0.190430 0.668457 +v -0.528320 0.147461 0.692871 +v 0.607910 0.289063 0.668945 +v 0.584473 0.242676 0.677734 +v -0.607910 0.289063 0.668945 +v -0.584473 0.242676 0.677734 +v 0.566406 0.361328 0.686523 +v 0.576172 0.320801 0.685059 +v -0.566406 0.361328 0.686523 +v -0.576172 0.320801 0.685059 +v 0.433594 0.415527 0.738281 +v 0.492676 0.377441 0.726562 +v -0.433594 0.415527 0.738281 +v -0.492676 0.377441 0.726562 +v 0.334961 0.447754 0.751465 +v 0.378418 0.417481 0.759277 +v -0.334961 0.447754 0.751465 +v -0.378418 0.417481 0.759277 +v 0.261230 0.439941 0.765137 +v 0.303223 0.428711 0.762695 +v -0.261230 0.439941 0.765137 +v -0.303223 0.428711 0.762695 +v 0.239746 0.401367 0.773926 +v -0.239746 0.401367 0.773926 +v 0.225098 0.363770 0.769531 +v 0.257812 0.378906 0.761719 +v 0.277832 0.405762 0.767578 +v -0.225098 0.363770 0.769531 +v -0.277832 0.405762 0.767578 +v -0.257812 0.378906 0.761719 +v 0.308594 0.402344 0.757812 +v 0.338867 0.414063 0.756836 +v -0.338867 0.414063 0.756836 +v -0.308594 0.402344 0.757812 +v 0.375000 0.398438 0.750000 +v 0.423828 0.391113 0.751465 +v -0.423828 0.391113 0.751465 +v -0.375000 0.398438 0.750000 +v 0.472656 0.363281 0.714844 +v 0.533691 0.342773 0.695801 +v -0.533691 0.342773 0.695801 +v -0.472656 0.363281 0.714844 +v 0.542969 0.308594 0.675781 +v 0.566406 0.282227 0.680176 +v -0.566406 0.282227 0.680176 +v -0.542969 0.308594 0.675781 +v 0.550781 0.246094 0.671875 +v 0.551758 0.201660 0.680664 +v -0.551758 0.201660 0.680664 +v -0.550781 0.246094 0.671875 +v 0.503906 0.164063 0.687500 +v 0.468750 0.116699 0.709473 +v -0.468750 0.116699 0.709473 +v -0.503906 0.164063 0.687500 +v 0.417969 0.101563 0.714844 +v 0.369141 0.083984 0.735840 +v -0.369141 0.083984 0.735840 +v -0.417969 0.101563 0.714844 +v 0.308594 0.105469 0.742188 +v 0.247070 0.117676 0.764160 +v -0.247070 0.117676 0.764160 +v -0.308594 0.105469 0.742188 +v 0.222656 0.148438 0.753906 +v 0.198730 0.165527 0.763184 +v -0.198730 0.165527 0.763184 +v -0.222656 0.148438 0.753906 +v 0.191406 0.296387 0.769043 +v 0.214844 0.328125 0.757812 +v -0.191406 0.296387 0.769043 +v -0.214844 0.328125 0.757812 +v 0.185547 0.224609 0.765625 +v 0.195312 0.261719 0.753906 +v -0.185547 0.224609 0.765625 +v -0.195312 0.261719 0.753906 +v 0.199219 0.199219 0.750000 +v -0.199219 0.199219 0.750000 +v 0.086426 0.496094 0.679687 +v 0.047201 0.448405 0.620117 +v 0.000000 0.432617 0.673828 +v -0.086426 0.496094 0.679687 +v -0.047201 0.448405 0.620117 +v 0.183594 0.668457 0.683594 +v 0.179199 0.575195 0.625977 +v -0.183594 0.668457 0.683594 +v -0.179199 0.575195 0.625977 +v 0.329102 0.700684 0.661621 +v 0.266113 0.662598 0.620117 +v -0.329102 0.700684 0.661621 +v -0.266113 0.662598 0.620117 +v 0.494141 0.582520 0.618652 +v 0.377441 0.621094 0.596191 +v -0.494141 0.582520 0.618652 +v -0.377441 0.621094 0.596191 +v 0.686035 0.476074 0.562500 +v 0.580078 0.506836 0.519043 +v -0.686035 0.476074 0.562500 +v -0.580078 0.506836 0.519043 +v 0.808105 0.391602 0.516602 +v 0.736816 0.433105 0.472168 +v -0.808105 0.391602 0.516602 +v -0.736816 0.433105 0.472168 +v 0.781250 0.166992 0.433105 +v 0.792969 0.290039 0.402832 +v -0.781250 0.166992 0.433105 +v -0.792969 0.290039 0.402832 +v 0.617676 -0.009766 0.471191 +v 0.706055 0.080078 0.365234 +v -0.617676 -0.009766 0.471191 +v -0.706055 0.080078 0.365234 +v 0.430664 -0.112793 0.500977 +v 0.527832 -0.060059 0.411621 +v -0.430664 -0.112793 0.500977 +v -0.527832 -0.060059 0.411621 +v 0.000000 -0.514648 0.302734 +v 0.088379 -0.459473 0.266602 +v 0.144531 -0.476562 0.318848 +v 0.066895 -0.569824 0.333008 +v -0.088379 -0.459473 0.266602 +v -0.066895 -0.569824 0.333008 +v -0.144531 -0.476562 0.318848 +v 0.000000 -0.677734 0.339844 +v 0.133301 -0.647949 0.362305 +v 0.070801 -0.775391 0.365234 +v -0.070801 -0.775391 0.365234 +v -0.133301 -0.647949 0.362305 +v 0.000000 -0.880859 0.402344 +v 0.152832 -0.851562 0.400879 +v 0.081543 -0.940430 0.452148 +v -0.081543 -0.940430 0.452148 +v -0.152832 -0.851562 0.400879 +v 0.000000 -0.974609 0.513672 +v 0.169922 -0.956543 0.494629 +v -0.169922 -0.956543 0.494629 +v 0.243164 -0.908691 0.427734 +v 0.308594 -0.916992 0.464844 +v -0.308594 -0.916992 0.464844 +v -0.243164 -0.908691 0.427734 +v 0.293945 -0.830566 0.409180 +v 0.324707 -0.791504 0.460449 +v -0.324707 -0.791504 0.460449 +v -0.293945 -0.830566 0.409180 +v 0.263672 -0.634277 0.404297 +v 0.300781 -0.597656 0.478516 +v -0.300781 -0.597656 0.478516 +v -0.263672 -0.634277 0.404297 +v 0.215332 -0.731934 0.380371 +v 0.193359 -0.529297 0.369629 +v -0.215332 -0.731934 0.380371 +v -0.193359 -0.529297 0.369629 +v 0.242188 -0.449707 0.408691 +v 0.205566 -0.394043 0.333008 +v -0.242188 -0.449707 0.408691 +v -0.205566 -0.394043 0.333008 +v 0.272461 -0.407715 0.487793 +v -0.272461 -0.407715 0.487793 +v 0.240723 -0.301758 0.494141 +v 0.244629 -0.242187 0.444824 +v 0.222168 -0.238281 0.511719 +v -0.244629 -0.242187 0.444824 +v -0.240723 -0.301758 0.494141 +v -0.222168 -0.238281 0.511719 +v 0.229980 -0.334473 0.410156 +v -0.229980 -0.334473 0.410156 +v 0.227702 -0.192871 0.492350 +v 0.234863 -0.179199 0.524902 +v -0.234863 -0.179199 0.524902 +v -0.227702 -0.192871 0.492350 +v 0.311198 -0.144531 0.490885 +v -0.311198 -0.144531 0.490885 +v 0.176270 0.087891 -0.732422 +v 0.309408 -0.042969 -0.615560 +v 0.168457 -0.164551 -0.585938 +v 0.000000 -0.058594 -0.712891 +v -0.309408 -0.042969 -0.615560 +v -0.176270 0.087891 -0.732422 +v -0.168457 -0.164551 -0.585938 +v 0.297363 -0.219727 -0.413086 +v 0.145996 -0.335449 -0.285156 +v 0.000000 -0.274414 -0.484375 +v -0.297363 -0.219727 -0.413086 +v -0.145996 -0.335449 -0.285156 +v 0.242676 -0.339355 -0.052246 +v 0.108887 -0.418945 0.126953 +v 0.000000 -0.404297 -0.074219 +v -0.242676 -0.339355 -0.052246 +v -0.108887 -0.418945 0.126953 +v 0.174805 -0.400391 0.239746 +v 0.000000 -0.455078 0.228516 +v -0.174805 -0.400391 0.239746 +v 0.229818 -0.328125 0.288900 +v -0.229818 -0.328125 0.288900 +v 0.655273 -0.009277 0.247070 +v 0.779785 0.100586 0.088867 +v 0.788086 0.197266 0.241211 +v -0.655273 -0.009277 0.247070 +v -0.788086 0.197266 0.241211 +v -0.779785 0.100586 0.088867 +v 0.222168 0.493164 -0.734375 +v 0.367676 0.246094 -0.688477 +v 0.000000 0.298828 -0.800781 +v -0.367676 0.246094 -0.688477 +v -0.222168 0.493164 -0.734375 +v 0.227051 0.844238 0.249512 +v 0.419922 0.876465 0.065918 +v 0.226562 0.937500 -0.081055 +v 0.000000 0.928711 0.099609 +v -0.419922 0.876465 0.065918 +v -0.227051 0.844238 0.249512 +v -0.226562 0.937500 -0.081055 +v 0.419922 0.880859 -0.249023 +v 0.227051 0.844238 -0.455078 +v 0.000000 0.930664 -0.291016 +v -0.419922 0.880859 -0.249023 +v -0.227051 0.844238 -0.455078 +v 0.421387 0.643555 -0.559082 +v 0.000000 0.710937 -0.660156 +v -0.421387 0.643555 -0.559082 +v 0.754883 0.386230 0.374023 +v 0.691895 0.455078 0.302246 +v 0.646484 0.458008 0.401367 +v -0.754883 0.386230 0.374023 +v -0.646484 0.458008 0.401367 +v -0.691895 0.455078 0.302246 +v 0.757812 0.470215 0.194336 +v 0.713867 0.613770 0.086426 +v 0.629883 0.580078 0.196289 +v -0.757812 0.470215 0.194336 +v -0.629883 0.580078 0.196289 +v -0.713867 0.613770 0.086426 +v 0.782715 0.573730 -0.021484 +v 0.718750 0.667969 -0.156250 +v 0.636719 0.729980 -0.041992 +v -0.782715 0.573730 -0.021484 +v -0.636719 0.729980 -0.041992 +v -0.718750 0.667969 -0.156250 +v 0.777344 0.560059 -0.258789 +v 0.715820 0.579590 -0.385254 +v 0.636719 0.720703 -0.298340 +v -0.777344 0.560059 -0.258789 +v -0.636719 0.720703 -0.298340 +v -0.715820 0.579590 -0.385254 +v 0.681641 0.299805 -0.495117 +v 0.626953 0.509766 -0.504395 +v 0.768555 0.423828 -0.383301 +v -0.626953 0.509766 -0.504395 +v -0.681641 0.299805 -0.495117 +v -0.768555 0.423828 -0.383301 +v 0.545898 0.732910 -0.407715 +v 0.523926 0.388672 -0.610840 +v -0.545898 0.732910 -0.407715 +v -0.523926 0.388672 -0.610840 +v 0.546875 0.823730 -0.133301 +v -0.546875 0.823730 -0.133301 +v 0.546875 0.749023 0.136230 +v -0.546875 0.749023 0.136230 +v 0.422363 0.679688 0.323730 +v 0.551270 0.526367 0.350098 +v -0.551270 0.526367 0.350098 +v -0.422363 0.679688 0.323730 +v 0.443359 0.525391 0.491211 +v -0.443359 0.525391 0.491211 +v 0.238281 0.583008 0.480469 +v 0.000000 0.722656 0.405273 +v -0.238281 0.583008 0.480469 +v 0.284668 0.533691 0.574707 +v -0.284668 0.533691 0.574707 +v 0.102376 0.509440 0.578613 +v -0.102376 0.509440 0.578613 +v 0.000000 0.483724 0.589844 +v 0.793457 0.331055 0.203613 +v -0.793457 0.331055 0.203613 +v 0.828125 0.265625 0.031738 +v 0.820801 0.430176 0.033691 +v -0.820801 0.430176 0.033691 +v -0.828125 0.265625 0.031738 +v 0.832031 0.347656 -0.119629 +v 0.808105 0.459961 -0.165039 +v -0.808105 0.459961 -0.165039 +v -0.832031 0.347656 -0.119629 +v 0.806641 0.344238 -0.308594 +v -0.806641 0.344238 -0.308594 +v 0.362793 -0.233398 -0.227051 +v 0.428223 -0.192383 -0.035645 +v 0.307943 -0.277832 0.129720 +v -0.362793 -0.233398 -0.227051 +v -0.307943 -0.277832 0.129720 +v -0.428223 -0.192383 -0.035645 +v 0.556641 -0.107910 0.113770 +v 0.525391 -0.132813 -0.187988 +v 0.640625 -0.088867 -0.054199 +v -0.556641 -0.107910 0.113770 +v -0.640625 -0.088867 -0.054199 +v -0.525391 -0.132813 -0.187988 +v 0.426758 -0.134277 0.317871 +v -0.426758 -0.134277 0.317871 +v 0.312663 -0.169434 0.448893 +v 0.305501 -0.231934 0.301107 +v -0.312663 -0.169434 0.448893 +v -0.305501 -0.231934 0.301107 +v 0.702148 0.134766 -0.440430 +v 0.572754 0.022949 -0.453613 +v 0.551270 0.178711 -0.564453 +v -0.572754 0.022949 -0.453613 +v -0.702148 0.134766 -0.440430 +v -0.551270 0.178711 -0.564453 +v 0.420736 0.066406 -0.604818 +v -0.420736 0.066406 -0.604818 +v 0.459961 -0.101563 -0.371582 +v 0.617676 -0.077148 -0.298340 +v -0.459961 -0.101563 -0.371582 +v -0.617676 -0.077148 -0.298340 +v 0.401204 -0.076660 -0.514974 +v -0.401204 -0.076660 -0.514974 +v 0.956543 0.430664 -0.291016 +v 0.911133 0.381348 -0.233398 +v 0.969238 0.385742 -0.265137 +v 1.029297 0.440918 -0.304688 +v -0.911133 0.381348 -0.233398 +v -0.956543 0.430664 -0.291016 +v -1.029297 0.440918 -0.304688 +v -0.969238 0.385742 -0.265137 +v 1.104492 0.424805 -0.350586 +v 1.199219 0.448242 -0.394043 +v 1.126465 0.479004 -0.382324 +v -1.104492 0.424805 -0.350586 +v -1.126465 0.479004 -0.382324 +v -1.199219 0.448242 -0.394043 +v 1.231934 0.362305 -0.406738 +v 1.288574 0.295898 -0.413574 +v 1.286621 0.403809 -0.431641 +v -1.231934 0.362305 -0.406738 +v -1.286621 0.403809 -0.431641 +v -1.288574 0.295898 -0.413574 +v 1.244629 0.182617 -0.413574 +v 1.227539 0.079102 -0.404785 +v 1.309570 0.185547 -0.434570 +v -1.244629 0.182617 -0.413574 +v -1.309570 0.185547 -0.434570 +v -1.227539 0.079102 -0.404785 +v 1.125000 0.017578 -0.364746 +v 1.032227 -0.057617 -0.305664 +v 1.157227 -0.017090 -0.392578 +v -1.125000 0.017578 -0.364746 +v -1.157227 -0.017090 -0.392578 +v -1.032227 -0.057617 -0.305664 +v 0.930664 -0.057617 -0.228516 +v 0.812012 -0.097168 -0.157227 +v 0.910156 -0.110840 -0.248535 +v -0.930664 -0.057617 -0.228516 +v -0.910156 -0.110840 -0.248535 +v -0.812012 -0.097168 -0.157227 +v 1.033203 -0.009766 -0.326660 +v 0.957520 -0.016602 -0.287109 +v 0.866699 -0.040039 -0.198730 +v -0.957520 -0.016602 -0.287109 +v -1.033203 -0.009766 -0.326660 +v -0.866699 -0.040039 -0.198730 +v 1.185059 0.095703 -0.414551 +v 1.116211 0.042969 -0.405762 +v -1.116211 0.042969 -0.405762 +v -1.185059 0.095703 -0.414551 +v 1.234863 0.262695 -0.424316 +v 1.217285 0.172363 -0.445801 +v -1.217285 0.172363 -0.445801 +v -1.234863 0.262695 -0.424316 +v 1.168457 0.379883 -0.404297 +v 1.208496 0.310547 -0.441895 +v -1.208496 0.310547 -0.441895 +v -1.168457 0.379883 -0.404297 +v 1.028809 0.375488 -0.326660 +v 1.100098 0.358887 -0.397461 +v -1.100098 0.358887 -0.397461 +v -1.028809 0.375488 -0.326660 +v 0.936035 0.329590 -0.260742 +v 0.983887 0.330078 -0.322266 +v -0.936035 0.329590 -0.260742 +v -0.983887 0.330078 -0.322266 +v 0.683594 -0.071777 -0.142090 +v 0.715007 -0.016764 -0.117350 +v 0.736816 0.007812 -0.020508 +v -0.683594 -0.071777 -0.142090 +v -0.736816 0.007812 -0.020508 +v -0.715007 -0.016764 -0.117350 +v 0.698730 -0.113770 -0.174805 +v 0.771484 -0.054199 -0.162598 +v -0.698730 -0.113770 -0.174805 +v -0.771484 -0.054199 -0.162598 +v 0.788574 0.146973 -0.061035 +v -0.788574 0.146973 -0.061035 +v 0.840820 0.312012 -0.199707 +v 0.883789 0.323242 -0.223145 +v 0.854004 0.356445 -0.242188 +v -0.883789 0.323242 -0.223145 +v -0.840820 0.312012 -0.199707 +v -0.854004 0.356445 -0.242188 +v 0.838379 -0.022461 -0.237305 +v 0.778809 -0.017090 -0.217285 +v -0.838379 -0.022461 -0.237305 +v -0.778809 -0.017090 -0.217285 +v 0.780273 0.029297 -0.234863 +v 0.733073 0.006185 -0.182292 +v 0.814941 0.000977 -0.267578 +v -0.733073 0.006185 -0.182292 +v -0.780273 0.029297 -0.234863 +v -0.814941 0.000977 -0.267578 +v 0.759766 0.079590 -0.237305 +v 0.821289 0.052246 -0.273926 +v 0.789062 0.081543 -0.273926 +v -0.759766 0.079590 -0.237305 +v -0.789062 0.081543 -0.273926 +v -0.821289 0.052246 -0.273926 +v 0.812988 0.182129 -0.240234 +v 0.767090 0.127441 -0.190918 +v 0.792969 0.131348 -0.270020 +v -0.767090 0.127441 -0.190918 +v -0.812988 0.182129 -0.240234 +v -0.792969 0.131348 -0.270020 +v 0.828125 0.250977 -0.207520 +v 0.858398 0.211426 -0.268555 +v 0.869141 0.264160 -0.240723 +v -0.828125 0.250977 -0.207520 +v -0.869141 0.264160 -0.240723 +v -0.858398 0.211426 -0.268555 +v 0.914062 0.278320 -0.276367 +v -0.914062 0.278320 -0.276367 +v 0.815430 0.237305 -0.138672 +v -0.815430 0.237305 -0.138672 +v 0.735514 0.046224 -0.132487 +v -0.735514 0.046224 -0.132487 +v 0.891602 0.237305 -0.296387 +v 0.929688 0.255859 -0.329102 +v 0.952148 0.294434 -0.322266 +v -0.891602 0.237305 -0.296387 +v -0.952148 0.294434 -0.322266 +v -0.929688 0.255859 -0.329102 +v 0.836426 0.170410 -0.296387 +v 0.874023 0.195801 -0.316406 +v -0.836426 0.170410 -0.296387 +v -0.874023 0.195801 -0.316406 +v 0.779297 0.102051 -0.296875 +v 0.810547 0.127930 -0.314941 +v -0.779297 0.102051 -0.296875 +v -0.810547 0.127930 -0.314941 +v 0.819336 0.075195 -0.296875 +v 0.804688 0.093262 -0.314941 +v -0.819336 0.075195 -0.296875 +v -0.804688 0.093262 -0.314941 +v 0.840332 0.020020 -0.296875 +v 0.848145 0.052246 -0.315918 +v -0.840332 0.020020 -0.296875 +v -0.848145 0.052246 -0.315918 +v 0.825684 -0.012207 -0.289551 +v 0.841309 0.002930 -0.312012 +v -0.825684 -0.012207 -0.289551 +v -0.841309 0.002930 -0.312012 +v 0.894043 -0.016602 -0.265137 +v 0.853516 -0.009277 -0.291016 +v -0.894043 -0.016602 -0.265137 +v -0.853516 -0.009277 -0.291016 +v 1.001465 0.303711 -0.372559 +v 1.039551 0.333008 -0.387207 +v -1.039551 0.333008 -0.387207 +v -1.001465 0.303711 -0.372559 +v 1.109050 0.332682 -0.437988 +v 1.169434 0.336426 -0.452637 +v -1.169434 0.336426 -0.452637 +v -1.109050 0.332682 -0.437988 +v 1.204590 0.288086 -0.470215 +v 1.231934 0.239746 -0.467285 +v -1.231934 0.239746 -0.467285 +v -1.204590 0.288086 -0.470215 +v 1.212402 0.169434 -0.470215 +v 1.185547 0.098144 -0.456543 +v -1.185547 0.098144 -0.456543 +v -1.212402 0.169434 -0.470215 +v 1.114258 0.057617 -0.437500 +v 1.042480 0.008789 -0.383789 +v -1.042480 0.008789 -0.383789 +v -1.114258 0.057617 -0.437500 +v 0.964844 0.004395 -0.337891 +v -0.964844 0.004395 -0.337891 +v 0.884277 0.039062 -0.323242 +v 0.913086 0.088867 -0.333984 +v 0.856934 0.091797 -0.324219 +v -0.884277 0.039062 -0.323242 +v -0.856934 0.091797 -0.324219 +v -0.913086 0.088867 -0.333984 +v 0.962891 0.086914 -0.349121 +v 0.979004 0.148437 -0.358398 +v 0.923828 0.142578 -0.338867 +v -0.962891 0.086914 -0.349121 +v -0.923828 0.142578 -0.338867 +v -0.979004 0.148437 -0.358398 +v 1.035156 0.147949 -0.383301 +v 1.036133 0.207031 -0.377930 +v 0.984863 0.204590 -0.360840 +v -1.035156 0.147949 -0.383301 +v -0.984863 0.204590 -0.360840 +v -1.036133 0.207031 -0.377930 +v 1.097168 0.201660 -0.398926 +v 1.105469 0.244629 -0.401367 +v 1.047852 0.253906 -0.382813 +v -1.097168 0.201660 -0.398926 +v -1.047852 0.253906 -0.382813 +v -1.105469 0.244629 -0.401367 +v 0.986816 0.259277 -0.361816 +v 1.063151 0.299479 -0.403809 +v -0.986816 0.259277 -0.361816 +v -1.063151 0.299479 -0.403809 +v 0.925781 0.202637 -0.337402 +v -0.925781 0.202637 -0.337402 +v 0.865723 0.141602 -0.325195 +v -0.865723 0.141602 -0.325195 +v 0.914551 0.025391 -0.314941 +v -0.914551 0.025391 -0.314941 +v 1.022949 0.066895 -0.387695 +v -1.022949 0.066895 -0.387695 +v 1.125488 0.138672 -0.429688 +v -1.125488 0.138672 -0.429688 +v 1.171387 0.225586 -0.439941 +v -1.171387 0.225586 -0.439941 +v 1.134440 0.299967 -0.436035 +v -1.134440 0.299967 -0.436035 +v 0.792969 -0.119629 -0.258301 +v 0.916504 -0.042969 -0.390137 +v 1.039062 -0.080078 -0.395996 +v -0.792969 -0.119629 -0.258301 +v -1.039062 -0.080078 -0.395996 +v -0.916504 -0.042969 -0.390137 +v 1.168945 0.041992 -0.495605 +v 1.272461 0.067871 -0.469238 +v -1.272461 0.067871 -0.469238 +v -1.168945 0.041992 -0.495605 +v 1.329427 0.201660 -0.506999 +v 1.336914 0.299316 -0.466309 +v -1.336914 0.299316 -0.466309 +v -1.329427 0.201660 -0.506999 +v 1.306966 0.359375 -0.511393 +v 1.229492 0.461914 -0.470703 +v -1.229492 0.461914 -0.470703 +v -1.306966 0.359375 -0.511393 +v 1.140625 0.399414 -0.496582 +v 1.032227 0.453125 -0.397949 +v -1.032227 0.453125 -0.397949 +v -1.140625 0.399414 -0.496582 +v 0.939941 0.349609 -0.410645 +v 0.883789 0.390137 -0.321289 +v -0.883789 0.390137 -0.321289 +v -0.939941 0.349609 -0.410645 +v 1.036621 0.180664 -0.478027 +v 0.835449 0.134766 -0.381836 +v -1.036621 0.180664 -0.478027 +v -0.835449 0.134766 -0.381836 +v 1.257161 0.253906 -0.529460 +v -1.257161 0.253906 -0.529460 +v 0.808594 0.280762 -0.382324 +v -0.808594 0.280762 -0.382324 +v 0.723633 -0.025879 -0.353516 +v -0.723633 -0.025879 -0.353516 +v 0.438843 0.160767 0.759033 +v -0.438843 0.160767 0.759033 +v 0.487305 0.108154 0.685547 +v -0.487305 0.108154 0.685547 +v 0.532227 0.068359 0.581055 +v -0.532227 0.068359 0.581055 +v 0.351562 -0.003906 0.616211 +v -0.351562 -0.003906 0.616211 +v 0.351685 0.052246 0.713013 +v -0.351685 0.052246 0.713013 +v 0.352417 0.127197 0.776123 +v -0.352417 0.127197 0.776123 +v 0.270142 0.160767 0.790894 +v -0.270142 0.160767 0.790894 +v 0.216675 0.108154 0.734497 +v -0.216675 0.108154 0.734497 +v 0.170898 0.068359 0.645508 +v -0.170898 0.068359 0.645508 +v 0.097656 0.243164 0.654297 +v -0.097656 0.243164 0.654297 +v 0.160645 0.242432 0.738037 +v -0.160645 0.242432 0.738037 +v 0.236572 0.243042 0.792969 +v -0.236572 0.243042 0.792969 +v 0.270142 0.329590 0.790894 +v -0.270142 0.329590 0.790894 +v 0.216675 0.377930 0.734497 +v -0.216675 0.377930 0.734497 +v 0.170898 0.422852 0.645508 +v -0.170898 0.422852 0.645508 +v 0.351562 0.496094 0.616211 +v -0.351562 0.496094 0.616211 +v 0.351685 0.434082 0.713013 +v -0.351685 0.434082 0.713013 +v 0.352417 0.364014 0.776123 +v -0.352417 0.364014 0.776123 +v 0.438843 0.329590 0.759033 +v -0.438843 0.329590 0.759033 +v 0.487305 0.377930 0.685547 +v -0.487305 0.377930 0.685547 +v 0.532227 0.422852 0.581055 +v -0.532227 0.422852 0.581055 +v 0.605469 0.243164 0.566406 +v -0.605469 0.243164 0.566406 +v 0.543457 0.242432 0.672607 +v -0.543457 0.242432 0.672607 +v 0.472656 0.243042 0.751221 +v -0.472656 0.243042 0.751221 +v 0.455404 0.243083 0.778971 +v -0.455404 0.243083 0.778971 +v 0.427531 0.318929 0.785848 +v -0.427531 0.318929 0.785848 +v 0.352458 0.350505 0.803426 +v -0.352458 0.350505 0.803426 +v 0.280924 0.318929 0.816406 +v -0.280924 0.318929 0.816406 +v 0.250244 0.243083 0.818197 +v -0.250244 0.243083 0.818197 +v 0.280924 0.171550 0.816406 +v -0.280924 0.171550 0.816406 +v 0.351969 0.242798 0.822428 +v -0.351969 0.242798 0.822428 +v 0.352458 0.140869 0.803426 +v -0.352458 0.140869 0.803426 +v 0.427531 0.171550 0.785848 +v -0.427531 0.171550 0.785848 +v 0.000000 0.432861 0.736816 +v 0.000000 0.352431 0.809679 +v 0.000000 -0.661133 0.736328 +v 0.000000 -0.306152 0.779297 +v 0.000000 -0.175293 0.791260 +v 0.000000 -0.765137 0.714600 +v 0.000000 0.439046 0.617676 +v 0.000000 0.566551 0.546875 +v 0.000000 0.855713 -0.490723 +v 0.000000 0.518066 -0.772461 +v 0.000000 0.093262 -0.771973 +v 0.000000 -0.352295 -0.304687 +v 0.226929 -0.177246 0.577515 +v -0.226929 -0.177246 0.577515 +v 0.288696 -0.443359 0.565674 +v -0.288696 -0.443359 0.565674 +v 0.323975 -0.664795 0.558960 +v -0.323975 -0.664795 0.558960 +v 0.338867 -0.845703 0.532471 +v -0.338867 -0.845703 0.532471 +v 0.302368 -0.930664 0.526001 +v -0.302368 -0.930664 0.526001 +v 0.171387 -0.960205 0.548950 +v -0.171387 -0.960205 0.548950 +v 0.000000 -0.974121 0.565918 +v 0.427002 -0.116943 0.545776 +v -0.427002 -0.116943 0.545776 +v 0.625366 -0.012573 0.525269 +v -0.625366 -0.012573 0.525269 +v 0.789429 0.167603 0.481934 +v -0.789429 0.167603 0.481934 +v 0.816772 0.393555 0.573364 +v -0.816772 0.393555 0.573364 +v 0.693237 0.479858 0.624512 +v -0.693237 0.479858 0.624512 +v 0.491455 0.592407 0.682251 +v -0.491455 0.592407 0.682251 +v 0.322388 0.711792 0.726685 +v -0.322388 0.711792 0.726685 +v 0.174561 0.676270 0.748169 +v -0.174561 0.676270 0.748169 +v 0.075806 0.499878 0.743896 +v -0.075806 0.499878 0.743896 +v 0.166992 0.407227 0.780640 +v -0.166992 0.407227 0.780640 +v 0.122559 0.310059 0.773926 +v -0.122559 0.310059 0.773926 +v 0.212769 0.071533 0.748657 +v -0.212769 0.071533 0.748657 +v 0.371216 0.027466 0.706543 +v -0.371216 0.027466 0.706543 +v 0.505615 0.075317 0.672729 +v -0.505615 0.075317 0.672729 +v 0.617920 0.189331 0.650635 +v -0.617920 0.189331 0.650635 +v 0.639771 0.300171 0.656006 +v -0.639771 0.300171 0.656006 +v 0.590088 0.376709 0.680542 +v -0.590088 0.376709 0.680542 +v 0.439087 0.443237 0.730469 +v -0.439087 0.443237 0.730469 +v 0.246460 0.473877 0.770996 +v -0.246460 0.473877 0.770996 +v 0.000000 -0.746338 0.732666 +v 0.112500 -0.737188 0.726406 +v -0.112500 -0.737188 0.726406 +v 0.121704 -0.835449 0.702271 +v -0.121704 -0.835449 0.702271 +v 0.071045 -0.882446 0.687744 +v -0.071045 -0.882446 0.687744 +v 0.000000 -0.893310 0.682861 +v 0.000000 -0.163086 0.768555 +v 0.000000 -0.134440 0.747396 +v 0.096069 -0.148437 0.753906 +v -0.096069 -0.148437 0.753906 +v 0.123657 -0.225220 0.750610 +v -0.123657 -0.225220 0.750610 +v 0.089531 -0.299844 0.743984 +v -0.089531 -0.299844 0.743984 +v 0.393799 -0.043579 0.667725 +v -0.393799 -0.043579 0.667725 +v 0.593506 0.050903 0.620850 +v -0.593506 0.050903 0.620850 +v 0.713379 0.197754 0.599365 +v -0.713379 0.197754 0.599365 +v 0.733887 0.355591 0.647339 +v -0.733887 0.355591 0.647339 +v 0.658203 0.427490 0.707153 +v -0.658203 0.427490 0.707153 +v 0.456909 0.536621 0.769775 +v -0.456909 0.536621 0.769775 +v 0.317383 0.619995 0.808838 +v -0.317383 0.619995 0.808838 +v 0.204834 0.592285 0.825439 +v -0.204834 0.592285 0.825439 +v 0.105713 0.447510 0.821411 +v -0.105713 0.447510 0.821411 +v 0.142415 -0.096300 0.776693 +v -0.142415 -0.096300 0.776693 +v 0.208252 -0.459473 0.695435 +v -0.208252 -0.459473 0.695435 +v 0.242798 -0.682739 0.678345 +v -0.242798 -0.682739 0.678345 +v 0.253784 -0.813354 0.655640 +v -0.253784 -0.813354 0.655640 +v 0.225098 -0.899292 0.632568 +v -0.225098 -0.899292 0.632568 +v 0.143921 -0.927978 0.631592 +v -0.143921 -0.927978 0.631592 +v 0.000000 -0.939697 0.636719 +v 0.000000 0.041558 0.738824 +v 0.000000 0.209473 0.765137 +v 0.329956 0.484375 0.755249 +v -0.329956 0.484375 0.755249 +v 0.149536 0.133545 0.752197 +v -0.149536 0.133545 0.752197 +v 0.123657 0.213989 0.761841 +v -0.123657 0.213989 0.761841 +v 0.114380 -0.664185 0.730225 +v -0.114380 -0.664185 0.730225 +v 0.089478 -0.457153 0.742188 +v -0.089478 -0.457153 0.742188 +v 0.000000 -0.458984 0.747070 +v 0.000000 -0.333008 0.748047 +v 0.084229 -0.272339 0.781250 +v -0.084229 -0.272339 0.781250 +v 0.121338 -0.221191 0.791138 +v -0.121338 -0.221191 0.791138 +v 0.100342 -0.147827 0.782104 +v -0.100342 -0.147827 0.782104 +v 0.039673 -0.138428 0.782104 +v -0.039673 -0.138428 0.782104 +v 0.000000 -0.202365 0.823351 +v 0.047743 -0.157769 0.809028 +v -0.047743 -0.157769 0.809028 +v 0.088108 -0.165148 0.809028 +v -0.088108 -0.165148 0.809028 +v 0.093994 -0.218994 0.819458 +v -0.093994 -0.218994 0.819458 +v 0.070095 -0.256510 0.804471 +v -0.070095 -0.256510 0.804471 +v 0.000000 -0.274170 0.804688 +v 0.246460 -0.315674 0.560303 +v -0.246460 -0.315674 0.560303 +v 0.164673 -0.234375 0.705688 +v -0.164673 -0.234375 0.705688 +v 0.180176 -0.317871 0.695801 +v -0.180176 -0.317871 0.695801 +v 0.221558 -0.244995 0.565674 +v -0.221558 -0.244995 0.565674 +v 0.000000 -0.873047 0.680908 +v 0.048706 -0.862427 0.684082 +v -0.048706 -0.862427 0.684082 +v 0.090576 -0.816894 0.701782 +v -0.090576 -0.816894 0.701782 +v 0.083862 -0.754517 0.716675 +v -0.083862 -0.754517 0.716675 +v 0.000000 -0.782959 0.662842 +v 0.077393 -0.771606 0.664876 +v -0.077393 -0.771606 0.664876 +v 0.084201 -0.809245 0.658782 +v -0.084201 -0.809245 0.658782 +v 0.051025 -0.838623 0.644247 +v -0.051025 -0.838623 0.644247 +v 0.000000 -0.848877 0.642822 +v 0.172607 0.220825 0.773804 +v -0.172607 0.220825 0.773804 +v 0.189331 0.158203 0.769165 +v -0.189331 0.158203 0.769165 +v 0.337891 0.427857 0.757935 +v -0.337891 0.427857 0.757935 +v 0.271484 0.419922 0.769409 +v -0.271484 0.419922 0.769409 +v 0.428711 0.399902 0.753296 +v -0.428711 0.399902 0.753296 +v 0.548950 0.350830 0.697632 +v -0.548950 0.350830 0.697632 +v 0.584595 0.285400 0.681030 +v -0.584595 0.285400 0.681030 +v 0.568237 0.195679 0.680054 +v -0.568237 0.195679 0.680054 +v 0.476807 0.105957 0.709473 +v -0.476807 0.105957 0.709473 +v 0.369019 0.070679 0.737061 +v -0.369019 0.070679 0.737061 +v 0.239380 0.109009 0.767944 +v -0.239380 0.109009 0.767944 +v 0.178345 0.297974 0.776245 +v -0.178345 0.297974 0.776245 +v 0.211792 0.373779 0.776367 +v -0.211792 0.373779 0.776367 +v 0.235352 0.356445 0.758789 +v -0.235352 0.356445 0.758789 +v 0.200195 0.295898 0.756836 +v -0.200195 0.295898 0.756836 +v 0.253906 0.125977 0.752930 +v -0.253906 0.125977 0.752930 +v 0.369141 0.094727 0.727539 +v -0.369141 0.094727 0.727539 +v 0.460938 0.125000 0.702148 +v -0.460938 0.125000 0.702148 +v 0.537109 0.208008 0.675781 +v -0.537109 0.208008 0.675781 +v 0.550781 0.279297 0.672852 +v -0.550781 0.279297 0.672852 +v 0.519531 0.335938 0.687500 +v -0.519531 0.335938 0.687500 +v 0.418945 0.385742 0.741211 +v -0.418945 0.385742 0.741211 +v 0.282227 0.394531 0.762695 +v -0.282227 0.394531 0.762695 +v 0.338867 0.403320 0.751953 +v -0.338867 0.403320 0.751953 +v 0.207031 0.172852 0.750977 +v -0.207031 0.172852 0.750977 +v 0.196289 0.228516 0.750977 +v -0.196289 0.228516 0.750977 +v 0.125521 0.489245 0.612891 +v -0.125521 0.489245 0.612891 +v 0.205729 0.638021 0.640625 +v -0.205729 0.638021 0.640625 +v 0.327691 0.665365 0.622179 +v -0.327691 0.665365 0.622179 +v 0.458125 0.553438 0.557578 +v -0.458125 0.553438 0.557578 +v 0.668091 0.463501 0.489014 +v -0.668091 0.463501 0.489014 +v 0.780396 0.385376 0.449707 +v -0.780396 0.385376 0.449707 +v 0.769775 0.177979 0.363892 +v -0.769775 0.177979 0.363892 +v 0.619507 -0.000366 0.387573 +v -0.619507 -0.000366 0.387573 +v 0.416667 -0.110781 0.449245 +v -0.416667 -0.110781 0.449245 +v 0.000000 0.856201 0.263916 +v 0.000000 0.950195 -0.085938 +v 0.000000 -0.175049 -0.618652 +v 0.000000 -0.436523 0.129395 +v 0.000000 -0.948242 0.456543 +v 0.000000 -0.786133 0.360352 +v 0.000000 -0.579346 0.325684 +v 0.000000 -0.475830 0.270508 +v 0.814697 0.226074 0.114136 +v -0.814697 0.226074 0.114136 +v 0.836328 0.300938 -0.049844 +v -0.836328 0.300938 -0.049844 +v 0.762813 0.282891 -0.418750 +v -0.762813 0.282891 -0.418750 +v 0.408691 0.442139 -0.665405 +v -0.408691 0.442139 -0.665405 +v 0.700312 -0.032500 0.072812 +v -0.700312 -0.032500 0.072812 +v 0.616094 -0.109531 -0.167969 +v -0.616094 -0.109531 -0.167969 +v 0.649780 0.005127 -0.400513 +v -0.649780 0.005127 -0.400513 +v 0.325521 0.077148 -0.660970 +v -0.325521 0.077148 -0.660970 +v 0.236694 -0.381103 0.408814 +v -0.236694 -0.381103 0.408814 +v 0.161865 -0.426758 0.279297 +v -0.161865 -0.426758 0.279297 +v 0.279541 -0.735840 0.403076 +v -0.279541 -0.735840 0.403076 +v 0.250000 -0.535644 0.407837 +v -0.250000 -0.535644 0.407837 +v 0.286024 -0.887153 0.430339 +v -0.286024 -0.887153 0.430339 +v 0.142334 -0.753784 0.373901 +v -0.142334 -0.753784 0.373901 +v 0.131958 -0.550171 0.349487 +v -0.131958 -0.550171 0.349487 +v 0.162720 -0.924683 0.441650 +v -0.162720 -0.924683 0.441650 +v 0.235833 -0.284766 0.404818 +v -0.235833 -0.284766 0.404818 +v 0.239176 -0.215210 0.474040 +v -0.239176 -0.215210 0.474040 +v 0.253111 -0.171658 0.501809 +v -0.253111 -0.171658 0.501809 +v 0.212630 -0.369297 0.155833 +v -0.212630 -0.369297 0.155833 +v 0.273193 -0.293091 -0.251099 +v -0.273193 -0.293091 -0.251099 +v 0.310872 -0.132080 -0.535482 +v -0.310872 -0.132080 -0.535482 +v 0.420288 0.804932 -0.407593 +v -0.420288 0.804932 -0.407593 +v 0.419922 0.896362 -0.089233 +v -0.419922 0.896362 -0.089233 +v 0.420532 0.808472 0.206787 +v -0.420532 0.808472 0.206787 +v 0.428833 0.561523 0.418091 +v -0.428833 0.561523 0.418091 +v 0.743408 0.409424 0.289062 +v -0.743408 0.409424 0.289062 +v 0.630615 0.487549 0.302734 +v -0.630615 0.487549 0.302734 +v 0.635010 0.679565 0.081543 +v -0.635010 0.679565 0.081543 +v 0.777710 0.535889 0.090576 +v -0.777710 0.535889 0.090576 +v 0.780151 0.582275 -0.138916 +v -0.780151 0.582275 -0.138916 +v 0.636719 0.740601 -0.170044 +v -0.636719 0.740601 -0.170044 +v 0.634277 0.650391 -0.414063 +v -0.634277 0.650391 -0.414063 +v 0.758247 0.524089 -0.343750 +v -0.758247 0.524089 -0.343750 +v 0.603149 0.339111 -0.558228 +v -0.603149 0.339111 -0.558228 +v 0.482005 0.019531 -0.522786 +v -0.482005 0.019531 -0.522786 +v 0.824766 0.347656 -0.205313 +v -0.824766 0.347656 -0.205313 +v 0.405755 -0.181641 0.158802 +v -0.405755 -0.181641 0.158802 +v 0.438843 -0.174072 -0.207764 +v -0.438843 -0.174072 -0.207764 +v 0.897949 0.395508 -0.258789 +v -0.897949 0.395508 -0.258789 +v 0.793335 -0.121094 -0.181519 +v -0.793335 -0.121094 -0.181519 +v 1.035889 -0.077881 -0.333130 +v -1.035889 -0.077881 -0.333130 +v 1.254883 0.070923 -0.426514 +v -1.254883 0.070923 -0.426514 +v 1.320435 0.304077 -0.431397 +v -1.320435 0.304077 -0.431397 +v 1.218506 0.467773 -0.420288 +v -1.218506 0.467773 -0.420288 +v 1.031982 0.459351 -0.333862 +v -1.031982 0.459351 -0.333862 +v 1.027222 0.409180 -0.304321 +v -1.027222 0.409180 -0.304321 +v 1.179810 0.415039 -0.389526 +v -1.179810 0.415039 -0.389526 +v 1.255371 0.280273 -0.411621 +v -1.255371 0.280273 -0.411621 +v 1.200806 0.088623 -0.401611 +v -1.200806 0.088623 -0.401611 +v 1.030518 -0.031006 -0.306030 +v -1.030518 -0.031006 -0.306030 +v 0.839600 -0.064575 -0.168579 +v -0.839600 -0.064575 -0.168579 +v 0.923706 0.356445 -0.236816 +v -0.923706 0.356445 -0.236816 +v 0.946167 0.306885 -0.293213 +v -0.946167 0.306885 -0.293213 +v 0.886963 -0.023926 -0.235107 +v -0.886963 -0.023926 -0.235107 +v 1.037964 0.004150 -0.356323 +v -1.037964 0.004150 -0.356323 +v 1.180054 0.099243 -0.435547 +v -1.180054 0.099243 -0.435547 +v 1.227051 0.247681 -0.445068 +v -1.227051 0.247681 -0.445068 +v 1.165039 0.350952 -0.428589 +v -1.165039 0.350952 -0.428589 +v 1.033203 0.347534 -0.358643 +v -1.033203 0.347534 -0.358643 +v 0.851318 0.287598 -0.216064 +v -0.851318 0.287598 -0.216064 +v 0.828857 0.173584 -0.270386 +v -0.828857 0.173584 -0.270386 +v 0.772461 0.096680 -0.270264 +v -0.772461 0.096680 -0.270264 +v 0.811632 0.071181 -0.281467 +v -0.811632 0.071181 -0.281467 +v 0.823608 0.022583 -0.269653 +v -0.823608 0.022583 -0.269653 +v 0.813721 -0.013672 -0.259521 +v -0.813721 -0.013672 -0.259521 +v 0.739176 0.032064 -0.078288 +v -0.739176 0.032064 -0.078288 +v 0.727526 -0.027448 -0.168229 +v -0.727526 -0.027448 -0.168229 +v 0.738307 0.048411 -0.191979 +v -0.738307 0.048411 -0.191979 +v 0.803589 0.201538 -0.198730 +v -0.803589 0.201538 -0.198730 +v 0.885498 0.245972 -0.269043 +v -0.885498 0.245972 -0.269043 +v 0.900635 0.226685 -0.320068 +v -0.900635 0.226685 -0.320068 +v 0.837023 -0.007596 -0.299479 +v -0.837023 -0.007596 -0.299479 +v 0.853027 0.024536 -0.315186 +v -0.853027 0.024536 -0.315186 +v 0.830688 0.077881 -0.315430 +v -0.830688 0.077881 -0.315430 +v 0.794705 0.106554 -0.310113 +v -0.794705 0.106554 -0.310113 +v 0.845459 0.162964 -0.315552 +v -0.845459 0.162964 -0.315552 +v 1.049113 0.320719 -0.404785 +v -1.049113 0.320719 -0.404785 +v 1.164836 0.323649 -0.460449 +v -1.164836 0.323649 -0.460449 +v 1.222412 0.234009 -0.471924 +v -1.222412 0.234009 -0.471924 +v 1.175903 0.106567 -0.460327 +v -1.175903 0.106567 -0.460327 +v 1.039795 0.023315 -0.397949 +v -1.039795 0.023315 -0.397949 +v 0.899414 -0.004639 -0.291016 +v -0.899414 -0.004639 -0.291016 +v 0.962769 0.281982 -0.345825 +v -0.962769 0.281982 -0.345825 +v 0.890503 0.114258 -0.329834 +v -0.890503 0.114258 -0.329834 +v 0.924194 0.060791 -0.331177 +v -0.924194 0.060791 -0.331177 +v 1.000000 0.116333 -0.369141 +v -1.000000 0.116333 -0.369141 +v 0.955688 0.173706 -0.349731 +v -0.955688 0.173706 -0.349731 +v 1.014526 0.232300 -0.371948 +v -1.014526 0.232300 -0.371948 +v 1.067749 0.177734 -0.392822 +v -1.067749 0.177734 -0.392822 +v 1.123481 0.223524 -0.411024 +v -1.123481 0.223524 -0.411024 +v 1.087077 0.274699 -0.401489 +v -1.087077 0.274699 -0.401489 +v 1.032837 0.375732 -0.458740 +v -1.032837 0.375732 -0.458740 +v 1.239828 0.400635 -0.516642 +v -1.239828 0.400635 -0.516642 +v 1.328270 0.287543 -0.496600 +v -1.328270 0.287543 -0.496600 +v 1.274984 0.114136 -0.510905 +v -1.274984 0.114136 -0.510905 +v 1.039917 -0.011475 -0.456787 +v -1.039917 -0.011475 -0.456787 +v 0.807983 -0.048950 -0.340698 +v -0.807983 -0.048950 -0.340698 +v 0.864380 0.318726 -0.376953 +v -0.864380 0.318726 -0.376953 +vt 0.481096 0.886074 +vt 0.482512 0.871343 +vt 0.492098 0.872954 +vt 0.490947 0.884499 +vt 0.491589 0.894442 +vt 0.481943 0.898903 +vt 0.468931 0.904205 +vt 0.467876 0.887817 +vt 0.469678 0.869121 +vt 0.039865 0.789567 +vt 0.041625 0.780388 +vt 0.054224 0.794512 +vt 0.049723 0.800654 +vt 0.045817 0.805288 +vt 0.038183 0.796684 +vt 0.028138 0.791794 +vt 0.027075 0.783199 +vt 0.025414 0.772242 +vt 0.453331 0.888300 +vt 0.455522 0.866536 +vt 0.454431 0.907321 +vt 0.439187 0.909345 +vt 0.438123 0.888153 +vt 0.440706 0.863769 +vt 0.041466 0.772283 +vt 0.040348 0.764715 +vt 0.056528 0.783129 +vt 0.056046 0.788730 +vt 0.022611 0.762815 +vt 0.019237 0.754154 +vt 0.458805 0.920523 +vt 0.472714 0.915572 +vt 0.749184 0.938074 +vt 0.748278 0.934282 +vt 0.759252 0.918124 +vt 0.762017 0.919061 +vt 0.465365 0.926301 +vt 0.451444 0.930481 +vt 0.444047 0.924041 +vt 0.063804 0.810211 +vt 0.065049 0.807405 +vt 0.065118 0.833437 +vt 0.063980 0.833149 +vt 0.061159 0.832400 +vt 0.060952 0.812869 +vt 0.484928 0.907759 +vt 0.493982 0.901303 +vt 0.744531 0.928959 +vt 0.741167 0.924705 +vt 0.748008 0.914379 +vt 0.753171 0.916091 +vt 0.054886 0.815232 +vt 0.054967 0.830731 +vt 0.049720 0.829302 +vt 0.049731 0.816915 +vt 0.756342 0.901570 +vt 0.750577 0.903021 +vt 0.748805 0.892144 +vt 0.753972 0.887511 +vt 0.760122 0.881485 +vt 0.763209 0.899571 +vt 0.049579 0.844583 +vt 0.054194 0.850046 +vt 0.041620 0.863407 +vt 0.039829 0.854986 +vt 0.038086 0.848267 +vt 0.045578 0.840185 +vt 0.766729 0.897198 +vt 0.763269 0.875942 +vt 0.764914 0.870640 +vt 0.768574 0.894639 +vt 0.763124 0.919450 +vt 0.964742 0.269659 +vt 0.951782 0.275614 +vt 0.952007 0.261999 +vt 0.964824 0.257430 +vt 0.976538 0.251908 +vt 0.976518 0.262435 +vt 0.055814 0.853969 +vt 0.055937 0.857122 +vt 0.751588 0.858748 +vt 0.749963 0.866729 +vt 0.734897 0.857300 +vt 0.734094 0.847799 +vt 0.732482 0.839216 +vt 0.752037 0.851423 +vt 0.964411 0.238893 +vt 0.951889 0.241320 +vt 0.951838 0.216860 +vt 0.964164 0.217079 +vt 0.975596 0.217228 +vt 0.975962 0.235954 +vt 0.745987 0.876023 +vt 0.742625 0.883261 +vt 0.733275 0.877508 +vt 0.734081 0.868638 +vt 0.985568 0.231991 +vt 0.985239 0.217237 +vt 0.992484 0.217197 +vt 0.992737 0.228767 +vt 0.026822 0.860140 +vt 0.028024 0.852250 +vt 0.024995 0.870082 +vt 0.719950 0.866886 +vt 0.721994 0.876021 +vt 0.710661 0.878758 +vt 0.705761 0.870592 +vt 0.699169 0.860094 +vt 0.717087 0.855192 +vt 0.576905 0.857737 +vt 0.588803 0.851307 +vt 0.593086 0.864077 +vt 0.580353 0.867693 +vt 0.570785 0.870708 +vt 0.567903 0.862933 +vt 0.563366 0.858817 +vt 0.571275 0.852489 +vt 0.581714 0.844594 +vt 0.836115 0.475434 +vt 0.838688 0.453512 +vt 0.829257 0.452073 +vt 0.826911 0.470779 +vt 0.826265 0.486994 +vt 0.835616 0.494473 +vt 0.846311 0.501639 +vt 0.846555 0.480014 +vt 0.849257 0.455181 +vt 0.601676 0.847373 +vt 0.615035 0.844686 +vt 0.621303 0.861691 +vt 0.606920 0.862400 +vt 0.593217 0.839496 +vt 0.964741 0.195019 +vt 0.952261 0.191899 +vt 0.838079 0.507319 +vt 0.828287 0.497991 +vt 0.662030 0.916118 +vt 0.649717 0.909228 +vt 0.659213 0.892314 +vt 0.673156 0.896153 +vt 0.687752 0.900137 +vt 0.675171 0.922841 +vt 0.849254 0.516083 +vt 0.608285 0.882997 +vt 0.622902 0.885054 +vt 0.620138 0.910863 +vt 0.605906 0.905728 +vt 0.592291 0.900850 +vt 0.594249 0.881552 +vt 0.639060 0.902006 +vt 0.630378 0.912575 +vt 0.624285 0.904606 +vt 0.631184 0.896458 +vt 0.637168 0.886101 +vt 0.646574 0.888766 +vt 0.638716 0.922856 +vt 0.581378 0.881332 +vt 0.579910 0.896484 +vt 0.570683 0.893256 +vt 0.571743 0.881341 +vt 0.651614 0.874590 +vt 0.641253 0.874767 +vt 0.642753 0.864493 +vt 0.653466 0.861765 +vt 0.667869 0.858377 +vt 0.665543 0.874471 +vt 0.677925 0.498737 +vt 0.689335 0.506109 +vt 0.689458 0.495315 +vt 0.677981 0.490263 +vt 0.669579 0.486309 +vt 0.669554 0.492981 +vt 0.575881 0.910980 +vt 0.567507 0.904876 +vt 0.587208 0.919088 +vt 0.680581 0.875178 +vt 0.683263 0.856306 +vt 0.699152 0.854893 +vt 0.696173 0.876297 +vt 0.702733 0.511573 +vt 0.717126 0.516083 +vt 0.717096 0.501984 +vt 0.702809 0.498993 +vt 0.599917 0.927158 +vt 0.613317 0.935209 +vt 0.680163 0.842652 +vt 0.665352 0.846712 +vt 0.461024 0.845105 +vt 0.474464 0.850828 +vt 0.446789 0.839496 +vt 0.695411 0.839496 +vt 0.700973 0.480098 +vt 0.714803 0.480610 +vt 0.710837 0.455956 +vt 0.697576 0.458288 +vt 0.685199 0.460521 +vt 0.688021 0.479168 +vt 0.651414 0.852579 +vt 0.640983 0.857312 +vt 0.486314 0.856780 +vt 0.495081 0.861287 +vt 0.013120 0.782779 +vt 0.007853 0.771817 +vt 0.016991 0.791306 +vt 0.676828 0.477398 +vt 0.668601 0.475975 +vt 0.499276 0.862675 +vt 0.496506 0.873707 +vt 0.636693 0.857970 +vt 0.638428 0.864587 +vt 0.634842 0.865361 +vt 0.633769 0.858828 +vt 0.501933 0.863788 +vt 0.499717 0.874300 +vt 0.664548 0.476086 +vt 0.662006 0.475916 +vt 0.662523 0.484818 +vt 0.665453 0.485920 +vt 0.666786 0.464098 +vt 0.662812 0.464867 +vt 0.660119 0.465346 +vt 0.636917 0.874237 +vt 0.632924 0.884910 +vt 0.629586 0.884020 +vt 0.633906 0.874069 +vt 0.563655 0.902816 +vt 0.561094 0.901223 +vt 0.563284 0.890597 +vt 0.566525 0.891789 +vt 0.665273 0.492365 +vt 0.662491 0.491556 +vt 0.627212 0.894590 +vt 0.067509 0.725084 +vt 0.071466 0.726806 +vt 0.069637 0.720075 +vt 0.065802 0.718615 +vt 0.063547 0.716803 +vt 0.065074 0.723573 +vt 0.624547 0.893296 +vt 0.567420 0.880585 +vt 0.564496 0.880111 +vt 0.563155 0.870915 +vt 0.566449 0.870584 +vt 0.065634 0.708733 +vt 0.069457 0.709685 +vt 0.070270 0.697702 +vt 0.066397 0.697410 +vt 0.064083 0.697195 +vt 0.063357 0.707910 +vt 0.997139 0.205853 +vt 0.999711 0.206503 +vt 0.999443 0.217239 +vt 0.996724 0.217198 +vt 0.992914 0.205191 +vt 0.563721 0.863167 +vt 0.561034 0.863404 +vt 0.067484 0.686620 +vt 0.071422 0.686190 +vt 0.742845 0.885075 +vt 0.748606 0.893511 +vt 0.747773 0.894733 +vt 0.742851 0.886280 +vt 0.734397 0.881643 +vt 0.734108 0.879604 +vt 0.996973 0.228043 +vt 0.999553 0.227516 +vt 1.000000 0.235714 +vt 0.997287 0.237123 +vt 0.993056 0.238446 +vt 0.750361 0.903747 +vt 0.063028 0.610557 +vt 0.058507 0.610915 +vt 0.058509 0.622843 +vt 0.062932 0.621762 +vt 0.748000 0.914410 +vt 0.747405 0.914179 +vt 0.750107 0.904237 +vt 0.045800 0.839461 +vt 0.045860 0.838912 +vt 0.049106 0.829135 +vt 0.049730 0.829272 +vt 0.997066 0.243173 +vt 0.992827 0.244780 +vt 0.999732 0.242058 +vt 0.498412 0.900428 +vt 0.501974 0.903367 +vt 0.497566 0.904311 +vt 0.495994 0.893850 +vt 0.499240 0.892977 +vt 0.501280 0.899786 +vt 0.741411 0.924123 +vt 0.741562 0.923508 +vt 0.049612 0.817535 +vt 0.049397 0.818131 +vt 0.045108 0.808121 +vt 0.045783 0.806548 +vt 0.062884 0.599126 +vt 0.058508 0.598012 +vt 0.058499 0.610092 +vt 0.495385 0.884521 +vt 0.498248 0.884476 +vt 0.038578 0.798606 +vt 0.038639 0.800269 +vt 0.029632 0.796769 +vt 0.029145 0.794131 +vt 0.660500 0.454268 +vt 0.664299 0.452794 +vt 0.723993 0.925671 +vt 0.718115 0.922183 +vt 0.726149 0.906586 +vt 0.730681 0.921604 +vt 0.732452 0.929475 +vt 0.722720 0.932439 +vt 0.713279 0.930374 +vt 0.035957 0.806612 +vt 0.029131 0.806267 +vt 0.039279 0.813699 +vt 0.027198 0.823705 +vt 0.737919 0.919156 +vt 0.740484 0.911683 +vt 0.043956 0.819742 +vt 0.041960 0.827387 +vt 0.744228 0.904980 +vt 0.740703 0.898933 +vt 0.041371 0.835042 +vt 0.038658 0.845540 +vt 0.035091 0.838131 +vt 0.738851 0.891691 +vt 0.731957 0.890340 +vt 0.029542 0.843140 +vt 0.029883 0.849860 +vt 0.020274 0.849047 +vt 0.023052 0.840452 +vt 0.725318 0.886942 +vt 0.718933 0.891293 +vt 0.714474 0.883047 +vt 0.724464 0.879828 +vt 0.015641 0.839612 +vt 0.010995 0.845067 +vt 0.004452 0.836859 +vt 0.012728 0.832455 +vt 0.711278 0.893417 +vt 0.709446 0.900791 +vt 0.700040 0.897557 +vt 0.705463 0.888570 +vt 0.007524 0.826452 +vt 0.000000 0.827274 +vt 0.000451 0.816788 +vt 0.010077 0.819296 +vt 0.704969 0.907593 +vt 0.708910 0.913964 +vt 0.627520 0.875332 +vt 0.626348 0.870133 +vt 0.622902 0.882261 +vt 0.010109 0.811152 +vt 0.003538 0.806493 +vt 0.010902 0.799441 +vt 0.016916 0.808024 +vt 0.710460 0.920922 +vt 0.704332 0.925987 +vt 0.699153 0.917789 +vt 0.022055 0.803084 +vt 0.019752 0.795475 +vt 0.992866 0.383426 +vt 0.981805 0.383672 +vt 0.979426 0.405182 +vt 0.991312 0.403568 +vt 0.112970 0.007488 +vt 0.132821 0.002970 +vt 0.128413 0.006026 +vt 0.111367 0.010033 +vt 0.092459 0.015988 +vt 0.091733 0.014053 +vt 0.993949 0.361255 +vt 0.983204 0.361005 +vt 0.071419 0.023077 +vt 0.074301 0.023939 +vt 0.058825 0.032133 +vt 0.053495 0.032731 +vt 0.993901 0.339058 +vt 0.993288 0.318866 +vt 0.981490 0.316699 +vt 0.982863 0.338296 +vt 0.678945 0.437389 +vt 0.668474 0.428378 +vt 0.669342 0.421808 +vt 0.680671 0.432693 +vt 0.148976 0.000088 +vt 0.161467 0.000000 +vt 0.150417 0.005006 +vt 0.140987 0.003919 +vt 0.989944 0.419678 +vt 0.976830 0.424417 +vt 0.039431 0.041186 +vt 0.047968 0.038818 +vt 0.041580 0.045839 +vt 0.029965 0.049336 +vt 0.992673 0.302709 +vt 0.993707 0.291005 +vt 0.979586 0.282825 +vt 0.979794 0.297364 +vt 0.686993 0.428080 +vt 0.675068 0.415649 +vt 0.686622 0.408289 +vt 0.698060 0.422330 +vt 0.714840 0.430086 +vt 0.703100 0.435653 +vt 0.694523 0.440226 +vt 0.025837 0.058073 +vt 0.039512 0.055041 +vt 0.041090 0.065620 +vt 0.026724 0.068694 +vt 0.021981 0.077227 +vt 0.019792 0.065292 +vt 0.023878 0.055454 +vt 0.714023 0.414220 +vt 0.704972 0.398117 +vt 0.727630 0.384752 +vt 0.734669 0.402476 +vt 0.745026 0.413812 +vt 0.727849 0.423582 +vt 0.032306 0.082496 +vt 0.045644 0.076769 +vt 0.053962 0.091852 +vt 0.042095 0.100690 +vt 0.887880 0.777665 +vt 0.887591 0.801327 +vt 0.904979 0.804846 +vt 0.904907 0.778624 +vt 0.905747 0.757689 +vt 0.889333 0.759830 +vt 0.759784 0.385827 +vt 0.752111 0.367817 +vt 0.774115 0.351321 +vt 0.783509 0.368858 +vt 0.793521 0.382130 +vt 0.769270 0.398444 +vt 0.055601 0.124487 +vt 0.066835 0.114230 +vt 0.080513 0.137227 +vt 0.069812 0.147649 +vt 0.889469 0.831328 +vt 0.892558 0.860292 +vt 0.910958 0.861718 +vt 0.907395 0.834463 +vt 0.851947 0.314539 +vt 0.851757 0.327390 +vt 0.831038 0.312464 +vt 0.832467 0.302566 +vt 0.833390 0.290527 +vt 0.849940 0.298952 +vt 0.868449 0.303928 +vt 0.873735 0.321132 +vt 0.881184 0.341525 +vt 0.090857 0.225638 +vt 0.097129 0.228716 +vt 0.071577 0.249803 +vt 0.065428 0.246873 +vt 0.061518 0.247399 +vt 0.085791 0.228286 +vt 0.370922 0.611255 +vt 0.347956 0.609997 +vt 0.352910 0.631669 +vt 0.378467 0.633670 +vt 0.109282 0.207988 +vt 0.124218 0.205325 +vt 0.853420 0.284963 +vt 0.838559 0.278740 +vt 0.846452 0.263753 +vt 0.860695 0.269659 +vt 0.875270 0.273461 +vt 0.869419 0.288575 +vt 0.295480 0.173792 +vt 0.292477 0.180146 +vt 0.261983 0.178368 +vt 0.264687 0.172116 +vt 0.043337 0.268389 +vt 0.049778 0.271200 +vt 0.032835 0.293339 +vt 0.025431 0.291729 +vt 0.023037 0.290644 +vt 0.040664 0.267368 +vt 0.870072 0.250126 +vt 0.855550 0.242111 +vt 0.871931 0.221180 +vt 0.885557 0.231754 +vt 0.898576 0.241924 +vt 0.884629 0.256976 +vt 0.012557 0.318434 +vt 0.021853 0.316649 +vt 0.016936 0.339078 +vt 0.006033 0.345191 +vt 0.000000 0.349839 +vt 0.008446 0.319679 +vt 0.911154 0.219934 +vt 0.901671 0.208327 +vt 0.931401 0.202057 +vt 0.936637 0.214214 +vt 0.937543 0.225359 +vt 0.918190 0.231111 +vt 0.007172 0.368688 +vt 0.018190 0.358571 +vt 0.023626 0.371983 +vt 0.014248 0.384675 +vt 0.009291 0.394704 +vt 0.000809 0.376493 +vt 0.414625 0.533938 +vt 0.415837 0.524795 +vt 0.427421 0.516083 +vt 0.427586 0.528035 +vt 0.427770 0.548000 +vt 0.413253 0.551420 +vt 0.403024 0.558206 +vt 0.405342 0.544215 +vt 0.951782 0.214145 +vt 0.950704 0.225487 +vt 0.025530 0.388905 +vt 0.031255 0.376169 +vt 0.045940 0.375827 +vt 0.043685 0.387764 +vt 0.805497 0.532629 +vt 0.796288 0.528110 +vt 0.792623 0.542154 +vt 0.801837 0.544945 +vt 0.816055 0.549878 +vt 0.818184 0.538527 +vt 0.413768 0.577396 +vt 0.428650 0.573549 +vt 0.430752 0.600019 +vt 0.415764 0.604818 +vt 0.404473 0.610171 +vt 0.403038 0.583196 +vt 0.071377 0.387636 +vt 0.072539 0.375659 +vt 0.101657 0.376371 +vt 0.101340 0.389255 +vt 0.782035 0.523910 +vt 0.765328 0.520395 +vt 0.762459 0.535210 +vt 0.779054 0.538594 +vt 0.418834 0.626637 +vt 0.434600 0.622748 +vt 0.437199 0.643422 +vt 0.420715 0.645356 +vt 0.407406 0.646101 +vt 0.406417 0.630117 +vt 0.126308 0.393352 +vt 0.123898 0.378671 +vt 0.141075 0.378874 +vt 0.146881 0.394708 +vt 0.748752 0.517932 +vt 0.737337 0.516083 +vt 0.730886 0.531208 +vt 0.744161 0.532946 +vt 0.419140 0.663477 +vt 0.435553 0.663724 +vt 0.657824 0.993693 +vt 0.672162 1.001452 +vt 0.668198 0.991199 +vt 0.654312 0.984171 +vt 0.283593 0.343106 +vt 0.264869 0.339860 +vt 0.264430 0.323794 +vt 0.280370 0.326261 +vt 0.405975 0.661187 +vt 0.425089 0.791435 +vt 0.412194 0.794938 +vt 0.406521 0.781129 +vt 0.418310 0.775343 +vt 0.433950 0.772028 +vt 0.441409 0.789667 +vt 0.444931 0.809729 +vt 0.428339 0.809333 +vt 0.163659 0.388102 +vt 0.155007 0.373294 +vt 0.652107 0.963208 +vt 0.665811 0.968984 +vt 0.664616 0.945784 +vt 0.651100 0.940913 +vt 0.245042 0.326495 +vt 0.226606 0.311812 +vt 0.228426 0.300692 +vt 0.246295 0.312965 +vt 0.407856 0.761945 +vt 0.397478 0.768360 +vt 0.388635 0.756339 +vt 0.397889 0.750292 +vt 0.412277 0.747509 +vt 0.422810 0.758473 +vt 0.651184 0.927396 +vt 0.664231 0.932578 +vt 0.663861 0.928495 +vt 0.651073 0.922856 +vt 0.212056 0.304615 +vt 0.200794 0.304747 +vt 0.195271 0.290025 +vt 0.213285 0.293892 +vt 0.392572 0.739432 +vt 0.383565 0.744773 +vt 0.378467 0.729486 +vt 0.390830 0.728911 +vt 0.404824 0.728260 +vt 0.406639 0.737642 +vt 0.249748 0.303145 +vt 0.234335 0.294368 +vt 0.241237 0.289791 +vt 0.253723 0.295436 +vt 0.266382 0.298398 +vt 0.265180 0.309822 +vt 0.162867 0.335741 +vt 0.171927 0.340866 +vt 0.165391 0.360951 +vt 0.155635 0.350920 +vt 0.147216 0.343106 +vt 0.154802 0.332549 +vt 0.160494 0.320083 +vt 0.168703 0.318991 +vt 0.177313 0.319868 +vt 0.278494 0.310851 +vt 0.277505 0.298165 +vt 0.287985 0.294603 +vt 0.290542 0.306714 +vt 0.294533 0.321300 +vt 0.146284 0.360453 +vt 0.134487 0.365262 +vt 0.128448 0.354457 +vt 0.138684 0.350247 +vt 0.302179 0.297890 +vt 0.298714 0.287577 +vt 0.311749 0.277724 +vt 0.316365 0.285093 +vt 0.322578 0.293487 +vt 0.307339 0.309849 +vt 0.119918 0.366270 +vt 0.100816 0.365961 +vt 0.099445 0.357374 +vt 0.115745 0.356222 +vt 0.336061 0.269037 +vt 0.329146 0.265681 +vt 0.344353 0.253630 +vt 0.353882 0.254085 +vt 0.364188 0.256429 +vt 0.344042 0.273798 +vt 0.075419 0.366823 +vt 0.052160 0.367283 +vt 0.059039 0.360672 +vt 0.078420 0.359747 +vt 0.362443 0.244601 +vt 0.350814 0.243752 +vt 0.350759 0.234184 +vt 0.363557 0.236083 +vt 0.378467 0.238856 +vt 0.375473 0.247027 +vt 0.039474 0.365770 +vt 0.033032 0.360087 +vt 0.041423 0.350238 +vt 0.047675 0.357481 +vt 0.359036 0.224032 +vt 0.346418 0.223061 +vt 0.336336 0.211802 +vt 0.348336 0.210945 +vt 0.361862 0.209671 +vt 0.373740 0.225178 +vt 0.028511 0.348036 +vt 0.027963 0.331141 +vt 0.037565 0.323893 +vt 0.037377 0.339005 +vt 0.330912 0.199320 +vt 0.319059 0.201824 +vt 0.298846 0.194429 +vt 0.309301 0.190532 +vt 0.319801 0.185677 +vt 0.343406 0.196011 +vt 0.033440 0.310924 +vt 0.043937 0.290090 +vt 0.054376 0.286147 +vt 0.044015 0.305013 +vt 0.286039 0.185957 +vt 0.279956 0.190919 +vt 0.261182 0.192832 +vt 0.260987 0.185942 +vt 0.058449 0.271343 +vt 0.077310 0.254853 +vt 0.081695 0.260170 +vt 0.066295 0.271078 +vt 0.234005 0.190835 +vt 0.241316 0.201706 +vt 0.225153 0.213273 +vt 0.211760 0.200454 +vt 0.193077 0.179491 +vt 0.228867 0.179292 +vt 0.100851 0.240791 +vt 0.123934 0.233404 +vt 0.122278 0.251869 +vt 0.102495 0.253788 +vt 0.200914 0.214616 +vt 0.217489 0.223262 +vt 0.214962 0.232590 +vt 0.197715 0.230138 +vt 0.171528 0.226740 +vt 0.183608 0.202947 +vt 0.141419 0.236937 +vt 0.146778 0.216764 +vt 0.154038 0.246524 +vt 0.142660 0.259716 +vt 0.134622 0.254352 +vt 0.221080 0.287033 +vt 0.230627 0.281972 +vt 0.210966 0.279098 +vt 0.222550 0.272644 +vt 0.173862 0.304748 +vt 0.176261 0.292118 +vt 0.184246 0.304787 +vt 0.163342 0.307216 +vt 0.163291 0.294876 +vt 0.204977 0.268519 +vt 0.217656 0.262472 +vt 0.189623 0.274972 +vt 0.184758 0.262005 +vt 0.201373 0.256398 +vt 0.215145 0.252100 +vt 0.173814 0.280211 +vt 0.168556 0.268710 +vt 0.160286 0.283996 +vt 0.155355 0.274531 +vt 0.198410 0.243835 +vt 0.179548 0.248116 +vt 0.214214 0.242171 +vt 0.162524 0.257298 +vt 0.149526 0.266440 +vt 0.107545 0.014562 +vt 0.120369 0.011773 +vt 0.112719 0.017764 +vt 0.103792 0.019274 +vt 0.094795 0.022213 +vt 0.093605 0.019043 +vt 0.080159 0.024837 +vt 0.086085 0.025917 +vt 0.078368 0.030651 +vt 0.068665 0.031171 +vt 0.130467 0.011054 +vt 0.138367 0.013804 +vt 0.128426 0.022391 +vt 0.121219 0.018347 +vt 0.060585 0.037272 +vt 0.072348 0.036681 +vt 0.069580 0.044468 +vt 0.056444 0.044539 +vt 0.144592 0.021423 +vt 0.149246 0.030956 +vt 0.133646 0.043072 +vt 0.133472 0.031265 +vt 0.158028 0.010577 +vt 0.163798 0.019583 +vt 0.056767 0.054373 +vt 0.071616 0.054472 +vt 0.079251 0.063479 +vt 0.059532 0.064614 +vt 0.160341 0.076323 +vt 0.176380 0.073132 +vt 0.181205 0.099450 +vt 0.165619 0.102234 +vt 0.151099 0.106514 +vt 0.145090 0.081256 +vt 0.140111 0.058923 +vt 0.155633 0.052647 +vt 0.171357 0.047808 +vt 0.081015 0.106084 +vt 0.068988 0.085154 +vt 0.084807 0.079671 +vt 0.095747 0.099768 +vt 0.107835 0.122745 +vt 0.094084 0.129072 +vt 0.152430 0.039444 +vt 0.136801 0.048036 +vt 0.167702 0.030976 +vt 0.062716 0.073103 +vt 0.080140 0.069293 +vt 0.145996 0.128536 +vt 0.157498 0.126173 +vt 0.164282 0.141614 +vt 0.150837 0.140005 +vt 0.139710 0.141931 +vt 0.135669 0.131161 +vt 0.129147 0.113777 +vt 0.139381 0.110187 +vt 0.126164 0.135976 +vt 0.119077 0.117805 +vt 0.130061 0.147799 +vt 0.120993 0.157855 +vt 0.115946 0.141762 +vt 0.132246 0.086145 +vt 0.125966 0.064380 +vt 0.120524 0.090792 +vt 0.112509 0.069429 +vt 0.108637 0.095002 +vt 0.099052 0.074478 +vt 0.121915 0.052865 +vt 0.107811 0.056907 +vt 0.105467 0.050658 +vt 0.119362 0.047855 +vt 0.094529 0.063139 +vt 0.093156 0.057686 +vt 0.848774 0.366161 +vt 0.844286 0.362787 +vt 0.851499 0.357724 +vt 0.855258 0.361348 +vt 0.858848 0.365213 +vt 0.852772 0.369326 +vt 0.847843 0.372601 +vt 0.843471 0.370228 +vt 0.836219 0.369471 +vt 0.064202 0.632029 +vt 0.063080 0.639497 +vt 0.067781 0.637657 +vt 0.068982 0.630228 +vt 0.071466 0.626224 +vt 0.066680 0.627960 +vt 0.061722 0.628988 +vt 0.058615 0.632509 +vt 0.119258 0.167819 +vt 0.118204 0.168957 +vt 0.123487 0.160067 +vt 0.865637 0.461077 +vt 0.868750 0.460967 +vt 0.872775 0.466861 +vt 0.870446 0.466345 +vt 0.866486 0.465470 +vt 0.861268 0.460752 +vt 0.862746 0.354564 +vt 0.866232 0.358300 +vt 0.860261 0.351872 +vt 0.066608 0.671507 +vt 0.066368 0.661389 +vt 0.061040 0.661454 +vt 0.061432 0.671581 +vt 0.062683 0.680527 +vt 0.067639 0.680156 +vt 0.070026 0.680802 +vt 0.070241 0.671469 +vt 0.071466 0.661027 +vt 0.872029 0.475504 +vt 0.875567 0.477559 +vt 0.874829 0.491787 +vt 0.870754 0.484407 +vt 0.866916 0.481171 +vt 0.868263 0.473187 +vt 0.068539 0.683746 +vt 0.063554 0.684209 +vt 0.870559 0.507747 +vt 0.866598 0.509712 +vt 0.865983 0.501638 +vt 0.870099 0.498765 +vt 0.874269 0.506023 +vt 0.071443 0.686190 +vt 0.866987 0.488905 +vt 0.868959 0.491519 +vt 0.866531 0.491408 +vt 0.864424 0.491312 +vt 0.739156 0.456290 +vt 0.739209 0.452389 +vt 0.733951 0.451374 +vt 0.734206 0.455488 +vt 0.734566 0.461392 +vt 0.739211 0.461917 +vt 0.666981 0.445965 +vt 0.660119 0.448062 +vt 0.660128 0.440226 +vt 0.667680 0.443519 +vt 0.866757 0.493943 +vt 0.862350 0.495761 +vt 0.859882 0.491105 +vt 0.152652 0.143407 +vt 0.140889 0.145073 +vt 0.163858 0.144921 +vt 0.839524 0.374778 +vt 0.843898 0.377002 +vt 0.842706 0.380149 +vt 0.838419 0.377832 +vt 0.063964 0.649738 +vt 0.068609 0.647984 +vt 0.130932 0.151556 +vt 0.066819 0.661027 +vt 0.071461 0.659407 +vt 0.797282 0.498317 +vt 0.800362 0.487352 +vt 0.801962 0.487426 +vt 0.798736 0.499015 +vt 0.793062 0.508788 +vt 0.792177 0.507548 +vt 0.791685 0.505071 +vt 0.794337 0.497929 +vt 0.796763 0.487186 +vt 0.798303 0.476151 +vt 0.795335 0.476266 +vt 0.793351 0.468911 +vt 0.794069 0.466490 +vt 0.795064 0.465336 +vt 0.799816 0.475589 +vt 0.730426 0.452911 +vt 0.730000 0.448882 +vt 0.858209 0.485476 +vt 0.855286 0.490895 +vt 0.849289 0.490622 +vt 0.854547 0.482406 +vt 0.858535 0.478703 +vt 0.862372 0.479607 +vt 0.730954 0.458664 +vt 0.857704 0.496559 +vt 0.861315 0.502782 +vt 0.857411 0.503333 +vt 0.853778 0.499283 +vt 0.863673 0.472599 +vt 0.858503 0.473247 +vt 0.858112 0.468251 +vt 0.861912 0.466208 +vt 0.861973 0.509880 +vt 0.859637 0.516083 +vt 0.856039 0.513704 +vt 0.856883 0.508764 +vt 0.059362 0.682194 +vt 0.058553 0.678736 +vt 0.856890 0.462193 +vt 0.853727 0.466466 +vt 0.849257 0.466325 +vt 0.851870 0.461207 +vt 0.868427 0.362442 +vt 0.861827 0.368721 +vt 0.776888 0.457494 +vt 0.785662 0.457141 +vt 0.786108 0.462400 +vt 0.778400 0.461981 +vt 0.772151 0.463278 +vt 0.768557 0.460897 +vt 0.057225 0.670449 +vt 0.056821 0.661331 +vt 0.856502 0.371756 +vt 0.788161 0.513547 +vt 0.782946 0.516083 +vt 0.783874 0.510888 +vt 0.788560 0.508643 +vt 0.852158 0.373999 +vt 0.790622 0.460146 +vt 0.790568 0.465066 +vt 0.784225 0.455245 +vt 0.790474 0.458499 +vt 0.775863 0.502379 +vt 0.773854 0.494587 +vt 0.781389 0.486478 +vt 0.783480 0.500710 +vt 0.776160 0.510596 +vt 0.852999 0.474636 +vt 0.768538 0.503682 +vt 0.768772 0.498237 +vt 0.777349 0.470137 +vt 0.770175 0.468165 +vt 0.784780 0.472498 +vt 0.774632 0.477710 +vt 0.851529 0.506879 +vt 0.789687 0.498977 +vt 0.790711 0.486908 +vt 0.790801 0.474795 +vt 0.173919 0.191478 +vt 0.162233 0.201966 +vt 0.156434 0.186508 +vt 0.170215 0.182683 +vt 0.178261 0.174067 +vt 0.181171 0.176710 +vt 0.146533 0.201753 +vt 0.131357 0.195400 +vt 0.131810 0.191494 +vt 0.143537 0.192692 +vt 0.183571 0.164083 +vt 0.180580 0.164007 +vt 0.178584 0.154649 +vt 0.182515 0.153731 +vt 0.861069 0.350865 +vt 0.848010 0.354940 +vt 0.841505 0.349055 +vt 0.857451 0.346360 +vt 0.189310 0.163945 +vt 0.121243 0.187466 +vt 0.116830 0.191138 +vt 0.109633 0.182812 +vt 0.115229 0.178974 +vt 0.118794 0.177080 +vt 0.123446 0.185441 +vt 0.839084 0.359732 +vt 0.179401 0.145790 +vt 0.173685 0.148142 +vt 0.175112 0.136510 +vt 0.830346 0.366571 +vt 0.822748 0.360131 +vt 0.831473 0.353477 +vt 0.112352 0.170945 +vt 0.104542 0.175253 +vt 0.099198 0.166393 +vt 0.109479 0.161134 +vt 0.170530 0.122144 +vt 0.184263 0.119268 +vt 0.914473 0.516083 +vt 0.907560 0.502295 +vt 0.914571 0.498319 +vt 0.919834 0.510321 +vt 0.103480 0.147300 +vt 0.091244 0.154167 +vt 0.799989 0.356155 +vt 0.789344 0.339276 +vt 0.906055 0.483928 +vt 0.911497 0.465608 +vt 0.919884 0.463204 +vt 0.914457 0.480830 +vt 0.898384 0.507546 +vt 0.895584 0.488692 +vt 0.895900 0.880841 +vt 0.881548 0.882602 +vt 0.883908 0.897682 +vt 0.898813 0.895218 +vt 0.917219 0.894166 +vt 0.914466 0.880849 +vt 0.878967 0.861452 +vt 0.819760 0.340504 +vt 0.811360 0.346958 +vt 0.799956 0.330845 +vt 0.808109 0.325190 +vt 0.815631 0.321217 +vt 0.828699 0.336130 +vt 0.900615 0.905667 +vt 0.885395 0.908551 +vt 0.884329 0.919352 +vt 0.900294 0.915487 +vt 0.918585 0.912385 +vt 0.918518 0.903978 +vt 0.841686 0.333173 +vt 0.824349 0.317828 +vt 0.896838 0.927979 +vt 0.879026 0.934230 +vt 0.110362 0.197227 +vt 0.103738 0.212861 +vt 0.105615 0.203955 +vt 0.917644 0.921488 +vt 0.117576 0.045606 +vt 0.127140 0.042743 +vt 0.104511 0.048111 +vt 0.104499 0.048078 +vt 0.116707 0.045444 +vt 0.125596 0.042665 +vt 0.093023 0.054818 +vt 0.093570 0.054124 +vt 0.083936 0.058953 +vt 0.085047 0.057877 +vt 0.128126 0.034962 +vt 0.123704 0.026765 +vt 0.126563 0.036181 +vt 0.122477 0.029418 +vt 0.906340 0.461405 +vt 0.906162 0.458615 +vt 0.912849 0.460101 +vt 0.914427 0.463205 +vt 0.078075 0.053740 +vt 0.076014 0.044657 +vt 0.895593 0.457945 +vt 0.897040 0.455401 +vt 0.117044 0.022653 +vt 0.109492 0.021548 +vt 0.116205 0.025799 +vt 0.939028 0.450052 +vt 0.941472 0.451665 +vt 0.942413 0.446822 +vt 0.939920 0.445120 +vt 0.879266 0.968901 +vt 0.882321 0.967093 +vt 0.883275 0.971722 +vt 0.880540 0.973729 +vt 0.078326 0.037180 +vt 0.083287 0.031379 +vt 0.878967 0.962209 +vt 0.882053 0.960874 +vt 0.102393 0.022366 +vt 0.095652 0.024497 +vt 0.879358 0.941156 +vt 0.879727 0.934230 +vt 0.882737 0.935730 +vt 0.882178 0.941990 +vt 0.881824 0.948286 +vt 0.879151 0.948214 +vt 0.878974 0.955272 +vt 0.881835 0.954593 +vt 0.089172 0.027327 +vt 0.887901 0.942398 +vt 0.887499 0.948441 +vt 0.947978 0.445934 +vt 0.942393 0.444571 +vt 0.948219 0.448064 +vt 0.888631 0.936363 +vt 0.894718 0.935457 +vt 0.893892 0.941804 +vt 0.893435 0.948602 +vt 0.887572 0.954497 +vt 0.893522 0.955415 +vt 0.894002 0.961797 +vt 0.887974 0.960562 +vt 0.947730 0.452567 +vt 0.940152 0.458933 +vt 0.946736 0.459162 +vt 0.951244 0.459596 +vt 0.953522 0.454794 +vt 0.953371 0.451702 +vt 0.888556 0.966631 +vt 0.894726 0.967304 +vt 0.894038 0.970368 +vt 0.889576 0.971200 +vt 0.945460 0.467568 +vt 0.939037 0.468463 +vt 0.629880 0.969789 +vt 0.635999 0.968976 +vt 0.639030 0.964980 +vt 0.632740 0.965995 +vt 0.624967 0.966620 +vt 0.622910 0.970416 +vt 0.951979 0.465264 +vt 0.906509 0.452045 +vt 0.905498 0.445030 +vt 0.909417 0.446343 +vt 0.912209 0.453639 +vt 0.898522 0.448872 +vt 0.899817 0.444347 +vt 0.630247 0.957258 +vt 0.636571 0.956007 +vt 0.632533 0.944268 +vt 0.626256 0.945767 +vt 0.506511 0.955486 +vt 0.508193 0.943383 +vt 0.508837 0.943383 +vt 0.507307 0.954730 +vt 0.506315 0.963366 +vt 0.505856 0.964564 +vt 0.506512 0.931279 +vt 0.507307 0.932036 +vt 0.624625 0.933713 +vt 0.630831 0.931971 +vt 0.628972 0.922856 +vt 0.622902 0.924791 +vt 0.505858 0.922202 +vt 0.506316 0.923399 +vt 0.513862 0.952461 +vt 0.514938 0.943383 +vt 0.522687 0.943383 +vt 0.522067 0.950191 +vt 0.520369 0.956558 +vt 0.512421 0.959773 +vt 0.513863 0.934305 +vt 0.512422 0.926993 +vt 0.520370 0.930208 +vt 0.522068 0.936575 +vt 0.512396 0.963556 +vt 0.509461 0.966581 +vt 0.517752 0.962043 +vt 0.954567 0.460384 +vt 0.901245 0.440905 +vt 0.895584 0.440526 +vt 0.512397 0.923210 +vt 0.517753 0.924724 +vt 0.509462 0.920184 +vt 0.222064 0.240841 +vt 0.222821 0.249320 +vt 0.222881 0.232969 +vt 0.226081 0.232974 +vt 0.225798 0.240384 +vt 0.227148 0.248110 +vt 0.142738 0.270602 +vt 0.139625 0.272714 +vt 0.134538 0.267319 +vt 0.136945 0.265210 +vt 0.147745 0.277486 +vt 0.143690 0.279421 +vt 0.225439 0.258450 +vt 0.230131 0.267727 +vt 0.230187 0.256391 +vt 0.234941 0.264665 +vt 0.151779 0.286086 +vt 0.146848 0.287657 +vt 0.154346 0.296160 +vt 0.148709 0.297017 +vt 0.237112 0.276645 +vt 0.246033 0.283913 +vt 0.241435 0.272374 +vt 0.249399 0.278616 +vt 0.154954 0.307469 +vt 0.148888 0.307096 +vt 0.153014 0.318811 +vt 0.146995 0.317035 +vt 0.224987 0.225661 +vt 0.231398 0.217266 +vt 0.234084 0.217516 +vt 0.227943 0.225643 +vt 0.130551 0.261091 +vt 0.128313 0.263022 +vt 0.118342 0.260940 +vt 0.120200 0.258985 +vt 0.245131 0.206133 +vt 0.262050 0.197030 +vt 0.263249 0.199958 +vt 0.247206 0.207716 +vt 0.102533 0.259632 +vt 0.102012 0.262189 +vt 0.084825 0.266898 +vt 0.083802 0.263903 +vt 0.278017 0.194724 +vt 0.294460 0.197634 +vt 0.293211 0.200587 +vt 0.278153 0.197957 +vt 0.070259 0.272669 +vt 0.072283 0.275194 +vt 0.062670 0.287080 +vt 0.059787 0.285677 +vt 0.724391 0.447821 +vt 0.724582 0.461230 +vt 0.717126 0.461917 +vt 0.717867 0.447033 +vt 0.312805 0.204177 +vt 0.328682 0.213116 +vt 0.323860 0.215070 +vt 0.309719 0.206721 +vt 0.729128 0.447039 +vt 0.730000 0.459378 +vt 0.050274 0.302672 +vt 0.054271 0.302559 +vt 0.049111 0.318149 +vt 0.044194 0.319848 +vt 0.337722 0.223213 +vt 0.341683 0.233413 +vt 0.335328 0.233613 +vt 0.331817 0.224344 +vt 0.044027 0.333399 +vt 0.049218 0.330367 +vt 0.052670 0.339659 +vt 0.047752 0.343688 +vt 0.342324 0.242658 +vt 0.337131 0.251788 +vt 0.331651 0.250013 +vt 0.336133 0.241949 +vt 0.053351 0.351073 +vt 0.057547 0.346467 +vt 0.066228 0.349593 +vt 0.063267 0.354534 +vt 0.323590 0.261640 +vt 0.307825 0.271303 +vt 0.070182 0.528500 +vt 0.068729 0.546805 +vt 0.063510 0.548440 +vt 0.065681 0.531727 +vt 0.067987 0.516996 +vt 0.071466 0.512230 +vt 0.668463 0.448377 +vt 0.673189 0.448566 +vt 0.673188 0.440540 +vt 0.667680 0.440226 +vt 0.079946 0.353048 +vt 0.098178 0.349957 +vt 0.081089 0.347837 +vt 0.295956 0.279868 +vt 0.286243 0.286281 +vt 0.069945 0.560875 +vt 0.071457 0.572143 +vt 0.067516 0.571665 +vt 0.065208 0.561338 +vt 0.112751 0.348602 +vt 0.110968 0.343491 +vt 0.121684 0.342263 +vt 0.124286 0.347043 +vt 0.063372 0.551295 +vt 0.063510 0.565417 +vt 0.058159 0.564289 +vt 0.058613 0.551288 +vt 0.276943 0.289490 +vt 0.267297 0.289980 +vt 0.267947 0.284176 +vt 0.276587 0.283875 +vt 0.285058 0.280970 +vt 0.133401 0.343343 +vt 0.129976 0.338879 +vt 0.136682 0.333423 +vt 0.140989 0.337368 +vt 0.256544 0.288239 +vt 0.258560 0.282486 +vt 0.147943 0.328984 +vt 0.142641 0.325976 +vt 0.260121 0.279107 +vt 0.268353 0.280591 +vt 0.252006 0.275783 +vt 0.254233 0.274183 +vt 0.261453 0.276916 +vt 0.268636 0.278116 +vt 0.139243 0.324457 +vt 0.136798 0.323683 +vt 0.140438 0.316876 +vt 0.143169 0.316616 +vt 0.134018 0.330990 +vt 0.132177 0.329312 +vt 0.276214 0.280369 +vt 0.284171 0.277888 +vt 0.275834 0.277918 +vt 0.283433 0.275921 +vt 0.127951 0.335994 +vt 0.126625 0.333897 +vt 0.120325 0.339358 +vt 0.119588 0.337392 +vt 0.292688 0.272594 +vt 0.302963 0.265523 +vt 0.304820 0.266885 +vt 0.293937 0.274846 +vt 0.291824 0.271726 +vt 0.301679 0.265689 +vt 0.110428 0.340973 +vt 0.110508 0.339751 +vt 0.097534 0.344653 +vt 0.098038 0.342405 +vt 0.099114 0.341686 +vt 0.316192 0.257712 +vt 0.327346 0.249756 +vt 0.319302 0.258466 +vt 0.313669 0.258167 +vt 0.323630 0.250259 +vt 0.082936 0.345223 +vt 0.085135 0.343907 +vt 0.069301 0.346568 +vt 0.072431 0.344502 +vt 0.331399 0.242253 +vt 0.330693 0.234629 +vt 0.327394 0.243063 +vt 0.326918 0.236051 +vt 0.061313 0.343582 +vt 0.064863 0.341558 +vt 0.056830 0.337375 +vt 0.060609 0.335963 +vt 0.327572 0.226311 +vt 0.320358 0.217851 +vt 0.324158 0.228696 +vt 0.317517 0.221044 +vt 0.053709 0.329057 +vt 0.057850 0.328608 +vt 0.053578 0.317940 +vt 0.057819 0.318476 +vt 0.307373 0.209799 +vt 0.292167 0.203726 +vt 0.305396 0.213143 +vt 0.013693 0.870082 +vt 0.016342 0.872796 +vt 0.009710 0.884822 +vt 0.006799 0.883158 +vt 0.064446 0.452817 +vt 0.067800 0.451047 +vt 0.071424 0.462214 +vt 0.068112 0.464839 +vt 0.058063 0.303333 +vt 0.065522 0.288757 +vt 0.061899 0.438256 +vt 0.065180 0.437566 +vt 0.278293 0.201202 +vt 0.264439 0.203041 +vt 0.002336 0.895925 +vt 0.005407 0.896551 +vt 0.003301 0.909528 +vt 0.000128 0.909930 +vt 0.061571 0.424736 +vt 0.064687 0.425082 +vt 0.074312 0.277730 +vt 0.085956 0.270003 +vt 0.063770 0.410729 +vt 0.066666 0.412086 +vt 0.249296 0.210056 +vt 0.236801 0.219104 +vt 0.000000 0.926723 +vt 0.003259 0.925292 +vt 0.005088 0.939294 +vt 0.001771 0.941494 +vt 0.068803 0.394708 +vt 0.071466 0.397070 +vt 0.101977 0.265327 +vt 0.117340 0.263922 +vt 0.058835 0.686190 +vt 0.056821 0.691838 +vt 0.059541 0.694017 +vt 0.061234 0.688743 +vt 0.005260 0.949440 +vt 0.009344 0.953115 +vt 0.007414 0.955807 +vt 0.003499 0.951951 +vt 0.230890 0.227039 +vt 0.008597 0.946983 +vt 0.012717 0.950527 +vt 0.057039 0.697767 +vt 0.059866 0.699821 +vt 0.127011 0.266012 +vt 0.133084 0.270354 +vt 0.057920 0.704209 +vt 0.060864 0.706138 +vt 0.244496 0.270483 +vt 0.238210 0.263776 +vt 0.060573 0.402886 +vt 0.061147 0.412080 +vt 0.057701 0.412276 +vt 0.057233 0.403962 +vt 0.056564 0.396662 +vt 0.059655 0.394708 +vt 0.145339 0.307685 +vt 0.143047 0.308755 +vt 0.143911 0.300289 +vt 0.145663 0.298497 +vt 0.233763 0.256230 +vt 0.230872 0.248525 +vt 0.061183 0.421537 +vt 0.060866 0.430623 +vt 0.056701 0.429698 +vt 0.057314 0.421111 +vt 0.144049 0.289888 +vt 0.142316 0.292449 +vt 0.139387 0.285359 +vt 0.141158 0.282183 +vt 0.229254 0.241342 +vt 0.012899 0.955079 +vt 0.010901 0.958255 +vt 0.016377 0.952096 +vt 0.060383 0.438704 +vt 0.056493 0.437269 +vt 0.057895 0.711396 +vt 0.061191 0.712952 +vt 0.137651 0.275708 +vt 0.057370 0.719459 +vt 0.060903 0.720513 +vt 0.680651 0.931251 +vt 0.680041 0.927497 +vt 0.681278 0.943843 +vt 0.699152 0.941040 +vt 0.695058 0.930713 +vt 0.694565 0.928321 +vt 0.420973 0.738616 +vt 0.433823 0.739113 +vt 0.441374 0.759007 +vt 0.426547 0.749590 +vt 0.419120 0.727596 +vt 0.432673 0.726965 +vt 0.682507 0.966504 +vt 0.684862 0.988216 +vt 0.695910 0.979706 +vt 0.698186 0.963326 +vt 0.407858 0.849184 +vt 0.415952 0.862408 +vt 0.399402 0.867861 +vt 0.387685 0.861117 +vt 0.436885 0.760595 +vt 0.447794 0.773719 +vt 0.429105 0.839496 +vt 0.438111 0.850402 +vt 0.688866 0.997962 +vt 0.449041 0.661077 +vt 0.450564 0.641065 +vt 0.204365 0.916029 +vt 0.203991 0.895856 +vt 0.216765 0.893927 +vt 0.220451 0.909419 +vt 0.226149 0.924235 +vt 0.215103 0.932031 +vt 0.378467 0.878040 +vt 0.395094 0.883140 +vt 0.392852 0.898906 +vt 0.379955 0.898162 +vt 0.455084 0.791050 +vt 0.458458 0.810835 +vt 0.447828 0.620745 +vt 0.443981 0.598710 +vt 0.213898 0.873320 +vt 0.228000 0.848235 +vt 0.243636 0.854870 +vt 0.227531 0.878936 +vt 0.391897 0.919689 +vt 0.404954 0.912839 +vt 0.423209 0.935319 +vt 0.408252 0.943367 +vt 0.749095 0.548931 +vt 0.767028 0.551076 +vt 0.735804 0.547325 +vt 0.442174 0.573551 +vt 0.441500 0.549223 +vt 0.456899 0.553145 +vt 0.456397 0.575919 +vt 0.240215 0.820416 +vt 0.253163 0.821160 +vt 0.782836 0.554509 +vt 0.789395 0.572295 +vt 0.801233 0.576746 +vt 0.795603 0.558016 +vt 0.776535 0.566478 +vt 0.441054 0.529680 +vt 0.440197 0.517730 +vt 0.059391 0.748977 +vt 0.059697 0.729792 +vt 0.071441 0.726806 +vt 0.071466 0.744451 +vt 0.456207 0.535413 +vt 0.804415 0.560384 +vt 0.809382 0.578647 +vt 0.822218 0.581943 +vt 0.818402 0.564489 +vt 0.893292 0.198498 +vt 0.922132 0.191321 +vt 0.864190 0.211793 +vt 0.855600 0.196568 +vt 0.883843 0.185525 +vt 0.910831 0.179816 +vt 0.846692 0.573208 +vt 0.848116 0.590579 +vt 0.878136 0.597390 +vt 0.878697 0.579125 +vt 0.341372 0.479322 +vt 0.323411 0.491599 +vt 0.334557 0.487251 +vt 0.353283 0.474792 +vt 0.844632 0.559867 +vt 0.847674 0.231901 +vt 0.838548 0.252753 +vt 0.827021 0.234525 +vt 0.837606 0.214532 +vt 0.320214 0.511247 +vt 0.303059 0.515397 +vt 0.305121 0.537768 +vt 0.324024 0.534526 +vt 0.336112 0.530121 +vt 0.331872 0.506637 +vt 0.308712 0.497514 +vt 0.831612 0.268279 +vt 0.826974 0.280826 +vt 0.819433 0.270607 +vt 0.821009 0.251654 +vt 0.327081 0.557697 +vt 0.309780 0.560976 +vt 0.319448 0.588328 +vt 0.330743 0.582419 +vt 0.341087 0.581345 +vt 0.338161 0.554872 +vt 0.089872 0.877857 +vt 0.101189 0.881919 +vt 0.101745 0.899454 +vt 0.090128 0.897768 +vt 0.273448 0.727090 +vt 0.268000 0.708117 +vt 0.264039 0.708897 +vt 0.269059 0.731167 +vt 0.278773 0.748593 +vt 0.282566 0.743284 +vt 0.084977 0.859311 +vt 0.097167 0.864360 +vt 0.265845 0.688495 +vt 0.268141 0.670053 +vt 0.262617 0.666579 +vt 0.260237 0.686387 +vt 0.267358 0.708243 +vt 0.266634 0.690781 +vt 0.269104 0.673172 +vt 0.121292 0.884963 +vt 0.143287 0.887007 +vt 0.143668 0.905535 +vt 0.121981 0.902389 +vt 0.117483 0.866957 +vt 0.140036 0.867872 +vt 0.116369 0.918901 +vt 0.107600 0.935083 +vt 0.087384 0.931800 +vt 0.096230 0.916107 +vt 0.138038 0.923191 +vt 0.129483 0.940614 +vt 0.953309 0.379981 +vt 0.958624 0.381492 +vt 0.959426 0.360450 +vt 0.952838 0.360296 +vt 0.160279 0.888071 +vt 0.159638 0.907852 +vt 0.158937 0.867874 +vt 0.954356 0.399805 +vt 0.957116 0.402423 +vt 0.154632 0.927000 +vt 0.147606 0.945982 +vt 0.954226 0.340655 +vt 0.954403 0.342046 +vt 0.951782 0.360272 +vt 0.959606 0.339393 +vt 0.959076 0.318415 +vt 0.956196 0.320901 +vt 0.969257 0.382875 +vt 0.966750 0.404508 +vt 0.970622 0.360711 +vt 0.970292 0.338508 +vt 0.968797 0.316781 +vt 0.660423 0.415937 +vt 0.662165 0.407233 +vt 0.963657 0.425119 +vt 0.954815 0.423147 +vt 0.957233 0.434273 +vt 0.962535 0.440226 +vt 0.966668 0.296049 +vt 0.957744 0.297607 +vt 0.966251 0.280906 +vt 0.960678 0.286605 +vt 0.671188 0.397125 +vt 0.686346 0.385596 +vt 0.663184 0.396198 +vt 0.671005 0.382602 +vt 0.161293 0.822480 +vt 0.158447 0.830985 +vt 0.137497 0.831704 +vt 0.144505 0.820937 +vt 0.969892 0.275946 +vt 0.961037 0.284568 +vt 0.982678 0.275614 +vt 0.924127 0.750352 +vt 0.906069 0.743931 +vt 0.926143 0.769286 +vt 0.943484 0.781638 +vt 0.938647 0.757589 +vt 0.706494 0.372629 +vt 0.728825 0.358554 +vt 0.122471 0.820416 +vt 0.114058 0.832445 +vt 0.091068 0.832677 +vt 0.098055 0.820441 +vt 0.927137 0.793171 +vt 0.946694 0.807666 +vt 0.928119 0.819472 +vt 0.949175 0.833020 +vt 0.113734 0.848705 +vt 0.137056 0.848393 +vt 0.092283 0.847631 +vt 0.098819 0.951517 +vt 0.077948 0.946451 +vt 0.121092 0.958443 +vt 0.116773 0.974569 +vt 0.094510 0.967199 +vt 0.072534 0.960444 +vt 0.157994 0.847481 +vt 0.168179 0.834464 +vt 0.171727 0.846161 +vt 0.954693 0.419865 +vt 0.957469 0.300879 +vt 0.140907 0.965267 +vt 0.136654 0.981211 +vt 0.740758 0.313008 +vt 0.748783 0.299443 +vt 0.762781 0.311537 +vt 0.748821 0.322444 +vt 0.076963 0.844390 +vt 0.071466 0.831872 +vt 0.731342 0.311754 +vt 0.734634 0.295316 +vt 0.276045 0.654618 +vt 0.272733 0.656298 +vt 0.278310 0.640293 +vt 0.286933 0.640651 +vt 0.293647 0.640774 +vt 0.276141 0.652414 +vt 0.750531 0.343701 +vt 0.769215 0.330522 +vt 0.074123 0.820536 +vt 0.930100 0.845650 +vt 0.951821 0.855052 +vt 0.932309 0.868370 +vt 0.954061 0.872624 +vt 0.800682 0.311915 +vt 0.792309 0.315585 +vt 0.781557 0.295190 +vt 0.796321 0.296016 +vt 0.806554 0.297633 +vt 0.808337 0.309367 +vt 0.933968 0.904623 +vt 0.932566 0.912416 +vt 0.942565 0.917191 +vt 0.949067 0.909375 +vt 0.958130 0.897186 +vt 0.934676 0.895644 +vt 0.782481 0.321469 +vt 0.772542 0.304462 +vt 0.933979 0.884297 +vt 0.955324 0.884600 +vt 0.816016 0.306847 +vt 0.822042 0.302068 +vt 0.814251 0.298005 +vt 0.819436 0.295714 +vt 0.931185 0.920207 +vt 0.938828 0.923461 +vt 0.337451 0.638409 +vt 0.350090 0.637204 +vt 0.339942 0.632136 +vt 0.334298 0.628445 +vt 0.331357 0.635712 +vt 0.824739 0.292742 +vt 0.821782 0.287450 +vt 0.490615 0.932841 +vt 0.484957 0.930718 +vt 0.479303 0.958206 +vt 0.491665 0.961315 +vt 0.501974 0.959945 +vt 0.501965 0.930481 +vt 0.336368 0.610352 +vt 0.331362 0.613737 +vt 0.660119 0.309235 +vt 0.660104 0.353457 +vt 0.633416 0.350086 +vt 0.635412 0.304462 +vt 0.633775 0.265411 +vt 0.654528 0.272226 +vt 0.089049 0.788087 +vt 0.111286 0.820416 +vt 0.121888 0.818713 +vt 0.099865 0.785571 +vt 0.082773 0.744605 +vt 0.071466 0.746833 +vt 0.072089 0.701991 +vt 0.083051 0.700217 +vt 0.090859 0.661199 +vt 0.080403 0.663645 +vt 0.080131 0.659458 +vt 0.071759 0.700386 +vt 0.071744 0.746778 +vt 0.120907 0.779242 +vt 0.105106 0.740206 +vt 0.141844 0.811226 +vt 0.171926 0.798940 +vt 0.153599 0.769075 +vt 0.140140 0.733304 +vt 0.104921 0.698094 +vt 0.139027 0.695101 +vt 0.144658 0.660518 +vt 0.112164 0.660558 +vt 0.199368 0.755047 +vt 0.189550 0.723571 +vt 0.688218 0.221010 +vt 0.663100 0.168592 +vt 0.674081 0.164798 +vt 0.697294 0.212780 +vt 0.725258 0.259946 +vt 0.713085 0.265262 +vt 0.240986 0.742094 +vt 0.234484 0.714719 +vt 0.186696 0.690723 +vt 0.230118 0.686924 +vt 0.236381 0.658388 +vt 0.188682 0.659877 +vt 0.261227 0.735152 +vt 0.256090 0.710463 +vt 0.724923 0.287653 +vt 0.733311 0.282795 +vt 0.727747 0.300231 +vt 0.251480 0.685671 +vt 0.255122 0.664650 +vt 0.752668 0.289985 +vt 0.754427 0.279289 +vt 0.274743 0.651276 +vt 0.270704 0.647103 +vt 0.979382 0.875169 +vt 0.973663 0.866852 +vt 0.298656 0.641554 +vt 0.301752 0.639404 +vt 0.816504 0.185036 +vt 0.839428 0.168477 +vt 0.801911 0.206932 +vt 0.768301 0.174659 +vt 0.795791 0.154009 +vt 0.824274 0.137729 +vt 0.269890 0.512936 +vt 0.236219 0.509160 +vt 0.223967 0.532489 +vt 0.269419 0.535388 +vt 0.280712 0.498211 +vt 0.252154 0.496833 +vt 0.596468 0.293570 +vt 0.590934 0.344720 +vt 0.538294 0.338071 +vt 0.546333 0.281264 +vt 0.555237 0.233844 +vt 0.600701 0.250262 +vt 0.081785 0.692371 +vt 0.087339 0.646944 +vt 0.583575 0.395639 +vt 0.576906 0.438640 +vt 0.528787 0.443236 +vt 0.531955 0.395092 +vt 0.624004 0.394773 +vt 0.607145 0.660290 +vt 0.610074 0.718717 +vt 0.563883 0.720865 +vt 0.562981 0.662217 +vt 0.566026 0.612063 +vt 0.606344 0.610346 +vt 0.644051 0.608593 +vt 0.649515 0.658320 +vt 0.654887 0.716634 +vt 0.699153 0.701395 +vt 0.699265 0.746724 +vt 0.709564 0.743489 +vt 0.709676 0.703229 +vt 0.612579 0.777164 +vt 0.616416 0.826967 +vt 0.576112 0.828999 +vt 0.568427 0.779344 +vt 0.654949 0.775194 +vt 0.690242 0.656426 +vt 0.699153 0.714576 +vt 0.678507 0.606991 +vt 0.393130 0.260769 +vt 0.405593 0.212019 +vt 0.442308 0.216561 +vt 0.433145 0.265696 +vt 0.427654 0.324095 +vt 0.386060 0.318841 +vt 0.709825 0.794286 +vt 0.735175 0.839216 +vt 0.743216 0.821828 +vt 0.718670 0.783792 +vt 0.695676 0.773301 +vt 0.378467 0.376847 +vt 0.418451 0.382025 +vt 0.488053 0.272251 +vt 0.481133 0.330850 +vt 0.497315 0.223224 +vt 0.473264 0.389330 +vt 0.470044 0.439120 +vt 0.415108 0.431895 +vt 0.474227 0.541769 +vt 0.476423 0.557823 +vt 0.454986 0.524205 +vt 0.472609 0.529980 +vt 0.494891 0.531910 +vt 0.498065 0.545078 +vt 0.502640 0.560706 +vt 0.812887 0.599436 +vt 0.809330 0.623674 +vt 0.821315 0.624504 +vt 0.824497 0.601824 +vt 0.805512 0.599397 +vt 0.801155 0.625863 +vt 0.530677 0.541669 +vt 0.538115 0.559246 +vt 0.523659 0.526849 +vt 0.555009 0.520249 +vt 0.566194 0.536231 +vt 0.576255 0.556147 +vt 0.793107 0.652287 +vt 0.775552 0.683446 +vt 0.795205 0.678556 +vt 0.808748 0.650355 +vt 0.780872 0.656043 +vt 0.759058 0.688445 +vt 0.598749 0.533455 +vt 0.610464 0.554115 +vt 0.585039 0.517559 +vt 0.612817 0.516930 +vt 0.627922 0.532099 +vt 0.640880 0.552701 +vt 0.768000 0.715326 +vt 0.769452 0.747061 +vt 0.790689 0.738603 +vt 0.789098 0.708287 +vt 0.750107 0.721580 +vt 0.751501 0.754621 +vt 0.653293 0.530919 +vt 0.667637 0.551457 +vt 0.637411 0.516512 +vt 0.471536 0.142575 +vt 0.490250 0.129689 +vt 0.508242 0.136932 +vt 0.499588 0.146881 +vt 0.484663 0.166218 +vt 0.454617 0.161788 +vt 0.778910 0.777789 +vt 0.799460 0.802749 +vt 0.812235 0.789485 +vt 0.800240 0.768554 +vt 0.760721 0.786740 +vt 0.782459 0.814074 +vt 0.536553 0.153838 +vt 0.535952 0.137750 +vt 0.571747 0.143766 +vt 0.574814 0.165279 +vt 0.571304 0.185264 +vt 0.527152 0.173492 +vt 0.834188 0.817183 +vt 0.821407 0.832759 +vt 0.490795 0.516083 +vt 0.486577 0.494709 +vt 0.532270 0.494286 +vt 0.530700 0.514516 +vt 0.874757 0.822490 +vt 0.876425 0.800766 +vt 0.839164 0.796139 +vt 0.512211 0.193462 +vt 0.564170 0.204982 +vt 0.462525 0.186424 +vt 0.477071 0.471651 +vt 0.443614 0.491189 +vt 0.427196 0.466113 +vt 0.530262 0.473414 +vt 0.429362 0.181980 +vt 0.673507 0.574407 +vt 0.643714 0.575792 +vt 0.738138 0.785009 +vt 0.729257 0.749775 +vt 0.761344 0.816940 +vt 0.610223 0.577350 +vt 0.573325 0.579065 +vt 0.728664 0.713753 +vt 0.739736 0.678541 +vt 0.722567 0.665053 +vt 0.533311 0.580926 +vt 0.523739 0.613555 +vt 0.487824 0.613553 +vt 0.497542 0.581453 +vt 0.765852 0.645744 +vt 0.789894 0.616661 +vt 0.779022 0.602963 +vt 0.751798 0.631002 +vt 0.266070 0.822936 +vt 0.258721 0.856029 +vt 0.473379 0.579168 +vt 0.466624 0.610784 +vt 0.794745 0.592595 +vt 0.785891 0.582827 +vt 0.516872 0.663982 +vt 0.515486 0.723115 +vt 0.464289 0.725495 +vt 0.478536 0.663240 +vt 0.522353 0.781864 +vt 0.533869 0.831436 +vt 0.498109 0.834771 +vt 0.484251 0.786160 +vt 0.238743 0.902155 +vt 0.253848 0.898540 +vt 0.266303 0.938052 +vt 0.243131 0.928139 +vt 0.413979 0.888686 +vt 0.429352 0.890894 +vt 0.457689 0.657646 +vt 0.448894 0.699748 +vt 0.438364 0.694255 +vt 0.450547 0.652551 +vt 0.260370 0.902874 +vt 0.464013 0.793664 +vt 0.477258 0.839496 +vt 0.435447 0.885976 +vt 0.438123 0.932775 +vt 0.457375 0.799400 +vt 0.451350 0.752560 +vt 0.442559 0.717082 +vt 0.444439 0.726418 +vt 0.432691 0.714764 +vt 0.443433 0.735889 +vt 0.871150 0.164485 +vt 0.899497 0.165351 +vt 0.860222 0.139200 +vt 0.477389 0.521428 +vt 0.504190 0.518541 +vt 0.847970 0.615673 +vt 0.844008 0.642763 +vt 0.873723 0.661788 +vt 0.877089 0.628960 +vt 0.856068 0.113490 +vt 0.818740 0.114529 +vt 0.812606 0.092330 +vt 0.852353 0.088528 +vt 0.883749 0.084029 +vt 0.887593 0.115897 +vt 0.890846 0.145015 +vt 0.833986 0.666124 +vt 0.824003 0.688783 +vt 0.859756 0.705627 +vt 0.866203 0.683826 +vt 0.842742 0.065487 +vt 0.810950 0.071003 +vt 0.797415 0.054150 +vt 0.826859 0.044502 +vt 0.856320 0.030748 +vt 0.873324 0.055444 +vt 0.820158 0.713763 +vt 0.822791 0.740752 +vt 0.852813 0.747071 +vt 0.850041 0.725041 +vt 0.804325 0.025705 +vt 0.780633 0.034662 +vt 0.751694 0.023926 +vt 0.785603 0.008031 +vt 0.812357 0.000000 +vt 0.832739 0.010547 +vt 0.832246 0.769436 +vt 0.857055 0.773709 +vt 0.711345 0.200677 +vt 0.737251 0.237162 +vt 0.687836 0.157310 +vt 0.701074 0.149330 +vt 0.725170 0.186884 +vt 0.758965 0.224535 +vt 0.193380 0.634407 +vt 0.197066 0.609394 +vt 0.157718 0.614351 +vt 0.151427 0.635604 +vt 0.234022 0.633769 +vt 0.244978 0.605869 +vt 0.733567 0.173581 +vt 0.738041 0.160225 +vt 0.763053 0.199221 +vt 0.710506 0.144060 +vt 0.716260 0.141625 +vt 0.196017 0.579917 +vt 0.161911 0.590748 +vt 0.233735 0.568375 +vt 0.191770 0.554539 +vt 0.164274 0.566412 +vt 0.795178 0.226940 +vt 0.793451 0.247944 +vt 0.275160 0.563299 +vt 0.285813 0.592297 +vt 0.793871 0.272831 +vt 0.810018 0.283069 +vt 0.773964 0.263949 +vt 0.300074 0.618016 +vt 0.278791 0.625592 +vt 0.314712 0.634536 +vt 0.065920 0.600575 +vt 0.067626 0.625538 +vt 0.066039 0.602368 +vt 0.319074 0.615772 +vt 0.756076 0.262012 +vt 0.262875 0.635795 +vt 0.816620 0.289075 +vt 0.063576 0.609576 +vt 0.063028 0.626211 +vt 0.328142 0.621567 +vt 0.064214 0.601702 +vt 0.068014 0.572143 +vt 0.606754 0.183038 +vt 0.605665 0.163839 +vt 0.628061 0.180297 +vt 0.628169 0.200358 +vt 0.634589 0.224787 +vt 0.606338 0.203192 +vt 0.109922 0.573747 +vt 0.101084 0.595920 +vt 0.099476 0.614465 +vt 0.111555 0.588203 +vt 0.118550 0.566554 +vt 0.118652 0.552752 +vt 0.134572 0.533651 +vt 0.122221 0.557446 +vt 0.114950 0.579535 +vt 0.604156 0.222868 +vt 0.632290 0.240443 +vt 0.093664 0.617556 +vt 0.087598 0.632929 +vt 0.111147 0.601133 +vt 0.573440 0.466032 +vt 0.017855 0.399361 +vt 0.028351 0.419633 +vt 0.056493 0.417720 +vt 0.048232 0.394708 +vt 0.697655 0.104702 +vt 0.688970 0.104358 +vt 0.688643 0.054017 +vt 0.698260 0.064582 +vt 0.000040 0.409530 +vt 0.012132 0.428801 +vt 0.131321 0.593690 +vt 0.124865 0.615265 +vt 0.136522 0.571462 +vt 0.676199 0.111813 +vt 0.662370 0.119964 +vt 0.660119 0.081018 +vt 0.672430 0.073494 +vt 0.118645 0.635251 +vt 0.096359 0.636343 +vt 0.648694 0.251029 +vt 0.672520 0.051660 +vt 0.686401 0.031896 +vt 0.665284 0.050704 +vt 0.085894 0.641874 +vt 0.032841 0.971857 +vt 0.039736 0.951721 +vt 0.048007 0.948398 +vt 0.041201 0.970951 +vt 0.034646 0.988866 +vt 0.026465 0.987866 +vt 0.779600 0.920118 +vt 0.780334 0.902977 +vt 0.786794 0.902249 +vt 0.785918 0.917498 +vt 0.787233 0.935126 +vt 0.781404 0.940087 +vt 0.880181 0.586699 +vt 0.886161 0.590018 +vt 0.885303 0.605268 +vt 0.878967 0.603813 +vt 0.175538 0.861875 +vt 0.174385 0.844682 +vt 0.182478 0.846242 +vt 0.183216 0.865304 +vt 0.182770 0.888858 +vt 0.175918 0.883155 +vt 0.884237 0.567063 +vt 0.889466 0.572652 +vt 0.045796 0.926561 +vt 0.053512 0.921100 +vt 0.787287 0.963316 +vt 0.792458 0.956060 +vt 0.802848 0.972561 +vt 0.799069 0.981571 +vt 0.046856 0.903499 +vt 0.053336 0.896246 +vt 0.892714 0.544651 +vt 0.906489 0.527849 +vt 0.909223 0.537229 +vt 0.897029 0.552446 +vt 0.173962 0.908961 +vt 0.179628 0.916527 +vt 0.753991 0.448296 +vt 0.746875 0.453554 +vt 0.747237 0.461917 +vt 0.753980 0.457409 +vt 0.038752 0.889657 +vt 0.043101 0.881024 +vt 0.818573 0.986619 +vt 0.819659 0.976892 +vt 0.838682 0.970392 +vt 0.840604 0.979625 +vt 0.026486 0.885712 +vt 0.028844 0.876349 +vt 0.926440 0.525044 +vt 0.947536 0.534489 +vt 0.944580 0.543445 +vt 0.926416 0.534831 +vt 0.904594 0.519291 +vt 0.926983 0.516083 +vt 0.950439 0.525957 +vt 0.859966 0.961756 +vt 0.863459 0.969518 +vt 0.842521 0.988431 +vt 0.855710 0.954399 +vt 0.868047 0.931253 +vt 0.873730 0.935639 +vt 0.877805 0.941407 +vt 0.964748 0.554437 +vt 0.975464 0.581945 +vt 0.969320 0.585660 +vt 0.959686 0.561264 +vt 0.969098 0.547121 +vt 0.980166 0.576677 +vt 0.019381 0.692825 +vt 0.011364 0.691570 +vt 0.015496 0.666042 +vt 0.021993 0.668394 +vt 0.878967 0.903904 +vt 0.872999 0.903290 +vt 0.530189 0.866580 +vt 0.533873 0.869035 +vt 0.530456 0.899945 +vt 0.527403 0.901333 +vt 0.026253 0.716301 +vt 0.017393 0.716066 +vt 0.977070 0.614070 +vt 0.972140 0.647694 +vt 0.967146 0.643656 +vt 0.971071 0.614003 +vt 0.980846 0.611726 +vt 0.974394 0.648497 +vt 0.041651 0.738079 +vt 0.033029 0.738905 +vt 0.030431 0.710846 +vt 0.044868 0.730687 +vt 0.509877 0.917124 +vt 0.501974 0.920184 +vt 0.510078 0.896886 +vt 0.519173 0.890011 +vt 0.056564 0.752631 +vt 0.048431 0.754154 +vt 0.963247 0.679701 +vt 0.950885 0.705560 +vt 0.949212 0.694282 +vt 0.959753 0.671980 +vt 0.964068 0.683217 +vt 0.950080 0.711395 +vt 0.531831 0.925033 +vt 0.526413 0.932881 +vt 0.536264 0.895495 +vt 0.542776 0.889974 +vt 0.538284 0.917280 +vt 0.537148 0.939477 +vt 0.531235 0.949345 +vt 0.526591 0.960502 +vt 0.954759 0.056654 +vt 0.955709 0.032352 +vt 0.960983 0.042576 +vt 0.960707 0.064800 +vt 0.963456 0.092337 +vt 0.957308 0.086413 +vt 0.059021 0.533406 +vt 0.057518 0.551288 +vt 0.061467 0.544800 +vt 0.063510 0.525765 +vt 0.061766 0.512230 +vt 0.057495 0.520813 +vt 0.539667 0.867791 +vt 0.537949 0.843401 +vt 0.543334 0.844829 +vt 0.880872 0.455510 +vt 0.885488 0.473971 +vt 0.890247 0.476475 +vt 0.886171 0.459879 +vt 0.545617 0.864368 +vt 0.958945 0.114278 +vt 0.964665 0.118071 +vt 0.966134 0.139414 +vt 0.961147 0.137426 +vt 0.955682 0.138510 +vt 0.953242 0.112669 +vt 0.951794 0.081604 +vt 0.852635 0.950244 +vt 0.837942 0.964360 +vt 0.882489 0.495266 +vt 0.875567 0.516083 +vt 0.881688 0.513993 +vt 0.887672 0.495489 +vt 0.863264 0.929727 +vt 0.965389 0.153039 +vt 0.969663 0.153778 +vt 0.071147 0.509497 +vt 0.065786 0.510992 +vt 0.065950 0.491537 +vt 0.071466 0.487548 +vt 0.956160 0.565044 +vt 0.943161 0.549354 +vt 0.960600 0.155965 +vt 0.821475 0.970072 +vt 0.806737 0.966054 +vt 0.809901 0.962626 +vt 0.823312 0.966683 +vt 0.838025 0.961792 +vt 0.071466 0.469137 +vt 0.065737 0.475305 +vt 0.927447 0.541812 +vt 0.928888 0.545388 +vt 0.915104 0.547899 +vt 0.912349 0.544134 +vt 0.772996 0.006830 +vt 0.783123 0.004541 +vt 0.789197 0.018547 +vt 0.778804 0.019498 +vt 0.767957 0.017519 +vt 0.762458 0.005991 +vt 0.797232 0.950976 +vt 0.800846 0.948470 +vt 0.901197 0.558038 +vt 0.904503 0.560938 +vt 0.897486 0.577995 +vt 0.894074 0.576374 +vt 0.782018 0.031755 +vt 0.792112 0.031841 +vt 0.791816 0.043553 +vt 0.782292 0.042768 +vt 0.772288 0.039675 +vt 0.771422 0.029033 +vt 0.891065 0.591993 +vt 0.894457 0.592562 +vt 0.893953 0.605388 +vt 0.890488 0.605739 +vt 0.740096 0.146273 +vt 0.760629 0.148484 +vt 0.726411 0.138696 +vt 0.730120 0.125035 +vt 0.740704 0.133808 +vt 0.756214 0.132985 +vt 0.185867 0.541824 +vt 0.179926 0.537297 +vt 0.166322 0.536682 +vt 0.170527 0.547416 +vt 0.203938 0.526827 +vt 0.192481 0.528463 +vt 0.518865 0.868435 +vt 0.515474 0.856737 +vt 0.522493 0.839496 +vt 0.522258 0.860835 +vt 0.522687 0.885284 +vt 0.508659 0.879252 +vt 0.502025 0.867714 +vt 0.935548 0.720739 +vt 0.935844 0.705870 +vt 0.932645 0.728539 +vt 0.917862 0.743931 +vt 0.924934 0.726710 +vt 0.923028 0.709456 +vt 0.786886 0.132048 +vt 0.780944 0.116394 +vt 0.217559 0.509367 +vt 0.204817 0.513033 +vt 0.235777 0.496526 +vt 0.219457 0.497582 +vt 0.739211 0.451083 +vt 0.746875 0.448475 +vt 0.746080 0.460557 +vt 0.739232 0.461917 +vt 0.782064 0.888233 +vt 0.785550 0.876581 +vt 0.790592 0.876233 +vt 0.788144 0.888451 +vt 0.720930 0.462834 +vt 0.722446 0.473270 +vt 0.726984 0.478923 +vt 0.725746 0.467648 +vt 0.879016 0.618658 +vt 0.885081 0.619130 +vt 0.886129 0.631547 +vt 0.881159 0.630630 +vt 0.174025 0.831140 +vt 0.171727 0.820552 +vt 0.178707 0.820416 +vt 0.182069 0.832048 +vt 0.539346 0.961374 +vt 0.535832 0.975467 +vt 0.544374 0.949754 +vt 0.552946 0.951903 +vt 0.549219 0.964866 +vt 0.545700 0.982684 +vt 0.964567 0.020863 +vt 0.974642 0.018005 +vt 0.977539 0.031178 +vt 0.968847 0.032779 +vt 0.951782 0.020923 +vt 0.961956 0.006576 +vt 0.972261 0.000000 +vt 0.726020 0.107001 +vt 0.717892 0.098088 +vt 0.721706 0.093927 +vt 0.729643 0.101042 +vt 0.741339 0.107723 +vt 0.734511 0.116633 +vt 0.553914 0.963566 +vt 0.551129 0.977600 +vt 0.555847 0.949718 +vt 0.950808 0.495529 +vt 0.956135 0.492834 +vt 0.958390 0.481188 +vt 0.949838 0.485004 +vt 0.921466 0.691825 +vt 0.917107 0.702000 +vt 0.909933 0.693491 +vt 0.917626 0.685848 +vt 0.923957 0.676456 +vt 0.926371 0.681753 +vt 0.951915 0.505186 +vt 0.955620 0.503002 +vt 0.472743 0.950225 +vt 0.474369 0.942532 +vt 0.478749 0.942997 +vt 0.478673 0.953127 +vt 0.479233 0.968282 +vt 0.471466 0.957682 +vt 0.463255 0.947278 +vt 0.465336 0.943148 +vt 0.469020 0.940176 +vt 0.912135 0.679025 +vt 0.918043 0.671779 +vt 0.446397 0.945137 +vt 0.452640 0.939077 +vt 0.445308 0.930481 +vt 0.440335 0.943362 +vt 0.438130 0.952426 +vt 0.442062 0.952021 +vt 0.445022 0.952772 +vt 0.449216 0.949470 +vt 0.749138 0.085046 +vt 0.744266 0.077167 +vt 0.751865 0.069213 +vt 0.758782 0.074302 +vt 0.769550 0.079706 +vt 0.756742 0.094313 +vt 0.739860 0.093025 +vt 0.737730 0.083325 +vt 0.162230 0.524378 +vt 0.158451 0.529749 +vt 0.167281 0.533656 +vt 0.173480 0.524860 +vt 0.176743 0.514612 +vt 0.164687 0.514513 +vt 0.899875 0.670390 +vt 0.896184 0.654398 +vt 0.898337 0.650806 +vt 0.899466 0.664502 +vt 0.153927 0.521902 +vt 0.151653 0.527747 +vt 0.767049 0.066274 +vt 0.773897 0.059296 +vt 0.782979 0.060940 +vt 0.777519 0.069243 +vt 0.759081 0.062594 +vt 0.765320 0.056116 +vt 0.893452 0.641706 +vt 0.897020 0.639170 +vt 0.894065 0.655742 +vt 0.889082 0.642465 +vt 0.891627 0.630374 +vt 0.895688 0.628382 +vt 0.779281 0.051705 +vt 0.788258 0.052815 +vt 0.769988 0.048590 +vt 0.890657 0.618459 +vt 0.894514 0.617224 +vt 0.790681 0.071537 +vt 0.786079 0.085539 +vt 0.790758 0.060559 +vt 0.886229 0.639126 +vt 0.190479 0.504935 +vt 0.178069 0.507894 +vt 0.193637 0.508168 +vt 0.867611 0.735411 +vt 0.878967 0.726690 +vt 0.861723 0.744159 +vt 0.769120 0.104289 +vt 0.750481 0.120008 +vt 0.188900 0.519638 +vt 0.181225 0.531645 +vt 0.740839 0.124915 +vt 0.949735 0.475192 +vt 0.939040 0.472518 +vt 0.939028 0.487781 +vt 0.955168 0.468463 +vt 0.175569 0.536485 +vt 0.763157 0.044514 +vt 0.764993 0.035726 +vt 0.759057 0.051893 +vt 0.752918 0.048628 +vt 0.756663 0.041260 +vt 0.758468 0.032736 +vt 0.895424 0.616004 +vt 0.897468 0.614461 +vt 0.898119 0.625306 +vt 0.896345 0.626841 +vt 0.152159 0.488839 +vt 0.152698 0.496186 +vt 0.160251 0.496305 +vt 0.160124 0.488812 +vt 0.895132 0.604375 +vt 0.897515 0.602846 +vt 0.753424 0.058168 +vt 0.746889 0.064326 +vt 0.742112 0.061448 +vt 0.747913 0.055131 +vt 0.897468 0.637178 +vt 0.899120 0.635614 +vt 0.899921 0.646425 +vt 0.898384 0.648181 +vt 0.151347 0.503392 +vt 0.149200 0.511396 +vt 0.155997 0.512049 +vt 0.158490 0.503783 +vt 0.740081 0.071352 +vt 0.734394 0.076956 +vt 0.732820 0.073139 +vt 0.735978 0.068257 +vt 0.898681 0.661020 +vt 0.899974 0.658780 +vt 0.901282 0.665578 +vt 0.900301 0.670097 +vt 0.147350 0.521141 +vt 0.145548 0.527150 +vt 0.475401 0.936538 +vt 0.479303 0.936078 +vt 0.468464 0.935746 +vt 0.467203 0.930481 +vt 0.474122 0.931038 +vt 0.476726 0.931400 +vt 0.441940 0.958071 +vt 0.441705 0.962683 +vt 0.445444 0.961283 +vt 0.444975 0.956715 +vt 0.438473 0.959265 +vt 0.438123 0.962545 +vt 0.463357 0.937359 +vt 0.722345 0.083901 +vt 0.727049 0.088348 +vt 0.716442 0.088430 +vt 0.714123 0.083680 +vt 0.719700 0.079197 +vt 0.461954 0.931832 +vt 0.451478 0.954669 +vt 0.451791 0.959389 +vt 0.458643 0.957310 +vt 0.458654 0.952711 +vt 0.456341 0.946434 +vt 0.712653 0.091967 +vt 0.716261 0.102097 +vt 0.711471 0.095528 +vt 0.559330 0.942930 +vt 0.557257 0.945565 +vt 0.558098 0.941586 +vt 0.561017 0.938455 +vt 0.710557 0.088149 +vt 0.952428 0.512397 +vt 0.951805 0.516083 +vt 0.954407 0.512851 +vt 0.955513 0.510490 +vt 0.927128 0.677122 +vt 0.925419 0.671920 +vt 0.925561 0.667578 +vt 0.927517 0.673525 +vt 0.549453 0.943753 +vt 0.542732 0.933830 +vt 0.548228 0.928757 +vt 0.554032 0.939106 +vt 0.973534 0.039090 +vt 0.977810 0.044019 +vt 0.971360 0.053978 +vt 0.966197 0.048567 +vt 0.981438 0.037778 +vt 0.982024 0.041802 +vt 0.763834 0.025222 +vt 0.760314 0.013992 +vt 0.757655 0.022763 +vt 0.754803 0.012198 +vt 0.895897 0.591662 +vt 0.898611 0.590226 +vt 0.899128 0.577283 +vt 0.901720 0.576321 +vt 0.802535 0.948948 +vt 0.811504 0.962855 +vt 0.755066 0.003029 +vt 0.750489 0.001893 +vt 0.804116 0.948931 +vt 0.812655 0.960532 +vt 0.906236 0.560654 +vt 0.907804 0.560850 +vt 0.916723 0.547853 +vt 0.917603 0.550291 +vt 0.058526 0.590305 +vt 0.056686 0.598012 +vt 0.060689 0.593255 +vt 0.063028 0.585315 +vt 0.061620 0.572143 +vt 0.056700 0.577461 +vt 0.824459 0.967247 +vt 0.824840 0.964304 +vt 0.837861 0.960425 +vt 0.838575 0.962954 +vt 0.930092 0.544957 +vt 0.930137 0.547925 +vt 0.060101 0.478445 +vt 0.061390 0.468709 +vt 0.067292 0.464839 +vt 0.060500 0.493696 +vt 0.943630 0.550823 +vt 0.942635 0.553254 +vt 0.893269 0.496110 +vt 0.895584 0.477908 +vt 0.887856 0.513839 +vt 0.851028 0.950802 +vt 0.848909 0.949074 +vt 0.856206 0.931991 +vt 0.859602 0.932582 +vt 0.954626 0.564308 +vt 0.952325 0.565784 +vt 0.060648 0.512230 +vt 0.061183 0.456068 +vt 0.058074 0.458386 +vt 0.058098 0.440375 +vt 0.060835 0.438704 +vt 0.961080 0.583382 +vt 0.957639 0.583585 +vt 0.891970 0.462289 +vt 0.882053 0.449265 +vt 0.875608 0.445917 +vt 0.549769 0.860285 +vt 0.551626 0.839496 +vt 0.554546 0.840606 +vt 0.553524 0.860019 +vt 0.552540 0.883310 +vt 0.547939 0.885370 +vt 0.968550 0.122410 +vt 0.972280 0.122914 +vt 0.972068 0.142352 +vt 0.969083 0.143275 +vt 0.968316 0.097260 +vt 0.972777 0.099607 +vt 0.543910 0.912126 +vt 0.549253 0.908191 +vt 0.965995 0.070301 +vt 0.971077 0.074568 +vt 0.828593 0.856453 +vt 0.820784 0.855787 +vt 0.820130 0.847851 +vt 0.827707 0.847866 +vt 0.833854 0.844903 +vt 0.835213 0.853924 +vt 0.836522 0.865763 +vt 0.829190 0.867603 +vt 0.821159 0.866277 +vt 0.921643 0.655506 +vt 0.923500 0.644496 +vt 0.930576 0.647155 +vt 0.927934 0.658769 +vt 0.919790 0.663938 +vt 0.912260 0.663094 +vt 0.913809 0.655283 +vt 0.915370 0.644903 +vt 0.828054 0.879402 +vt 0.820101 0.877745 +vt 0.836017 0.878546 +vt 0.834473 0.891586 +vt 0.825904 0.891166 +vt 0.818136 0.889412 +vt 0.923709 0.632644 +vt 0.922906 0.620712 +vt 0.931467 0.621266 +vt 0.931524 0.634397 +vt 0.915619 0.633389 +vt 0.914989 0.621575 +vt 0.823457 0.902209 +vt 0.815787 0.900500 +vt 0.832662 0.904196 +vt 0.831172 0.915748 +vt 0.821840 0.912484 +vt 0.814125 0.911239 +vt 0.921726 0.609463 +vt 0.921284 0.599071 +vt 0.930926 0.596885 +vt 0.931097 0.608532 +vt 0.913912 0.610292 +vt 0.913477 0.599434 +vt 0.822181 0.921943 +vt 0.814220 0.921858 +vt 0.830592 0.925612 +vt 0.829466 0.934881 +vt 0.823390 0.933581 +vt 0.814767 0.934036 +vt 0.922694 0.589712 +vt 0.925215 0.578286 +vt 0.931399 0.577683 +vt 0.931467 0.587020 +vt 0.914775 0.588894 +vt 0.916698 0.576856 +vt 0.931158 0.511327 +vt 0.933460 0.498866 +vt 0.937557 0.500927 +vt 0.934365 0.516083 +vt 0.805361 0.920555 +vt 0.798311 0.918702 +vt 0.798652 0.906040 +vt 0.805468 0.908730 +vt 0.925838 0.506881 +vt 0.927599 0.494818 +vt 0.905825 0.589184 +vt 0.904592 0.600945 +vt 0.907982 0.577191 +vt 0.806885 0.897506 +vt 0.799922 0.894494 +vt 0.801798 0.883793 +vt 0.808937 0.886598 +vt 0.904728 0.612257 +vt 0.905530 0.623328 +vt 0.810949 0.875719 +vt 0.803960 0.873664 +vt 0.805981 0.863013 +vt 0.812453 0.864744 +vt 0.906297 0.634365 +vt 0.906547 0.645440 +vt 0.812980 0.853550 +vt 0.813220 0.844602 +vt 0.807435 0.850745 +vt 0.809505 0.844139 +vt 0.905802 0.656621 +vt 0.905027 0.665538 +vt 0.557564 0.931544 +vt 0.553876 0.920616 +vt 0.559345 0.914259 +vt 0.561034 0.925359 +vt 0.839453 0.846572 +vt 0.836471 0.839216 +vt 0.980855 0.051790 +vt 0.983925 0.058182 +vt 0.981534 0.069153 +vt 0.976479 0.062461 +vt 0.984739 0.045112 +vt 0.931313 0.666554 +vt 0.554854 0.904094 +vt 0.557367 0.885422 +vt 0.845201 0.873032 +vt 0.846075 0.890397 +vt 0.560210 0.901934 +vt 0.940023 0.640917 +vt 0.936241 0.655816 +vt 0.942860 0.623762 +vt 0.952740 0.627003 +vt 0.947138 0.648215 +vt 0.940542 0.665147 +vt 0.846170 0.908025 +vt 0.857976 0.910915 +vt 0.844679 0.924315 +vt 0.856259 0.888297 +vt 0.944951 0.606258 +vt 0.945316 0.589905 +vt 0.957009 0.604725 +vt 0.840799 0.937667 +vt 0.834157 0.946789 +vt 0.942974 0.576199 +vt 0.937409 0.566383 +vt 0.926489 0.474439 +vt 0.930122 0.459992 +vt 0.936446 0.468348 +vt 0.932207 0.479068 +vt 0.922223 0.492122 +vt 0.919884 0.488084 +vt 0.920724 0.473977 +vt 0.922547 0.458508 +vt 0.928103 0.561696 +vt 0.918144 0.561507 +vt 0.935933 0.485951 +vt 0.939028 0.483077 +vt 0.910618 0.565184 +vt 0.024145 0.495486 +vt 0.010473 0.529549 +vt 0.025704 0.527915 +vt 0.041111 0.494580 +vt 0.054588 0.463653 +vt 0.036321 0.464534 +vt 0.023503 0.469101 +vt 0.013718 0.497685 +vt 0.002440 0.530199 +vt 0.113835 0.498959 +vt 0.103187 0.499386 +vt 0.119685 0.524695 +vt 0.133232 0.525978 +vt 0.151157 0.522351 +vt 0.130505 0.495675 +vt 0.107390 0.467139 +vt 0.092223 0.469292 +vt 0.084277 0.470632 +vt 0.000000 0.683584 +vt 0.005855 0.659587 +vt 0.004244 0.706622 +vt 0.006188 0.563110 +vt 0.000000 0.563693 +vt 0.004656 0.590864 +vt 0.010206 0.589915 +vt 0.079835 0.437808 +vt 0.073693 0.438760 +vt 0.093017 0.438038 +vt 0.088378 0.413252 +vt 0.077155 0.410836 +vt 0.071541 0.411278 +vt 0.021866 0.634834 +vt 0.041072 0.611276 +vt 0.049371 0.616136 +vt 0.030338 0.640107 +vt 0.004662 0.643568 +vt 0.017651 0.625045 +vt 0.033348 0.609149 +vt 0.084664 0.394708 +vt 0.077923 0.395693 +vt 0.970487 0.541871 +vt 0.980766 0.571039 +vt 0.810866 0.441888 +vt 0.801991 0.443191 +vt 0.801962 0.473814 +vt 0.810559 0.472300 +vt 0.818430 0.468997 +vt 0.820346 0.446745 +vt 0.050292 0.874935 +vt 0.062757 0.890315 +vt 0.034600 0.870082 +vt 0.056515 0.590864 +vt 0.042333 0.592652 +vt 0.569960 0.003528 +vt 0.583723 0.003656 +vt 0.572957 0.019770 +vt 0.553188 0.019963 +vt 0.813792 0.497800 +vt 0.823293 0.487394 +vt 0.803583 0.499526 +vt 0.802628 0.516083 +vt 0.815317 0.514149 +vt 0.285236 0.966108 +vt 0.271399 0.947135 +vt 0.290867 0.950580 +vt 0.298832 0.968247 +vt 0.768323 0.516083 +vt 0.757724 0.506625 +vt 0.741741 0.502226 +vt 0.753413 0.512129 +vt 0.065118 0.915739 +vt 0.544014 0.043966 +vt 0.567530 0.044903 +vt 0.564649 0.072592 +vt 0.539739 0.069605 +vt 0.266303 0.921949 +vt 0.289651 0.924897 +vt 0.189027 0.925192 +vt 0.178975 0.948663 +vt 0.171830 0.940127 +vt 0.194022 0.896904 +vt 0.266307 0.895957 +vt 0.291368 0.897111 +vt 0.747954 0.495323 +vt 0.742227 0.483275 +vt 0.732624 0.490803 +vt 0.537662 0.090946 +vt 0.561523 0.096374 +vt 0.768538 0.482144 +vt 0.761821 0.471033 +vt 0.267772 0.874565 +vt 0.292201 0.873139 +vt 0.195436 0.871641 +vt 0.195670 0.851104 +vt 0.272456 0.857843 +vt 0.295732 0.853753 +vt 0.606722 0.105261 +vt 0.610873 0.077446 +vt 0.651393 0.081707 +vt 0.646179 0.113168 +vt 0.637869 0.140362 +vt 0.602702 0.128771 +vt 0.561814 0.116077 +vt 0.338247 0.871815 +vt 0.338153 0.847964 +vt 0.374747 0.842322 +vt 0.378467 0.870513 +vt 0.378430 0.902403 +vt 0.337762 0.899934 +vt 0.610637 0.046282 +vt 0.607986 0.019683 +vt 0.637576 0.018783 +vt 0.648409 0.047749 +vt 0.332398 0.930634 +vt 0.369896 0.935406 +vt 0.354441 0.962193 +vt 0.325403 0.956433 +vt 0.604892 0.005568 +vt 0.598670 0.000000 +vt 0.619242 0.002297 +vt 0.320027 0.969847 +vt 0.333643 0.975435 +vt 0.826265 0.465093 +vt 0.547087 0.121619 +vt 0.569182 0.131527 +vt 0.545740 0.129074 +vt 0.743755 0.471574 +vt 0.741912 0.470464 +vt 0.765532 0.461957 +vt 0.197124 0.836994 +vt 0.200262 0.829946 +vt 0.282119 0.845862 +vt 0.282017 0.838287 +vt 0.310088 0.828078 +vt 0.305543 0.839727 +vt 0.603332 0.147023 +vt 0.631561 0.161518 +vt 0.341779 0.830065 +vt 0.372009 0.820416 +vt 0.125744 0.534649 +vt 0.140665 0.520402 +vt 0.123033 0.546235 +vt 0.036114 0.440303 +vt 0.055590 0.439137 +vt 0.021463 0.447399 +vt 0.138977 0.549519 +vt 0.158144 0.545872 +vn 0.702628 -0.228828 0.673727 +vn 0.744530 -0.000946 0.667562 +vn 0.821741 -0.001953 0.569811 +vn 0.776666 -0.263192 0.572283 +vn 0.628681 -0.515580 0.582141 +vn 0.573046 -0.435255 0.694357 +vn 0.618030 -0.502579 0.604480 +vn 0.759575 -0.264351 0.594226 +vn 0.806085 -0.001007 0.591784 +vn -0.702628 -0.228828 0.673727 +vn -0.759575 -0.264351 0.594226 +vn -0.618030 -0.502579 0.604480 +vn -0.573046 -0.435255 0.694357 +vn -0.628681 -0.515580 0.582141 +vn -0.776666 -0.263192 0.572283 +vn -0.821741 -0.001953 0.569811 +vn -0.744530 -0.000946 0.667562 +vn -0.806085 -0.001007 0.591784 +vn 0.813959 -0.298746 0.498154 +vn 0.865261 -0.001373 0.501297 +vn 0.656545 -0.569659 0.494369 +vn 0.678762 -0.572985 0.459304 +vn 0.831935 -0.299051 0.467330 +vn 0.881466 -0.000488 0.472243 +vn -0.813959 -0.298746 0.498154 +vn -0.831935 -0.299051 0.467330 +vn -0.678762 -0.572985 0.459304 +vn -0.656545 -0.569659 0.494369 +vn -0.865261 -0.001373 0.501297 +vn -0.881466 -0.000488 0.472243 +vn 0.406262 -0.770928 0.490493 +vn 0.395215 -0.676229 0.621662 +vn 0.112339 -0.755150 0.645802 +vn 0.086734 -0.861873 0.499588 +vn 0.097751 -0.888424 0.448469 +vn 0.425947 -0.786645 0.446913 +vn -0.406262 -0.770928 0.490493 +vn -0.425947 -0.786645 0.446913 +vn -0.097751 -0.888424 0.448469 +vn -0.086734 -0.861873 0.499588 +vn -0.112339 -0.755150 0.645802 +vn -0.395215 -0.676229 0.621662 +vn 0.375835 -0.578753 0.723685 +vn 0.398541 -0.700583 0.591876 +vn 0.116977 -0.783868 0.609760 +vn 0.136601 -0.641163 0.755120 +vn -0.375835 -0.578753 0.723685 +vn -0.136601 -0.641163 0.755120 +vn -0.116977 -0.783868 0.609760 +vn -0.398541 -0.700583 0.591876 +vn -0.126438 -0.613758 0.779260 +vn -0.199194 -0.751244 0.629231 +vn -0.493759 -0.578539 0.649190 +vn -0.373547 -0.470656 0.799310 +vn -0.460891 -0.548357 0.697745 +vn -0.190802 -0.716514 0.670949 +vn 0.126438 -0.613758 0.779260 +vn 0.190802 -0.716514 0.670949 +vn 0.460891 -0.548357 0.697745 +vn 0.373547 -0.470656 0.799310 +vn 0.493759 -0.578539 0.649190 +vn 0.199194 -0.751244 0.629231 +vn -0.250557 -0.815119 0.522233 +vn -0.544816 -0.626118 0.557756 +vn -0.563097 -0.651204 0.508713 +vn -0.256233 -0.844874 0.469558 +vn 0.250557 -0.815119 0.522233 +vn 0.256233 -0.844874 0.469558 +vn 0.563097 -0.651204 0.508713 +vn 0.544816 -0.626118 0.557756 +vn -0.740043 -0.332652 0.584490 +vn -0.637928 -0.287179 0.714530 +vn -0.693930 -0.001129 0.720023 +vn -0.802972 -0.001556 0.595965 +vn -0.833247 -0.000397 0.552843 +vn -0.767418 -0.346934 0.539140 +vn 0.740043 -0.332652 0.584490 +vn 0.767418 -0.346934 0.539140 +vn 0.833247 -0.000397 0.552843 +vn 0.802972 -0.001556 0.595965 +vn 0.693930 -0.001129 0.720023 +vn 0.637928 -0.287179 0.714530 +vn -0.537706 -0.242714 0.807428 +vn -0.695853 -0.304788 0.650258 +vn -0.761956 -0.002441 0.647603 +vn -0.588061 -0.000977 0.808802 +vn 0.537706 -0.242714 0.807428 +vn 0.588061 -0.000977 0.808802 +vn 0.761956 -0.002441 0.647603 +vn 0.695853 -0.304788 0.650258 +vn -0.546037 0.239967 0.802606 +vn -0.709311 0.295450 0.639973 +vn -0.520798 0.579211 0.627094 +vn -0.391308 0.476699 0.787133 +vn -0.464522 0.542283 0.700064 +vn -0.639332 0.281961 0.715323 +vn 0.546037 0.239967 0.802606 +vn 0.639332 0.281961 0.715323 +vn 0.464522 0.542283 0.700064 +vn 0.391308 0.476699 0.787133 +vn 0.520798 0.579211 0.627094 +vn 0.709311 0.295450 0.639973 +vn -0.736900 0.323862 0.593341 +vn -0.538316 0.609912 0.581530 +vn -0.549242 0.636402 0.541520 +vn -0.761406 0.340892 0.551378 +vn 0.736900 0.323862 0.593341 +vn 0.761406 0.340892 0.551378 +vn 0.549242 0.636402 0.541520 +vn 0.538316 0.609912 0.581530 +vn -0.240150 0.794610 0.557573 +vn -0.191931 0.713370 0.673940 +vn 0.113468 0.752190 0.649068 +vn 0.093722 0.838130 0.537309 +vn 0.105930 0.859462 0.500046 +vn -0.239448 0.820978 0.518296 +vn 0.240150 0.794610 0.557573 +vn 0.239448 0.820978 0.518296 +vn -0.105930 0.859462 0.500046 +vn -0.093722 0.838130 0.537309 +vn -0.113468 0.752190 0.649068 +vn 0.191931 0.713370 0.673940 +vn -0.141057 0.634602 0.759819 +vn -0.220679 0.773247 0.594409 +vn 0.109226 0.813990 0.570452 +vn 0.132633 0.666555 0.733512 +vn 0.141057 0.634602 0.759819 +vn -0.132633 0.666555 0.733512 +vn -0.109226 0.813990 0.570452 +vn 0.220679 0.773247 0.594409 +vn 0.382397 0.598102 0.704276 +vn 0.406446 0.723075 0.558489 +vn 0.646077 0.517411 0.561083 +vn 0.584490 0.440382 0.681448 +vn 0.621296 0.496261 0.606342 +vn 0.397504 0.672292 0.624470 +vn -0.382397 0.598102 0.704276 +vn -0.397504 0.672292 0.624470 +vn -0.621296 0.496261 0.606342 +vn -0.584490 0.440382 0.681448 +vn -0.646077 0.517411 0.561083 +vn -0.406446 0.723075 0.558489 +vn 0.407208 0.748802 0.522904 +vn 0.655599 0.552446 0.514725 +vn 0.673513 0.557360 0.485488 +vn 0.423811 0.761650 0.490127 +vn -0.407208 0.748802 0.522904 +vn -0.423811 0.761650 0.490127 +vn -0.673513 0.557360 0.485488 +vn -0.655599 0.552446 0.514725 +vn 0.812494 0.290292 0.505478 +vn 0.760796 0.259560 0.594775 +vn 0.828669 0.293191 0.476760 +vn -0.812494 0.290292 0.505478 +vn -0.828669 0.293191 0.476760 +vn -0.760796 0.259560 0.594775 +vn 0.708518 0.226173 0.668447 +vn 0.785943 0.255776 0.562883 +vn -0.708518 0.226173 0.668447 +vn -0.785943 0.255776 0.562883 +vn 0.824183 0.271401 0.496994 +vn 0.863857 -0.002106 0.503677 +vn 0.675893 0.563616 0.474837 +vn 0.548784 0.361492 0.753746 +vn 0.645161 0.176244 0.743400 +vn 0.660970 -0.001709 0.750389 +vn -0.824183 0.271401 0.496994 +vn -0.645161 0.176244 0.743400 +vn -0.548784 0.361492 0.753746 +vn -0.675893 0.563616 0.474837 +vn -0.863857 -0.002106 0.503677 +vn -0.660970 -0.001709 0.750389 +vn 0.414014 0.792566 0.447645 +vn 0.078982 0.898923 0.430860 +vn 0.151891 0.613575 0.774865 +vn 0.380291 0.541185 0.749962 +vn -0.414014 0.792566 0.447645 +vn -0.380291 0.541185 0.749962 +vn -0.151891 0.613575 0.774865 +vn -0.078982 0.898923 0.430860 +vn -0.279702 0.845119 0.455519 +vn -0.604297 0.635365 0.480697 +vn -0.356517 0.427961 0.830470 +vn -0.123997 0.589801 0.797937 +vn 0.279702 0.845119 0.455519 +vn 0.123997 0.589801 0.797937 +vn 0.356517 0.427961 0.830470 +vn 0.604297 0.635365 0.480697 +vn -0.813898 0.334635 0.474929 +vn -0.887906 -0.002686 0.459944 +vn -0.581988 -0.002594 0.813166 +vn -0.531388 0.225166 0.816645 +vn 0.813898 0.334635 0.474929 +vn 0.531388 0.225166 0.816645 +vn 0.581988 -0.002594 0.813166 +vn 0.887906 -0.002686 0.459944 +vn -0.807276 -0.349498 0.475509 +vn -0.589953 -0.648976 0.480361 +vn -0.354289 -0.452528 0.818323 +vn -0.532456 -0.242286 0.810999 +vn 0.807276 -0.349498 0.475509 +vn 0.532456 -0.242286 0.810999 +vn 0.354289 -0.452528 0.818323 +vn 0.589953 -0.648976 0.480361 +vn -0.273812 -0.846767 0.456038 +vn 0.080294 -0.897336 0.433943 +vn 0.146611 -0.641591 0.752861 +vn -0.129795 -0.613849 0.778649 +vn 0.273812 -0.846767 0.456038 +vn 0.129795 -0.613849 0.778649 +vn -0.146611 -0.641591 0.752861 +vn -0.080294 -0.897336 0.433943 +vn 0.410382 -0.791955 0.452071 +vn 0.663991 -0.573199 0.480087 +vn 0.546373 -0.385266 0.743645 +vn 0.379864 -0.565935 0.731681 +vn -0.410382 -0.791955 0.452071 +vn -0.379864 -0.565935 0.731681 +vn -0.546373 -0.385266 0.743645 +vn -0.663991 -0.573199 0.480087 +vn 0.818628 -0.283395 0.499466 +vn 0.646107 -0.190893 0.738975 +vn -0.818628 -0.283395 0.499466 +vn -0.646107 -0.190893 0.738975 +vn 0.414655 -0.075259 0.906827 +vn 0.399304 -0.000946 0.916807 +vn 0.190924 -0.002625 0.981567 +vn 0.353343 -0.126255 0.926908 +vn -0.414655 -0.075259 0.906827 +vn -0.399304 -0.000946 0.916807 +vn -0.353343 -0.126255 0.926908 +vn -0.190924 -0.002625 0.981567 +vn 0.302194 -0.207068 0.930448 +vn 0.201788 -0.197821 0.959227 +vn -0.302194 -0.207068 0.930448 +vn -0.201788 -0.197821 0.959227 +vn 0.080477 -0.215522 0.973144 +vn 0.008667 -0.124638 0.992157 +vn -0.080477 -0.215522 0.973144 +vn -0.008667 -0.124638 0.992157 +vn -0.086825 -0.063814 0.994171 +vn -0.067965 -0.000488 0.997681 +vn 0.086825 -0.063814 0.994171 +vn 0.067965 -0.000488 0.997681 +vn -0.086001 0.058077 0.994598 +vn 0.009919 0.117069 0.993042 +vn 0.086001 0.058077 0.994598 +vn -0.009919 0.117069 0.993042 +vn 0.082675 0.203406 0.975585 +vn 0.202460 0.185369 0.961577 +vn -0.082675 0.203406 0.975585 +vn -0.202460 0.185369 0.961577 +vn 0.301309 0.195471 0.933256 +vn 0.352641 0.118686 0.928190 +vn -0.301309 0.195471 0.933256 +vn -0.352641 0.118686 0.928190 +vn 0.414106 0.069247 0.907559 +vn -0.414106 0.069247 0.907559 +vn 0.101260 -0.898770 0.426527 +vn 0.097354 -0.985443 0.139348 +vn 0.148595 -0.983032 0.107456 +vn 0.163762 -0.895932 0.412854 +vn 0.155339 -0.768944 0.620106 +vn 0.091342 -0.788354 0.608386 +vn 0.000000 -0.793664 0.608325 +vn 0.000000 -0.899777 0.436323 +vn 0.000000 -0.987457 0.157811 +vn -0.101260 -0.898770 0.426527 +vn -0.091342 -0.788354 0.608386 +vn -0.155339 -0.768944 0.620106 +vn -0.163762 -0.895932 0.412854 +vn -0.148595 -0.983032 0.107456 +vn -0.097354 -0.985443 0.139348 +vn 0.269112 -0.856258 0.440840 +vn 0.261757 -0.960234 0.096774 +vn 0.596606 -0.794824 0.110782 +vn 0.472793 -0.715720 0.513962 +vn 0.379040 -0.551775 0.742851 +vn 0.249580 -0.701102 0.667928 +vn -0.269112 -0.856258 0.440840 +vn -0.249580 -0.701102 0.667928 +vn -0.379040 -0.551775 0.742851 +vn -0.472793 -0.715720 0.513962 +vn -0.596606 -0.794824 0.110782 +vn -0.261757 -0.960234 0.096774 +vn 0.687613 -0.438643 0.578570 +vn 0.911008 -0.392804 0.125523 +vn 0.988800 -0.070467 0.131535 +vn 0.787652 -0.196081 0.584063 +vn 0.532762 -0.222114 0.816553 +vn 0.473128 -0.367229 0.800775 +vn -0.687613 -0.438643 0.578570 +vn -0.473128 -0.367229 0.800775 +vn -0.532762 -0.222114 0.816553 +vn -0.787652 -0.196081 0.584063 +vn -0.988800 -0.070467 0.131535 +vn -0.911008 -0.392804 0.125523 +vn 0.819910 -0.045137 0.570666 +vn 0.990966 0.060823 0.119358 +vn 0.985717 0.115085 0.122745 +vn 0.822443 0.042573 0.567217 +vn 0.582995 -0.012940 0.812342 +vn 0.569811 -0.100864 0.815546 +vn -0.819910 -0.045137 0.570666 +vn -0.569811 -0.100864 0.815546 +vn -0.582995 -0.012940 0.812342 +vn -0.822443 0.042573 0.567217 +vn -0.985717 0.115085 0.122745 +vn -0.990966 0.060823 0.119358 +vn 0.823603 0.097568 0.558672 +vn 0.981048 0.148167 0.124607 +vn 0.966369 0.215583 0.140049 +vn 0.829035 0.167577 0.533464 +vn 0.612690 0.085604 0.785668 +vn 0.590472 0.034791 0.806299 +vn -0.823603 0.097568 0.558672 +vn -0.590472 0.034791 0.806299 +vn -0.612690 0.085604 0.785668 +vn -0.829035 0.167577 0.533464 +vn -0.966369 0.215583 0.140049 +vn -0.981048 0.148167 0.124607 +vn 0.376049 -0.776391 0.505722 +vn 0.639485 -0.578784 0.505997 +vn 0.631184 -0.731529 0.257729 +vn 0.335704 -0.914762 0.224708 +vn 0.359386 -0.910428 0.204810 +vn 0.369854 -0.755303 0.540971 +vn 0.383312 -0.553758 0.739158 +vn 0.405866 -0.478866 0.778375 +vn 0.222449 -0.201544 0.953856 +vn -0.376049 -0.776391 0.505722 +vn -0.405866 -0.478866 0.778375 +vn -0.383312 -0.553758 0.739158 +vn -0.369854 -0.755303 0.540971 +vn -0.359386 -0.910428 0.204810 +vn -0.335704 -0.914762 0.224708 +vn -0.631184 -0.731529 0.257729 +vn -0.639485 -0.578784 0.505997 +vn -0.222449 -0.201544 0.953856 +vn 0.443587 -0.688955 0.573168 +vn 0.474654 -0.857418 0.198798 +vn 0.609302 -0.770562 0.186926 +vn 0.570544 -0.589099 0.572192 +vn 0.494156 -0.431806 0.754540 +vn 0.414258 -0.524247 0.743980 +vn -0.443587 -0.688955 0.573168 +vn -0.414258 -0.524247 0.743980 +vn -0.494156 -0.431806 0.754540 +vn -0.570544 -0.589099 0.572192 +vn -0.609302 -0.770562 0.186926 +vn -0.474654 -0.857418 0.198798 +vn 0.683340 -0.479904 0.550157 +vn 0.748680 -0.646260 0.147526 +vn 0.895779 -0.427473 0.121677 +vn 0.732322 -0.391614 0.557024 +vn 0.584826 -0.320688 0.745048 +vn 0.569659 -0.354442 0.741508 +vn -0.683340 -0.479904 0.550157 +vn -0.569659 -0.354442 0.741508 +vn -0.584826 -0.320688 0.745048 +vn -0.732322 -0.391614 0.557024 +vn -0.895779 -0.427473 0.121677 +vn -0.748680 -0.646260 0.147526 +vn 0.737266 -0.304788 0.602893 +vn 0.975402 -0.151616 0.159856 +vn 0.934965 0.283181 0.213569 +vn 0.746300 -0.055361 0.663259 +vn 0.487716 -0.293344 0.822199 +vn 0.534257 -0.312326 0.785485 +vn -0.737266 -0.304788 0.602893 +vn -0.534257 -0.312326 0.785485 +vn -0.487716 -0.293344 0.822199 +vn -0.746300 -0.055361 0.663259 +vn -0.934965 0.283181 0.213569 +vn -0.975402 -0.151616 0.159856 +vn 0.639821 0.326090 0.695883 +vn 0.659658 0.715781 0.229041 +vn 0.472213 0.865291 0.168004 +vn 0.527757 0.548540 0.648488 +vn 0.353099 -0.046693 0.934416 +vn 0.438765 -0.154241 0.885250 +vn -0.639821 0.326090 0.695883 +vn -0.438765 -0.154241 0.885250 +vn -0.353099 -0.046693 0.934416 +vn -0.527757 0.548540 0.648488 +vn -0.472213 0.865291 0.168004 +vn -0.659658 0.715781 0.229041 +vn 0.518601 0.602802 0.606311 +vn 0.512772 0.846828 0.141179 +vn 0.588946 0.793542 0.152837 +vn 0.551195 0.563891 0.614948 +vn 0.305002 0.028565 0.951903 +vn 0.308786 0.016785 0.950957 +vn -0.518601 0.602802 0.606311 +vn -0.308786 0.016785 0.950957 +vn -0.305002 0.028565 0.951903 +vn -0.551195 0.563891 0.614948 +vn -0.588946 0.793542 0.152837 +vn -0.512772 0.846828 0.141179 +vn 0.535295 0.554613 0.637013 +vn 0.588519 0.790490 0.169439 +vn 0.314859 0.932188 0.178411 +vn 0.331889 0.642659 0.690512 +vn 0.258614 0.102695 0.960479 +vn 0.308603 0.051515 0.949767 +vn -0.535295 0.554613 0.637013 +vn -0.308603 0.051515 0.949767 +vn -0.258614 0.102695 0.960479 +vn -0.331889 0.642659 0.690512 +vn -0.314859 0.932188 0.178411 +vn -0.588519 0.790490 0.169439 +vn -0.072085 0.670247 0.738609 +vn -0.204352 0.961180 0.185247 +vn -0.669118 0.726371 0.156896 +vn -0.467086 0.521805 0.713767 +vn -0.048372 0.074313 0.996033 +vn 0.107334 0.122379 0.986633 +vn 0.072085 0.670247 0.738609 +vn -0.107334 0.122379 0.986633 +vn 0.048372 0.074313 0.996033 +vn 0.467086 0.521805 0.713767 +vn 0.669118 0.726371 0.156896 +vn 0.204352 0.961180 0.185247 +vn -0.661824 0.379559 0.646443 +vn -0.862850 0.493942 0.107089 +vn -0.842006 0.529923 0.100711 +vn -0.677358 0.403363 0.615162 +vn -0.127689 0.001282 0.991791 +vn -0.103122 0.022492 0.994385 +vn 0.661824 0.379559 0.646443 +vn 0.103122 0.022492 0.994385 +vn 0.127689 0.001282 0.991791 +vn 0.677358 0.403363 0.615162 +vn 0.842006 0.529923 0.100711 +vn 0.862850 0.493942 0.107089 +vn -0.508835 0.551195 0.661214 +vn -0.607227 0.777795 0.162114 +vn 0.000000 0.975646 0.219306 +vn 0.000000 0.666799 0.745232 +vn 0.000000 -0.060488 0.998138 +vn -0.110080 0.026826 0.993530 +vn 0.508835 0.551195 0.661214 +vn 0.110080 0.026826 0.993530 +vn 0.607227 0.777795 0.162114 +vn 0.361126 -0.297250 0.883847 +vn 0.339518 -0.307047 0.889035 +vn 0.302316 -0.228248 0.925443 +vn 0.322275 -0.232215 0.917692 +vn 0.286660 -0.247475 0.925504 +vn 0.304483 -0.303201 0.902951 +vn -0.361126 -0.297250 0.883847 +vn -0.304483 -0.303201 0.902951 +vn -0.286660 -0.247475 0.925504 +vn -0.322275 -0.232215 0.917692 +vn -0.302316 -0.228248 0.925443 +vn -0.339518 -0.307047 0.889035 +vn 0.213721 -0.326029 0.920865 +vn 0.209662 -0.271767 0.939238 +vn 0.122135 -0.268166 0.955565 +vn 0.124485 -0.349864 0.928465 +vn -0.213721 -0.326029 0.920865 +vn -0.124485 -0.349864 0.928465 +vn -0.122135 -0.268166 0.955565 +vn -0.209662 -0.271767 0.939238 +vn 0.064577 -0.372417 0.925779 +vn 0.108188 -0.213935 0.970824 +vn 0.180120 -0.183996 0.966277 +vn 0.055727 -0.407514 0.911466 +vn -0.064577 -0.372417 0.925779 +vn -0.055727 -0.407514 0.911466 +vn -0.180120 -0.183996 0.966277 +vn -0.108188 -0.213935 0.970824 +vn 0.053041 -0.461562 0.885495 +vn 0.192083 -0.271920 0.942930 +vn 0.152776 -0.334452 0.929929 +vn 0.071841 -0.471908 0.878689 +vn -0.053041 -0.461562 0.885495 +vn -0.071841 -0.471908 0.878689 +vn -0.152776 -0.334452 0.929929 +vn -0.192083 -0.271920 0.942930 +vn 0.160405 -0.412763 0.896573 +vn 0.175787 -0.264229 0.948271 +vn 0.276498 -0.159490 0.947661 +vn 0.259651 -0.305307 0.916166 +vn -0.160405 -0.412763 0.896573 +vn -0.259651 -0.305307 0.916166 +vn -0.276498 -0.159490 0.947661 +vn -0.175787 -0.264229 0.948271 +vn 0.370861 -0.236457 0.898068 +vn 0.378155 -0.146825 0.913999 +vn 0.442640 -0.200476 0.873989 +vn 0.447859 -0.234565 0.862758 +vn -0.370861 -0.236457 0.898068 +vn -0.447859 -0.234565 0.862758 +vn -0.442640 -0.200476 0.873989 +vn -0.378155 -0.146825 0.913999 +vn 0.452254 -0.264138 0.851863 +vn 0.459487 -0.281838 0.842250 +vn 0.438124 -0.367443 0.820338 +vn 0.417951 -0.328410 0.847011 +vn -0.452254 -0.264138 0.851863 +vn -0.417951 -0.328410 0.847011 +vn -0.438124 -0.367443 0.820338 +vn -0.459487 -0.281838 0.842250 +vn 0.377239 -0.383160 0.843104 +vn 0.365307 -0.419843 0.830805 +vn 0.275399 -0.364513 0.889523 +vn 0.348949 -0.318552 0.881314 +vn -0.377239 -0.383160 0.843104 +vn -0.348949 -0.318552 0.881314 +vn -0.275399 -0.364513 0.889523 +vn -0.365307 -0.419843 0.830805 +vn 0.338481 -0.136174 0.931059 +vn 0.204169 -0.239418 0.949187 +vn 0.094852 -0.116398 0.988647 +vn 0.177343 0.033418 0.983551 +vn -0.338481 -0.136174 0.931059 +vn -0.177343 0.033418 0.983551 +vn -0.094852 -0.116398 0.988647 +vn -0.204169 -0.239418 0.949187 +vn -0.055269 0.122837 0.990875 +vn -0.074313 -0.043031 0.996277 +vn -0.147954 -0.087100 0.985137 +vn -0.079257 0.017029 0.996704 +vn 0.000000 0.041017 0.999146 +vn -0.158422 0.075228 0.984497 +vn 0.055269 0.122837 0.990875 +vn 0.158422 0.075228 0.984497 +vn 0.079257 0.017029 0.996704 +vn 0.147954 -0.087100 0.985137 +vn 0.074313 -0.043031 0.996277 +vn 0.259468 -0.291025 0.920835 +vn 0.226875 -0.202857 0.952544 +vn 0.186041 -0.259163 0.947722 +vn 0.129154 -0.162084 0.978271 +vn -0.259468 -0.291025 0.920835 +vn -0.186041 -0.259163 0.947722 +vn -0.226875 -0.202857 0.952544 +vn -0.129154 -0.162084 0.978271 +vn 0.115818 -0.236366 0.964721 +vn 0.020539 -0.147801 0.988769 +vn 0.000000 -0.275155 0.961364 +vn 0.000000 -0.246376 0.969146 +vn 0.040773 -0.215949 0.975524 +vn -0.075320 -0.164678 0.983459 +vn -0.115818 -0.236366 0.964721 +vn -0.040773 -0.215949 0.975524 +vn -0.020539 -0.147801 0.988769 +vn 0.075320 -0.164678 0.983459 +vn -0.014710 -0.146702 0.989044 +vn 0.000000 -0.149754 0.988708 +vn -0.126865 -0.161229 0.978698 +vn 0.014710 -0.146702 0.989044 +vn 0.126865 -0.161229 0.978698 +vn 0.070284 -0.702017 0.708670 +vn 0.134465 -0.664174 0.735343 +vn 0.071474 -0.528581 0.845820 +vn 0.026521 -0.547014 0.836665 +vn 0.000000 -0.548997 0.835810 +vn 0.000000 -0.710440 0.703726 +vn -0.070284 -0.702017 0.708670 +vn -0.026521 -0.547014 0.836665 +vn -0.071474 -0.528581 0.845820 +vn -0.134465 -0.664174 0.735343 +vn 0.217627 -0.571978 0.790826 +vn 0.311380 -0.418317 0.853236 +vn 0.229713 -0.334361 0.913999 +vn 0.144993 -0.458571 0.876736 +vn -0.217627 -0.571978 0.790826 +vn -0.144993 -0.458571 0.876736 +vn -0.229713 -0.334361 0.913999 +vn -0.311380 -0.418317 0.853236 +vn 0.353099 -0.284188 0.891354 +vn 0.362377 -0.194220 0.911557 +vn 0.203925 -0.139592 0.968963 +vn 0.262490 -0.223548 0.938658 +vn -0.353099 -0.284188 0.891354 +vn -0.262490 -0.223548 0.938658 +vn -0.203925 -0.139592 0.968963 +vn -0.362377 -0.194220 0.911557 +vn 0.360149 -0.013428 0.932768 +vn 0.362774 0.017914 0.931700 +vn 0.170934 -0.008209 0.985229 +vn 0.175909 -0.038148 0.983642 +vn 0.187933 -0.050172 0.980895 +vn 0.368816 -0.043062 0.928495 +vn -0.360149 -0.013428 0.932768 +vn -0.368816 -0.043062 0.928495 +vn -0.187933 -0.050172 0.980895 +vn -0.175909 -0.038148 0.983642 +vn -0.170934 -0.008209 0.985229 +vn -0.362774 0.017914 0.931700 +vn 0.371868 -0.108249 0.921934 +vn 0.204077 -0.078494 0.975768 +vn -0.371868 -0.108249 0.921934 +vn -0.204077 -0.078494 0.975768 +vn 0.051637 -0.013306 0.998566 +vn 0.181982 -0.007080 0.983245 +vn 0.368725 -0.294595 0.881588 +vn 0.137120 -0.292978 0.946226 +vn 0.000000 -0.278146 0.960509 +vn 0.000000 -0.011170 0.999908 +vn 0.000000 -0.017365 0.999847 +vn 0.048891 -0.015168 0.998688 +vn -0.051637 -0.013306 0.998566 +vn -0.048891 -0.015168 0.998688 +vn -0.137120 -0.292978 0.946226 +vn -0.368725 -0.294595 0.881588 +vn -0.181982 -0.007080 0.983245 +vn 0.049776 -0.049593 0.997497 +vn 0.053468 -0.049196 0.997345 +vn 0.000000 -0.053133 0.998566 +vn 0.000000 -0.048097 0.998840 +vn -0.049776 -0.049593 0.997497 +vn -0.053468 -0.049196 0.997345 +vn 0.060335 -0.050630 0.996887 +vn 0.000000 -0.048982 0.998779 +vn 0.000000 -0.219092 0.975677 +vn 0.061586 -0.160375 0.985107 +vn -0.060335 -0.050630 0.996887 +vn -0.061586 -0.160375 0.985107 +vn 0.810083 -0.520646 0.269478 +vn 0.659139 -0.328501 0.676443 +vn 0.806879 -0.153996 0.570238 +vn 0.989288 -0.119633 0.083438 +vn 0.965361 -0.144719 0.217017 +vn 0.759514 -0.577013 0.300241 +vn 0.506821 -0.753075 0.419477 +vn 0.544420 -0.686453 0.482009 +vn -0.810083 -0.520646 0.269478 +vn -0.544420 -0.686453 0.482009 +vn -0.506821 -0.753075 0.419477 +vn -0.759514 -0.577013 0.300241 +vn -0.965361 -0.144719 0.217017 +vn -0.989288 -0.119633 0.083438 +vn -0.806879 -0.153996 0.570238 +vn -0.659139 -0.328501 0.676443 +vn 0.952116 0.305338 -0.015046 +vn 0.711783 0.053468 0.700308 +vn 0.188299 0.327921 0.925718 +vn 0.689993 0.722953 -0.034791 +vn 0.677145 0.730949 0.084475 +vn 0.940306 0.302744 0.155339 +vn -0.952116 0.305338 -0.015046 +vn -0.940306 0.302744 0.155339 +vn -0.677145 0.730949 0.084475 +vn -0.689993 0.722953 -0.034791 +vn -0.188299 0.327921 0.925718 +vn -0.711783 0.053468 0.700308 +vn 0.151280 0.975982 0.156652 +vn -0.139348 0.337718 0.930845 +vn 0.000000 0.307138 0.951628 +vn -0.381909 0.794244 0.472518 +vn -0.362163 0.904599 0.224769 +vn 0.169103 0.981475 0.089755 +vn -0.151280 0.975982 0.156652 +vn -0.169103 0.981475 0.089755 +vn 0.362163 0.904599 0.224769 +vn 0.381909 0.794244 0.472518 +vn 0.139348 0.337718 0.930845 +vn -0.562883 0.662496 0.494156 +vn 0.000000 0.564135 0.825678 +vn 0.000000 0.759239 0.650777 +vn 0.000000 0.951170 0.308573 +vn 0.000000 0.960356 0.278726 +vn -0.630390 0.724021 0.279916 +vn 0.562883 0.662496 0.494156 +vn 0.630390 0.724021 0.279916 +vn 0.301340 -0.732719 0.610126 +vn 0.000000 -0.752556 0.658498 +vn 0.319376 -0.804498 0.500748 +vn 0.000000 -0.850154 0.526505 +vn -0.301340 -0.732719 0.610126 +vn -0.319376 -0.804498 0.500748 +vn 0.230171 -0.645100 0.728568 +vn 0.000000 -0.657582 0.753349 +vn 0.346294 -0.669454 0.657155 +vn 0.207953 -0.566973 0.797021 +vn 0.134922 -0.503952 0.853114 +vn 0.000000 -0.476363 0.879208 +vn -0.230171 -0.645100 0.728568 +vn -0.134922 -0.503952 0.853114 +vn -0.207953 -0.566973 0.797021 +vn -0.346294 -0.669454 0.657155 +vn -0.430891 0.679769 0.593463 +vn 0.000000 0.728080 0.685476 +vn 0.000000 0.138005 0.990417 +vn -0.149205 0.407788 0.900784 +vn -0.142949 0.526994 0.837733 +vn -0.276009 0.759453 0.589068 +vn 0.430891 0.679769 0.593463 +vn 0.276009 0.759453 0.589068 +vn 0.142949 0.526994 0.837733 +vn 0.149205 0.407788 0.900784 +vn 0.147801 0.804193 0.575640 +vn 0.089755 0.494522 0.864498 +vn 0.316141 0.417035 0.852107 +vn 0.547441 0.603656 0.579516 +vn -0.147801 0.804193 0.575640 +vn -0.547441 0.603656 0.579516 +vn -0.316141 0.417035 0.852107 +vn -0.089755 0.494522 0.864498 +vn 0.738151 0.266640 0.619678 +vn 0.341075 0.213202 0.915525 +vn 0.308206 -0.132298 0.942045 +vn 0.732963 -0.157720 0.661702 +vn -0.738151 0.266640 0.619678 +vn -0.732963 -0.157720 0.661702 +vn -0.308206 -0.132298 0.942045 +vn -0.341075 0.213202 0.915525 +vn 0.541887 -0.547990 0.637165 +vn 0.226051 -0.456496 0.860500 +vn -0.541887 -0.547990 0.637165 +vn -0.226051 -0.456496 0.860500 +vn 0.062929 0.234931 0.969939 +vn 0.043458 -0.059755 0.997253 +vn -0.062929 0.234931 0.969939 +vn -0.043458 -0.059755 0.997253 +vn 0.057711 -0.344737 0.936918 +vn 0.000000 -0.300943 0.953612 +vn -0.057711 -0.344737 0.936918 +vn -0.217780 -0.045137 0.974944 +vn 0.000000 0.003052 0.999969 +vn -0.110630 -0.207984 0.971831 +vn 0.217780 -0.045137 0.974944 +vn 0.110630 -0.207984 0.971831 +vn 0.296396 -0.239326 0.924558 +vn 0.646657 -0.139744 0.749840 +vn 0.829463 0.004578 0.558489 +vn 0.728111 -0.196020 0.656789 +vn -0.296396 -0.239326 0.924558 +vn -0.728111 -0.196020 0.656789 +vn -0.829463 0.004578 0.558489 +vn -0.646657 -0.139744 0.749840 +vn 0.593005 -0.108005 0.797876 +vn 0.470168 -0.018891 0.882351 +vn 0.713187 0.139805 0.686880 +vn 0.784692 0.079257 0.614765 +vn -0.593005 -0.108005 0.797876 +vn -0.784692 0.079257 0.614765 +vn -0.713187 0.139805 0.686880 +vn -0.470168 -0.018891 0.882351 +vn 0.393689 0.037416 0.918455 +vn 0.653523 0.137974 0.744194 +vn -0.393689 0.037416 0.918455 +vn -0.653523 0.137974 0.744194 +vn 0.836787 0.247383 0.488388 +vn 0.940306 0.303110 0.154668 +vn 0.931883 0.327677 0.155492 +vn 0.857723 0.269082 0.438032 +vn -0.836787 0.247383 0.488388 +vn -0.857723 0.269082 0.438032 +vn -0.931883 0.327677 0.155492 +vn -0.940306 0.303110 0.154668 +vn 0.889706 0.228065 0.395398 +vn 0.942228 0.295877 0.156926 +vn 0.945036 0.280862 0.167302 +vn 0.915891 0.182318 0.357555 +vn -0.889706 0.228065 0.395398 +vn -0.915891 0.182318 0.357555 +vn -0.945036 0.280862 0.167302 +vn -0.942228 0.295877 0.156926 +vn 0.903287 -0.131199 0.408429 +vn 0.968047 -0.106754 0.226875 +vn -0.903287 -0.131199 0.408429 +vn -0.968047 -0.106754 0.226875 +vn 0.086398 -0.624805 0.775964 +vn -0.029145 -0.399731 0.916135 +vn 0.000000 -0.701529 0.712607 +vn 0.000000 -0.954924 0.296762 +vn 0.114475 -0.940580 0.319651 +vn -0.385876 -0.804651 0.451216 +vn -0.086398 -0.624805 0.775964 +vn -0.114475 -0.940580 0.319651 +vn 0.029145 -0.399731 0.916135 +vn 0.385876 -0.804651 0.451216 +vn -0.235115 -0.263222 0.935606 +vn -0.268319 -0.152867 0.951109 +vn -0.882229 -0.229377 0.411115 +vn -0.871975 0.194006 0.449385 +vn 0.235115 -0.263222 0.935606 +vn 0.882229 -0.229377 0.411115 +vn 0.268319 -0.152867 0.951109 +vn 0.871975 0.194006 0.449385 +vn -0.278787 -0.087436 0.956359 +vn -0.235023 0.025391 0.971648 +vn -0.710685 0.485427 0.509140 +vn -0.471603 0.701071 0.534806 +vn 0.278787 -0.087436 0.956359 +vn 0.710685 0.485427 0.509140 +vn 0.235023 0.025391 0.971648 +vn 0.471603 0.701071 0.534806 +vn -0.117710 0.118748 0.985900 +vn 0.000000 0.157262 0.987548 +vn -0.203192 0.812983 0.545640 +vn 0.000000 0.837458 0.546464 +vn 0.117710 0.118748 0.985900 +vn 0.203192 0.812983 0.545640 +vn -0.190344 0.835902 0.514756 +vn 0.000000 0.848506 0.529160 +vn -0.441786 0.782098 0.439436 +vn -0.283273 0.403424 0.870022 +vn -0.117740 0.377331 0.918546 +vn 0.000000 0.374004 0.927396 +vn 0.190344 0.835902 0.514756 +vn 0.117740 0.377331 0.918546 +vn 0.283273 0.403424 0.870022 +vn 0.441786 0.782098 0.439436 +vn -0.717185 0.632710 0.292062 +vn -0.926328 0.333995 0.174230 +vn -0.899045 0.302957 0.316019 +vn -0.550249 0.375317 0.745842 +vn 0.717185 0.632710 0.292062 +vn 0.550249 0.375317 0.745842 +vn 0.899045 0.302957 0.316019 +vn 0.926328 0.333995 0.174230 +vn -0.953703 -0.224860 0.199591 +vn -0.436445 -0.853481 0.284646 +vn -0.263375 -0.733696 0.626301 +vn -0.748772 -0.273293 0.603809 +vn 0.953703 -0.224860 0.199591 +vn 0.748772 -0.273293 0.603809 +vn 0.263375 -0.733696 0.626301 +vn 0.436445 -0.853481 0.284646 +vn 0.112491 -0.943907 0.310434 +vn 0.000000 -0.944823 0.327555 +vn 0.000000 -0.694723 0.719230 +vn 0.088046 -0.720023 0.688314 +vn -0.112491 -0.943907 0.310434 +vn -0.088046 -0.720023 0.688314 +vn 0.018799 -0.282723 0.958983 +vn 0.000000 -0.267281 0.963591 +vn 0.001862 -0.297555 0.954680 +vn -0.018799 -0.282723 0.958983 +vn -0.001862 -0.297555 0.954680 +vn -0.465896 -0.095767 0.879604 +vn 0.465896 -0.095767 0.879604 +vn -0.261605 -0.178777 0.948454 +vn -0.217139 -0.122898 0.968352 +vn -0.298502 -0.207038 0.931669 +vn -0.013153 -0.028687 0.999481 +vn 0.092990 -0.056795 0.994018 +vn 0.138279 -0.059999 0.988556 +vn 0.261605 -0.178777 0.948454 +vn -0.092990 -0.056795 0.994018 +vn 0.013153 -0.028687 0.999481 +vn 0.298502 -0.207038 0.931669 +vn 0.217139 -0.122898 0.968352 +vn -0.138279 -0.059999 0.988556 +vn -0.148350 -0.068087 0.986572 +vn -0.067995 -0.045351 0.996643 +vn 0.155950 -0.054353 0.986236 +vn 0.162053 -0.069796 0.984283 +vn 0.148350 -0.068087 0.986572 +vn -0.155950 -0.054353 0.986236 +vn 0.067995 -0.045351 0.996643 +vn -0.162053 -0.069796 0.984283 +vn 0.005524 -0.036653 0.999298 +vn 0.069430 -0.011597 0.997497 +vn 0.153691 -0.082430 0.984649 +vn 0.131748 -0.059999 0.989441 +vn -0.005524 -0.036653 0.999298 +vn -0.153691 -0.082430 0.984649 +vn -0.069430 -0.011597 0.997497 +vn -0.131748 -0.059999 0.989441 +vn -0.253121 -0.301431 0.919248 +vn -0.108188 -0.461409 0.880520 +vn 0.010407 -0.254494 0.967009 +vn -0.081484 -0.125309 0.988739 +vn 0.253121 -0.301431 0.919248 +vn 0.081484 -0.125309 0.988739 +vn -0.010407 -0.254494 0.967009 +vn 0.108188 -0.461409 0.880520 +vn 0.037568 -0.534654 0.844203 +vn 0.186346 -0.554064 0.811335 +vn 0.209449 -0.299264 0.930876 +vn 0.117405 -0.307413 0.944273 +vn -0.037568 -0.534654 0.844203 +vn -0.117405 -0.307413 0.944273 +vn -0.209449 -0.299264 0.930876 +vn -0.186346 -0.554064 0.811335 +vn 0.360393 -0.526597 0.769890 +vn 0.479415 -0.437269 0.760857 +vn 0.365703 -0.161596 0.916562 +vn 0.311472 -0.246803 0.917600 +vn -0.360393 -0.526597 0.769890 +vn -0.311472 -0.246803 0.917600 +vn -0.365703 -0.161596 0.916562 +vn -0.479415 -0.437269 0.760857 +vn 0.509262 -0.320627 0.798639 +vn 0.491165 -0.203009 0.847072 +vn 0.310862 -0.056795 0.948729 +vn 0.357707 -0.085299 0.929899 +vn -0.509262 -0.320627 0.798639 +vn -0.357707 -0.085299 0.929899 +vn -0.310862 -0.056795 0.948729 +vn -0.491165 -0.203009 0.847072 +vn 0.460250 -0.109989 0.880917 +vn 0.425031 -0.052095 0.903653 +vn 0.242164 -0.060823 0.968322 +vn 0.263680 -0.061098 0.962645 +vn -0.460250 -0.109989 0.880917 +vn -0.263680 -0.061098 0.962645 +vn -0.242164 -0.060823 0.968322 +vn -0.425031 -0.052095 0.903653 +vn 0.387738 -0.025880 0.921384 +vn 0.401471 0.034669 0.915189 +vn 0.346110 -0.079379 0.934812 +vn 0.283303 -0.067721 0.956603 +vn -0.387738 -0.025880 0.921384 +vn -0.283303 -0.067721 0.956603 +vn -0.346110 -0.079379 0.934812 +vn -0.401471 0.034669 0.915189 +vn 0.428449 0.203955 0.880215 +vn 0.363475 0.340465 0.867122 +vn 0.298685 0.113559 0.947539 +vn 0.393689 -0.024262 0.918912 +vn -0.428449 0.203955 0.880215 +vn -0.393689 -0.024262 0.918912 +vn -0.298685 0.113559 0.947539 +vn -0.363475 0.340465 0.867122 +vn 0.219398 0.286538 0.932585 +vn 0.153050 0.142674 0.977844 +vn 0.107395 0.172155 0.979186 +vn 0.128117 0.198492 0.971679 +vn -0.219398 0.286538 0.932585 +vn -0.128117 0.198492 0.971679 +vn -0.107395 0.172155 0.979186 +vn -0.153050 0.142674 0.977844 +vn 0.170660 0.080355 0.982025 +vn 0.152257 0.064943 0.986175 +vn 0.137211 0.094607 0.985992 +vn 0.160375 0.141301 0.976867 +vn -0.170660 0.080355 0.982025 +vn -0.160375 0.141301 0.976867 +vn -0.137211 0.094607 0.985992 +vn -0.152257 0.064943 0.986175 +vn 0.113102 0.030427 0.993103 +vn 0.113804 0.006561 0.993469 +vn -0.113102 0.030427 0.993103 +vn -0.113804 0.006561 0.993469 +vn 0.270608 -0.284249 0.919736 +vn 0.226051 -0.191931 0.954985 +vn 0.397351 -0.314310 0.862117 +vn 0.488113 -0.426557 0.761406 +vn 0.330943 -0.419019 0.845485 +vn 0.260292 -0.334666 0.905667 +vn -0.270608 -0.284249 0.919736 +vn -0.330943 -0.419019 0.845485 +vn -0.488113 -0.426557 0.761406 +vn -0.397351 -0.314310 0.862117 +vn -0.226051 -0.191931 0.954985 +vn -0.260292 -0.334666 0.905667 +vn 0.195196 -0.165593 0.966674 +vn 0.042787 -0.259590 0.964751 +vn 0.211554 -0.315836 0.924894 +vn 0.032899 -0.464797 0.884762 +vn -0.195196 -0.165593 0.966674 +vn -0.211554 -0.315836 0.924894 +vn -0.042787 -0.259590 0.964751 +vn -0.032899 -0.464797 0.884762 +vn -0.048921 -0.412000 0.909848 +vn 0.027650 -0.570574 0.820734 +vn -0.111759 -0.677816 0.726646 +vn -0.107700 -0.811182 0.574755 +vn 0.048921 -0.412000 0.909848 +vn 0.111759 -0.677816 0.726646 +vn -0.027650 -0.570574 0.820734 +vn 0.107700 -0.811182 0.574755 +vn 0.084536 -0.644581 0.759819 +vn 0.008942 -0.549821 0.835200 +vn -0.073183 -0.811975 0.579058 +vn -0.133641 -0.676046 0.724601 +vn -0.084536 -0.644581 0.759819 +vn 0.073183 -0.811975 0.579058 +vn -0.008942 -0.549821 0.835200 +vn 0.133641 -0.676046 0.724601 +vn -0.126774 -0.301431 0.945006 +vn -0.197241 -0.108707 0.974273 +vn -0.303690 -0.372570 0.876888 +vn -0.386670 -0.114383 0.915067 +vn 0.126774 -0.301431 0.945006 +vn 0.303690 -0.372570 0.876888 +vn 0.197241 -0.108707 0.974273 +vn 0.386670 -0.114383 0.915067 +vn -0.153111 0.025513 0.987854 +vn -0.060823 0.190924 0.979705 +vn -0.337535 0.070559 0.938627 +vn -0.226417 0.283303 0.931883 +vn 0.153111 0.025513 0.987854 +vn 0.337535 0.070559 0.938627 +vn 0.060823 0.190924 0.979705 +vn 0.226417 0.283303 0.931883 +vn 0.005951 0.304239 0.952574 +vn 0.045167 0.351115 0.935209 +vn -0.160772 0.446089 0.880398 +vn -0.125309 0.555132 0.822230 +vn -0.005951 0.304239 0.952574 +vn 0.160772 0.446089 0.880398 +vn -0.045167 0.351115 0.935209 +vn 0.125309 0.555132 0.822230 +vn 0.127873 0.354625 0.926206 +vn 0.236335 0.345408 0.908170 +vn -0.000763 0.616901 0.787011 +vn 0.192267 0.617420 0.762749 +vn -0.127873 0.354625 0.926206 +vn 0.000763 0.616901 0.787011 +vn -0.236335 0.345408 0.908170 +vn -0.192267 0.617420 0.762749 +vn 0.308939 0.358501 0.880917 +vn 0.335368 0.382122 0.861080 +vn 0.326395 0.601337 0.729270 +vn 0.402081 0.591449 0.698904 +vn -0.308939 0.358501 0.880917 +vn -0.326395 0.601337 0.729270 +vn -0.335368 0.382122 0.861080 +vn -0.402081 0.591449 0.698904 +vn 0.389050 0.384259 0.837214 +vn 0.534898 0.279977 0.797143 +vn 0.515946 0.514359 0.684957 +vn 0.676565 0.330302 0.658132 +vn -0.389050 0.384259 0.837214 +vn -0.515946 0.514359 0.684957 +vn -0.534898 0.279977 0.797143 +vn -0.676565 0.330302 0.658132 +vn 0.527604 -0.273049 0.804376 +vn 0.622272 -0.186132 0.760308 +vn 0.765069 -0.241066 0.597064 +vn 0.648152 -0.360881 0.670522 +vn -0.527604 -0.273049 0.804376 +vn -0.648152 -0.360881 0.670522 +vn -0.765069 -0.241066 0.597064 +vn -0.622272 -0.186132 0.760308 +vn 0.668783 -0.082888 0.738792 +vn 0.676351 0.002106 0.736564 +vn 0.806848 0.011963 0.590625 +vn 0.811151 -0.097079 0.576708 +vn -0.668783 -0.082888 0.738792 +vn -0.811151 -0.097079 0.576708 +vn -0.806848 0.011963 0.590625 +vn -0.676351 0.002106 0.736564 +vn 0.644093 0.114261 0.756340 +vn 0.774621 0.137913 0.617145 +vn -0.644093 0.114261 0.756340 +vn -0.774621 0.137913 0.617145 +vn -0.505081 0.858516 -0.088290 +vn 0.000000 0.999878 0.013855 +vn -0.746269 0.605304 -0.276864 +vn -0.497360 0.863094 -0.087497 +vn -0.351115 0.871151 0.343181 +vn 0.000000 0.878750 0.477218 +vn 0.505081 0.858516 -0.088290 +vn 0.351115 0.871151 0.343181 +vn 0.497360 0.863094 -0.087497 +vn 0.746269 0.605304 -0.276864 +vn -0.791742 0.472396 -0.387219 +vn -0.644093 0.648366 -0.405835 +vn -0.500351 0.452437 -0.738151 +vn -0.511338 0.377941 -0.771783 +vn 0.791742 0.472396 -0.387219 +vn 0.511338 0.377941 -0.771783 +vn 0.500351 0.452437 -0.738151 +vn 0.644093 0.648366 -0.405835 +vn -0.244026 0.879971 -0.407483 +vn 0.232704 0.890439 -0.391064 +vn 0.082522 0.628529 -0.773370 +vn -0.226386 0.551347 -0.802942 +vn 0.244026 0.879971 -0.407483 +vn 0.226386 0.551347 -0.802942 +vn -0.082522 0.628529 -0.773370 +vn -0.232704 0.890439 -0.391064 +vn 0.467757 0.801263 -0.372997 +vn 0.440931 0.839595 -0.317179 +vn 0.155065 0.854091 -0.496445 +vn 0.120731 0.563433 -0.817255 +vn -0.467757 0.801263 -0.372997 +vn -0.120731 0.563433 -0.817255 +vn -0.155065 0.854091 -0.496445 +vn -0.440931 0.839595 -0.317179 +vn 0.391461 0.891324 -0.228614 +vn 0.375439 0.909665 -0.177435 +vn 0.361827 0.909024 -0.206641 +vn 0.303110 0.910794 -0.280221 +vn -0.391461 0.891324 -0.228614 +vn -0.303110 0.910794 -0.280221 +vn -0.361827 0.909024 -0.206641 +vn -0.375439 0.909665 -0.177435 +vn 0.528886 0.825526 -0.196875 +vn 0.853084 0.460738 -0.244728 +vn 0.807184 0.518357 -0.282327 +vn 0.501938 0.828883 -0.246895 +vn -0.528886 0.825526 -0.196875 +vn -0.501938 0.828883 -0.246895 +vn -0.807184 0.518357 -0.282327 +vn -0.853084 0.460738 -0.244728 +vn 0.971374 0.002319 -0.237403 +vn 0.898373 -0.398968 -0.183538 +vn 0.937559 -0.344340 -0.048463 +vn 0.980651 0.075594 -0.180517 +vn -0.971374 0.002319 -0.237403 +vn -0.980651 0.075594 -0.180517 +vn -0.937559 -0.344340 -0.048463 +vn -0.898373 -0.398968 -0.183538 +vn 0.719077 -0.682333 -0.131443 +vn 0.578265 -0.808924 -0.105930 +vn 0.621387 -0.782037 0.047487 +vn 0.778100 -0.627796 0.020264 +vn -0.719077 -0.682333 -0.131443 +vn -0.778100 -0.627796 0.020264 +vn -0.621387 -0.782037 0.047487 +vn -0.578265 -0.808924 -0.105930 +vn 0.459853 -0.882473 -0.098819 +vn 0.342631 -0.934935 -0.091922 +vn 0.422193 -0.904386 0.061525 +vn 0.496261 -0.866848 0.047670 +vn -0.459853 -0.882473 -0.098819 +vn -0.496261 -0.866848 0.047670 +vn -0.422193 -0.904386 0.061525 +vn -0.342631 -0.934935 -0.091922 +vn 0.238777 -0.534837 -0.810480 +vn 0.209296 -0.259468 -0.942778 +vn 0.000000 -0.250649 -0.968047 +vn 0.000000 -0.511399 -0.859310 +vn 0.000000 -0.786798 -0.617176 +vn 0.285134 -0.784204 -0.551073 +vn 0.645039 -0.633320 -0.427473 +vn 0.504624 -0.480880 -0.716971 +vn 0.340556 -0.236152 -0.910062 +vn -0.238777 -0.534837 -0.810480 +vn -0.504624 -0.480880 -0.716971 +vn -0.645039 -0.633320 -0.427473 +vn -0.285134 -0.784204 -0.551073 +vn -0.209296 -0.259468 -0.942778 +vn -0.340556 -0.236152 -0.910062 +vn 0.175726 -0.141453 -0.974212 +vn 0.130833 -0.248421 -0.959746 +vn 0.000000 -0.286630 -0.958007 +vn 0.000000 -0.159459 -0.987182 +vn 0.220435 -0.098758 -0.970367 +vn 0.138737 -0.172582 -0.975158 +vn -0.175726 -0.141453 -0.974212 +vn -0.220435 -0.098758 -0.970367 +vn -0.130833 -0.248421 -0.959746 +vn -0.138737 -0.172582 -0.975158 +vn 0.077181 -0.463210 -0.882839 +vn 0.057894 -0.754051 -0.654225 +vn 0.000000 -0.782281 -0.622883 +vn 0.000000 -0.504929 -0.863155 +vn 0.062502 -0.372387 -0.925962 +vn 0.046327 -0.694968 -0.717521 +vn -0.077181 -0.463210 -0.882839 +vn -0.062502 -0.372387 -0.925962 +vn -0.057894 -0.754051 -0.654225 +vn -0.046327 -0.694968 -0.717521 +vn 0.078799 -0.966521 -0.244118 +vn 0.101199 -0.949919 -0.295541 +vn 0.000000 -0.976318 -0.216254 +vn -0.078799 -0.966521 -0.244118 +vn -0.101199 -0.949919 -0.295541 +vn 0.211432 -0.911527 -0.352702 +vn 0.604419 -0.677328 -0.419385 +vn 0.134678 -0.614338 -0.777429 +vn 0.408521 -0.468001 -0.783593 +vn -0.211432 -0.911527 -0.352702 +vn -0.134678 -0.614338 -0.777429 +vn -0.604419 -0.677328 -0.419385 +vn -0.408521 -0.468001 -0.783593 +vn 0.885556 -0.204413 -0.417066 +vn 0.930631 0.077364 -0.357646 +vn 0.555773 -0.135075 -0.820276 +vn 0.611133 0.051668 -0.789819 +vn -0.885556 -0.204413 -0.417066 +vn -0.555773 -0.135075 -0.820276 +vn -0.930631 0.077364 -0.357646 +vn -0.611133 0.051668 -0.789819 +vn 0.929411 0.136906 -0.342662 +vn 0.932859 0.142796 -0.330668 +vn 0.668264 0.097812 -0.737449 +vn 0.741050 0.065981 -0.668142 +vn -0.929411 0.136906 -0.342662 +vn -0.668264 0.097812 -0.737449 +vn -0.932859 0.142796 -0.330668 +vn -0.741050 0.065981 -0.668142 +vn 0.334391 -0.015107 -0.942289 +vn 0.224799 -0.069185 -0.971923 +vn 0.511826 -0.112339 -0.851680 +vn -0.334391 -0.015107 -0.942289 +vn -0.511826 -0.112339 -0.851680 +vn -0.224799 -0.069185 -0.971923 +vn 0.138066 -0.257668 -0.956298 +vn -0.138066 -0.257668 -0.956298 +vn 0.734458 -0.242225 -0.633931 +vn 0.886502 -0.270272 -0.375500 +vn 0.928983 0.054781 -0.365978 +vn 0.844508 0.023957 -0.534959 +vn -0.734458 -0.242225 -0.633931 +vn -0.844508 0.023957 -0.534959 +vn -0.928983 0.054781 -0.365978 +vn -0.886502 -0.270272 -0.375500 +vn 0.942167 0.143651 -0.302713 +vn 0.951659 0.199347 -0.233558 +vn -0.942167 0.143651 -0.302713 +vn -0.951659 0.199347 -0.233558 +vn 0.983551 0.149571 0.101169 +vn 0.982940 0.180456 -0.034913 +vn 0.941069 -0.327708 0.083407 +vn 0.893460 -0.355846 0.273995 +vn 0.810053 -0.288949 0.510178 +vn 0.957060 0.182775 0.224891 +vn -0.983551 0.149571 0.101169 +vn -0.957060 0.182775 0.224891 +vn -0.810053 -0.288949 0.510178 +vn -0.893460 -0.355846 0.273995 +vn -0.941069 -0.327708 0.083407 +vn -0.982940 0.180456 -0.034913 +vn 0.960540 0.237526 -0.144658 +vn 0.982849 -0.017487 -0.183416 +vn -0.960540 0.237526 -0.144658 +vn -0.982849 -0.017487 -0.183416 +vn 0.963408 -0.150548 0.221656 +vn 0.612262 -0.787164 0.074007 +vn 0.769280 -0.447340 0.456099 +vn 0.488052 -0.863704 0.125614 +vn -0.963408 -0.150548 0.221656 +vn -0.769280 -0.447340 0.456099 +vn -0.612262 -0.787164 0.074007 +vn -0.488052 -0.863704 0.125614 +vn 0.306284 -0.949522 -0.067415 +vn 0.385022 -0.912259 0.139561 +vn -0.306284 -0.949522 -0.067415 +vn -0.385022 -0.912259 0.139561 +vn 0.286752 -0.493240 -0.821253 +vn 0.000000 -0.499283 -0.866421 +vn 0.000000 -0.258156 -0.966094 +vn 0.288797 -0.271310 -0.918119 +vn 0.412061 -0.299722 -0.860439 +vn 0.418378 -0.496597 -0.760460 +vn 0.452223 -0.685965 -0.569994 +vn 0.276925 -0.705466 -0.652364 +vn 0.000000 -0.719779 -0.694174 +vn -0.286752 -0.493240 -0.821253 +vn -0.276925 -0.705466 -0.652364 +vn -0.452223 -0.685965 -0.569994 +vn -0.418378 -0.496597 -0.760460 +vn -0.412061 -0.299722 -0.860439 +vn -0.288797 -0.271310 -0.918119 +vn 0.268380 -0.842799 -0.466475 +vn 0.000000 -0.864620 -0.502396 +vn 0.476150 -0.788720 -0.388745 +vn 0.482253 -0.849330 -0.214545 +vn 0.260842 -0.922483 -0.284463 +vn 0.000000 -0.949736 -0.312998 +vn -0.268380 -0.842799 -0.466475 +vn -0.260842 -0.922483 -0.284463 +vn -0.482253 -0.849330 -0.214545 +vn -0.476150 -0.788720 -0.388745 +vn 0.272622 -0.948973 -0.158330 +vn 0.000000 -0.982391 -0.186682 +vn 0.513932 -0.854030 -0.080386 +vn 0.599872 -0.800073 -0.004547 +vn 0.298746 -0.944731 -0.134770 +vn 0.000000 -0.986053 -0.166265 +vn -0.272622 -0.948973 -0.158330 +vn -0.298746 -0.944731 -0.134770 +vn -0.599872 -0.800073 -0.004547 +vn -0.513932 -0.854030 -0.080386 +vn 0.314982 -0.910886 -0.266549 +vn 0.000000 -0.948271 -0.317423 +vn 0.655751 -0.741020 -0.144444 +vn -0.314982 -0.910886 -0.266549 +vn -0.655751 -0.741020 -0.144444 +vn 0.916898 -0.382458 -0.114078 +vn 0.810053 -0.578295 0.096652 +vn -0.916898 -0.382458 -0.114078 +vn -0.810053 -0.578295 0.096652 +vn 0.837550 -0.514420 0.183905 +vn 0.962828 -0.236457 0.130467 +vn 0.656911 -0.724540 0.208563 +vn 0.692953 -0.705496 0.148350 +vn 0.897031 -0.422987 0.127995 +vn 0.978484 -0.154546 0.136479 +vn -0.837550 -0.514420 0.183905 +vn -0.897031 -0.422987 0.127995 +vn -0.692953 -0.705496 0.148350 +vn -0.656911 -0.724540 0.208563 +vn -0.962828 -0.236457 0.130467 +vn -0.978484 -0.154546 0.136479 +vn 0.288552 -0.043245 -0.956450 +vn 0.000000 -0.011048 -0.999908 +vn 0.000000 0.319987 -0.947417 +vn 0.295999 0.283822 -0.912015 +vn 0.434156 0.214789 -0.874844 +vn 0.420698 -0.107517 -0.900784 +vn -0.288552 -0.043245 -0.956450 +vn -0.420698 -0.107517 -0.900784 +vn -0.434156 0.214789 -0.874844 +vn -0.295999 0.283822 -0.912015 +vn 0.147130 0.950682 0.272958 +vn 0.000000 0.963225 0.268624 +vn 0.000000 0.824610 0.565661 +vn 0.177679 0.804682 0.566454 +vn 0.422620 0.734001 0.531571 +vn 0.387707 0.884182 0.260537 +vn 0.356822 0.933988 0.016968 +vn 0.133305 0.990936 0.015534 +vn 0.000000 0.999939 0.010834 +vn -0.147130 0.950682 0.272958 +vn -0.133305 0.990936 0.015534 +vn -0.356822 0.933988 0.016968 +vn -0.387707 0.884182 0.260537 +vn -0.422620 0.734001 0.531571 +vn -0.177679 0.804682 0.566454 +vn 0.151311 0.959624 -0.237068 +vn 0.000000 0.971221 -0.238044 +vn 0.344554 0.908902 -0.234840 +vn 0.359935 0.767052 -0.531053 +vn 0.211371 0.823267 -0.526780 +vn 0.000000 0.847011 -0.531510 +vn -0.151311 0.959624 -0.237068 +vn -0.211371 0.823267 -0.526780 +vn -0.359935 0.767052 -0.531053 +vn -0.344554 0.908902 -0.234840 +vn 0.273568 0.593371 -0.756981 +vn 0.000000 0.628010 -0.778191 +vn 0.404309 0.527024 -0.747459 +vn -0.273568 0.593371 -0.756981 +vn -0.404309 0.527024 -0.747459 +vn 0.551958 0.832759 -0.042329 +vn 0.435133 0.900021 0.024720 +vn 0.830073 0.553514 -0.067568 +vn 0.827845 0.512711 0.227424 +vn 0.570269 0.749840 0.335337 +vn 0.518967 0.752251 0.405896 +vn -0.551958 0.832759 -0.042329 +vn -0.570269 0.749840 0.335337 +vn -0.827845 0.512711 0.227424 +vn -0.830073 0.553514 -0.067568 +vn -0.435133 0.900021 0.024720 +vn -0.518967 0.752251 0.405896 +vn 0.552629 0.637471 0.536821 +vn 0.569964 0.611957 0.548265 +vn 0.806818 0.427686 0.407514 +vn 0.836085 0.447737 0.316965 +vn 0.629475 0.658742 0.412000 +vn 0.641011 0.653615 0.402264 +vn -0.552629 0.637471 0.536821 +vn -0.629475 0.658742 0.412000 +vn -0.836085 0.447737 0.316965 +vn -0.806818 0.427686 0.407514 +vn -0.569964 0.611957 0.548265 +vn -0.641011 0.653615 0.402264 +vn 0.708090 0.686544 0.164922 +vn 0.670644 0.723197 0.164922 +vn 0.879086 0.464949 0.104831 +vn 0.901608 0.429914 -0.047243 +vn 0.742759 0.668722 -0.032960 +vn 0.671621 0.740074 -0.034059 +vn -0.708090 0.686544 0.164922 +vn -0.742759 0.668722 -0.032960 +vn -0.901608 0.429914 -0.047243 +vn -0.879086 0.464949 0.104831 +vn -0.670644 0.723197 0.164922 +vn -0.671621 0.740074 -0.034059 +vn 0.756340 0.605823 -0.246803 +vn 0.650349 0.706229 -0.279641 +vn 0.909421 0.365398 -0.198462 +vn 0.871395 0.300211 -0.387921 +vn 0.737114 0.439436 -0.513321 +vn 0.599506 0.554643 -0.576983 +vn -0.756340 0.605823 -0.246803 +vn -0.737114 0.439436 -0.513321 +vn -0.871395 0.300211 -0.387921 +vn -0.909421 0.365398 -0.198462 +vn -0.650349 0.706229 -0.279641 +vn -0.599506 0.554643 -0.576983 +vn 0.700430 0.207892 -0.682730 +vn 0.863552 0.163060 -0.477096 +vn 0.686392 0.052278 -0.725333 +vn 0.645894 -0.041200 -0.762261 +vn 0.581469 0.013855 -0.813440 +vn 0.581927 0.305002 -0.753838 +vn -0.700430 0.207892 -0.682730 +vn -0.581927 0.305002 -0.753838 +vn -0.581469 0.013855 -0.813440 +vn -0.645894 -0.041200 -0.762261 +vn -0.686392 0.052278 -0.725333 +vn -0.863552 0.163060 -0.477096 +vn 0.487533 0.429731 -0.760002 +vn 0.514939 0.118656 -0.848933 +vn 0.494400 0.667928 -0.556230 +vn -0.487533 0.429731 -0.760002 +vn -0.494400 0.667928 -0.556230 +vn -0.514939 0.118656 -0.848933 +vn 0.546007 0.797845 -0.255531 +vn 0.587054 0.809503 -0.007202 +vn -0.546007 0.797845 -0.255531 +vn -0.587054 0.809503 -0.007202 +vn 0.617512 0.759880 0.203070 +vn 0.636219 0.635090 0.438002 +vn -0.617512 0.759880 0.203070 +vn -0.636219 0.635090 0.438002 +vn 0.615406 0.552629 0.561998 +vn 0.433485 0.604114 0.668630 +vn 0.370983 0.745781 0.553270 +vn 0.538408 0.729637 0.421552 +vn -0.615406 0.552629 0.561998 +vn -0.538408 0.729637 0.421552 +vn -0.370983 0.745781 0.553270 +vn -0.433485 0.604114 0.668630 +vn 0.376568 0.926267 0.014100 +vn 0.182409 0.981872 0.051424 +vn -0.376568 0.926267 0.014100 +vn -0.182409 0.981872 0.051424 +vn 0.209052 0.657765 0.723594 +vn 0.000000 0.682516 0.730827 +vn 0.000000 0.610889 0.791681 +vn 0.193762 0.700705 0.686605 +vn -0.209052 0.657765 0.723594 +vn -0.193762 0.700705 0.686605 +vn -0.207953 0.341136 -0.916684 +vn -0.198553 0.775750 -0.598926 +vn 0.207953 0.341136 -0.916684 +vn 0.198553 0.775750 -0.598926 +vn 0.028169 0.936552 0.349315 +vn -0.045198 0.741386 0.669515 +vn -0.028169 0.936552 0.349315 +vn 0.045198 0.741386 0.669515 +vn -0.103214 0.565844 0.817988 +vn 0.000000 0.515732 0.856716 +vn 0.103214 0.565844 0.817988 +vn 0.988647 0.143651 0.043428 +vn 0.969390 0.150731 0.193640 +vn -0.988647 0.143651 0.043428 +vn -0.969390 0.150731 0.193640 +vn 0.961150 0.141972 0.236671 +vn 0.991089 -0.112064 0.071688 +vn 0.996094 -0.087863 0.005982 +vn 0.972930 0.178503 0.146702 +vn -0.961150 0.141972 0.236671 +vn -0.972930 0.178503 0.146702 +vn -0.996094 -0.087863 0.005982 +vn -0.991089 -0.112064 0.071688 +vn 0.976745 0.214362 0.000549 +vn 0.997345 0.067721 -0.026521 +vn 0.862911 0.481582 0.152989 +vn 0.974273 0.205939 -0.091464 +vn -0.976745 0.214362 0.000549 +vn -0.974273 0.205939 -0.091464 +vn -0.862911 0.481582 0.152989 +vn -0.997345 0.067721 -0.026521 +vn 0.958953 0.179296 -0.219642 +vn 0.659688 0.631062 -0.408063 +vn -0.958953 0.179296 -0.219642 +vn -0.659688 0.631062 -0.408063 +vn 0.634510 -0.772881 -0.002594 +vn 0.677480 -0.727989 0.104923 +vn 0.611896 -0.775109 -0.157231 +vn 0.552232 -0.817194 -0.164861 +vn 0.554521 -0.831813 0.023713 +vn 0.574145 -0.803827 0.155492 +vn -0.634510 -0.772881 -0.002594 +vn -0.554521 -0.831813 0.023713 +vn -0.552232 -0.817194 -0.164861 +vn -0.611896 -0.775109 -0.157231 +vn -0.677480 -0.727989 0.104923 +vn -0.574145 -0.803827 0.155492 +vn 0.431562 -0.900662 0.050020 +vn 0.514512 -0.853725 0.079958 +vn 0.484573 -0.859371 0.163091 +vn 0.372662 -0.912870 -0.166662 +vn 0.220649 -0.975311 -0.008881 +vn -0.431562 -0.900662 0.050020 +vn -0.372662 -0.912870 -0.166662 +vn -0.484573 -0.859371 0.163091 +vn -0.514512 -0.853725 0.079958 +vn -0.220649 -0.975311 -0.008881 +vn 0.510849 -0.837336 0.194555 +vn 0.517472 -0.832362 0.198401 +vn -0.510849 -0.837336 0.194555 +vn -0.517472 -0.832362 0.198401 +vn 0.636128 -0.723289 0.268624 +vn 0.531968 -0.730094 0.428877 +vn 0.691885 -0.690512 0.210791 +vn -0.636128 -0.723289 0.268624 +vn -0.691885 -0.690512 0.210791 +vn -0.531968 -0.730094 0.428877 +vn 0.719932 -0.671804 0.174139 +vn -0.719932 -0.671804 0.174139 +vn 0.479995 -0.700980 0.527421 +vn -0.479995 -0.700980 0.527421 +vn 0.553697 -0.316263 -0.770287 +vn 0.538865 -0.290139 -0.790796 +vn 0.330943 -0.613880 -0.716636 +vn 0.439894 -0.608448 -0.660482 +vn 0.568682 -0.533830 -0.625782 +vn 0.555193 -0.280313 -0.783044 +vn -0.553697 -0.316263 -0.770287 +vn -0.555193 -0.280313 -0.783044 +vn -0.568682 -0.533830 -0.625782 +vn -0.439894 -0.608448 -0.660482 +vn -0.330943 -0.613880 -0.716636 +vn -0.538865 -0.290139 -0.790796 +vn 0.506241 -0.193304 -0.840419 +vn 0.513321 -0.365490 -0.776452 +vn -0.506241 -0.193304 -0.840419 +vn -0.513321 -0.365490 -0.776452 +vn 0.352123 -0.829249 -0.433973 +vn 0.124088 -0.887814 -0.443129 +vn 0.560656 -0.740715 -0.370067 +vn -0.352123 -0.829249 -0.433973 +vn -0.560656 -0.740715 -0.370067 +vn -0.124088 -0.887814 -0.443129 +vn 0.612964 -0.717734 -0.330302 +vn 0.591662 -0.625507 -0.508560 +vn -0.612964 -0.717734 -0.330302 +vn -0.591662 -0.625507 -0.508560 +vn 0.521714 -0.487045 -0.700400 +vn -0.521714 -0.487045 -0.700400 +vn 0.209479 0.534135 0.818995 +vn 0.309488 0.497574 0.810297 +vn -0.128636 0.958678 0.253670 +vn -0.277871 0.920347 0.275124 +vn -0.451857 0.830683 0.325175 +vn 0.052919 0.487991 0.871212 +vn 0.519364 -0.143529 0.842372 +vn 0.585803 -0.145634 0.797235 +vn 0.565172 -0.212806 0.797021 +vn -0.209479 0.534135 0.818995 +vn -0.585803 -0.145634 0.797235 +vn -0.519364 -0.143529 0.842372 +vn -0.052919 0.487991 0.871212 +vn 0.451857 0.830683 0.325175 +vn 0.277871 0.920347 0.275124 +vn 0.128636 0.958678 0.253670 +vn -0.309488 0.497574 0.810297 +vn -0.565172 -0.212806 0.797021 +vn 0.368633 0.426740 0.825800 +vn 0.088748 0.954375 0.285012 +vn 0.474837 -0.253914 0.842616 +vn 0.286019 -0.227882 0.930692 +vn 0.416242 0.313120 0.853633 +vn 0.432173 0.832484 0.346629 +vn -0.368633 0.426740 0.825800 +vn -0.416242 0.313120 0.853633 +vn -0.286019 -0.227882 0.930692 +vn -0.474837 -0.253914 0.842616 +vn -0.088748 0.954375 0.285012 +vn -0.432173 0.832484 0.346629 +vn 0.384899 0.142399 0.911893 +vn 0.719260 0.472427 0.509323 +vn 0.015687 -0.158208 0.987274 +vn -0.158818 -0.063173 0.985260 +vn 0.330943 0.009430 0.943571 +vn 0.762993 0.105686 0.637684 +vn -0.384899 0.142399 0.911893 +vn -0.330943 0.009430 0.943571 +vn 0.158818 -0.063173 0.985260 +vn -0.015687 -0.158208 0.987274 +vn -0.719260 0.472427 0.509323 +vn -0.762993 0.105686 0.637684 +vn 0.335917 -0.072848 0.939055 +vn 0.735893 -0.192053 0.649251 +vn -0.209998 0.093936 0.973144 +vn -0.082522 0.330973 0.940001 +vn 0.414380 -0.113132 0.903012 +vn 0.699118 -0.451277 0.554582 +vn -0.335917 -0.072848 0.939055 +vn -0.414380 -0.113132 0.903012 +vn 0.082522 0.330973 0.940001 +vn 0.209998 0.093936 0.973144 +vn -0.735893 -0.192053 0.649251 +vn -0.699118 -0.451277 0.554582 +vn 0.520920 -0.197363 0.830439 +vn 0.605914 -0.676687 0.418226 +vn 0.182531 0.440687 0.878872 +vn 0.453230 0.337626 0.824946 +vn 0.592914 -0.341716 0.729148 +vn 0.473220 -0.824915 0.309122 +vn -0.520920 -0.197363 0.830439 +vn -0.592914 -0.341716 0.729148 +vn -0.453230 0.337626 0.824946 +vn -0.182531 0.440687 0.878872 +vn -0.605914 -0.676687 0.418226 +vn -0.473220 -0.824915 0.309122 +vn 0.539781 -0.447951 0.712699 +vn 0.318339 -0.905057 0.281961 +vn 0.548692 0.202307 0.811151 +vn 0.358654 0.326518 0.874477 +vn 0.265175 -0.352886 0.897275 +vn 0.067537 -0.934385 0.349773 +vn -0.539781 -0.447951 0.712699 +vn -0.265175 -0.352886 0.897275 +vn -0.358654 0.326518 0.874477 +vn -0.548692 0.202307 0.811151 +vn -0.318339 -0.905057 0.281961 +vn -0.067537 -0.934385 0.349773 +vn 0.363628 0.683462 0.632923 +vn 0.127232 0.795526 0.592364 +vn -0.150975 0.949828 0.273812 +vn 0.124302 0.922849 0.364483 +vn 0.148930 0.913938 0.377453 +vn 0.288919 0.722434 0.628132 +vn -0.363628 0.683462 0.632923 +vn -0.288919 0.722434 0.628132 +vn -0.148930 0.913938 0.377453 +vn -0.124302 0.922849 0.364483 +vn 0.150975 0.949828 0.273812 +vn -0.127232 0.795526 0.592364 +vn -0.234443 0.799249 0.553362 +vn -0.574297 0.610431 0.545457 +vn -0.779595 0.625935 0.019868 +vn -0.486496 0.860500 0.151006 +vn 0.234443 0.799249 0.553362 +vn 0.486496 0.860500 0.151006 +vn 0.779595 0.625935 0.019868 +vn 0.574297 0.610431 0.545457 +vn -0.769890 0.263863 0.581042 +vn -0.704611 -0.087863 0.704093 +vn -0.990295 -0.073580 0.117740 +vn -0.952635 0.303598 -0.016480 +vn 0.769890 0.263863 0.581042 +vn 0.952635 0.303598 -0.016480 +vn 0.990295 -0.073580 0.117740 +vn 0.704611 -0.087863 0.704093 +vn -0.387768 -0.376843 0.841151 +vn 0.100284 -0.528642 0.842860 +vn -0.094821 -0.757225 0.646168 +vn -0.738731 -0.525468 0.422071 +vn 0.387768 -0.376843 0.841151 +vn 0.738731 -0.525468 0.422071 +vn 0.094821 -0.757225 0.646168 +vn -0.100284 -0.528642 0.842860 +vn 0.422437 -0.542375 0.726157 +vn 0.569414 -0.503067 0.650105 +vn 0.529191 -0.653615 0.541002 +vn 0.346049 -0.718528 0.603259 +vn -0.422437 -0.542375 0.726157 +vn -0.346049 -0.718528 0.603259 +vn -0.529191 -0.653615 0.541002 +vn -0.569414 -0.503067 0.650105 +vn 0.654408 -0.438581 0.615894 +vn 0.652272 -0.404614 0.640919 +vn 0.695608 -0.531571 0.483261 +vn 0.647786 -0.578509 0.495621 +vn -0.654408 -0.438581 0.615894 +vn -0.647786 -0.578509 0.495621 +vn -0.695608 -0.531571 0.483261 +vn -0.652272 -0.404614 0.640919 +vn 0.736076 -0.673910 0.063051 +vn 0.844966 -0.534532 -0.016541 +vn 0.465987 -0.659810 0.589465 +vn 0.719077 0.184545 0.669942 +vn 0.905606 -0.420331 0.056154 +vn 0.906552 -0.403455 -0.123905 +vn -0.736076 -0.673910 0.063051 +vn -0.905606 -0.420331 0.056154 +vn -0.719077 0.184545 0.669942 +vn -0.465987 -0.659810 0.589465 +vn -0.844966 -0.534532 -0.016541 +vn -0.906552 -0.403455 -0.123905 +vn 0.072939 -0.280648 0.957030 +vn -0.086459 -0.935301 0.343089 +vn 0.180700 0.506149 0.843287 +vn -0.072939 -0.280648 0.957030 +vn -0.180700 0.506149 0.843287 +vn 0.086459 -0.935301 0.343089 +vn 0.937071 -0.347606 -0.032167 +vn 0.937071 -0.333140 -0.104312 +vn -0.937071 -0.347606 -0.032167 +vn -0.937071 -0.333140 -0.104312 +vn 0.196753 0.375072 0.905850 +vn -0.321055 0.893460 0.313974 +vn 0.838160 0.062502 0.541795 +vn 0.692587 -0.287454 0.661550 +vn 0.500412 -0.212104 0.839381 +vn -0.196753 0.375072 0.905850 +vn -0.500412 -0.212104 0.839381 +vn -0.692587 -0.287454 0.661550 +vn -0.838160 0.062502 0.541795 +vn 0.321055 0.893460 0.313974 +vn 0.170232 0.840114 0.514969 +vn 0.101382 0.954955 0.278817 +vn 0.516526 0.734611 0.439924 +vn 0.482498 0.642048 0.595752 +vn -0.170232 0.840114 0.514969 +vn -0.482498 0.642048 0.595752 +vn -0.516526 0.734611 0.439924 +vn -0.101382 0.954955 0.278817 +vn 0.701376 0.176153 0.690664 +vn 0.808008 0.000305 0.589129 +vn 0.766289 -0.015839 0.642262 +vn 0.690451 0.137425 0.710166 +vn 0.930479 -0.143223 0.337138 +vn 0.879299 0.032075 0.475112 +vn -0.701376 0.176153 0.690664 +vn -0.879299 0.032075 0.475112 +vn -0.930479 -0.143223 0.337138 +vn -0.690451 0.137425 0.710166 +vn -0.766289 -0.015839 0.642262 +vn -0.808008 0.000305 0.589129 +vn 0.634571 0.390851 0.666707 +vn 0.598041 0.631001 0.494095 +vn 0.923490 0.029298 0.382427 +vn 0.895505 0.003876 0.445021 +vn 0.697745 0.361339 0.618488 +vn 0.567370 0.646229 0.510300 +vn -0.634571 0.390851 0.666707 +vn -0.697745 0.361339 0.618488 +vn -0.895505 0.003876 0.445021 +vn -0.923490 0.029298 0.382427 +vn -0.598041 0.631001 0.494095 +vn -0.567370 0.646229 0.510300 +vn 0.859279 -0.428602 0.279122 +vn 0.799005 -0.537950 0.268654 +vn 0.724265 -0.574358 0.381481 +vn 0.768731 -0.471847 0.431684 +vn 0.885067 -0.408612 0.222755 +vn 0.912687 -0.390545 0.119999 +vn -0.859279 -0.428602 0.279122 +vn -0.912687 -0.390545 0.119999 +vn -0.885067 -0.408612 0.222755 +vn -0.768731 -0.471847 0.431684 +vn -0.724265 -0.574358 0.381481 +vn -0.799005 -0.537950 0.268654 +vn 0.690695 -0.430616 0.580920 +vn 0.618641 -0.408277 0.671194 +vn 0.845790 -0.350261 0.402387 +vn 0.698721 -0.538987 0.470351 +vn 0.689261 -0.521226 0.503159 +vn -0.690695 -0.430616 0.580920 +vn -0.698721 -0.538987 0.470351 +vn -0.845790 -0.350261 0.402387 +vn -0.618641 -0.408277 0.671194 +vn -0.689261 -0.521226 0.503159 +vn 0.607532 -0.405286 0.683096 +vn 0.694205 -0.520005 0.497635 +vn -0.607532 -0.405286 0.683096 +vn -0.694205 -0.520005 0.497635 +vn 0.982055 -0.147588 0.117283 +vn 0.952727 -0.303751 0.002106 +vn -0.982055 -0.147588 0.117283 +vn -0.952727 -0.303751 0.002106 +vn 0.926328 -0.372692 -0.054415 +vn 0.941984 -0.332560 -0.044923 +vn -0.926328 -0.372692 -0.054415 +vn -0.941984 -0.332560 -0.044923 +vn 0.977569 -0.182928 0.104068 +vn -0.977569 -0.182928 0.104068 +vn 0.707999 -0.549516 0.443525 +vn 0.690878 -0.544816 0.475173 +vn 0.714774 -0.555223 0.425184 +vn 0.584887 -0.375927 0.718680 +vn 0.609638 -0.346934 0.712699 +vn 0.619343 -0.281106 0.733055 +vn -0.707999 -0.549516 0.443525 +vn -0.609638 -0.346934 0.712699 +vn -0.584887 -0.375927 0.718680 +vn -0.714774 -0.555223 0.425184 +vn -0.690878 -0.544816 0.475173 +vn -0.619343 -0.281106 0.733055 +vn 0.705802 -0.572039 0.417829 +vn 0.687246 -0.607318 0.398541 +vn 0.475661 -0.408002 0.779260 +vn 0.536027 -0.392132 0.747581 +vn -0.705802 -0.572039 0.417829 +vn -0.536027 -0.392132 0.747581 +vn -0.475661 -0.408002 0.779260 +vn -0.687246 -0.607318 0.398541 +vn 0.730583 -0.570421 0.375225 +vn 0.885372 0.034120 0.463576 +vn 0.619587 -0.055605 0.782922 +vn 0.497299 -0.348430 0.794488 +vn -0.730583 -0.570421 0.375225 +vn -0.497299 -0.348430 0.794488 +vn -0.619587 -0.055605 0.782922 +vn -0.885372 0.034120 0.463576 +vn 0.570269 0.679434 0.461623 +vn 0.590411 0.667165 0.454146 +vn 0.422071 0.390790 0.817988 +vn 0.428846 0.399457 0.810205 +vn -0.570269 0.679434 0.461623 +vn -0.428846 0.399457 0.810205 +vn -0.422071 0.390790 0.817988 +vn -0.590411 0.667165 0.454146 +vn 0.795618 0.299814 0.526353 +vn 0.839259 -0.140477 0.525224 +vn 0.426160 0.141331 0.893521 +vn 0.512314 0.218146 0.830622 +vn -0.795618 0.299814 0.526353 +vn -0.512314 0.218146 0.830622 +vn -0.426160 0.141331 0.893521 +vn -0.839259 -0.140477 0.525224 +vn 0.811670 -0.029969 0.583300 +vn 0.569933 0.671621 0.473312 +vn 0.196142 0.751152 0.630299 +vn 0.401044 0.303232 0.864406 +vn -0.811670 -0.029969 0.583300 +vn -0.401044 0.303232 0.864406 +vn -0.196142 0.751152 0.630299 +vn -0.569933 0.671621 0.473312 +vn 0.020386 0.969848 0.242744 +vn 0.047304 0.951781 0.303079 +vn 0.115604 0.781182 0.613453 +vn -0.127476 0.869442 0.477248 +vn -0.020386 0.969848 0.242744 +vn 0.127476 0.869442 0.477248 +vn -0.115604 0.781182 0.613453 +vn -0.047304 0.951781 0.303079 +vn 0.638417 -0.564226 0.523484 +vn 0.522507 -0.634632 0.569353 +vn 0.593188 -0.200720 0.779626 +vn 0.527177 -0.185858 0.829157 +vn -0.638417 -0.564226 0.523484 +vn -0.593188 -0.200720 0.779626 +vn -0.522507 -0.634632 0.569353 +vn -0.527177 -0.185858 0.829157 +vn 0.311045 -0.738121 0.598621 +vn -0.181371 -0.756493 0.628285 +vn 0.399609 -0.227363 0.888028 +vn 0.221595 -0.208228 0.952635 +vn -0.311045 -0.738121 0.598621 +vn -0.399609 -0.227363 0.888028 +vn 0.181371 -0.756493 0.628285 +vn -0.221595 -0.208228 0.952635 +vn -0.682760 -0.473373 0.556536 +vn -0.892361 -0.070894 0.445692 +vn 0.053926 -0.077456 0.995514 +vn 0.008545 0.006623 0.999939 +vn 0.682760 -0.473373 0.556536 +vn -0.053926 -0.077456 0.995514 +vn 0.892361 -0.070894 0.445692 +vn -0.008545 0.006623 0.999939 +vn -0.846126 0.296640 0.442732 +vn -0.630207 0.621174 0.465773 +vn 0.063662 0.051057 0.996643 +vn 0.142338 0.143925 0.979278 +vn 0.846126 0.296640 0.442732 +vn -0.063662 0.051057 0.996643 +vn 0.630207 0.621174 0.465773 +vn -0.142338 0.143925 0.979278 +vn -0.330302 0.830805 0.447890 +vn -0.041536 0.910733 0.410840 +vn 0.250374 0.260567 0.932401 +vn 0.380749 0.392041 0.837428 +vn 0.330302 0.830805 0.447890 +vn -0.250374 0.260567 0.932401 +vn 0.041536 0.910733 0.410840 +vn -0.380749 0.392041 0.837428 +vn 0.115299 0.922727 0.367718 +vn 0.433882 0.528275 0.729820 +vn -0.115299 0.922727 0.367718 +vn -0.433882 0.528275 0.729820 +vn 0.190069 0.106021 0.976012 +vn 0.184973 0.080081 0.979461 +vn 0.071474 0.275521 0.958617 +vn 0.256203 0.189581 0.947844 +vn 0.223090 0.083560 0.971191 +vn 0.200720 0.009705 0.979583 +vn -0.190069 0.106021 0.976012 +vn -0.223090 0.083560 0.971191 +vn -0.256203 0.189581 0.947844 +vn -0.071474 0.275521 0.958617 +vn -0.184973 0.080081 0.979461 +vn -0.200720 0.009705 0.979583 +vn 0.330729 0.049165 0.942412 +vn 0.287027 0.000153 0.957884 +vn 0.426984 0.070040 0.901517 +vn 0.458724 -0.036988 0.887783 +vn 0.373791 -0.001068 0.927488 +vn 0.342174 -0.009735 0.939573 +vn -0.330729 0.049165 0.942412 +vn -0.373791 -0.001068 0.927488 +vn -0.458724 -0.036988 0.887783 +vn -0.426984 0.070040 0.901517 +vn -0.287027 0.000153 0.957884 +vn -0.342174 -0.009735 0.939573 +vn 0.358135 -0.030854 0.933134 +vn 0.353832 -0.002441 0.935270 +vn 0.421094 -0.110385 0.900235 +vn 0.385754 -0.160710 0.908475 +vn 0.308817 -0.040498 0.950255 +vn 0.319712 0.044679 0.946440 +vn -0.358135 -0.030854 0.933134 +vn -0.308817 -0.040498 0.950255 +vn -0.385754 -0.160710 0.908475 +vn -0.421094 -0.110385 0.900235 +vn -0.353832 -0.002441 0.935270 +vn -0.319712 0.044679 0.946440 +vn 0.319590 0.010529 0.947478 +vn 0.307443 0.165899 0.936979 +vn 0.417768 -0.135441 0.898373 +vn 0.496597 -0.014313 0.867855 +vn 0.432875 0.131718 0.891751 +vn 0.374279 0.302072 0.876705 +vn -0.319590 0.010529 0.947478 +vn -0.432875 0.131718 0.891751 +vn -0.496597 -0.014313 0.867855 +vn -0.417768 -0.135441 0.898373 +vn -0.307443 0.165899 0.936979 +vn -0.374279 0.302072 0.876705 +vn 0.404675 0.175359 0.897458 +vn 0.389996 0.311075 0.866665 +vn 0.435591 0.013337 0.900021 +vn -0.404675 0.175359 0.897458 +vn -0.435591 0.013337 0.900021 +vn -0.389996 0.311075 0.866665 +vn 0.431257 -0.077639 0.898862 +vn 0.386761 -0.107608 0.915860 +vn -0.431257 -0.077639 0.898862 +vn -0.386761 -0.107608 0.915860 +vn 0.310953 -0.111362 0.943846 +vn 0.221564 -0.101260 0.969848 +vn -0.310953 -0.111362 0.943846 +vn -0.221564 -0.101260 0.969848 +vn 0.209540 -0.015351 0.977660 +vn -0.209540 -0.015351 0.977660 +vn -0.088900 0.548936 0.831111 +vn 0.228919 0.393719 0.890255 +vn 0.088900 0.548936 0.831111 +vn -0.228919 0.393719 0.890255 +vn 0.514298 0.133152 0.847194 +vn 0.520341 -0.017182 0.853755 +vn -0.514298 0.133152 0.847194 +vn -0.520341 -0.017182 0.853755 +vn 0.454390 -0.122684 0.882290 +vn 0.441420 -0.177038 0.879635 +vn -0.454390 -0.122684 0.882290 +vn -0.441420 -0.177038 0.879635 +vn 0.484085 -0.142033 0.863399 +vn 0.520737 0.011444 0.853603 +vn -0.484085 -0.142033 0.863399 +vn -0.520737 0.011444 0.853603 +vn 0.492141 0.201575 0.846828 +vn 0.423444 0.324931 0.845637 +vn -0.492141 0.201575 0.846828 +vn -0.423444 0.324931 0.845637 +vn 0.392621 0.359020 0.846706 +vn -0.392621 0.359020 0.846706 +vn -0.026399 -0.935240 -0.352977 +vn 0.137211 -0.908963 -0.393567 +vn -0.098086 -0.943632 -0.316080 +vn -0.183935 -0.532579 -0.826136 +vn -0.344218 -0.434950 -0.832026 +vn -0.224647 -0.413709 -0.882229 +vn 0.026399 -0.935240 -0.352977 +vn 0.344218 -0.434950 -0.832026 +vn 0.183935 -0.532579 -0.826136 +vn 0.098086 -0.943632 -0.316080 +vn -0.137211 -0.908963 -0.393567 +vn 0.224647 -0.413709 -0.882229 +vn 0.424421 -0.837733 -0.343577 +vn 0.734916 -0.653279 -0.181860 +vn 0.031709 -0.412824 -0.910245 +vn 0.423048 -0.386914 -0.819330 +vn -0.424421 -0.837733 -0.343577 +vn -0.031709 -0.412824 -0.910245 +vn -0.734916 -0.653279 -0.181860 +vn -0.423048 -0.386914 -0.819330 +vn 0.952635 -0.293619 0.078890 +vn 0.984252 0.134922 0.114139 +vn 0.760125 -0.172033 -0.626545 +vn 0.931089 0.104984 -0.349284 +vn -0.952635 -0.293619 0.078890 +vn -0.760125 -0.172033 -0.626545 +vn -0.984252 0.134922 0.114139 +vn -0.931089 0.104984 -0.349284 +vn 0.799799 0.574877 -0.172643 +vn 0.294778 0.852626 -0.431379 +vn 0.620319 0.322001 -0.715171 +vn 0.097324 0.416272 -0.903989 +vn -0.799799 0.574877 -0.172643 +vn -0.620319 0.322001 -0.715171 +vn -0.294778 0.852626 -0.431379 +vn -0.097324 0.416272 -0.903989 +vn -0.187536 0.859859 -0.474776 +vn -0.405438 0.807031 -0.429273 +vn -0.277871 0.341594 -0.897824 +vn -0.439253 0.320627 -0.839167 +vn 0.187536 0.859859 -0.474776 +vn 0.277871 0.341594 -0.897824 +vn 0.405438 0.807031 -0.429273 +vn 0.439253 0.320627 -0.839167 +vn -0.505356 0.789941 -0.347209 +vn -0.597308 0.761711 -0.250954 +vn -0.515336 0.340556 -0.786401 +vn -0.456252 0.414350 -0.787469 +vn 0.505356 0.789941 -0.347209 +vn 0.515336 0.340556 -0.786401 +vn 0.597308 0.761711 -0.250954 +vn 0.456252 0.414350 -0.787469 +vn -0.434492 -0.022065 -0.900388 +vn -0.370830 -0.020356 -0.928465 +vn -0.142460 -0.078219 -0.986694 +vn 0.434492 -0.022065 -0.900388 +vn 0.142460 -0.078219 -0.986694 +vn 0.370830 -0.020356 -0.928465 +vn -0.210334 -0.024171 -0.977325 +vn 0.006409 -0.019349 -0.999786 +vn 0.210334 -0.024171 -0.977325 +vn -0.006409 -0.019349 -0.999786 +vn 0.483657 0.054659 -0.873531 +vn -0.483657 0.054659 -0.873531 +vn -0.350017 0.872494 -0.340922 +vn 0.213660 0.358959 -0.908536 +vn 0.350017 0.872494 -0.340922 +vn -0.213660 0.358959 -0.908536 +vn 0.348430 -0.203070 -0.915067 +vn 0.121982 -0.595965 -0.793664 +vn -0.348430 -0.203070 -0.915067 +vn -0.121982 -0.595965 -0.793664 +vn -0.093081 -0.918058 -0.385296 +vn 0.093081 -0.918058 -0.385296 +usemtl (null) +s 1 +f 1/1/1 504/2/2 1552/3/3 +f 1/1/1 1552/3/3 501/4/4 +f 1/1/1 501/4/4 1506/5/5 +f 1/1/1 1506/5/5 502/6/6 +f 1/1/1 502/6/6 1508/7/7 +f 1/1/1 1508/7/7 503/8/8 +f 1/1/1 503/8/8 1550/9/9 +f 1/1/1 1550/9/9 504/2/2 +f 2/10/10 508/11/11 1509/12/12 +f 2/10/10 1509/12/12 505/13/13 +f 2/10/10 505/13/13 1507/14/14 +f 2/10/10 1507/14/14 506/15/15 +f 2/10/10 506/15/15 1553/16/16 +f 2/10/10 1553/16/16 507/17/17 +f 2/10/10 507/17/17 1551/18/18 +f 2/10/10 1551/18/18 508/11/11 +f 3/19/19 511/20/20 1550/9/9 +f 3/19/19 1550/9/9 503/8/8 +f 3/19/19 503/8/8 1508/7/7 +f 3/19/19 1508/7/7 509/21/21 +f 3/19/19 509/21/21 1510/22/22 +f 3/19/19 1510/22/22 510/23/23 +f 3/19/19 510/23/23 1548/24/24 +f 3/19/19 1548/24/24 511/20/20 +f 4/25/25 514/26/26 1511/27/27 +f 4/25/25 1511/27/27 512/28/28 +f 4/25/25 512/28/28 1509/12/12 +f 4/25/25 1509/12/12 508/11/11 +f 4/25/25 508/11/11 1551/18/18 +f 4/25/25 1551/18/18 513/29/29 +f 4/25/25 513/29/29 1549/30/30 +f 4/25/25 1549/30/30 514/26/26 +f 5/31/31 509/21/21 1508/7/7 +f 5/31/31 1508/7/7 515/32/32 +f 5/33/31 515/34/32 1514/35/33 +f 5/33/31 1514/35/33 516/36/34 +f 5/31/31 516/37/34 1512/38/35 +f 5/31/31 1512/38/35 517/39/36 +f 5/31/31 517/39/36 1510/22/22 +f 5/31/31 1510/22/22 509/21/21 +f 6/40/37 520/41/38 1513/42/39 +f 6/40/37 1513/42/39 518/43/40 +f 6/40/37 518/43/40 1515/44/41 +f 6/40/37 1515/44/41 519/45/42 +f 6/40/37 519/45/42 1509/12/12 +f 6/40/37 1509/12/12 512/28/28 +f 6/40/37 512/28/28 1511/27/27 +f 6/40/37 1511/27/27 520/41/38 +f 7/46/43 502/6/6 1506/5/5 +f 7/46/43 1506/5/5 521/47/44 +f 7/48/43 521/49/44 1516/50/45 +f 7/48/43 1516/50/45 522/51/46 +f 7/48/43 522/51/46 1514/35/33 +f 7/48/43 1514/35/33 515/34/32 +f 7/46/43 515/32/32 1508/7/7 +f 7/46/43 1508/7/7 502/6/6 +f 8/52/47 519/45/42 1515/44/41 +f 8/52/47 1515/44/41 523/53/48 +f 8/52/47 523/53/48 1517/54/49 +f 8/52/47 1517/54/49 524/55/50 +f 8/52/47 524/55/50 1507/14/14 +f 8/52/47 1507/14/14 505/13/13 +f 8/52/47 505/13/13 1509/12/12 +f 8/52/47 1509/12/12 519/45/42 +f 9/56/51 522/51/46 1516/50/45 +f 9/56/51 1516/50/45 525/57/52 +f 9/56/51 525/57/52 1518/58/53 +f 9/56/51 1518/58/53 526/59/54 +f 9/56/51 526/59/54 1520/60/55 +f 9/56/51 1520/60/55 527/61/56 +f 9/56/51 527/61/56 1514/35/33 +f 9/56/51 1514/35/33 522/51/46 +f 10/62/57 530/63/58 1521/64/59 +f 10/62/57 1521/64/59 528/65/60 +f 10/62/57 528/65/60 1519/66/61 +f 10/62/57 1519/66/61 529/67/62 +f 10/62/57 529/67/62 1517/54/49 +f 10/62/57 1517/54/49 523/53/48 +f 10/62/57 523/53/48 1515/44/41 +f 10/62/57 1515/44/41 530/63/58 +f 11/68/63 516/36/34 1514/35/33 +f 11/68/63 1514/35/33 527/61/56 +f 11/68/63 527/61/56 1520/60/55 +f 11/68/63 1520/60/55 531/69/64 +f 11/68/63 531/69/64 1522/70/65 +f 11/68/63 1522/70/65 532/71/66 +f 11/68/63 532/71/66 1512/72/35 +f 11/68/63 1512/72/35 516/36/34 +f 12/73/67 534/74/68 1523/75/69 +f 12/73/67 1523/75/69 533/76/70 +f 12/73/67 533/76/70 1521/77/59 +f 12/73/67 1521/77/59 530/78/58 +f 12/79/67 530/63/58 1515/44/41 +f 12/79/67 1515/44/41 518/43/40 +f 12/79/67 518/43/40 1513/42/39 +f 12/79/67 1513/42/39 534/80/68 +f 13/81/71 531/69/64 1520/60/55 +f 13/81/71 1520/60/55 535/82/72 +f 13/81/71 535/82/72 1526/83/73 +f 13/81/71 1526/83/73 536/84/74 +f 13/81/71 536/84/74 1524/85/75 +f 13/81/71 1524/85/75 537/86/76 +f 13/81/71 537/86/76 1522/70/65 +f 13/81/71 1522/70/65 531/69/64 +f 14/87/77 540/88/78 1525/89/79 +f 14/87/77 1525/89/79 538/90/80 +f 14/87/77 538/90/80 1527/91/81 +f 14/87/77 1527/91/81 539/92/82 +f 14/87/77 539/92/82 1521/77/59 +f 14/87/77 1521/77/59 533/76/70 +f 14/87/77 533/76/70 1523/75/69 +f 14/87/77 1523/75/69 540/88/78 +f 15/93/83 526/59/54 1518/58/53 +f 15/93/83 1518/58/53 541/94/84 +f 15/93/83 541/94/84 1528/95/85 +f 15/93/83 1528/95/85 542/96/86 +f 15/93/83 542/96/86 1526/83/73 +f 15/93/83 1526/83/73 535/82/72 +f 15/93/83 535/82/72 1520/60/55 +f 15/93/83 1520/60/55 526/59/54 +f 16/97/87 539/92/82 1527/91/81 +f 16/97/87 1527/91/81 543/98/88 +f 16/97/87 543/98/88 1529/99/89 +f 16/97/87 1529/99/89 544/100/90 +f 16/101/87 544/102/90 1519/66/61 +f 16/101/87 1519/66/61 528/65/60 +f 16/101/87 528/65/60 1521/64/59 +f 16/101/87 1521/64/59 539/103/82 +f 17/104/91 542/96/86 1528/95/85 +f 17/104/91 1528/95/85 545/105/92 +f 17/104/91 545/105/92 1530/106/93 +f 17/104/91 1530/106/93 546/107/94 +f 17/104/91 546/107/94 1532/108/95 +f 17/104/91 1532/108/95 547/109/96 +f 17/104/91 547/109/96 1526/83/73 +f 17/104/91 1526/83/73 542/96/86 +f 18/110/97 550/111/98 1533/112/99 +f 18/110/97 1533/112/99 548/113/100 +f 18/110/97 548/113/100 1531/114/101 +f 18/110/97 1531/114/101 549/115/102 +f 18/110/97 549/115/102 1529/116/89 +f 18/110/97 1529/116/89 543/117/88 +f 18/110/97 543/117/88 1527/118/81 +f 18/110/97 1527/118/81 550/111/98 +f 19/119/103 536/120/74 1526/121/73 +f 19/119/103 1526/121/73 547/122/96 +f 19/119/103 547/122/96 1532/123/95 +f 19/119/103 1532/123/95 551/124/104 +f 19/119/103 551/124/104 1534/125/105 +f 19/119/103 1534/125/105 552/126/106 +f 19/119/103 552/126/106 1524/127/75 +f 19/119/103 1524/127/75 536/120/74 +f 20/128/107 554/129/108 1535/130/109 +f 20/128/107 1535/130/109 553/131/110 +f 20/128/107 553/131/110 1533/112/99 +f 20/128/107 1533/112/99 550/111/98 +f 20/128/107 550/111/98 1527/118/81 +f 20/128/107 1527/118/81 538/132/80 +f 20/133/107 538/90/80 1525/89/79 +f 20/133/107 1525/89/79 554/134/108 +f 21/135/111 551/124/104 1532/123/95 +f 21/135/111 1532/123/95 555/136/112 +f 21/137/111 555/138/112 1538/139/113 +f 21/137/111 1538/139/113 556/140/114 +f 21/137/111 556/140/114 1536/141/115 +f 21/137/111 1536/141/115 557/142/116 +f 21/135/111 557/143/116 1534/125/105 +f 21/135/111 1534/125/105 551/124/104 +f 22/144/117 560/145/118 1537/146/119 +f 22/144/117 1537/146/119 558/147/120 +f 22/144/117 558/147/120 1539/148/121 +f 22/144/117 1539/148/121 559/149/122 +f 22/144/117 559/149/122 1533/112/99 +f 22/144/117 1533/112/99 553/131/110 +f 22/144/117 553/131/110 1535/130/109 +f 22/144/117 1535/130/109 560/145/118 +f 23/150/123 546/151/94 1530/152/93 +f 23/150/123 1530/152/93 561/153/124 +f 23/150/123 561/153/124 1540/154/125 +f 23/150/123 1540/154/125 562/155/126 +f 23/150/123 562/155/126 1538/139/113 +f 23/150/123 1538/139/113 555/138/112 +f 23/150/123 555/138/112 1532/156/95 +f 23/150/123 1532/156/95 546/151/94 +f 24/157/127 559/149/122 1539/148/121 +f 24/157/127 1539/148/121 563/158/128 +f 24/157/127 563/158/128 1541/159/129 +f 24/157/127 1541/159/129 564/160/130 +f 24/157/127 564/160/130 1531/114/101 +f 24/157/127 1531/114/101 548/113/100 +f 24/157/127 548/113/100 1533/112/99 +f 24/157/127 1533/112/99 559/149/122 +f 25/161/131 562/155/126 1540/154/125 +f 25/161/131 1540/154/125 565/162/132 +f 25/161/131 565/162/132 1542/163/133 +f 25/161/131 1542/163/133 566/164/134 +f 25/161/131 566/164/134 1544/165/135 +f 25/161/131 1544/165/135 567/166/136 +f 25/161/131 567/166/136 1538/139/113 +f 25/161/131 1538/139/113 562/155/126 +f 26/167/137 570/168/138 1545/169/139 +f 26/167/137 1545/169/139 568/170/140 +f 26/167/137 568/170/140 1543/171/141 +f 26/167/137 1543/171/141 569/172/142 +f 26/173/137 569/174/142 1541/159/129 +f 26/173/137 1541/159/129 563/158/128 +f 26/173/137 563/158/128 1539/148/121 +f 26/173/137 1539/148/121 570/175/138 +f 27/176/143 556/140/114 1538/139/113 +f 27/176/143 1538/139/113 567/166/136 +f 27/176/143 567/166/136 1544/165/135 +f 27/176/143 1544/165/135 571/177/144 +f 27/176/143 571/177/144 1546/178/145 +f 27/176/143 1546/178/145 572/179/146 +f 27/176/143 572/179/146 1536/141/115 +f 27/176/143 1536/141/115 556/140/114 +f 28/180/147 574/181/148 1547/182/149 +f 28/180/147 1547/182/149 573/183/150 +f 28/180/147 573/183/150 1545/169/139 +f 28/180/147 1545/169/139 570/168/138 +f 28/184/147 570/175/138 1539/148/121 +f 28/184/147 1539/148/121 558/147/120 +f 28/184/147 558/147/120 1537/146/119 +f 28/184/147 1537/146/119 574/185/148 +f 29/186/151 571/177/144 1544/165/135 +f 29/186/151 1544/165/135 575/187/152 +f 29/188/151 575/189/152 1550/9/9 +f 29/188/151 1550/9/9 511/20/20 +f 29/188/151 511/20/20 1548/24/24 +f 29/188/151 1548/24/24 576/190/153 +f 29/186/151 576/191/153 1546/178/145 +f 29/186/151 1546/178/145 571/177/144 +f 30/192/154 578/193/155 1549/194/30 +f 30/192/154 1549/194/30 513/195/29 +f 30/192/154 513/195/29 1551/196/18 +f 30/192/154 1551/196/18 577/197/156 +f 30/192/154 577/197/156 1545/169/139 +f 30/192/154 1545/169/139 573/183/150 +f 30/192/154 573/183/150 1547/182/149 +f 30/192/154 1547/182/149 578/193/155 +f 31/198/157 566/164/134 1542/163/133 +f 31/198/157 1542/163/133 579/199/158 +f 31/200/157 579/201/158 1552/3/3 +f 31/200/157 1552/3/3 504/2/2 +f 31/200/157 504/2/2 1550/9/9 +f 31/200/157 1550/9/9 575/189/152 +f 31/198/157 575/187/152 1544/165/135 +f 31/198/157 1544/165/135 566/164/134 +f 32/202/159 577/203/156 1551/18/18 +f 32/202/159 1551/18/18 507/17/17 +f 32/202/159 507/17/17 1553/16/16 +f 32/202/159 1553/16/16 580/204/160 +f 32/205/159 580/206/160 1543/171/141 +f 32/205/159 1543/171/141 568/170/140 +f 32/205/159 568/170/140 1545/169/139 +f 32/205/159 1545/169/139 577/197/156 +f 33/207/161 583/208/162 1552/3/3 +f 33/207/161 1552/3/3 579/201/158 +f 33/209/161 579/199/158 1542/163/133 +f 33/209/161 1542/163/133 581/210/163 +f 33/209/161 581/210/163 1556/211/164 +f 33/209/161 1556/211/164 582/212/165 +f 33/207/161 582/213/165 1554/214/166 +f 33/207/161 1554/214/166 583/208/162 +f 34/215/167 586/216/168 1557/217/169 +f 34/215/167 1557/217/169 584/218/170 +f 34/215/167 584/218/170 1543/171/141 +f 34/215/167 1543/171/141 580/206/160 +f 34/215/167 580/206/160 1553/219/16 +f 34/215/167 1553/219/16 585/220/171 +f 34/215/167 585/220/171 1555/221/172 +f 34/215/167 1555/221/172 586/216/168 +f 35/222/173 581/210/163 1542/163/133 +f 35/222/173 1542/163/133 565/162/132 +f 35/222/173 565/162/132 1540/154/125 +f 35/222/173 1540/154/125 587/223/174 +f 35/222/173 587/223/174 1558/224/175 +f 35/222/173 1558/224/175 588/225/176 +f 35/222/173 588/225/176 1556/211/164 +f 35/222/173 1556/211/164 581/210/163 +f 36/226/177 590/227/178 1559/228/179 +f 36/226/177 1559/228/179 589/229/180 +f 36/226/177 589/229/180 1541/159/129 +f 36/226/177 1541/159/129 569/174/142 +f 36/230/177 569/172/142 1543/171/141 +f 36/230/177 1543/171/141 584/218/170 +f 36/230/177 584/218/170 1557/217/169 +f 36/230/177 1557/217/169 590/231/178 +f 37/232/181 587/223/174 1540/154/125 +f 37/232/181 1540/154/125 561/153/124 +f 37/233/181 561/234/124 1530/235/93 +f 37/233/181 1530/235/93 591/236/182 +f 37/233/181 591/236/182 1560/237/183 +f 37/233/181 1560/237/183 592/238/184 +f 37/232/181 592/239/184 1558/224/175 +f 37/232/181 1558/224/175 587/223/174 +f 38/240/185 594/241/186 1561/242/187 +f 38/240/185 1561/242/187 593/243/188 +f 38/240/185 593/243/188 1531/114/101 +f 38/240/185 1531/114/101 564/160/130 +f 38/240/185 564/160/130 1541/159/129 +f 38/240/185 1541/159/129 589/229/180 +f 38/240/185 589/229/180 1559/228/179 +f 38/240/185 1559/228/179 594/241/186 +f 39/244/189 591/236/182 1530/235/93 +f 39/244/189 1530/235/93 545/245/92 +f 39/244/189 545/245/92 1528/246/85 +f 39/244/189 1528/246/85 595/247/190 +f 39/244/189 595/247/190 1562/248/191 +f 39/244/189 1562/248/191 596/249/192 +f 39/244/189 596/249/192 1560/237/183 +f 39/244/189 1560/237/183 591/236/182 +f 40/250/193 598/251/194 1563/252/195 +f 40/250/193 1563/252/195 597/253/196 +f 40/250/193 597/253/196 1529/99/89 +f 40/250/193 1529/99/89 549/254/102 +f 40/255/193 549/115/102 1531/114/101 +f 40/255/193 1531/114/101 593/243/188 +f 40/255/193 593/243/188 1561/242/187 +f 40/255/193 1561/242/187 598/256/194 +f 41/257/197 595/247/190 1528/246/85 +f 41/257/197 1528/246/85 541/258/84 +f 41/259/197 541/94/84 1518/58/53 +f 41/259/197 1518/58/53 599/260/198 +f 41/259/197 599/260/198 1564/261/199 +f 41/259/197 1564/261/199 600/262/200 +f 41/259/197 600/262/200 1562/263/191 +f 41/259/197 1562/263/191 595/264/190 +f 42/265/201 602/266/202 1565/267/203 +f 42/265/201 1565/267/203 601/268/204 +f 42/265/201 601/268/204 1519/269/61 +f 42/265/201 1519/269/61 544/100/90 +f 42/265/201 544/100/90 1529/99/89 +f 42/265/201 1529/99/89 597/253/196 +f 42/265/201 597/253/196 1563/252/195 +f 42/265/201 1563/252/195 602/266/202 +f 43/270/205 599/260/198 1518/58/53 +f 43/270/205 1518/58/53 525/57/52 +f 43/271/205 525/272/52 1516/273/45 +f 43/271/205 1516/273/45 603/274/206 +f 43/270/205 603/275/206 1568/276/207 +f 43/270/205 1568/276/207 604/277/208 +f 43/270/205 604/277/208 1564/261/199 +f 43/270/205 1564/261/199 599/260/198 +f 44/278/209 606/279/210 1569/280/211 +f 44/278/209 1569/280/211 605/281/212 +f 44/278/209 605/281/212 1517/54/49 +f 44/278/209 1517/54/49 529/67/62 +f 44/282/209 529/283/62 1519/269/61 +f 44/282/209 1519/269/61 601/268/204 +f 44/282/209 601/268/204 1565/267/203 +f 44/282/209 1565/267/203 606/284/210 +f 45/285/213 603/286/206 1516/287/45 +f 45/285/213 1516/287/45 521/47/44 +f 45/285/213 521/47/44 1506/5/5 +f 45/285/213 1506/5/5 607/288/214 +f 45/285/213 607/288/214 1570/289/215 +f 45/285/213 1570/289/215 608/290/216 +f 45/291/213 608/292/216 1568/276/207 +f 45/291/213 1568/276/207 603/275/206 +f 46/293/217 610/294/218 1571/295/219 +f 46/293/217 1571/295/219 609/296/220 +f 46/293/217 609/296/220 1507/14/14 +f 46/293/217 1507/14/14 524/55/50 +f 46/297/217 524/298/50 1517/299/49 +f 46/297/217 1517/299/49 605/271/212 +f 46/293/217 605/281/212 1569/280/211 +f 46/293/217 1569/280/211 610/294/218 +f 47/300/221 607/288/214 1506/5/5 +f 47/300/221 1506/5/5 501/4/4 +f 47/300/221 501/4/4 1552/3/3 +f 47/300/221 1552/3/3 583/208/162 +f 47/300/221 583/208/162 1554/214/166 +f 47/300/221 1554/214/166 611/301/222 +f 47/300/221 611/301/222 1570/289/215 +f 47/300/221 1570/289/215 607/288/214 +f 48/302/223 612/303/224 1555/304/172 +f 48/302/223 1555/304/172 585/305/171 +f 48/306/223 585/220/171 1553/219/16 +f 48/306/223 1553/219/16 506/307/15 +f 48/302/223 506/15/15 1507/14/14 +f 48/302/223 1507/14/14 609/296/220 +f 48/302/223 609/296/220 1571/295/219 +f 48/302/223 1571/295/219 612/303/224 +f 49/308/225 614/309/226 1566/310/227 +f 49/308/225 1566/310/227 613/311/228 +f 49/308/225 613/311/228 1570/312/215 +f 49/308/225 1570/312/215 611/313/222 +f 49/308/225 611/313/222 1554/314/166 +f 49/308/225 1554/314/166 614/309/226 +f 50/315/229 616/316/230 1555/304/172 +f 50/315/229 1555/304/172 612/303/224 +f 50/315/229 612/303/224 1571/295/219 +f 50/315/229 1571/295/219 615/317/231 +f 50/315/229 615/317/231 1567/318/232 +f 50/315/229 1567/318/232 616/316/230 +f 51/319/233 617/320/234 1568/276/207 +f 51/319/233 1568/276/207 608/292/216 +f 51/319/233 608/292/216 1570/312/215 +f 51/319/233 1570/312/215 613/311/228 +f 51/319/233 613/311/228 1566/310/227 +f 51/319/233 1566/310/227 617/320/234 +f 52/321/235 618/322/236 1567/318/232 +f 52/321/235 1567/318/232 615/317/231 +f 52/321/235 615/317/231 1571/295/219 +f 52/321/235 1571/295/219 610/294/218 +f 52/321/235 610/294/218 1569/280/211 +f 52/321/235 1569/280/211 618/322/236 +f 53/323/237 617/320/234 1566/310/227 +f 53/323/237 1566/310/227 619/324/238 +f 53/323/237 619/324/238 1564/261/199 +f 53/323/237 1564/261/199 604/277/208 +f 53/323/237 604/277/208 1568/276/207 +f 53/323/237 1568/276/207 617/320/234 +f 54/325/239 618/322/236 1569/280/211 +f 54/325/239 1569/280/211 606/279/210 +f 54/325/239 606/279/210 1565/326/203 +f 54/325/239 1565/326/203 620/327/240 +f 54/325/239 620/327/240 1567/318/232 +f 54/325/239 1567/318/232 618/322/236 +f 55/328/241 619/324/238 1566/310/227 +f 55/328/241 1566/310/227 621/329/242 +f 55/328/241 621/329/242 1562/263/191 +f 55/328/241 1562/263/191 600/262/200 +f 55/328/241 600/262/200 1564/261/199 +f 55/328/241 1564/261/199 619/324/238 +f 56/330/243 620/327/240 1565/326/203 +f 56/330/243 1565/326/203 602/331/202 +f 56/330/243 602/331/202 1563/332/195 +f 56/330/243 1563/332/195 622/333/244 +f 56/330/243 622/333/244 1567/318/232 +f 56/330/243 1567/318/232 620/327/240 +f 57/334/245 621/329/242 1566/310/227 +f 57/334/245 1566/310/227 623/335/246 +f 57/334/245 623/335/246 1560/336/183 +f 57/334/245 1560/336/183 596/337/192 +f 57/334/245 596/337/192 1562/263/191 +f 57/334/245 1562/263/191 621/329/242 +f 58/338/247 622/333/244 1563/332/195 +f 58/338/247 1563/332/195 598/339/194 +f 58/338/247 598/339/194 1561/340/187 +f 58/338/247 1561/340/187 624/341/248 +f 58/338/247 624/341/248 1567/318/232 +f 58/338/247 1567/318/232 622/333/244 +f 59/342/249 623/335/246 1566/310/227 +f 59/342/249 1566/310/227 625/343/250 +f 59/342/249 625/343/250 1558/344/175 +f 59/342/249 1558/344/175 592/345/184 +f 59/342/249 592/345/184 1560/336/183 +f 59/342/249 1560/336/183 623/335/246 +f 60/346/251 624/341/248 1561/340/187 +f 60/346/251 1561/340/187 594/347/186 +f 60/346/251 594/347/186 1559/348/179 +f 60/346/251 1559/348/179 626/349/252 +f 60/346/251 626/349/252 1567/318/232 +f 60/346/251 1567/318/232 624/341/248 +f 61/350/253 625/343/250 1566/310/227 +f 61/350/253 1566/310/227 627/351/254 +f 61/352/253 627/353/254 1556/211/164 +f 61/352/253 1556/211/164 588/225/176 +f 61/352/253 588/225/176 1558/224/175 +f 61/352/253 1558/224/175 625/354/250 +f 62/355/255 626/349/252 1559/348/179 +f 62/355/255 1559/348/179 590/356/178 +f 62/355/255 590/356/178 1557/357/169 +f 62/355/255 1557/357/169 628/358/256 +f 62/355/255 628/358/256 1567/318/232 +f 62/355/255 1567/318/232 626/349/252 +f 63/359/257 627/351/254 1566/310/227 +f 63/359/257 1566/310/227 614/309/226 +f 63/359/257 614/309/226 1554/314/166 +f 63/359/257 1554/314/166 582/360/165 +f 63/359/257 582/360/165 1556/361/164 +f 63/359/257 1556/361/164 627/351/254 +f 64/362/258 628/358/256 1557/357/169 +f 64/362/258 1557/357/169 586/363/168 +f 64/362/258 586/363/168 1555/304/172 +f 64/362/258 1555/304/172 616/316/230 +f 64/362/258 616/316/230 1567/318/232 +f 64/362/258 1567/318/232 628/358/256 +f 65/364/259 632/365/260 1594/366/261 +f 65/364/259 1594/366/261 629/367/262 +f 65/368/259 629/369/262 1679/370/263 +f 65/368/259 1679/370/263 630/371/264 +f 65/368/259 630/371/264 1681/372/265 +f 65/368/259 1681/372/265 631/373/266 +f 65/364/259 631/374/266 1596/375/267 +f 65/364/259 1596/375/267 632/365/260 +f 66/376/268 631/373/266 1681/372/265 +f 66/376/268 1681/372/265 633/377/269 +f 66/376/268 633/377/269 1680/378/270 +f 66/376/268 1680/378/270 634/379/271 +f 66/380/268 634/381/271 1595/382/272 +f 66/380/268 1595/382/272 635/383/273 +f 66/380/268 635/383/273 1596/375/267 +f 66/380/268 1596/375/267 631/374/266 +f 67/384/274 638/385/275 1592/386/276 +f 67/384/274 1592/386/276 636/387/277 +f 67/388/274 636/389/277 1677/390/278 +f 67/388/274 1677/390/278 637/391/279 +f 67/388/274 637/391/279 1679/370/263 +f 67/388/274 1679/370/263 629/369/262 +f 67/392/274 629/367/262 1594/366/261 +f 67/392/274 1594/366/261 638/393/275 +f 68/394/280 634/379/271 1680/378/270 +f 68/394/280 1680/378/270 639/395/281 +f 68/394/280 639/395/281 1678/396/282 +f 68/394/280 1678/396/282 640/397/283 +f 68/398/280 640/399/283 1593/400/284 +f 68/398/280 1593/400/284 641/401/285 +f 68/398/280 641/401/285 1595/382/272 +f 68/398/280 1595/382/272 634/381/271 +f 69/402/286 644/403/287 1590/404/288 +f 69/402/286 1590/404/288 642/405/289 +f 69/402/286 642/405/289 1675/406/290 +f 69/402/286 1675/406/290 643/407/291 +f 69/402/286 643/407/291 1677/408/278 +f 69/402/286 1677/408/278 636/387/277 +f 69/402/286 636/387/277 1592/386/276 +f 69/402/286 1592/386/276 644/403/287 +f 70/409/292 640/397/283 1678/396/282 +f 70/409/292 1678/396/282 645/410/293 +f 70/409/292 645/410/293 1676/411/294 +f 70/409/292 1676/411/294 646/412/295 +f 70/409/292 646/412/295 1591/413/296 +f 70/409/292 1591/413/296 647/414/297 +f 70/409/292 647/414/297 1593/415/284 +f 70/409/292 1593/415/284 640/397/283 +f 71/416/298 650/417/299 1588/418/300 +f 71/416/298 1588/418/300 648/419/301 +f 71/416/298 648/419/301 1673/420/302 +f 71/416/298 1673/420/302 649/421/303 +f 71/416/298 649/421/303 1675/406/290 +f 71/416/298 1675/406/290 642/405/289 +f 71/416/298 642/405/289 1590/404/288 +f 71/416/298 1590/404/288 650/417/299 +f 72/422/304 646/412/295 1676/411/294 +f 72/422/304 1676/411/294 651/423/305 +f 72/422/304 651/423/305 1674/424/306 +f 72/422/304 1674/424/306 652/425/307 +f 72/426/304 652/427/307 1589/428/308 +f 72/426/304 1589/428/308 653/429/309 +f 72/426/304 653/429/309 1591/430/296 +f 72/426/304 1591/430/296 646/431/295 +f 73/432/310 656/433/311 1586/434/312 +f 73/432/310 1586/434/312 654/435/313 +f 73/432/310 654/435/313 1671/436/314 +f 73/432/310 1671/436/314 655/437/315 +f 73/432/310 655/437/315 1673/420/302 +f 73/432/310 1673/420/302 648/419/301 +f 73/432/310 648/419/301 1588/418/300 +f 73/432/310 1588/418/300 656/433/311 +f 74/438/316 652/425/307 1674/424/306 +f 74/438/316 1674/424/306 657/439/317 +f 74/438/316 657/439/317 1672/440/318 +f 74/438/316 1672/440/318 658/441/319 +f 74/442/316 658/443/319 1587/444/320 +f 74/442/316 1587/444/320 659/445/321 +f 74/442/316 659/445/321 1589/428/308 +f 74/442/316 1589/428/308 652/427/307 +f 75/446/322 663/447/323 1584/448/324 +f 75/446/322 1584/448/324 660/449/325 +f 75/446/322 660/449/325 1597/450/326 +f 75/446/322 1597/450/326 661/451/327 +f 75/446/322 661/451/327 1651/452/328 +f 75/446/322 1651/452/328 662/453/329 +f 75/446/322 662/453/329 1669/454/330 +f 75/446/322 1669/454/330 663/447/323 +f 76/455/331 667/456/332 1652/457/333 +f 76/455/331 1652/457/333 664/458/334 +f 76/455/331 664/458/334 1598/459/335 +f 76/455/331 1598/459/335 665/460/336 +f 76/461/331 665/462/336 1585/463/337 +f 76/461/331 1585/463/337 666/464/338 +f 76/455/331 666/465/338 1670/466/339 +f 76/455/331 1670/466/339 667/456/332 +f 77/467/340 661/451/327 1597/450/326 +f 77/467/340 1597/450/326 668/468/341 +f 77/467/340 668/468/341 1599/469/342 +f 77/467/340 1599/469/342 669/470/343 +f 77/467/340 669/470/343 1653/471/344 +f 77/467/340 1653/471/344 670/472/345 +f 77/473/340 670/474/345 1651/475/328 +f 77/473/340 1651/475/328 661/476/327 +f 78/477/346 673/478/347 1654/479/348 +f 78/477/346 1654/479/348 671/480/349 +f 78/477/346 671/480/349 1600/481/350 +f 78/477/346 1600/481/350 672/482/351 +f 78/477/346 672/482/351 1598/459/335 +f 78/477/346 1598/459/335 664/458/334 +f 78/477/346 664/458/334 1652/457/333 +f 78/477/346 1652/457/333 673/478/347 +f 79/483/352 669/470/343 1599/469/342 +f 79/483/352 1599/469/342 674/484/353 +f 79/483/352 674/484/353 1601/485/354 +f 79/483/352 1601/485/354 675/486/355 +f 79/483/352 675/486/355 1655/487/356 +f 79/483/352 1655/487/356 676/488/357 +f 79/483/352 676/488/357 1653/471/344 +f 79/483/352 1653/471/344 669/470/343 +f 80/489/358 679/490/359 1656/491/360 +f 80/489/358 1656/491/360 677/492/361 +f 80/489/358 677/492/361 1602/493/362 +f 80/489/358 1602/493/362 678/494/363 +f 80/489/358 678/494/363 1600/481/350 +f 80/489/358 1600/481/350 671/480/349 +f 80/489/358 671/480/349 1654/479/348 +f 80/489/358 1654/479/348 679/490/359 +f 81/495/364 675/486/355 1601/485/354 +f 81/495/364 1601/485/354 680/496/365 +f 81/495/364 680/496/365 1603/497/366 +f 81/495/364 1603/497/366 681/498/367 +f 81/495/364 681/498/367 1657/499/368 +f 81/495/364 1657/499/368 682/500/369 +f 81/495/364 682/500/369 1655/487/356 +f 81/495/364 1655/487/356 675/486/355 +f 82/501/370 685/502/371 1658/503/372 +f 82/501/370 1658/503/372 683/504/373 +f 82/501/370 683/504/373 1604/505/374 +f 82/501/370 1604/505/374 684/506/375 +f 82/501/370 684/506/375 1602/493/362 +f 82/501/370 1602/493/362 677/492/361 +f 82/501/370 677/492/361 1656/491/360 +f 82/501/370 1656/491/360 685/502/371 +f 83/507/376 681/508/367 1603/509/366 +f 83/507/376 1603/509/366 686/510/377 +f 83/507/376 686/510/377 1605/511/378 +f 83/507/376 1605/511/378 687/512/379 +f 83/507/376 687/512/379 1659/513/380 +f 83/507/376 1659/513/380 688/514/381 +f 83/515/376 688/516/381 1657/499/368 +f 83/515/376 1657/499/368 681/498/367 +f 84/517/382 691/518/383 1660/519/384 +f 84/517/382 1660/519/384 689/520/385 +f 84/521/382 689/522/385 1606/523/386 +f 84/521/382 1606/523/386 690/524/387 +f 84/521/382 690/524/387 1604/525/374 +f 84/521/382 1604/525/374 683/526/373 +f 84/517/382 683/504/373 1658/503/372 +f 84/517/382 1658/503/372 691/518/383 +f 85/527/388 687/512/379 1605/511/378 +f 85/527/388 1605/511/378 692/528/389 +f 85/527/388 692/528/389 1607/529/390 +f 85/527/388 1607/529/390 693/530/391 +f 85/527/388 693/530/391 1661/531/392 +f 85/527/388 1661/531/392 694/532/393 +f 85/527/388 694/532/393 1659/513/380 +f 85/527/388 1659/513/380 687/512/379 +f 86/533/394 697/534/395 1662/535/396 +f 86/533/394 1662/535/396 695/536/397 +f 86/537/394 695/538/397 1608/539/398 +f 86/537/394 1608/539/398 696/540/399 +f 86/537/394 696/540/399 1606/523/386 +f 86/537/394 1606/523/386 689/522/385 +f 86/533/394 689/520/385 1660/519/384 +f 86/533/394 1660/519/384 697/534/395 +f 87/541/400 693/530/391 1607/529/390 +f 87/541/400 1607/529/390 698/542/401 +f 87/541/400 698/542/401 1609/543/402 +f 87/541/400 1609/543/402 699/544/403 +f 87/541/400 699/544/403 1663/545/404 +f 87/541/400 1663/545/404 700/546/405 +f 87/541/400 700/546/405 1661/531/392 +f 87/541/400 1661/531/392 693/530/391 +f 88/547/406 703/548/407 1664/549/408 +f 88/547/406 1664/549/408 701/550/409 +f 88/551/406 701/552/409 1610/553/410 +f 88/551/406 1610/553/410 702/554/411 +f 88/551/406 702/554/411 1608/539/398 +f 88/551/406 1608/539/398 695/538/397 +f 88/547/406 695/536/397 1662/535/396 +f 88/547/406 1662/535/396 703/548/407 +f 89/555/412 699/544/403 1609/543/402 +f 89/555/412 1609/543/402 704/556/413 +f 89/557/412 704/558/413 1611/559/414 +f 89/557/412 1611/559/414 705/560/415 +f 89/561/412 705/562/415 1665/563/416 +f 89/561/412 1665/563/416 706/564/417 +f 89/555/412 706/565/417 1663/545/404 +f 89/555/412 1663/545/404 699/544/403 +f 90/566/418 709/567/419 1666/568/420 +f 90/566/418 1666/568/420 707/569/421 +f 90/566/418 707/569/421 1612/570/422 +f 90/566/418 1612/570/422 708/571/423 +f 90/566/418 708/571/423 1610/572/410 +f 90/566/418 1610/572/410 701/573/409 +f 90/574/418 701/550/409 1664/549/408 +f 90/574/418 1664/549/408 709/575/419 +f 91/576/424 705/560/415 1611/559/414 +f 91/576/424 1611/559/414 710/577/425 +f 91/576/424 710/577/425 1613/578/426 +f 91/576/424 1613/578/426 711/579/427 +f 91/580/424 711/581/427 1667/582/428 +f 91/580/424 1667/582/428 712/583/429 +f 91/580/424 712/583/429 1665/563/416 +f 91/580/424 1665/563/416 705/562/415 +f 92/584/430 715/585/431 1668/586/432 +f 92/584/430 1668/586/432 713/587/433 +f 92/584/430 713/587/433 1614/588/434 +f 92/584/430 1614/588/434 714/589/435 +f 92/584/430 714/589/435 1612/570/422 +f 92/584/430 1612/570/422 707/569/421 +f 92/584/430 707/569/421 1666/568/420 +f 92/584/430 1666/568/420 715/585/431 +f 93/590/436 711/579/427 1613/578/426 +f 93/590/436 1613/578/426 716/591/437 +f 93/590/436 716/591/437 1572/592/438 +f 93/590/436 1572/592/438 717/593/439 +f 93/594/436 717/595/439 1573/596/440 +f 93/594/436 1573/596/440 718/597/441 +f 93/594/436 718/597/441 1667/582/428 +f 93/594/436 1667/582/428 711/581/427 +f 94/598/442 720/599/443 1573/600/440 +f 94/598/442 1573/600/440 717/601/439 +f 94/598/442 717/601/439 1572/602/438 +f 94/598/442 1572/602/438 719/603/444 +f 94/598/442 719/603/444 1614/588/434 +f 94/598/442 1614/588/434 713/587/433 +f 94/598/442 713/587/433 1668/586/432 +f 94/598/442 1668/586/432 720/599/443 +f 95/604/445 723/605/446 1615/606/447 +f 95/604/445 1615/606/447 721/607/448 +f 95/604/445 721/607/448 1633/608/449 +f 95/604/445 1633/608/449 722/609/450 +f 95/604/445 722/609/450 1665/563/416 +f 95/604/445 1665/563/416 712/583/429 +f 95/604/445 712/583/429 1667/582/428 +f 95/604/445 1667/582/428 723/605/446 +f 96/610/451 715/611/431 1666/612/420 +f 96/610/451 1666/612/420 724/613/452 +f 96/610/451 724/613/452 1634/614/453 +f 96/610/451 1634/614/453 725/615/454 +f 96/610/451 725/615/454 1616/616/455 +f 96/610/451 1616/616/455 726/617/456 +f 96/610/451 726/617/456 1668/618/432 +f 96/610/451 1668/618/432 715/611/431 +f 97/619/457 722/609/450 1633/608/449 +f 97/619/457 1633/608/449 727/620/458 +f 97/619/457 727/620/458 1684/621/459 +f 97/619/457 1684/621/459 728/622/460 +f 97/619/457 728/622/460 1663/623/404 +f 97/619/457 1663/623/404 706/564/417 +f 97/619/457 706/564/417 1665/563/416 +f 97/619/457 1665/563/416 722/609/450 +f 98/624/461 709/575/419 1664/549/408 +f 98/624/461 1664/549/408 729/625/462 +f 98/624/461 729/625/462 1685/626/463 +f 98/624/461 1685/626/463 730/627/464 +f 98/624/461 730/627/464 1634/614/453 +f 98/624/461 1634/614/453 724/613/452 +f 98/624/461 724/613/452 1666/612/420 +f 98/624/461 1666/612/420 709/575/419 +f 99/628/465 732/629/466 1631/630/467 +f 99/628/465 1631/630/467 731/631/468 +f 99/628/465 731/631/468 1661/632/392 +f 99/628/465 1661/632/392 700/633/405 +f 99/628/465 700/633/405 1663/623/404 +f 99/628/465 1663/623/404 728/622/460 +f 99/628/465 728/622/460 1684/621/459 +f 99/628/465 1684/621/459 732/629/466 +f 100/634/469 729/625/462 1664/549/408 +f 100/634/469 1664/549/408 703/548/407 +f 100/634/469 703/548/407 1662/535/396 +f 100/634/469 1662/535/396 733/635/470 +f 100/634/469 733/635/470 1632/636/471 +f 100/634/469 1632/636/471 734/637/472 +f 100/634/469 734/637/472 1685/626/463 +f 100/634/469 1685/626/463 729/625/462 +f 101/638/473 736/639/474 1629/640/475 +f 101/638/473 1629/640/475 735/641/476 +f 101/638/473 735/641/476 1659/642/380 +f 101/638/473 1659/642/380 694/643/393 +f 101/638/473 694/643/393 1661/632/392 +f 101/638/473 1661/632/392 731/631/468 +f 101/638/473 731/631/468 1631/630/467 +f 101/638/473 1631/630/467 736/639/474 +f 102/644/477 733/635/470 1662/535/396 +f 102/644/477 1662/535/396 697/534/395 +f 102/644/477 697/534/395 1660/519/384 +f 102/644/477 1660/519/384 737/645/478 +f 102/644/477 737/645/478 1630/646/479 +f 102/644/477 1630/646/479 738/647/480 +f 102/644/477 738/647/480 1632/636/471 +f 102/644/477 1632/636/471 733/635/470 +f 103/648/481 740/649/482 1627/650/483 +f 103/648/481 1627/650/483 739/651/484 +f 103/648/481 739/651/484 1657/652/368 +f 103/648/481 1657/652/368 688/653/381 +f 103/648/481 688/653/381 1659/642/380 +f 103/648/481 1659/642/380 735/641/476 +f 103/648/481 735/641/476 1629/640/475 +f 103/648/481 1629/640/475 740/649/482 +f 104/654/485 737/645/478 1660/519/384 +f 104/654/485 1660/519/384 691/518/383 +f 104/654/485 691/518/383 1658/503/372 +f 104/654/485 1658/503/372 741/655/486 +f 104/654/485 741/655/486 1628/656/487 +f 104/654/485 1628/656/487 742/657/488 +f 104/654/485 742/657/488 1630/646/479 +f 104/654/485 1630/646/479 737/645/478 +f 105/658/489 744/659/490 1625/660/491 +f 105/658/489 1625/660/491 743/661/492 +f 105/658/489 743/661/492 1655/662/356 +f 105/658/489 1655/662/356 682/663/369 +f 105/658/489 682/663/369 1657/652/368 +f 105/658/489 1657/652/368 739/651/484 +f 105/658/489 739/651/484 1627/650/483 +f 105/658/489 1627/650/483 744/659/490 +f 106/664/493 741/655/486 1658/503/372 +f 106/664/493 1658/503/372 685/502/371 +f 106/664/493 685/502/371 1656/491/360 +f 106/664/493 1656/491/360 745/665/494 +f 106/664/493 745/665/494 1626/666/495 +f 106/664/493 1626/666/495 746/667/496 +f 106/664/493 746/667/496 1628/656/487 +f 106/664/493 1628/656/487 741/655/486 +f 107/668/497 748/669/498 1623/670/499 +f 107/668/497 1623/670/499 747/671/500 +f 107/668/497 747/671/500 1653/672/344 +f 107/668/497 1653/672/344 676/673/357 +f 107/668/497 676/673/357 1655/662/356 +f 107/668/497 1655/662/356 743/661/492 +f 107/668/497 743/661/492 1625/660/491 +f 107/668/497 1625/660/491 748/669/498 +f 108/674/501 745/665/494 1656/491/360 +f 108/674/501 1656/491/360 679/490/359 +f 108/674/501 679/490/359 1654/479/348 +f 108/674/501 1654/479/348 749/675/502 +f 108/674/501 749/675/502 1624/676/503 +f 108/674/501 1624/676/503 750/677/504 +f 108/674/501 750/677/504 1626/666/495 +f 108/674/501 1626/666/495 745/665/494 +f 109/678/505 752/679/506 1621/680/507 +f 109/678/505 1621/680/507 751/681/508 +f 109/678/505 751/681/508 1651/475/328 +f 109/678/505 1651/475/328 670/474/345 +f 109/678/505 670/474/345 1653/672/344 +f 109/678/505 1653/672/344 747/671/500 +f 109/678/505 747/671/500 1623/670/499 +f 109/678/505 1623/670/499 752/679/506 +f 110/682/509 749/675/502 1654/479/348 +f 110/682/509 1654/479/348 673/478/347 +f 110/682/509 673/478/347 1652/457/333 +f 110/682/509 1652/457/333 753/683/510 +f 110/682/509 753/683/510 1622/684/511 +f 110/682/509 1622/684/511 754/685/512 +f 110/682/509 754/685/512 1624/676/503 +f 110/682/509 1624/676/503 749/675/502 +f 111/686/513 756/687/514 1619/688/515 +f 111/686/513 1619/688/515 755/689/516 +f 111/686/513 755/689/516 1669/690/330 +f 111/686/513 1669/690/330 662/691/329 +f 111/686/513 662/691/329 1651/475/328 +f 111/686/513 1651/475/328 751/681/508 +f 111/686/513 751/681/508 1621/680/507 +f 111/686/513 1621/680/507 756/687/514 +f 112/692/517 753/683/510 1652/457/333 +f 112/692/517 1652/457/333 667/456/332 +f 112/692/517 667/456/332 1670/466/339 +f 112/692/517 1670/466/339 757/693/518 +f 112/692/517 757/693/518 1620/694/519 +f 112/692/517 1620/694/519 758/695/520 +f 112/692/517 758/695/520 1622/684/511 +f 112/692/517 1622/684/511 753/683/510 +f 113/696/521 755/689/516 1619/688/515 +f 113/696/521 1619/688/515 759/697/522 +f 113/696/521 759/697/522 1686/698/523 +f 113/696/521 1686/698/523 760/699/524 +f 113/696/521 760/699/524 1682/700/525 +f 113/696/521 1682/700/525 761/701/526 +f 113/696/521 761/701/526 1669/690/330 +f 113/696/521 1669/690/330 755/689/516 +f 114/702/527 764/703/528 1682/700/525 +f 114/702/527 1682/700/525 762/704/529 +f 114/702/527 762/704/529 1687/705/530 +f 114/702/527 1687/705/530 763/706/531 +f 114/702/527 763/706/531 1620/694/519 +f 114/702/527 1620/694/519 757/693/518 +f 114/702/527 757/693/518 1670/466/339 +f 114/702/527 1670/466/339 764/703/528 +f 115/707/532 766/708/533 1615/606/447 +f 115/707/532 1615/606/447 723/605/446 +f 115/707/532 723/605/446 1667/582/428 +f 115/707/532 1667/582/428 718/597/441 +f 115/707/532 718/597/441 1573/596/440 +f 115/707/532 1573/596/440 765/709/534 +f 115/707/532 765/709/534 1617/710/535 +f 115/707/532 1617/710/535 766/708/533 +f 116/711/536 768/712/537 1573/596/440 +f 116/711/536 1573/596/440 720/713/443 +f 116/711/536 720/713/443 1668/618/432 +f 116/711/536 1668/618/432 726/617/456 +f 116/711/536 726/617/456 1616/616/455 +f 116/711/536 1616/616/455 767/714/538 +f 116/711/536 767/714/538 1618/715/539 +f 116/711/536 1618/715/539 768/712/537 +f 117/716/540 771/717/541 1617/710/535 +f 117/716/540 1617/710/535 765/709/534 +f 117/716/540 765/709/534 1573/596/440 +f 117/716/540 1573/596/440 769/718/542 +f 117/716/540 769/718/542 1683/719/543 +f 117/716/540 1683/719/543 770/720/544 +f 117/716/540 770/720/544 1688/721/545 +f 117/716/540 1688/721/545 771/717/541 +f 118/722/546 773/723/547 1683/719/543 +f 118/722/546 1683/719/543 769/718/542 +f 118/722/546 769/718/542 1573/596/440 +f 118/722/546 1573/596/440 768/712/537 +f 118/722/546 768/712/537 1618/715/539 +f 118/722/546 1618/715/539 772/724/548 +f 118/722/546 772/724/548 1689/725/549 +f 118/722/546 1689/725/549 773/723/547 +f 119/726/550 775/727/551 1682/700/525 +f 119/726/550 1682/700/525 760/699/524 +f 119/726/550 760/699/524 1686/698/523 +f 119/726/550 1686/698/523 774/728/552 +f 119/726/550 774/728/552 1688/721/545 +f 119/726/550 1688/721/545 770/720/544 +f 119/726/550 770/720/544 1683/719/543 +f 119/726/550 1683/719/543 775/727/551 +f 120/729/553 773/723/547 1689/725/549 +f 120/729/553 1689/725/549 776/730/554 +f 120/729/553 776/730/554 1687/705/530 +f 120/729/553 1687/705/530 762/704/529 +f 120/729/553 762/704/529 1682/700/525 +f 120/729/553 1682/700/525 775/727/551 +f 120/729/553 775/727/551 1683/719/543 +f 120/729/553 1683/719/543 773/723/547 +f 121/731/555 779/732/556 1640/733/557 +f 121/731/555 1640/733/557 777/734/558 +f 121/731/555 777/734/558 1642/735/559 +f 121/731/555 1642/735/559 778/736/560 +f 121/731/555 778/736/560 1681/372/265 +f 121/731/555 1681/372/265 630/371/264 +f 121/731/555 630/371/264 1679/370/263 +f 121/731/555 1679/370/263 779/732/556 +f 122/737/561 633/377/269 1681/372/265 +f 122/737/561 1681/372/265 778/736/560 +f 122/737/561 778/736/560 1642/735/559 +f 122/737/561 1642/735/559 780/738/562 +f 122/737/561 780/738/562 1641/739/563 +f 122/737/561 1641/739/563 781/740/564 +f 122/737/561 781/740/564 1680/378/270 +f 122/737/561 1680/378/270 633/377/269 +f 123/741/565 783/742/566 1638/743/567 +f 123/741/565 1638/743/567 782/744/568 +f 123/741/565 782/744/568 1640/733/557 +f 123/741/565 1640/733/557 779/732/556 +f 123/741/565 779/732/556 1679/370/263 +f 123/741/565 1679/370/263 637/391/279 +f 123/741/565 637/391/279 1677/390/278 +f 123/741/565 1677/390/278 783/742/566 +f 124/745/569 639/395/281 1680/378/270 +f 124/745/569 1680/378/270 781/740/564 +f 124/745/569 781/740/564 1641/739/563 +f 124/745/569 1641/739/563 784/746/570 +f 124/745/569 784/746/570 1639/747/571 +f 124/745/569 1639/747/571 785/748/572 +f 124/745/569 785/748/572 1678/396/282 +f 124/745/569 1678/396/282 639/395/281 +f 125/749/573 787/750/574 1636/751/575 +f 125/749/573 1636/751/575 786/752/576 +f 125/749/573 786/752/576 1638/743/567 +f 125/749/573 1638/743/567 783/742/566 +f 125/749/573 783/742/566 1677/390/278 +f 125/749/573 1677/390/278 643/753/291 +f 125/749/573 643/753/291 1675/754/290 +f 125/749/573 1675/754/290 787/750/574 +f 126/755/577 645/410/293 1678/396/282 +f 126/755/577 1678/396/282 785/748/572 +f 126/755/577 785/748/572 1639/747/571 +f 126/755/577 1639/747/571 788/756/578 +f 126/755/577 788/756/578 1637/757/579 +f 126/755/577 1637/757/579 789/758/580 +f 126/755/577 789/758/580 1676/411/294 +f 126/755/577 1676/411/294 645/410/293 +f 127/759/581 655/760/315 1671/761/314 +f 127/759/581 1671/761/314 790/762/582 +f 127/759/581 790/762/582 1692/763/583 +f 127/759/581 1692/763/583 791/764/584 +f 127/759/581 791/764/584 1690/765/585 +f 127/759/581 1690/765/585 792/766/586 +f 127/759/581 792/766/586 1673/767/302 +f 127/759/581 1673/767/302 655/760/315 +f 128/768/587 795/769/588 1691/770/589 +f 128/768/587 1691/770/589 793/771/590 +f 128/768/587 793/771/590 1693/772/591 +f 128/768/587 1693/772/591 794/773/592 +f 128/768/587 794/773/592 1672/440/318 +f 128/768/587 1672/440/318 657/439/317 +f 128/768/587 657/439/317 1674/424/306 +f 128/768/587 1674/424/306 795/769/588 +f 129/774/593 796/775/594 1636/751/575 +f 129/774/593 1636/751/575 787/750/574 +f 129/774/593 787/750/574 1675/754/290 +f 129/774/593 1675/754/290 649/776/303 +f 129/774/593 649/776/303 1673/767/302 +f 129/774/593 1673/767/302 792/766/586 +f 129/774/593 792/766/586 1690/765/585 +f 129/774/593 1690/765/585 796/775/594 +f 130/777/595 795/769/588 1674/424/306 +f 130/777/595 1674/424/306 651/423/305 +f 130/777/595 651/423/305 1676/411/294 +f 130/777/595 1676/411/294 789/758/580 +f 130/777/595 789/758/580 1637/757/579 +f 130/777/595 1637/757/579 797/778/596 +f 130/777/595 797/778/596 1691/770/589 +f 130/777/595 1691/770/589 795/769/588 +f 131/779/597 801/780/598 1649/781/599 +f 131/779/597 1649/781/599 798/782/600 +f 131/779/597 798/782/600 1695/783/601 +f 131/779/597 1695/783/601 799/784/602 +f 131/779/597 799/784/602 1694/785/603 +f 131/779/597 1694/785/603 800/786/604 +f 131/779/597 800/786/604 1692/763/583 +f 131/779/597 1692/763/583 801/780/598 +f 132/787/605 804/788/606 1694/785/603 +f 132/787/605 1694/785/603 799/784/602 +f 132/787/605 799/784/602 1695/783/601 +f 132/787/605 1695/783/601 802/789/607 +f 132/787/605 802/789/607 1650/790/608 +f 132/787/605 1650/790/608 803/791/609 +f 132/787/605 803/791/609 1693/772/591 +f 132/787/605 1693/772/591 804/788/606 +f 133/792/610 806/793/611 1690/765/585 +f 133/792/610 1690/765/585 791/764/584 +f 133/792/610 791/764/584 1692/763/583 +f 133/792/610 1692/763/583 800/786/604 +f 133/792/610 800/786/604 1694/785/603 +f 133/792/610 1694/785/603 805/794/612 +f 133/792/610 805/794/612 1574/795/613 +f 133/792/610 1574/795/613 806/793/611 +f 134/796/614 805/794/612 1694/785/603 +f 134/796/614 1694/785/603 804/788/606 +f 134/796/614 804/788/606 1693/772/591 +f 134/796/614 1693/772/591 793/771/590 +f 134/796/614 793/771/590 1691/770/589 +f 134/796/614 1691/770/589 807/797/615 +f 134/796/614 807/797/615 1574/795/613 +f 134/796/614 1574/795/613 805/794/612 +f 135/798/616 809/799/617 1635/800/618 +f 135/798/616 1635/800/618 808/801/619 +f 135/798/616 808/801/619 1636/751/575 +f 135/798/616 1636/751/575 796/775/594 +f 135/798/616 796/775/594 1690/765/585 +f 135/798/616 1690/765/585 806/793/611 +f 135/798/616 806/793/611 1574/795/613 +f 135/798/616 1574/795/613 809/799/617 +f 136/802/620 807/797/615 1691/770/589 +f 136/802/620 1691/770/589 797/778/596 +f 136/802/620 797/778/596 1637/757/579 +f 136/802/620 1637/757/579 810/803/621 +f 136/802/620 810/803/621 1635/800/618 +f 136/802/620 1635/800/618 809/799/617 +f 136/802/620 809/799/617 1574/795/613 +f 136/802/620 1574/795/613 807/797/615 +f 137/804/622 814/805/623 1647/806/624 +f 137/804/622 1647/806/624 811/807/625 +f 137/804/622 811/807/625 1698/808/626 +f 137/804/622 1698/808/626 812/809/627 +f 137/804/622 812/809/627 1696/810/628 +f 137/804/622 1696/810/628 813/811/629 +f 137/804/622 813/811/629 1649/812/599 +f 137/804/622 1649/812/599 814/805/623 +f 138/813/630 818/814/631 1697/815/632 +f 138/813/630 1697/815/632 815/816/633 +f 138/813/630 815/816/633 1699/817/634 +f 138/813/630 1699/817/634 816/818/635 +f 138/813/630 816/818/635 1648/819/636 +f 138/813/630 1648/819/636 817/820/637 +f 138/821/630 817/822/637 1650/790/608 +f 138/821/630 1650/790/608 818/823/631 +f 139/824/638 821/825/639 1645/826/640 +f 139/824/638 1645/826/640 819/827/641 +f 139/824/638 819/827/641 1700/828/642 +f 139/824/638 1700/828/642 820/829/643 +f 139/830/638 820/831/643 1698/808/626 +f 139/830/638 1698/808/626 811/807/625 +f 139/830/638 811/807/625 1647/806/624 +f 139/830/638 1647/806/624 821/832/639 +f 140/833/644 816/834/635 1699/835/634 +f 140/833/644 1699/835/634 822/836/645 +f 140/833/644 822/836/645 1701/837/646 +f 140/833/644 1701/837/646 823/838/647 +f 140/833/644 823/838/647 1646/839/648 +f 140/833/644 1646/839/648 824/840/649 +f 140/833/644 824/840/649 1648/841/636 +f 140/833/644 1648/841/636 816/834/635 +f 141/842/650 827/843/651 1644/844/652 +f 141/842/650 1644/844/652 825/845/653 +f 141/842/650 825/845/653 1702/846/654 +f 141/842/650 1702/846/654 826/847/655 +f 141/842/650 826/847/655 1700/828/642 +f 141/842/650 1700/828/642 819/827/641 +f 141/842/650 819/827/641 1645/826/640 +f 141/842/650 1645/826/640 827/843/651 +f 142/848/656 823/838/647 1701/837/646 +f 142/848/656 1701/837/646 828/849/657 +f 142/850/656 828/851/657 1703/852/658 +f 142/850/656 1703/852/658 829/853/659 +f 142/850/656 829/853/659 1644/844/652 +f 142/850/656 1644/844/652 830/854/660 +f 142/848/656 830/855/660 1646/839/648 +f 142/848/656 1646/839/648 823/838/647 +f 143/856/661 833/857/662 1643/858/663 +f 143/856/661 1643/858/663 831/859/664 +f 143/860/661 831/861/664 1576/862/665 +f 143/860/661 1576/862/665 832/863/666 +f 143/860/661 832/863/666 1702/864/654 +f 143/860/661 1702/864/654 825/865/653 +f 143/866/661 825/867/653 1644/868/652 +f 143/866/661 1644/868/652 833/869/662 +f 144/870/667 829/853/659 1703/852/658 +f 144/870/667 1703/852/658 834/871/668 +f 144/870/667 834/871/668 1576/872/665 +f 144/870/667 1576/872/665 831/859/664 +f 144/870/667 831/859/664 1643/858/663 +f 144/870/667 1643/858/663 833/857/662 +f 144/870/667 833/857/662 1644/844/652 +f 144/870/667 1644/844/652 829/853/659 +f 145/873/669 836/874/670 1695/783/601 +f 145/873/669 1695/783/601 798/782/600 +f 145/873/669 798/782/600 1649/781/599 +f 145/873/669 1649/781/599 813/875/629 +f 145/876/669 813/811/629 1696/810/628 +f 145/876/669 1696/810/628 835/877/671 +f 145/876/669 835/877/671 1575/878/672 +f 145/876/669 1575/878/672 836/879/670 +f 146/880/673 837/881/674 1697/815/632 +f 146/880/673 1697/815/632 818/814/631 +f 146/882/673 818/823/631 1650/790/608 +f 146/882/673 1650/790/608 802/789/607 +f 146/882/673 802/789/607 1695/783/601 +f 146/882/673 1695/783/601 836/874/670 +f 146/880/673 836/883/670 1575/884/672 +f 146/880/673 1575/884/672 837/881/674 +f 147/885/675 840/886/676 1575/887/672 +f 147/885/675 1575/887/672 835/888/671 +f 147/885/675 835/888/671 1696/889/628 +f 147/885/675 1696/889/628 838/890/677 +f 147/885/675 838/890/677 1711/891/678 +f 147/885/675 1711/891/678 839/892/679 +f 147/885/675 839/892/679 1713/893/680 +f 147/885/675 1713/893/680 840/886/676 +f 148/894/681 842/895/682 1712/896/683 +f 148/894/681 1712/896/683 841/897/684 +f 148/894/681 841/897/684 1697/898/632 +f 148/894/681 1697/898/632 837/899/674 +f 148/894/681 837/899/674 1575/887/672 +f 148/894/681 1575/887/672 840/886/676 +f 148/894/681 840/886/676 1713/893/680 +f 148/894/681 1713/893/680 842/895/682 +f 149/900/685 832/863/666 1576/862/665 +f 149/900/685 1576/862/665 843/901/686 +f 149/902/685 843/903/686 1704/904/687 +f 149/902/685 1704/904/687 844/905/688 +f 149/902/685 844/905/688 1705/906/689 +f 149/902/685 1705/906/689 845/907/690 +f 149/900/685 845/908/690 1702/864/654 +f 149/900/685 1702/864/654 832/863/666 +f 150/909/691 847/910/692 1706/911/693 +f 150/909/691 1706/911/693 846/912/694 +f 150/909/691 846/912/694 1704/904/687 +f 150/909/691 1704/904/687 843/903/686 +f 150/909/691 843/903/686 1576/872/665 +f 150/909/691 1576/872/665 834/871/668 +f 150/909/691 834/871/668 1703/852/658 +f 150/909/691 1703/852/658 847/910/692 +f 151/913/695 826/847/655 1702/846/654 +f 151/913/695 1702/846/654 845/907/690 +f 151/913/695 845/907/690 1705/906/689 +f 151/913/695 1705/906/689 848/914/696 +f 151/913/695 848/914/696 1707/915/697 +f 151/913/695 1707/915/697 849/916/698 +f 151/913/695 849/916/698 1700/828/642 +f 151/913/695 1700/828/642 826/847/655 +f 152/917/699 851/918/700 1708/919/701 +f 152/917/699 1708/919/701 850/920/702 +f 152/917/699 850/920/702 1706/911/693 +f 152/917/699 1706/911/693 847/910/692 +f 152/917/699 847/910/692 1703/852/658 +f 152/917/699 1703/852/658 828/851/657 +f 152/921/699 828/849/657 1701/837/646 +f 152/921/699 1701/837/646 851/922/700 +f 153/923/703 820/829/643 1700/828/642 +f 153/923/703 1700/828/642 849/916/698 +f 153/923/703 849/916/698 1707/915/697 +f 153/923/703 1707/915/697 852/924/704 +f 153/923/703 852/924/704 1709/925/705 +f 153/923/703 1709/925/705 853/926/706 +f 153/927/703 853/928/706 1698/808/626 +f 153/927/703 1698/808/626 820/831/643 +f 154/929/707 855/930/708 1710/931/709 +f 154/929/707 1710/931/709 854/932/710 +f 154/929/707 854/932/710 1708/933/701 +f 154/929/707 1708/933/701 851/934/700 +f 154/935/707 851/922/700 1701/837/646 +f 154/935/707 1701/837/646 822/836/645 +f 154/935/707 822/836/645 1699/835/634 +f 154/935/707 1699/835/634 855/936/708 +f 155/937/711 812/809/627 1698/808/626 +f 155/937/711 1698/808/626 853/928/706 +f 155/938/711 853/939/706 1709/940/705 +f 155/938/711 1709/940/705 856/941/712 +f 155/938/711 856/941/712 1711/891/678 +f 155/938/711 1711/891/678 838/890/677 +f 155/937/711 838/942/677 1696/810/628 +f 155/937/711 1696/810/628 812/809/627 +f 156/943/713 841/897/684 1712/896/683 +f 156/943/713 1712/896/683 857/944/714 +f 156/943/713 857/944/714 1710/931/709 +f 156/943/713 1710/931/709 855/930/708 +f 156/943/713 855/930/708 1699/945/634 +f 156/943/713 1699/945/634 815/946/633 +f 156/943/713 815/946/633 1697/898/632 +f 156/943/713 1697/898/632 841/897/684 +f 157/947/715 844/948/688 1704/949/687 +f 157/947/715 1704/949/687 858/950/716 +f 157/947/715 858/950/716 1709/940/705 +f 157/947/715 1709/940/705 852/951/704 +f 157/952/715 852/924/704 1707/915/697 +f 157/952/715 1707/915/697 848/914/696 +f 157/947/715 848/953/696 1705/954/689 +f 157/947/715 1705/954/689 844/948/688 +f 158/955/717 850/956/702 1708/933/701 +f 158/955/717 1708/933/701 854/932/710 +f 158/955/717 854/932/710 1710/931/709 +f 158/955/717 1710/931/709 859/957/718 +f 158/955/717 859/957/718 1704/949/687 +f 158/955/717 1704/949/687 846/958/694 +f 158/959/717 846/912/694 1706/911/693 +f 158/959/717 1706/911/693 850/920/702 +f 159/960/719 858/950/716 1704/949/687 +f 159/960/719 1704/949/687 860/961/720 +f 159/960/719 860/961/720 1713/893/680 +f 159/960/719 1713/893/680 839/892/679 +f 159/960/719 839/892/679 1711/891/678 +f 159/960/719 1711/891/678 856/941/712 +f 159/960/719 856/941/712 1709/940/705 +f 159/960/719 1709/940/705 858/950/716 +f 160/962/721 857/944/714 1712/896/683 +f 160/962/721 1712/896/683 842/895/682 +f 160/962/721 842/895/682 1713/893/680 +f 160/962/721 1713/893/680 860/961/720 +f 160/962/721 860/961/720 1704/949/687 +f 160/962/721 1704/949/687 859/957/718 +f 160/962/721 859/957/718 1710/931/709 +f 160/962/721 1710/931/709 857/944/714 +f 161/963/722 862/964/723 1644/965/652 +f 161/963/722 1644/965/652 827/966/651 +f 161/963/722 827/966/651 1645/967/640 +f 161/963/722 1645/967/640 861/968/724 +f 161/963/722 861/968/724 1669/690/330 +f 161/963/722 1669/690/330 761/701/526 +f 161/963/722 761/701/526 1682/700/525 +f 161/963/722 1682/700/525 862/964/723 +f 162/969/725 764/703/528 1670/466/339 +f 162/969/725 1670/466/339 863/970/726 +f 162/969/725 863/970/726 1646/971/648 +f 162/969/725 1646/971/648 830/972/660 +f 162/969/725 830/972/660 1644/965/652 +f 162/969/725 1644/965/652 862/964/723 +f 162/969/725 862/964/723 1682/700/525 +f 162/969/725 1682/700/525 764/703/528 +f 163/973/727 861/968/724 1645/967/640 +f 163/973/727 1645/967/640 821/974/639 +f 163/973/727 821/974/639 1647/975/624 +f 163/973/727 1647/975/624 864/976/728 +f 163/977/727 864/978/728 1716/979/729 +f 163/977/727 1716/979/729 865/980/730 +f 163/973/727 865/981/730 1669/690/330 +f 163/973/727 1669/690/330 861/968/724 +f 164/982/731 867/983/732 1717/984/733 +f 164/982/731 1717/984/733 866/985/734 +f 164/982/731 866/985/734 1648/986/636 +f 164/982/731 1648/986/636 824/987/649 +f 164/982/731 824/987/649 1646/971/648 +f 164/982/731 1646/971/648 863/970/726 +f 164/982/731 863/970/726 1670/466/339 +f 164/982/731 1670/466/339 867/983/732 +f 165/988/735 864/978/728 1647/806/624 +f 165/988/735 1647/806/624 814/805/623 +f 165/989/735 814/990/623 1649/781/599 +f 165/989/735 1649/781/599 868/991/736 +f 165/988/735 868/992/736 1718/993/737 +f 165/988/735 1718/993/737 869/994/738 +f 165/988/735 869/994/738 1716/979/729 +f 165/988/735 1716/979/729 864/978/728 +f 166/995/739 871/996/740 1719/997/741 +f 166/995/739 1719/997/741 870/998/742 +f 166/995/739 870/998/742 1650/790/608 +f 166/995/739 1650/790/608 817/822/637 +f 166/995/739 817/822/637 1648/986/636 +f 166/995/739 1648/986/636 866/985/734 +f 166/995/739 866/985/734 1717/984/733 +f 166/995/739 1717/984/733 871/996/740 +f 167/999/743 868/991/736 1649/781/599 +f 167/999/743 1649/781/599 801/780/598 +f 167/999/743 801/780/598 1692/763/583 +f 167/999/743 1692/763/583 790/762/582 +f 167/999/743 790/762/582 1671/761/314 +f 167/999/743 1671/761/314 872/1000/744 +f 167/1001/743 872/1002/744 1718/1003/737 +f 167/1001/743 1718/1003/737 868/1004/736 +f 168/1005/745 873/1006/746 1672/440/318 +f 168/1005/745 1672/440/318 794/773/592 +f 168/1005/745 794/773/592 1693/772/591 +f 168/1005/745 1693/772/591 803/791/609 +f 168/1005/745 803/791/609 1650/790/608 +f 168/1005/745 1650/790/608 870/998/742 +f 168/1005/745 870/998/742 1719/997/741 +f 168/1005/745 1719/997/741 873/1006/746 +f 169/1007/747 654/435/313 1586/434/312 +f 169/1007/747 1586/434/312 874/1008/748 +f 169/1009/747 874/1010/748 1714/1011/749 +f 169/1009/747 1714/1011/749 875/1012/750 +f 169/1009/747 875/1012/750 1718/1003/737 +f 169/1009/747 1718/1003/737 872/1002/744 +f 169/1009/747 872/1002/744 1671/1013/314 +f 169/1009/747 1671/1013/314 654/1014/313 +f 170/1015/751 873/1016/746 1719/1017/741 +f 170/1015/751 1719/1017/741 876/1018/752 +f 170/1015/751 876/1018/752 1715/1019/753 +f 170/1015/751 1715/1019/753 877/1020/754 +f 170/1015/751 877/1020/754 1587/444/320 +f 170/1015/751 1587/444/320 658/443/319 +f 170/1015/751 658/443/319 1672/1021/318 +f 170/1015/751 1672/1021/318 873/1016/746 +f 171/1022/755 875/1023/750 1714/1024/749 +f 171/1022/755 1714/1024/749 878/1025/756 +f 171/1022/755 878/1025/756 1720/1026/757 +f 171/1022/755 1720/1026/757 879/1027/758 +f 171/1022/755 879/1027/758 1716/979/729 +f 171/1022/755 1716/979/729 869/994/738 +f 171/1022/755 869/994/738 1718/993/737 +f 171/1022/755 1718/993/737 875/1023/750 +f 172/1028/759 871/1029/740 1717/1030/733 +f 172/1028/759 1717/1030/733 880/1031/760 +f 172/1028/759 880/1031/760 1721/1032/761 +f 172/1028/759 1721/1032/761 881/1033/762 +f 172/1028/759 881/1033/762 1715/1019/753 +f 172/1028/759 1715/1019/753 876/1018/752 +f 172/1028/759 876/1018/752 1719/1017/741 +f 172/1028/759 1719/1017/741 871/1029/740 +f 173/1034/763 882/1035/764 1584/448/324 +f 173/1034/763 1584/448/324 663/447/323 +f 173/1034/763 663/447/323 1669/454/330 +f 173/1034/763 1669/454/330 865/980/730 +f 173/1034/763 865/980/730 1716/979/729 +f 173/1034/763 1716/979/729 879/1027/758 +f 173/1034/763 879/1027/758 1720/1026/757 +f 173/1034/763 1720/1026/757 882/1035/764 +f 174/1036/765 880/1031/760 1717/1030/733 +f 174/1036/765 1717/1030/733 867/1037/732 +f 174/1038/765 867/983/732 1670/466/339 +f 174/1038/765 1670/466/339 666/465/338 +f 174/1038/765 666/465/338 1585/1039/337 +f 174/1038/765 1585/1039/337 883/1040/766 +f 174/1036/765 883/1041/766 1721/1032/761 +f 174/1036/765 1721/1032/761 880/1031/760 +f 175/1042/767 886/1043/768 1636/751/575 +f 175/1042/767 1636/751/575 808/801/619 +f 175/1042/767 808/801/619 1635/800/618 +f 175/1042/767 1635/800/618 884/1044/769 +f 175/1042/767 884/1044/769 1577/1045/770 +f 175/1042/767 1577/1045/770 885/1046/771 +f 175/1042/767 885/1046/771 1727/1047/772 +f 175/1042/767 1727/1047/772 886/1043/768 +f 176/1048/773 888/1049/774 1577/1045/770 +f 176/1048/773 1577/1045/770 884/1044/769 +f 176/1048/773 884/1044/769 1635/800/618 +f 176/1048/773 1635/800/618 810/803/621 +f 176/1048/773 810/803/621 1637/757/579 +f 176/1048/773 1637/757/579 887/1050/775 +f 176/1048/773 887/1050/775 1728/1051/776 +f 176/1048/773 1728/1051/776 888/1049/774 +f 177/1052/777 890/1053/778 1638/743/567 +f 177/1052/777 1638/743/567 786/752/576 +f 177/1052/777 786/752/576 1636/751/575 +f 177/1052/777 1636/751/575 886/1043/768 +f 177/1052/777 886/1043/768 1727/1047/772 +f 177/1052/777 1727/1047/772 889/1054/779 +f 177/1052/777 889/1054/779 1725/1055/780 +f 177/1052/777 1725/1055/780 890/1053/778 +f 178/1056/781 892/1057/782 1728/1058/776 +f 178/1056/781 1728/1058/776 887/1059/775 +f 178/1060/781 887/1050/775 1637/757/579 +f 178/1060/781 1637/757/579 788/756/578 +f 178/1060/781 788/756/578 1639/747/571 +f 178/1060/781 1639/747/571 891/1061/783 +f 178/1056/781 891/1062/783 1726/1063/784 +f 178/1056/781 1726/1063/784 892/1057/782 +f 179/1064/785 894/1065/786 1640/733/557 +f 179/1064/785 1640/733/557 782/744/568 +f 179/1064/785 782/744/568 1638/743/567 +f 179/1064/785 1638/743/567 890/1053/778 +f 179/1064/785 890/1053/778 1725/1055/780 +f 179/1064/785 1725/1055/780 893/1066/787 +f 179/1067/785 893/1068/787 1723/1069/788 +f 179/1067/785 1723/1069/788 894/1070/786 +f 180/1071/789 896/1072/790 1726/1073/784 +f 180/1071/789 1726/1073/784 891/1074/783 +f 180/1075/789 891/1061/783 1639/747/571 +f 180/1075/789 1639/747/571 784/746/570 +f 180/1075/789 784/746/570 1641/739/563 +f 180/1075/789 1641/739/563 895/1076/791 +f 180/1071/789 895/1077/791 1724/1078/792 +f 180/1071/789 1724/1078/792 896/1072/790 +f 181/1079/793 898/1080/794 1642/735/559 +f 181/1079/793 1642/735/559 777/734/558 +f 181/1079/793 777/734/558 1640/733/557 +f 181/1079/793 1640/733/557 894/1065/786 +f 181/1081/793 894/1082/786 1723/1083/788 +f 181/1081/793 1723/1083/788 897/1084/795 +f 181/1081/793 897/1084/795 1722/1085/796 +f 181/1081/793 1722/1085/796 898/1086/794 +f 182/1087/797 899/1088/798 1724/1078/792 +f 182/1087/797 1724/1078/792 895/1077/791 +f 182/1089/797 895/1076/791 1641/739/563 +f 182/1089/797 1641/739/563 780/738/562 +f 182/1089/797 780/738/562 1642/735/559 +f 182/1089/797 1642/735/559 898/1080/794 +f 182/1087/797 898/1086/794 1722/1085/796 +f 182/1087/797 1722/1085/796 899/1088/798 +f 183/1090/799 902/1091/800 1722/1085/796 +f 183/1090/799 1722/1085/796 897/1084/795 +f 183/1092/799 897/1093/795 1723/1069/788 +f 183/1092/799 1723/1069/788 900/1094/801 +f 183/1090/799 900/1095/801 1734/1096/802 +f 183/1090/799 1734/1096/802 901/1097/803 +f 183/1090/799 901/1097/803 1736/1098/804 +f 183/1090/799 1736/1098/804 902/1091/800 +f 184/1099/805 904/1100/806 1735/1101/807 +f 184/1099/805 1735/1101/807 903/1102/808 +f 184/1099/805 903/1102/808 1724/1078/792 +f 184/1099/805 1724/1078/792 899/1088/798 +f 184/1099/805 899/1088/798 1722/1085/796 +f 184/1099/805 1722/1085/796 902/1091/800 +f 184/1099/805 902/1091/800 1736/1098/804 +f 184/1099/805 1736/1098/804 904/1100/806 +f 185/1103/809 900/1094/801 1723/1069/788 +f 185/1103/809 1723/1069/788 893/1068/787 +f 185/1103/809 893/1068/787 1725/1104/780 +f 185/1103/809 1725/1104/780 905/1105/810 +f 185/1103/809 905/1105/810 1732/1106/811 +f 185/1103/809 1732/1106/811 906/1107/812 +f 185/1103/809 906/1107/812 1734/1108/802 +f 185/1103/809 1734/1108/802 900/1094/801 +f 186/1109/813 908/1110/814 1733/1111/815 +f 186/1109/813 1733/1111/815 907/1112/816 +f 186/1109/813 907/1112/816 1726/1073/784 +f 186/1109/813 1726/1073/784 896/1072/790 +f 186/1109/813 896/1072/790 1724/1078/792 +f 186/1109/813 1724/1078/792 903/1102/808 +f 186/1109/813 903/1102/808 1735/1101/807 +f 186/1109/813 1735/1101/807 908/1110/814 +f 187/1113/817 905/1105/810 1725/1104/780 +f 187/1113/817 1725/1104/780 889/1114/779 +f 187/1115/817 889/1116/779 1727/1117/772 +f 187/1115/817 1727/1117/772 909/1118/818 +f 187/1115/817 909/1118/818 1730/1119/819 +f 187/1115/817 1730/1119/819 910/1120/820 +f 187/1113/817 910/1121/820 1732/1106/811 +f 187/1113/817 1732/1106/811 905/1105/810 +f 188/1122/821 912/1123/822 1731/1124/823 +f 188/1122/821 1731/1124/823 911/1125/824 +f 188/1122/821 911/1125/824 1728/1058/776 +f 188/1122/821 1728/1058/776 892/1057/782 +f 188/1122/821 892/1057/782 1726/1063/784 +f 188/1122/821 1726/1063/784 907/1126/816 +f 188/1122/821 907/1126/816 1733/1127/815 +f 188/1122/821 1733/1127/815 912/1123/822 +f 189/1128/825 909/1118/818 1727/1117/772 +f 189/1128/825 1727/1117/772 885/1129/771 +f 189/1128/825 885/1129/771 1577/1130/770 +f 189/1128/825 1577/1130/770 913/1131/826 +f 189/1132/825 913/1133/826 1729/1134/827 +f 189/1132/825 1729/1134/827 914/1135/828 +f 189/1132/825 914/1135/828 1730/1136/819 +f 189/1132/825 1730/1136/819 909/1137/818 +f 190/1138/829 915/1139/830 1729/1134/827 +f 190/1138/829 1729/1134/827 913/1133/826 +f 190/1140/829 913/1131/826 1577/1130/770 +f 190/1140/829 1577/1130/770 888/1141/774 +f 190/1140/829 888/1141/774 1728/1142/776 +f 190/1140/829 1728/1142/776 911/1143/824 +f 190/1138/829 911/1144/824 1731/1145/823 +f 190/1138/829 1731/1145/823 915/1139/830 +f 191/1146/831 914/1135/828 1729/1134/827 +f 191/1146/831 1729/1134/827 916/1147/832 +f 191/1146/831 916/1147/832 1736/1148/804 +f 191/1146/831 1736/1148/804 901/1149/803 +f 191/1146/831 901/1149/803 1734/1150/802 +f 191/1146/831 1734/1150/802 917/1151/833 +f 191/1146/831 917/1151/833 1730/1136/819 +f 191/1146/831 1730/1136/819 914/1135/828 +f 192/1152/834 918/1153/835 1735/1154/807 +f 192/1152/834 1735/1154/807 904/1155/806 +f 192/1152/834 904/1155/806 1736/1148/804 +f 192/1152/834 1736/1148/804 916/1147/832 +f 192/1152/834 916/1147/832 1729/1134/827 +f 192/1152/834 1729/1134/827 915/1139/830 +f 192/1152/834 915/1139/830 1731/1145/823 +f 192/1152/834 1731/1145/823 918/1153/835 +f 193/1156/836 910/1157/820 1730/1136/819 +f 193/1156/836 1730/1136/819 917/1151/833 +f 193/1156/836 917/1151/833 1734/1150/802 +f 193/1156/836 1734/1150/802 906/1158/812 +f 193/1159/836 906/1107/812 1732/1106/811 +f 193/1159/836 1732/1106/811 910/1121/820 +f 194/1160/837 912/1123/822 1733/1127/815 +f 194/1160/837 1733/1127/815 908/1161/814 +f 194/1162/837 908/1163/814 1735/1154/807 +f 194/1162/837 1735/1154/807 918/1153/835 +f 194/1162/837 918/1153/835 1731/1145/823 +f 194/1162/837 1731/1145/823 912/1164/822 +f 195/1165/838 921/1166/839 1688/721/545 +f 195/1165/838 1688/721/545 774/728/552 +f 195/1165/838 774/728/552 1686/698/523 +f 195/1165/838 1686/698/523 919/1167/840 +f 195/1165/838 919/1167/840 1739/1168/841 +f 195/1165/838 1739/1168/841 920/1169/842 +f 195/1165/838 920/1169/842 1737/1170/843 +f 195/1165/838 1737/1170/843 921/1166/839 +f 196/1171/844 924/1172/845 1740/1173/846 +f 196/1171/844 1740/1173/846 922/1174/847 +f 196/1171/844 922/1174/847 1687/705/530 +f 196/1171/844 1687/705/530 776/730/554 +f 196/1171/844 776/730/554 1689/725/549 +f 196/1171/844 1689/725/549 923/1175/848 +f 196/1171/844 923/1175/848 1738/1176/849 +f 196/1171/844 1738/1176/849 924/1172/845 +f 197/1177/850 926/1178/851 1617/710/535 +f 197/1177/850 1617/710/535 771/717/541 +f 197/1177/850 771/717/541 1688/721/545 +f 197/1177/850 1688/721/545 921/1166/839 +f 197/1177/850 921/1166/839 1737/1170/843 +f 197/1177/850 1737/1170/843 925/1179/852 +f 197/1177/850 925/1179/852 1759/1180/853 +f 197/1177/850 1759/1180/853 926/1178/851 +f 198/1181/854 928/1182/855 1738/1176/849 +f 198/1181/854 1738/1176/849 923/1175/848 +f 198/1181/854 923/1175/848 1689/725/549 +f 198/1181/854 1689/725/549 772/724/548 +f 198/1181/854 772/724/548 1618/715/539 +f 198/1181/854 1618/715/539 927/1183/856 +f 198/1181/854 927/1183/856 1760/1184/857 +f 198/1181/854 1760/1184/857 928/1182/855 +f 199/1185/858 930/1186/859 1615/606/447 +f 199/1185/858 1615/606/447 766/708/533 +f 199/1185/858 766/708/533 1617/710/535 +f 199/1185/858 1617/710/535 926/1178/851 +f 199/1185/858 926/1178/851 1759/1180/853 +f 199/1185/858 1759/1180/853 929/1187/860 +f 199/1185/858 929/1187/860 1761/1188/861 +f 199/1185/858 1761/1188/861 930/1186/859 +f 200/1189/862 932/1190/863 1760/1184/857 +f 200/1189/862 1760/1184/857 927/1183/856 +f 200/1189/862 927/1183/856 1618/715/539 +f 200/1189/862 1618/715/539 767/714/538 +f 200/1189/862 767/714/538 1616/616/455 +f 200/1189/862 1616/616/455 931/1191/864 +f 200/1189/862 931/1191/864 1762/1192/865 +f 200/1189/862 1762/1192/865 932/1190/863 +f 201/1193/866 919/1167/840 1686/698/523 +f 201/1193/866 1686/698/523 759/697/522 +f 201/1193/866 759/697/522 1619/688/515 +f 201/1193/866 1619/688/515 933/1194/867 +f 201/1193/866 933/1194/867 1757/1195/868 +f 201/1193/866 1757/1195/868 934/1196/869 +f 201/1193/866 934/1196/869 1739/1168/841 +f 201/1193/866 1739/1168/841 919/1167/840 +f 202/1197/870 936/1198/871 1758/1199/872 +f 202/1197/870 1758/1199/872 935/1200/873 +f 202/1197/870 935/1200/873 1620/694/519 +f 202/1197/870 1620/694/519 763/706/531 +f 202/1197/870 763/706/531 1687/705/530 +f 202/1197/870 1687/705/530 922/1174/847 +f 202/1197/870 922/1174/847 1740/1173/846 +f 202/1197/870 1740/1173/846 936/1198/871 +f 203/1201/874 933/1194/867 1619/688/515 +f 203/1201/874 1619/688/515 756/687/514 +f 203/1201/874 756/687/514 1621/680/507 +f 203/1201/874 1621/680/507 937/1202/875 +f 203/1201/874 937/1202/875 1755/1203/876 +f 203/1201/874 1755/1203/876 938/1204/877 +f 203/1201/874 938/1204/877 1757/1195/868 +f 203/1201/874 1757/1195/868 933/1194/867 +f 204/1205/878 940/1206/879 1756/1207/880 +f 204/1205/878 1756/1207/880 939/1208/881 +f 204/1205/878 939/1208/881 1622/684/511 +f 204/1205/878 1622/684/511 758/695/520 +f 204/1205/878 758/695/520 1620/694/519 +f 204/1205/878 1620/694/519 935/1200/873 +f 204/1205/878 935/1200/873 1758/1199/872 +f 204/1205/878 1758/1199/872 940/1206/879 +f 205/1209/882 937/1202/875 1621/680/507 +f 205/1209/882 1621/680/507 752/679/506 +f 205/1209/882 752/679/506 1623/670/499 +f 205/1209/882 1623/670/499 941/1210/883 +f 205/1209/882 941/1210/883 1753/1211/884 +f 205/1209/882 1753/1211/884 942/1212/885 +f 205/1209/882 942/1212/885 1755/1203/876 +f 205/1209/882 1755/1203/876 937/1202/875 +f 206/1213/886 944/1214/887 1754/1215/888 +f 206/1213/886 1754/1215/888 943/1216/889 +f 206/1213/886 943/1216/889 1624/676/503 +f 206/1213/886 1624/676/503 754/685/512 +f 206/1213/886 754/685/512 1622/684/511 +f 206/1213/886 1622/684/511 939/1208/881 +f 206/1213/886 939/1208/881 1756/1207/880 +f 206/1213/886 1756/1207/880 944/1214/887 +f 207/1217/890 941/1218/883 1623/1219/499 +f 207/1217/890 1623/1219/499 748/1220/498 +f 207/1221/890 748/669/498 1625/660/491 +f 207/1221/890 1625/660/491 945/1222/891 +f 207/1221/890 945/1222/891 1751/1223/892 +f 207/1221/890 1751/1223/892 946/1224/893 +f 207/1217/890 946/1225/893 1753/1226/884 +f 207/1217/890 1753/1226/884 941/1218/883 +f 208/1227/894 948/1228/895 1752/1229/896 +f 208/1227/894 1752/1229/896 947/1230/897 +f 208/1227/894 947/1230/897 1626/666/495 +f 208/1227/894 1626/666/495 750/677/504 +f 208/1227/894 750/677/504 1624/676/503 +f 208/1227/894 1624/676/503 943/1216/889 +f 208/1227/894 943/1216/889 1754/1215/888 +f 208/1227/894 1754/1215/888 948/1228/895 +f 209/1231/898 945/1222/891 1625/660/491 +f 209/1231/898 1625/660/491 744/659/490 +f 209/1231/898 744/659/490 1627/650/483 +f 209/1231/898 1627/650/483 949/1232/899 +f 209/1231/898 949/1232/899 1749/1233/900 +f 209/1231/898 1749/1233/900 950/1234/901 +f 209/1231/898 950/1234/901 1751/1223/892 +f 209/1231/898 1751/1223/892 945/1222/891 +f 210/1235/902 952/1236/903 1750/1237/904 +f 210/1235/902 1750/1237/904 951/1238/905 +f 210/1235/902 951/1238/905 1628/656/487 +f 210/1235/902 1628/656/487 746/667/496 +f 210/1235/902 746/667/496 1626/666/495 +f 210/1235/902 1626/666/495 947/1230/897 +f 210/1235/902 947/1230/897 1752/1229/896 +f 210/1235/902 1752/1229/896 952/1236/903 +f 211/1239/906 949/1232/899 1627/650/483 +f 211/1239/906 1627/650/483 740/649/482 +f 211/1239/906 740/649/482 1629/640/475 +f 211/1239/906 1629/640/475 953/1240/907 +f 211/1239/906 953/1240/907 1747/1241/908 +f 211/1239/906 1747/1241/908 954/1242/909 +f 211/1239/906 954/1242/909 1749/1233/900 +f 211/1239/906 1749/1233/900 949/1232/899 +f 212/1243/910 956/1244/911 1748/1245/912 +f 212/1243/910 1748/1245/912 955/1246/913 +f 212/1243/910 955/1246/913 1630/646/479 +f 212/1243/910 1630/646/479 742/657/488 +f 212/1243/910 742/657/488 1628/656/487 +f 212/1243/910 1628/656/487 951/1238/905 +f 212/1243/910 951/1238/905 1750/1237/904 +f 212/1243/910 1750/1237/904 956/1244/911 +f 213/1247/914 953/1240/907 1629/640/475 +f 213/1247/914 1629/640/475 736/639/474 +f 213/1247/914 736/639/474 1631/630/467 +f 213/1247/914 1631/630/467 957/1248/915 +f 213/1249/914 957/1250/915 1745/1251/916 +f 213/1249/914 1745/1251/916 958/1252/917 +f 213/1249/914 958/1252/917 1747/1253/908 +f 213/1249/914 1747/1253/908 953/1254/907 +f 214/1255/918 960/1256/919 1746/1257/920 +f 214/1255/918 1746/1257/920 959/1258/921 +f 214/1259/918 959/1260/921 1632/636/471 +f 214/1259/918 1632/636/471 738/647/480 +f 214/1259/918 738/647/480 1630/646/479 +f 214/1259/918 1630/646/479 955/1246/913 +f 214/1259/918 955/1246/913 1748/1245/912 +f 214/1259/918 1748/1245/912 960/1261/919 +f 215/1262/922 957/1248/915 1631/630/467 +f 215/1262/922 1631/630/467 732/629/466 +f 215/1262/922 732/629/466 1684/621/459 +f 215/1262/922 1684/621/459 961/1263/923 +f 215/1264/922 961/1265/923 1741/1266/924 +f 215/1264/922 1741/1266/924 962/1267/925 +f 215/1264/922 962/1267/925 1745/1251/916 +f 215/1264/922 1745/1251/916 957/1250/915 +f 216/1268/926 964/1269/927 1742/1270/928 +f 216/1268/926 1742/1270/928 963/1271/929 +f 216/1268/926 963/1271/929 1685/626/463 +f 216/1268/926 1685/626/463 734/637/472 +f 216/1268/926 734/637/472 1632/636/471 +f 216/1268/926 1632/636/471 959/1260/921 +f 216/1272/926 959/1273/921 1746/1274/920 +f 216/1272/926 1746/1274/920 964/1275/927 +f 217/1276/930 961/1263/923 1684/621/459 +f 217/1276/930 1684/621/459 727/620/458 +f 217/1276/930 727/620/458 1633/608/449 +f 217/1276/930 1633/608/449 965/1277/931 +f 217/1276/930 965/1277/931 1743/1278/932 +f 217/1276/930 1743/1278/932 966/1279/933 +f 217/1276/930 966/1279/933 1741/1280/924 +f 217/1276/930 1741/1280/924 961/1263/923 +f 218/1281/934 968/1282/935 1744/1283/936 +f 218/1281/934 1744/1283/936 967/1284/937 +f 218/1281/934 967/1284/937 1634/614/453 +f 218/1281/934 1634/614/453 730/627/464 +f 218/1281/934 730/627/464 1685/626/463 +f 218/1281/934 1685/626/463 963/1271/929 +f 218/1281/934 963/1271/929 1742/1270/928 +f 218/1281/934 1742/1270/928 968/1282/935 +f 219/1285/938 965/1277/931 1633/608/449 +f 219/1285/938 1633/608/449 721/607/448 +f 219/1285/938 721/607/448 1615/606/447 +f 219/1285/938 1615/606/447 930/1186/859 +f 219/1285/938 930/1186/859 1761/1188/861 +f 219/1285/938 1761/1188/861 969/1286/939 +f 219/1285/938 969/1286/939 1743/1278/932 +f 219/1285/938 1743/1278/932 965/1277/931 +f 220/1287/940 970/1288/941 1762/1192/865 +f 220/1287/940 1762/1192/865 931/1191/864 +f 220/1287/940 931/1191/864 1616/616/455 +f 220/1287/940 1616/616/455 725/615/454 +f 220/1287/940 725/615/454 1634/614/453 +f 220/1287/940 1634/614/453 967/1284/937 +f 220/1287/940 967/1284/937 1744/1283/936 +f 220/1287/940 1744/1283/936 970/1288/941 +f 221/1289/942 973/1290/943 1743/1278/932 +f 221/1289/942 1743/1278/932 969/1286/939 +f 221/1289/942 969/1286/939 1761/1188/861 +f 221/1289/942 1761/1188/861 971/1291/944 +f 221/1289/942 971/1291/944 1763/1292/945 +f 221/1289/942 1763/1292/945 972/1293/946 +f 221/1289/942 972/1293/946 1781/1294/947 +f 221/1289/942 1781/1294/947 973/1290/943 +f 222/1295/948 976/1296/949 1764/1297/950 +f 222/1295/948 1764/1297/950 974/1298/951 +f 222/1295/948 974/1298/951 1762/1192/865 +f 222/1295/948 1762/1192/865 970/1288/941 +f 222/1295/948 970/1288/941 1744/1283/936 +f 222/1295/948 1744/1283/936 975/1299/952 +f 222/1295/948 975/1299/952 1782/1300/953 +f 222/1295/948 1782/1300/953 976/1296/949 +f 223/1301/954 978/1302/955 1741/1280/924 +f 223/1301/954 1741/1280/924 966/1279/933 +f 223/1301/954 966/1279/933 1743/1278/932 +f 223/1301/954 1743/1278/932 973/1290/943 +f 223/1301/954 973/1290/943 1781/1294/947 +f 223/1301/954 1781/1294/947 977/1303/956 +f 223/1301/954 977/1303/956 1783/1304/957 +f 223/1301/954 1783/1304/957 978/1302/955 +f 224/1305/958 980/1306/959 1782/1300/953 +f 224/1305/958 1782/1300/953 975/1299/952 +f 224/1305/958 975/1299/952 1744/1283/936 +f 224/1305/958 1744/1283/936 968/1282/935 +f 224/1305/958 968/1282/935 1742/1270/928 +f 224/1305/958 1742/1270/928 979/1307/960 +f 224/1305/958 979/1307/960 1784/1308/961 +f 224/1305/958 1784/1308/961 980/1306/959 +f 225/1309/962 982/1310/963 1745/1311/916 +f 225/1309/962 1745/1311/916 962/1312/925 +f 225/1309/962 962/1312/925 1741/1280/924 +f 225/1309/962 1741/1280/924 978/1302/955 +f 225/1309/962 978/1302/955 1783/1304/957 +f 225/1309/962 1783/1304/957 981/1313/964 +f 225/1309/962 981/1313/964 1779/1314/965 +f 225/1309/962 1779/1314/965 982/1310/963 +f 226/1315/966 984/1316/967 1784/1308/961 +f 226/1315/966 1784/1308/961 979/1307/960 +f 226/1315/966 979/1307/960 1742/1270/928 +f 226/1315/966 1742/1270/928 964/1269/927 +f 226/1315/966 964/1269/927 1746/1317/920 +f 226/1315/966 1746/1317/920 983/1318/968 +f 226/1315/966 983/1318/968 1780/1319/969 +f 226/1315/966 1780/1319/969 984/1316/967 +f 227/1320/970 986/1321/971 1747/1241/908 +f 227/1320/970 1747/1241/908 958/1322/917 +f 227/1320/970 958/1322/917 1745/1311/916 +f 227/1320/970 1745/1311/916 982/1310/963 +f 227/1320/970 982/1310/963 1779/1314/965 +f 227/1320/970 1779/1314/965 985/1323/972 +f 227/1320/970 985/1323/972 1777/1324/973 +f 227/1320/970 1777/1324/973 986/1321/971 +f 228/1325/974 988/1326/975 1780/1319/969 +f 228/1325/974 1780/1319/969 983/1318/968 +f 228/1325/974 983/1318/968 1746/1317/920 +f 228/1325/974 1746/1317/920 960/1261/919 +f 228/1325/974 960/1261/919 1748/1245/912 +f 228/1325/974 1748/1245/912 987/1327/976 +f 228/1325/974 987/1327/976 1778/1328/977 +f 228/1325/974 1778/1328/977 988/1326/975 +f 229/1329/978 990/1330/979 1749/1233/900 +f 229/1329/978 1749/1233/900 954/1242/909 +f 229/1329/978 954/1242/909 1747/1241/908 +f 229/1329/978 1747/1241/908 986/1321/971 +f 229/1329/978 986/1321/971 1777/1324/973 +f 229/1329/978 1777/1324/973 989/1331/980 +f 229/1329/978 989/1331/980 1775/1332/981 +f 229/1329/978 1775/1332/981 990/1330/979 +f 230/1333/982 992/1334/983 1778/1328/977 +f 230/1333/982 1778/1328/977 987/1327/976 +f 230/1333/982 987/1327/976 1748/1245/912 +f 230/1333/982 1748/1245/912 956/1244/911 +f 230/1333/982 956/1244/911 1750/1237/904 +f 230/1333/982 1750/1237/904 991/1335/984 +f 230/1333/982 991/1335/984 1776/1336/985 +f 230/1333/982 1776/1336/985 992/1334/983 +f 231/1337/986 994/1338/987 1751/1223/892 +f 231/1337/986 1751/1223/892 950/1234/901 +f 231/1337/986 950/1234/901 1749/1233/900 +f 231/1337/986 1749/1233/900 990/1330/979 +f 231/1337/986 990/1330/979 1775/1332/981 +f 231/1337/986 1775/1332/981 993/1339/988 +f 231/1337/986 993/1339/988 1773/1340/989 +f 231/1337/986 1773/1340/989 994/1338/987 +f 232/1341/990 996/1342/991 1776/1336/985 +f 232/1341/990 1776/1336/985 991/1335/984 +f 232/1341/990 991/1335/984 1750/1237/904 +f 232/1341/990 1750/1237/904 952/1236/903 +f 232/1341/990 952/1236/903 1752/1229/896 +f 232/1341/990 1752/1229/896 995/1343/992 +f 232/1341/990 995/1343/992 1774/1344/993 +f 232/1341/990 1774/1344/993 996/1342/991 +f 233/1345/994 998/1346/995 1753/1211/884 +f 233/1345/994 1753/1211/884 946/1224/893 +f 233/1345/994 946/1224/893 1751/1223/892 +f 233/1345/994 1751/1223/892 994/1338/987 +f 233/1345/994 994/1338/987 1773/1340/989 +f 233/1345/994 1773/1340/989 997/1347/996 +f 233/1348/994 997/1349/996 1771/1350/997 +f 233/1348/994 1771/1350/997 998/1351/995 +f 234/1352/998 1000/1353/999 1774/1354/993 +f 234/1352/998 1774/1354/993 995/1355/992 +f 234/1356/998 995/1343/992 1752/1229/896 +f 234/1356/998 1752/1229/896 948/1228/895 +f 234/1356/998 948/1228/895 1754/1215/888 +f 234/1356/998 1754/1215/888 999/1357/1000 +f 234/1352/998 999/1358/1000 1772/1359/1001 +f 234/1352/998 1772/1359/1001 1000/1353/999 +f 235/1360/1002 1002/1361/1003 1755/1203/876 +f 235/1360/1002 1755/1203/876 942/1212/885 +f 235/1360/1002 942/1212/885 1753/1211/884 +f 235/1360/1002 1753/1211/884 998/1346/995 +f 235/1362/1002 998/1351/995 1771/1350/997 +f 235/1362/1002 1771/1350/997 1001/1363/1004 +f 235/1362/1002 1001/1363/1004 1769/1364/1005 +f 235/1362/1002 1769/1364/1005 1002/1365/1003 +f 236/1366/1006 1004/1367/1007 1772/1359/1001 +f 236/1366/1006 1772/1359/1001 999/1358/1000 +f 236/1368/1006 999/1357/1000 1754/1215/888 +f 236/1368/1006 1754/1215/888 944/1214/887 +f 236/1368/1006 944/1214/887 1756/1207/880 +f 236/1368/1006 1756/1207/880 1003/1369/1008 +f 236/1366/1006 1003/1370/1008 1770/1371/1009 +f 236/1366/1006 1770/1371/1009 1004/1367/1007 +f 237/1372/1010 1006/1373/1011 1757/1195/868 +f 237/1372/1010 1757/1195/868 938/1204/877 +f 237/1372/1010 938/1204/877 1755/1203/876 +f 237/1372/1010 1755/1203/876 1002/1361/1003 +f 237/1374/1010 1002/1365/1003 1769/1364/1005 +f 237/1374/1010 1769/1364/1005 1005/1375/1012 +f 237/1374/1010 1005/1375/1012 1767/1376/1013 +f 237/1374/1010 1767/1376/1013 1006/1377/1011 +f 238/1378/1014 1008/1379/1015 1770/1371/1009 +f 238/1378/1014 1770/1371/1009 1003/1370/1008 +f 238/1380/1014 1003/1369/1008 1756/1207/880 +f 238/1380/1014 1756/1207/880 940/1206/879 +f 238/1380/1014 940/1206/879 1758/1199/872 +f 238/1380/1014 1758/1199/872 1007/1381/1016 +f 238/1382/1014 1007/1383/1016 1768/1384/1017 +f 238/1382/1014 1768/1384/1017 1008/1385/1015 +f 239/1386/1018 1010/1387/1019 1739/1388/841 +f 239/1386/1018 1739/1388/841 934/1389/869 +f 239/1390/1018 934/1196/869 1757/1195/868 +f 239/1390/1018 1757/1195/868 1006/1373/1011 +f 239/1386/1018 1006/1377/1011 1767/1376/1013 +f 239/1386/1018 1767/1376/1013 1009/1391/1020 +f 239/1386/1018 1009/1391/1020 1785/1392/1021 +f 239/1386/1018 1785/1392/1021 1010/1387/1019 +f 240/1393/1022 1012/1394/1023 1768/1384/1017 +f 240/1393/1022 1768/1384/1017 1007/1383/1016 +f 240/1395/1022 1007/1381/1016 1758/1199/872 +f 240/1395/1022 1758/1199/872 936/1198/871 +f 240/1395/1022 936/1198/871 1740/1173/846 +f 240/1395/1022 1740/1173/846 1011/1396/1024 +f 240/1393/1022 1011/1397/1024 1786/1398/1025 +f 240/1393/1022 1786/1398/1025 1012/1394/1023 +f 241/1399/1026 971/1291/944 1761/1188/861 +f 241/1399/1026 1761/1188/861 929/1187/860 +f 241/1399/1026 929/1187/860 1759/1180/853 +f 241/1399/1026 1759/1180/853 1013/1400/1027 +f 241/1401/1026 1013/1402/1027 1765/1403/1028 +f 241/1401/1026 1765/1403/1028 1014/1404/1029 +f 241/1401/1026 1014/1404/1029 1763/1405/945 +f 241/1401/1026 1763/1405/945 971/1406/944 +f 242/1407/1030 1016/1408/1031 1766/1409/1032 +f 242/1407/1030 1766/1409/1032 1015/1410/1033 +f 242/1407/1030 1015/1410/1033 1760/1184/857 +f 242/1407/1030 1760/1184/857 932/1190/863 +f 242/1407/1030 932/1190/863 1762/1192/865 +f 242/1407/1030 1762/1192/865 974/1298/951 +f 242/1407/1030 974/1298/951 1764/1297/950 +f 242/1407/1030 1764/1297/950 1016/1408/1031 +f 243/1411/1034 1013/1400/1027 1759/1180/853 +f 243/1411/1034 1759/1180/853 925/1179/852 +f 243/1411/1034 925/1179/852 1737/1170/843 +f 243/1411/1034 1737/1170/843 1017/1412/1035 +f 243/1413/1034 1017/1414/1035 1787/1415/1036 +f 243/1413/1034 1787/1415/1036 1018/1416/1037 +f 243/1413/1034 1018/1416/1037 1765/1403/1028 +f 243/1413/1034 1765/1403/1028 1013/1402/1027 +f 244/1417/1038 1020/1418/1039 1788/1419/1040 +f 244/1417/1038 1788/1419/1040 1019/1420/1041 +f 244/1417/1038 1019/1420/1041 1738/1176/849 +f 244/1417/1038 1738/1176/849 928/1182/855 +f 244/1417/1038 928/1182/855 1760/1184/857 +f 244/1417/1038 1760/1184/857 1015/1410/1033 +f 244/1417/1038 1015/1410/1033 1766/1409/1032 +f 244/1417/1038 1766/1409/1032 1020/1418/1039 +f 245/1421/1042 1017/1412/1035 1737/1170/843 +f 245/1421/1042 1737/1170/843 920/1169/842 +f 245/1422/1042 920/1423/842 1739/1388/841 +f 245/1422/1042 1739/1388/841 1010/1387/1019 +f 245/1422/1042 1010/1387/1019 1785/1392/1021 +f 245/1422/1042 1785/1392/1021 1021/1424/1043 +f 245/1425/1042 1021/1426/1043 1787/1415/1036 +f 245/1425/1042 1787/1415/1036 1017/1414/1035 +f 246/1427/1044 1022/1428/1045 1786/1398/1025 +f 246/1427/1044 1786/1398/1025 1011/1397/1024 +f 246/1429/1044 1011/1396/1024 1740/1173/846 +f 246/1429/1044 1740/1173/846 924/1172/845 +f 246/1429/1044 924/1172/845 1738/1176/849 +f 246/1429/1044 1738/1176/849 1019/1420/1041 +f 246/1427/1044 1019/1430/1041 1788/1431/1040 +f 246/1427/1044 1788/1431/1040 1022/1428/1045 +f 247/1432/1046 1025/1433/1047 1572/592/438 +f 247/1432/1046 1572/592/438 716/591/437 +f 247/1432/1046 716/591/437 1613/578/426 +f 247/1432/1046 1613/578/426 1023/1434/1048 +f 247/1432/1046 1023/1434/1048 1789/1435/1049 +f 247/1432/1046 1789/1435/1049 1024/1436/1050 +f 247/1432/1046 1024/1436/1050 1578/1437/1051 +f 247/1432/1046 1578/1437/1051 1025/1433/1047 +f 248/1438/1052 1027/1439/1053 1790/1440/1054 +f 248/1438/1052 1790/1440/1054 1026/1441/1055 +f 248/1438/1052 1026/1441/1055 1614/588/434 +f 248/1438/1052 1614/588/434 719/603/444 +f 248/1438/1052 719/603/444 1572/602/438 +f 248/1438/1052 1572/602/438 1025/1442/1047 +f 248/1438/1052 1025/1442/1047 1578/1443/1051 +f 248/1438/1052 1578/1443/1051 1027/1439/1053 +f 249/1444/1056 1023/1434/1048 1613/578/426 +f 249/1444/1056 1613/578/426 710/577/425 +f 249/1444/1056 710/577/425 1611/559/414 +f 249/1444/1056 1611/559/414 1028/1445/1057 +f 249/1444/1056 1028/1445/1057 1791/1446/1058 +f 249/1444/1056 1791/1446/1058 1029/1447/1059 +f 249/1444/1056 1029/1447/1059 1789/1435/1049 +f 249/1444/1056 1789/1435/1049 1023/1434/1048 +f 250/1448/1060 1031/1449/1061 1792/1450/1062 +f 250/1448/1060 1792/1450/1062 1030/1451/1063 +f 250/1452/1060 1030/1453/1063 1612/570/422 +f 250/1452/1060 1612/570/422 714/589/435 +f 250/1452/1060 714/589/435 1614/588/434 +f 250/1452/1060 1614/588/434 1026/1441/1055 +f 250/1448/1060 1026/1454/1055 1790/1455/1054 +f 250/1448/1060 1790/1455/1054 1031/1449/1061 +f 251/1456/1064 1028/1445/1057 1611/559/414 +f 251/1456/1064 1611/559/414 704/558/413 +f 251/1457/1064 704/556/413 1609/543/402 +f 251/1457/1064 1609/543/402 1032/1458/1065 +f 251/1459/1064 1032/1460/1065 1793/1461/1066 +f 251/1459/1064 1793/1461/1066 1033/1462/1067 +f 251/1459/1064 1033/1462/1067 1791/1463/1058 +f 251/1459/1064 1791/1463/1058 1028/1464/1057 +f 252/1465/1068 1035/1466/1069 1794/1467/1070 +f 252/1465/1068 1794/1467/1070 1034/1468/1071 +f 252/1469/1068 1034/1470/1071 1610/572/410 +f 252/1469/1068 1610/572/410 708/571/423 +f 252/1469/1068 708/571/423 1612/570/422 +f 252/1469/1068 1612/570/422 1030/1453/1063 +f 252/1465/1068 1030/1451/1063 1792/1450/1062 +f 252/1465/1068 1792/1450/1062 1035/1466/1069 +f 253/1471/1072 1032/1458/1065 1609/543/402 +f 253/1471/1072 1609/543/402 698/542/401 +f 253/1471/1072 698/542/401 1607/529/390 +f 253/1471/1072 1607/529/390 1036/1472/1073 +f 253/1473/1072 1036/1474/1073 1795/1475/1074 +f 253/1473/1072 1795/1475/1074 1037/1476/1075 +f 253/1473/1072 1037/1476/1075 1793/1461/1066 +f 253/1473/1072 1793/1461/1066 1032/1460/1065 +f 254/1477/1076 1039/1478/1077 1796/1479/1078 +f 254/1477/1076 1796/1479/1078 1038/1480/1079 +f 254/1481/1076 1038/1482/1079 1608/539/398 +f 254/1481/1076 1608/539/398 702/554/411 +f 254/1481/1076 702/554/411 1610/553/410 +f 254/1481/1076 1610/553/410 1034/1483/1071 +f 254/1477/1076 1034/1468/1071 1794/1467/1070 +f 254/1477/1076 1794/1467/1070 1039/1478/1077 +f 255/1484/1080 1036/1472/1073 1607/529/390 +f 255/1484/1080 1607/529/390 692/528/389 +f 255/1484/1080 692/528/389 1605/511/378 +f 255/1484/1080 1605/511/378 1040/1485/1081 +f 255/1484/1080 1040/1485/1081 1797/1486/1082 +f 255/1484/1080 1797/1486/1082 1041/1487/1083 +f 255/1488/1080 1041/1489/1083 1795/1475/1074 +f 255/1488/1080 1795/1475/1074 1036/1474/1073 +f 256/1490/1084 1043/1491/1085 1798/1492/1086 +f 256/1490/1084 1798/1492/1086 1042/1493/1087 +f 256/1490/1084 1042/1493/1087 1606/523/386 +f 256/1490/1084 1606/523/386 696/540/399 +f 256/1490/1084 696/540/399 1608/539/398 +f 256/1490/1084 1608/539/398 1038/1482/1079 +f 256/1490/1084 1038/1482/1079 1796/1494/1078 +f 256/1490/1084 1796/1494/1078 1043/1491/1085 +f 257/1495/1088 1040/1485/1081 1605/511/378 +f 257/1495/1088 1605/511/378 686/510/377 +f 257/1495/1088 686/510/377 1603/509/366 +f 257/1495/1088 1603/509/366 1044/1496/1089 +f 257/1497/1088 1044/1498/1089 1799/1499/1090 +f 257/1497/1088 1799/1499/1090 1045/1500/1091 +f 257/1495/1088 1045/1501/1091 1797/1486/1082 +f 257/1495/1088 1797/1486/1082 1040/1485/1081 +f 258/1502/1092 1047/1503/1093 1800/1504/1094 +f 258/1502/1092 1800/1504/1094 1046/1505/1095 +f 258/1502/1092 1046/1505/1095 1604/525/374 +f 258/1502/1092 1604/525/374 690/524/387 +f 258/1502/1092 690/524/387 1606/523/386 +f 258/1502/1092 1606/523/386 1042/1493/1087 +f 258/1502/1092 1042/1493/1087 1798/1492/1086 +f 258/1502/1092 1798/1492/1086 1047/1503/1093 +f 259/1506/1096 1044/1507/1089 1603/497/366 +f 259/1506/1096 1603/497/366 680/496/365 +f 259/1506/1096 680/496/365 1601/485/354 +f 259/1506/1096 1601/485/354 1048/1508/1097 +f 259/1506/1096 1048/1508/1097 1801/1509/1098 +f 259/1506/1096 1801/1509/1098 1049/1510/1099 +f 259/1506/1096 1049/1510/1099 1799/1511/1090 +f 259/1506/1096 1799/1511/1090 1044/1507/1089 +f 260/1512/1100 1051/1513/1101 1802/1514/1102 +f 260/1512/1100 1802/1514/1102 1050/1515/1103 +f 260/1516/1100 1050/1517/1103 1602/1518/362 +f 260/1516/1100 1602/1518/362 684/1519/375 +f 260/1512/1100 684/1520/375 1604/525/374 +f 260/1512/1100 1604/525/374 1046/1505/1095 +f 260/1512/1100 1046/1505/1095 1800/1504/1094 +f 260/1512/1100 1800/1504/1094 1051/1513/1101 +f 261/1521/1104 1048/1508/1097 1601/485/354 +f 261/1521/1104 1601/485/354 674/484/353 +f 261/1521/1104 674/484/353 1599/469/342 +f 261/1521/1104 1599/469/342 1052/1522/1105 +f 261/1521/1104 1052/1522/1105 1803/1523/1106 +f 261/1521/1104 1803/1523/1106 1053/1524/1107 +f 261/1521/1104 1053/1524/1107 1801/1509/1098 +f 261/1521/1104 1801/1509/1098 1048/1508/1097 +f 262/1525/1108 1055/1526/1109 1804/1527/1110 +f 262/1525/1108 1804/1527/1110 1054/1528/1111 +f 262/1525/1108 1054/1528/1111 1600/1529/350 +f 262/1525/1108 1600/1529/350 678/1530/363 +f 262/1525/1108 678/1530/363 1602/1518/362 +f 262/1525/1108 1602/1518/362 1050/1517/1103 +f 262/1525/1108 1050/1517/1103 1802/1531/1102 +f 262/1525/1108 1802/1531/1102 1055/1526/1109 +f 263/1532/1112 1052/1522/1105 1599/469/342 +f 263/1532/1112 1599/469/342 668/468/341 +f 263/1532/1112 668/468/341 1597/450/326 +f 263/1532/1112 1597/450/326 1056/1533/1113 +f 263/1532/1112 1056/1533/1113 1805/1534/1114 +f 263/1532/1112 1805/1534/1114 1057/1535/1115 +f 263/1532/1112 1057/1535/1115 1803/1523/1106 +f 263/1532/1112 1803/1523/1106 1052/1522/1105 +f 264/1536/1116 1059/1537/1117 1806/1538/1118 +f 264/1536/1116 1806/1538/1118 1058/1539/1119 +f 264/1536/1116 1058/1539/1119 1598/1540/335 +f 264/1536/1116 1598/1540/335 672/1541/351 +f 264/1536/1116 672/1541/351 1600/1529/350 +f 264/1536/1116 1600/1529/350 1054/1528/1111 +f 264/1536/1116 1054/1528/1111 1804/1527/1110 +f 264/1536/1116 1804/1527/1110 1059/1537/1117 +f 265/1542/1120 1063/1543/1121 1813/1544/1122 +f 265/1542/1120 1813/1544/1122 1060/1545/1123 +f 265/1546/1120 1060/1547/1123 1814/1548/1124 +f 265/1546/1120 1814/1548/1124 1061/1549/1125 +f 265/1546/1120 1061/1549/1125 1833/1550/1126 +f 265/1546/1120 1833/1550/1126 1062/1551/1127 +f 265/1542/1120 1062/1552/1127 1843/1553/1128 +f 265/1542/1120 1843/1553/1128 1063/1543/1121 +f 266/1554/1129 1066/1555/1130 1834/1556/1131 +f 266/1554/1129 1834/1556/1131 1064/1557/1132 +f 266/1554/1129 1064/1557/1132 1814/1548/1124 +f 266/1554/1129 1814/1548/1124 1060/1547/1123 +f 266/1554/1129 1060/1547/1123 1813/1558/1122 +f 266/1554/1129 1813/1558/1122 1065/1559/1133 +f 266/1554/1129 1065/1559/1133 1844/1560/1134 +f 266/1554/1129 1844/1560/1134 1066/1555/1130 +f 267/1561/1135 1069/1562/1136 1812/1563/1137 +f 267/1561/1135 1812/1563/1137 1067/1564/1138 +f 267/1561/1135 1067/1564/1138 1813/1544/1122 +f 267/1561/1135 1813/1544/1122 1063/1543/1121 +f 267/1561/1135 1063/1543/1121 1843/1553/1128 +f 267/1561/1135 1843/1553/1128 1068/1565/1139 +f 267/1561/1135 1068/1565/1139 1841/1566/1140 +f 267/1561/1135 1841/1566/1140 1069/1562/1136 +f 268/1567/1141 1071/1568/1142 1844/1569/1134 +f 268/1567/1141 1844/1569/1134 1065/1570/1133 +f 268/1567/1141 1065/1570/1133 1813/1544/1122 +f 268/1567/1141 1813/1544/1122 1067/1564/1138 +f 268/1567/1141 1067/1564/1138 1812/1563/1137 +f 268/1567/1141 1812/1563/1137 1070/1571/1143 +f 268/1567/1141 1070/1571/1143 1842/1572/1144 +f 268/1567/1141 1842/1572/1144 1071/1568/1142 +f 269/1573/1145 1074/1574/1146 1811/1575/1147 +f 269/1573/1145 1811/1575/1147 1072/1576/1148 +f 269/1577/1145 1072/1578/1148 1812/1563/1137 +f 269/1577/1145 1812/1563/1137 1069/1562/1136 +f 269/1577/1145 1069/1562/1136 1841/1566/1140 +f 269/1577/1145 1841/1566/1140 1073/1579/1149 +f 269/1573/1145 1073/1580/1149 1845/1581/1150 +f 269/1573/1145 1845/1581/1150 1074/1574/1146 +f 270/1582/1151 1076/1583/1152 1842/1572/1144 +f 270/1582/1151 1842/1572/1144 1070/1571/1143 +f 270/1584/1151 1070/1585/1143 1812/1586/1137 +f 270/1584/1151 1812/1586/1137 1072/1576/1148 +f 270/1584/1151 1072/1576/1148 1811/1575/1147 +f 270/1584/1151 1811/1575/1147 1075/1587/1153 +f 270/1584/1151 1075/1587/1153 1846/1588/1154 +f 270/1584/1151 1846/1588/1154 1076/1589/1152 +f 271/1590/1155 1078/1591/1156 1594/366/261 +f 271/1590/1155 1594/366/261 632/365/260 +f 271/1590/1155 632/365/260 1596/375/267 +f 271/1590/1155 1596/375/267 1077/1592/1157 +f 271/1590/1155 1077/1592/1157 1811/1575/1147 +f 271/1590/1155 1811/1575/1147 1074/1574/1146 +f 271/1590/1155 1074/1574/1146 1845/1581/1150 +f 271/1590/1155 1845/1581/1150 1078/1591/1156 +f 272/1593/1158 1075/1587/1153 1811/1575/1147 +f 272/1593/1158 1811/1575/1147 1077/1592/1157 +f 272/1593/1158 1077/1592/1157 1596/375/267 +f 272/1593/1158 1596/375/267 635/383/273 +f 272/1593/1158 635/383/273 1595/382/272 +f 272/1593/1158 1595/382/272 1079/1594/1159 +f 272/1593/1158 1079/1594/1159 1846/1588/1154 +f 272/1593/1158 1846/1588/1154 1075/1587/1153 +f 273/1595/1160 1081/1596/1161 1592/386/276 +f 273/1595/1160 1592/386/276 638/385/275 +f 273/1597/1160 638/393/275 1594/366/261 +f 273/1597/1160 1594/366/261 1078/1591/1156 +f 273/1597/1160 1078/1591/1156 1845/1581/1150 +f 273/1597/1160 1845/1581/1150 1080/1598/1162 +f 273/1597/1160 1080/1598/1162 1839/1599/1163 +f 273/1597/1160 1839/1599/1163 1081/1600/1161 +f 274/1601/1164 1083/1602/1165 1846/1588/1154 +f 274/1601/1164 1846/1588/1154 1079/1594/1159 +f 274/1601/1164 1079/1594/1159 1595/382/272 +f 274/1601/1164 1595/382/272 641/401/285 +f 274/1601/1164 641/401/285 1593/400/284 +f 274/1601/1164 1593/400/284 1082/1603/1166 +f 274/1601/1164 1082/1603/1166 1840/1604/1167 +f 274/1601/1164 1840/1604/1167 1083/1602/1165 +f 275/1605/1168 1085/1606/1169 1590/404/288 +f 275/1605/1168 1590/404/288 644/403/287 +f 275/1605/1168 644/403/287 1592/386/276 +f 275/1605/1168 1592/386/276 1081/1596/1161 +f 275/1605/1168 1081/1596/1161 1839/1607/1163 +f 275/1605/1168 1839/1607/1163 1084/1608/1170 +f 275/1609/1168 1084/1610/1170 1835/1611/1171 +f 275/1609/1168 1835/1611/1171 1085/1612/1169 +f 276/1613/1172 1087/1614/1173 1840/1604/1167 +f 276/1613/1172 1840/1604/1167 1082/1603/1166 +f 276/1613/1172 1082/1603/1166 1593/400/284 +f 276/1613/1172 1593/400/284 647/1615/297 +f 276/1616/1172 647/1617/297 1591/430/296 +f 276/1616/1172 1591/430/296 1086/1618/1174 +f 276/1616/1172 1086/1618/1174 1836/1619/1175 +f 276/1616/1172 1836/1619/1175 1087/1620/1173 +f 277/1621/1176 1089/1622/1177 1588/418/300 +f 277/1621/1176 1588/418/300 650/417/299 +f 277/1621/1176 650/417/299 1590/404/288 +f 277/1621/1176 1590/404/288 1085/1606/1169 +f 277/1623/1176 1085/1612/1169 1835/1611/1171 +f 277/1623/1176 1835/1611/1171 1088/1624/1178 +f 277/1623/1176 1088/1624/1178 1837/1625/1179 +f 277/1623/1176 1837/1625/1179 1089/1626/1177 +f 278/1627/1180 1091/1628/1181 1836/1619/1175 +f 278/1627/1180 1836/1619/1175 1086/1618/1174 +f 278/1627/1180 1086/1618/1174 1591/430/296 +f 278/1627/1180 1591/430/296 653/429/309 +f 278/1627/1180 653/429/309 1589/428/308 +f 278/1627/1180 1589/428/308 1090/1629/1182 +f 278/1627/1180 1090/1629/1182 1838/1630/1183 +f 278/1627/1180 1838/1630/1183 1091/1628/1181 +f 279/1631/1184 1088/1624/1178 1835/1611/1171 +f 279/1631/1184 1835/1611/1171 1092/1632/1185 +f 279/1631/1184 1092/1632/1185 1841/1566/1140 +f 279/1631/1184 1841/1566/1140 1068/1565/1139 +f 279/1631/1184 1068/1565/1139 1843/1553/1128 +f 279/1631/1184 1843/1553/1128 1093/1633/1186 +f 279/1631/1184 1093/1633/1186 1837/1625/1179 +f 279/1631/1184 1837/1625/1179 1088/1624/1178 +f 280/1634/1187 1095/1635/1188 1844/1569/1134 +f 280/1634/1187 1844/1569/1134 1071/1568/1142 +f 280/1634/1187 1071/1568/1142 1842/1572/1144 +f 280/1634/1187 1842/1572/1144 1094/1636/1189 +f 280/1634/1187 1094/1636/1189 1836/1637/1175 +f 280/1634/1187 1836/1637/1175 1091/1638/1181 +f 280/1634/1187 1091/1638/1181 1838/1639/1183 +f 280/1634/1187 1838/1639/1183 1095/1635/1188 +f 281/1640/1190 1092/1632/1185 1835/1611/1171 +f 281/1640/1190 1835/1611/1171 1084/1610/1170 +f 281/1640/1190 1084/1610/1170 1839/1641/1163 +f 281/1640/1190 1839/1641/1163 1080/1642/1162 +f 281/1643/1190 1080/1598/1162 1845/1581/1150 +f 281/1643/1190 1845/1581/1150 1073/1580/1149 +f 281/1640/1190 1073/1579/1149 1841/1566/1140 +f 281/1640/1190 1841/1566/1140 1092/1632/1185 +f 282/1644/1191 1076/1589/1152 1846/1588/1154 +f 282/1644/1191 1846/1588/1154 1083/1602/1165 +f 282/1644/1191 1083/1602/1165 1840/1604/1167 +f 282/1644/1191 1840/1604/1167 1087/1614/1173 +f 282/1645/1191 1087/1646/1173 1836/1637/1175 +f 282/1645/1191 1836/1637/1175 1094/1636/1189 +f 282/1645/1191 1094/1636/1189 1842/1572/1144 +f 282/1645/1191 1842/1572/1144 1076/1583/1152 +f 283/1647/1192 1097/1648/1193 1831/1649/1194 +f 283/1647/1192 1831/1649/1194 1096/1650/1195 +f 283/1651/1192 1096/1652/1195 1837/1625/1179 +f 283/1651/1192 1837/1625/1179 1093/1633/1186 +f 283/1651/1192 1093/1633/1186 1843/1553/1128 +f 283/1651/1192 1843/1553/1128 1062/1552/1127 +f 283/1647/1192 1062/1653/1127 1833/1654/1126 +f 283/1647/1192 1833/1654/1126 1097/1648/1193 +f 284/1655/1196 1066/1555/1130 1844/1560/1134 +f 284/1655/1196 1844/1560/1134 1095/1656/1188 +f 284/1655/1196 1095/1656/1188 1838/1657/1183 +f 284/1655/1196 1838/1657/1183 1098/1658/1197 +f 284/1655/1196 1098/1658/1197 1832/1659/1198 +f 284/1655/1196 1832/1659/1198 1099/1660/1199 +f 284/1655/1196 1099/1660/1199 1834/1556/1131 +f 284/1655/1196 1834/1556/1131 1066/1555/1130 +f 285/1661/1200 1100/1662/1201 1586/434/312 +f 285/1661/1200 1586/434/312 656/433/311 +f 285/1661/1200 656/433/311 1588/418/300 +f 285/1661/1200 1588/418/300 1089/1622/1177 +f 285/1663/1200 1089/1626/1177 1837/1625/1179 +f 285/1663/1200 1837/1625/1179 1096/1652/1195 +f 285/1661/1200 1096/1650/1195 1831/1649/1194 +f 285/1661/1200 1831/1649/1194 1100/1662/1201 +f 286/1664/1202 1098/1665/1197 1838/1630/1183 +f 286/1664/1202 1838/1630/1183 1090/1629/1182 +f 286/1664/1202 1090/1629/1182 1589/428/308 +f 286/1664/1202 1589/428/308 659/445/321 +f 286/1664/1202 659/445/321 1587/444/320 +f 286/1664/1202 1587/444/320 1101/1666/1203 +f 286/1664/1202 1101/1666/1203 1832/1667/1198 +f 286/1664/1202 1832/1667/1198 1098/1665/1197 +f 287/1668/1204 878/1025/756 1714/1024/749 +f 287/1668/1204 1714/1024/749 1102/1669/1205 +f 287/1668/1204 1102/1669/1205 1847/1670/1206 +f 287/1668/1204 1847/1670/1206 1103/1671/1207 +f 287/1668/1204 1103/1671/1207 1849/1672/1208 +f 287/1668/1204 1849/1672/1208 1104/1673/1209 +f 287/1668/1204 1104/1673/1209 1720/1026/757 +f 287/1668/1204 1720/1026/757 878/1025/756 +f 288/1674/1210 1107/1675/1211 1850/1676/1212 +f 288/1674/1210 1850/1676/1212 1105/1677/1213 +f 288/1674/1210 1105/1677/1213 1848/1678/1214 +f 288/1674/1210 1848/1678/1214 1106/1679/1215 +f 288/1674/1210 1106/1679/1215 1715/1019/753 +f 288/1674/1210 1715/1019/753 881/1033/762 +f 288/1674/1210 881/1033/762 1721/1032/761 +f 288/1674/1210 1721/1032/761 1107/1675/1211 +f 289/1680/1216 874/1008/748 1586/434/312 +f 289/1680/1216 1586/434/312 1100/1662/1201 +f 289/1680/1216 1100/1662/1201 1831/1649/1194 +f 289/1680/1216 1831/1649/1194 1108/1681/1217 +f 289/1680/1216 1108/1681/1217 1847/1670/1206 +f 289/1680/1216 1847/1670/1206 1102/1669/1205 +f 289/1680/1216 1102/1669/1205 1714/1024/749 +f 289/1680/1216 1714/1024/749 874/1008/748 +f 290/1682/1218 1106/1679/1215 1848/1678/1214 +f 290/1682/1218 1848/1678/1214 1109/1683/1219 +f 290/1682/1218 1109/1683/1219 1832/1667/1198 +f 290/1682/1218 1832/1667/1198 1101/1666/1203 +f 290/1682/1218 1101/1666/1203 1587/444/320 +f 290/1682/1218 1587/444/320 877/1020/754 +f 290/1682/1218 877/1020/754 1715/1019/753 +f 290/1682/1218 1715/1019/753 1106/1679/1215 +f 291/1684/1220 1111/1685/1221 1584/448/324 +f 291/1684/1220 1584/448/324 882/1035/764 +f 291/1684/1220 882/1035/764 1720/1026/757 +f 291/1684/1220 1720/1026/757 1104/1673/1209 +f 291/1684/1220 1104/1673/1209 1849/1672/1208 +f 291/1684/1220 1849/1672/1208 1110/1686/1222 +f 291/1684/1220 1110/1686/1222 1851/1687/1223 +f 291/1684/1220 1851/1687/1223 1111/1685/1221 +f 292/1688/1224 1113/1689/1225 1850/1676/1212 +f 292/1688/1224 1850/1676/1212 1107/1675/1211 +f 292/1688/1224 1107/1675/1211 1721/1032/761 +f 292/1688/1224 1721/1032/761 883/1041/766 +f 292/1690/1224 883/1691/766 1585/463/337 +f 292/1690/1224 1585/463/337 1112/1692/1226 +f 292/1690/1224 1112/1692/1226 1852/1693/1227 +f 292/1690/1224 1852/1693/1227 1113/1694/1225 +f 293/1695/1228 660/449/325 1584/448/324 +f 293/1695/1228 1584/448/324 1111/1685/1221 +f 293/1695/1228 1111/1685/1221 1851/1687/1223 +f 293/1695/1228 1851/1687/1223 1114/1696/1229 +f 293/1697/1228 1114/1698/1229 1805/1699/1114 +f 293/1697/1228 1805/1699/1114 1056/1700/1113 +f 293/1697/1228 1056/1700/1113 1597/1701/326 +f 293/1697/1228 1597/1701/326 660/1702/325 +f 294/1703/1230 1058/1539/1119 1806/1538/1118 +f 294/1703/1230 1806/1538/1118 1115/1704/1231 +f 294/1703/1230 1115/1704/1231 1852/1693/1227 +f 294/1703/1230 1852/1693/1227 1112/1692/1226 +f 294/1703/1230 1112/1692/1226 1585/463/337 +f 294/1703/1230 1585/463/337 665/462/336 +f 294/1703/1230 665/462/336 1598/1540/335 +f 294/1703/1230 1598/1540/335 1058/1539/1119 +f 295/1705/1232 1119/1706/1233 1582/1707/1234 +f 295/1705/1232 1582/1707/1234 1116/1708/1235 +f 295/1705/1232 1116/1708/1235 1829/1709/1236 +f 295/1705/1232 1829/1709/1236 1117/1710/1237 +f 295/1711/1232 1117/1712/1237 1857/1713/1238 +f 295/1711/1232 1857/1713/1238 1118/1714/1239 +f 295/1711/1232 1118/1714/1239 1809/1715/1240 +f 295/1711/1232 1809/1715/1240 1119/1716/1233 +f 296/1717/1241 1122/1718/1242 1858/1719/1243 +f 296/1717/1241 1858/1719/1243 1120/1720/1244 +f 296/1717/1241 1120/1720/1244 1830/1721/1245 +f 296/1717/1241 1830/1721/1245 1121/1722/1246 +f 296/1717/1241 1121/1722/1246 1582/1723/1234 +f 296/1717/1241 1582/1723/1234 1119/1716/1233 +f 296/1717/1241 1119/1716/1233 1809/1715/1240 +f 296/1717/1241 1809/1715/1240 1122/1718/1242 +f 297/1724/1247 1125/1725/1248 1809/1715/1240 +f 297/1724/1247 1809/1715/1240 1118/1714/1239 +f 297/1724/1247 1118/1714/1239 1857/1713/1238 +f 297/1724/1247 1857/1713/1238 1123/1726/1249 +f 297/1724/1247 1123/1726/1249 1855/1727/1250 +f 297/1724/1247 1855/1727/1250 1124/1728/1251 +f 297/1724/1247 1124/1728/1251 1583/1729/1252 +f 297/1724/1247 1583/1729/1252 1125/1725/1248 +f 298/1730/1253 1127/1731/1254 1856/1732/1255 +f 298/1730/1253 1856/1732/1255 1126/1733/1256 +f 298/1730/1253 1126/1733/1256 1858/1719/1243 +f 298/1730/1253 1858/1719/1243 1122/1718/1242 +f 298/1730/1253 1122/1718/1242 1809/1715/1240 +f 298/1730/1253 1809/1715/1240 1125/1725/1248 +f 298/1730/1253 1125/1725/1248 1583/1729/1252 +f 298/1730/1253 1583/1729/1252 1127/1731/1254 +f 299/1734/1257 1130/1735/1258 1583/1729/1252 +f 299/1734/1257 1583/1729/1252 1124/1728/1251 +f 299/1736/1257 1124/1737/1251 1855/1738/1250 +f 299/1736/1257 1855/1738/1250 1128/1739/1259 +f 299/1736/1257 1128/1739/1259 1853/1740/1260 +f 299/1736/1257 1853/1740/1260 1129/1741/1261 +f 299/1734/1257 1129/1742/1261 1810/1743/1262 +f 299/1734/1257 1810/1743/1262 1130/1735/1258 +f 300/1744/1263 1132/1745/1264 1854/1746/1265 +f 300/1744/1263 1854/1746/1265 1131/1747/1266 +f 300/1744/1263 1131/1747/1266 1856/1732/1255 +f 300/1744/1263 1856/1732/1255 1127/1731/1254 +f 300/1744/1263 1127/1731/1254 1583/1729/1252 +f 300/1744/1263 1583/1729/1252 1130/1735/1258 +f 300/1744/1263 1130/1735/1258 1810/1743/1262 +f 300/1744/1263 1810/1743/1262 1132/1745/1264 +f 301/1748/1267 1134/1749/1268 1810/1743/1262 +f 301/1748/1267 1810/1743/1262 1129/1742/1261 +f 301/1750/1267 1129/1741/1261 1853/1740/1260 +f 301/1750/1267 1853/1740/1260 1133/1751/1269 +f 301/1750/1267 1133/1751/1269 1833/1654/1126 +f 301/1750/1267 1833/1654/1126 1061/1752/1125 +f 301/1748/1267 1061/1549/1125 1814/1548/1124 +f 301/1748/1267 1814/1548/1124 1134/1749/1268 +f 302/1753/1270 1064/1557/1132 1834/1556/1131 +f 302/1753/1270 1834/1556/1131 1135/1754/1271 +f 302/1753/1270 1135/1754/1271 1854/1746/1265 +f 302/1753/1270 1854/1746/1265 1132/1745/1264 +f 302/1753/1270 1132/1745/1264 1810/1743/1262 +f 302/1753/1270 1810/1743/1262 1134/1749/1268 +f 302/1753/1270 1134/1749/1268 1814/1548/1124 +f 302/1753/1270 1814/1548/1124 1064/1557/1132 +f 303/1755/1272 1108/1681/1217 1831/1649/1194 +f 303/1755/1272 1831/1649/1194 1097/1648/1193 +f 303/1755/1272 1097/1648/1193 1833/1654/1126 +f 303/1755/1272 1833/1654/1126 1133/1751/1269 +f 303/1755/1272 1133/1751/1269 1853/1740/1260 +f 303/1755/1272 1853/1740/1260 1136/1756/1273 +f 303/1755/1272 1136/1756/1273 1847/1670/1206 +f 303/1755/1272 1847/1670/1206 1108/1681/1217 +f 304/1757/1274 1137/1758/1275 1854/1746/1265 +f 304/1757/1274 1854/1746/1265 1135/1754/1271 +f 304/1757/1274 1135/1754/1271 1834/1556/1131 +f 304/1757/1274 1834/1556/1131 1099/1660/1199 +f 304/1759/1274 1099/1760/1199 1832/1667/1198 +f 304/1759/1274 1832/1667/1198 1109/1683/1219 +f 304/1757/1274 1109/1761/1219 1848/1762/1214 +f 304/1757/1274 1848/1762/1214 1137/1758/1275 +f 305/1763/1276 1140/1764/1277 1801/1509/1098 +f 305/1763/1276 1801/1509/1098 1053/1524/1107 +f 305/1763/1276 1053/1524/1107 1803/1523/1106 +f 305/1763/1276 1803/1523/1106 1138/1765/1278 +f 305/1763/1276 1138/1765/1278 1823/1766/1279 +f 305/1763/1276 1823/1766/1279 1139/1767/1280 +f 305/1763/1276 1139/1767/1280 1815/1768/1281 +f 305/1763/1276 1815/1768/1281 1140/1764/1277 +f 306/1769/1282 1143/1770/1283 1824/1771/1284 +f 306/1769/1282 1824/1771/1284 1141/1772/1285 +f 306/1769/1282 1141/1772/1285 1804/1527/1110 +f 306/1769/1282 1804/1527/1110 1055/1526/1109 +f 306/1769/1282 1055/1526/1109 1802/1531/1102 +f 306/1769/1282 1802/1531/1102 1142/1773/1286 +f 306/1769/1282 1142/1773/1286 1816/1774/1287 +f 306/1769/1282 1816/1774/1287 1143/1770/1283 +f 307/1775/1288 1146/1776/1289 1581/1777/1290 +f 307/1775/1288 1581/1777/1290 1144/1778/1291 +f 307/1775/1288 1144/1778/1291 1821/1779/1292 +f 307/1775/1288 1821/1779/1292 1145/1780/1293 +f 307/1775/1288 1145/1780/1293 1829/1709/1236 +f 307/1775/1288 1829/1709/1236 1116/1708/1235 +f 307/1775/1288 1116/1708/1235 1582/1707/1234 +f 307/1775/1288 1582/1707/1234 1146/1776/1289 +f 308/1781/1294 1121/1722/1246 1830/1721/1245 +f 308/1781/1294 1830/1721/1245 1147/1782/1295 +f 308/1783/1294 1147/1784/1295 1822/1785/1296 +f 308/1783/1294 1822/1785/1296 1148/1786/1297 +f 308/1783/1294 1148/1786/1297 1581/1777/1290 +f 308/1783/1294 1581/1777/1290 1146/1776/1289 +f 308/1783/1294 1146/1776/1289 1582/1707/1234 +f 308/1783/1294 1582/1707/1234 1121/1787/1246 +f 309/1788/1298 1152/1789/1299 1807/1790/1300 +f 309/1788/1298 1807/1790/1300 1149/1791/1301 +f 309/1788/1298 1149/1791/1301 1863/1792/1302 +f 309/1788/1298 1863/1792/1302 1150/1793/1303 +f 309/1788/1298 1150/1793/1303 1861/1794/1304 +f 309/1788/1298 1861/1794/1304 1151/1795/1305 +f 309/1788/1298 1151/1795/1305 1808/1796/1306 +f 309/1788/1298 1808/1796/1306 1152/1789/1299 +f 310/1797/1307 1155/1798/1308 1862/1799/1309 +f 310/1797/1307 1862/1799/1309 1153/1800/1310 +f 310/1801/1307 1153/1802/1310 1864/1803/1311 +f 310/1801/1307 1864/1803/1311 1154/1804/1312 +f 310/1801/1307 1154/1804/1312 1807/1790/1300 +f 310/1801/1307 1807/1790/1300 1152/1789/1299 +f 310/1801/1307 1152/1789/1299 1808/1796/1306 +f 310/1801/1307 1808/1796/1306 1155/1805/1308 +f 311/1806/1313 1158/1807/1314 1808/1796/1306 +f 311/1806/1313 1808/1796/1306 1151/1795/1305 +f 311/1806/1313 1151/1795/1305 1861/1794/1304 +f 311/1806/1313 1861/1794/1304 1156/1808/1315 +f 311/1809/1313 1156/1810/1315 1859/1811/1316 +f 311/1809/1313 1859/1811/1316 1157/1812/1317 +f 311/1809/1313 1157/1812/1317 1580/1813/1318 +f 311/1809/1313 1580/1813/1318 1158/1814/1314 +f 312/1815/1319 1160/1816/1320 1860/1817/1321 +f 312/1815/1319 1860/1817/1321 1159/1818/1322 +f 312/1815/1319 1159/1818/1322 1862/1799/1309 +f 312/1815/1319 1862/1799/1309 1155/1798/1308 +f 312/1819/1319 1155/1805/1308 1808/1796/1306 +f 312/1819/1319 1808/1796/1306 1158/1807/1314 +f 312/1820/1319 1158/1814/1314 1580/1813/1318 +f 312/1820/1319 1580/1813/1318 1160/1821/1320 +f 313/1822/1323 1162/1823/1324 1580/1813/1318 +f 313/1822/1323 1580/1813/1318 1157/1812/1317 +f 313/1822/1323 1157/1812/1317 1859/1811/1316 +f 313/1822/1323 1859/1811/1316 1161/1824/1325 +f 313/1822/1323 1161/1824/1325 1821/1779/1292 +f 313/1822/1323 1821/1779/1292 1144/1778/1291 +f 313/1822/1323 1144/1778/1291 1581/1777/1290 +f 313/1822/1323 1581/1777/1290 1162/1823/1324 +f 314/1825/1326 1148/1786/1297 1822/1785/1296 +f 314/1825/1326 1822/1785/1296 1163/1826/1327 +f 314/1825/1326 1163/1826/1327 1860/1827/1321 +f 314/1825/1326 1860/1827/1321 1160/1821/1320 +f 314/1825/1326 1160/1821/1320 1580/1813/1318 +f 314/1825/1326 1580/1813/1318 1162/1823/1324 +f 314/1825/1326 1162/1823/1324 1581/1777/1290 +f 314/1825/1326 1581/1777/1290 1148/1786/1297 +f 315/1828/1328 1166/1829/1329 1797/1486/1082 +f 315/1828/1328 1797/1486/1082 1045/1501/1091 +f 315/1828/1328 1045/1501/1091 1799/1830/1090 +f 315/1828/1328 1799/1830/1090 1164/1831/1330 +f 315/1828/1328 1164/1831/1330 1867/1832/1331 +f 315/1828/1328 1867/1832/1331 1165/1833/1332 +f 315/1828/1328 1165/1833/1332 1869/1834/1333 +f 315/1828/1328 1869/1834/1333 1166/1829/1329 +f 316/1835/1334 1169/1836/1335 1868/1837/1336 +f 316/1835/1334 1868/1837/1336 1167/1838/1337 +f 316/1835/1334 1167/1838/1337 1800/1504/1094 +f 316/1835/1334 1800/1504/1094 1047/1503/1093 +f 316/1835/1334 1047/1503/1093 1798/1492/1086 +f 316/1835/1334 1798/1492/1086 1168/1839/1338 +f 316/1835/1334 1168/1839/1338 1870/1840/1339 +f 316/1835/1334 1870/1840/1339 1169/1836/1335 +f 317/1841/1340 1172/1842/1341 1869/1834/1333 +f 317/1841/1340 1869/1834/1333 1165/1833/1332 +f 317/1841/1340 1165/1833/1332 1867/1832/1331 +f 317/1841/1340 1867/1832/1331 1170/1843/1342 +f 317/1841/1340 1170/1843/1342 1873/1844/1343 +f 317/1841/1340 1873/1844/1343 1171/1845/1344 +f 317/1841/1340 1171/1845/1344 1871/1846/1345 +f 317/1841/1340 1871/1846/1345 1172/1842/1341 +f 318/1847/1346 1175/1848/1347 1874/1849/1348 +f 318/1847/1346 1874/1849/1348 1173/1850/1349 +f 318/1847/1346 1173/1850/1349 1868/1837/1336 +f 318/1847/1346 1868/1837/1336 1169/1836/1335 +f 318/1847/1346 1169/1836/1335 1870/1840/1339 +f 318/1847/1346 1870/1840/1339 1174/1851/1350 +f 318/1847/1346 1174/1851/1350 1872/1852/1351 +f 318/1847/1346 1872/1852/1351 1175/1848/1347 +f 319/1853/1352 1178/1854/1353 1871/1846/1345 +f 319/1853/1352 1871/1846/1345 1171/1845/1344 +f 319/1853/1352 1171/1845/1344 1873/1844/1343 +f 319/1853/1352 1873/1844/1343 1176/1855/1354 +f 319/1853/1352 1176/1855/1354 1875/1856/1355 +f 319/1853/1352 1875/1856/1355 1177/1857/1356 +f 319/1853/1352 1177/1857/1356 1877/1858/1357 +f 319/1853/1352 1877/1858/1357 1178/1854/1353 +f 320/1859/1358 1181/1860/1359 1876/1861/1360 +f 320/1859/1358 1876/1861/1360 1179/1862/1361 +f 320/1859/1358 1179/1862/1361 1874/1849/1348 +f 320/1859/1358 1874/1849/1348 1175/1848/1347 +f 320/1859/1358 1175/1848/1347 1872/1852/1351 +f 320/1859/1358 1872/1852/1351 1180/1863/1362 +f 320/1859/1358 1180/1863/1362 1878/1864/1363 +f 320/1859/1358 1878/1864/1363 1181/1860/1359 +f 321/1865/1364 1184/1866/1365 1877/1858/1357 +f 321/1865/1364 1877/1858/1357 1177/1857/1356 +f 321/1865/1364 1177/1857/1356 1875/1856/1355 +f 321/1865/1364 1875/1856/1355 1182/1867/1366 +f 321/1868/1364 1182/1869/1366 1881/1870/1367 +f 321/1868/1364 1881/1870/1367 1183/1871/1368 +f 321/1868/1364 1183/1871/1368 1879/1872/1369 +f 321/1868/1364 1879/1872/1369 1184/1873/1365 +f 322/1874/1370 1187/1875/1371 1882/1876/1372 +f 322/1874/1370 1882/1876/1372 1185/1877/1373 +f 322/1874/1370 1185/1877/1373 1876/1861/1360 +f 322/1874/1370 1876/1861/1360 1181/1860/1359 +f 322/1874/1370 1181/1860/1359 1878/1864/1363 +f 322/1874/1370 1878/1864/1363 1186/1878/1374 +f 322/1874/1370 1186/1878/1374 1880/1879/1375 +f 322/1874/1370 1880/1879/1375 1187/1875/1371 +f 323/1880/1376 1190/1881/1377 1819/1882/1378 +f 323/1880/1376 1819/1882/1378 1188/1883/1379 +f 323/1880/1376 1188/1883/1379 1883/1884/1380 +f 323/1880/1376 1883/1884/1380 1189/1885/1381 +f 323/1880/1376 1189/1885/1381 1879/1872/1369 +f 323/1880/1376 1879/1872/1369 1183/1871/1368 +f 323/1880/1376 1183/1871/1368 1881/1870/1367 +f 323/1880/1376 1881/1870/1367 1190/1881/1377 +f 324/1886/1382 1187/1875/1371 1880/1879/1375 +f 324/1886/1382 1880/1879/1375 1191/1887/1383 +f 324/1888/1382 1191/1889/1383 1884/1890/1384 +f 324/1888/1382 1884/1890/1384 1192/1891/1385 +f 324/1886/1382 1192/1892/1385 1820/1893/1386 +f 324/1886/1382 1820/1893/1386 1193/1894/1387 +f 324/1886/1382 1193/1894/1387 1882/1876/1372 +f 324/1886/1382 1882/1876/1372 1187/1875/1371 +f 325/1895/1388 1195/1896/1389 1821/1779/1292 +f 325/1895/1388 1821/1779/1292 1161/1824/1325 +f 325/1895/1388 1161/1824/1325 1859/1811/1316 +f 325/1895/1388 1859/1811/1316 1194/1897/1390 +f 325/1895/1388 1194/1897/1390 1879/1872/1369 +f 325/1895/1388 1879/1872/1369 1189/1885/1381 +f 325/1895/1388 1189/1885/1381 1883/1884/1380 +f 325/1895/1388 1883/1884/1380 1195/1896/1389 +f 326/1898/1391 1191/1889/1383 1880/1899/1375 +f 326/1898/1391 1880/1899/1375 1196/1900/1392 +f 326/1898/1391 1196/1900/1392 1860/1827/1321 +f 326/1898/1391 1860/1827/1321 1163/1826/1327 +f 326/1898/1391 1163/1826/1327 1822/1785/1296 +f 326/1898/1391 1822/1785/1296 1197/1901/1393 +f 326/1898/1391 1197/1901/1393 1884/1890/1384 +f 326/1898/1391 1884/1890/1384 1191/1889/1383 +f 327/1902/1394 1194/1897/1390 1859/1811/1316 +f 327/1902/1394 1859/1811/1316 1156/1810/1315 +f 327/1903/1394 1156/1808/1315 1861/1794/1304 +f 327/1903/1394 1861/1794/1304 1198/1904/1395 +f 327/1903/1394 1198/1904/1395 1877/1858/1357 +f 327/1903/1394 1877/1858/1357 1184/1866/1365 +f 327/1902/1394 1184/1873/1365 1879/1872/1369 +f 327/1902/1394 1879/1872/1369 1194/1897/1390 +f 328/1905/1396 1186/1878/1374 1878/1864/1363 +f 328/1905/1396 1878/1864/1363 1199/1906/1397 +f 328/1905/1396 1199/1906/1397 1862/1799/1309 +f 328/1905/1396 1862/1799/1309 1159/1818/1322 +f 328/1905/1396 1159/1818/1322 1860/1817/1321 +f 328/1905/1396 1860/1817/1321 1196/1907/1392 +f 328/1905/1396 1196/1907/1392 1880/1879/1375 +f 328/1905/1396 1880/1879/1375 1186/1878/1374 +f 329/1908/1398 1198/1904/1395 1861/1794/1304 +f 329/1908/1398 1861/1794/1304 1150/1793/1303 +f 329/1908/1398 1150/1793/1303 1863/1792/1302 +f 329/1908/1398 1863/1792/1302 1200/1909/1399 +f 329/1908/1398 1200/1909/1399 1871/1846/1345 +f 329/1908/1398 1871/1846/1345 1178/1854/1353 +f 329/1908/1398 1178/1854/1353 1877/1858/1357 +f 329/1908/1398 1877/1858/1357 1198/1904/1395 +f 330/1910/1400 1180/1863/1362 1872/1852/1351 +f 330/1910/1400 1872/1852/1351 1201/1911/1401 +f 330/1910/1400 1201/1911/1401 1864/1912/1311 +f 330/1910/1400 1864/1912/1311 1153/1800/1310 +f 330/1910/1400 1153/1800/1310 1862/1799/1309 +f 330/1910/1400 1862/1799/1309 1199/1906/1397 +f 330/1910/1400 1199/1906/1397 1878/1864/1363 +f 330/1910/1400 1878/1864/1363 1180/1863/1362 +f 331/1913/1402 1200/1909/1399 1863/1792/1302 +f 331/1913/1402 1863/1792/1302 1202/1914/1403 +f 331/1913/1402 1202/1914/1403 1865/1915/1404 +f 331/1913/1402 1865/1915/1404 1203/1916/1405 +f 331/1913/1402 1203/1916/1405 1869/1834/1333 +f 331/1913/1402 1869/1834/1333 1172/1842/1341 +f 331/1913/1402 1172/1842/1341 1871/1846/1345 +f 331/1913/1402 1871/1846/1345 1200/1909/1399 +f 332/1917/1406 1174/1851/1350 1870/1840/1339 +f 332/1917/1406 1870/1840/1339 1204/1918/1407 +f 332/1917/1406 1204/1918/1407 1866/1919/1408 +f 332/1917/1406 1866/1919/1408 1205/1920/1409 +f 332/1917/1406 1205/1920/1409 1864/1912/1311 +f 332/1917/1406 1864/1912/1311 1201/1911/1401 +f 332/1917/1406 1201/1911/1401 1872/1852/1351 +f 332/1917/1406 1872/1852/1351 1174/1851/1350 +f 333/1921/1410 1206/1922/1411 1795/1475/1074 +f 333/1921/1410 1795/1475/1074 1041/1489/1083 +f 333/1923/1410 1041/1487/1083 1797/1486/1082 +f 333/1923/1410 1797/1486/1082 1166/1829/1329 +f 333/1923/1410 1166/1829/1329 1869/1834/1333 +f 333/1923/1410 1869/1834/1333 1203/1916/1405 +f 333/1923/1410 1203/1916/1405 1865/1915/1404 +f 333/1923/1410 1865/1915/1404 1206/1924/1411 +f 334/1925/1412 1204/1918/1407 1870/1840/1339 +f 334/1925/1412 1870/1840/1339 1168/1839/1338 +f 334/1925/1412 1168/1839/1338 1798/1492/1086 +f 334/1925/1412 1798/1492/1086 1043/1491/1085 +f 334/1925/1412 1043/1491/1085 1796/1494/1078 +f 334/1925/1412 1796/1494/1078 1207/1926/1413 +f 334/1925/1412 1207/1926/1413 1866/1919/1408 +f 334/1925/1412 1866/1919/1408 1204/1918/1407 +f 335/1927/1414 1209/1928/1415 1579/1929/1416 +f 335/1927/1414 1579/1929/1416 1208/1930/1417 +f 335/1927/1414 1208/1930/1417 1865/1915/1404 +f 335/1927/1414 1865/1915/1404 1202/1914/1403 +f 335/1927/1414 1202/1914/1403 1863/1792/1302 +f 335/1927/1414 1863/1792/1302 1149/1791/1301 +f 335/1927/1414 1149/1791/1301 1807/1790/1300 +f 335/1927/1414 1807/1790/1300 1209/1928/1415 +f 336/1931/1418 1154/1804/1312 1864/1803/1311 +f 336/1931/1418 1864/1803/1311 1205/1932/1409 +f 336/1931/1418 1205/1932/1409 1866/1933/1408 +f 336/1931/1418 1866/1933/1408 1210/1934/1419 +f 336/1931/1418 1210/1934/1419 1579/1929/1416 +f 336/1931/1418 1579/1929/1416 1209/1928/1415 +f 336/1931/1418 1209/1928/1415 1807/1790/1300 +f 336/1931/1418 1807/1790/1300 1154/1804/1312 +f 337/1935/1420 1211/1936/1421 1789/1937/1049 +f 337/1935/1420 1789/1937/1049 1029/1938/1059 +f 337/1935/1420 1029/1938/1059 1791/1463/1058 +f 337/1935/1420 1791/1463/1058 1033/1462/1067 +f 337/1935/1420 1033/1462/1067 1793/1461/1066 +f 337/1935/1420 1793/1461/1066 1037/1476/1075 +f 337/1935/1420 1037/1476/1075 1795/1475/1074 +f 337/1935/1420 1795/1475/1074 1211/1936/1421 +f 338/1939/1422 1039/1478/1077 1794/1467/1070 +f 338/1939/1422 1794/1467/1070 1035/1466/1069 +f 338/1939/1422 1035/1466/1069 1792/1450/1062 +f 338/1939/1422 1792/1450/1062 1031/1449/1061 +f 338/1939/1422 1031/1449/1061 1790/1455/1054 +f 338/1939/1422 1790/1455/1054 1212/1940/1423 +f 338/1939/1422 1212/1940/1423 1796/1479/1078 +f 338/1939/1422 1796/1479/1078 1039/1478/1077 +f 339/1941/1424 1213/1942/1425 1789/1943/1049 +f 339/1941/1424 1789/1943/1049 1211/1944/1421 +f 339/1945/1424 1211/1936/1421 1795/1475/1074 +f 339/1945/1424 1795/1475/1074 1206/1922/1411 +f 339/1941/1424 1206/1924/1411 1865/1915/1404 +f 339/1941/1424 1865/1915/1404 1208/1930/1417 +f 339/1941/1424 1208/1930/1417 1579/1929/1416 +f 339/1941/1424 1579/1929/1416 1213/1942/1425 +f 340/1946/1426 1210/1934/1419 1866/1933/1408 +f 340/1946/1426 1866/1933/1408 1207/1947/1413 +f 340/1948/1426 1207/1949/1413 1796/1479/1078 +f 340/1948/1426 1796/1479/1078 1212/1940/1423 +f 340/1946/1426 1212/1950/1423 1790/1440/1054 +f 340/1946/1426 1790/1440/1054 1214/1951/1427 +f 340/1946/1426 1214/1951/1427 1579/1929/1416 +f 340/1946/1426 1579/1929/1416 1210/1934/1419 +f 341/1952/1428 1215/1953/1429 1578/1443/1051 +f 341/1952/1428 1578/1443/1051 1024/1954/1050 +f 341/1952/1428 1024/1954/1050 1789/1943/1049 +f 341/1952/1428 1789/1943/1049 1213/1942/1425 +f 341/1952/1428 1213/1942/1425 1579/1929/1416 +f 341/1952/1428 1579/1929/1416 1215/1953/1429 +f 342/1955/1430 1215/1953/1429 1579/1929/1416 +f 342/1955/1430 1579/1929/1416 1214/1951/1427 +f 342/1955/1430 1214/1951/1427 1790/1440/1054 +f 342/1955/1430 1790/1440/1054 1027/1439/1053 +f 342/1955/1430 1027/1439/1053 1578/1443/1051 +f 342/1955/1430 1578/1443/1051 1215/1953/1429 +f 343/1956/1431 1164/1957/1330 1799/1511/1090 +f 343/1956/1431 1799/1511/1090 1049/1510/1099 +f 343/1956/1431 1049/1510/1099 1801/1509/1098 +f 343/1956/1431 1801/1509/1098 1140/1764/1277 +f 343/1956/1431 1140/1764/1277 1815/1768/1281 +f 343/1956/1431 1815/1768/1281 1216/1958/1432 +f 343/1959/1431 1216/1960/1432 1867/1832/1331 +f 343/1959/1431 1867/1832/1331 1164/1831/1330 +f 344/1961/1433 1217/1962/1434 1816/1963/1287 +f 344/1961/1433 1816/1963/1287 1142/1964/1286 +f 344/1961/1433 1142/1964/1286 1802/1514/1102 +f 344/1961/1433 1802/1514/1102 1051/1513/1101 +f 344/1961/1433 1051/1513/1101 1800/1504/1094 +f 344/1961/1433 1800/1504/1094 1167/1838/1337 +f 344/1961/1433 1167/1838/1337 1868/1837/1336 +f 344/1961/1433 1868/1837/1336 1217/1962/1434 +f 345/1965/1435 1216/1958/1432 1815/1768/1281 +f 345/1965/1435 1815/1768/1281 1218/1966/1436 +f 345/1965/1435 1218/1966/1436 1817/1967/1437 +f 345/1965/1435 1817/1967/1437 1219/1968/1438 +f 345/1965/1435 1219/1968/1438 1873/1969/1343 +f 345/1965/1435 1873/1969/1343 1170/1970/1342 +f 345/1965/1435 1170/1970/1342 1867/1971/1331 +f 345/1965/1435 1867/1971/1331 1216/1958/1432 +f 346/1972/1439 1173/1850/1349 1874/1849/1348 +f 346/1972/1439 1874/1849/1348 1220/1973/1440 +f 346/1972/1439 1220/1973/1440 1818/1974/1441 +f 346/1972/1439 1818/1974/1441 1221/1975/1442 +f 346/1972/1439 1221/1975/1442 1816/1963/1287 +f 346/1972/1439 1816/1963/1287 1217/1962/1434 +f 346/1972/1439 1217/1962/1434 1868/1837/1336 +f 346/1972/1439 1868/1837/1336 1173/1850/1349 +f 347/1976/1443 1219/1968/1438 1817/1967/1437 +f 347/1976/1443 1817/1967/1437 1222/1977/1444 +f 347/1976/1443 1222/1977/1444 1887/1978/1445 +f 347/1976/1443 1887/1978/1445 1223/1979/1446 +f 347/1976/1443 1223/1979/1446 1875/1980/1355 +f 347/1976/1443 1875/1980/1355 1176/1981/1354 +f 347/1976/1443 1176/1981/1354 1873/1969/1343 +f 347/1976/1443 1873/1969/1343 1219/1968/1438 +f 348/1982/1447 1179/1862/1361 1876/1861/1360 +f 348/1982/1447 1876/1861/1360 1224/1983/1448 +f 348/1982/1447 1224/1983/1448 1888/1984/1449 +f 348/1982/1447 1888/1984/1449 1225/1985/1450 +f 348/1982/1447 1225/1985/1450 1818/1974/1441 +f 348/1982/1447 1818/1974/1441 1220/1973/1440 +f 348/1982/1447 1220/1973/1440 1874/1849/1348 +f 348/1982/1447 1874/1849/1348 1179/1862/1361 +f 349/1986/1451 1226/1987/1452 1819/1988/1378 +f 349/1986/1451 1819/1988/1378 1190/1989/1377 +f 349/1986/1451 1190/1989/1377 1881/1990/1367 +f 349/1986/1451 1881/1990/1367 1182/1991/1366 +f 349/1986/1451 1182/1991/1366 1875/1980/1355 +f 349/1986/1451 1875/1980/1355 1223/1979/1446 +f 349/1986/1451 1223/1979/1446 1887/1978/1445 +f 349/1986/1451 1887/1978/1445 1226/1987/1452 +f 350/1992/1453 1224/1983/1448 1876/1861/1360 +f 350/1992/1453 1876/1861/1360 1185/1877/1373 +f 350/1992/1453 1185/1877/1373 1882/1876/1372 +f 350/1992/1453 1882/1876/1372 1193/1894/1387 +f 350/1992/1453 1193/1894/1387 1820/1893/1386 +f 350/1992/1453 1820/1893/1386 1227/1993/1454 +f 350/1992/1453 1227/1993/1454 1888/1984/1449 +f 350/1992/1453 1888/1984/1449 1224/1983/1448 +f 351/1994/1455 1230/1995/1456 1853/1740/1260 +f 351/1994/1455 1853/1740/1260 1128/1739/1259 +f 351/1994/1455 1128/1739/1259 1855/1738/1250 +f 351/1994/1455 1855/1738/1250 1228/1996/1457 +f 351/1994/1455 1228/1996/1457 1891/1997/1458 +f 351/1994/1455 1891/1997/1458 1229/1998/1459 +f 351/1994/1455 1229/1998/1459 1889/1999/1460 +f 351/1994/1455 1889/1999/1460 1230/1995/1456 +f 352/2000/1461 1233/2001/1462 1892/2002/1463 +f 352/2000/1461 1892/2002/1463 1231/2003/1464 +f 352/2000/1461 1231/2003/1464 1856/1732/1255 +f 352/2000/1461 1856/1732/1255 1131/1747/1266 +f 352/2000/1461 1131/1747/1266 1854/1746/1265 +f 352/2000/1461 1854/1746/1265 1232/2004/1465 +f 352/2000/1461 1232/2004/1465 1890/2005/1466 +f 352/2000/1461 1890/2005/1466 1233/2001/1462 +f 353/2006/1467 1236/2007/1468 1823/1766/1279 +f 353/2006/1467 1823/1766/1279 1234/2008/1469 +f 353/2006/1467 1234/2008/1469 1889/1999/1460 +f 353/2006/1467 1889/1999/1460 1229/1998/1459 +f 353/2006/1467 1229/1998/1459 1891/1997/1458 +f 353/2006/1467 1891/1997/1458 1235/2009/1470 +f 353/2006/1467 1235/2009/1470 1825/2010/1471 +f 353/2006/1467 1825/2010/1471 1236/2007/1468 +f 354/2011/1472 1239/2012/1473 1892/2002/1463 +f 354/2011/1472 1892/2002/1463 1233/2001/1462 +f 354/2011/1472 1233/2001/1462 1890/2005/1466 +f 354/2011/1472 1890/2005/1466 1237/2013/1474 +f 354/2011/1472 1237/2013/1474 1824/1771/1284 +f 354/2011/1472 1824/1771/1284 1238/2014/1475 +f 354/2011/1472 1238/2014/1475 1826/2015/1476 +f 354/2011/1472 1826/2015/1476 1239/2012/1473 +f 355/2016/1477 1138/1765/1278 1803/1523/1106 +f 355/2016/1477 1803/1523/1106 1057/1535/1115 +f 355/2016/1477 1057/1535/1115 1805/1534/1114 +f 355/2016/1477 1805/1534/1114 1240/2017/1478 +f 355/2016/1477 1240/2017/1478 1889/1999/1460 +f 355/2016/1477 1889/1999/1460 1234/2008/1469 +f 355/2016/1477 1234/2008/1469 1823/1766/1279 +f 355/2016/1477 1823/1766/1279 1138/1765/1278 +f 356/2018/1479 1237/2013/1474 1890/2005/1466 +f 356/2018/1479 1890/2005/1466 1241/2019/1480 +f 356/2018/1479 1241/2019/1480 1806/1538/1118 +f 356/2018/1479 1806/1538/1118 1059/1537/1117 +f 356/2018/1479 1059/1537/1117 1804/1527/1110 +f 356/2018/1479 1804/1527/1110 1141/1772/1285 +f 356/2018/1479 1141/1772/1285 1824/1771/1284 +f 356/2018/1479 1824/1771/1284 1237/2013/1474 +f 357/2020/1481 1240/2017/1478 1805/1534/1114 +f 357/2020/1481 1805/1534/1114 1242/2021/1482 +f 357/2020/1481 1242/2021/1482 1849/1672/1208 +f 357/2020/1481 1849/1672/1208 1103/1671/1207 +f 357/2020/1481 1103/1671/1207 1847/1670/1206 +f 357/2020/1481 1847/1670/1206 1243/2022/1483 +f 357/2020/1481 1243/2022/1483 1889/1999/1460 +f 357/2020/1481 1889/1999/1460 1240/2017/1478 +f 358/2023/1484 1245/2024/1485 1848/1762/1214 +f 358/2023/1484 1848/1762/1214 1105/2025/1213 +f 358/2026/1484 1105/817/1213 1850/2027/1212 +f 358/2026/1484 1850/2027/1212 1244/2028/1486 +f 358/2023/1484 1244/2029/1486 1806/1538/1118 +f 358/2023/1484 1806/1538/1118 1241/2019/1480 +f 358/2023/1484 1241/2019/1480 1890/2005/1466 +f 358/2023/1484 1890/2005/1466 1245/2024/1485 +f 359/2030/1487 1243/2022/1483 1847/1670/1206 +f 359/2030/1487 1847/1670/1206 1136/1756/1273 +f 359/2030/1487 1136/1756/1273 1853/1740/1260 +f 359/2030/1487 1853/1740/1260 1230/1995/1456 +f 359/2030/1487 1230/1995/1456 1889/1999/1460 +f 359/2030/1487 1889/1999/1460 1243/2022/1483 +f 360/2031/1488 1245/2024/1485 1890/2005/1466 +f 360/2031/1488 1890/2005/1466 1232/2004/1465 +f 360/2031/1488 1232/2004/1465 1854/1746/1265 +f 360/2031/1488 1854/1746/1265 1137/1758/1275 +f 360/2031/1488 1137/1758/1275 1848/1762/1214 +f 360/2031/1488 1848/1762/1214 1245/2024/1485 +f 361/2032/1489 1242/2021/1482 1805/1534/1114 +f 361/2032/1489 1805/1534/1114 1114/1696/1229 +f 361/2032/1489 1114/1696/1229 1851/1687/1223 +f 361/2032/1489 1851/1687/1223 1110/1686/1222 +f 361/2032/1489 1110/1686/1222 1849/1672/1208 +f 361/2032/1489 1849/1672/1208 1242/2021/1482 +f 362/2033/1490 1244/2028/1486 1850/2027/1212 +f 362/2033/1490 1850/2027/1212 1113/2034/1225 +f 362/2035/1490 1113/1694/1225 1852/1693/1227 +f 362/2035/1490 1852/1693/1227 1115/1704/1231 +f 362/2033/1490 1115/2036/1231 1806/2037/1118 +f 362/2033/1490 1806/2037/1118 1244/2028/1486 +f 363/2038/1491 1188/1883/1379 1819/1882/1378 +f 363/2038/1491 1819/1882/1378 1246/2039/1492 +f 363/2038/1491 1246/2039/1492 1827/2040/1493 +f 363/2038/1491 1827/2040/1493 1247/2041/1494 +f 363/2038/1491 1247/2041/1494 1885/2042/1495 +f 363/2038/1491 1885/2042/1495 1248/2043/1496 +f 363/2038/1491 1248/2043/1496 1883/1884/1380 +f 363/2038/1491 1883/1884/1380 1188/1883/1379 +f 364/2044/1497 1251/2045/1498 1886/2046/1499 +f 364/2044/1497 1886/2046/1499 1249/2047/1500 +f 364/2044/1497 1249/2047/1500 1828/2048/1501 +f 364/2044/1497 1828/2048/1501 1250/2049/1502 +f 364/2044/1497 1250/2049/1502 1820/2050/1386 +f 364/2044/1497 1820/2050/1386 1192/2051/1385 +f 364/2044/1497 1192/2051/1385 1884/2052/1384 +f 364/2044/1497 1884/2052/1384 1251/2045/1498 +f 365/2053/1503 1145/1780/1293 1821/1779/1292 +f 365/2053/1503 1821/1779/1292 1195/1896/1389 +f 365/2053/1503 1195/1896/1389 1883/1884/1380 +f 365/2053/1503 1883/1884/1380 1248/2043/1496 +f 365/2053/1503 1248/2043/1496 1885/2042/1495 +f 365/2053/1503 1885/2042/1495 1252/2054/1504 +f 365/2053/1503 1252/2054/1504 1829/1709/1236 +f 365/2053/1503 1829/1709/1236 1145/1780/1293 +f 366/2055/1505 1253/2056/1506 1886/2046/1499 +f 366/2055/1505 1886/2046/1499 1251/2045/1498 +f 366/2055/1505 1251/2045/1498 1884/2052/1384 +f 366/2055/1505 1884/2052/1384 1197/2057/1393 +f 366/2058/1505 1197/1901/1393 1822/1785/1296 +f 366/2058/1505 1822/1785/1296 1147/1784/1295 +f 366/2055/1505 1147/1782/1295 1830/1721/1245 +f 366/2055/1505 1830/1721/1245 1253/2056/1506 +f 367/2059/1507 1255/2060/1508 1825/2061/1471 +f 367/2059/1507 1825/2061/1471 1235/2062/1470 +f 367/2063/1507 1235/2009/1470 1891/1997/1458 +f 367/2063/1507 1891/1997/1458 1254/2064/1509 +f 367/2063/1507 1254/2064/1509 1885/2065/1495 +f 367/2063/1507 1885/2065/1495 1247/2066/1494 +f 367/2059/1507 1247/2067/1494 1827/2068/1493 +f 367/2059/1507 1827/2068/1493 1255/2060/1508 +f 368/2069/1510 1249/2047/1500 1886/2046/1499 +f 368/2069/1510 1886/2046/1499 1256/2070/1511 +f 368/2069/1510 1256/2070/1511 1892/2002/1463 +f 368/2069/1510 1892/2002/1463 1239/2012/1473 +f 368/2069/1510 1239/2012/1473 1826/2015/1476 +f 368/2069/1510 1826/2015/1476 1257/2071/1512 +f 368/2069/1510 1257/2071/1512 1828/2048/1501 +f 368/2069/1510 1828/2048/1501 1249/2047/1500 +f 369/2072/1513 1228/1996/1457 1855/1738/1250 +f 369/2072/1513 1855/1738/1250 1123/2073/1249 +f 369/2072/1513 1123/2073/1249 1857/2074/1238 +f 369/2072/1513 1857/2074/1238 1258/2075/1514 +f 369/2072/1513 1258/2075/1514 1885/2065/1495 +f 369/2072/1513 1885/2065/1495 1254/2064/1509 +f 369/2072/1513 1254/2064/1509 1891/1997/1458 +f 369/2072/1513 1891/1997/1458 1228/1996/1457 +f 370/2076/1515 1256/2070/1511 1886/2046/1499 +f 370/2076/1515 1886/2046/1499 1259/2077/1516 +f 370/2076/1515 1259/2077/1516 1858/1719/1243 +f 370/2076/1515 1858/1719/1243 1126/1733/1256 +f 370/2076/1515 1126/1733/1256 1856/1732/1255 +f 370/2076/1515 1856/1732/1255 1231/2003/1464 +f 370/2076/1515 1231/2003/1464 1892/2002/1463 +f 370/2076/1515 1892/2002/1463 1256/2070/1511 +f 371/2078/1517 1117/1710/1237 1829/1709/1236 +f 371/2078/1517 1829/1709/1236 1252/2054/1504 +f 371/2079/1517 1252/2080/1504 1885/2065/1495 +f 371/2079/1517 1885/2065/1495 1258/2075/1514 +f 371/2079/1517 1258/2075/1514 1857/2074/1238 +f 371/2079/1517 1857/2074/1238 1117/2081/1237 +f 372/2082/1518 1120/1720/1244 1858/1719/1243 +f 372/2082/1518 1858/1719/1243 1259/2077/1516 +f 372/2082/1518 1259/2077/1516 1886/2046/1499 +f 372/2082/1518 1886/2046/1499 1253/2056/1506 +f 372/2082/1518 1253/2056/1506 1830/1721/1245 +f 372/2082/1518 1830/1721/1245 1120/1720/1244 +f 373/2083/1519 1263/2084/1520 1905/2085/1521 +f 373/2083/1519 1905/2085/1521 1260/2086/1522 +f 373/2083/1519 1260/2086/1522 1893/2087/1523 +f 373/2083/1519 1893/2087/1523 1261/2088/1524 +f 373/2089/1519 1261/2090/1524 1919/2091/1525 +f 373/2089/1519 1919/2091/1525 1262/2092/1526 +f 373/2089/1519 1262/2092/1526 1907/2093/1527 +f 373/2089/1519 1907/2093/1527 1263/2094/1520 +f 374/2095/1528 1267/2096/1529 1920/2097/1530 +f 374/2095/1528 1920/2097/1530 1264/2098/1531 +f 374/2099/1528 1264/2100/1531 1894/2101/1532 +f 374/2099/1528 1894/2101/1532 1265/2102/1533 +f 374/2099/1528 1265/2102/1533 1906/2103/1534 +f 374/2099/1528 1906/2103/1534 1266/2104/1535 +f 374/2095/1528 1266/2105/1535 1908/2106/1536 +f 374/2095/1528 1908/2106/1536 1267/2096/1529 +f 375/2107/1537 1270/2108/1538 1905/2085/1521 +f 375/2107/1537 1905/2085/1521 1263/2084/1520 +f 375/2109/1537 1263/2094/1520 1907/2093/1527 +f 375/2109/1537 1907/2093/1527 1268/2110/1539 +f 375/2109/1537 1268/2110/1539 1909/2111/1540 +f 375/2109/1537 1909/2111/1540 1269/2112/1541 +f 375/2107/1537 1269/2113/1541 1903/2114/1542 +f 375/2107/1537 1903/2114/1542 1270/2108/1538 +f 376/2115/1543 1273/2116/1544 1910/2117/1545 +f 376/2115/1543 1910/2117/1545 1271/2118/1546 +f 376/2115/1543 1271/2118/1546 1908/2106/1536 +f 376/2115/1543 1908/2106/1536 1266/2105/1535 +f 376/2119/1543 1266/2104/1535 1906/2103/1534 +f 376/2119/1543 1906/2103/1534 1272/2120/1547 +f 376/2121/1543 1272/2122/1547 1904/2123/1548 +f 376/2121/1543 1904/2123/1548 1273/2124/1544 +f 377/2125/1549 1276/2126/1550 1903/2114/1542 +f 377/2125/1549 1903/2114/1542 1269/2113/1541 +f 377/2127/1549 1269/2112/1541 1909/2111/1540 +f 377/2127/1549 1909/2111/1540 1274/2128/1551 +f 377/2127/1549 1274/2128/1551 1911/2129/1552 +f 377/2127/1549 1911/2129/1552 1275/2130/1553 +f 377/2125/1549 1275/2131/1553 1901/2132/1554 +f 377/2125/1549 1901/2132/1554 1276/2126/1550 +f 378/2133/1555 1279/2134/1556 1912/2135/1557 +f 378/2133/1555 1912/2135/1557 1277/2136/1558 +f 378/2133/1555 1277/2136/1558 1910/2117/1545 +f 378/2133/1555 1910/2117/1545 1273/2116/1544 +f 378/2133/1555 1273/2116/1544 1904/2137/1548 +f 378/2133/1555 1904/2137/1548 1278/2138/1559 +f 378/2133/1555 1278/2138/1559 1902/2139/1560 +f 378/2133/1555 1902/2139/1560 1279/2134/1556 +f 379/2140/1561 1282/2141/1562 1901/2142/1554 +f 379/2140/1561 1901/2142/1554 1275/2130/1553 +f 379/2140/1561 1275/2130/1553 1911/2129/1552 +f 379/2140/1561 1911/2129/1552 1280/2143/1563 +f 379/2140/1561 1280/2143/1563 1913/2144/1564 +f 379/2140/1561 1913/2144/1564 1281/2145/1565 +f 379/2140/1561 1281/2145/1565 1899/2146/1566 +f 379/2140/1561 1899/2146/1566 1282/2141/1562 +f 380/2147/1567 1285/2148/1568 1914/2149/1569 +f 380/2147/1567 1914/2149/1569 1283/2150/1570 +f 380/2147/1567 1283/2150/1570 1912/2135/1557 +f 380/2147/1567 1912/2135/1557 1279/2134/1556 +f 380/2147/1567 1279/2134/1556 1902/2139/1560 +f 380/2147/1567 1902/2139/1560 1284/2151/1571 +f 380/2147/1567 1284/2151/1571 1900/2152/1572 +f 380/2147/1567 1900/2152/1572 1285/2148/1568 +f 381/2153/1573 1288/2154/1574 1899/2155/1566 +f 381/2153/1573 1899/2155/1566 1281/2156/1565 +f 381/2157/1573 1281/2145/1565 1913/2144/1564 +f 381/2157/1573 1913/2144/1564 1286/2158/1575 +f 381/2159/1573 1286/2160/1575 1915/2161/1576 +f 381/2159/1573 1915/2161/1576 1287/2162/1577 +f 381/2153/1573 1287/2163/1577 1897/2164/1578 +f 381/2153/1573 1897/2164/1578 1288/2154/1574 +f 382/2165/1579 1291/2166/1580 1916/2167/1581 +f 382/2165/1579 1916/2167/1581 1289/2168/1582 +f 382/2165/1579 1289/2168/1582 1914/2149/1569 +f 382/2165/1579 1914/2149/1569 1285/2148/1568 +f 382/2165/1579 1285/2148/1568 1900/2152/1572 +f 382/2165/1579 1900/2152/1572 1290/2169/1583 +f 382/2165/1579 1290/2169/1583 1898/2170/1584 +f 382/2165/1579 1898/2170/1584 1291/2166/1580 +f 383/2171/1585 1294/2172/1586 1897/2164/1578 +f 383/2171/1585 1897/2164/1578 1287/2163/1577 +f 383/2171/1585 1287/2163/1577 1915/2173/1576 +f 383/2171/1585 1915/2173/1576 1292/2174/1587 +f 383/2175/1585 1292/2176/1587 1917/2177/1588 +f 383/2175/1585 1917/2177/1588 1293/2178/1589 +f 383/2171/1585 1293/2179/1589 1895/2180/1590 +f 383/2171/1585 1895/2180/1590 1294/2172/1586 +f 384/2181/1591 1297/2182/1592 1918/2183/1593 +f 384/2181/1591 1918/2183/1593 1295/2184/1594 +f 384/2181/1591 1295/2184/1594 1916/2167/1581 +f 384/2181/1591 1916/2167/1581 1291/2166/1580 +f 384/2181/1591 1291/2166/1580 1898/2170/1584 +f 384/2181/1591 1898/2170/1584 1296/2185/1595 +f 384/2181/1591 1296/2185/1595 1896/2186/1596 +f 384/2181/1591 1896/2186/1596 1297/2182/1592 +f 385/2187/1597 1292/2188/1587 1915/2161/1576 +f 385/2187/1597 1915/2161/1576 1298/2189/1598 +f 385/2187/1597 1298/2189/1598 1925/2190/1599 +f 385/2187/1597 1925/2190/1599 1299/2191/1600 +f 385/2187/1597 1299/2191/1600 1923/2192/1601 +f 385/2187/1597 1923/2192/1601 1300/2193/1602 +f 385/2187/1597 1300/2193/1602 1917/2194/1588 +f 385/2187/1597 1917/2194/1588 1292/2188/1587 +f 386/2195/1603 1303/2196/1604 1924/2197/1605 +f 386/2195/1603 1924/2197/1605 1301/2198/1606 +f 386/2195/1603 1301/2198/1606 1926/2199/1607 +f 386/2195/1603 1926/2199/1607 1302/2200/1608 +f 386/2201/1603 1302/2202/1608 1916/2203/1581 +f 386/2201/1603 1916/2203/1581 1295/2204/1594 +f 386/2201/1603 1295/2204/1594 1918/2205/1593 +f 386/2201/1603 1918/2205/1593 1303/2206/1604 +f 387/2207/1609 1286/2160/1575 1913/2208/1564 +f 387/2207/1609 1913/2208/1564 1304/2209/1610 +f 387/2210/1609 1304/2211/1610 1927/2212/1611 +f 387/2210/1609 1927/2212/1611 1305/2213/1612 +f 387/2207/1609 1305/2214/1612 1925/2190/1599 +f 387/2207/1609 1925/2190/1599 1298/2189/1598 +f 387/2207/1609 1298/2189/1598 1915/2161/1576 +f 387/2207/1609 1915/2161/1576 1286/2160/1575 +f 388/2215/1613 1302/2200/1608 1926/2199/1607 +f 388/2215/1613 1926/2199/1607 1306/2216/1614 +f 388/2215/1613 1306/2216/1614 1928/2217/1615 +f 388/2215/1613 1928/2217/1615 1307/2218/1616 +f 388/2215/1613 1307/2218/1616 1914/2219/1569 +f 388/2215/1613 1914/2219/1569 1289/2220/1582 +f 388/2215/1613 1289/2220/1582 1916/2221/1581 +f 388/2215/1613 1916/2221/1581 1302/2200/1608 +f 389/2222/1617 1280/2143/1563 1911/2129/1552 +f 389/2222/1617 1911/2129/1552 1308/2223/1618 +f 389/2224/1617 1308/2225/1618 1929/2226/1619 +f 389/2224/1617 1929/2226/1619 1309/2227/1620 +f 389/2224/1617 1309/2227/1620 1927/2212/1611 +f 389/2224/1617 1927/2212/1611 1304/2211/1610 +f 389/2222/1617 1304/2228/1610 1913/2144/1564 +f 389/2222/1617 1913/2144/1564 1280/2143/1563 +f 390/2229/1621 1307/2218/1616 1928/2217/1615 +f 390/2229/1621 1928/2217/1615 1310/2230/1622 +f 390/2231/1621 1310/2232/1622 1930/2233/1623 +f 390/2231/1621 1930/2233/1623 1311/2234/1624 +f 390/2235/1621 1311/2236/1624 1912/2135/1557 +f 390/2235/1621 1912/2135/1557 1283/2150/1570 +f 390/2229/1621 1283/2237/1570 1914/2219/1569 +f 390/2229/1621 1914/2219/1569 1307/2218/1616 +f 391/2238/1625 1274/2128/1551 1909/2111/1540 +f 391/2238/1625 1909/2111/1540 1312/2239/1626 +f 391/2238/1625 1312/2239/1626 1931/2240/1627 +f 391/2238/1625 1931/2240/1627 1313/2241/1628 +f 391/2238/1625 1313/2241/1628 1929/2242/1619 +f 391/2238/1625 1929/2242/1619 1308/2223/1618 +f 391/2238/1625 1308/2223/1618 1911/2129/1552 +f 391/2238/1625 1911/2129/1552 1274/2128/1551 +f 392/2243/1629 1311/2234/1624 1930/2233/1623 +f 392/2243/1629 1930/2233/1623 1314/2244/1630 +f 392/2245/1629 1314/2246/1630 1932/2247/1631 +f 392/2245/1629 1932/2247/1631 1315/2248/1632 +f 392/2245/1629 1315/2248/1632 1910/2117/1545 +f 392/2245/1629 1910/2117/1545 1277/2136/1558 +f 392/2245/1629 1277/2136/1558 1912/2135/1557 +f 392/2245/1629 1912/2135/1557 1311/2236/1624 +f 393/2249/1633 1268/2250/1539 1907/2251/1527 +f 393/2249/1633 1907/2251/1527 1316/2252/1634 +f 393/2249/1633 1316/2252/1634 1933/2253/1635 +f 393/2249/1633 1933/2253/1635 1317/2254/1636 +f 393/2255/1633 1317/2256/1636 1931/2240/1627 +f 393/2255/1633 1931/2240/1627 1312/2239/1626 +f 393/2255/1633 1312/2239/1626 1909/2111/1540 +f 393/2255/1633 1909/2111/1540 1268/2110/1539 +f 394/2257/1637 1315/2248/1632 1932/2247/1631 +f 394/2257/1637 1932/2247/1631 1318/2258/1638 +f 394/2257/1637 1318/2258/1638 1934/2259/1639 +f 394/2257/1637 1934/2259/1639 1319/2260/1640 +f 394/2257/1637 1319/2260/1640 1908/2106/1536 +f 394/2257/1637 1908/2106/1536 1271/2118/1546 +f 394/2257/1637 1271/2118/1546 1910/2117/1545 +f 394/2257/1637 1910/2117/1545 1315/2248/1632 +f 395/2261/1641 1316/2252/1634 1907/2251/1527 +f 395/2261/1641 1907/2251/1527 1262/2262/1526 +f 395/2261/1641 1262/2262/1526 1919/2263/1525 +f 395/2261/1641 1919/2263/1525 1320/2264/1642 +f 395/2261/1641 1320/2264/1642 1921/2265/1643 +f 395/2261/1641 1921/2265/1643 1321/2266/1644 +f 395/2261/1641 1321/2266/1644 1933/2253/1635 +f 395/2261/1641 1933/2253/1635 1316/2252/1634 +f 396/2267/1645 1323/2268/1646 1922/2269/1647 +f 396/2267/1645 1922/2269/1647 1322/2270/1648 +f 396/2267/1645 1322/2270/1648 1920/2097/1530 +f 396/2267/1645 1920/2097/1530 1267/2096/1529 +f 396/2267/1645 1267/2096/1529 1908/2106/1536 +f 396/2267/1645 1908/2106/1536 1319/2260/1640 +f 396/2267/1645 1319/2260/1640 1934/2259/1639 +f 396/2267/1645 1934/2259/1639 1323/2268/1646 +f 397/2271/1649 1326/2272/1650 1823/1766/1279 +f 397/2271/1649 1823/1766/1279 1236/2007/1468 +f 397/2271/1649 1236/2007/1468 1825/2010/1471 +f 397/2271/1649 1825/2010/1471 1324/2273/1651 +f 397/2271/1649 1324/2273/1651 1949/2274/1652 +f 397/2271/1649 1949/2274/1652 1325/2275/1653 +f 397/2271/1649 1325/2275/1653 1947/2276/1654 +f 397/2271/1649 1947/2276/1654 1326/2272/1650 +f 398/2277/1655 1329/2278/1656 1950/2279/1657 +f 398/2277/1655 1950/2279/1657 1327/2280/1658 +f 398/2277/1655 1327/2280/1658 1826/2015/1476 +f 398/2277/1655 1826/2015/1476 1238/2014/1475 +f 398/2277/1655 1238/2014/1475 1824/1771/1284 +f 398/2277/1655 1824/1771/1284 1328/2281/1659 +f 398/2277/1655 1328/2281/1659 1948/2282/1660 +f 398/2277/1655 1948/2282/1660 1329/2278/1656 +f 399/2283/1661 1324/2284/1651 1825/2285/1471 +f 399/2283/1661 1825/2285/1471 1330/2286/1662 +f 399/2283/1661 1330/2286/1662 1895/2287/1590 +f 399/2283/1661 1895/2287/1590 1293/2178/1589 +f 399/2283/1661 1293/2178/1589 1917/2177/1588 +f 399/2283/1661 1917/2177/1588 1331/2288/1663 +f 399/2283/1661 1331/2288/1663 1949/2289/1652 +f 399/2283/1661 1949/2289/1652 1324/2284/1651 +f 400/2290/1664 1333/2291/1665 1918/2183/1593 +f 400/2290/1664 1918/2183/1593 1297/2182/1592 +f 400/2290/1664 1297/2182/1592 1896/2186/1596 +f 400/2290/1664 1896/2186/1596 1332/2292/1666 +f 400/2290/1664 1332/2292/1666 1826/2293/1476 +f 400/2290/1664 1826/2293/1476 1327/2294/1658 +f 400/2290/1664 1327/2294/1658 1950/2295/1657 +f 400/2290/1664 1950/2295/1657 1333/2291/1665 +f 401/2296/1667 1218/1966/1436 1815/1768/1281 +f 401/2296/1667 1815/1768/1281 1139/1767/1280 +f 401/2296/1667 1139/1767/1280 1823/1766/1279 +f 401/2296/1667 1823/1766/1279 1326/2272/1650 +f 401/2296/1667 1326/2272/1650 1947/2276/1654 +f 401/2296/1667 1947/2276/1654 1334/2297/1668 +f 401/2296/1667 1334/2297/1668 1817/1967/1437 +f 401/2296/1667 1817/1967/1437 1218/1966/1436 +f 402/2298/1669 1335/2299/1670 1948/2282/1660 +f 402/2298/1669 1948/2282/1660 1328/2281/1659 +f 402/2298/1669 1328/2281/1659 1824/1771/1284 +f 402/2298/1669 1824/1771/1284 1143/1770/1283 +f 402/2298/1669 1143/1770/1283 1816/1774/1287 +f 402/2298/1669 1816/1774/1287 1221/2300/1442 +f 402/2298/1669 1221/2300/1442 1818/2301/1441 +f 402/2298/1669 1818/2301/1441 1335/2299/1670 +f 403/2302/1671 1338/2303/1672 1887/2304/1445 +f 403/2302/1671 1887/2304/1445 1336/2305/1673 +f 403/2306/1671 1336/2307/1673 1935/2308/1674 +f 403/2306/1671 1935/2308/1674 1337/2309/1675 +f 403/2306/1671 1337/2309/1675 1919/2091/1525 +f 403/2306/1671 1919/2091/1525 1261/2090/1524 +f 403/2310/1671 1261/2311/1524 1893/2312/1523 +f 403/2310/1671 1893/2312/1523 1338/2313/1672 +f 404/2314/1676 1264/2098/1531 1920/2097/1530 +f 404/2314/1676 1920/2097/1530 1339/2315/1677 +f 404/2314/1676 1339/2315/1677 1936/2316/1678 +f 404/2314/1676 1936/2316/1678 1340/2317/1679 +f 404/2318/1676 1340/2319/1679 1888/2320/1449 +f 404/2318/1676 1888/2320/1449 1341/2321/1680 +f 404/2318/1676 1341/2321/1680 1894/2101/1532 +f 404/2318/1676 1894/2101/1532 1264/2100/1531 +f 405/2322/1681 1331/2323/1663 1917/2194/1588 +f 405/2322/1681 1917/2194/1588 1300/2193/1602 +f 405/2322/1681 1300/2193/1602 1923/2192/1601 +f 405/2322/1681 1923/2192/1601 1342/2324/1682 +f 405/2322/1681 1342/2324/1682 1945/2325/1683 +f 405/2322/1681 1945/2325/1683 1343/2326/1684 +f 405/2322/1681 1343/2326/1684 1949/2327/1652 +f 405/2322/1681 1949/2327/1652 1331/2323/1663 +f 406/2328/1685 1345/2329/1686 1946/2330/1687 +f 406/2328/1685 1946/2330/1687 1344/2331/1688 +f 406/2328/1685 1344/2331/1688 1924/2197/1605 +f 406/2328/1685 1924/2197/1605 1303/2196/1604 +f 406/2328/1685 1303/2196/1604 1918/2332/1593 +f 406/2328/1685 1918/2332/1593 1333/2333/1665 +f 406/2328/1685 1333/2333/1665 1950/2334/1657 +f 406/2328/1685 1950/2334/1657 1345/2329/1686 +f 407/2335/1689 1348/2336/1690 1943/2337/1691 +f 407/2335/1689 1943/2337/1691 1346/2338/1692 +f 407/2335/1689 1346/2338/1692 1951/2339/1693 +f 407/2335/1689 1951/2339/1693 1347/2340/1694 +f 407/2341/1689 1347/2342/1694 1949/2327/1652 +f 407/2341/1689 1949/2327/1652 1343/2326/1684 +f 407/2341/1689 1343/2326/1684 1945/2325/1683 +f 407/2341/1689 1945/2325/1683 1348/2343/1690 +f 408/2344/1695 1345/2345/1686 1950/2346/1657 +f 408/2344/1695 1950/2346/1657 1349/2347/1696 +f 408/2348/1695 1349/2349/1696 1952/2350/1697 +f 408/2348/1695 1952/2350/1697 1350/2351/1698 +f 408/2348/1695 1350/2351/1698 1944/2352/1699 +f 408/2348/1695 1944/2352/1699 1351/2353/1700 +f 408/2344/1695 1351/2354/1700 1946/2355/1687 +f 408/2344/1695 1946/2355/1687 1345/2345/1686 +f 409/2356/1701 1354/2357/1702 1939/2358/1703 +f 409/2356/1701 1939/2358/1703 1352/2359/1704 +f 409/2356/1701 1352/2359/1704 1951/2360/1693 +f 409/2356/1701 1951/2360/1693 1346/2361/1692 +f 409/2356/1701 1346/2361/1692 1943/2362/1691 +f 409/2356/1701 1943/2362/1691 1353/2363/1705 +f 409/2356/1701 1353/2363/1705 1941/2364/1706 +f 409/2356/1701 1941/2364/1706 1354/2357/1702 +f 410/2365/1707 1357/2366/1708 1944/2352/1699 +f 410/2365/1707 1944/2352/1699 1350/2351/1698 +f 410/2367/1707 1350/2368/1698 1952/2369/1697 +f 410/2367/1707 1952/2369/1697 1355/2370/1709 +f 410/2367/1707 1355/2370/1709 1940/2371/1710 +f 410/2367/1707 1940/2371/1710 1356/2372/1711 +f 410/2367/1707 1356/2372/1711 1942/2373/1712 +f 410/2367/1707 1942/2373/1712 1357/2374/1708 +f 411/2375/1713 1360/2376/1714 1937/2377/1715 +f 411/2375/1713 1937/2377/1715 1358/2378/1716 +f 411/2375/1713 1358/2378/1716 1953/2379/1717 +f 411/2375/1713 1953/2379/1717 1359/2380/1718 +f 411/2375/1713 1359/2380/1718 1951/2339/1693 +f 411/2375/1713 1951/2339/1693 1352/2381/1704 +f 411/2375/1713 1352/2381/1704 1939/2382/1703 +f 411/2375/1713 1939/2382/1703 1360/2376/1714 +f 412/2383/1719 1355/2384/1709 1952/2385/1697 +f 412/2383/1719 1952/2385/1697 1361/2386/1720 +f 412/2383/1719 1361/2386/1720 1954/2387/1721 +f 412/2383/1719 1954/2387/1721 1362/2388/1722 +f 412/2389/1719 1362/2390/1722 1938/2391/1723 +f 412/2389/1719 1938/2391/1723 1363/2392/1724 +f 412/2383/1719 1363/2393/1724 1940/2394/1710 +f 412/2383/1719 1940/2394/1710 1355/2384/1709 +f 413/2395/1725 1366/2396/1726 1935/2397/1674 +f 413/2395/1725 1935/2397/1674 1364/2398/1727 +f 413/2395/1725 1364/2398/1727 1953/2379/1717 +f 413/2395/1725 1953/2379/1717 1358/2378/1716 +f 413/2395/1725 1358/2378/1716 1937/2377/1715 +f 413/2395/1725 1937/2377/1715 1365/2399/1728 +f 413/2395/1725 1365/2399/1728 1955/2400/1729 +f 413/2395/1725 1955/2400/1729 1366/2396/1726 +f 414/2401/1730 1369/2402/1731 1938/2391/1723 +f 414/2401/1730 1938/2391/1723 1362/2390/1722 +f 414/2401/1730 1362/2390/1722 1954/2403/1721 +f 414/2401/1730 1954/2403/1721 1367/2404/1732 +f 414/2401/1730 1367/2404/1732 1936/2316/1678 +f 414/2401/1730 1936/2316/1678 1368/2405/1733 +f 414/2401/1730 1368/2405/1733 1956/2406/1734 +f 414/2401/1730 1956/2406/1734 1369/2402/1731 +f 415/2407/1735 1320/2264/1642 1919/2263/1525 +f 415/2407/1735 1919/2263/1525 1337/2408/1675 +f 415/2407/1735 1337/2408/1675 1935/2397/1674 +f 415/2407/1735 1935/2397/1674 1366/2396/1726 +f 415/2407/1735 1366/2396/1726 1955/2400/1729 +f 415/2407/1735 1955/2400/1729 1370/2409/1736 +f 415/2407/1735 1370/2409/1736 1921/2265/1643 +f 415/2407/1735 1921/2265/1643 1320/2264/1642 +f 416/2410/1737 1371/2411/1738 1956/2406/1734 +f 416/2410/1737 1956/2406/1734 1368/2405/1733 +f 416/2410/1737 1368/2405/1733 1936/2316/1678 +f 416/2410/1737 1936/2316/1678 1339/2315/1677 +f 416/2410/1737 1339/2315/1677 1920/2097/1530 +f 416/2410/1737 1920/2097/1530 1322/2270/1648 +f 416/2410/1737 1322/2270/1648 1922/2269/1647 +f 416/2410/1737 1922/2269/1647 1371/2411/1738 +f 417/2412/1739 1222/1977/1444 1817/1967/1437 +f 417/2412/1739 1817/1967/1437 1372/2413/1740 +f 417/2412/1739 1372/2413/1740 1953/2379/1717 +f 417/2412/1739 1953/2379/1717 1364/2398/1727 +f 417/2412/1739 1364/2398/1727 1935/2397/1674 +f 417/2412/1739 1935/2397/1674 1336/2414/1673 +f 417/2412/1739 1336/2414/1673 1887/1978/1445 +f 417/2412/1739 1887/1978/1445 1222/1977/1444 +f 418/2415/1741 1340/2317/1679 1936/2316/1678 +f 418/2415/1741 1936/2316/1678 1367/2404/1732 +f 418/2416/1741 1367/2417/1732 1954/2387/1721 +f 418/2416/1741 1954/2387/1721 1373/2418/1742 +f 418/2419/1741 1373/2420/1742 1818/1974/1441 +f 418/2419/1741 1818/1974/1441 1225/1985/1450 +f 418/2419/1741 1225/1985/1450 1888/1984/1449 +f 418/2419/1741 1888/1984/1449 1340/2421/1679 +f 419/2422/1743 1372/2413/1740 1817/1967/1437 +f 419/2422/1743 1817/1967/1437 1334/2297/1668 +f 419/2422/1743 1334/2297/1668 1947/2276/1654 +f 419/2422/1743 1947/2276/1654 1374/2423/1744 +f 419/2422/1743 1374/2423/1744 1951/2339/1693 +f 419/2422/1743 1951/2339/1693 1359/2380/1718 +f 419/2422/1743 1359/2380/1718 1953/2379/1717 +f 419/2422/1743 1953/2379/1717 1372/2413/1740 +f 420/2424/1745 1361/2386/1720 1952/2385/1697 +f 420/2424/1745 1952/2385/1697 1375/2425/1746 +f 420/2424/1745 1375/2425/1746 1948/2282/1660 +f 420/2424/1745 1948/2282/1660 1335/2299/1670 +f 420/2424/1745 1335/2299/1670 1818/2301/1441 +f 420/2424/1745 1818/2301/1441 1373/2418/1742 +f 420/2424/1745 1373/2418/1742 1954/2387/1721 +f 420/2424/1745 1954/2387/1721 1361/2386/1720 +f 421/2426/1747 1374/2423/1744 1947/2276/1654 +f 421/2426/1747 1947/2276/1654 1325/2275/1653 +f 421/2426/1747 1325/2275/1653 1949/2274/1652 +f 421/2426/1747 1949/2274/1652 1347/2340/1694 +f 421/2426/1747 1347/2340/1694 1951/2339/1693 +f 421/2426/1747 1951/2339/1693 1374/2423/1744 +f 422/2427/1748 1375/2428/1746 1952/2429/1697 +f 422/2427/1748 1952/2429/1697 1349/2347/1696 +f 422/2427/1748 1349/2347/1696 1950/2346/1657 +f 422/2427/1748 1950/2346/1657 1329/2430/1656 +f 422/2431/1748 1329/2278/1656 1948/2282/1660 +f 422/2431/1748 1948/2282/1660 1375/2425/1746 +f 423/2432/1749 1378/2433/1750 1921/2265/1643 +f 423/2432/1749 1921/2265/1643 1370/2409/1736 +f 423/2432/1749 1370/2409/1736 1955/2400/1729 +f 423/2432/1749 1955/2400/1729 1376/2434/1751 +f 423/2432/1749 1376/2434/1751 1957/2435/1752 +f 423/2432/1749 1957/2435/1752 1377/2436/1753 +f 423/2432/1749 1377/2436/1753 1981/2437/1754 +f 423/2432/1749 1981/2437/1754 1378/2433/1750 +f 424/2438/1755 1381/2439/1756 1958/2440/1757 +f 424/2438/1755 1958/2440/1757 1379/2441/1758 +f 424/2442/1755 1379/2443/1758 1956/2444/1734 +f 424/2442/1755 1956/2444/1734 1371/2445/1738 +f 424/2438/1755 1371/2411/1738 1922/2269/1647 +f 424/2438/1755 1922/2269/1647 1380/2446/1759 +f 424/2438/1755 1380/2446/1759 1982/2447/1760 +f 424/2438/1755 1982/2447/1760 1381/2439/1756 +f 425/2448/1761 1376/2434/1751 1955/2400/1729 +f 425/2448/1761 1955/2400/1729 1365/2399/1728 +f 425/2448/1761 1365/2399/1728 1937/2377/1715 +f 425/2448/1761 1937/2377/1715 1382/2449/1762 +f 425/2448/1761 1382/2449/1762 1967/2450/1763 +f 425/2448/1761 1967/2450/1763 1383/2451/1764 +f 425/2448/1761 1383/2451/1764 1957/2435/1752 +f 425/2448/1761 1957/2435/1752 1376/2434/1751 +f 426/2452/1765 1385/2453/1766 1968/2454/1767 +f 426/2452/1765 1968/2454/1767 1384/2455/1768 +f 426/2456/1765 1384/2457/1768 1938/2458/1723 +f 426/2456/1765 1938/2458/1723 1369/2459/1731 +f 426/2456/1765 1369/2459/1731 1956/2444/1734 +f 426/2456/1765 1956/2444/1734 1379/2443/1758 +f 426/2452/1765 1379/2441/1758 1958/2440/1757 +f 426/2452/1765 1958/2440/1757 1385/2453/1766 +f 427/2460/1769 1382/2449/1762 1937/2377/1715 +f 427/2460/1769 1937/2377/1715 1360/2376/1714 +f 427/2460/1769 1360/2376/1714 1939/2382/1703 +f 427/2460/1769 1939/2382/1703 1386/2461/1770 +f 427/2460/1769 1386/2461/1770 1965/2462/1771 +f 427/2460/1769 1965/2462/1771 1387/2463/1772 +f 427/2460/1769 1387/2463/1772 1967/2450/1763 +f 427/2460/1769 1967/2450/1763 1382/2449/1762 +f 428/2464/1773 1389/2465/1774 1966/2466/1775 +f 428/2464/1773 1966/2466/1775 1388/2467/1776 +f 428/2468/1773 1388/2469/1776 1940/2394/1710 +f 428/2468/1773 1940/2394/1710 1363/2393/1724 +f 428/2468/1773 1363/2393/1724 1938/2458/1723 +f 428/2468/1773 1938/2458/1723 1384/2457/1768 +f 428/2464/1773 1384/2455/1768 1968/2454/1767 +f 428/2464/1773 1968/2454/1767 1389/2465/1774 +f 429/2470/1777 1386/2471/1770 1939/2358/1703 +f 429/2470/1777 1939/2358/1703 1354/2357/1702 +f 429/2470/1777 1354/2357/1702 1941/2364/1706 +f 429/2470/1777 1941/2364/1706 1390/2472/1778 +f 429/2470/1777 1390/2472/1778 1963/2473/1779 +f 429/2470/1777 1963/2473/1779 1391/2474/1780 +f 429/2470/1777 1391/2474/1780 1965/2475/1771 +f 429/2470/1777 1965/2475/1771 1386/2471/1770 +f 430/2476/1781 1393/2477/1782 1964/2478/1783 +f 430/2476/1781 1964/2478/1783 1392/2479/1784 +f 430/2476/1781 1392/2479/1784 1942/2373/1712 +f 430/2476/1781 1942/2373/1712 1356/2372/1711 +f 430/2476/1781 1356/2372/1711 1940/2371/1710 +f 430/2476/1781 1940/2371/1710 1388/2480/1776 +f 430/2476/1781 1388/2480/1776 1966/2481/1775 +f 430/2476/1781 1966/2481/1775 1393/2477/1782 +f 431/2482/1785 1390/2472/1778 1941/2364/1706 +f 431/2482/1785 1941/2364/1706 1353/2363/1705 +f 431/2483/1785 1353/2484/1705 1943/2337/1691 +f 431/2483/1785 1943/2337/1691 1394/2485/1786 +f 431/2483/1785 1394/2485/1786 1961/2486/1787 +f 431/2483/1785 1961/2486/1787 1395/2487/1788 +f 431/2482/1785 1395/2488/1788 1963/2473/1779 +f 431/2482/1785 1963/2473/1779 1390/2472/1778 +f 432/2489/1789 1397/2490/1790 1962/2491/1791 +f 432/2489/1789 1962/2491/1791 1396/2492/1792 +f 432/2489/1789 1396/2492/1792 1944/2493/1699 +f 432/2489/1789 1944/2493/1699 1357/2374/1708 +f 432/2489/1789 1357/2374/1708 1942/2373/1712 +f 432/2489/1789 1942/2373/1712 1392/2479/1784 +f 432/2489/1789 1392/2479/1784 1964/2478/1783 +f 432/2489/1789 1964/2478/1783 1397/2490/1790 +f 433/2494/1793 1394/2485/1786 1943/2337/1691 +f 433/2494/1793 1943/2337/1691 1348/2336/1690 +f 433/2494/1793 1348/2336/1690 1945/2495/1683 +f 433/2494/1793 1945/2495/1683 1398/2496/1794 +f 433/2497/1793 1398/2498/1794 1959/2499/1795 +f 433/2497/1793 1959/2499/1795 1399/2500/1796 +f 433/2494/1793 1399/2501/1796 1961/2486/1787 +f 433/2494/1793 1961/2486/1787 1394/2485/1786 +f 434/2502/1797 1401/2503/1798 1960/2504/1799 +f 434/2502/1797 1960/2504/1799 1400/2505/1800 +f 434/2502/1797 1400/2505/1800 1946/2355/1687 +f 434/2502/1797 1946/2355/1687 1351/2354/1700 +f 434/2506/1797 1351/2353/1700 1944/2352/1699 +f 434/2506/1797 1944/2352/1699 1396/2507/1792 +f 434/2506/1797 1396/2507/1792 1962/2508/1791 +f 434/2506/1797 1962/2508/1791 1401/2509/1798 +f 435/2510/1801 1398/2498/1794 1945/2325/1683 +f 435/2510/1801 1945/2325/1683 1342/2324/1682 +f 435/2510/1801 1342/2324/1682 1923/2192/1601 +f 435/2510/1801 1923/2192/1601 1402/2511/1802 +f 435/2510/1801 1402/2511/1802 1979/2512/1803 +f 435/2510/1801 1979/2512/1803 1403/2513/1804 +f 435/2510/1801 1403/2513/1804 1959/2499/1795 +f 435/2510/1801 1959/2499/1795 1398/2498/1794 +f 436/2514/1805 1405/2515/1806 1980/2516/1807 +f 436/2514/1805 1980/2516/1807 1404/2517/1808 +f 436/2514/1805 1404/2517/1808 1924/2197/1605 +f 436/2514/1805 1924/2197/1605 1344/2331/1688 +f 436/2514/1805 1344/2331/1688 1946/2330/1687 +f 436/2514/1805 1946/2330/1687 1400/2518/1800 +f 436/2514/1805 1400/2518/1800 1960/2519/1799 +f 436/2514/1805 1960/2519/1799 1405/2515/1806 +f 437/2520/1809 1407/2521/1810 1933/2253/1635 +f 437/2520/1809 1933/2253/1635 1321/2266/1644 +f 437/2520/1809 1321/2266/1644 1921/2265/1643 +f 437/2520/1809 1921/2265/1643 1378/2433/1750 +f 437/2520/1809 1378/2433/1750 1981/2437/1754 +f 437/2520/1809 1981/2437/1754 1406/2522/1811 +f 437/2520/1809 1406/2522/1811 1969/2523/1812 +f 437/2520/1809 1969/2523/1812 1407/2521/1810 +f 438/2524/1813 1409/2525/1814 1982/2447/1760 +f 438/2524/1813 1982/2447/1760 1380/2446/1759 +f 438/2524/1813 1380/2446/1759 1922/2269/1647 +f 438/2524/1813 1922/2269/1647 1323/2268/1646 +f 438/2524/1813 1323/2268/1646 1934/2259/1639 +f 438/2524/1813 1934/2259/1639 1408/2526/1815 +f 438/2524/1813 1408/2526/1815 1970/2527/1816 +f 438/2524/1813 1970/2527/1816 1409/2525/1814 +f 439/2528/1817 1411/2529/1818 1931/2240/1627 +f 439/2528/1817 1931/2240/1627 1317/2256/1636 +f 439/2530/1817 1317/2254/1636 1933/2253/1635 +f 439/2530/1817 1933/2253/1635 1407/2521/1810 +f 439/2530/1817 1407/2521/1810 1969/2523/1812 +f 439/2530/1817 1969/2523/1812 1410/2531/1819 +f 439/2528/1817 1410/2532/1819 1971/2533/1820 +f 439/2528/1817 1971/2533/1820 1411/2529/1818 +f 440/2534/1821 1413/2535/1822 1970/2527/1816 +f 440/2534/1821 1970/2527/1816 1408/2526/1815 +f 440/2534/1821 1408/2526/1815 1934/2259/1639 +f 440/2534/1821 1934/2259/1639 1318/2258/1638 +f 440/2534/1821 1318/2258/1638 1932/2247/1631 +f 440/2534/1821 1932/2247/1631 1412/2536/1823 +f 440/2534/1821 1412/2536/1823 1972/2537/1824 +f 440/2534/1821 1972/2537/1824 1413/2535/1822 +f 441/2538/1825 1415/2539/1826 1929/2540/1619 +f 441/2538/1825 1929/2540/1619 1313/2541/1628 +f 441/2538/1825 1313/2541/1628 1931/2542/1627 +f 441/2538/1825 1931/2542/1627 1411/2543/1818 +f 441/2544/1825 1411/2529/1818 1971/2533/1820 +f 441/2544/1825 1971/2533/1820 1414/2545/1827 +f 441/2544/1825 1414/2545/1827 1973/2546/1828 +f 441/2544/1825 1973/2546/1828 1415/2547/1826 +f 442/2548/1829 1417/2549/1830 1972/2537/1824 +f 442/2548/1829 1972/2537/1824 1412/2536/1823 +f 442/2550/1829 1412/2551/1823 1932/2552/1631 +f 442/2550/1829 1932/2552/1631 1314/2244/1630 +f 442/2550/1829 1314/2244/1630 1930/2233/1623 +f 442/2550/1829 1930/2233/1623 1416/2553/1831 +f 442/2548/1829 1416/2554/1831 1974/2555/1832 +f 442/2548/1829 1974/2555/1832 1417/2549/1830 +f 443/2556/1833 1419/2557/1834 1927/2212/1611 +f 443/2556/1833 1927/2212/1611 1309/2227/1620 +f 443/2556/1833 1309/2227/1620 1929/2226/1619 +f 443/2556/1833 1929/2226/1619 1415/2558/1826 +f 443/2559/1833 1415/2547/1826 1973/2546/1828 +f 443/2559/1833 1973/2546/1828 1418/2560/1835 +f 443/2559/1833 1418/2560/1835 1975/2561/1836 +f 443/2559/1833 1975/2561/1836 1419/2562/1834 +f 444/2563/1837 1421/2564/1838 1974/2555/1832 +f 444/2563/1837 1974/2555/1832 1416/2554/1831 +f 444/2565/1837 1416/2553/1831 1930/2233/1623 +f 444/2565/1837 1930/2233/1623 1310/2232/1622 +f 444/2566/1837 1310/2567/1622 1928/2568/1615 +f 444/2566/1837 1928/2568/1615 1420/2569/1839 +f 444/2563/1837 1420/2570/1839 1976/2571/1840 +f 444/2563/1837 1976/2571/1840 1421/2564/1838 +f 445/2572/1841 1423/2573/1842 1925/2574/1599 +f 445/2572/1841 1925/2574/1599 1305/2213/1612 +f 445/2572/1841 1305/2213/1612 1927/2212/1611 +f 445/2572/1841 1927/2212/1611 1419/2557/1834 +f 445/2575/1841 1419/2576/1834 1975/2577/1836 +f 445/2575/1841 1975/2577/1836 1422/2578/1843 +f 445/2575/1841 1422/2578/1843 1977/2579/1844 +f 445/2575/1841 1977/2579/1844 1423/2580/1842 +f 446/2581/1845 1425/2582/1846 1976/2583/1840 +f 446/2581/1845 1976/2583/1840 1420/2584/1839 +f 446/2581/1845 1420/2584/1839 1928/2217/1615 +f 446/2581/1845 1928/2217/1615 1306/2216/1614 +f 446/2581/1845 1306/2216/1614 1926/2199/1607 +f 446/2581/1845 1926/2199/1607 1424/2585/1847 +f 446/2581/1845 1424/2585/1847 1978/2586/1848 +f 446/2581/1845 1978/2586/1848 1425/2582/1846 +f 447/2587/1849 1402/2511/1802 1923/2192/1601 +f 447/2587/1849 1923/2192/1601 1299/2191/1600 +f 447/2587/1849 1299/2191/1600 1925/2190/1599 +f 447/2587/1849 1925/2190/1599 1423/2580/1842 +f 447/2587/1849 1423/2580/1842 1977/2579/1844 +f 447/2587/1849 1977/2579/1844 1426/2588/1850 +f 447/2587/1849 1426/2588/1850 1979/2512/1803 +f 447/2587/1849 1979/2512/1803 1402/2511/1802 +f 448/2589/1851 1427/2590/1852 1978/2586/1848 +f 448/2589/1851 1978/2586/1848 1424/2585/1847 +f 448/2589/1851 1424/2585/1847 1926/2199/1607 +f 448/2589/1851 1926/2199/1607 1301/2198/1606 +f 448/2589/1851 1301/2198/1606 1924/2197/1605 +f 448/2589/1851 1924/2197/1605 1404/2517/1808 +f 448/2589/1851 1404/2517/1808 1980/2516/1807 +f 448/2589/1851 1980/2516/1807 1427/2590/1852 +f 449/2591/1853 1430/2592/1854 1963/2593/1779 +f 449/2591/1853 1963/2593/1779 1395/2594/1788 +f 449/2591/1853 1395/2594/1788 1961/2595/1787 +f 449/2591/1853 1961/2595/1787 1428/2596/1855 +f 449/2591/1853 1428/2596/1855 1985/2597/1856 +f 449/2591/1853 1985/2597/1856 1429/2598/1857 +f 449/2591/1853 1429/2598/1857 1983/2599/1858 +f 449/2591/1853 1983/2599/1858 1430/2592/1854 +f 450/2600/1859 1433/2601/1860 1986/2602/1861 +f 450/2600/1859 1986/2602/1861 1431/2603/1862 +f 450/2600/1859 1431/2603/1862 1962/2508/1791 +f 450/2600/1859 1962/2508/1791 1397/2604/1790 +f 450/2600/1859 1397/2604/1790 1964/2605/1783 +f 450/2600/1859 1964/2605/1783 1432/2606/1863 +f 450/2600/1859 1432/2606/1863 1984/2607/1864 +f 450/2600/1859 1984/2607/1864 1433/2601/1860 +f 451/2608/1865 1436/2609/1866 1983/2599/1858 +f 451/2608/1865 1983/2599/1858 1429/2598/1857 +f 451/2608/1865 1429/2598/1857 1985/2597/1856 +f 451/2608/1865 1985/2597/1856 1434/2610/1867 +f 451/2608/1865 1434/2610/1867 1987/2611/1868 +f 451/2608/1865 1987/2611/1868 1435/2612/1869 +f 451/2608/1865 1435/2612/1869 1989/2613/1870 +f 451/2608/1865 1989/2613/1870 1436/2609/1866 +f 452/2614/1871 1439/2615/1872 1988/2616/1873 +f 452/2614/1871 1988/2616/1873 1437/2617/1874 +f 452/2614/1871 1437/2617/1874 1986/2602/1861 +f 452/2614/1871 1986/2602/1861 1433/2601/1860 +f 452/2614/1871 1433/2601/1860 1984/2607/1864 +f 452/2614/1871 1984/2607/1864 1438/2618/1875 +f 452/2614/1871 1438/2618/1875 1990/2619/1876 +f 452/2614/1871 1990/2619/1876 1439/2615/1872 +f 453/2620/1877 1442/2621/1878 1989/2613/1870 +f 453/2620/1877 1989/2613/1870 1435/2612/1869 +f 453/2620/1877 1435/2612/1869 1987/2611/1868 +f 453/2620/1877 1987/2611/1868 1440/2622/1879 +f 453/2620/1877 1440/2622/1879 1993/2623/1880 +f 453/2620/1877 1993/2623/1880 1441/2624/1881 +f 453/2620/1877 1441/2624/1881 1991/2625/1882 +f 453/2620/1877 1991/2625/1882 1442/2621/1878 +f 454/2626/1883 1445/2627/1884 1994/2628/1885 +f 454/2626/1883 1994/2628/1885 1443/2629/1886 +f 454/2626/1883 1443/2629/1886 1988/2616/1873 +f 454/2626/1883 1988/2616/1873 1439/2615/1872 +f 454/2626/1883 1439/2615/1872 1990/2619/1876 +f 454/2626/1883 1990/2619/1876 1444/2630/1887 +f 454/2626/1883 1444/2630/1887 1992/2631/1888 +f 454/2626/1883 1992/2631/1888 1445/2627/1884 +f 455/2632/1889 1448/2633/1890 1991/2625/1882 +f 455/2632/1889 1991/2625/1882 1441/2624/1881 +f 455/2632/1889 1441/2624/1881 1993/2623/1880 +f 455/2632/1889 1993/2623/1880 1446/2634/1891 +f 455/2632/1889 1446/2634/1891 1995/2635/1892 +f 455/2632/1889 1995/2635/1892 1447/2636/1893 +f 455/2632/1889 1447/2636/1893 1997/2637/1894 +f 455/2632/1889 1997/2637/1894 1448/2633/1890 +f 456/2638/1895 1451/2639/1896 1996/2640/1897 +f 456/2638/1895 1996/2640/1897 1449/2641/1898 +f 456/2638/1895 1449/2641/1898 1994/2628/1885 +f 456/2638/1895 1994/2628/1885 1445/2627/1884 +f 456/2638/1895 1445/2627/1884 1992/2631/1888 +f 456/2638/1895 1992/2631/1888 1450/2642/1899 +f 456/2638/1895 1450/2642/1899 1998/2643/1900 +f 456/2638/1895 1998/2643/1900 1451/2639/1896 +f 457/2644/1901 1453/2645/1902 1969/2646/1812 +f 457/2644/1901 1969/2646/1812 1406/2647/1811 +f 457/2648/1901 1406/2649/1811 1981/2650/1754 +f 457/2648/1901 1981/2650/1754 1452/2651/1903 +f 457/2648/1901 1452/2651/1903 1991/2625/1882 +f 457/2648/1901 1991/2625/1882 1448/2633/1890 +f 457/2644/1901 1448/2652/1890 1997/2653/1894 +f 457/2644/1901 1997/2653/1894 1453/2645/1902 +f 458/2654/1904 1450/2642/1899 1992/2631/1888 +f 458/2654/1904 1992/2631/1888 1454/2655/1905 +f 458/2654/1904 1454/2655/1905 1982/2447/1760 +f 458/2654/1904 1982/2447/1760 1409/2525/1814 +f 458/2654/1904 1409/2525/1814 1970/2527/1816 +f 458/2654/1904 1970/2527/1816 1455/2656/1906 +f 458/2654/1904 1455/2656/1906 1998/2643/1900 +f 458/2654/1904 1998/2643/1900 1450/2642/1899 +f 459/2657/1907 1377/2658/1753 1957/2659/1752 +f 459/2657/1907 1957/2659/1752 1456/2660/1908 +f 459/2657/1907 1456/2660/1908 1989/2613/1870 +f 459/2657/1907 1989/2613/1870 1442/2621/1878 +f 459/2657/1907 1442/2621/1878 1991/2625/1882 +f 459/2657/1907 1991/2625/1882 1452/2651/1903 +f 459/2657/1907 1452/2651/1903 1981/2650/1754 +f 459/2657/1907 1981/2650/1754 1377/2658/1753 +f 460/2661/1909 1454/2655/1905 1992/2631/1888 +f 460/2661/1909 1992/2631/1888 1444/2630/1887 +f 460/2661/1909 1444/2630/1887 1990/2619/1876 +f 460/2661/1909 1990/2619/1876 1457/2662/1910 +f 460/2661/1909 1457/2662/1910 1958/2440/1757 +f 460/2661/1909 1958/2440/1757 1381/2439/1756 +f 460/2661/1909 1381/2439/1756 1982/2447/1760 +f 460/2661/1909 1982/2447/1760 1454/2655/1905 +f 461/2663/1911 1456/2660/1908 1957/2659/1752 +f 461/2663/1911 1957/2659/1752 1383/2664/1764 +f 461/2663/1911 1383/2664/1764 1967/2665/1763 +f 461/2663/1911 1967/2665/1763 1458/2666/1912 +f 461/2663/1911 1458/2666/1912 1983/2599/1858 +f 461/2663/1911 1983/2599/1858 1436/2609/1866 +f 461/2663/1911 1436/2609/1866 1989/2613/1870 +f 461/2663/1911 1989/2613/1870 1456/2660/1908 +f 462/2667/1913 1438/2618/1875 1984/2607/1864 +f 462/2667/1913 1984/2607/1864 1459/2668/1914 +f 462/2667/1913 1459/2668/1914 1968/2454/1767 +f 462/2667/1913 1968/2454/1767 1385/2453/1766 +f 462/2667/1913 1385/2453/1766 1958/2440/1757 +f 462/2667/1913 1958/2440/1757 1457/2662/1910 +f 462/2667/1913 1457/2662/1910 1990/2619/1876 +f 462/2667/1913 1990/2619/1876 1438/2618/1875 +f 463/2669/1915 1391/2670/1780 1963/2593/1779 +f 463/2669/1915 1963/2593/1779 1430/2592/1854 +f 463/2669/1915 1430/2592/1854 1983/2599/1858 +f 463/2669/1915 1983/2599/1858 1458/2666/1912 +f 463/2669/1915 1458/2666/1912 1967/2665/1763 +f 463/2669/1915 1967/2665/1763 1387/2671/1772 +f 463/2669/1915 1387/2671/1772 1965/2672/1771 +f 463/2669/1915 1965/2672/1771 1391/2670/1780 +f 464/2673/1916 1389/2465/1774 1968/2454/1767 +f 464/2673/1916 1968/2454/1767 1459/2668/1914 +f 464/2673/1916 1459/2668/1914 1984/2607/1864 +f 464/2673/1916 1984/2607/1864 1432/2606/1863 +f 464/2673/1916 1432/2606/1863 1964/2605/1783 +f 464/2673/1916 1964/2605/1783 1393/2674/1782 +f 464/2673/1916 1393/2674/1782 1966/2466/1775 +f 464/2673/1916 1966/2466/1775 1389/2465/1774 +f 465/2675/1917 1399/2500/1796 1959/2499/1795 +f 465/2675/1917 1959/2499/1795 1403/2513/1804 +f 465/2675/1917 1403/2513/1804 1979/2512/1803 +f 465/2675/1917 1979/2512/1803 1460/2676/1918 +f 465/2675/1917 1460/2676/1918 1985/2677/1856 +f 465/2675/1917 1985/2677/1856 1428/2678/1855 +f 465/2679/1917 1428/2596/1855 1961/2595/1787 +f 465/2679/1917 1961/2595/1787 1399/2680/1796 +f 466/2681/1919 1431/2682/1862 1986/2683/1861 +f 466/2681/1919 1986/2683/1861 1461/2684/1920 +f 466/2681/1919 1461/2684/1920 1980/2516/1807 +f 466/2681/1919 1980/2516/1807 1405/2515/1806 +f 466/2681/1919 1405/2515/1806 1960/2519/1799 +f 466/2681/1919 1960/2519/1799 1401/2685/1798 +f 466/2686/1919 1401/2509/1798 1962/2508/1791 +f 466/2686/1919 1962/2508/1791 1431/2603/1862 +f 467/2687/1921 1426/2588/1850 1977/2579/1844 +f 467/2687/1921 1977/2579/1844 1462/2688/1922 +f 467/2689/1921 1462/2690/1922 1987/2611/1868 +f 467/2689/1921 1987/2611/1868 1434/2610/1867 +f 467/2687/1921 1434/2691/1867 1985/2677/1856 +f 467/2687/1921 1985/2677/1856 1460/2676/1918 +f 467/2687/1921 1460/2676/1918 1979/2512/1803 +f 467/2687/1921 1979/2512/1803 1426/2588/1850 +f 468/2692/1923 1461/2693/1920 1986/2602/1861 +f 468/2692/1923 1986/2602/1861 1437/2617/1874 +f 468/2692/1923 1437/2617/1874 1988/2616/1873 +f 468/2692/1923 1988/2616/1873 1463/2694/1924 +f 468/2692/1923 1463/2694/1924 1978/2695/1848 +f 468/2692/1923 1978/2695/1848 1427/2696/1852 +f 468/2692/1923 1427/2696/1852 1980/2697/1807 +f 468/2692/1923 1980/2697/1807 1461/2693/1920 +f 469/2698/1925 1422/2699/1843 1975/2561/1836 +f 469/2698/1925 1975/2561/1836 1464/2700/1926 +f 469/2698/1925 1464/2700/1926 1993/2623/1880 +f 469/2698/1925 1993/2623/1880 1440/2622/1879 +f 469/2698/1925 1440/2622/1879 1987/2611/1868 +f 469/2698/1925 1987/2611/1868 1462/2690/1922 +f 469/2698/1925 1462/2690/1922 1977/2701/1844 +f 469/2698/1925 1977/2701/1844 1422/2699/1843 +f 470/2702/1927 1463/2694/1924 1988/2616/1873 +f 470/2702/1927 1988/2616/1873 1443/2629/1886 +f 470/2702/1927 1443/2629/1886 1994/2628/1885 +f 470/2702/1927 1994/2628/1885 1465/2703/1928 +f 470/2702/1927 1465/2703/1928 1976/2571/1840 +f 470/2702/1927 1976/2571/1840 1425/2704/1846 +f 470/2702/1927 1425/2704/1846 1978/2695/1848 +f 470/2702/1927 1978/2695/1848 1463/2694/1924 +f 471/2705/1929 1418/2560/1835 1973/2546/1828 +f 471/2705/1929 1973/2546/1828 1466/2706/1930 +f 471/2705/1929 1466/2706/1930 1995/2635/1892 +f 471/2705/1929 1995/2635/1892 1446/2634/1891 +f 471/2705/1929 1446/2634/1891 1993/2623/1880 +f 471/2705/1929 1993/2623/1880 1464/2700/1926 +f 471/2705/1929 1464/2700/1926 1975/2561/1836 +f 471/2705/1929 1975/2561/1836 1418/2560/1835 +f 472/2707/1931 1465/2703/1928 1994/2628/1885 +f 472/2707/1931 1994/2628/1885 1449/2641/1898 +f 472/2707/1931 1449/2641/1898 1996/2640/1897 +f 472/2707/1931 1996/2640/1897 1467/2708/1932 +f 472/2707/1931 1467/2708/1932 1974/2555/1832 +f 472/2707/1931 1974/2555/1832 1421/2564/1838 +f 472/2707/1931 1421/2564/1838 1976/2571/1840 +f 472/2707/1931 1976/2571/1840 1465/2703/1928 +f 473/2709/1933 1414/2710/1827 1971/2711/1820 +f 473/2709/1933 1971/2711/1820 1468/2712/1934 +f 473/2709/1933 1468/2712/1934 1997/2653/1894 +f 473/2709/1933 1997/2653/1894 1447/2713/1893 +f 473/2709/1933 1447/2713/1893 1995/2714/1892 +f 473/2709/1933 1995/2714/1892 1466/2715/1930 +f 473/2709/1933 1466/2715/1930 1973/2716/1828 +f 473/2709/1933 1973/2716/1828 1414/2710/1827 +f 474/2717/1935 1467/2708/1932 1996/2640/1897 +f 474/2717/1935 1996/2640/1897 1451/2639/1896 +f 474/2717/1935 1451/2639/1896 1998/2643/1900 +f 474/2717/1935 1998/2643/1900 1469/2718/1936 +f 474/2717/1935 1469/2718/1936 1972/2537/1824 +f 474/2717/1935 1972/2537/1824 1417/2549/1830 +f 474/2717/1935 1417/2549/1830 1974/2555/1832 +f 474/2717/1935 1974/2555/1832 1467/2708/1932 +f 475/2719/1937 1410/2720/1819 1969/2646/1812 +f 475/2719/1937 1969/2646/1812 1453/2645/1902 +f 475/2719/1937 1453/2645/1902 1997/2653/1894 +f 475/2719/1937 1997/2653/1894 1468/2712/1934 +f 475/2719/1937 1468/2712/1934 1971/2711/1820 +f 475/2719/1937 1971/2711/1820 1410/2720/1819 +f 476/2721/1938 1413/2535/1822 1972/2537/1824 +f 476/2721/1938 1972/2537/1824 1469/2718/1936 +f 476/2721/1938 1469/2718/1936 1998/2643/1900 +f 476/2721/1938 1998/2643/1900 1455/2656/1906 +f 476/2721/1938 1455/2656/1906 1970/2527/1816 +f 476/2721/1938 1970/2527/1816 1413/2535/1822 +f 477/2722/1939 1472/2723/1940 1897/2724/1578 +f 477/2722/1939 1897/2724/1578 1294/2725/1586 +f 477/2722/1939 1294/2725/1586 1895/2726/1590 +f 477/2722/1939 1895/2726/1590 1470/2727/1941 +f 477/2722/1939 1470/2727/1941 2009/2728/1942 +f 477/2722/1939 2009/2728/1942 1471/2729/1943 +f 477/2722/1939 1471/2729/1943 2007/2730/1944 +f 477/2722/1939 2007/2730/1944 1472/2723/1940 +f 478/2731/1945 1475/2732/1946 2010/2733/1947 +f 478/2731/1945 2010/2733/1947 1473/2734/1948 +f 478/2731/1945 1473/2734/1948 1896/2735/1596 +f 478/2731/1945 1896/2735/1596 1296/2736/1595 +f 478/2731/1945 1296/2736/1595 1898/2737/1584 +f 478/2731/1945 1898/2737/1584 1474/2738/1949 +f 478/2731/1945 1474/2738/1949 2008/2739/1950 +f 478/2731/1945 2008/2739/1950 1475/2732/1946 +f 479/2740/1951 1477/2741/1952 1899/2155/1566 +f 479/2740/1951 1899/2155/1566 1288/2154/1574 +f 479/2740/1951 1288/2154/1574 1897/2164/1578 +f 479/2740/1951 1897/2164/1578 1472/2742/1940 +f 479/2743/1951 1472/2723/1940 2007/2730/1944 +f 479/2743/1951 2007/2730/1944 1476/2744/1953 +f 479/2743/1951 1476/2744/1953 2005/2745/1954 +f 479/2743/1951 2005/2745/1954 1477/2746/1952 +f 480/2747/1955 1479/2748/1956 2008/2739/1950 +f 480/2747/1955 2008/2739/1950 1474/2738/1949 +f 480/2747/1955 1474/2738/1949 1898/2737/1584 +f 480/2747/1955 1898/2737/1584 1290/2749/1583 +f 480/2747/1955 1290/2749/1583 1900/2750/1572 +f 480/2747/1955 1900/2750/1572 1478/2751/1957 +f 480/2747/1955 1478/2751/1957 2006/2752/1958 +f 480/2747/1955 2006/2752/1958 1479/2748/1956 +f 481/2753/1959 1481/2754/1960 1901/2755/1554 +f 481/2753/1959 1901/2755/1554 1282/2756/1562 +f 481/2753/1959 1282/2756/1562 1899/2155/1566 +f 481/2753/1959 1899/2155/1566 1477/2741/1952 +f 481/2753/1959 1477/2741/1952 2005/2757/1954 +f 481/2753/1959 2005/2757/1954 1480/2758/1961 +f 481/2753/1959 1480/2758/1961 2003/2759/1962 +f 481/2753/1959 2003/2759/1962 1481/2754/1960 +f 482/2760/1963 1483/2761/1964 2006/2752/1958 +f 482/2760/1963 2006/2752/1958 1478/2751/1957 +f 482/2762/1963 1478/2763/1957 1900/2152/1572 +f 482/2762/1963 1900/2152/1572 1284/2151/1571 +f 482/2764/1963 1284/2765/1571 1902/2766/1560 +f 482/2764/1963 1902/2766/1560 1482/2767/1965 +f 482/2764/1963 1482/2767/1965 2004/2768/1966 +f 482/2764/1963 2004/2768/1966 1483/2769/1964 +f 483/2770/1967 1485/2771/1968 1903/2114/1542 +f 483/2770/1967 1903/2114/1542 1276/2126/1550 +f 483/2770/1967 1276/2126/1550 1901/2132/1554 +f 483/2770/1967 1901/2132/1554 1481/2772/1960 +f 483/2773/1967 1481/2754/1960 2003/2759/1962 +f 483/2773/1967 2003/2759/1962 1484/2774/1969 +f 483/2775/1967 1484/2776/1969 2001/2777/1970 +f 483/2775/1967 2001/2777/1970 1485/2778/1968 +f 484/2779/1971 1487/2780/1972 2004/2768/1966 +f 484/2779/1971 2004/2768/1966 1482/2767/1965 +f 484/2779/1971 1482/2767/1965 1902/2766/1560 +f 484/2779/1971 1902/2766/1560 1278/2781/1559 +f 484/2779/1971 1278/2781/1559 1904/2782/1548 +f 484/2779/1971 1904/2782/1548 1486/2783/1973 +f 484/2784/1971 1486/2785/1973 2002/2786/1974 +f 484/2784/1971 2002/2786/1974 1487/2787/1972 +f 485/2788/1975 1489/2789/1976 1905/2790/1521 +f 485/2788/1975 1905/2790/1521 1270/2791/1538 +f 485/2792/1975 1270/2108/1538 1903/2114/1542 +f 485/2792/1975 1903/2114/1542 1485/2771/1968 +f 485/2793/1975 1485/2778/1968 2001/2777/1970 +f 485/2793/1975 2001/2777/1970 1488/2794/1977 +f 485/2793/1975 1488/2794/1977 1999/2795/1978 +f 485/2793/1975 1999/2795/1978 1489/2796/1976 +f 486/2797/1979 1491/2798/1980 2002/2786/1974 +f 486/2797/1979 2002/2786/1974 1486/2785/1973 +f 486/2799/1979 1486/2800/1973 1904/2801/1548 +f 486/2799/1979 1904/2801/1548 1272/2120/1547 +f 486/2799/1979 1272/2120/1547 1906/2103/1534 +f 486/2799/1979 1906/2103/1534 1490/2802/1981 +f 486/2797/1979 1490/2803/1981 2000/2804/1982 +f 486/2797/1979 2000/2804/1982 1491/2798/1980 +f 487/2805/1983 1493/2806/1984 1893/2312/1523 +f 487/2805/1983 1893/2312/1523 1260/2807/1522 +f 487/2805/1983 1260/2807/1522 1905/2790/1521 +f 487/2805/1983 1905/2790/1521 1489/2789/1976 +f 487/2808/1983 1489/2796/1976 1999/2795/1978 +f 487/2808/1983 1999/2795/1978 1492/2809/1985 +f 487/2805/1983 1492/2810/1985 2011/2811/1986 +f 487/2805/1983 2011/2811/1986 1493/2806/1984 +f 488/2812/1987 1495/2813/1988 2000/2804/1982 +f 488/2812/1987 2000/2804/1982 1490/2803/1981 +f 488/2814/1987 1490/2802/1981 1906/2103/1534 +f 488/2814/1987 1906/2103/1534 1265/2102/1533 +f 488/2814/1987 1265/2102/1533 1894/2101/1532 +f 488/2814/1987 1894/2101/1532 1494/2815/1989 +f 488/2812/1987 1494/2816/1989 2012/2817/1990 +f 488/2812/1987 2012/2817/1990 1495/2813/1988 +f 489/2818/1991 1492/2809/1985 1999/2795/1978 +f 489/2818/1991 1999/2795/1978 1496/2819/1992 +f 489/2818/1991 1496/2819/1992 2007/2820/1944 +f 489/2818/1991 2007/2820/1944 1471/2821/1943 +f 489/2818/1991 1471/2821/1943 2009/2822/1942 +f 489/2818/1991 2009/2822/1942 1497/2823/1993 +f 489/2818/1991 1497/2823/1993 2011/2824/1986 +f 489/2818/1991 2011/2824/1986 1492/2809/1985 +f 490/2825/1994 1499/2826/1995 2010/2827/1947 +f 490/2825/1994 2010/2827/1947 1475/2828/1946 +f 490/2825/1994 1475/2828/1946 2008/2829/1950 +f 490/2825/1994 2008/2829/1950 1498/2830/1996 +f 490/2825/1994 1498/2830/1996 2000/2804/1982 +f 490/2825/1994 2000/2804/1982 1495/2813/1988 +f 490/2825/1994 1495/2813/1988 2012/2817/1990 +f 490/2825/1994 2012/2817/1990 1499/2826/1995 +f 491/2831/1997 1496/2819/1992 1999/2795/1978 +f 491/2831/1997 1999/2795/1978 1488/2794/1977 +f 491/2831/1997 1488/2794/1977 2001/2777/1970 +f 491/2831/1997 2001/2777/1970 1500/2832/1998 +f 491/2831/1997 1500/2832/1998 2005/2833/1954 +f 491/2831/1997 2005/2833/1954 1476/2834/1953 +f 491/2831/1997 1476/2834/1953 2007/2820/1944 +f 491/2831/1997 2007/2820/1944 1496/2819/1992 +f 492/2835/1999 1479/2836/1956 2006/2837/1958 +f 492/2835/1999 2006/2837/1958 1501/2838/2000 +f 492/2835/1999 1501/2838/2000 2002/2786/1974 +f 492/2835/1999 2002/2786/1974 1491/2798/1980 +f 492/2835/1999 1491/2798/1980 2000/2804/1982 +f 492/2835/1999 2000/2804/1982 1498/2830/1996 +f 492/2835/1999 1498/2830/1996 2008/2829/1950 +f 492/2835/1999 2008/2829/1950 1479/2836/1956 +f 493/2839/2001 1500/2832/1998 2001/2777/1970 +f 493/2839/2001 2001/2777/1970 1484/2776/1969 +f 493/2839/2001 1484/2776/1969 2003/2840/1962 +f 493/2839/2001 2003/2840/1962 1480/2841/1961 +f 493/2839/2001 1480/2841/1961 2005/2833/1954 +f 493/2839/2001 2005/2833/1954 1500/2832/1998 +f 494/2842/2002 1501/2838/2000 2006/2837/1958 +f 494/2842/2002 2006/2837/1958 1483/2843/1964 +f 494/2844/2002 1483/2769/1964 2004/2768/1966 +f 494/2844/2002 2004/2768/1966 1487/2780/1972 +f 494/2842/2002 1487/2787/1972 2002/2786/1974 +f 494/2842/2002 2002/2786/1974 1501/2838/2000 +f 495/2845/2003 1502/2846/2004 1819/1882/1378 +f 495/2845/2003 1819/1882/1378 1226/2847/1452 +f 495/2848/2003 1226/2849/1452 1887/1219/1445 +f 495/2848/2003 1887/1219/1445 1338/2313/1672 +f 495/2848/2003 1338/2313/1672 1893/2312/1523 +f 495/2848/2003 1893/2312/1523 1493/2806/1984 +f 495/2848/2003 1493/2806/1984 2011/2811/1986 +f 495/2848/2003 2011/2811/1986 1502/2850/2004 +f 496/2851/2005 1494/2815/1989 1894/2101/1532 +f 496/2851/2005 1894/2101/1532 1341/2321/1680 +f 496/2851/2005 1341/2321/1680 1888/2320/1449 +f 496/2851/2005 1888/2320/1449 1227/2852/1454 +f 496/2853/2005 1227/2854/1454 1820/2855/1386 +f 496/2853/2005 1820/2855/1386 1503/2856/2006 +f 496/2853/2005 1503/2856/2006 2012/2817/1990 +f 496/2853/2005 2012/2817/1990 1494/2816/1989 +f 497/2857/2007 1246/2039/1492 1819/1882/1378 +f 497/2857/2007 1819/1882/1378 1502/2846/2004 +f 497/2857/2007 1502/2846/2004 2011/2824/1986 +f 497/2857/2007 2011/2824/1986 1497/2823/1993 +f 497/2857/2007 1497/2823/1993 2009/2822/1942 +f 497/2857/2007 2009/2822/1942 1504/2858/2008 +f 497/2857/2007 1504/2858/2008 1827/2040/1493 +f 497/2857/2007 1827/2040/1493 1246/2039/1492 +f 498/2859/2009 1505/2860/2010 2010/2827/1947 +f 498/2859/2009 2010/2827/1947 1499/2826/1995 +f 498/2859/2009 1499/2826/1995 2012/2817/1990 +f 498/2859/2009 2012/2817/1990 1503/2856/2006 +f 498/2861/2009 1503/2862/2006 1820/2050/1386 +f 498/2861/2009 1820/2050/1386 1250/2049/1502 +f 498/2861/2009 1250/2049/1502 1828/2048/1501 +f 498/2861/2009 1828/2048/1501 1505/2863/2010 +f 499/2864/2011 1330/2865/1662 1825/2061/1471 +f 499/2864/2011 1825/2061/1471 1255/2060/1508 +f 499/2864/2011 1255/2060/1508 1827/2068/1493 +f 499/2864/2011 1827/2068/1493 1504/2866/2008 +f 499/2864/2011 1504/2866/2008 2009/2728/1942 +f 499/2864/2011 2009/2728/1942 1470/2727/1941 +f 499/2864/2011 1470/2727/1941 1895/2726/1590 +f 499/2864/2011 1895/2726/1590 1330/2865/1662 +f 500/2867/2012 1473/2734/1948 2010/2733/1947 +f 500/2867/2012 2010/2733/1947 1505/2863/2010 +f 500/2867/2012 1505/2863/2010 1828/2048/1501 +f 500/2867/2012 1828/2048/1501 1257/2071/1512 +f 500/2867/2012 1257/2071/1512 1826/2015/1476 +f 500/2867/2012 1826/2015/1476 1332/2868/1666 +f 500/2867/2012 1332/2868/1666 1896/2735/1596 +f 500/2867/2012 1896/2735/1596 1473/2734/1948 diff --git a/plugins/guacamole-oculus/example-oculus/data/textures/stones_diffuse.jpg b/plugins/guacamole-oculus/example-oculus/data/textures/stones_diffuse.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ad60085eefb11b533f2e89ee438cf78f72a79415 GIT binary patch literal 158843 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 literal 0 HcmV?d00001 diff --git a/plugins/guacamole-oculus/example-oculus/data/textures/stones_normal.jpg b/plugins/guacamole-oculus/example-oculus/data/textures/stones_normal.jpg new file mode 100644 index 0000000000000000000000000000000000000000..44d5ca93fe63c19422164d4d43fae0c1ed3caf58 GIT binary patch literal 196678 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�%1z?GiT*t=f~Y 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 literal 0 HcmV?d00001 diff --git a/plugins/guacamole-oculus/example-oculus/main.cpp b/plugins/guacamole-oculus/example-oculus/main.cpp new file mode 100644 index 000000000..bef0b4c65 --- /dev/null +++ b/plugins/guacamole-oculus/example-oculus/main.cpp @@ -0,0 +1,257 @@ +/****************************************************************************** + * guacamole - delicious VR * + * * + * Copyright: (c) 2011-2013 Bauhaus-Universität Weimar * + * Contact: felix.lauer@uni-weimar.de / simon.schneegans@uni-weimar.de * + * * + * This program is free software: you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the Free * + * Software Foundation, either version 3 of the License, or (at your option) * + * any later version. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * + * for more details. * + * * + * You should have received a copy of the GNU General Public License along * + * with this program. If not, see . * + * * + ******************************************************************************/ + +#include +#include + +const std::string geometry("data/objects/monkey.obj"); +// const std::string geometry("data/objects/cube.obj"); + +std::vector> add_lights(gua::SceneGraph& graph, + int count) { + + std::vector> lights(count); + + for (int i(0); i < count; ++i) { + scm::math::vec3 randdir(gua::math::random::get(-1.f, 1.f), + gua::math::random::get(-1.f, 1.f), + gua::math::random::get(-1.f, 1.f)); + scm::math::normalize(randdir); + + gua::GeometryLoader loader; + auto sphere_geometry( + loader.create_geometry_from_file( + "sphere" + gua::string_utils::to_string(i), + "data/objects/light_sphere.obj", + "White" + )); + + sphere_geometry->scale(0.04, 0.04, 0.04); + + lights[i] = graph.add_node("/", std::make_shared("light" + gua::string_utils::to_string(i))); + lights[i]->add_child(sphere_geometry); + lights[i]->translate(randdir[0], randdir[1], randdir[2]); + + auto light = lights[i]->add_child(std::make_shared("light")); + light->data.set_color(gua::utils::Color3f::random()); + } + + return lights; +} + +void setup_scene(gua::SceneGraph& graph, + std::shared_ptr const& root_monkey, + int depth_count) { + + if (depth_count > 0) { + gua::GeometryLoader loader; + + float offset(2.f); + std::vector directions = { + gua::math::vec3(0, offset, 0), + gua::math::vec3(0, -offset, 0), + gua::math::vec3(offset, 0, 0), + gua::math::vec3(-offset, 0, 0), + gua::math::vec3(0, 0, offset), + gua::math::vec3(0, 0, -offset) + }; + + for (auto direction: directions) { + auto monkey_geometry(loader.create_geometry_from_file( + "monkey", + geometry, + "Stones" + )); + + auto monkey = root_monkey->add_child(monkey_geometry); + monkey->scale(0.5, 0.5, 0.5); + monkey->translate(direction[0], direction[1], direction[2]); + + setup_scene(graph, monkey, depth_count - 1); + } + } +} + +int main(int argc, char** argv) { + + // initialize guacamole + gua::init(argc, argv); + gua::OculusRift::init(); + + gua::ShadingModelDatabase::load_shading_models_from("data/materials/"); + gua::MaterialDatabase::load_materials_from("data/materials/"); + + // setup scene + gua::SceneGraph graph("main_scenegraph"); + + gua::GeometryLoader loader; + + auto monkey_geometry(loader.create_geometry_from_file( + "root_ape", + geometry, + "Stones" + )); + + auto root_monkey = graph.add_node("/", monkey_geometry); + root_monkey->scale(0.5, 0.5, 0.5); + + // depth monkey cube car + // 1 14.084 56 3.619.000 Vertices / 7 draw calls + // 2 74.444 296 19.129.000 Vertices / 37 draw calls + // 3 436.604 1.736 112.189.000 Vertices / 217 draw calls + // 4 2.609.564 10.376 Vertices / 1.297 draw calls + // 5 15.647.324 62.216 Vertices / 7.777 draw calls + // 6 93.873.884 373.256 Vertices / 46.657 draw calls + setup_scene(graph, root_monkey, 4); + + auto lights = add_lights(graph, 50); + + auto pos = graph.add_node("/", "pos"); + pos->translate(0, 0, 2); + auto nav1 = graph.add_node("/pos", "nav1"); + auto nav2 = graph.add_node("/pos", "nav2"); + + // hmd1 + auto screen = graph.add_node("/pos/nav1", "screen_l"); + screen->data.set_size(gua::math::vec2(0.08, 0.1)); + screen->translate(-0.04, 0, -0.05f); + + screen = graph.add_node("/pos/nav1", "screen_r"); + screen->data.set_size(gua::math::vec2(0.08, 0.1)); + screen->translate(0.04, 0, -0.05f); + + // hmd2 + screen = graph.add_node("/pos/nav2", "screen_l"); + screen->data.set_size(gua::math::vec2(0.08, 0.1)); + screen->translate(-0.04, 0, -0.05f); + + screen = graph.add_node("/pos/nav2", "screen_r"); + screen->data.set_size(gua::math::vec2(0.08, 0.1)); + screen->translate(0.04, 0, -0.05f); + + // eye1 + auto eye = graph.add_node("/pos/nav1", "eye_l"); + eye->translate(-0.032, 0, 0); + + eye = graph.add_node("/pos/nav1", "eye_r"); + eye->translate(0.032, 0, 0); + + // eye2 + eye = graph.add_node("/pos/nav2", "eye_l"); + eye->translate(-0.032, 0, 0); + + eye = graph.add_node("/pos/nav2", "eye_r"); + eye->translate(0.032, 0, 0); + + unsigned width = 1280 / 2; + unsigned height = 800; + + std::array pipes; + + unsigned npipe = 0; + for (auto pipe : pipes) + { + pipe = new gua::Pipeline(); + + if (npipe == 0) { + pipe->config.set_camera(gua::Camera("/pos/nav1/eye_l", "/pos/nav1/eye_r", "/pos/nav1/screen_l", "/pos/nav1/screen_r", "main_scenegraph")); + } else { + pipe->config.set_camera(gua::Camera("/pos/nav2/eye_l", "/pos/nav2/eye_r", "/pos/nav2/screen_l", "/pos/nav2/screen_r", "main_scenegraph")); + } + pipe->config.set_left_resolution(gua::math::vec2ui(width, height)); + pipe->config.set_right_resolution(gua::math::vec2ui(width, height)); + pipe->config.set_enable_fps_display(true); + pipe->config.set_enable_frustum_culling(true); + pipe->config.set_enable_stereo(true); + pipe->config.set_enable_ssao(true); + pipe->config.set_ssao_intensity(2.f); + pipe->config.set_enable_fxaa(true); + pipe->config.set_enable_hdr(true); + pipe->config.set_hdr_key(5.f); + pipe->config.set_enable_bloom(true); + pipe->config.set_bloom_radius(10.f); + pipe->config.set_bloom_threshold(0.8f); + pipe->config.set_bloom_intensity(0.8f); + + ++npipe; + } + +#if WIN32 + auto oculus_rift1(new gua::OculusRift("\\\\.\\DISPLAY1")); + auto oculus_rift2(new gua::OculusRift("\\\\.\\DISPLAY1")); + auto window(new gua::Window); +#else + auto oculus_rift(new gua::OculusRift(":0.0")); +#endif + pipes[0]->set_window(oculus_rift1); + pipes[1]->set_window(oculus_rift2); + pipes[2]->set_window(window); + + gua::Renderer renderer({ + pipes[0], + pipes[1], + pipes[2] + }); + + gua::Timer timer; + timer.start(); + + double time(0); + float desired_frame_time(1.0 / 60.0); + gua::events::MainLoop loop; + + // application loop + gua::events::Ticker ticker(loop, desired_frame_time); + + ticker.on_tick.connect([&]() { + double frame_time(timer.get_elapsed()); + time += frame_time; + timer.reset(); + + std::function, int)> rotate; + rotate = [&](std::shared_ptr node, int depth) { + node->rotate(frame_time * (1+depth) * 0.5, 1, 1, 0); + for (auto child: node->get_children()) { + rotate(child, ++depth); + } + }; + + rotate(graph["/root_ape"], 1); + + for (int i = 0; i < lights.size(); ++i) { + lights[i]->rotate( + std::sin(time * (i * 0.1 + 0.5)) * frame_time * 2.5, 0, 1, 0); + } + + graph["/root_ape"]->rotate(15 * frame_time, 0, 1, 0); + //graph["/screen"]->rotate(20*frame_time, 0, 1, 0); + + nav1->set_transform(oculus_rift1->get_transform()); + nav2->set_transform(oculus_rift2->get_transform()); + + renderer.queue_draw({&graph}); + }); + + loop.start(); + + return 0; +} + diff --git a/plugins/guacamole-oculus/include/gua/OculusDeviceManager.hpp b/plugins/guacamole-oculus/include/gua/OculusDeviceManager.hpp new file mode 100644 index 000000000..3885d3645 --- /dev/null +++ b/plugins/guacamole-oculus/include/gua/OculusDeviceManager.hpp @@ -0,0 +1,43 @@ +#ifndef GUA_OCULUS_DEVICE_MANAGER_HPP +#define GUA_OCULUS_DEVICE_MANAGER_HPP + +#if defined (_MSC_VER) + #if defined (GUA_OCULUS_LIBRARY) + #define GUA_OCULUS_DLL __declspec( dllexport ) + #else +#define GUA_OCULUS_DLL __declspec( dllimport ) + #endif +#else + #define GUA_OCULUS_DLL +#endif // #if defined(_MSC_VER) + +namespace OVR { + class SensorFusion; + class DeviceManager; + class HMDDevice; + class SensorDevice; +} + +namespace gua +{ + class GUA_OCULUS_DLL OculusDeviceManager + { + public: + static OculusDeviceManager& getInstance(); + virtual ~OculusDeviceManager(); + + OVR::HMDDevice* getNextAvailableDevice(); + + protected: + OculusDeviceManager(); + + private: + OVR::DeviceManager* device_manager_; + static int numberOfDevicesCreated; + + OculusDeviceManager(OculusDeviceManager const& src); + OculusDeviceManager& operator=(OculusDeviceManager const& src); + }; +} + +#endif // GUA_OCULUS_DEVICE_MANAGER_HPP \ No newline at end of file diff --git a/plugins/guacamole-oculus/include/gua/OculusRift.hpp b/plugins/guacamole-oculus/include/gua/OculusRift.hpp new file mode 100644 index 000000000..9809c77a9 --- /dev/null +++ b/plugins/guacamole-oculus/include/gua/OculusRift.hpp @@ -0,0 +1,78 @@ +/****************************************************************************** + * guacamole - delicious VR * + * * + * Copyright: (c) 2011-2013 Bauhaus-Universität Weimar * + * Contact: felix.lauer@uni-weimar.de / simon.schneegans@uni-weimar.de * + * * + * This program is free software: you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the Free * + * Software Foundation, either version 3 of the License, or (at your option) * + * any later version. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * + * for more details. * + * * + * You should have received a copy of the GNU General Public License along * + * with this program. If not, see . * + * * + ******************************************************************************/ + +#ifndef GUA_OCULUS_RIFT_HPP +#define GUA_OCULUS_RIFT_HPP + +#if defined (_MSC_VER) + #if defined (GUA_OCULUS_LIBRARY) + #define GUA_OCULUS_DLL __declspec( dllexport ) + #else +#define GUA_OCULUS_DLL __declspec( dllimport ) + #endif +#else + #define GUA_OCULUS_DLL +#endif // #if defined(_MSC_VER) + +// guacamole headers +#include + +namespace OVR { + class SensorFusion; + class DeviceManager; + class HMDDevice; + class SensorDevice; +} + +namespace gua { + +class GUA_OCULUS_DLL OculusRift : public Window { + public: + + static void init(); + + OculusRift(std::string const& display); + virtual ~OculusRift(); + + void create_shader(); + + // virtual + void display(std::shared_ptr const& left_texture, + std::shared_ptr const& right_texture); + + math::mat4 const get_transform() const; + + private: + void display(std::shared_ptr const& texture, + math::vec2ui const& size, + math::vec2ui const& position, + bool left); + + math::vec4 distortion_; + + OVR::SensorDevice* sensor_; + OVR::SensorFusion* sensor_fusion_; + OVR::HMDDevice* device_; +}; + +} + +#endif // GUA_OCULUS_RIFT_HPP diff --git a/plugins/guacamole-oculus/readme.md b/plugins/guacamole-oculus/readme.md new file mode 100644 index 000000000..09f692214 --- /dev/null +++ b/plugins/guacamole-oculus/readme.md @@ -0,0 +1 @@ +== Guacamole Material Editor == diff --git a/plugins/guacamole-oculus/src/gua/OculusDeviceManager.cpp b/plugins/guacamole-oculus/src/gua/OculusDeviceManager.cpp new file mode 100644 index 000000000..13ef3a749 --- /dev/null +++ b/plugins/guacamole-oculus/src/gua/OculusDeviceManager.cpp @@ -0,0 +1,78 @@ +// class header +#include + +// external headers +#include + +#include + +namespace gua +{ + int OculusDeviceManager::numberOfDevicesCreated(0); + + OculusDeviceManager& OculusDeviceManager::getInstance() + { + static OculusDeviceManager instance; + return instance; + } + + OculusDeviceManager::OculusDeviceManager() + { + device_manager_ = OVR::DeviceManager::Create(); + } + + /* virtual */ OculusDeviceManager::~OculusDeviceManager() + { + if (device_manager_) + delete device_manager_; + } + + OVR::HMDDevice* OculusDeviceManager::getNextAvailableDevice() + { + OVR::HMDDevice* deviceToReturn(nullptr); + + std::cout << "Getting some device" << std::endl; + + if (numberOfDevicesCreated == 0) + { + std::cout << "Getting first device" << std::endl; + deviceToReturn = device_manager_->EnumerateDevices().CreateDevice(); + } + else if (numberOfDevicesCreated == 1) + { + std::cout << "Getting second device" << std::endl; + auto enumerator = device_manager_->EnumerateDevices(); + enumerator.Next(); + deviceToReturn = enumerator.CreateDevice(); + } + + if (deviceToReturn != nullptr) + ++numberOfDevicesCreated; + + /* // Probably a better solution, to be used instead of the upper one as it supports more than 2 devices + auto enumerator = device_manager_->EnumerateDevices(); + int currentDevice(1); + bool deviceAvailable(true); + + while(currentDevice < numberOfDevicesCreated + 1) + { + if (enumerator.GetType() == Device_None) + { + deviceAvailable = false; + break; + } + + enumerator.Next(); + ++currentDevice; + } + + if (deviceAvailable) + { + deviceToReturn = enumerator.CreateDevice(); + ++numberOfDevicesCreated; + } + */ + + return deviceToReturn; + } +} diff --git a/plugins/guacamole-oculus/src/gua/OculusRift.cpp b/plugins/guacamole-oculus/src/gua/OculusRift.cpp new file mode 100644 index 000000000..5880c308c --- /dev/null +++ b/plugins/guacamole-oculus/src/gua/OculusRift.cpp @@ -0,0 +1,197 @@ +/****************************************************************************** + * guacamole - delicious VR * + * * + * Copyright: (c) 2011-2013 Bauhaus-Universität Weimar * + * Contact: felix.lauer@uni-weimar.de / simon.schneegans@uni-weimar.de * + * * + * This program is free software: you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the Free * + * Software Foundation, either version 3 of the License, or (at your option) * + * any later version. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * + * for more details. * + * * + * You should have received a copy of the GNU General Public License along * + * with this program. If not, see . * + * * + ******************************************************************************/ + +// class header +#include +#include + +// external headers +#include + +namespace gua { + +void OculusRift::init() { + OVR::System::Init(OVR::Log::ConfigureDefaultLog(OVR::LogMask_All)); +} + +OculusRift::OculusRift(std::string const& display): + Window(), + distortion_(4), + sensor_fusion_(nullptr), + sensor_(nullptr), + device_(nullptr) { + + config.set_size(math::vec2ui(1280, 800)); + config.set_title("guacamole"); + config.set_display_name(display); + config.set_stereo_mode(StereoMode::SIDE_BY_SIDE); + config.set_left_resolution(math::vec2ui(1280/2, 800)); + config.set_left_position(math::vec2ui(0, 0)); + config.set_right_resolution(math::vec2ui(1280/2, 800)); + config.set_right_position(math::vec2ui(1280/2, 0)); + + //Every time an Oculus Rift Window is created, it is checked if an additional Oculus is connected + device_ = OculusDeviceManager::getInstance().getNextAvailableDevice(); + + if (!device_) { + WARNING("Failed to initialize an Oculus Rift device! Are enough Oculus Rifts attached?"); + return; + } + + OVR::HMDInfo hmd; + if (device_->GetDeviceInfo(&hmd)) { + MESSAGE("Oculus EyeDistance: %f", hmd.InterpupillaryDistance); + + distortion_[0] = hmd.DistortionK[0]; + distortion_[1] = hmd.DistortionK[1]; + distortion_[2] = hmd.DistortionK[2]; + distortion_[3] = hmd.DistortionK[3]; + } + + sensor_ = device_->GetSensor(); + sensor_fusion_ = new OVR::SensorFusion(); + + if (sensor_) { + sensor_fusion_->AttachToSensor(sensor_); + } else { + WARNING("Failed to get Oculus gyroskop data"); + } +} + +//////////////////////////////////////////////////////////////////////////////// + +OculusRift::~OculusRift() { + if (sensor_fusion_) + delete sensor_fusion_; +} + +//////////////////////////////////////////////////////////////////////////////// + +void OculusRift::create_shader() { + fullscreen_shader_.create_from_sources(R"( + #version 420 + #extension GL_NV_bindless_texture : require + + layout(location=0) in vec3 in_position; + + out vec2 tex_coord; + + void main() { + tex_coord = in_position.xy*0.5 + 0.5; + gl_Position = vec4(in_position, 1.0); + } + )", R"( + #version 420 + #extension GL_NV_bindless_texture : require + #extension GL_NV_gpu_shader5 : enable + + in vec2 tex_coord; + + uniform uvec2 sampler; + + // oculus parameters + uniform vec2 lens_center; + uniform vec2 scale; + uniform vec4 hmd_warp_param; + + layout (location = 0) out vec3 out_color; + + sampler2D get_tex(uvec2 handle) { + return sampler2D(uint64_t(handle.x) + uint64_t(handle.y) * 4294967295); + } + + vec2 hmd_warp(vec2 in_texcoord) { + vec2 theta = (in_texcoord - lens_center) * 2.0; // Scales to [-1, 1] + float rSq = theta.x * theta.x + theta.y * theta.y; + vec2 rvector = theta * (hmd_warp_param.x+hmd_warp_param.y*rSq + +hmd_warp_param.z*rSq*rSq + +hmd_warp_param.w*rSq*rSq*rSq); + return lens_center + scale * rvector; + } + + vec3 get_color() { + + vec2 tc = hmd_warp(tex_coord); + + if (tc.x < 0.0 || tc.y < 0.0 || tc.x > 1.0 || tc.y > 1.0 ) + return vec3(0); + + return vec3(texture2D( get_tex(sampler), tc).rgb); + } + + void main() { + out_color = get_color(); + } + )"); +} + +//////////////////////////////////////////////////////////////////////////////// + +void OculusRift::display(std::shared_ptr const& left_texture, + std::shared_ptr const& right_texture) { + + display(left_texture, config.get_left_resolution(), config.get_left_position(), true); + display(right_texture, config.get_right_resolution(), config.get_right_position(), false); +} + +//////////////////////////////////////////////////////////////////////////////// + +math::mat4 const OculusRift::get_transform() const { + if (sensor_fusion_) { + OVR::Quatf orient = sensor_fusion_->GetPredictedOrientation(); + OVR::Matrix4f ovr_mat(orient.Inverted()); + + return math::mat4(ovr_mat.M[0][0], ovr_mat.M[0][1], ovr_mat.M[0][2], ovr_mat.M[0][3], + ovr_mat.M[1][0], ovr_mat.M[1][1], ovr_mat.M[1][2], ovr_mat.M[1][3], + ovr_mat.M[2][0], ovr_mat.M[2][1], ovr_mat.M[2][2], ovr_mat.M[2][3], + ovr_mat.M[3][0], ovr_mat.M[3][1], ovr_mat.M[3][2], ovr_mat.M[3][3]); + } + + return math::mat4::identity(); +} + +//////////////////////////////////////////////////////////////////////////////// + +void OculusRift::display(std::shared_ptr const& texture, + math::vec2ui const& size, + math::vec2ui const& position, + bool left) { + + + fullscreen_shader_.use(*get_context()); + fullscreen_shader_.set_uniform(*get_context(), texture, "sampler"); + + if (left) fullscreen_shader_.set_uniform(*get_context(), math::vec2(0.6f, 0.5f), "lens_center"); + else fullscreen_shader_.set_uniform(*get_context(), math::vec2(0.4f, 0.5f), "lens_center"); + + fullscreen_shader_.set_uniform(*get_context(), math::vec2(0.4f, 0.4f), "scale"); + fullscreen_shader_.set_uniform(*get_context(), distortion_, "hmd_warp_param"); + + get_context()->render_context->set_viewport(scm::gl::viewport(position, size)); + get_context()->render_context->set_depth_stencil_state(depth_stencil_state_); + + fullscreen_quad_->draw(get_context()->render_context); + + get_context()->render_context->reset_state_objects(); + fullscreen_shader_.unuse(*get_context()); +} + +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 854b1ee17..bdd3ee83e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -14,8 +14,6 @@ ADD_LIBRARY( guacamole SHARED ${GUACAMOLE_SRC} ) -MESSAGE(${CMAKE_SYSTEM_VERSION}) - IF (MSVC) GET_WIN32_WINNT(WIN_VERSION) SET_TARGET_PROPERTIES(guacamole PROPERTIES COMPILE_FLAGS "-D GUA_LIBRARY -D _WIN32_WINNT=${WIN_VERSION}") diff --git a/src/gua/renderer/ShaderProgram.cpp b/src/gua/renderer/ShaderProgram.cpp index 4eae21024..c05e32403 100644 --- a/src/gua/renderer/ShaderProgram.cpp +++ b/src/gua/renderer/ShaderProgram.cpp @@ -92,17 +92,9 @@ void ShaderProgram::create_from_files(std::string const & v_file, programs_.clear(); interleaved_stream_capture_.clear(); -#if GUA_COMPILER == GUA_COMPILER_MSVC&& SCM_COMPILER_VER <= 1600 - stages_.clear(); - stages_.push_back( - ShaderProgramStage(scm::gl::STAGE_VERTEX_SHADER, v_file, false)); - stages_.push_back( - ShaderProgramStage(scm::gl::STAGE_FRAGMENT_SHADER, f_file, false)); -#else stages_ = { ShaderProgramStage(scm::gl::STAGE_VERTEX_SHADER, v_file, false), ShaderProgramStage( scm::gl::STAGE_FRAGMENT_SHADER, f_file, false) }; -#endif } //////////////////////////////////////////////////////////////////////////////// @@ -118,17 +110,9 @@ void ShaderProgram::create_from_sources(std::string const & v_source, programs_.clear(); interleaved_stream_capture_.clear(); -#if GUA_COMPILER == GUA_COMPILER_MSVC&& SCM_COMPILER_VER <= 1600 - stages_.clear(); - stages_.push_back( - ShaderProgramStage(scm::gl::STAGE_VERTEX_SHADER, v_source, true)); - stages_.push_back( - ShaderProgramStage(scm::gl::STAGE_FRAGMENT_SHADER, f_source, true)); -#else stages_ = { ShaderProgramStage(scm::gl::STAGE_VERTEX_SHADER, v_source, true), ShaderProgramStage( scm::gl::STAGE_FRAGMENT_SHADER, f_source, true) }; -#endif } //////////////////////////////////////////////////////////////////////// From b12fd114dcffdb2ec5e4a3d665ac74c47e0bcca2 Mon Sep 17 00:00:00 2001 From: Andre Schollmeyer Date: Thu, 28 Nov 2013 19:27:49 +0100 Subject: [PATCH 048/146] - fixed cmake bugs for unix --- cmake/modules/find_boost.cmake | 5 +- cmake/modules/find_compiler.cmake | 3 - cmake/modules/find_json.cmake | 8 +- cmake/modules/find_ovr.cmake | 4 +- plugins/guacamole-oculus/CMakeLists.txt | 2 + .../example-oculus/CMakeLists.txt | 4 +- .../guacamole-oculus/example-oculus/main.cpp | 95 ++++++------------- 7 files changed, 41 insertions(+), 80 deletions(-) diff --git a/cmake/modules/find_boost.cmake b/cmake/modules/find_boost.cmake index 0406b2732..353e04576 100644 --- a/cmake/modules/find_boost.cmake +++ b/cmake/modules/find_boost.cmake @@ -167,11 +167,12 @@ IF ( BOOST_INCLUDE_DIRS AND NOT BOOST_LIBRARY_DIRS ) ENDFOREACH(_SEARCH_DIR ${BOOST_LIBRARY_SEARCH_DIRS}) IF (NOT _BOOST_FOUND_LIB_DIR) - MESSAGE(FATAL_ERROR "guacamole_boost.cmake: unable to find boost library") + request_boost_search_directories() ELSE (NOT _BOOST_FOUND_LIB_DIR) SET(BOOST_LIBRARY_DIRS ${_BOOST_FOUND_LIB_DIR} CACHE PATH "boost library path.") IF (UNIX) - FILE(GLOB BOOST_LIBRARIES ${_BOOST_FOUND_LIB_DIR}/*.so) + FILE(GLOB _BOOST_LIBRARIES ${_BOOST_FOUND_LIB_DIR}/*.so) + SET(BOOST_LIBRARIES ${_BOOST_LIBRARIES} CACHE PATH "boost libraries") ELSEIF (MSVC) # use boost auto link ENDIF (UNIX) diff --git a/cmake/modules/find_compiler.cmake b/cmake/modules/find_compiler.cmake index e6ebced9b..1367cd53b 100644 --- a/cmake/modules/find_compiler.cmake +++ b/cmake/modules/find_compiler.cmake @@ -40,11 +40,8 @@ if (UNIX) ARGS --version OUTPUT_VARIABLE _COMPILER_VERSION ) - message(${_COMPILER_VERSION}) string(REGEX REPLACE ".* ([0-9])\\.([0-9])\\.[0-9].*" "\\1\\2" _COMPILER_VERSION ${_COMPILER_VERSION}) - message(${_COMPILER_VERSION}) set (_COMPILER_SUFFIX "gcc${_COMPILER_VERSION}") - message(${COMPILER_SUFFIX}) endif (CMAKE_COMPILER_IS_GNUCXX) endif(UNIX) diff --git a/cmake/modules/find_json.cmake b/cmake/modules/find_json.cmake index a5cf1d015..0c06a1526 100644 --- a/cmake/modules/find_json.cmake +++ b/cmake/modules/find_json.cmake @@ -80,7 +80,7 @@ ELSEIF(WIN32) SET(JSON_LIB_FILENAME "json.lib") ENDIF(UNIX) -IF ( JSON_INCLUDE_DIRS AND NOT JSON_LIBRARIES ) +IF ( JSON_INCLUDE_DIRS AND NOT JSON_LIBRARY_DIRS ) SET(_JSON_FOUND_LIB_DIR "") SET(_JSON_POSTFIX "") @@ -100,15 +100,15 @@ IF ( JSON_INCLUDE_DIRS AND NOT JSON_LIBRARIES ) IF (NOT _JSON_FOUND_LIB_DIR) request_json_search_directories() ELSE (NOT _JSON_FOUND_LIB_DIR) - SET(JSON_LIBRARY_DIRS ${_JSON_FOUND_LIB_DIR} CACHE STRING "The json library directory.") - message(STATUS "-- found matching version") + SET(JSON_LIBRARY_DIRS ${_JSON_FOUND_LIB_DIR}) + SET(JSON_LIBRARY_DIRS ${JSON_LIBRARY_DIRS} CACHE STRING "The json library directory.") ENDIF (NOT _JSON_FOUND_LIB_DIR) IF (_JSON_FOUND_LIB_DIR) SET(JSON_LIBRARIES ${JSON_LIB_FILENAME} CACHE STRING "The json library.") ENDIF (_JSON_FOUND_LIB_DIR) -ENDIF( JSON_INCLUDE_DIRS AND NOT JSON_LIBRARIES ) +ENDIF( JSON_INCLUDE_DIRS AND NOT JSON_LIBRARY_DIRS ) ############################################################################## # verify diff --git a/cmake/modules/find_ovr.cmake b/cmake/modules/find_ovr.cmake index a45d4b667..65d68dc40 100644 --- a/cmake/modules/find_ovr.cmake +++ b/cmake/modules/find_ovr.cmake @@ -5,14 +5,14 @@ SET(OVR_INCLUDE_SEARCH_DIRS ${GLOBAL_EXT_DIR}/inc/OculusSDK ${OVR_INCLUDE_DIRS} ${OVR_INCLUDE_SEARCH_DIR} - "/opt/OculusSDK/LibOVR/Include" + /opt/OculusSDK/LibOVR/Include ) SET(OVR_LIBRARY_SEARCH_DIRS ${GLOBAL_EXT_DIR}/lib ${OVR_LIBRARY_DIRS} ${OVR_LIBRARY_SEARCH_DIR} - "/opt/OculusSDK/LibOVR/Lib" + /opt/OculusSDK/LibOVR/Lib/Linux/Release/x86_64 ) ############################################################################## diff --git a/plugins/guacamole-oculus/CMakeLists.txt b/plugins/guacamole-oculus/CMakeLists.txt index bd42582d4..a7db83581 100644 --- a/plugins/guacamole-oculus/CMakeLists.txt +++ b/plugins/guacamole-oculus/CMakeLists.txt @@ -22,6 +22,8 @@ ADD_LIBRARY( guacamole-oculus SHARED ${GUACAMOLE_OCULUS_SRC} ) +ADD_DEPENDENCIES(guacamole-oculus guacamole) + IF (MSVC) set_target_properties(guacamole-oculus PROPERTIES COMPILE_FLAGS "-D GUA_OCULUS_LIBRARY") ENDIF (MSVC) diff --git a/plugins/guacamole-oculus/example-oculus/CMakeLists.txt b/plugins/guacamole-oculus/example-oculus/CMakeLists.txt index 62039d357..2d854eacf 100644 --- a/plugins/guacamole-oculus/example-oculus/CMakeLists.txt +++ b/plugins/guacamole-oculus/example-oculus/CMakeLists.txt @@ -21,7 +21,9 @@ ADD_EXECUTABLE( ${_EXE_NAME} ADD_DEPENDENCIES(${_EXE_NAME} guacamole-oculus) -TARGET_LINK_LIBRARIES( ${_EXE_NAME} debug ${LIBS} guacamole-oculus optimized ${LIBS} guacamole-oculus) +LIST(APPEND LIBS guacamole-oculus guacamole ${OVR_LIBRARIES}) + +TARGET_LINK_LIBRARIES( ${_EXE_NAME} debug ${LIBS} optimized ${LIBS}) # copy runtime libraries as a post-build process IF (MSVC) diff --git a/plugins/guacamole-oculus/example-oculus/main.cpp b/plugins/guacamole-oculus/example-oculus/main.cpp index bef0b4c65..eb35bd491 100644 --- a/plugins/guacamole-oculus/example-oculus/main.cpp +++ b/plugins/guacamole-oculus/example-oculus/main.cpp @@ -108,7 +108,7 @@ int main(int argc, char** argv) { "root_ape", geometry, "Stones" - )); + )); auto root_monkey = graph.add_node("/", monkey_geometry); root_monkey->scale(0.5, 0.5, 0.5); @@ -126,89 +126,49 @@ int main(int argc, char** argv) { auto pos = graph.add_node("/", "pos"); pos->translate(0, 0, 2); - auto nav1 = graph.add_node("/pos", "nav1"); - auto nav2 = graph.add_node("/pos", "nav2"); + auto nav = graph.add_node("/pos", "nav"); - // hmd1 - auto screen = graph.add_node("/pos/nav1", "screen_l"); + auto screen = graph.add_node("/pos/nav", "screen_l"); screen->data.set_size(gua::math::vec2(0.08, 0.1)); screen->translate(-0.04, 0, -0.05f); - screen = graph.add_node("/pos/nav1", "screen_r"); + screen = graph.add_node("/pos/nav", "screen_r"); screen->data.set_size(gua::math::vec2(0.08, 0.1)); screen->translate(0.04, 0, -0.05f); - // hmd2 - screen = graph.add_node("/pos/nav2", "screen_l"); - screen->data.set_size(gua::math::vec2(0.08, 0.1)); - screen->translate(-0.04, 0, -0.05f); - - screen = graph.add_node("/pos/nav2", "screen_r"); - screen->data.set_size(gua::math::vec2(0.08, 0.1)); - screen->translate(0.04, 0, -0.05f); - - // eye1 - auto eye = graph.add_node("/pos/nav1", "eye_l"); + auto eye = graph.add_node("/pos/nav", "eye_l"); eye->translate(-0.032, 0, 0); - eye = graph.add_node("/pos/nav1", "eye_r"); + eye = graph.add_node("/pos/nav", "eye_r"); eye->translate(0.032, 0, 0); - // eye2 - eye = graph.add_node("/pos/nav2", "eye_l"); - eye->translate(-0.032, 0, 0); - - eye = graph.add_node("/pos/nav2", "eye_r"); - eye->translate(0.032, 0, 0); - - unsigned width = 1280 / 2; + unsigned width = 1280/2; unsigned height = 800; - std::array pipes; + auto pipe = new gua::Pipeline(); + pipe->config.set_camera(gua::Camera("/pos/nav/eye_l", "/pos/nav/eye_r", "/pos/nav/screen_l", "/pos/nav/screen_r", "main_scenegraph")); + pipe->config.set_left_resolution(gua::math::vec2ui(width, height)); + pipe->config.set_right_resolution(gua::math::vec2ui(width, height)); + pipe->config.set_enable_fps_display(true); + pipe->config.set_enable_frustum_culling(true); + pipe->config.set_enable_stereo(true); + + pipe->config.set_enable_ssao(true); + pipe->config.set_ssao_intensity(2.f); + pipe->config.set_enable_fxaa(true); + pipe->config.set_enable_hdr(true); + pipe->config.set_hdr_key(5.f); + pipe->config.set_enable_bloom(true); + pipe->config.set_bloom_radius(10.f); + pipe->config.set_bloom_threshold(0.8f); + pipe->config.set_bloom_intensity(0.8f); - unsigned npipe = 0; - for (auto pipe : pipes) - { - pipe = new gua::Pipeline(); - - if (npipe == 0) { - pipe->config.set_camera(gua::Camera("/pos/nav1/eye_l", "/pos/nav1/eye_r", "/pos/nav1/screen_l", "/pos/nav1/screen_r", "main_scenegraph")); - } else { - pipe->config.set_camera(gua::Camera("/pos/nav2/eye_l", "/pos/nav2/eye_r", "/pos/nav2/screen_l", "/pos/nav2/screen_r", "main_scenegraph")); - } - pipe->config.set_left_resolution(gua::math::vec2ui(width, height)); - pipe->config.set_right_resolution(gua::math::vec2ui(width, height)); - pipe->config.set_enable_fps_display(true); - pipe->config.set_enable_frustum_culling(true); - pipe->config.set_enable_stereo(true); - pipe->config.set_enable_ssao(true); - pipe->config.set_ssao_intensity(2.f); - pipe->config.set_enable_fxaa(true); - pipe->config.set_enable_hdr(true); - pipe->config.set_hdr_key(5.f); - pipe->config.set_enable_bloom(true); - pipe->config.set_bloom_radius(10.f); - pipe->config.set_bloom_threshold(0.8f); - pipe->config.set_bloom_intensity(0.8f); - - ++npipe; - } -#if WIN32 - auto oculus_rift1(new gua::OculusRift("\\\\.\\DISPLAY1")); - auto oculus_rift2(new gua::OculusRift("\\\\.\\DISPLAY1")); - auto window(new gua::Window); -#else auto oculus_rift(new gua::OculusRift(":0.0")); -#endif - pipes[0]->set_window(oculus_rift1); - pipes[1]->set_window(oculus_rift2); - pipes[2]->set_window(window); + pipe->set_window(oculus_rift); gua::Renderer renderer({ - pipes[0], - pipes[1], - pipes[2] + pipe }); gua::Timer timer; @@ -244,8 +204,7 @@ int main(int argc, char** argv) { graph["/root_ape"]->rotate(15 * frame_time, 0, 1, 0); //graph["/screen"]->rotate(20*frame_time, 0, 1, 0); - nav1->set_transform(oculus_rift1->get_transform()); - nav2->set_transform(oculus_rift2->get_transform()); + nav->set_transform(oculus_rift->get_transform()); renderer.queue_draw({&graph}); }); From 12167c331ed5df04d2a34ec6b3f6bf59de16ffbe Mon Sep 17 00:00:00 2001 From: gandideluxe Date: Thu, 28 Nov 2013 22:55:16 +0100 Subject: [PATCH 049/146] Added Volume.h Added VolumeLoader.h Integrated laoding of Volumes Integrated managing of VolumeNodes Integraded compositing of Volumes in CompositePass.cpp --- .../gua/physics/CollisionShapeNodeVisitor.hpp | 3 + include/gua/renderer.hpp | 1 + include/gua/renderer/Volume.hpp | 177 +++++++++ include/gua/renderer/VolumeLoader.hpp | 103 +++++ include/gua/scenegraph/Node.hpp | 1 + include/gua/scenegraph/NodeVisitor.hpp | 10 + include/gua/utils/DotGenerator.hpp | 1 + .../common/get_sampler_casts.glsl | 16 + .../uber_shaders/composite/compose.frag | 113 +++++- .../composite/ray_generation.vert | 8 +- src/gua/renderer/CompositePass.cpp | 147 ++++--- src/gua/renderer/Serializer.cpp | 5 +- src/gua/renderer/Volume.cpp | 369 ++++++++++++++++++ src/gua/renderer/VolumeLoader.cpp | 142 +++++++ src/gua/scenegraph/VolumeNode.cpp | 119 +----- src/gua/utils/DotGenerator.cpp | 19 + 16 files changed, 1054 insertions(+), 180 deletions(-) create mode 100644 include/gua/renderer/Volume.hpp create mode 100644 include/gua/renderer/VolumeLoader.hpp create mode 100644 src/gua/renderer/Volume.cpp create mode 100644 src/gua/renderer/VolumeLoader.cpp diff --git a/include/gua/physics/CollisionShapeNodeVisitor.hpp b/include/gua/physics/CollisionShapeNodeVisitor.hpp index 015e862ae..64c51a3a2 100644 --- a/include/gua/physics/CollisionShapeNodeVisitor.hpp +++ b/include/gua/physics/CollisionShapeNodeVisitor.hpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -103,6 +104,8 @@ class CollisionShapeNodeVisitor : public NodeVisitor { /* virtual */ void visit(TransformNode* node) { generic_visit(node); } /* virtual */ void visit(GeometryNode* node) { generic_visit(node); } + + /* virtual */ void visit(VolumeNode* node) { generic_visit(node); } /* virtual */ void visit(PointLightNode* node) { generic_visit(node); } diff --git a/include/gua/renderer.hpp b/include/gua/renderer.hpp index 5f72f1e44..9aa8c13e1 100644 --- a/include/gua/renderer.hpp +++ b/include/gua/renderer.hpp @@ -24,6 +24,7 @@ // renderer headers #include +#include #include #include #include diff --git a/include/gua/renderer/Volume.hpp b/include/gua/renderer/Volume.hpp new file mode 100644 index 000000000..74bec9e43 --- /dev/null +++ b/include/gua/renderer/Volume.hpp @@ -0,0 +1,177 @@ +/****************************************************************************** +* guacamole - delicious VR * +* * +* Copyright: (c) 2011-2013 Bauhaus-Universität Weimar * +* Contact: felix.lauer@uni-weimar.de / simon.schneegans@uni-weimar.de * +* * +* This program is free software: you can redistribute it and/or modify it * +* under the terms of the GNU General Public License as published by the Free * +* Software Foundation, either version 3 of the License, or (at your option) * +* any later version. * +* * +* This program is distributed in the hope that it will be useful, but * +* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * +* for more details. * +* * +* You should have received a copy of the GNU General Public License along * +* with this program. If not, see . * +* * +******************************************************************************/ + +#ifndef GUA_VOLUME_HPP +#define GUA_VOLUME_HPP + +// guacamole headers +#include +#include +#include +#include +#include + +// external headers +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +//struct aiMesh; + + +namespace gua { + + struct RenderContext; + + /** + * Stores geometry data. + * + * A volume can be loaded from an Assimp volume and the draw onto multiple + * contexts. + * Do not use this class directly, it is just used by the Geometry class to + * store the individual meshes of a file. + */ + class Volume : public Geometry { + public: + + /** + * Default constructor. + * + * Creates a new and empty Volume. + */ + Volume(); + + /** + * Constructor from an Assimp volume. + * + * Initializes the volume from a given file path + * + * \param volume The Assimp volume to load the data from. + */ + Volume(std::string const& file_name); + + /** + * Draws the Volume. + * + * Draws the Volume to the given context. There is no compositing happening + * + * \param context The RenderContext to draw onto. + */ + void draw(RenderContext const& context) const; + + /** + * Draws the Volume. + * + * Draws the Volume proxy to the given context. + * + * \param context The RenderContext to draw onto. + */ + void draw_proxy(RenderContext const& context) const; + + /** + * Sets the necessary uniform values for compositing shader + * + * Draws the Volume proxy to the given context. + * + * \param shaderProgram The RenderContext to draw onto. + */ + void set_uniforms(RenderContext const& ctx, ShaderProgram* cs) const; + + void ray_test(Ray const& ray, PickResult::Options options, + Node* owner, std::set& hits); + + + float step_size() const; + void step_size(const float size); + + void set_transfer_function(const scm::data::piecewise_function_1d& in_alpha, const scm::data::piecewise_function_1d& in_color); + + private: + void upload_to(RenderContext const& context) const; + + std::shared_ptr create_color_map(RenderContext const& context, + unsigned in_size, + const scm::data::piecewise_function_1d& in_alpha, + const scm::data::piecewise_function_1d& in_color) const; + + bool update_color_map(RenderContext const& context, + std::shared_ptr, + const scm::data::piecewise_function_1d& in_alpha, + const scm::data::piecewise_function_1d& in_color) const; + + mutable bool _update_transfer_function; + + ////Volume files + //mutable std::vector + // _volume_file_pathes; + // Volume File path + std::string _volume_file_path; + + //Volume boxes for each volume + mutable std::vector + _volume_boxes_ptr; + + //Texture3D for volume data for each volume + mutable std::vector> + _volume_texture_ptr; + + mutable std::vector> + _transfer_texture_ptr; + + mutable std::vector _sstate; + +#if GUA_COMPILER == GUA_COMPILER_MSVC && SCM_COMPILER_VER <= 1700 + mutable boost::mutex upload_mutex_; +#else + mutable std::mutex upload_mutex_; +#endif + + scm::data::piecewise_function_1d _alpha_transfer; + scm::data::piecewise_function_1d _color_transfer; + + ///Volume Info + math::vec3ui _volume_dimensions; + math::vec3 _volume_dimensions_normalized; + float _step_size; + + public: + + }; + +} + +#endif // GUA_VOLUME_HPP diff --git a/include/gua/renderer/VolumeLoader.hpp b/include/gua/renderer/VolumeLoader.hpp new file mode 100644 index 000000000..ca4048216 --- /dev/null +++ b/include/gua/renderer/VolumeLoader.hpp @@ -0,0 +1,103 @@ +/****************************************************************************** +* guacamole - delicious VR * +* * +* Copyright: (c) 2011-2013 Bauhaus-Universität Weimar * +* Contact: felix.lauer@uni-weimar.de / simon.schneegans@uni-weimar.de * +* * +* This program is free software: you can redistribute it and/or modify it * +* under the terms of the GNU General Public License as published by the Free * +* Software Foundation, either version 3 of the License, or (at your option) * +* any later version. * +* * +* This program is distributed in the hope that it will be useful, but * +* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * +* for more details. * +* * +* You should have received a copy of the GNU General Public License along * +* with this program. If not, see . * +* * +******************************************************************************/ + +#ifndef GUA_VOLUME_LOADER_HPP +#define GUA_VOLUME_LOADER_HPP + +// guacamole headers +#include +#include +#include + +// external headers +#include +#include +#include + +namespace gua { + + class Node; + class InnerNode; + class VolumeNode; + + /** + * Loads and draws meshes. + * + * This class can load mesh data from files and display them in multiple + * contexts. A VolumeLoader object is made of several Mesh objects. + */ + class GUA_DLL VolumeLoader : public LoaderBase { + public: + + enum Flags { + DEFAULTS = 0, + MAKE_PICKABLE = 1 << 0, + NORMALIZE_POSITION = 1 << 1, + NORMALIZE_SCALE = 1 << 2 + }; + + /** + * Default constructor. + * + * Constructs a new and empty VolumeLoader. + */ + VolumeLoader(); + + /** + * Constructor from a file. + * + * Creates a new VolumeLoader from a given file. + * + * \param file_name The file to load the meh's data from. + */ + + + std::shared_ptr create_volume_from_file(std::string const& node_name, + std::string const& file_name, + unsigned flags); + + std::shared_ptr load(std::string const& file_name, + unsigned flags); + + /** + * Constructor from memory buffer. + * + * Creates a new VolumeLoader from a existing memory buffer. + * + * \param buffer_name The buffer to load the meh's data from. + * \param buffer_size The buffer's size. + */ + //std::vector const load_from_buffer(char const* buffer_name, + // unsigned buffer_size); + + /* virtual */ bool is_supported(std::string const& file_name) const; + + private: + + static std::unordered_map> loaded_files_; + + boost::unordered_set _supported_file_extensions; + + }; + +} + +#endif // GUA_VOLUME_LOADER_HPP diff --git a/include/gua/scenegraph/Node.hpp b/include/gua/scenegraph/Node.hpp index 8284ad970..9467b022a 100644 --- a/include/gua/scenegraph/Node.hpp +++ b/include/gua/scenegraph/Node.hpp @@ -287,6 +287,7 @@ class GUA_DLL Node { friend class SceneGraph; friend class GeometryLoader; + friend class VolumeLoader; friend class MeshLoader; friend class Serializer; friend class DotGenerator; diff --git a/include/gua/scenegraph/NodeVisitor.hpp b/include/gua/scenegraph/NodeVisitor.hpp index 5aab79741..9f9f2112e 100644 --- a/include/gua/scenegraph/NodeVisitor.hpp +++ b/include/gua/scenegraph/NodeVisitor.hpp @@ -31,6 +31,7 @@ namespace gua { class Node; class TransformNode; class GeometryNode; +class VolumeNode; class PointLightNode; class ScreenNode; class SpotLightNode; @@ -92,6 +93,15 @@ class NodeVisitor { */ virtual void visit(GeometryNode* node) { visit(reinterpret_cast(node)); } + /** + * Visits a GeometryNode + * + * This function provides the interface to visit a GeometryNode + * + * \param cam Pointer to GeometryNode + */ + virtual void visit(VolumeNode* node) { visit(reinterpret_cast(node)); } + /** * Visits a PointLightNode * diff --git a/include/gua/utils/DotGenerator.hpp b/include/gua/utils/DotGenerator.hpp index ad55b2136..e53e34581 100644 --- a/include/gua/utils/DotGenerator.hpp +++ b/include/gua/utils/DotGenerator.hpp @@ -67,6 +67,7 @@ class GUA_DLL DotGenerator : public NodeVisitor { /*virtual*/ void visit(Node* node); /*virtual*/ void visit(TransformNode* cam); /*virtual*/ void visit(GeometryNode* geometry); + /*virtual*/ void visit(VolumeNode* volume); /*virtual*/ void visit(PointLightNode* pointlight); /*virtual*/ void visit(ScreenNode* screen); /*virtual*/ void visit(SpotLightNode* spotlight); diff --git a/resources/shaders/uber_shaders/common/get_sampler_casts.glsl b/resources/shaders/uber_shaders/common/get_sampler_casts.glsl index 81d9563c7..497686179 100644 --- a/resources/shaders/uber_shaders/common/get_sampler_casts.glsl +++ b/resources/shaders/uber_shaders/common/get_sampler_casts.glsl @@ -17,3 +17,19 @@ sampler2D gua_get_double_sampler(uvec2 handle) { sampler2DShadow gua_get_shadow_sampler(uvec2 handle) { return sampler2DShadow(uint64_t(handle.x) | (uint64_t(handle.y) << 32UL)); } + +isampler3D gua_get_int_sampler3D(uvec2 handle) { + return isampler3D(uint64_t(handle.x) | (uint64_t(handle.y) << 32UL)); +} + +usampler3D gua_get_uint_sampler3D(uvec2 handle) { + return usampler3D(uint64_t(handle.x) | (uint64_t(handle.y) << 32UL)); +} + +sampler3D gua_get_float_sampler3D(uvec2 handle) { + return sampler3D(uint64_t(handle.x) | (uint64_t(handle.y) << 32UL)); +} + +sampler3D gua_get_double_sampler3D(uvec2 handle) { + return sampler3D(uint64_t(handle.x) | (uint64_t(handle.y) << 32UL)); +} \ No newline at end of file diff --git a/resources/shaders/uber_shaders/composite/compose.frag b/resources/shaders/uber_shaders/composite/compose.frag index cbc851142..b38d0455e 100644 --- a/resources/shaders/uber_shaders/composite/compose.frag +++ b/resources/shaders/uber_shaders/composite/compose.frag @@ -27,10 +27,19 @@ uniform uvec2 gua_color_gbuffer_in; uniform uvec2 gua_normal_gbuffer_in; uniform uvec2 gua_ray_entry_in; +uniform uvec2 volume_texture; +uniform uvec2 transfer_texture; +uniform float sampling_distance; +uniform vec3 volume_bounds; + + // uniforms @include "shaders/uber_shaders/common/get_sampler_casts.glsl" @include "shaders/uber_shaders/common/gua_camera_uniforms.glsl" + + + // methods --------------------------------------------------------------------- // global gua_* methods @@ -41,12 +50,110 @@ vec2 gua_get_quad_coords() { // write outputs --------------------------------------------------------------------- layout(location=0) out vec3 gua_out_color; +float get_depth_z(vec3 world_position) { + vec4 pos = gua_projection_matrix * gua_view_matrix * vec4(world_position, 1.0); + float ndc = pos.z/pos.w; + return ((gl_DepthRange.diff * ndc) + gl_DepthRange.near + gl_DepthRange.far) / 2.0; + + //gd = (((gl_DepthRange.diff) * ndc) + gl_DepthRange.near + gl_DepthRange.far) / 2.0; + //gd * 2.0 - gl_DepthRange.near + gl_DepthRange.far) = gl_DepthRange.diff * ndc; + //(gd * 2.0 - gl_DepthRange.near + gl_DepthRange.far)/gl_DepthRange.diff = ndc; +} + +float get_depth_linear(float depth_buffer_d) { + //float z_n = 2.0 * depth_buffer_d - 1.0; + //float z_e = 2.0 * zNear * zFar / (zFar + zNear - z_n * (zFar - zNear)); + //float z_e = 2.0 * gl_DepthRange.near * gl_DepthRange.far / (gl_DepthRange.far + gl_DepthRange.near - z_n * (gl_DepthRange.far - gl_DepthRange.near)); + //return z_e; + + float ndc = (depth_buffer_d * 2.0 - gl_DepthRange.near - gl_DepthRange.far)/gl_DepthRange.diff; + + vec4 enit = vec4(gl_FragCoord.xy * 2.0 - vec2(1.0), ndc, 1.0); + + vec4 enit_inv = (gua_inverse_projection_view_matrix * enit); + enit_inv /= enit_inv.w; + + return enit_inv.z; +} + + +bool +inside_volume_bounds(const in vec3 sampling_position) +{ + return ( all(greaterThanEqual(sampling_position, vec3(0.0))) + && all(lessThanEqual(sampling_position, volume_bounds))); +} + +vec4 get_raycast_color(vec3 gua_object_volume_position, + vec3 object_ray, + float d_gbuffer, + float d_volume, + float d_step){ + + vec3 obj_to_tex = vec3(1.0) / volume_bounds; + vec3 ray_increment = object_ray * sampling_distance; + vec3 sampling_pos = gua_object_volume_position + ray_increment; // test, increment just to be sure we are in the volume + + bool inside_volume = inside_volume_bounds(sampling_pos); + + vec4 color = vec4(0.0); + + int d_steps = int(abs(d_volume - d_gbuffer) / d_step); + int d_step_cur = 0; + + //return vec4(d_diff, d_diff, d_diff, 1.0); + + while (inside_volume && (d_step_cur < d_steps)) { + ++d_step_cur; + // get sample + float s = texture(gua_get_float_sampler3D(volume_texture), sampling_pos * obj_to_tex).x;//texture3D(volume_texture, sampling_pos * obj_to_tex).r; + vec4 src = texture2D(gua_get_float_sampler(transfer_texture), vec2(s, 0.5)); + + // increment ray + sampling_pos += ray_increment; + inside_volume = inside_volume_bounds(sampling_pos) && (color.a < 0.99f); + // compositing + float omda_sa = (1.0f - color.a) * src.a; + color.rgb += omda_sa * src.rgb; + color.a += omda_sa; + } + + return color; +} + + // main ------------------------------------------------------------------------ void main() { - // compose - if ( length ( texture2D(gua_get_float_sampler(gua_color_gbuffer_in), gua_get_quad_coords()).xyz) > 0.0 ) + + vec3 gua_object_volume_position = texture2D(gua_get_float_sampler(gua_ray_entry_in), gua_get_quad_coords()).xyz; + vec3 gua_world_volume_position = (gua_model_matrix * vec4(gua_object_volume_position, 1.0)).xyz; + + float d_gbuffer = texture2D(gua_get_float_sampler(gua_depth_gbuffer_in), gua_get_quad_coords()).x; + float d_volume = get_depth_z(gua_world_volume_position); + + d_gbuffer = -1.0 * get_depth_linear(d_gbuffer); + d_volume = -1.0 * get_depth_linear(d_volume); + + // compose + if(d_gbuffer > d_volume && + (gua_object_volume_position.x != 0.00 || + gua_object_volume_position.y != 0.00 || + gua_object_volume_position.z != 0.00)) { - gua_out_color = texture2D(gua_get_float_sampler(gua_ray_entry_in), gua_get_quad_coords()).xyz; + mat4 gua_invers_model_matrix = inverse(gua_model_matrix); + vec3 object_ray = normalize(gua_object_volume_position - (gua_invers_model_matrix * vec4(gua_camera_position, 1.0)).xyz); + float d_step = abs(-1.0 * get_depth_linear(get_depth_z((gua_model_matrix * vec4(gua_object_volume_position + object_ray * sampling_distance, 1.0)).xyz)) - d_volume); + vec4 compositing_color = get_raycast_color(gua_object_volume_position, object_ray, d_gbuffer, d_volume, d_step); + + vec3 gbuffer_color = texture2D(gua_get_float_sampler(gua_color_gbuffer_in), gua_get_quad_coords()).xyz; + + gua_out_color = gbuffer_color * (1.0 - compositing_color.a) + compositing_color.rgb; } + else{ + gua_out_color = texture2D(gua_get_float_sampler(gua_color_gbuffer_in), gua_get_quad_coords()).xyz; + } + + + } diff --git a/resources/shaders/uber_shaders/composite/ray_generation.vert b/resources/shaders/uber_shaders/composite/ray_generation.vert index db1800245..9cc502889 100644 --- a/resources/shaders/uber_shaders/composite/ray_generation.vert +++ b/resources/shaders/uber_shaders/composite/ray_generation.vert @@ -34,9 +34,9 @@ void main() { gua_position_varying = gua_in_position; - vec3 gua_world_position = (gua_model_matrix * vec4(gua_in_position, 1.0)).xyz; - - //gl_Position = gua_projection_matrix * gua_view_matrix * vec4(gua_world_position.xyz, 1.0); - gl_Position = vec4(gua_in_position, 1.0); + //vec3 gua_world_position = (gua_model_matrix * vec4(gua_in_position, 1.0)).xyz; + + gl_Position = gua_projection_matrix * gua_view_matrix * gua_model_matrix * vec4(gua_in_position.xyz, 1.0); + //gl_Position = vec4(gua_in_position, 1.0); } diff --git a/src/gua/renderer/CompositePass.cpp b/src/gua/renderer/CompositePass.cpp index b68b7b5cc..b563a38b2 100644 --- a/src/gua/renderer/CompositePass.cpp +++ b/src/gua/renderer/CompositePass.cpp @@ -28,8 +28,11 @@ #include #include #include +#include #include +#include + namespace gua { //////////////////////////////////////////////////////////////////////////////// @@ -102,61 +105,95 @@ void CompositePass::create(RenderContext const& ctx, RenderContext const& ctx, CameraMode eye, Camera const& camera, - FrameBufferObject* target) { - - init_ressources(ctx); - - ctx.render_context->set_depth_stencil_state(depth_stencil_state_); - - // 1. render proxy geometry into fbo - volume_raygeneration_->bind(ctx); - { - scm::math::vec2f resolution(volume_raygeneration_->width(), volume_raygeneration_->height()); - ctx.render_context->set_viewport(scm::gl::viewport(math::vec2(0, 0), resolution)); - - // gather input textures and set uniforms - Pass::set_camera_matrices(*ray_generation_shader_, camera, pipeline_->get_current_scene(eye), eye, ctx); - - ray_generation_shader_->set_uniform(ctx, 1.f / gbuffer_->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->width(), "gua_texel_width"); - ray_generation_shader_->set_uniform(ctx, 1.f / gbuffer_->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->height(), "gua_texel_height"); - - ray_generation_shader_->use(ctx); - { - fullscreen_quad_->draw(ctx.render_context); - } - ray_generation_shader_->unuse(ctx); - } - volume_raygeneration_->unbind(ctx); - - // 2. render fullscreen quad for compositing and volume ray castinG - Pass::set_camera_matrices(*composite_shader_, camera, pipeline_->get_current_scene(eye), eye, ctx); - - auto input_tex(inputs_[Pipeline::shading]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); - auto normal_tex(inputs_[Pipeline::geometry]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); - auto depth_tex(inputs_[Pipeline::geometry]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_depth_buffer()); - auto raygen_tex(volume_raygeneration_->get_color_buffers(TYPE_FLOAT)[0]); - - composite_shader_->set_uniform(ctx, input_tex, "gua_color_gbuffer_in"); - composite_shader_->set_uniform(ctx, normal_tex, "gua_normal_gbuffer_in"); - composite_shader_->set_uniform(ctx, depth_tex, "gua_depth_gbuffer_in"); - composite_shader_->set_uniform(ctx, raygen_tex, "gua_ray_entry_in"); - - composite_shader_->set_uniform(ctx, 1.f / gbuffer_->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->width(), "gua_texel_width"); - composite_shader_->set_uniform(ctx, 1.f / gbuffer_->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->height(), "gua_texel_height"); - - // bind target fbo and set viewport - target->bind(ctx); - ctx.render_context->set_viewport(scm::gl::viewport( - math::vec2(0, 0), - ::scm::math::vec2f(target->width(), target->height()))); - - composite_shader_->use(ctx); - { - fullscreen_quad_->draw(ctx.render_context); - } - composite_shader_->unuse(ctx); - - target->unbind(ctx); + FrameBufferObject* target) +{ + ///TODO: Toplevel + if (!scene.volumenodes_.empty()) + { + init_ressources(ctx); + + ctx.render_context->set_depth_stencil_state(depth_stencil_state_); + + // 1. render proxy geometry into fbo + volume_raygeneration_->bind(ctx); + { + scm::math::vec2f resolution(volume_raygeneration_->width(), volume_raygeneration_->height()); + ctx.render_context->set_viewport(scm::gl::viewport(math::vec2(0, 0), resolution)); + + // gather input textures and set uniforms + Pass::set_camera_matrices(*ray_generation_shader_, camera, pipeline_->get_current_scene(eye), eye, ctx); + + ray_generation_shader_->set_uniform(ctx, 1.f / gbuffer_->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->width(), "gua_texel_width"); + ray_generation_shader_->set_uniform(ctx, 1.f / gbuffer_->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->height(), "gua_texel_height"); + + volume_raygeneration_->clear_color_buffers(ctx, gua::utils::Color3f(0.0f, 0.0f, 0.0f)); + volume_raygeneration_->clear_depth_stencil_buffer(ctx); + + //fullscreen_quad_->draw(ctx.render_context); + for (auto const& node : scene.volumenodes_) { + + auto volume = + std::static_pointer_cast(GeometryDatabase::instance()->lookup(node.data.get_volume())); + + if (volume) { + ray_generation_shader_->set_uniform( + ctx, node.transform, "gua_model_matrix"); + + ray_generation_shader_->use(ctx); + { + volume->draw_proxy(ctx); + } + ray_generation_shader_->unuse(ctx); + } + } + } + volume_raygeneration_->unbind(ctx); + + // 2. render fullscreen quad for compositing and volume ray castinG + Pass::set_camera_matrices(*composite_shader_, camera, pipeline_->get_current_scene(eye), eye, ctx); + + auto input_tex(inputs_[Pipeline::shading]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); + auto normal_tex(inputs_[Pipeline::geometry]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); + auto depth_tex(inputs_[Pipeline::geometry]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_depth_buffer()); + auto raygen_tex(volume_raygeneration_->get_color_buffers(TYPE_FLOAT)[0]); + + composite_shader_->set_uniform(ctx, input_tex, "gua_color_gbuffer_in"); + composite_shader_->set_uniform(ctx, normal_tex, "gua_normal_gbuffer_in"); + composite_shader_->set_uniform(ctx, depth_tex, "gua_depth_gbuffer_in"); + composite_shader_->set_uniform(ctx, raygen_tex, "gua_ray_entry_in"); + + composite_shader_->set_uniform(ctx, 1.f / gbuffer_->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->width(), "gua_texel_width"); + composite_shader_->set_uniform(ctx, 1.f / gbuffer_->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->height(), "gua_texel_height"); + + // bind target fbo and set viewport + target->bind(ctx); + ctx.render_context->set_viewport(scm::gl::viewport( + math::vec2(0, 0), + ::scm::math::vec2f(target->width(), target->height()))); + + for (auto const& node : scene.volumenodes_) { + + auto volume = + std::static_pointer_cast(GeometryDatabase::instance()->lookup(node.data.get_volume())); + + if (volume) { + composite_shader_->set_uniform( + ctx, node.transform, "gua_model_matrix"); + + volume->set_uniforms(ctx, composite_shader_); + + composite_shader_->use(ctx); + { + fullscreen_quad_->draw(ctx.render_context); + } + composite_shader_->unuse(ctx); + } + } + + + + target->unbind(ctx); + } ctx.render_context->reset_state_objects(); } diff --git a/src/gua/renderer/Serializer.cpp b/src/gua/renderer/Serializer.cpp index a32dd8f2d..43779dcdc 100644 --- a/src/gua/renderer/Serializer.cpp +++ b/src/gua/renderer/Serializer.cpp @@ -72,6 +72,7 @@ void Serializer::check(SerializedScene* output, std::size_t mesh_count = data_->meshnodes_.size(); std::size_t nurbs_count = data_->nurbsnodes_.size(); + std::size_t volume_count = data_->volumenodes_.size(); std::size_t point_light_count = data_->point_lights_.size(); std::size_t spot_light_count = data_->spot_lights_.size(); std::size_t ray_count = data_->rays_.size(); @@ -79,6 +80,7 @@ void Serializer::check(SerializedScene* output, data_->meshnodes_.clear(); data_->nurbsnodes_.clear(); + data_->volumenodes_.clear(); data_->point_lights_.clear(); data_->spot_lights_.clear(); data_->textured_quads_.clear(); @@ -105,6 +107,7 @@ void Serializer::check(SerializedScene* output, // reserving the old size might save some time data_->meshnodes_.reserve(mesh_count); data_->nurbsnodes_.reserve(nurbs_count); + data_->volumenodes_.reserve(nurbs_count); data_->point_lights_.reserve(point_light_count); data_->spot_lights_.reserve(spot_light_count); data_->textured_quads_.reserve(textured_quad_count); @@ -166,7 +169,7 @@ void Serializer::check(SerializedScene* output, if ( is_visible(node) ) { if ( !node->data.get_volume().empty() ) { add_bbox(node); - data_->volumenodes_.push_back(make_serialized_node(node->get_world_transform(), node->data)); + data_->volumenodes_.push_back(make_serialized_node(node->get_world_transform(), node->data)); } visit_children(node); diff --git a/src/gua/renderer/Volume.cpp b/src/gua/renderer/Volume.cpp new file mode 100644 index 000000000..c687259fc --- /dev/null +++ b/src/gua/renderer/Volume.cpp @@ -0,0 +1,369 @@ +/****************************************************************************** +* guacamole - delicious VR * +* * +* Copyright: (c) 2011-2013 Bauhaus-Universität Weimar * +* Contact: felix.lauer@uni-weimar.de / simon.schneegans@uni-weimar.de * +* * +* This program is free software: you can redistribute it and/or modify it * +* under the terms of the GNU General Public License as published by the Free * +* Software Foundation, either version 3 of the License, or (at your option) * +* any later version. * +* * +* This program is distributed in the hope that it will be useful, but * +* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * +* for more details. * +* * +* You should have received a copy of the GNU General Public License along * +* with this program. If not, see . * +* * +******************************************************************************/ + +// class header +#include + +// guacamole headers +#include +#include +#include +#include + +// external headers +#if ASSIMP_VERSION == 3 +#include +#include +#else +#include +#include +#include +#endif + +#include +#include +#include + +namespace { + struct Vertex { + scm::math::vec3f pos; + scm::math::vec2f tex; + scm::math::vec3f normal; + scm::math::vec3f tangent; + scm::math::vec3f bitangent; + }; +} + +namespace gua { + + //////////////////////////////////////////////////////////////////////////////// + + Volume::Volume() + : _volume_boxes_ptr(), upload_mutex_() {} + + //////////////////////////////////////////////////////////////////////////////// + + Volume::Volume(std::string const& file_name) + : + _volume_file_path(file_name), + _volume_boxes_ptr(), + upload_mutex_() + { + scm::gl::volume_loader scm_volume_loader; + _volume_dimensions = scm_volume_loader.read_dimensions(file_name); + //scm::math::vec3ui volume_dimensions = scm::math::vec3ui(256, 256, 225); + + unsigned max_dimension_volume = scm::math::max(scm::math::max(_volume_dimensions.x, _volume_dimensions.y), _volume_dimensions.z); + + step_size(0.5f / (float)max_dimension_volume); + + _volume_dimensions_normalized = math::vec3((float)_volume_dimensions.x / (float)max_dimension_volume, + (float)_volume_dimensions.y / (float)max_dimension_volume, + (float)_volume_dimensions.z / (float)max_dimension_volume); + + MESSAGE("%f %f %f", _volume_dimensions_normalized.x, _volume_dimensions_normalized.y, _volume_dimensions_normalized.z); + //getchar(); + + bounding_box_ = math::BoundingBox(math::vec3::zero(), _volume_dimensions_normalized); + + // initialize transfer functions ////////////////////////////////////////////////////////////// + _alpha_transfer.clear(); + _color_transfer.clear(); + +#if 0 + _alpha_transfer.add_stop(0, 1.0f); + _alpha_transfer.add_stop(0.45f, 0.0f); + _alpha_transfer.add_stop(0.50f, 0.0f); + _alpha_transfer.add_stop(0.55f, 0.0f); + _alpha_transfer.add_stop(1.0f, 1.0f); +#else + _alpha_transfer.add_stop(0.0f, 0.0f); + _alpha_transfer.add_stop(0.3f, 0.0f); + _alpha_transfer.add_stop(0.4f, 0.2f); + _alpha_transfer.add_stop(0.7f, 0.0f); + _alpha_transfer.add_stop(1.0f, 1.0f); +#endif + +#if 0 + // blue-white-red + _color_transfer.add_stop(0, scm::math::vec3f(1.0f, 0.0f, 0.0f)); + _color_transfer.add_stop(0.5, scm::math::vec3f(1.0f, 1.0f, 1.0f)); + _color_transfer.add_stop(1.0f, scm::math::vec3f(0.0f, 0.0f, 1.0f)); +#else + // blue-white-red + _color_transfer.add_stop(0.0f, scm::math::vec3f(0.0f, 0.0f, 0.0f)); + _color_transfer.add_stop(0.5f, scm::math::vec3f(0.4f, 0.0f, 0.4f)); + _color_transfer.add_stop(0.7f, scm::math::vec3f(1.0f, 1.0f, 1.0f)); + _color_transfer.add_stop(1.0f, scm::math::vec3f(1.0f, 1.0f, 1.0f)); +#endif + + } + + //////////////////////////////////////////////////////////////////////////////// + + void Volume::upload_to(RenderContext const& ctx) const { + + //if (!_volume_texture_ptr[ctx.id]) { + // WARNING("Unable to load Volume! Has no volume data."); + // return; + //} + + std::unique_lock lock(upload_mutex_); + + if (_volume_boxes_ptr.size() <= ctx.id){ + _volume_texture_ptr.resize(ctx.id + 1); + _transfer_texture_ptr.resize(ctx.id + 1); + _volume_boxes_ptr.resize(ctx.id + 1); + _sstate.resize(ctx.id + 1); + } + + //scm::gl::volume_loader scm_volume_loader; + + //texture_3d_ptr load_texture_3d(render_device& in_device, + // const std::string& in_image_path, + // bool in_create_mips, + // bool in_color_mips = false, + // const data_format in_force_internal_format = FORMAT_NULL); + + _volume_texture_ptr[ctx.id] = std::shared_ptr(new Texture3D(_volume_file_path));// scm_volume_loader.load_texture_3d(*(ctx.render_device.get()), _volume_file_path, false); + _volume_texture_ptr[ctx.id]->upload_to(ctx); + + MESSAGE("%s loaded!", _volume_file_path.c_str()); + + //scm::gl::texture_loader scm_image_loader; + _transfer_texture_ptr[ctx.id] = create_color_map(ctx, 255, _alpha_transfer, _color_transfer); + _transfer_texture_ptr[ctx.id]->upload_to(ctx); + + //box_volume_geometry + _volume_boxes_ptr[ctx.id] = + scm::gl::box_volume_geometry_ptr(new scm::gl::box_volume_geometry(ctx.render_device, scm::math::vec3(0.0), _volume_dimensions_normalized)); + + _sstate[ctx.id] = ctx.render_device->create_sampler_state( + scm::gl::FILTER_MIN_MAG_MIP_LINEAR, scm::gl::WRAP_CLAMP_TO_EDGE); + } + + std::shared_ptr + Volume::create_color_map(RenderContext const& ctx, + unsigned in_size, + const scm::data::piecewise_function_1d& in_alpha, + const scm::data::piecewise_function_1d& in_color) const + { + using namespace scm::gl; + using namespace scm::math; + + scm::scoped_array color_lut; + scm::scoped_array alpha_lut; + + color_lut.reset(new vec3f[in_size]); + alpha_lut.reset(new float[in_size]); + + if (!scm::data::build_lookup_table(color_lut, in_color, in_size) + || !scm::data::build_lookup_table(alpha_lut, in_alpha, in_size)) { + std::cout << "Volume::create_color_map(): error during lookuptable generation" << std::endl; + return (std::shared_ptr(new Texture2D(in_size, 1))); + } + scm::scoped_array combined_lut; + + combined_lut.reset(new float[in_size * 4]); + + for (unsigned i = 0; i < in_size; ++i) { + combined_lut[i * 4] = color_lut[i].x; + combined_lut[i * 4 + 1] = color_lut[i].y; + combined_lut[i * 4 + 2] = color_lut[i].z; + combined_lut[i * 4 + 3] = alpha_lut[i]; + } + + //for (unsigned i = 0; i < in_size; ++i) { + // combined_lut[i * 4] = i; + // combined_lut[i * 4 + 1] = i; + // combined_lut[i * 4 + 2] = i; + // combined_lut[i * 4 + 3] = 1.0; + //} + + std::vector in_data; + in_data.push_back(combined_lut.get()); + + std::shared_ptr new_tex = + std::shared_ptr(new Texture2D(in_size, 1, FORMAT_RGBA_32F, in_data));// ctx.render_device->create_texture_2d(scm::math::vec2ui(in_size, 1), FORMAT_RGBA_8, 1, 1, 1, FORMAT_RGBA_32F, in_data); + + if (!new_tex) { + std::cerr << "Volume::create_color_map(): error during color map texture generation." << std::endl; + return (std::shared_ptr(new Texture2D(in_size, 1))); + } + else{ + std::cout << "Volume::create_color_map(): color map texture generated." << std::endl; + return (new_tex); + } + } + + bool Volume::update_color_map(RenderContext const& ctx, + std::shared_ptr transfer_texture_ptr, + const scm::data::piecewise_function_1d& in_alpha, + const scm::data::piecewise_function_1d& in_color) const + { + using namespace scm::gl; + using namespace scm::math; + + scm::scoped_array color_lut; + scm::scoped_array alpha_lut; + + unsigned in_size = transfer_texture_ptr->width(); + + color_lut.reset(new vec3f[in_size]); + alpha_lut.reset(new float[in_size]); + + if (!scm::data::build_lookup_table(color_lut, in_color, in_size) + || !scm::data::build_lookup_table(alpha_lut, in_alpha, in_size)) { + MESSAGE("volume_data::update_color_alpha_map(): error during lookuptable generation"); + return false; + } + scm::scoped_array combined_lut; + + combined_lut.reset(new float[in_size * 4]); + + for (unsigned i = 0; i < in_size; ++i) { + combined_lut[i * 4] = color_lut[i].x; + combined_lut[i * 4 + 1] = color_lut[i].y; + combined_lut[i * 4 + 2] = color_lut[i].z; + combined_lut[i * 4 + 3] = alpha_lut[i]; + } + + MESSAGE("generating color map texture data done."); + + MESSAGE("uploading texture data ( size: %d KiB)...", static_cast(in_size * size_of_format(FORMAT_RGBA_32F)) / (1024.0)); + + texture_region ur(vec3ui(0u), vec3ui(in_size, 1, 1)); + bool res = ctx.render_context->update_sub_texture(transfer_texture_ptr->get_buffer(ctx), ur, 0u, FORMAT_RGBA_32F, combined_lut.get()); + + MESSAGE("uploading texture data done."); + + if (!res) { + MESSAGE("Volume::update_color_alpha_map(): error during color map texture generation."); + return false; + } + + return true; + } + + //////////////////////////////////////////////////////////////////////////////// + + void Volume::draw(RenderContext const& ctx) const { + + // upload to GPU if neccessary + if (_volume_boxes_ptr.size() <= ctx.id || _volume_boxes_ptr[ctx.id] == nullptr) { + upload_to(ctx); + } + + if (_update_transfer_function){ + for (auto color_map_texture : _transfer_texture_ptr) + { + update_color_map(ctx, color_map_texture, _alpha_transfer, _color_transfer); + } + _update_transfer_function = false; + } + + scm::gl::context_vertex_input_guard vig(ctx.render_context); + + ctx.render_context->bind_texture( _volume_texture_ptr[ctx.id]->get_buffer(ctx), _sstate[ctx.id], 5); + ctx.render_context->bind_texture(_transfer_texture_ptr[ctx.id]->get_buffer(ctx), _sstate[ctx.id], 6); + scm::gl::program_ptr p = ctx.render_context->current_program(); + p->uniform_sampler("volume_texture", 5); + p->uniform_sampler("transfer_texture", 6); + p->uniform("sampling_distance", _step_size); + //p->uniform("iso_value", 0.8f); + p->uniform("volume_bounds", _volume_dimensions_normalized); + + ctx.render_context->apply(); + _volume_boxes_ptr[ctx.id]->draw(ctx.render_context); + } + + void Volume::draw_proxy(RenderContext const& ctx) const { + + // upload to GPU if neccessary + if (_volume_boxes_ptr.size() <= ctx.id || _volume_boxes_ptr[ctx.id] == nullptr) { + upload_to(ctx); + } + + scm::gl::context_vertex_input_guard vig(ctx.render_context); + + ctx.render_context->apply(); + _volume_boxes_ptr[ctx.id]->draw(ctx.render_context); + } + + void Volume::set_uniforms(RenderContext const& ctx, ShaderProgram* cs) const + { + if (_update_transfer_function){ + for (auto color_map_texture : _transfer_texture_ptr) + { + update_color_map(ctx, color_map_texture, _alpha_transfer, _color_transfer); + } + _update_transfer_function = false; + } + + //_volume_texture_ptr[ctx.id]->make_resident(ctx); + //_transfer_texture_ptr[ctx.id]->make_resident(ctx); + + if (!_transfer_texture_ptr[ctx.id]){ + std::cerr << "No Transfer Texture2D ptr!" << std::endl; + return; + } + + cs->set_uniform(ctx, _volume_texture_ptr[ctx.id], "volume_texture"); + cs->set_uniform(ctx, _transfer_texture_ptr[ctx.id], "transfer_texture"); + cs->set_uniform(ctx, _step_size, "sampling_distance"); + cs->set_uniform(ctx, _volume_dimensions_normalized, "volume_bounds"); + + //_volume_texture_ptr[ctx.id]->make_non_resident(ctx); + //_transfer_texture_ptr[ctx.id]->make_non_resident(ctx); + + } + + //////////////////////////////////////////////////////////////////////////////// + + void Volume::ray_test(Ray const& ray, PickResult::Options options, + Node* owner, std::set& hits) { + + //kd_tree_.ray_test(ray, mesh_, options, owner, hits); + } + + //////////////////////////////////////////////////////////////////////////////// + + float Volume::step_size() const + { + return _step_size; + } + + void Volume::step_size(const float in_step_size) + { + _step_size = in_step_size; + } + + void Volume::set_transfer_function(const scm::data::piecewise_function_1d& in_alpha, const scm::data::piecewise_function_1d& in_color) + { + _alpha_transfer.clear(); + _color_transfer.clear(); + + _alpha_transfer = in_alpha; + _color_transfer = in_color; + + _update_transfer_function = true; + } +} diff --git a/src/gua/renderer/VolumeLoader.cpp b/src/gua/renderer/VolumeLoader.cpp new file mode 100644 index 000000000..3b65e9c83 --- /dev/null +++ b/src/gua/renderer/VolumeLoader.cpp @@ -0,0 +1,142 @@ +/****************************************************************************** +* guacamole - delicious VR * +* * +* Copyright: (c) 2011-2013 Bauhaus-Universität Weimar * +* Contact: felix.lauer@uni-weimar.de / simon.schneegans@uni-weimar.de * +* * +* This program is free software: you can redistribute it and/or modify it * +* under the terms of the GNU General Public License as published by the Free * +* Software Foundation, either version 3 of the License, or (at your option) * +* any later version. * +* * +* This program is distributed in the hope that it will be useful, but * +* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * +* for more details. * +* * +* You should have received a copy of the GNU General Public License along * +* with this program. If not, see . * +* * +******************************************************************************/ + +// class header +#include "gua/renderer/VolumeLoader.hpp" + +// guacamole headers +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include + + +namespace gua { + + //////////////////////////////////////////////////////////////////////////////// + + std::unordered_map> + VolumeLoader::loaded_files_ = + std::unordered_map>(); + + //////////////////////////////////////////////////////////////////////////////// + VolumeLoader::VolumeLoader() : LoaderBase(), _supported_file_extensions() { + _supported_file_extensions.insert("raw"); + _supported_file_extensions.insert("vol"); + } + + //VolumeLoader::VolumeLoader() + // {} + + std::shared_ptr VolumeLoader::create_volume_from_file(std::string const& node_name, + std::string const& file_name, + unsigned flags) + { + + std::shared_ptr cached_node; + std::string key(file_name + "_" + string_utils::to_string(flags)); + + auto searched(loaded_files_.find(key)); + if (searched != loaded_files_.end()) { + + cached_node = searched->second; + + } + else { + + if (is_supported(file_name)) { + cached_node = load(file_name, flags); + cached_node->update_cache(); + loaded_files_.insert(std::make_pair(key, cached_node)); + + // normalize volume position and rotation + if (flags & VolumeLoader::NORMALIZE_POSITION || flags & VolumeLoader::NORMALIZE_SCALE) { + auto bbox = cached_node->get_bounding_box(); + + if (flags & VolumeLoader::NORMALIZE_POSITION) { + auto center((bbox.min + bbox.max)*0.5); + cached_node->translate(-center); + } + + if (flags & VolumeLoader::NORMALIZE_SCALE) { + auto size(bbox.max - bbox.min); + auto max_size(std::max(std::max(size.x, size.y), size.z)); + cached_node->scale(1.f / max_size); + } + + } + } + + if (!cached_node) { + + WARNING("Unable to load %s: Volume Type is not supported!", file_name.c_str()); + } + } + + if (cached_node) { + auto copy(cached_node->deep_copy()); + + copy->set_name(node_name); + return copy; + } + + return std::make_shared(node_name); + } + + std::shared_ptr VolumeLoader::load(std::string const& file_name, + unsigned flags) + { + try { + GeometryDatabase::instance()->add( + file_name, std::make_shared(file_name)); + + auto result = std::make_shared("unnamed_volume"); + result->data.set_volume(file_name); + + return result; + + } + catch (std::exception &e) { + WARNING("Warning: \"%s\" \n", e.what()); + WARNING("Failed to load Volume object \"%s\": ", file_name.c_str()); + return nullptr; + } + } + + bool VolumeLoader::is_supported(std::string const& file_name) const { + std::vector filename_decomposition = + gua::string_utils::split(file_name, '.'); + return filename_decomposition.empty() + ? false + : _supported_file_extensions.count(filename_decomposition.back()) > + 0; + } + +} diff --git a/src/gua/scenegraph/VolumeNode.cpp b/src/gua/scenegraph/VolumeNode.cpp index db51d76df..1dd31b311 100644 --- a/src/gua/scenegraph/VolumeNode.cpp +++ b/src/gua/scenegraph/VolumeNode.cpp @@ -25,7 +25,7 @@ // guacamole headers #include #include -#include +#include #include #include #include @@ -70,122 +70,7 @@ namespace gua { // first of all, check bbox auto box_hits(ray.intersect(bounding_box_)); - // ray did not intersect bbox -- therefore it wont intersect - if (box_hits.first == RayNode::END && box_hits.second == RayNode::END) { - return; - } - - // return if only first object shall be returned and the current first hit - // is in front of the bbox entry point and the ray does not start inside - // the bbox - if (options & PickResult::PICK_ONLY_FIRST_OBJECT - && hits.size() > 0 && hits.begin()->distance < box_hits.first - && box_hits.first != Ray::END) { - - return; - } - - // bbox is intersected, but check geometry only if mask tells us to check - if (data.get_volume() != "" && mask.check(get_groups())) { - - auto geometry(GeometryDatabase::instance()->lookup(data.get_volume())); - - if (geometry) { - - bool check_kd_tree(true); - - math::mat4 world_transform(get_world_transform()); - - // check for bounding box intersection of contained geometry if node - // has children (in this case, the bbox might be larger - // than the actual geometry) - if (has_children()) { - auto geometry_bbox(geometry->get_bounding_box()); - - math::BoundingBox inner_bbox; - inner_bbox.expandBy(world_transform * geometry_bbox.min); - inner_bbox.expandBy(world_transform * geometry_bbox.max); - inner_bbox.expandBy(world_transform * - math::vec3(geometry_bbox.min.x, - geometry_bbox.min.y, - geometry_bbox.max.z)); - inner_bbox.expandBy(world_transform * - math::vec3(geometry_bbox.min.x, - geometry_bbox.max.y, - geometry_bbox.min.z)); - inner_bbox.expandBy(world_transform * - math::vec3(geometry_bbox.min.x, - geometry_bbox.max.y, - geometry_bbox.max.z)); - inner_bbox.expandBy(world_transform * - math::vec3(geometry_bbox.max.x, - geometry_bbox.min.y, - geometry_bbox.max.z)); - inner_bbox.expandBy(world_transform * - math::vec3(geometry_bbox.max.x, - geometry_bbox.max.y, - geometry_bbox.min.z)); - inner_bbox.expandBy(world_transform * - math::vec3(geometry_bbox.max.x, - geometry_bbox.min.y, - geometry_bbox.min.z)); - - auto inner_hits(ray.intersect(inner_bbox)); - if (inner_hits.first == RayNode::END && - inner_hits.second == RayNode::END) - check_kd_tree = false; - } - - if (check_kd_tree) { - Ray world_ray(ray.get_world_ray()); - - math::mat4 ori_transform(scm::math::inverse(world_transform)); - - math::vec4 ori(world_ray.origin_[0], - world_ray.origin_[1], - world_ray.origin_[2], - 1.0); - math::vec4 dir(world_ray.direction_[0], - world_ray.direction_[1], - world_ray.direction_[2], - 0.0); - - ori = ori_transform * ori; - dir = ori_transform * dir; - - Ray object_ray(ori, dir, world_ray.t_max_); - geometry->ray_test(object_ray, options, this, hits); - - float const inf(std::numeric_limits::max()); - - if (options & PickResult::GET_WORLD_POSITIONS) { - - for (auto& hit: hits) { - if (hit.world_position == math::vec3(inf, inf, inf)) { - auto transformed(world_transform * math::vec4(hit.position.x, hit.position.y, hit.position.z, 0.0)); - hit.world_position = scm::math::vec3(transformed.x, transformed.y, transformed.z); - } - } - } - - if (options & PickResult::GET_WORLD_NORMALS) { - - math::mat4 normal_matrix(scm::math::inverse(scm::math::transpose(world_transform))); - for (auto& hit: hits) { - if (hit.world_normal == math::vec3(inf, inf, inf)) { - auto transformed(normal_matrix * math::vec4(hit.normal.x, hit.normal.y, hit.normal.z, 0.0)); - hit.world_normal = scm::math::normalize(scm::math::vec3(transformed.x, transformed.y, transformed.z)); - } - } - } - } - } - } - - for (auto child : get_children()) { - // test for intersection with each child - child->ray_test_impl(ray, options, mask, hits); - } + return; } diff --git a/src/gua/utils/DotGenerator.cpp b/src/gua/utils/DotGenerator.cpp index 3d2d42198..6f78ec0b0 100644 --- a/src/gua/utils/DotGenerator.cpp +++ b/src/gua/utils/DotGenerator.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -113,6 +114,24 @@ void DotGenerator::parse_graph(SceneGraph const* graph) { child->accept(*this); } + +//////////////////////////////////////////////////////////////////////////////// +/* virtual */ void DotGenerator::visit(VolumeNode* volume) { + pre_node_info(volume); + + std::string fillcolor("[fillcolor ="); + fillcolor += " \"#CCEECC\""; + if (volume->data.get_volume() != "") + parse_data_ += "| volume: " + volume->data.get_volume(); + + fillcolor += "]"; + + post_node_info(volume, fillcolor); + + for (auto child : volume->children_) + child->accept(*this); +} + //////////////////////////////////////////////////////////////////////////////// /* virtual */ void DotGenerator::visit(PointLightNode* pointlight) { pre_node_info(pointlight); From eb48defd2bea0ae8058b44634b262c0451cd838c Mon Sep 17 00:00:00 2001 From: gandideluxe Date: Thu, 28 Nov 2013 22:56:25 +0100 Subject: [PATCH 050/146] texture upload_to() change from protected to public --- include/gua/renderer/Texture.hpp | 5 +++-- include/gua/renderer/Texture2D.hpp | 6 +++--- include/gua/renderer/Texture3D.hpp | 3 ++- src/gua/renderer/Texture2D.cpp | 4 ++-- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/include/gua/renderer/Texture.hpp b/include/gua/renderer/Texture.hpp index 1ace0dfbd..67e8800fc 100644 --- a/include/gua/renderer/Texture.hpp +++ b/include/gua/renderer/Texture.hpp @@ -116,6 +116,8 @@ class GUA_DLL Texture { void make_non_resident(RenderContext const& context) const; void make_non_resident() const; + virtual void upload_to(RenderContext const& context) const = 0; + protected: mutable unsigned mipmap_layers_; scm::gl::data_format color_format_; @@ -129,8 +131,7 @@ class GUA_DLL Texture { #else mutable std::mutex upload_mutex_; #endif - virtual void upload_to(RenderContext const& context) const = 0; - + std::vector data_; std::string file_name_; diff --git a/include/gua/renderer/Texture2D.hpp b/include/gua/renderer/Texture2D.hpp index b98c578e3..e70e93977 100644 --- a/include/gua/renderer/Texture2D.hpp +++ b/include/gua/renderer/Texture2D.hpp @@ -114,14 +114,14 @@ namespace gua { unsigned width() const { return width_; } unsigned height() const { return height_; } + virtual void upload_to(RenderContext const& context) const; + ///@} protected: mutable unsigned width_; mutable unsigned height_; - - virtual void upload_to(RenderContext const& context) const; - + private: }; diff --git a/include/gua/renderer/Texture3D.hpp b/include/gua/renderer/Texture3D.hpp index e774a106d..39723b51f 100644 --- a/include/gua/renderer/Texture3D.hpp +++ b/include/gua/renderer/Texture3D.hpp @@ -120,6 +120,7 @@ class GUA_DLL Texture3D : public Texture { inline unsigned height() const { return height_; } inline unsigned depth() const { return depth_; } + virtual void upload_to(RenderContext const& context) const; ///@} protected: @@ -127,7 +128,7 @@ class GUA_DLL Texture3D : public Texture { mutable unsigned height_; mutable unsigned depth_; - virtual void upload_to(RenderContext const& context) const; + private: diff --git a/src/gua/renderer/Texture2D.cpp b/src/gua/renderer/Texture2D.cpp index 4d8a34350..fdfa0caf5 100644 --- a/src/gua/renderer/Texture2D.cpp +++ b/src/gua/renderer/Texture2D.cpp @@ -68,7 +68,7 @@ void Texture2D::upload_to(RenderContext const& context) const { sampler_states_.resize(context.id + 1); render_contexts_.resize(context.id + 1); } - + if (file_name_ == "") { @@ -77,7 +77,7 @@ void Texture2D::upload_to(RenderContext const& context) const { math::vec2ui(width_, height_), color_format_, mipmap_layers_); else textures_[context.id] = context.render_device->create_texture_2d( - scm::gl::texture_2d_desc( + scm::gl::texture_2d_desc( math::vec2ui(width_, height_), color_format_, mipmap_layers_), color_format_, data_); From 96f8bb2a0078d3d4f2f2c7ca6e32b0b42e43e5f1 Mon Sep 17 00:00:00 2001 From: gandideluxe Date: Fri, 29 Nov 2013 00:01:48 +0100 Subject: [PATCH 051/146] Cleanup Volume Rendering Code in compose.frag --- .../uber_shaders/composite/compose.frag | 28 +++++-------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/resources/shaders/uber_shaders/composite/compose.frag b/resources/shaders/uber_shaders/composite/compose.frag index b38d0455e..b39b0ca16 100644 --- a/resources/shaders/uber_shaders/composite/compose.frag +++ b/resources/shaders/uber_shaders/composite/compose.frag @@ -55,28 +55,17 @@ float get_depth_z(vec3 world_position) { float ndc = pos.z/pos.w; return ((gl_DepthRange.diff * ndc) + gl_DepthRange.near + gl_DepthRange.far) / 2.0; - //gd = (((gl_DepthRange.diff) * ndc) + gl_DepthRange.near + gl_DepthRange.far) / 2.0; - //gd * 2.0 - gl_DepthRange.near + gl_DepthRange.far) = gl_DepthRange.diff * ndc; - //(gd * 2.0 - gl_DepthRange.near + gl_DepthRange.far)/gl_DepthRange.diff = ndc; } float get_depth_linear(float depth_buffer_d) { - //float z_n = 2.0 * depth_buffer_d - 1.0; - //float z_e = 2.0 * zNear * zFar / (zFar + zNear - z_n * (zFar - zNear)); - //float z_e = 2.0 * gl_DepthRange.near * gl_DepthRange.far / (gl_DepthRange.far + gl_DepthRange.near - z_n * (gl_DepthRange.far - gl_DepthRange.near)); - //return z_e; float ndc = (depth_buffer_d * 2.0 - gl_DepthRange.near - gl_DepthRange.far)/gl_DepthRange.diff; - vec4 enit = vec4(gl_FragCoord.xy * 2.0 - vec2(1.0), ndc, 1.0); - vec4 enit_inv = (gua_inverse_projection_view_matrix * enit); enit_inv /= enit_inv.w; - return enit_inv.z; } - bool inside_volume_bounds(const in vec3 sampling_position) { @@ -85,11 +74,13 @@ inside_volume_bounds(const in vec3 sampling_position) } vec4 get_raycast_color(vec3 gua_object_volume_position, - vec3 object_ray, - float d_gbuffer, - float d_volume, - float d_step){ + float d_gbuffer, + float d_volume){ + mat4 gua_invers_model_matrix = inverse(gua_model_matrix); + vec3 object_ray = normalize(gua_object_volume_position - (gua_invers_model_matrix * vec4(gua_camera_position, 1.0)).xyz); + float d_step = abs(-1.0 * get_depth_linear(get_depth_z((gua_model_matrix * vec4(gua_object_volume_position + object_ray * sampling_distance, 1.0)).xyz)) - d_volume); + vec3 obj_to_tex = vec3(1.0) / volume_bounds; vec3 ray_increment = object_ray * sampling_distance; vec3 sampling_pos = gua_object_volume_position + ray_increment; // test, increment just to be sure we are in the volume @@ -101,8 +92,6 @@ vec4 get_raycast_color(vec3 gua_object_volume_position, int d_steps = int(abs(d_volume - d_gbuffer) / d_step); int d_step_cur = 0; - //return vec4(d_diff, d_diff, d_diff, 1.0); - while (inside_volume && (d_step_cur < d_steps)) { ++d_step_cur; // get sample @@ -140,10 +129,7 @@ void main() { gua_object_volume_position.y != 0.00 || gua_object_volume_position.z != 0.00)) { - mat4 gua_invers_model_matrix = inverse(gua_model_matrix); - vec3 object_ray = normalize(gua_object_volume_position - (gua_invers_model_matrix * vec4(gua_camera_position, 1.0)).xyz); - float d_step = abs(-1.0 * get_depth_linear(get_depth_z((gua_model_matrix * vec4(gua_object_volume_position + object_ray * sampling_distance, 1.0)).xyz)) - d_volume); - vec4 compositing_color = get_raycast_color(gua_object_volume_position, object_ray, d_gbuffer, d_volume, d_step); + vec4 compositing_color = get_raycast_color(gua_object_volume_position, d_gbuffer, d_volume); vec3 gbuffer_color = texture2D(gua_get_float_sampler(gua_color_gbuffer_in), gua_get_quad_coords()).xyz; From 5c544529aeff7176a7250c730986af69a8422c0d Mon Sep 17 00:00:00 2001 From: thelaui Date: Fri, 29 Nov 2013 10:35:55 +0100 Subject: [PATCH 052/146] removed print_shaders() from CompositePass. PLEASE REMOVE DEBUGGING STUFF BEFORE COMMITING TO develop --- src/gua/renderer/CompositePass.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/gua/renderer/CompositePass.cpp b/src/gua/renderer/CompositePass.cpp index b68b7b5cc..eac31574d 100644 --- a/src/gua/renderer/CompositePass.cpp +++ b/src/gua/renderer/CompositePass.cpp @@ -50,7 +50,7 @@ CompositePass::CompositePass(Pipeline* pipeline) : ray_generation_shader_->create_from_sources(ray_generation_vertex_shader, ray_generation_fragment_shader); - print_shaders("debug", "composite.txt"); + // print_shaders("debug", "composite.txt"); } //////////////////////////////////////////////////////////////////////////////// @@ -147,9 +147,9 @@ void CompositePass::create(RenderContext const& ctx, // bind target fbo and set viewport target->bind(ctx); ctx.render_context->set_viewport(scm::gl::viewport( - math::vec2(0, 0), + math::vec2(0, 0), ::scm::math::vec2f(target->width(), target->height()))); - + composite_shader_->use(ctx); { fullscreen_quad_->draw(ctx.render_context); From 4a416c9cc4b43fb024ebded96a595996e8e74007 Mon Sep 17 00:00:00 2001 From: thelaui Date: Fri, 29 Nov 2013 10:35:55 +0100 Subject: [PATCH 053/146] removed print_shaders() from CompositePass. PLEASE REMOVE DEBUGGING STUFF BEFORE COMMITTING TO develop --- src/gua/renderer/CompositePass.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/gua/renderer/CompositePass.cpp b/src/gua/renderer/CompositePass.cpp index b68b7b5cc..eac31574d 100644 --- a/src/gua/renderer/CompositePass.cpp +++ b/src/gua/renderer/CompositePass.cpp @@ -50,7 +50,7 @@ CompositePass::CompositePass(Pipeline* pipeline) : ray_generation_shader_->create_from_sources(ray_generation_vertex_shader, ray_generation_fragment_shader); - print_shaders("debug", "composite.txt"); + // print_shaders("debug", "composite.txt"); } //////////////////////////////////////////////////////////////////////////////// @@ -147,9 +147,9 @@ void CompositePass::create(RenderContext const& ctx, // bind target fbo and set viewport target->bind(ctx); ctx.render_context->set_viewport(scm::gl::viewport( - math::vec2(0, 0), + math::vec2(0, 0), ::scm::math::vec2f(target->width(), target->height()))); - + composite_shader_->use(ctx); { fullscreen_quad_->draw(ctx.render_context); From 43fa386bbea2d1b5af6baabfb72fe03f4ef5376d Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Fri, 29 Nov 2013 11:18:17 +0100 Subject: [PATCH 054/146] fixed bug in find_schism.cmake --- cmake/modules/find_schism.cmake | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/cmake/modules/find_schism.cmake b/cmake/modules/find_schism.cmake index 776382577..28822a8ea 100644 --- a/cmake/modules/find_schism.cmake +++ b/cmake/modules/find_schism.cmake @@ -2,13 +2,13 @@ # search paths ############################################################################## SET(SCHISM_INCLUDE_SEARCH_DIRS - ${GLOBAL_EXT_DIR}/inc/schism + ${SCHISM_INCLUDE_DIRS} ${SCHISM_INCLUDE_SEARCH_DIR} /opt/schism/current ) SET(SCHISM_LIBRARY_SEARCH_DIRS - ${GLOBAL_EXT_DIR}/lib + ${SCHISM_LIBRARY_DIRS} ${SCHISM_LIBRARY_SEARCH_DIR} ../ /opt/schism/current/lib/linux_x86 @@ -18,7 +18,7 @@ SET(SCHISM_LIBRARY_SEARCH_DIRS # feedback to provide user-defined paths to search for schism ############################################################################## MACRO (request_schism_search_directories) - + IF ( NOT SCHISM_INCLUDE_DIRS AND NOT SCHISM_LIBRARY_DIRS ) SET(SCHISM_INCLUDE_SEARCH_DIR "Please provide schism include path." CACHE PATH "path to schism headers.") SET(SCHISM_LIBRARY_SEARCH_DIR "Please provide schism library path." CACHE PATH "path to schism libraries.") @@ -37,7 +37,7 @@ MACRO (request_schism_search_directories) MESSAGE(FATAL_ERROR "find_schism.cmake: unable to find schism libraries.") ELSE ( NOT SCHISM_LIBRARY_DIRS ) UNSET(SCHISM_LIBRARY_SEARCH_DIR CACHE) - ENDIF ( NOT SCHISM_LIBRARY_DIRS ) + ENDIF ( NOT SCHISM_LIBRARY_DIRS ) ENDMACRO (request_schism_search_directories) @@ -62,7 +62,7 @@ IF ( NOT SCHISM_INCLUDE_DIRS ) IF (NOT _SCHISM_FOUND_INC_DIRS) request_schism_search_directories() ENDIF (NOT _SCHISM_FOUND_INC_DIRS) - + FOREACH(_INC_DIR ${_SCHISM_FOUND_INC_DIRS}) LIST(APPEND _SCHISM_INCLUDE_DIRS ${_INC_DIR}/scm_cl_core/src) LIST(APPEND _SCHISM_INCLUDE_DIRS ${_INC_DIR}/scm_core/src) @@ -107,14 +107,14 @@ IF ( SCHISM_INCLUDE_DIRS AND ( NOT SCHISM_LIBRARY_DIRS OR NOT SCHISM_LIBRARIES)) ELSE (NOT _SCHISM_FOUND_LIB_DIR) SET(SCHISM_LIBRARY_DIRS ${_SCHISM_FOUND_LIB_DIR} CACHE STRING "The schism library directory.") ENDIF (NOT _SCHISM_FOUND_LIB_DIR) - + SET(_SCHISM_LIBRARIES "") FOREACH(_LIB_DIR ${_SCHISM_FOUND_LIB_DIR}) IF (UNIX) - file(GLOB SCHISM_LIBRARIES ${_LIB_DIR}/*.so) + file(GLOB _SCHISM_LIBRARIES ${_LIB_DIR}/*.so) ELSEIF(WIN32) - file(GLOB _SCHISM_LIBRARY_ABSOLUTE_PATHS ${_LIB_DIR}/release/scm*.lib) + file(GLOB _SCHISM_LIBRARY_ABSOLUTE_PATHS ${_LIB_DIR}/release/scm*.lib) FOREACH (_SCHISM_LIB_PATH ${_SCHISM_LIBRARY_ABSOLUTE_PATHS}) SET(_SCHISM_LIB_FILENAME "") GET_FILENAME_COMPONENT(_SCHISM_LIB_FILENAME ${_SCHISM_LIB_PATH} NAME) @@ -126,7 +126,7 @@ IF ( SCHISM_INCLUDE_DIRS AND ( NOT SCHISM_LIBRARY_DIRS OR NOT SCHISM_LIBRARIES)) IF (_SCHISM_FOUND_LIB_DIR) SET(SCHISM_LIBRARIES ${_SCHISM_LIBRARIES} CACHE STRING "schism libraries.") ENDIF (_SCHISM_FOUND_LIB_DIR) - + ENDIF ( SCHISM_INCLUDE_DIRS AND ( NOT SCHISM_LIBRARY_DIRS OR NOT SCHISM_LIBRARIES)) ############################################################################## From 9fabde3c634895ea0ba810a1b4294c5442b3c125 Mon Sep 17 00:00:00 2001 From: thelaui Date: Fri, 29 Nov 2013 11:29:47 +0100 Subject: [PATCH 055/146] added test printing --- src/gua/scenegraph/ScreenNode.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/gua/scenegraph/ScreenNode.cpp b/src/gua/scenegraph/ScreenNode.cpp index 17db27739..9ddb66124 100644 --- a/src/gua/scenegraph/ScreenNode.cpp +++ b/src/gua/scenegraph/ScreenNode.cpp @@ -31,7 +31,9 @@ namespace gua { ScreenNode::ScreenNode(std::string const& name, Configuration const& configuration, math::mat4 const& transform) - : Node(name, transform), data(configuration) {} + : Node(name, transform), data(configuration) { + std::cout << "screen!" << std::endl; + } /* virtual */ void ScreenNode::accept(NodeVisitor& visitor) { From 239a2a1637fbcf6477dd09b059604566c49a80a7 Mon Sep 17 00:00:00 2001 From: thelaui Date: Fri, 29 Nov 2013 11:45:02 +0100 Subject: [PATCH 056/146] fixed bug in serializer which made it impossible to see geometry attached to a screen --- include/gua/renderer/Serializer.hpp | 2 +- src/gua/renderer/Serializer.cpp | 2 +- src/gua/scenegraph/ScreenNode.cpp | 4 +--- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/include/gua/renderer/Serializer.hpp b/include/gua/renderer/Serializer.hpp index 1c9a4f860..fe03b2f2f 100644 --- a/include/gua/renderer/Serializer.hpp +++ b/include/gua/renderer/Serializer.hpp @@ -73,7 +73,7 @@ class Serializer : public NodeVisitor { * * \param cam Pointer to TransformNode */ - /* virtual */ void visit(TransformNode* cam); + /* virtual */ void visit(Node* node); /** * Visits a GeometryNode diff --git a/src/gua/renderer/Serializer.cpp b/src/gua/renderer/Serializer.cpp index a32dd8f2d..6daec14fa 100644 --- a/src/gua/renderer/Serializer.cpp +++ b/src/gua/renderer/Serializer.cpp @@ -120,7 +120,7 @@ void Serializer::check(SerializedScene* output, //////////////////////////////////////////////////////////////////////// -/* virtual */ void Serializer::visit(TransformNode* node) { +/* virtual */ void Serializer::visit(Node* node) { if (is_visible(node)) { visit_children(node); } diff --git a/src/gua/scenegraph/ScreenNode.cpp b/src/gua/scenegraph/ScreenNode.cpp index 9ddb66124..17db27739 100644 --- a/src/gua/scenegraph/ScreenNode.cpp +++ b/src/gua/scenegraph/ScreenNode.cpp @@ -31,9 +31,7 @@ namespace gua { ScreenNode::ScreenNode(std::string const& name, Configuration const& configuration, math::mat4 const& transform) - : Node(name, transform), data(configuration) { - std::cout << "screen!" << std::endl; - } + : Node(name, transform), data(configuration) {} /* virtual */ void ScreenNode::accept(NodeVisitor& visitor) { From 727700f2c725925be10ef358df95adf4bcec8b3e Mon Sep 17 00:00:00 2001 From: Andre Schollmeyer Date: Fri, 29 Nov 2013 14:27:08 +0100 Subject: [PATCH 057/146] added RayNode to dll-interface removed cuda from dependencies --- CMakeLists.txt | 6 -- cmake/modules/find_cuda.cmake | 126 ----------------------------- cmake/modules/find_ovr.cmake | 3 +- include/gua/scenegraph/RayNode.hpp | 2 +- src/CMakeLists.txt | 2 +- 5 files changed, 4 insertions(+), 135 deletions(-) delete mode 100644 cmake/modules/find_cuda.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 646fa48bf..9bc2e0f88 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,7 +42,6 @@ include(define_macros) include(find_compiler) include(find_schism) include(find_boost) -#include(find_cuda) include(find_bullet) include(find_json) @@ -54,9 +53,6 @@ set(LIBS ${GL_LIBRARIES} ${GLEW_LIBRARIES} ${BULLET_LIBRARIES} - ${CUDA_LIBRARIES} - #profiler - #tcmalloc ) set(LIB_PATHS @@ -67,7 +63,6 @@ set(LIB_PATHS ${BOOST_LIBRARY_DIRS} ${BULLET_LIBRARY_DIRS} ${SCHISM_LIBRARY_DIRS} - ${CUDA_LIBRARY_DIRS} ) set(INCLUDE_PATHS @@ -80,7 +75,6 @@ set(INCLUDE_PATHS ${GL_INCLUDE_DIRS} ${GLEW_INCLUDE_DIRS} ${BULLET_INCLUDE_DIRS} - ${CUDA_INCLUDE_DIRS} ) if (NOT CMAKE_BUILD_TYPE) diff --git a/cmake/modules/find_cuda.cmake b/cmake/modules/find_cuda.cmake deleted file mode 100644 index c232d98c6..000000000 --- a/cmake/modules/find_cuda.cmake +++ /dev/null @@ -1,126 +0,0 @@ -############################################################################## -# pre-defined search paths -############################################################################## -SET(CUDA_INCLUDE_SEARCH_DIRS - ${GLOBAL_EXT_DIR}/inc/cuda - /opt/cuda/current/cuda/include - /usr/include - /usr/local/include -) - -SET(CUDA_LIBRARY_SEARCH_DIRS - ${GLOBAL_EXT_DIR}/lib - /opt/cuda/current/cuda/lib - /usr/lib - /usr/local/lib -) - -############################################################################## -# feedback to provide user-defined paths to search for cuda -############################################################################## -MACRO (request_cuda_search_directories) - - IF ( NOT CUDA_INCLUDE_DIRS AND NOT CUDA_LIBRARY_DIRS ) - SET(CUDA_INCLUDE_SEARCH_DIR "Please provide cuda include path." CACHE PATH "path to cuda headers.") - SET(CUDA_LIBRARY_SEARCH_DIR "Please provide cuda library path." CACHE PATH "path to cuda libraries.") - MESSAGE(FATAL_ERROR "find_cuda.cmake: unable to find cuda.") - ENDIF ( NOT CUDA_INCLUDE_DIRS AND NOT CUDA_LIBRARY_DIRS ) - - IF ( NOT CUDA_INCLUDE_DIRS ) - SET(CUDA_INCLUDE_SEARCH_DIR "Please provide cuda include path." CACHE PATH "path to cuda headers.") - MESSAGE(FATAL_ERROR "find_cuda.cmake: unable to find cuda headers.") - ELSE ( NOT CUDA_INCLUDE_DIRS ) - UNSET(CUDA_INCLUDE_SEARCH_DIR CACHE) - ENDIF ( NOT CUDA_INCLUDE_DIRS ) - - IF ( NOT CUDA_LIBRARY_DIRS ) - SET(CUDA_LIBRARY_SEARCH_DIR "Please provide cuda library path." CACHE PATH "path to cuda libraries.") - MESSAGE(FATAL_ERROR "find_cuda.cmake: unable to find cuda libraries.") - ELSE ( NOT CUDA_LIBRARY_DIRS ) - UNSET(CUDA_LIBRARY_SEARCH_DIR CACHE) - ENDIF ( NOT CUDA_LIBRARY_DIRS ) - -ENDMACRO (request_cuda_search_directories) - -############################################################################## -# search -############################################################################## -message(STATUS "-- checking for CUDA") - -IF (NOT CUDA_INCLUDE_DIRS) - - SET(_CUDA_FOUND_INC_DIRS "") - - FOREACH(_SEARCH_DIR ${CUDA_INCLUDE_SEARCH_DIRS}) - FIND_PATH(_CUR_SEARCH - NAMES cuda.h - PATHS ${_SEARCH_DIR} - NO_DEFAULT_PATH) - IF (_CUR_SEARCH) - LIST(APPEND _CUDA_FOUND_INC_DIRS ${_CUR_SEARCH}) - ENDIF(_CUR_SEARCH) - SET(_CUR_SEARCH _CUR_SEARCH-NOTFOUND CACHE INTERNAL "internal use") - ENDFOREACH(_SEARCH_DIR ${CUDA_INCLUDE_SEARCH_DIRS}) - - IF (NOT _CUDA_FOUND_INC_DIRS) - request_cuda_search_directories() - ENDIF (NOT _CUDA_FOUND_INC_DIRS) - - FOREACH(_INC_DIR ${_CUDA_FOUND_INC_DIRS}) - LIST(APPEND _CUDA_INCLUDE_DIRS ${_INC_DIR}) - ENDFOREACH(_INC_DIR ${_BOOST_FOUND_INC_DIRS}) - - IF (_CUDA_FOUND_INC_DIRS) - SET(CUDA_INCLUDE_DIRS ${_CUDA_INCLUDE_DIRS} CACHE PATH "path to cuda headers.") - ENDIF (_CUDA_FOUND_INC_DIRS) - -ENDIF(NOT CUDA_INCLUDE_DIRS) - -IF(UNIX) - LIST(APPEND _CUDA_LIB_FILENAMES "libcudart.so") -ELSEIF(WIN32) - LIST(APPEND _CUDA_LIB_FILENAMES "cudart.lib") - LIST(APPEND _CUDA_LIB_FILENAMES "cuda.lib") -ENDIF(UNIX) - -IF ( CUDA_INCLUDE_DIRS AND NOT CUDA_LIBRARIES ) - - SET(_CUDA_FOUND_LIB_DIR "") - SET(_CUDA_POSTFIX "") - LIST(GET _CUDA_LIB_FILENAMES 0 _CUDA_RUNTIME_LIBRARY) - - FOREACH(_SEARCH_DIR ${CUDA_LIBRARY_SEARCH_DIRS}) - FIND_PATH(_CUR_SEARCH - NAMES ${_CUDA_RUNTIME_LIBRARY} - PATHS ${_SEARCH_DIR} - PATH_SUFFIXES debug release - NO_DEFAULT_PATH) - IF (_CUR_SEARCH) - LIST(APPEND _CUDA_FOUND_LIB_DIR ${_SEARCH_DIR}) - ENDIF(_CUR_SEARCH) - SET(_CUR_SEARCH _CUR_SEARCH-NOTFOUND CACHE INTERNAL "internal use") - ENDFOREACH(_SEARCH_DIR ${CUDA_LIBRARY_SEARCH_DIRS}) - - IF (NOT _CUDA_FOUND_LIB_DIR) - request_cuda_search_directories() - ELSE (NOT _CUDA_FOUND_LIB_DIR) - SET(CUDA_LIBRARY_DIRS ${_CUDA_FOUND_LIB_DIR} CACHE STRING "The cuda library directory.") - message(STATUS "-- found matching version") - ENDIF (NOT _CUDA_FOUND_LIB_DIR) - - IF (_CUDA_FOUND_LIB_DIR) - SET(CUDA_LIBRARIES ${_CUDA_LIB_FILENAMES} CACHE STRING "The cuda libraries.") - ENDIF (_CUDA_FOUND_LIB_DIR) - -ENDIF( CUDA_INCLUDE_DIRS AND NOT CUDA_LIBRARIES ) - -############################################################################## -# verify -############################################################################## -IF ( NOT CUDA_INCLUDE_DIRS OR NOT CUDA_LIBRARY_DIRS ) - request_cuda_search_directories() -ELSE ( NOT CUDA_INCLUDE_DIRS OR NOT CUDA_LIBRARY_DIRS ) - UNSET(CUDA_INCLUDE_SEARCH_DIR CACHE) - UNSET(CUDA_LIBRARY_SEARCH_DIR CACHE) - MESSAGE(STATUS "-- found matching cuda version") -ENDIF ( NOT CUDA_INCLUDE_DIRS OR NOT CUDA_LIBRARY_DIRS ) \ No newline at end of file diff --git a/cmake/modules/find_ovr.cmake b/cmake/modules/find_ovr.cmake index 65d68dc40..8ea0f0127 100644 --- a/cmake/modules/find_ovr.cmake +++ b/cmake/modules/find_ovr.cmake @@ -2,7 +2,7 @@ # search paths ############################################################################## SET(OVR_INCLUDE_SEARCH_DIRS - ${GLOBAL_EXT_DIR}/inc/OculusSDK + ${GLOBAL_EXT_DIR}/inc/ovr ${OVR_INCLUDE_DIRS} ${OVR_INCLUDE_SEARCH_DIR} /opt/OculusSDK/LibOVR/Include @@ -86,6 +86,7 @@ IF ( NOT OVR_LIBRARY_DIRS ) FIND_PATH(_CUR_SEARCH NAMES ${OVR_LIB_FILENAME} PATHS ${_SEARCH_DIR} + PATH_SUFFIXES debug release NO_DEFAULT_PATH) IF (_CUR_SEARCH) LIST(APPEND _OVR_FOUND_LIB_DIR ${_SEARCH_DIR}) diff --git a/include/gua/scenegraph/RayNode.hpp b/include/gua/scenegraph/RayNode.hpp index 3d88b09d3..6dc2f5fc2 100644 --- a/include/gua/scenegraph/RayNode.hpp +++ b/include/gua/scenegraph/RayNode.hpp @@ -33,7 +33,7 @@ namespace gua { -class RayNode : public Node { +class GUA_DLL RayNode : public Node { public: RayNode() {} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bdd3ee83e..b6960a1fe 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -21,4 +21,4 @@ ENDIF(MSVC) TARGET_LINK_LIBRARIES( guacamole debug ${LIBS} optimized ${LIBS}) -ADD_DEPENDENCIES(guacamole guarc) +ADD_DEPENDENCIES(guacamole guarc CompileResources) From 2782557a95c514413621a13c134542ecf14a177c Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Mon, 2 Dec 2013 11:35:38 +0100 Subject: [PATCH 058/146] Fix bug in setting of BOOST_LIBRARIES. --- cmake/modules/find_boost.cmake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/modules/find_boost.cmake b/cmake/modules/find_boost.cmake index 353e04576..f0e0e8b40 100644 --- a/cmake/modules/find_boost.cmake +++ b/cmake/modules/find_boost.cmake @@ -152,10 +152,10 @@ IF ( BOOST_INCLUDE_DIRS AND NOT BOOST_LIBRARY_DIRS ) if (UNIX) FIND_FILE(_CUR_SEARCH_FILE NAMES ${_BOOST_FILESYSTEM_LIB} PATHS ${_CUR_SEARCH}) if (_CUR_SEARCH_FILE) - LIST(APPEND BOOST_LIBRARIES ${_CUR_SEARCH_FILE}) + #LIST(APPEND BOOST_LIBRARIES ${_CUR_SEARCH_FILE}) STRING(REGEX REPLACE "${_CUR_SEARCH}/${CMAKE_SHARED_LIBRARY_PREFIX}boost_filesystem(.*).so(.*)" "\\2" _BOOST_UNIX_LIB_SUF ${_CUR_SEARCH_FILE}) if (${_BOOST_UNIX_LIB_SUF} STREQUAL ${BOOST_LIB_SUFFIX}) - list(APPEND BOOST_LIBRARIES _BOOST_FILESYSTEM_LIB) + #list(APPEND BOOST_LIBRARIES _BOOST_FILESYSTEM_LIB) STRING(REGEX REPLACE "${_CUR_SEARCH}/${CMAKE_SHARED_LIBRARY_PREFIX}boost_filesystem(.*).so(.*)" "\\1" _BOOST_POSTFIX ${_CUR_SEARCH_FILE}) endif (${_BOOST_UNIX_LIB_SUF} STREQUAL ${BOOST_LIB_SUFFIX}) endif (_CUR_SEARCH_FILE) @@ -201,4 +201,4 @@ ELSE ( NOT BOOST_INCLUDE_DIRS OR NOT BOOST_LIBRARY_DIRS ) UNSET(BOOST_INCLUDE_SEARCH_DIR CACHE) UNSET(BOOST_LIBRARY_SEARCH_DIR CACHE) MESSAGE(STATUS "-- found matching boost version") -ENDIF ( NOT BOOST_INCLUDE_DIRS OR NOT BOOST_LIBRARY_DIRS ) \ No newline at end of file +ENDIF ( NOT BOOST_INCLUDE_DIRS OR NOT BOOST_LIBRARY_DIRS ) From b3dbd7d9e822b557e14ae6696dd9dc8d32e4436b Mon Sep 17 00:00:00 2001 From: Andre Schollmeyer Date: Mon, 2 Dec 2013 13:02:55 +0100 Subject: [PATCH 059/146] added external-folder as search dir for schism changed default search path for OVR --- cmake/modules/find_ovr.cmake | 2 +- cmake/modules/find_schism.cmake | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/modules/find_ovr.cmake b/cmake/modules/find_ovr.cmake index 8ea0f0127..40a724d12 100644 --- a/cmake/modules/find_ovr.cmake +++ b/cmake/modules/find_ovr.cmake @@ -2,7 +2,7 @@ # search paths ############################################################################## SET(OVR_INCLUDE_SEARCH_DIRS - ${GLOBAL_EXT_DIR}/inc/ovr + ${GLOBAL_EXT_DIR}/inc/ovr/include ${OVR_INCLUDE_DIRS} ${OVR_INCLUDE_SEARCH_DIR} /opt/OculusSDK/LibOVR/Include diff --git a/cmake/modules/find_schism.cmake b/cmake/modules/find_schism.cmake index 28822a8ea..5a7a5b5ae 100644 --- a/cmake/modules/find_schism.cmake +++ b/cmake/modules/find_schism.cmake @@ -2,13 +2,13 @@ # search paths ############################################################################## SET(SCHISM_INCLUDE_SEARCH_DIRS - ${SCHISM_INCLUDE_DIRS} + ${GLOBAL_EXT_DIR}/inc/schism ${SCHISM_INCLUDE_SEARCH_DIR} /opt/schism/current ) SET(SCHISM_LIBRARY_SEARCH_DIRS - ${SCHISM_LIBRARY_DIRS} + ${GLOBAL_EXT_DIR}/lib ${SCHISM_LIBRARY_SEARCH_DIR} ../ /opt/schism/current/lib/linux_x86 From 3cfe9378d6bf67a1000fbcc2142d2a032cf4e476 Mon Sep 17 00:00:00 2001 From: gandideluxe Date: Mon, 2 Dec 2013 13:55:02 +0100 Subject: [PATCH 060/146] PostFXPass used only pipeline::shading gbuffers, change to pipeline::compositing, TODO: Use pipeline::shading gbuffers only and read write in to this gbuffers using them in pipeline::compositing --- src/gua/renderer/CompositePass.cpp | 10 ++++++---- src/gua/renderer/PostFXPass.cpp | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/gua/renderer/CompositePass.cpp b/src/gua/renderer/CompositePass.cpp index b563a38b2..ad8c30a40 100644 --- a/src/gua/renderer/CompositePass.cpp +++ b/src/gua/renderer/CompositePass.cpp @@ -108,7 +108,7 @@ void CompositePass::create(RenderContext const& ctx, FrameBufferObject* target) { ///TODO: Toplevel - if (!scene.volumenodes_.empty()) + if (!scene.volumenodes_.empty() /*|| !scene.transparentnodes_.empty()*/) { init_ressources(ctx); @@ -131,13 +131,15 @@ void CompositePass::create(RenderContext const& ctx, //fullscreen_quad_->draw(ctx.render_context); for (auto const& node : scene.volumenodes_) { - + auto volume = std::static_pointer_cast(GeometryDatabase::instance()->lookup(node.data.get_volume())); if (volume) { ray_generation_shader_->set_uniform( ctx, node.transform, "gua_model_matrix"); + + //volume->set_uniforms(ctx, ray_generation_shader_); ray_generation_shader_->use(ctx); { @@ -193,9 +195,9 @@ void CompositePass::create(RenderContext const& ctx, target->unbind(ctx); + + ctx.render_context->reset_state_objects(); } - - ctx.render_context->reset_state_objects(); } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/gua/renderer/PostFXPass.cpp b/src/gua/renderer/PostFXPass.cpp index 7802aae79..9b176a5b7 100644 --- a/src/gua/renderer/PostFXPass.cpp +++ b/src/gua/renderer/PostFXPass.cpp @@ -288,7 +288,7 @@ void PostFXPass::render_scene(Camera const& camera, RenderContext const& ctx) { any_godrays = render_godrays(camera, pipeline_->get_current_scene(eye), eye, ctx); render_glow(eye, ctx); - auto input_tex(inputs_[Pipeline::shading]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); + auto input_tex(inputs_[Pipeline::compositing]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); auto ping_tex(ping_buffer_->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); auto pong_tex(pong_buffer_->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); auto normal_tex(inputs_[Pipeline::geometry]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); From 709ec4dcd71ad15bfac874a01257e3cc36578f75 Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Mon, 2 Dec 2013 17:18:22 +0100 Subject: [PATCH 061/146] Remove whitespaces. --- src/gua/renderer/CompositePass.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/gua/renderer/CompositePass.cpp b/src/gua/renderer/CompositePass.cpp index ad8c30a40..60635db6a 100644 --- a/src/gua/renderer/CompositePass.cpp +++ b/src/gua/renderer/CompositePass.cpp @@ -108,7 +108,7 @@ void CompositePass::create(RenderContext const& ctx, FrameBufferObject* target) { ///TODO: Toplevel - if (!scene.volumenodes_.empty() /*|| !scene.transparentnodes_.empty()*/) + if (!scene.volumenodes_.empty() /*|| !scene.transparentnodes_.empty()*/) { init_ressources(ctx); @@ -131,7 +131,7 @@ void CompositePass::create(RenderContext const& ctx, //fullscreen_quad_->draw(ctx.render_context); for (auto const& node : scene.volumenodes_) { - + auto volume = std::static_pointer_cast(GeometryDatabase::instance()->lookup(node.data.get_volume())); @@ -195,7 +195,7 @@ void CompositePass::create(RenderContext const& ctx, target->unbind(ctx); - + ctx.render_context->reset_state_objects(); } } From 2f671cdfeeead37a5e0d31e519170f31c294e088 Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Mon, 2 Dec 2013 17:18:49 +0100 Subject: [PATCH 062/146] Change to shading. --- src/gua/renderer/PostFXPass.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gua/renderer/PostFXPass.cpp b/src/gua/renderer/PostFXPass.cpp index 9b176a5b7..7802aae79 100644 --- a/src/gua/renderer/PostFXPass.cpp +++ b/src/gua/renderer/PostFXPass.cpp @@ -288,7 +288,7 @@ void PostFXPass::render_scene(Camera const& camera, RenderContext const& ctx) { any_godrays = render_godrays(camera, pipeline_->get_current_scene(eye), eye, ctx); render_glow(eye, ctx); - auto input_tex(inputs_[Pipeline::compositing]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); + auto input_tex(inputs_[Pipeline::shading]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); auto ping_tex(ping_buffer_->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); auto pong_tex(pong_buffer_->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); auto normal_tex(inputs_[Pipeline::geometry]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); From ac64c2e87174fbeff8c472bdf7eef264bb7e5109 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Tue, 3 Dec 2013 17:32:30 +0100 Subject: [PATCH 063/146] added simple loading screen instead of ugly transparent window --- include/gua/renderer/BuiltInTextures.hpp | 9 + include/gua/renderer/Pipeline.hpp | 1 + src/gua/guacamole.cpp | 1 + src/gua/renderer/BuiltInTextures.cpp | 472 ++++++++++++++++++++++- src/gua/renderer/Pipeline.cpp | 211 +++++----- 5 files changed, 604 insertions(+), 90 deletions(-) diff --git a/include/gua/renderer/BuiltInTextures.hpp b/include/gua/renderer/BuiltInTextures.hpp index 0846f0c89..8924c5b32 100644 --- a/include/gua/renderer/BuiltInTextures.hpp +++ b/include/gua/renderer/BuiltInTextures.hpp @@ -51,6 +51,15 @@ class GUA_DLL DefaultTexture : public Texture2D { #endif }; +class GUA_DLL LoadingTexture : public Texture2D { + public: + + LoadingTexture(); + + private: + static unsigned char pixel_data[128 * 39 * 3 + 1]; +}; + } #endif // GUA_NOISE_TEXTURE_HPP diff --git a/include/gua/renderer/Pipeline.hpp b/include/gua/renderer/Pipeline.hpp index b01ecfc6b..727d2e779 100644 --- a/include/gua/renderer/Pipeline.hpp +++ b/include/gua/renderer/Pipeline.hpp @@ -206,6 +206,7 @@ class GUA_DLL Pipeline { bool passes_need_reload_; bool buffers_need_reload_; + bool display_loading_screen_; unsigned last_shading_model_revision_; }; diff --git a/src/gua/guacamole.cpp b/src/gua/guacamole.cpp index 1a7e17745..5c1158cb5 100644 --- a/src/gua/guacamole.cpp +++ b/src/gua/guacamole.cpp @@ -43,6 +43,7 @@ void init(int argc, char** argv) { Resources::materials_gua_textured_quad_gmd); gua::TextureDatabase::instance()->add("gua_default_texture", std::shared_ptr(new DefaultTexture())); + gua::TextureDatabase::instance()->add("gua_loading_texture", std::shared_ptr(new LoadingTexture())); MeshLoader mesh_loader; diff --git a/src/gua/renderer/BuiltInTextures.cpp b/src/gua/renderer/BuiltInTextures.cpp index 2db42e548..926395324 100644 --- a/src/gua/renderer/BuiltInTextures.cpp +++ b/src/gua/renderer/BuiltInTextures.cpp @@ -460,7 +460,7 @@ NoiseTexture() {} //////////////////////////////////////////////////////////////////////////////// -#if WIN32 +#if WIN32 unsigned char DefaultTexture::pixel_data[64 * 64 * 3 + 1] = "sEk\211|p\211|p\211|p\200vs\204\337s\200vs\200vs\214\177\214\204A\224\214\ \177\214\204A\224\211\211{B\252\214\211\211{\211\211{\204]{c\266\214s\211\ @@ -888,7 +888,7 @@ k\204\205ke\217\211p\\~Z\303\224{(s\224Jxs\256s\245\30{\204|vn\241\214s<\ \204s\224\204s\224\204s\204g\204\224]\224\203\246s\203\246s\203\246s~w\211\ ~w\211~w\211c\232s"; -#else +#else unsigned char DefaultTexture::pixel_data[256 * 256 * 3 + 1] = "????????????????????????????????????????????????????????????????????????\ ????????????????????????\225\225\225\225\225\225\225\225\225\225\225\225\ @@ -7733,6 +7733,474 @@ I?????????????????????????????????????????????\225\225\225\225\225\225\225\ ????????????????????????????????"; #endif +unsigned char LoadingTexture::pixel_data[128 * 39 * 3 + 1] = + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\3\3\33\33\35""226HIDUXIVYIIJD326\32" + "\31\33\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1%$'XZJ" + "\213\230N\254\300N\273\322N\276\325N\276\325N\273\322N\254\300N\206\222O" + "KLF\25\25\25\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\10\10\10FFC\215\233N\273" + "\323N\273\322O\272\321N\273\322O\273\322O\273\322O\273\322O\272\321N\274" + "\323O\265\312NmsN\"\"$\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\14\14\13PQJ\253\277M\274\323" + "O\273\322O\273\322O\273\322O\273\322O\273\322O\273\322O\273\322O\273\322" + "O\273\322O\272\321N\274\323Nz\202O!!#\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\7\7\7PRJ\264\311N\272\321" + "O\273\322O\273\322O\273\322O\273\322O\273\322O\273\322O\273\322O\273\322" + "O\273\322O\273\322O\273\322O\272\321N\273\322NflM\21\21\21\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0CCA\250\273" + "N\273\322O\273\322O\273\322O\273\322O\273\322O\273\322O\273\322O\273\322" + "O\273\322O\273\322O\273\322O\273\322O\273\322O\273\322O\272\321N\262\310" + "NGHD\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0##&\214\230Q\276\324Q\273\322O\273\322O\273\322O\272\321N\276\325O\266" + "\315M\255\302K\265\313M\277\326P\273\322O\273\322O\273\322O\273\322O\273" + "\322O\273\322O\274\323O\210\224N\36\36\40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\6\6\5Z]L\277\323T\275\323S\274\322Q\273\322" + "O\272\321N\265\313NemA665+*.)))DI-\231\253D\276\325P\273\322O\273\322O\273" + "\322O\273\322O\272\321N\267\315NJKE\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0%%(\234\250T\300\324V\277\322U\276\323S\274\322P\266" + "\314OQTC<;<777111,,,''&\37\37!~\214;\275\324O\273\322O\273\322O\273\322O" + "\273\322O\274\324O{\204P\22\22\22\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0OPG\302\323Y\301\323X\300\323W\277\322U\300\326SioLDDD???:" + "::444///***$$$\36\36\36\245\270G\272\321N\273\322O\273\322O\273\322O\272" + "\321O\244\266N,,0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\20\20\21" + "}\202U\306\327\\\303\323Y\302\323X\301\324W\234\251RHGLGGGBBB===777222,," + ",&&&!!!U].\276\325O\273\322O\273\322O\273\322O\273\322O\273\321NGHC\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0##&\243\255Z\306\325^\304\324\\" + "\303\324Y\304\327Y^aNLLLJJJDDD???999444///***$$$/1$\276\326P\273\322O\273" + "\322O\273\322O\273\322O\276\325NUWI\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0""447\275\310_\307\325_\306\325^\304\325\\\254\272WHHLMMMKKKGG" + "GBBB<<<777222,,,'''##!\267\315M\273\322O\273\322O\273\322O\273\322O\276\326" + "NY\\K\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\207\207\207\377\377\377\377\377\377\377" + "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\207\207\207\0\0" + "\0\0\0\0\0\0\0MMM\271\271\271\366\366\366\377\377\377\370\370\370\275\275" + "\275QQQ\0\0\0\0\0\0\0\0\0VVV\377\377\377\340\340\340\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\347\347\347\377\377\377OOO\0\0\0\326\326\326" + "\363\363\363\377\377\377\377\377\377\377\377\377\351\351\351\260\260\260" + "TTT\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\0\0\0\0\0" + "\0\0\0\0\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\15\15" + "\15\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0%%%\247\247\247" + "\356\356\356\377\377\377\377\377\377\357\357\357\304\304\304\216\216\216" + "\0\0\0\0\0\0""000\377\377\377\264\264\264\0\0\0\0\0\0""000\377\377\377\264" + "\264\264\0\0\0\0\0\0""000\377\377\377\264\264\264\0\0\0\0\0\0\0\0\0\0\0\0" + "FFC\311\325b\310\326b\307\325`\307\326^\207\217TLLLHHH}}}RRREEE???999444" + "///)))\"!#\261\307L\273\322O\273\322O\273\322O\273\322O\276\326NY\\K\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\177\177\177\377\377\377\277\277\277\177\177\177" + "\177\177\177\177\177\177\177\177\177\177\177\177???\0\0\0\1\1\1\305\305\305" + "\377\377\377\367\367\367\240\240\240\204\204\204\237\237\237\367\367\367" + "\377\377\377\310\310\310\1\1\1\0\0\0\0\0\0\377\377\377\377\377\377\33\33" + "\33\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0///\377\377\377\361\361\361\0\0\0" + "\0\0\0\377\377\377\377\377\377\200\200\200\177\177\177\211\211\211\250\250" + "\250\363\363\363\377\377\377\352\352\352(((\0\0\0\0\0\0\0\0\0\0\0\0\377\377" + "\377\377\377\377\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\266\266\266\376\376\376\377\377\377\0\0\0\0\0\0\0" + "\0\0\0\0\0ttt\377\377\377\377\377\377\267\267\267\207\207\207\205\205\205" + "\245\245\245\377\377\377\377\377\377\0\0\0\0\0\0rrr\377\377\377\377\377\377" + "\0\0\0\0\0\0rrr\377\377\377\377\377\377\0\0\0\0\0\0rrr\377\377\377\377\377" + "\377\0\0\0\0\0\0\0\0\0\1\1\1^`O\317\333f\312\325c\310\326b\311\327`x}RLL" + "Mnnn\377\377\377\341\341\341LLLBBB<<<777222,,,$#&\260\306L\273\322O\273\322" + "O\273\322O\273\322O\276\326NVYJ\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\177\177\177" + "\377\377\377\177\177\177\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\303\303" + "\303\377\377\377\255\255\255\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\255\255\255\377" + "\377\377\303\303\303\0\0\0\0\0\0\235\235\235\377\377\377\204\204\204\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\244\244\244\377\377\377}}}\0\0\0\0\0\0\377" + "\377\377\377\377\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0fff\377\377\377\377\377" + "\377\40\40\40\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0" + "\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0CCC\377\377\377\377" + "\377\377\377\377\377\0\0\0\0\0\0\0\0\0NNN\377\377\377\361\361\361\33\33\33" + "\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0rrr111" + "\0\0\0\0\0\0\0\0\0rrr111\0\0\0\0\0\0\0\0\0rrr111\0\0\0\0\0\0\0\0\0\2\2\2" + "ceP\321\333g\313\326d\312\325c\312\327b\177\205TKKL\224\224\224\377\377\377" + "\377\377\377\252\252\252BBB???999444...)))\265\313M\273\322O\273\322O\273" + "\322O\273\322O\274\324NNOF\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\177\177\177\377" + "\377\377\177\177\177\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0III\377\377\377\334" + "\334\334\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\334\334\334\377\377\377" + "GGG\0\0\0""666\377\377\377\353\353\353zzz\177\177\177\177\177\177\177\177" + "\177\177\177\177xxx\377\377\377\377\377\377\13\13\13\0\0\0\0\0\0\377\377" + "\377\377\377\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0ccc\377\377\377\321\321" + "\321\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\377\377" + "\377\377\377\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\361\361\361\360\360\360\377" + "\377\377\377\377\377\0\0\0\0\0\0\0\0\0\355\355\355\377\377\377111\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\\^N\322\333i\314\327g\313\326d\312\326d\231\242YJJL\223" + "\223\223\377\377\377\376\376\376\373\373\373QQQBBB<<<77711123-\273\323O\273" + "\322O\273\322O\273\322O\273\322O\271\320NDDB\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\177\177\177\377\377\377\177\177\177\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\273\273\273\377\377\377VVV\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0X" + "XX\377\377\377\270\270\270\0\0\0\0\0\0\357\357\357\376\376\376\377\377\377" + "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\376\376\376" + "\266\266\266\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\355\355\355\377\377\377%%%\0\0\0\0\0\0\377\377\377" + "\377\377\377\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0" + "\0\0\0\234\234\234\377\377\377YYY\377\377\377\377\377\377\0\0\0\0\0\0---" + "\377\377\377\333\333\333\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377" + "\377\377\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0KLF\320\330k\316\327" + "i\314\327g\313\326e\275\310aJILuuu\377\377\377\377\377\377\377\377\377\201" + "\201\201DDD???:::444FI4\277\327P\273\322O\273\322O\273\322O\273\322O\263" + "\310N88:\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\177\177\177\377\377\377\177\177\177" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\366\366\366\377\377\377\17\17\17\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\20\20\20\377\377\377\365\365\365\0" + "\0\0\0\0\0\207\207\207\377\377\377\217\217\217\0\0\0\0\0\0\0\0\0\0\0\0\334" + "\334\334\377\377\377BBB\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\241\241\241\377\377\377eee\0\0\0\0\0\0" + "\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\0\0\0" + "\0\0\0\0\0\0""888\377\377\377\334\334\334\0\0\0\377\377\377\377\377\377\0" + "\0\0\0\0\0fff\377\377\377\232\232\232\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0""447\303" + "\311i\317\330j\315\327i\315\327f\317\333fmpRLLL\362\362\362\376\376\376\377" + "\377\377\227\227\227GGGAAA<<<667fn>\276\324Q\272\322O\273\322O\273\322O\272" + "\321N\247\272N,+/\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\177\177\177\377\377\377\177" + "\177\177\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377" + "\0\0\0\0\0\0\37\37\37\377\377\377\375\375\375\0\0\0\0\0\0\0\0\0""444\377" + "\377\377\356\356\356\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\205\205\205\377\377\377|||\0\0\0\0" + "\0\0\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\0" + "\0\0\0\0\0\0\0\0\364\364\364\377\377\377###\0\0\0\377\377\377\377\377\377" + "\0\0\0\0\0\0zzz\377\377\377\203\203\203\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\207\207\207\207\207\207\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0!!$\247" + "\253c\321\330m\317\330k\316\327i\314\326g\267\300aKKK\205\205\205\377\377" + "\377\377\377\377hhhJJJDDD???669\225\242J\275\323R\274\321Q\272\322O\273\322" + "O\273\322O\225\245N\40\40\"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\177\177\177\377" + "\377\377\177\177\177\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377" + "\377\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377" + "\377\377\377\0\0\0\0\0\0\0\0\0\333\333\333\377\377\377III\0\0\0\0\0\0\250" + "\250\250\377\377\377zzz\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\216\216\216\377\377\377ttt\0\0\0" + "\0\0\0\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377" + "\0\0\0\0\0\0\263\263\263\377\377\377\210\210\210\0\0\0\0\0\0\377\377\377" + "\377\377\377\0\0\0\0\0\0uuu\377\377\377\215\215\215\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\15\15" + "\15z|Z\324\333o\320\327l\317\330k\316\327h\317\331h\213\220YGGI^^^aaaKKK" + "LLLGGGBBBDE>\275\320U\276\323T\275\322R\274\321Q\273\322O\274\323O\200\212" + "O\22\22\23\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\177\177\177\377\377\377\177\177" + "\177\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\335\335\335\377\377\377,,,\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0...\377\377\377\334\334\334\0\0\0\0\0" + "\0\0\0\0rrr\377\377\377\260\260\260\0\0\0\2\2\2\377\377\377\377\377\377\11" + "\11\11\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\300\300\300\377\377\377KKK\0\0\0\0\0\0\377\377\377" + "\377\377\377\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\0\0\0bbb\377\377\377" + "\346\346\346\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\0\0\0\0\0\0LLL\377" + "\377\377\276\276\276\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0JJE\321\326o\322\330o\321\327" + "m\317\330k\316\327h\315\327h~\202VHHLMMMMMMLLLJJJA@D\211\223N\302\325X\300" + "\323V\276\323S\275\322R\274\321Q\276\326OdhM\4\4\4\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\177\177\177\377\377\377\177\177\177\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\213\213\213\377\377\377\217\217\217\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\221\221\221\377\377\377\210\210\210\0\0\0\0\0\0\0\0\0\13\13" + "\13\377\377\377\377\377\377\0\0\0ppp\377\377\377\263\263\263\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\17\17\17\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\377\377\377\377" + "\377\377\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\21\21\21\377\377\377\377" + "\377\377\"\"\"\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\0\0\0\0\0\0\0\0" + "\0\377\377\377\377\377\377\7\7\7\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0##&\250\253f\324\331p\322" + "\330n\321\327m\317\330k\316\327i\317\332h\233\242\\WXNHHLIILGFKy\177O\304" + "\325[\302\323Z\301\324X\300\323V\277\323T\275\323S\271\316PCCA\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\177\177\177\377\377\377\177\177\177\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\6\6\6\377\377\377\377\377\377%%%\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0%%%\377\377\377\377\377\377\5\5\5\0\0\0\0\0\0\0\0\0\0\0\0" + "\304\304\304\377\377\377RRR\345\345\345\377\377\377>>>\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\322\322\322\377\377\377{{{\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\0\0" + "\0\0\0\0\0\0\0\377\377\377\377\377\377\344\344\344\377\377\377ttt\0\0\0\0" + "\0\0\0\0\0\0\0\0\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\211\211\211\377" + "\377\377\273\273\273\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\7\7\7hiT\331\335s\323\331p\322\330" + "o\321\327m\317\327j\316\327i\316\327h\315\327f\264\275_\251\262\\\267\302" + "^\311\330_\305\324\\\304\324[\302\323Y\301\324X\300\323V\277\323T\241\260" + "Q('+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\177\177\177\377\377\377\177\177" + "\177\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0FFF\377\377\377\377\377\377" + "sss\21\21\21\0\0\0\21\21\21rrr\377\377\377\377\377\377GGG\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0^^^\377\377\377\351\351\351\377\377\377\353\353\353\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\32" + "\32\32eee\354\354\354\377\377\377\257\257\257\0\0\0\0\0\0\0\0\0\0\0\0\377" + "\377\377\377\377\377\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\376\376\376" + "\310\310\310\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\0\0\0" + "\0\0\0\0\0\0\0\0\0\323\323\323\377\377\377\312\312\312888\0\0\0\0\0\0\36" + "\36\36nnnzzz\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0""114\273" + "\276l\324\331r\323\331q\322\330o\321\327m\317\327k\316\327j\315\326h\314" + "\327f\312\326d\311\326c\310\325a\306\325_\305\325\\\304\324\\\303\323Z\301" + "\324X\302\325Vy\200Q\17\17\20\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\177\177" + "\177\377\377\377\177\177\177\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0""111\336\336\336\377\377\377\377\377\377\377\377\377\377\377\377\377" + "\377\377\342\342\342333\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377" + "\377\377\377\377\377\377\377www\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377" + "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" + "\377\343\343\343[[[\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377" + "\0\0\0\0\0\0\0\0\0\377\377\377\376\376\376\364\364\364\5\5\5\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\224\224\224\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" + "\377\377\377\324\324\324\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\15\15\15tuY\332\336u\325\331r\323\331p\322\330o\321\327m\320\327k" + "\316\327i\315\326h\314\327f\312\326d\311\326b\307\326a\307\325_\305\325]" + "\304\324\\\303\323Z\300\322XKMF\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "???\177\177\177???\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0(((fff{{{hhh,,,\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0aaa\177\177\177\205\205\205\14\14\14\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0===aaavvv}}}qqqWWW\35\35\35\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\207" + "\207\207\207\207\207\0\0\0\0\0\0\0\0\0\207\207\207\201\201\201%%%\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\207\207\207\207\207\207\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\3\3\3RRRuuuzzz]]]\40\40\40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0""668\300\302o\326\331s\325\331r\323" + "\331q\322\330o\321\327l\320\330k\316\327j\315\326h\314\327e\312\326d\311" + "\326c\310\326a\307\325^\305\325]\304\325\\\236\251V%$'\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\15\15\16uvZ" + "\334\336w\326\332t\325\331r\324\331q\322\331o\321\330m\320\330l\316\327j" + "\315\326h\313\327f\313\326e\311\325c\310\326a\307\325^\311\330^cfP\7\7\7" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0""658\301\302p\327\331u\326\332t\325\331s\324\331q\322\331o\321" + "\330m\320\330k\316\327j\315\326h\314\327f\313\326d\311\325c\310\326a\252" + "\264Z//2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\15\15\15vvZ\335\337y\330\331v\326\332u\325\331" + "s\324\330q\322\331n\321\330n\320\330l\317\327j\315\326g\314\326g\313\326" + "e\314\330cdfP\11\11\11\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0""99:\307\307s\331\332w\330" + "\331v\326\332t\325\331s\324\330q\322\331o\321\330m\320\330l\317\327j\315" + "\326h\314\327g\236\246\\((+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\16\16\16llW\334" + "\334z\331\332w\330\331w\326\331u\325\331s\324\330p\323\331o\321\330n\320" + "\330l\316\327i\306\317gLMH\2\2\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0!!#\206" + "\206a\334\334z\331\332x\327\331v\327\331u\325\331s\324\330q\323\331p\321" + "\330m\322\331lsuV\24\24\25\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\"\"$ppY\310\310s\336\337z\330\332w\327\332u\326\332s\325\332q\321\326" + "o\201\204\\%%'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\17\17\17::;rrZ\234\236f\254\255i\250\252g\212\214_RSK\34\34\36\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\11" + "\11\11\31\31\32!!#\37\37!\22\22\23\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0"; + +//////////////////////////////////////////////////////////////////////////////// + +LoadingTexture:: +LoadingTexture() + : Texture2D(128, 39, scm::gl::FORMAT_RGB_8, {pixel_data}) +{} + //////////////////////////////////////////////////////////////////////////////// DefaultTexture:: diff --git a/src/gua/renderer/Pipeline.cpp b/src/gua/renderer/Pipeline.cpp index d4f0bb7fa..a2652562c 100644 --- a/src/gua/renderer/Pipeline.cpp +++ b/src/gua/renderer/Pipeline.cpp @@ -56,7 +56,8 @@ Pipeline::Pipeline() current_scenes_(2), passes_need_reload_(true), buffers_need_reload_(true), - last_shading_model_revision_(0) { + last_shading_model_revision_(0), + display_loading_screen_(true) { create_passes(); } @@ -157,114 +158,148 @@ void Pipeline::process(std::vector> const& sce return; } - if (passes_need_reload_) { - create_passes(); - } + if (display_loading_screen_) { + display_loading_screen_ = false; - if (buffers_need_reload_) { - create_buffers(); - } + if (window_) { + auto loading_texture(std::dynamic_pointer_cast(TextureDatabase::instance()->lookup("gua_loading_texture"))); + if (config.get_enable_stereo()) { + auto tmp_left_resolution(window_->config.left_resolution()); + auto tmp_right_resolution(window_->config.right_resolution()); + auto tmp_left_position(window_->config.left_position()); + auto tmp_right_position(window_->config.right_position()); + window_->display(loading_texture, loading_texture); + } else { + auto tmp_left_resolution(window_->config.left_resolution()); + auto tmp_left_position(window_->config.left_position()); - if (!config.get_enable_stereo()) { + math::vec2ui loading_texture_size(loading_texture->width(), loading_texture->height()); - auto eye((*current_graph_)[config.camera().eye_l]); - if (!eye) { - WARNING("Cannot render scene: No valid eye specified"); - return; - } + window_->config.set_left_resolution(loading_texture_size); + window_->config.set_left_position(tmp_left_position + 0.5*(tmp_left_resolution - loading_texture_size)); - auto screen_it((*current_graph_)[config.camera().screen_l]); - auto screen(std::dynamic_pointer_cast(screen_it)); - if (!screen) { - WARNING("Cannot render scene: No valid screen specified"); - return; - } + window_->display(loading_texture); - current_scenes_[0].frustum = Frustum(eye->get_world_transform(), - screen->get_scaled_world_transform(), - config.near_clip(), - config.far_clip()); - - serializer_->check(¤t_scenes_[0], - current_graph_, - config.camera(), - config.enable_bbox_display(), - config.enable_ray_display(), - config.enable_frustum_culling()); - } else { + window_->config.set_left_position(tmp_left_position); + window_->config.set_left_resolution(tmp_left_resolution); + } - auto eye_l((*current_graph_)[config.camera().eye_l]); - if (!eye_l) { - WARNING("Cannot render scene: No valid left eye specified"); - return; + window_->finish_frame(); } - auto eye_r((*current_graph_)[config.camera().eye_r]); - if (!eye_r) { - WARNING("Cannot render scene: No valid right eye specified"); - return; - } + } else { - auto screen_it_l((*current_graph_)[config.camera().screen_l]); - auto screen_l(std::dynamic_pointer_cast(screen_it_l)); - if (!screen_l) { - WARNING("Cannot render scene: No valid left screen specified"); - return; + if (passes_need_reload_) { + create_passes(); } - auto screen_it_r((*current_graph_)[config.camera().screen_r]); - auto screen_r(std::dynamic_pointer_cast(screen_it_r)); - if (!screen_r) { - WARNING("Cannot render scene: No valid right screen specified"); - return; + if (buffers_need_reload_) { + create_buffers(); } - current_scenes_[0].frustum = Frustum(eye_l->get_world_transform(), - screen_l->get_scaled_world_transform(), - config.near_clip(), - config.far_clip()); - current_scenes_[1].frustum = Frustum(eye_r->get_world_transform(), - screen_r->get_scaled_world_transform(), - config.near_clip(), - config.far_clip()); - - serializer_->check(¤t_scenes_[0], - current_graph_, - config.camera(), - config.enable_bbox_display(), - config.enable_ray_display(), - config.enable_frustum_culling()); - - serializer_->check(¤t_scenes_[1], - current_graph_, - config.camera(), - config.enable_bbox_display(), - config.enable_ray_display(), - config.enable_frustum_culling()); - } + if (!config.get_enable_stereo()) { - for (auto pass : passes_) { - pass->render_scene(config.camera(), *context_); - } + auto eye((*current_graph_)[config.camera().eye_l]); + if (!eye) { + WARNING("Cannot render scene: No valid eye specified"); + return; + } - if (window_) { - if (config.get_enable_stereo()) { - window_->display(passes_[PipelineStage::postfx]->get_gbuffer()->get_eye_buffers()[0] - ->get_color_buffers(TYPE_FLOAT)[0], - passes_[PipelineStage::postfx]->get_gbuffer()->get_eye_buffers()[1] - ->get_color_buffers(TYPE_FLOAT)[0]); + auto screen_it((*current_graph_)[config.camera().screen_l]); + auto screen(std::dynamic_pointer_cast(screen_it)); + if (!screen) { + WARNING("Cannot render scene: No valid screen specified"); + return; + } + + current_scenes_[0].frustum = Frustum(eye->get_world_transform(), + screen->get_scaled_world_transform(), + config.near_clip(), + config.far_clip()); + + serializer_->check(¤t_scenes_[0], + current_graph_, + config.camera(), + config.enable_bbox_display(), + config.enable_ray_display(), + config.enable_frustum_culling()); } else { - window_->display(passes_[PipelineStage::postfx]->get_gbuffer()->get_eye_buffers()[0] - ->get_color_buffers(TYPE_FLOAT)[0]); + + + auto eye_l((*current_graph_)[config.camera().eye_l]); + if (!eye_l) { + WARNING("Cannot render scene: No valid left eye specified"); + return; + } + + auto eye_r((*current_graph_)[config.camera().eye_r]); + if (!eye_r) { + WARNING("Cannot render scene: No valid right eye specified"); + return; + } + + auto screen_it_l((*current_graph_)[config.camera().screen_l]); + auto screen_l(std::dynamic_pointer_cast(screen_it_l)); + if (!screen_l) { + WARNING("Cannot render scene: No valid left screen specified"); + return; + } + + auto screen_it_r((*current_graph_)[config.camera().screen_r]); + auto screen_r(std::dynamic_pointer_cast(screen_it_r)); + if (!screen_r) { + WARNING("Cannot render scene: No valid right screen specified"); + return; + } + + + current_scenes_[0].frustum = Frustum(eye_l->get_world_transform(), + screen_l->get_scaled_world_transform(), + config.near_clip(), + config.far_clip()); + current_scenes_[1].frustum = Frustum(eye_r->get_world_transform(), + screen_r->get_scaled_world_transform(), + config.near_clip(), + config.far_clip()); + + serializer_->check(¤t_scenes_[0], + current_graph_, + config.camera(), + config.enable_bbox_display(), + config.enable_ray_display(), + config.enable_frustum_culling()); + + serializer_->check(¤t_scenes_[1], + current_graph_, + config.camera(), + config.enable_bbox_display(), + config.enable_ray_display(), + config.enable_frustum_culling()); } - window_->finish_frame(); + for (auto pass : passes_) { + pass->render_scene(config.camera(), *context_); + } + + if (window_) { + if (config.get_enable_stereo()) { + window_->display(passes_[PipelineStage::postfx]->get_gbuffer()->get_eye_buffers()[0] + ->get_color_buffers(TYPE_FLOAT)[0], + passes_[PipelineStage::postfx]->get_gbuffer()->get_eye_buffers()[1] + ->get_color_buffers(TYPE_FLOAT)[0]); + } else { + window_->display(passes_[PipelineStage::postfx]->get_gbuffer()->get_eye_buffers()[0] + ->get_color_buffers(TYPE_FLOAT)[0]); + } + + window_->finish_frame(); + } } } @@ -327,7 +362,7 @@ void Pipeline::create_passes() { for (auto pass : passes_) { delete pass; } - + passes_.clear(); passes_.push_back(pre_pass); @@ -354,7 +389,7 @@ void Pipeline::create_passes() { void Pipeline::create_buffers() { if (buffers_need_reload_) { - + std::vector> stereobuffers; passes_[PipelineStage::geometry]->create(*context_, config, passes_[PipelineStage::geometry]->get_gbuffer_mapping()->get_layers()); @@ -368,7 +403,7 @@ void Pipeline::create_buffers() { passes_[PipelineStage::shading]->set_inputs(stereobuffers); stereobuffers.push_back(passes_[PipelineStage::shading]->get_gbuffer()); - scm::gl::sampler_state_desc state(scm::gl::FILTER_MIN_MAG_LINEAR, + scm::gl::sampler_state_desc state(scm::gl::FILTER_MIN_MAG_LINEAR, scm::gl::WRAP_REPEAT, scm::gl::WRAP_REPEAT); From e05caec2c18a8ee99d1d5dea411b5d461ce0cce2 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Tue, 3 Dec 2013 17:37:17 +0100 Subject: [PATCH 064/146] fixed loading screen for stereo pipelines --- src/gua/renderer/Pipeline.cpp | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/src/gua/renderer/Pipeline.cpp b/src/gua/renderer/Pipeline.cpp index a2652562c..6e5f88cf8 100644 --- a/src/gua/renderer/Pipeline.cpp +++ b/src/gua/renderer/Pipeline.cpp @@ -163,21 +163,35 @@ void Pipeline::process(std::vector> const& sce if (window_) { auto loading_texture(std::dynamic_pointer_cast(TextureDatabase::instance()->lookup("gua_loading_texture"))); + math::vec2ui loading_texture_size(loading_texture->width(), loading_texture->height()); + if (config.get_enable_stereo()) { - auto tmp_left_resolution(window_->config.left_resolution()); - auto tmp_right_resolution(window_->config.right_resolution()); + auto tmp_left_resolution(window_->config.left_resolution()); + auto tmp_right_resolution(window_->config.right_resolution()); + + auto tmp_left_position(window_->config.left_position()); + auto tmp_right_position(window_->config.right_position()); + + window_->config.set_left_resolution(loading_texture_size); + window_->config.set_left_position(tmp_left_position + 0.5*(tmp_left_resolution - loading_texture_size)); + + window_->config.set_right_resolution(loading_texture_size); + window_->config.set_right_position(tmp_right_position + 0.5*(tmp_right_resolution - loading_texture_size)); + + window_->display(loading_texture, loading_texture); + + window_->config.set_left_position(tmp_left_position); + window_->config.set_left_resolution(tmp_left_resolution); - auto tmp_left_position(window_->config.left_position()); - auto tmp_right_position(window_->config.right_position()); + window_->config.set_right_position(tmp_right_position); + window_->config.set_right_resolution(tmp_right_resolution); - window_->display(loading_texture, loading_texture); } else { auto tmp_left_resolution(window_->config.left_resolution()); auto tmp_left_position(window_->config.left_position()); - math::vec2ui loading_texture_size(loading_texture->width(), loading_texture->height()); window_->config.set_left_resolution(loading_texture_size); window_->config.set_left_position(tmp_left_position + 0.5*(tmp_left_resolution - loading_texture_size)); From 34f0204fae0c5b1847e9255a1b2d2b440c483fd5 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Fri, 6 Dec 2013 11:44:04 +0100 Subject: [PATCH 065/146] fixed NURBS not being displayed --- src/gua/renderer/GBufferNURBSUberShader.cpp | 36 ++++++++++----------- src/gua/renderer/GBufferPass.cpp | 2 +- src/gua/renderer/NURBS.cpp | 18 ----------- 3 files changed, 19 insertions(+), 37 deletions(-) diff --git a/src/gua/renderer/GBufferNURBSUberShader.cpp b/src/gua/renderer/GBufferNURBSUberShader.cpp index 0abea5e9c..ab2ae87cb 100644 --- a/src/gua/renderer/GBufferNURBSUberShader.cpp +++ b/src/gua/renderer/GBufferNURBSUberShader.cpp @@ -154,8 +154,8 @@ std::string const GBufferNURBSUberShader::_transform_feedback_tess_control_shade \n\ layout(vertices = 4) out; \n\ \n\ - uniform samplerBuffer parameter_texture; \n\ - uniform samplerBuffer attribute_texture; \n\ + layout(binding=5) uniform samplerBuffer parameter_texture; \n\ + layout(binding=6) uniform samplerBuffer attribute_texture; \n\ \n\ // uniforms \n\ uniform mat4 gua_projection_matrix; \n\ @@ -288,8 +288,8 @@ std::string const GBufferNURBSUberShader::_transform_feedback_tess_evaluation_sh uniform mat4 gua_inverse_projection_view_matrix; \n\ uniform vec3 gua_camera_position; \n\ \n\ - uniform samplerBuffer parameter_texture; \n\ - uniform samplerBuffer attribute_texture; \n\ + layout(binding=5) uniform samplerBuffer parameter_texture; \n\ + layout(binding=6) uniform samplerBuffer attribute_texture; \n\ "); tess_eval << NURBSShader::surface_horner_evaluation(); @@ -346,8 +346,8 @@ std::string const GBufferNURBSUberShader::_transform_feedback_geometry_shader () layout (location = 1) flat out uint xfb_index; \n\ layout (location = 2) out vec2 xfb_tesscoord; \n\ \n\ - uniform samplerBuffer parameter_texture; \n\ - uniform samplerBuffer attribute_texture; \n\ + layout(binding=5) uniform samplerBuffer parameter_texture; \n\ + layout(binding=6) uniform samplerBuffer attribute_texture; \n\ "); tf_geom << NURBSShader::surface_horner_evaluation(); @@ -464,8 +464,8 @@ std::string const GBufferNURBSUberShader::_final_tess_control_shader () const \n\ layout(vertices = 4) out; \n\ \n\ - uniform samplerBuffer parameter_texture; \n\ - uniform samplerBuffer attribute_texture; \n\ + layout(binding=5) uniform samplerBuffer parameter_texture; \n\ + layout(binding=6) uniform samplerBuffer attribute_texture; \n\ \n\ uniform mat4 gua_projection_matrix; \n\ uniform mat4 gua_view_matrix; \n\ @@ -651,8 +651,8 @@ std::string const GBufferNURBSUberShader::_final_tess_evaluation_shader () const uniform mat4 gua_inverse_projection_view_matrix; \n\ uniform vec3 gua_camera_position; \n\ \n\ - uniform samplerBuffer parameter_texture; \n\ - uniform samplerBuffer attribute_texture; \n\ + layout(binding=5) uniform samplerBuffer parameter_texture; \n\ + layout(binding=6) uniform samplerBuffer attribute_texture; \n\ "); tess_eval << NURBSShader::surface_horner_evaluation(); @@ -732,8 +732,8 @@ std::string const GBufferNURBSUberShader::_final_geometry_shader () const flat out uint gIndex; \n\ out vec2 gTessCoord; \n\ \n\ - uniform samplerBuffer parameter_texture; \n\ - uniform samplerBuffer attribute_texture; \n\ + layout(binding=5) uniform samplerBuffer parameter_texture; \n\ + layout(binding=6) uniform samplerBuffer attribute_texture; \n\ "); // generated material-dependent uniform definitions @@ -853,13 +853,13 @@ std::string const GBufferNURBSUberShader::_final_fragment_shader () const fragment_shader << "// hard-coded uniform input"; fragment_shader << std::string(" \n\ \n\ - uniform samplerBuffer attribute_texture; \n\ + layout(binding=6) uniform samplerBuffer attribute_texture; \n\ \n\ - uniform samplerBuffer trim_partition; \n\ - uniform samplerBuffer trim_contourlist; \n\ - uniform samplerBuffer trim_curvelist; \n\ - uniform samplerBuffer trim_curvedata; \n\ - uniform samplerBuffer trim_pointdata; \n\ + layout(binding=7) uniform samplerBuffer trim_partition; \n\ + layout(binding=8) uniform samplerBuffer trim_contourlist; \n\ + layout(binding=9) uniform samplerBuffer trim_curvelist; \n\ + layout(binding=10) uniform samplerBuffer trim_curvedata; \n\ + layout(binding=11) uniform samplerBuffer trim_pointdata; \n\ \n\ "); diff --git a/src/gua/renderer/GBufferPass.cpp b/src/gua/renderer/GBufferPass.cpp index b4e0e8582..a2e247d8b 100644 --- a/src/gua/renderer/GBufferPass.cpp +++ b/src/gua/renderer/GBufferPass.cpp @@ -32,7 +32,7 @@ #include #include -#define DEBUG_XFB_OUTPUT +// #define DEBUG_XFB_OUTPUT namespace gua { diff --git a/src/gua/renderer/NURBS.cpp b/src/gua/renderer/NURBS.cpp index c49026f69..eb0c1b557 100644 --- a/src/gua/renderer/NURBS.cpp +++ b/src/gua/renderer/NURBS.cpp @@ -168,15 +168,6 @@ void NURBS::predraw(RenderContext const& context) const { in_context->bind_texture( _trim_pointdata_texture_buffer[context.id], _sstate[context.id], 11); - scm::gl::program_ptr p = in_context->current_program(); - p->uniform("parameter_texture", 5); - p->uniform("attribute_texture", 6); - p->uniform("trim_partition", 7); - p->uniform("trim_contourlist", 8); - p->uniform("trim_curvelist", 9); - p->uniform("trim_curvedata", 10); - p->uniform("trim_pointdata", 11); - in_context->apply(); in_context->draw_elements(_data->index_data.size()); @@ -238,15 +229,6 @@ void NURBS::draw(RenderContext const& context) const { in_context->bind_texture( _trim_pointdata_texture_buffer[context.id], _sstate[context.id], 11); - scm::gl::program_ptr p = in_context->current_program(); - p->uniform("parameter_texture", 5); - p->uniform("attribute_texture", 6); - p->uniform("trim_partition", 7); - p->uniform("trim_contourlist", 8); - p->uniform("trim_curvelist", 9); - p->uniform("trim_curvedata", 10); - p->uniform("trim_pointdata", 11); - in_context->apply(); in_context->draw_transform_feedback( From b4a8d5f3b0c3c86aa997266d2e86b344d3e7ab11 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Fri, 6 Dec 2013 14:36:18 +0100 Subject: [PATCH 066/146] reloading materials loads new materials as well --- src/gua/renderer/Pipeline.cpp | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/gua/renderer/Pipeline.cpp b/src/gua/renderer/Pipeline.cpp index 6e5f88cf8..0f94e2e4e 100644 --- a/src/gua/renderer/Pipeline.cpp +++ b/src/gua/renderer/Pipeline.cpp @@ -357,18 +357,21 @@ void Pipeline::create_passes() { passes_need_reload_ = false; + std::vector new_passes; + new_passes.push_back(pre_pass); + new_passes.push_back(light_pass); + new_passes.push_back(final_pass); + new_passes.push_back(composite_pass); + new_passes.push_back(post_fx_pass); + // try compilation if context is already present if (context_) { - - for (auto pass : passes_) { - - if (!pass->pre_compile_shaders(*context_)) { - compilation_succeeded = false; - } - + for (auto pass : new_passes) { + if (!pass->pre_compile_shaders(*context_)) { + compilation_succeeded = false; + break; } - } else { - compilation_succeeded = true; + } } if (compilation_succeeded) { @@ -389,11 +392,11 @@ void Pipeline::create_passes() { } else { - delete pre_pass; - delete light_pass; - delete final_pass; - delete composite_pass; - delete post_fx_pass; + WARNING("Failed to recompile shaders!"); + + for (auto pass : new_passes) { + delete pass; + } } } } From 5e555dff6b84530507e9f6ffb7e3ece25a335d75 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Fri, 6 Dec 2013 15:20:43 +0100 Subject: [PATCH 067/146] guarc ignores temporary files now --- guarc/main.cpp | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/guarc/main.cpp b/guarc/main.cpp index 0d1bef428..1c3bf6e65 100644 --- a/guarc/main.cpp +++ b/guarc/main.cpp @@ -41,6 +41,7 @@ void read_directory(fs::path const& directory, std::string const& root); void read_file(fs::path const& file, std::string const& root); void write_output(fs::path const& hpp_file, fs::path const& cpp_file); void make_variable_name(std::string& file); +bool is_temporary_file(std::string const& file); //////////////////////////////////////////////////////////////////////////////// @@ -113,7 +114,13 @@ void read_directory(fs::path const& directory, std::string const& root) { read_directory(dir_itr->path(), root); } else if (fs::is_regular_file(dir_itr->status())) { - read_file(dir_itr->path(), root); + + if (!is_temporary_file(dir_itr->path().filename().string())) { + read_file(dir_itr->path(), root); + } else { + std::cout << "Skipping temporary file "; + std::cout << dir_itr->path().filename() << std::endl; + } } else { std::cout << "Skipping non-regular file "; @@ -133,10 +140,10 @@ void read_file(fs::path const& file, std::string const& root) { std::string full_path(file.string()); std::string rel_path(full_path.substr(root.length() + 1, std::string::npos)); - // replace windows '\' with '/' for map key + // replace windows '\' with '/' for map key std::replace(rel_path.begin(), rel_path.end(), '\\', '/'); - std::cout << rel_path << std::endl; - + std::cout << "Embedding " << rel_path << " ..." << std::endl; + // open the file: std::ifstream s(file.string(), std::ios::in | std::ios::binary); @@ -189,12 +196,13 @@ void write_output(fs::path const& hpp_file, fs::path const& cpp_file) { cpp << "0x" << hex.str(); - if (i Date: Fri, 6 Dec 2013 17:01:54 +0100 Subject: [PATCH 068/146] simplified composite pass fixes in nurbs shading --- include/gua/renderer/CompositePass.hpp | 14 +++-- include/gua/renderer/GeometryPass.hpp | 2 +- src/gua/renderer/CompositePass.cpp | 64 +++++++++++++++------ src/gua/renderer/GBufferNURBSUberShader.cpp | 36 ++++++------ src/gua/renderer/NURBS.cpp | 20 +++++++ src/gua/renderer/Pipeline.cpp | 8 +-- src/gua/renderer/PostFXPass.cpp | 4 +- 7 files changed, 98 insertions(+), 50 deletions(-) diff --git a/include/gua/renderer/CompositePass.hpp b/include/gua/renderer/CompositePass.hpp index 9a2f50836..e79b0c396 100644 --- a/include/gua/renderer/CompositePass.hpp +++ b/include/gua/renderer/CompositePass.hpp @@ -35,7 +35,7 @@ struct PipelineConfiguration; /** * */ -class CompositePass : public GeometryPass { +class CompositePass : public Pass { public: /** @@ -48,10 +48,10 @@ class CompositePass : public GeometryPass { */ virtual ~CompositePass(); - void create( RenderContext const& ctx, - PipelineConfiguration const& config, - std::vector > const& layers); + virtual void create(RenderContext const& ctx, + PipelineConfiguration const& config, + std::vector > const& layers); /* virtual */ LayerMapping const* get_gbuffer_mapping() const; @@ -60,6 +60,8 @@ class CompositePass : public GeometryPass { bool pre_compile_shaders(RenderContext const& ctx); + /* virtual */ void render_scene(Camera const& camera, RenderContext const& ctx); + protected : /* virtual */ void rendering( SerializedScene const& scene, @@ -72,7 +74,7 @@ protected : private: - GBuffer* volume_raygeneration_; + GBuffer* volume_raygeneration_buffer_; scm::gl::depth_stencil_state_ptr depth_stencil_state_; scm::gl::quad_geometry_ptr fullscreen_quad_; diff --git a/include/gua/renderer/GeometryPass.hpp b/include/gua/renderer/GeometryPass.hpp index e0c6af151..0072f30c5 100644 --- a/include/gua/renderer/GeometryPass.hpp +++ b/include/gua/renderer/GeometryPass.hpp @@ -58,7 +58,7 @@ class GeometryPass : public Pass { */ virtual ~GeometryPass() {} - void render_scene(Camera const& camera, RenderContext const& ctx); + virtual void render_scene(Camera const& camera, RenderContext const& ctx); protected: virtual void rendering(SerializedScene const& scene, diff --git a/src/gua/renderer/CompositePass.cpp b/src/gua/renderer/CompositePass.cpp index 60635db6a..b906e0381 100644 --- a/src/gua/renderer/CompositePass.cpp +++ b/src/gua/renderer/CompositePass.cpp @@ -38,10 +38,10 @@ namespace gua { //////////////////////////////////////////////////////////////////////////////// CompositePass::CompositePass(Pipeline* pipeline) : - GeometryPass(pipeline), + Pass(pipeline), composite_shader_(new ShaderProgram), ray_generation_shader_(new ShaderProgram), - volume_raygeneration_(nullptr) + volume_raygeneration_buffer_(nullptr) { std::string vertex_shader (Resources::lookup_shader(Resources::shaders_uber_shaders_composite_compose_vert)); std::string fragment_shader(Resources::lookup_shader(Resources::shaders_uber_shaders_composite_compose_frag)); @@ -64,8 +64,8 @@ CompositePass::~CompositePass() { delete composite_shader_; } - if (volume_raygeneration_) { - delete volume_raygeneration_; + if (volume_raygeneration_buffer_) { + delete volume_raygeneration_buffer_; } if (ray_generation_shader_) { @@ -79,11 +79,12 @@ void CompositePass::create(RenderContext const& ctx, PipelineConfiguration const& config, std::vector> const& layers) { - Pass::create(ctx, config, layers); + // reuse gbuffer from shading-pass + gbuffer_ = inputs_[Pipeline::PipelineStage::shading]; - if (volume_raygeneration_) { - volume_raygeneration_->remove_buffers(ctx); - delete volume_raygeneration_; + if (volume_raygeneration_buffer_) { + volume_raygeneration_buffer_->remove_buffers(ctx); + delete volume_raygeneration_buffer_; } scm::gl::sampler_state_desc state(scm::gl::FILTER_MIN_MAG_LINEAR, @@ -93,20 +94,21 @@ void CompositePass::create(RenderContext const& ctx, std::vector> layer_3f_desc; layer_3f_desc.push_back(std::make_pair(BufferComponent::F3, state)); - volume_raygeneration_ = new GBuffer(layer_3f_desc, + volume_raygeneration_buffer_ = new GBuffer(layer_3f_desc, config.get_left_resolution()[0], config.get_left_resolution()[1]); - volume_raygeneration_->create(ctx); + volume_raygeneration_buffer_->create(ctx); } //////////////////////////////////////////////////////////////////////////////// /* virtual */ void CompositePass::rendering(SerializedScene const& scene, RenderContext const& ctx, - CameraMode eye, + CameraMode eye, Camera const& camera, FrameBufferObject* target) { +#if 0 ///TODO: Toplevel if (!scene.volumenodes_.empty() /*|| !scene.transparentnodes_.empty()*/) { @@ -115,9 +117,9 @@ void CompositePass::create(RenderContext const& ctx, ctx.render_context->set_depth_stencil_state(depth_stencil_state_); // 1. render proxy geometry into fbo - volume_raygeneration_->bind(ctx); + volume_raygeneration_buffer_->bind(ctx); { - scm::math::vec2f resolution(volume_raygeneration_->width(), volume_raygeneration_->height()); + scm::math::vec2f resolution(volume_raygeneration_buffer_->width(), volume_raygeneration_buffer_->height()); ctx.render_context->set_viewport(scm::gl::viewport(math::vec2(0, 0), resolution)); // gather input textures and set uniforms @@ -126,8 +128,8 @@ void CompositePass::create(RenderContext const& ctx, ray_generation_shader_->set_uniform(ctx, 1.f / gbuffer_->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->width(), "gua_texel_width"); ray_generation_shader_->set_uniform(ctx, 1.f / gbuffer_->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->height(), "gua_texel_height"); - volume_raygeneration_->clear_color_buffers(ctx, gua::utils::Color3f(0.0f, 0.0f, 0.0f)); - volume_raygeneration_->clear_depth_stencil_buffer(ctx); + volume_raygeneration_buffer_->clear_color_buffers(ctx, gua::utils::Color3f(0.0f, 0.0f, 0.0f)); + volume_raygeneration_buffer_->clear_depth_stencil_buffer(ctx); //fullscreen_quad_->draw(ctx.render_context); for (auto const& node : scene.volumenodes_) { @@ -149,7 +151,7 @@ void CompositePass::create(RenderContext const& ctx, } } } - volume_raygeneration_->unbind(ctx); + volume_raygeneration_buffer_->unbind(ctx); // 2. render fullscreen quad for compositing and volume ray castinG Pass::set_camera_matrices(*composite_shader_, camera, pipeline_->get_current_scene(eye), eye, ctx); @@ -157,7 +159,7 @@ void CompositePass::create(RenderContext const& ctx, auto input_tex(inputs_[Pipeline::shading]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); auto normal_tex(inputs_[Pipeline::geometry]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); auto depth_tex(inputs_[Pipeline::geometry]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_depth_buffer()); - auto raygen_tex(volume_raygeneration_->get_color_buffers(TYPE_FLOAT)[0]); + auto raygen_tex(volume_raygeneration_buffer_->get_color_buffers(TYPE_FLOAT)[0]); composite_shader_->set_uniform(ctx, input_tex, "gua_color_gbuffer_in"); composite_shader_->set_uniform(ctx, normal_tex, "gua_normal_gbuffer_in"); @@ -192,12 +194,11 @@ void CompositePass::create(RenderContext const& ctx, } } - - target->unbind(ctx); ctx.render_context->reset_state_objects(); } +#endif } //////////////////////////////////////////////////////////////////////////////// @@ -237,4 +238,29 @@ bool CompositePass::pre_compile_shaders(RenderContext const& ctx) { return false; } +//////////////////////////////////////////////////////////////////////////////// + +void CompositePass::render_scene(Camera const& camera, RenderContext const& ctx) { + + for (int i(0); i < gbuffer_->get_eye_buffers().size(); ++i) { + + FrameBufferObject* fbo(gbuffer_->get_eye_buffers()[i]); + + CameraMode eye(CameraMode::CENTER); + if (gbuffer_->get_eye_buffers().size() > 1 && i == 0) + eye = CameraMode::LEFT; + if (gbuffer_->get_eye_buffers().size() > 1 && i == 1) + eye = CameraMode::RIGHT; + + fbo->bind(ctx); + + ctx.render_context->set_viewport(scm::gl::viewport( + math::vec2(0, 0), ::scm::math::vec2f(fbo->width(), fbo->height()))); + + rendering(pipeline_->get_current_scene(eye), ctx, eye, camera, fbo); + + fbo->unbind(ctx); + } +} + } diff --git a/src/gua/renderer/GBufferNURBSUberShader.cpp b/src/gua/renderer/GBufferNURBSUberShader.cpp index ab2ae87cb..0abea5e9c 100644 --- a/src/gua/renderer/GBufferNURBSUberShader.cpp +++ b/src/gua/renderer/GBufferNURBSUberShader.cpp @@ -154,8 +154,8 @@ std::string const GBufferNURBSUberShader::_transform_feedback_tess_control_shade \n\ layout(vertices = 4) out; \n\ \n\ - layout(binding=5) uniform samplerBuffer parameter_texture; \n\ - layout(binding=6) uniform samplerBuffer attribute_texture; \n\ + uniform samplerBuffer parameter_texture; \n\ + uniform samplerBuffer attribute_texture; \n\ \n\ // uniforms \n\ uniform mat4 gua_projection_matrix; \n\ @@ -288,8 +288,8 @@ std::string const GBufferNURBSUberShader::_transform_feedback_tess_evaluation_sh uniform mat4 gua_inverse_projection_view_matrix; \n\ uniform vec3 gua_camera_position; \n\ \n\ - layout(binding=5) uniform samplerBuffer parameter_texture; \n\ - layout(binding=6) uniform samplerBuffer attribute_texture; \n\ + uniform samplerBuffer parameter_texture; \n\ + uniform samplerBuffer attribute_texture; \n\ "); tess_eval << NURBSShader::surface_horner_evaluation(); @@ -346,8 +346,8 @@ std::string const GBufferNURBSUberShader::_transform_feedback_geometry_shader () layout (location = 1) flat out uint xfb_index; \n\ layout (location = 2) out vec2 xfb_tesscoord; \n\ \n\ - layout(binding=5) uniform samplerBuffer parameter_texture; \n\ - layout(binding=6) uniform samplerBuffer attribute_texture; \n\ + uniform samplerBuffer parameter_texture; \n\ + uniform samplerBuffer attribute_texture; \n\ "); tf_geom << NURBSShader::surface_horner_evaluation(); @@ -464,8 +464,8 @@ std::string const GBufferNURBSUberShader::_final_tess_control_shader () const \n\ layout(vertices = 4) out; \n\ \n\ - layout(binding=5) uniform samplerBuffer parameter_texture; \n\ - layout(binding=6) uniform samplerBuffer attribute_texture; \n\ + uniform samplerBuffer parameter_texture; \n\ + uniform samplerBuffer attribute_texture; \n\ \n\ uniform mat4 gua_projection_matrix; \n\ uniform mat4 gua_view_matrix; \n\ @@ -651,8 +651,8 @@ std::string const GBufferNURBSUberShader::_final_tess_evaluation_shader () const uniform mat4 gua_inverse_projection_view_matrix; \n\ uniform vec3 gua_camera_position; \n\ \n\ - layout(binding=5) uniform samplerBuffer parameter_texture; \n\ - layout(binding=6) uniform samplerBuffer attribute_texture; \n\ + uniform samplerBuffer parameter_texture; \n\ + uniform samplerBuffer attribute_texture; \n\ "); tess_eval << NURBSShader::surface_horner_evaluation(); @@ -732,8 +732,8 @@ std::string const GBufferNURBSUberShader::_final_geometry_shader () const flat out uint gIndex; \n\ out vec2 gTessCoord; \n\ \n\ - layout(binding=5) uniform samplerBuffer parameter_texture; \n\ - layout(binding=6) uniform samplerBuffer attribute_texture; \n\ + uniform samplerBuffer parameter_texture; \n\ + uniform samplerBuffer attribute_texture; \n\ "); // generated material-dependent uniform definitions @@ -853,13 +853,13 @@ std::string const GBufferNURBSUberShader::_final_fragment_shader () const fragment_shader << "// hard-coded uniform input"; fragment_shader << std::string(" \n\ \n\ - layout(binding=6) uniform samplerBuffer attribute_texture; \n\ + uniform samplerBuffer attribute_texture; \n\ \n\ - layout(binding=7) uniform samplerBuffer trim_partition; \n\ - layout(binding=8) uniform samplerBuffer trim_contourlist; \n\ - layout(binding=9) uniform samplerBuffer trim_curvelist; \n\ - layout(binding=10) uniform samplerBuffer trim_curvedata; \n\ - layout(binding=11) uniform samplerBuffer trim_pointdata; \n\ + uniform samplerBuffer trim_partition; \n\ + uniform samplerBuffer trim_contourlist; \n\ + uniform samplerBuffer trim_curvelist; \n\ + uniform samplerBuffer trim_curvedata; \n\ + uniform samplerBuffer trim_pointdata; \n\ \n\ "); diff --git a/src/gua/renderer/NURBS.cpp b/src/gua/renderer/NURBS.cpp index eb0c1b557..82d4dc1a4 100644 --- a/src/gua/renderer/NURBS.cpp +++ b/src/gua/renderer/NURBS.cpp @@ -143,6 +143,8 @@ void NURBS::predraw(RenderContext const& context) const { scm::gl::context_image_units_guard cig(in_context); scm::gl::context_texture_units_guard ctg(in_context); + context.render_context->set_rasterizer_state(_rstate_ms_solid[context.id], 1.0f); + //Transform Feedback Stage Begins in_context->begin_transform_feedback(_transform_feedback[context.id], scm::gl::PRIMITIVE_POINTS); @@ -168,6 +170,14 @@ void NURBS::predraw(RenderContext const& context) const { in_context->bind_texture( _trim_pointdata_texture_buffer[context.id], _sstate[context.id], 11); + in_context->current_program()->uniform_sampler("parameter_texture", 5); + in_context->current_program()->uniform_sampler("attribute_texture", 6); + in_context->current_program()->uniform_sampler("trim_partition", 7); + in_context->current_program()->uniform_sampler("trim_contourlist", 8); + in_context->current_program()->uniform_sampler("trim_curvelist", 9); + in_context->current_program()->uniform_sampler("trim_curvedata", 10); + in_context->current_program()->uniform_sampler("trim_pointdata", 11); + in_context->apply(); in_context->draw_elements(_data->index_data.size()); @@ -212,6 +222,8 @@ void NURBS::draw(RenderContext const& context) const { scm::gl::context_image_units_guard cig1(in_context); scm::gl::context_texture_units_guard ctg1(in_context); + context.render_context->set_rasterizer_state(_rstate_ms_solid[context.id], 1.0f); + in_context->bind_vertex_array(_transform_feedback_vao[context.id]); in_context->bind_texture( @@ -229,6 +241,14 @@ void NURBS::draw(RenderContext const& context) const { in_context->bind_texture( _trim_pointdata_texture_buffer[context.id], _sstate[context.id], 11); + in_context->current_program()->uniform_sampler("parameter_texture", 5); + in_context->current_program()->uniform_sampler("attribute_texture", 6); + in_context->current_program()->uniform_sampler("trim_partition", 7); + in_context->current_program()->uniform_sampler("trim_contourlist", 8); + in_context->current_program()->uniform_sampler("trim_curvelist", 9); + in_context->current_program()->uniform_sampler("trim_curvedata", 10); + in_context->current_program()->uniform_sampler("trim_pointdata", 11); + in_context->apply(); in_context->draw_transform_feedback( diff --git a/src/gua/renderer/Pipeline.cpp b/src/gua/renderer/Pipeline.cpp index 6e5f88cf8..5e1368475 100644 --- a/src/gua/renderer/Pipeline.cpp +++ b/src/gua/renderer/Pipeline.cpp @@ -417,14 +417,14 @@ void Pipeline::create_buffers() { passes_[PipelineStage::shading]->set_inputs(stereobuffers); stereobuffers.push_back(passes_[PipelineStage::shading]->get_gbuffer()); + passes_[PipelineStage::compositing]->set_inputs(stereobuffers); + passes_[PipelineStage::compositing]->create(*context_, config, {} ); + stereobuffers.push_back(passes_[PipelineStage::compositing]->get_gbuffer()); + scm::gl::sampler_state_desc state(scm::gl::FILTER_MIN_MAG_LINEAR, scm::gl::WRAP_REPEAT, scm::gl::WRAP_REPEAT); - passes_[PipelineStage::compositing]->create(*context_, config, { { BufferComponent::F3, state } }); - passes_[PipelineStage::compositing]->set_inputs(stereobuffers); - stereobuffers.push_back(passes_[PipelineStage::compositing]->get_gbuffer()); - passes_[PipelineStage::postfx]->create(*context_, config, { { BufferComponent::F3, state } }); passes_[PipelineStage::postfx]->set_inputs(stereobuffers); diff --git a/src/gua/renderer/PostFXPass.cpp b/src/gua/renderer/PostFXPass.cpp index 7802aae79..b967d424f 100644 --- a/src/gua/renderer/PostFXPass.cpp +++ b/src/gua/renderer/PostFXPass.cpp @@ -288,7 +288,7 @@ void PostFXPass::render_scene(Camera const& camera, RenderContext const& ctx) { any_godrays = render_godrays(camera, pipeline_->get_current_scene(eye), eye, ctx); render_glow(eye, ctx); - auto input_tex(inputs_[Pipeline::shading]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); + auto input_tex(inputs_[Pipeline::compositing]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); auto ping_tex(ping_buffer_->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); auto pong_tex(pong_buffer_->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); auto normal_tex(inputs_[Pipeline::geometry]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); @@ -533,7 +533,7 @@ void PostFXPass::render_glow(CameraMode eye, RenderContext const& ctx) { glow_shader_->set_uniform(ctx, pipeline_->config.bloom_threshold(), "gua_glow_threshold"); - auto color_buffer(inputs_[Pipeline::shading]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); + auto color_buffer(inputs_[Pipeline::compositing]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); ctx.render_context->set_viewport(scm::gl::viewport( math::vec2(0,0), math::vec2(float(glow_buffers_[0]->width()), float(glow_buffers_[0]->height())))); From 766970fc872dfbb826a03ea7ff81ca30de749fe8 Mon Sep 17 00:00:00 2001 From: gandideluxe Date: Wed, 4 Dec 2013 18:56:23 +0100 Subject: [PATCH 069/146] RAW Geometry is not supported (used for volumes) --- src/gua/renderer/MeshLoader.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/gua/renderer/MeshLoader.cpp b/src/gua/renderer/MeshLoader.cpp index ad6bb10b7..2caa061f2 100644 --- a/src/gua/renderer/MeshLoader.cpp +++ b/src/gua/renderer/MeshLoader.cpp @@ -132,6 +132,11 @@ std::vector const MeshLoader::load_from_buffer(char const* buffer_name, bool MeshLoader::is_supported(std::string const& file_name) const { auto point_pos(file_name.find_last_of(".")); Assimp::Importer importer; + + if (file_name.substr(point_pos + 1) == "raw"){ + return false; + } + return importer.IsExtensionSupported(file_name.substr(point_pos + 1)); } From 416f85c2bd8dade69b0ab1c119ce89b28928f871 Mon Sep 17 00:00:00 2001 From: gandideluxe Date: Wed, 4 Dec 2013 18:54:04 +0100 Subject: [PATCH 070/146] Integrate VolumeLoader into GeometryLoader Load Volume with create_geometry_from_file Material doesnt matter --- include/gua/renderer/GeometryLoader.hpp | 11 ++ src/gua/renderer/GeometryLoader.cpp | 128 ++++++++++++++++++++++++ 2 files changed, 139 insertions(+) diff --git a/include/gua/renderer/GeometryLoader.hpp b/include/gua/renderer/GeometryLoader.hpp index 74c4e5b95..b0e323d4c 100644 --- a/include/gua/renderer/GeometryLoader.hpp +++ b/include/gua/renderer/GeometryLoader.hpp @@ -59,6 +59,17 @@ class GUA_DLL GeometryLoader { std::string const& fallback_material, unsigned flags = DEFAULTS); + std::shared_ptr GeometryLoader::create_volume_from_file(std::string const& node_name, + std::string const& file_name, + unsigned flags = DEFAULTS); + + + std::shared_ptr GeometryLoader::create_vvolume_from_file(std::string const& node_name, + std::string const& vfile_name, + unsigned flags = DEFAULTS, + scm::size_t vol_hdd_cache_size = 2147483648, + scm::size_t vol_gpu_cache_size = 536870912); + private: diff --git a/src/gua/renderer/GeometryLoader.cpp b/src/gua/renderer/GeometryLoader.cpp index 15a84da51..83973813f 100644 --- a/src/gua/renderer/GeometryLoader.cpp +++ b/src/gua/renderer/GeometryLoader.cpp @@ -27,7 +27,9 @@ #include #include #include +#include #include +#include #include // external headers @@ -45,6 +47,7 @@ std::unordered_map> GeometryLoader::GeometryLoader() : fileloaders_() { fileloaders_.push_back(new MeshLoader); fileloaders_.push_back(new NURBSLoader); + fileloaders_.push_back(new VolumeLoader); } //////////////////////////////////////////////////////////////////////////////// @@ -127,6 +130,131 @@ std::shared_ptr GeometryLoader::create_geometry_from_file //////////////////////////////////////////////////////////////////////////////// +std::shared_ptr GeometryLoader::create_volume_from_file(std::string const& node_name, + std::string const& file_name, + unsigned flags) +{ + + std::shared_ptr cached_node; + std::string key(file_name + "_" + string_utils::to_string(flags)); + + auto searched(loaded_files_.find(key)); + if (searched != loaded_files_.end()) { + + cached_node = searched->second; + + } + else { + + bool fileload_succeed = false; + for (auto f : fileloaders_) { + if (f->is_supported(file_name)) { + cached_node = f->load(file_name, flags); + cached_node->update_cache(); + loaded_files_.insert(std::make_pair(key, cached_node)); + + // normalize volume position and rotation + if (flags & VolumeLoader::NORMALIZE_POSITION || flags & VolumeLoader::NORMALIZE_SCALE) { + auto bbox = cached_node->get_bounding_box(); + + if (flags & VolumeLoader::NORMALIZE_POSITION) { + auto center((bbox.min + bbox.max)*0.5); + cached_node->translate(-center); + } + + if (flags & VolumeLoader::NORMALIZE_SCALE) { + auto size(bbox.max - bbox.min); + auto max_size(std::max(std::max(size.x, size.y), size.z)); + cached_node->scale(1.f / max_size); + } + + } + } + fileload_succeed = true; + break; + } + + if (!cached_node) { + WARNING("Unable to load %s: Volume Type is not supported!", file_name.c_str()); + } + } + + if (cached_node) { + auto copy(cached_node->deep_copy()); + + copy->set_name(node_name); + return copy; + } + + return std::make_shared(node_name); +} + +std::shared_ptr GeometryLoader::create_vvolume_from_file( + std::string const& node_name, + std::string const& vfile_name, + unsigned flags, + scm::size_t vol_hdd_cache_size, + scm::size_t vol_gpu_cache_size + ) +{ + + std::shared_ptr cached_node; + std::string key(vfile_name + "_" + string_utils::to_string(flags)); + + auto searched(loaded_files_.find(key)); + if (searched != loaded_files_.end()) { + + cached_node = searched->second; + + } + else { + + bool fileload_succeed = false; + for (auto f : fileloaders_) { + if (f->is_supported(vfile_name)) { + cached_node = static_cast(f)->load(vfile_name, vol_hdd_cache_size, vol_gpu_cache_size); + cached_node->update_cache(); + loaded_files_.insert(std::make_pair(key, cached_node)); + + // normalize volume position and rotation + if (flags & VolumeLoader::NORMALIZE_POSITION || flags & VolumeLoader::NORMALIZE_SCALE) { + auto bbox = cached_node->get_bounding_box(); + + if (flags & VolumeLoader::NORMALIZE_POSITION) { + auto center((bbox.min + bbox.max)*0.5); + cached_node->translate(-center); + } + + if (flags & VolumeLoader::NORMALIZE_SCALE) { + auto size(bbox.max - bbox.min); + auto max_size(std::max(std::max(size.x, size.y), size.z)); + cached_node->scale(1.f / max_size); + } + + } + + fileload_succeed = true; + break; + } + } + + if (!cached_node) { + WARNING("Unable to load %s: Volume Type is not supported!", vfile_name.c_str()); + } + } + + if (cached_node) { + auto copy(cached_node->deep_copy()); + + copy->set_name(node_name); + return copy; + } + + return std::make_shared(node_name); +} + +//////////////////////////////////////////////////////////////////////////////// + void GeometryLoader::apply_fallback_material(std::shared_ptr const& root, std::string const& fallback_material) const { auto g_node(std::dynamic_pointer_cast(root)); From 36ad1feb4292211c5b1e374d513533475d964f6d Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Sat, 7 Dec 2013 15:19:44 +0100 Subject: [PATCH 071/146] Change indentation. --- src/gua/renderer/GeometryLoader.cpp | 199 ++++++++++++++-------------- 1 file changed, 98 insertions(+), 101 deletions(-) diff --git a/src/gua/renderer/GeometryLoader.cpp b/src/gua/renderer/GeometryLoader.cpp index 83973813f..e0b0ce1c3 100644 --- a/src/gua/renderer/GeometryLoader.cpp +++ b/src/gua/renderer/GeometryLoader.cpp @@ -134,128 +134,125 @@ std::shared_ptr GeometryLoader::create_volume_from_file(std::string const& std::string const& file_name, unsigned flags) { + std::shared_ptr cached_node; + std::string key(file_name + "_" + string_utils::to_string(flags)); + + auto searched(loaded_files_.find(key)); + if (searched != loaded_files_.end()) { + cached_node = searched->second; + } else { + + bool fileload_succeed = false; + for (auto f : fileloaders_) { + if (f->is_supported(file_name)) { + cached_node = f->load(file_name, flags); + cached_node->update_cache(); + loaded_files_.insert(std::make_pair(key, cached_node)); + + // normalize volume position and rotation + if ( flags & VolumeLoader::NORMALIZE_POSITION + || flags & VolumeLoader::NORMALIZE_SCALE) { + auto bbox = cached_node->get_bounding_box(); + + if (flags & VolumeLoader::NORMALIZE_POSITION) { + auto center((bbox.min + bbox.max)*0.5); + cached_node->translate(-center); + } + + if (flags & VolumeLoader::NORMALIZE_SCALE) { + auto size(bbox.max - bbox.min); + auto max_size(std::max(std::max(size.x, size.y), + size.z)); + cached_node->scale(1.f / max_size); + } + + } + } + fileload_succeed = true; + break; + } - std::shared_ptr cached_node; - std::string key(file_name + "_" + string_utils::to_string(flags)); - - auto searched(loaded_files_.find(key)); - if (searched != loaded_files_.end()) { - - cached_node = searched->second; - - } - else { - - bool fileload_succeed = false; - for (auto f : fileloaders_) { - if (f->is_supported(file_name)) { - cached_node = f->load(file_name, flags); - cached_node->update_cache(); - loaded_files_.insert(std::make_pair(key, cached_node)); - - // normalize volume position and rotation - if (flags & VolumeLoader::NORMALIZE_POSITION || flags & VolumeLoader::NORMALIZE_SCALE) { - auto bbox = cached_node->get_bounding_box(); - - if (flags & VolumeLoader::NORMALIZE_POSITION) { - auto center((bbox.min + bbox.max)*0.5); - cached_node->translate(-center); - } - - if (flags & VolumeLoader::NORMALIZE_SCALE) { - auto size(bbox.max - bbox.min); - auto max_size(std::max(std::max(size.x, size.y), size.z)); - cached_node->scale(1.f / max_size); - } - - } - } - fileload_succeed = true; - break; - } - - if (!cached_node) { - WARNING("Unable to load %s: Volume Type is not supported!", file_name.c_str()); - } - } + if (!cached_node) { + WARNING("Unable to load %s: Volume Type is not supported!", + file_name.c_str()); + } + } - if (cached_node) { - auto copy(cached_node->deep_copy()); + if (cached_node) { + auto copy(cached_node->deep_copy()); - copy->set_name(node_name); - return copy; - } + copy->set_name(node_name); + return copy; + } - return std::make_shared(node_name); + return std::make_shared(node_name); } std::shared_ptr GeometryLoader::create_vvolume_from_file( - std::string const& node_name, - std::string const& vfile_name, - unsigned flags, - scm::size_t vol_hdd_cache_size, - scm::size_t vol_gpu_cache_size - ) -{ - - std::shared_ptr cached_node; - std::string key(vfile_name + "_" + string_utils::to_string(flags)); - - auto searched(loaded_files_.find(key)); - if (searched != loaded_files_.end()) { + std::string const& node_name, + std::string const& vfile_name, + unsigned flags, + scm::size_t vol_hdd_cache_size, + scm::size_t vol_gpu_cache_size) { - cached_node = searched->second; - - } - else { + std::shared_ptr cached_node; + std::string key(vfile_name + "_" + string_utils::to_string(flags)); - bool fileload_succeed = false; - for (auto f : fileloaders_) { - if (f->is_supported(vfile_name)) { - cached_node = static_cast(f)->load(vfile_name, vol_hdd_cache_size, vol_gpu_cache_size); - cached_node->update_cache(); - loaded_files_.insert(std::make_pair(key, cached_node)); + auto searched(loaded_files_.find(key)); + if (searched != loaded_files_.end()) { + cached_node = searched->second; + } else { + bool fileload_succeed = false; + for (auto f : fileloaders_) { + if (f->is_supported(vfile_name)) { + cached_node = static_cast(f)->load(vfile_name, + vol_hdd_cache_size, vol_gpu_cache_size); + cached_node->update_cache(); + loaded_files_.insert(std::make_pair(key, cached_node)); - // normalize volume position and rotation - if (flags & VolumeLoader::NORMALIZE_POSITION || flags & VolumeLoader::NORMALIZE_SCALE) { - auto bbox = cached_node->get_bounding_box(); + // normalize volume position and rotation + if ( flags & VolumeLoader::NORMALIZE_POSITION + || flags & VolumeLoader::NORMALIZE_SCALE) { + auto bbox = cached_node->get_bounding_box(); - if (flags & VolumeLoader::NORMALIZE_POSITION) { - auto center((bbox.min + bbox.max)*0.5); - cached_node->translate(-center); - } + if (flags & VolumeLoader::NORMALIZE_POSITION) { + auto center((bbox.min + bbox.max)*0.5); + cached_node->translate(-center); + } - if (flags & VolumeLoader::NORMALIZE_SCALE) { - auto size(bbox.max - bbox.min); - auto max_size(std::max(std::max(size.x, size.y), size.z)); - cached_node->scale(1.f / max_size); - } + if (flags & VolumeLoader::NORMALIZE_SCALE) { + auto size(bbox.max - bbox.min); + auto max_size(std::max(std::max(size.x, size.y), + size.z)); + cached_node->scale(1.f / max_size); + } - } - - fileload_succeed = true; - break; - } - } + } + fileload_succeed = true; + break; + } + } - if (!cached_node) { - WARNING("Unable to load %s: Volume Type is not supported!", vfile_name.c_str()); - } - } + if (!cached_node) { + WARNING("Unable to load %s: Volume Type is not supported!", + vfile_name.c_str()); + } + } - if (cached_node) { - auto copy(cached_node->deep_copy()); + if (cached_node) { + auto copy(cached_node->deep_copy()); - copy->set_name(node_name); - return copy; - } + copy->set_name(node_name); + return copy; + } - return std::make_shared(node_name); + return std::make_shared(node_name); } //////////////////////////////////////////////////////////////////////////////// -void GeometryLoader::apply_fallback_material(std::shared_ptr const& root, std::string const& fallback_material) const { +void GeometryLoader::apply_fallback_material(std::shared_ptr const& root, + std::string const& fallback_material) const { auto g_node(std::dynamic_pointer_cast(root)); From 0c744eab140e962681451810be89c6d47d42d55e Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Sat, 7 Dec 2013 15:25:43 +0100 Subject: [PATCH 072/146] Remove unused load function --- include/gua/renderer/GeometryLoader.hpp | 14 ++---- src/gua/renderer/GeometryLoader.cpp | 61 ------------------------- 2 files changed, 3 insertions(+), 72 deletions(-) diff --git a/include/gua/renderer/GeometryLoader.hpp b/include/gua/renderer/GeometryLoader.hpp index b0e323d4c..8a02b4a9d 100644 --- a/include/gua/renderer/GeometryLoader.hpp +++ b/include/gua/renderer/GeometryLoader.hpp @@ -59,17 +59,9 @@ class GUA_DLL GeometryLoader { std::string const& fallback_material, unsigned flags = DEFAULTS); - std::shared_ptr GeometryLoader::create_volume_from_file(std::string const& node_name, - std::string const& file_name, - unsigned flags = DEFAULTS); - - - std::shared_ptr GeometryLoader::create_vvolume_from_file(std::string const& node_name, - std::string const& vfile_name, - unsigned flags = DEFAULTS, - scm::size_t vol_hdd_cache_size = 2147483648, - scm::size_t vol_gpu_cache_size = 536870912); - + std::shared_ptr create_volume_from_file(std::string const& node_name, + std::string const& file_name, + unsigned flags = DEFAULTS); private: diff --git a/src/gua/renderer/GeometryLoader.cpp b/src/gua/renderer/GeometryLoader.cpp index e0b0ce1c3..cd19fa2d2 100644 --- a/src/gua/renderer/GeometryLoader.cpp +++ b/src/gua/renderer/GeometryLoader.cpp @@ -188,67 +188,6 @@ std::shared_ptr GeometryLoader::create_volume_from_file(std::string const& return std::make_shared(node_name); } -std::shared_ptr GeometryLoader::create_vvolume_from_file( - std::string const& node_name, - std::string const& vfile_name, - unsigned flags, - scm::size_t vol_hdd_cache_size, - scm::size_t vol_gpu_cache_size) { - - std::shared_ptr cached_node; - std::string key(vfile_name + "_" + string_utils::to_string(flags)); - - auto searched(loaded_files_.find(key)); - if (searched != loaded_files_.end()) { - cached_node = searched->second; - } else { - bool fileload_succeed = false; - for (auto f : fileloaders_) { - if (f->is_supported(vfile_name)) { - cached_node = static_cast(f)->load(vfile_name, - vol_hdd_cache_size, vol_gpu_cache_size); - cached_node->update_cache(); - loaded_files_.insert(std::make_pair(key, cached_node)); - - // normalize volume position and rotation - if ( flags & VolumeLoader::NORMALIZE_POSITION - || flags & VolumeLoader::NORMALIZE_SCALE) { - auto bbox = cached_node->get_bounding_box(); - - if (flags & VolumeLoader::NORMALIZE_POSITION) { - auto center((bbox.min + bbox.max)*0.5); - cached_node->translate(-center); - } - - if (flags & VolumeLoader::NORMALIZE_SCALE) { - auto size(bbox.max - bbox.min); - auto max_size(std::max(std::max(size.x, size.y), - size.z)); - cached_node->scale(1.f / max_size); - } - - } - fileload_succeed = true; - break; - } - } - - if (!cached_node) { - WARNING("Unable to load %s: Volume Type is not supported!", - vfile_name.c_str()); - } - } - - if (cached_node) { - auto copy(cached_node->deep_copy()); - - copy->set_name(node_name); - return copy; - } - - return std::make_shared(node_name); -} - //////////////////////////////////////////////////////////////////////////////// void GeometryLoader::apply_fallback_material(std::shared_ptr const& root, From a0ed59fc19b1b46fdb22ae41fdb85f865b8b3d05 Mon Sep 17 00:00:00 2001 From: gandideluxe Date: Sat, 7 Dec 2013 16:34:57 +0100 Subject: [PATCH 073/146] Volume Rendering CompositingPass fixed --- src/gua/renderer/CompositePass.cpp | 191 +++++++++++++++-------------- 1 file changed, 100 insertions(+), 91 deletions(-) diff --git a/src/gua/renderer/CompositePass.cpp b/src/gua/renderer/CompositePass.cpp index b906e0381..22b49fd65 100644 --- a/src/gua/renderer/CompositePass.cpp +++ b/src/gua/renderer/CompositePass.cpp @@ -108,97 +108,106 @@ void CompositePass::create(RenderContext const& ctx, Camera const& camera, FrameBufferObject* target) { -#if 0 - ///TODO: Toplevel - if (!scene.volumenodes_.empty() /*|| !scene.transparentnodes_.empty()*/) - { - init_ressources(ctx); - - ctx.render_context->set_depth_stencil_state(depth_stencil_state_); - - // 1. render proxy geometry into fbo - volume_raygeneration_buffer_->bind(ctx); - { - scm::math::vec2f resolution(volume_raygeneration_buffer_->width(), volume_raygeneration_buffer_->height()); - ctx.render_context->set_viewport(scm::gl::viewport(math::vec2(0, 0), resolution)); - - // gather input textures and set uniforms - Pass::set_camera_matrices(*ray_generation_shader_, camera, pipeline_->get_current_scene(eye), eye, ctx); - - ray_generation_shader_->set_uniform(ctx, 1.f / gbuffer_->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->width(), "gua_texel_width"); - ray_generation_shader_->set_uniform(ctx, 1.f / gbuffer_->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->height(), "gua_texel_height"); - - volume_raygeneration_buffer_->clear_color_buffers(ctx, gua::utils::Color3f(0.0f, 0.0f, 0.0f)); - volume_raygeneration_buffer_->clear_depth_stencil_buffer(ctx); - - //fullscreen_quad_->draw(ctx.render_context); - for (auto const& node : scene.volumenodes_) { - - auto volume = - std::static_pointer_cast(GeometryDatabase::instance()->lookup(node.data.get_volume())); - - if (volume) { - ray_generation_shader_->set_uniform( - ctx, node.transform, "gua_model_matrix"); - - //volume->set_uniforms(ctx, ray_generation_shader_); - - ray_generation_shader_->use(ctx); - { - volume->draw_proxy(ctx); - } - ray_generation_shader_->unuse(ctx); - } - } - } - volume_raygeneration_buffer_->unbind(ctx); - - // 2. render fullscreen quad for compositing and volume ray castinG - Pass::set_camera_matrices(*composite_shader_, camera, pipeline_->get_current_scene(eye), eye, ctx); - - auto input_tex(inputs_[Pipeline::shading]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); - auto normal_tex(inputs_[Pipeline::geometry]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); - auto depth_tex(inputs_[Pipeline::geometry]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_depth_buffer()); - auto raygen_tex(volume_raygeneration_buffer_->get_color_buffers(TYPE_FLOAT)[0]); - - composite_shader_->set_uniform(ctx, input_tex, "gua_color_gbuffer_in"); - composite_shader_->set_uniform(ctx, normal_tex, "gua_normal_gbuffer_in"); - composite_shader_->set_uniform(ctx, depth_tex, "gua_depth_gbuffer_in"); - composite_shader_->set_uniform(ctx, raygen_tex, "gua_ray_entry_in"); - - composite_shader_->set_uniform(ctx, 1.f / gbuffer_->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->width(), "gua_texel_width"); - composite_shader_->set_uniform(ctx, 1.f / gbuffer_->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->height(), "gua_texel_height"); - - // bind target fbo and set viewport - target->bind(ctx); - ctx.render_context->set_viewport(scm::gl::viewport( - math::vec2(0, 0), - ::scm::math::vec2f(target->width(), target->height()))); - - for (auto const& node : scene.volumenodes_) { - - auto volume = - std::static_pointer_cast(GeometryDatabase::instance()->lookup(node.data.get_volume())); - - if (volume) { - composite_shader_->set_uniform( - ctx, node.transform, "gua_model_matrix"); - - volume->set_uniforms(ctx, composite_shader_); - - composite_shader_->use(ctx); - { - fullscreen_quad_->draw(ctx.render_context); - } - composite_shader_->unuse(ctx); - } - } - - target->unbind(ctx); - - ctx.render_context->reset_state_objects(); - } -#endif + init_ressources(ctx); + + ctx.render_context->set_depth_stencil_state(depth_stencil_state_); + + // 1. render proxy geometry into fbo + volume_raygeneration_buffer_->bind(ctx); + { + scm::math::vec2f resolution(volume_raygeneration_buffer_->width(), volume_raygeneration_buffer_->height()); + ctx.render_context->set_viewport(scm::gl::viewport(math::vec2(0, 0), resolution)); + + volume_raygeneration_buffer_->clear_color_buffers(ctx, gua::utils::Color3f(0.0f, 0.0f, 0.0f)); + volume_raygeneration_buffer_->clear_depth_stencil_buffer(ctx); + + ///TODO: Toplevel + if (!scene.volumenodes_.empty()) + { + // gather input textures and set uniforms + Pass::set_camera_matrices(*ray_generation_shader_, camera, pipeline_->get_current_scene(eye), eye, ctx); + + ray_generation_shader_->set_uniform(ctx, 1.f / gbuffer_->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->width(), "gua_texel_width"); + ray_generation_shader_->set_uniform(ctx, 1.f / gbuffer_->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->height(), "gua_texel_height"); + + + volume_raygeneration_buffer_->clear_color_buffers(ctx, gua::utils::Color3f(0.0f, 0.0f, 0.0f)); + volume_raygeneration_buffer_->clear_depth_stencil_buffer(ctx); + + for (auto const& node : scene.volumenodes_) { + + auto volume = + std::static_pointer_cast(GeometryDatabase::instance()->lookup(node.data.get_volume())); + + if (volume) { + ray_generation_shader_->set_uniform( + ctx, node.transform, "gua_model_matrix"); + + ray_generation_shader_->set_uniform( + ctx, 0, "volume_frag_id"); + + ray_generation_shader_->use(ctx); + { + volume->draw_proxy(ctx); + } + ray_generation_shader_->unuse(ctx); + } + } + } + } + volume_raygeneration_buffer_->unbind(ctx); + + scm::gl::context_all_guard cug(ctx.render_context); + + // 2. render fullscreen quad for compositing and volume ray castinG + Pass::set_camera_matrices(*composite_shader_, camera, pipeline_->get_current_scene(eye), eye, ctx); + + auto input_tex(inputs_[Pipeline::shading]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); + auto normal_tex(inputs_[Pipeline::geometry]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); + auto depth_tex(inputs_[Pipeline::geometry]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_depth_buffer()); + + auto raygen_tex(volume_raygeneration_buffer_->get_color_buffers(TYPE_FLOAT)[0]); + + composite_shader_->set_uniform(ctx, input_tex, "gua_color_gbuffer_in"); + composite_shader_->set_uniform(ctx, normal_tex, "gua_normal_gbuffer_in"); + composite_shader_->set_uniform(ctx, depth_tex, "gua_depth_gbuffer_in"); + composite_shader_->set_uniform(ctx, raygen_tex, "gua_ray_entry_in"); + + composite_shader_->set_uniform(ctx, 1.f / gbuffer_->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->width(), "gua_texel_width"); + composite_shader_->set_uniform(ctx, 1.f / gbuffer_->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->height(), "gua_texel_height"); + + // bind target fbo and set viewport + target->bind(ctx); + ctx.render_context->set_viewport(scm::gl::viewport( + math::vec2(0, 0), + ::scm::math::vec2f(target->width(), target->height()))); + + if (!scene.volumenodes_.empty()){ + + for (auto const& node : scene.volumenodes_) { + + auto volume = + std::static_pointer_cast(GeometryDatabase::instance()->lookup(node.data.get_volume())); + + if (volume) { + composite_shader_->set_uniform( + ctx, node.transform, "gua_model_matrix"); + + volume->set_uniforms(ctx, composite_shader_); + + composite_shader_->use(ctx); + { + fullscreen_quad_->draw(ctx.render_context); + } + composite_shader_->unuse(ctx); + } + } + + } + + target->unbind(ctx); + + ctx.render_context->reset_state_objects(); } //////////////////////////////////////////////////////////////////////////////// From c0f9dcfc3861114ec3b519545a547bf74b36b1e4 Mon Sep 17 00:00:00 2001 From: gandideluxe Date: Sat, 7 Dec 2013 16:50:49 +0100 Subject: [PATCH 074/146] Messages in Volume load function erased --- src/gua/renderer/Volume.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gua/renderer/Volume.cpp b/src/gua/renderer/Volume.cpp index c687259fc..de1168a80 100644 --- a/src/gua/renderer/Volume.cpp +++ b/src/gua/renderer/Volume.cpp @@ -79,7 +79,7 @@ namespace gua { (float)_volume_dimensions.y / (float)max_dimension_volume, (float)_volume_dimensions.z / (float)max_dimension_volume); - MESSAGE("%f %f %f", _volume_dimensions_normalized.x, _volume_dimensions_normalized.y, _volume_dimensions_normalized.z); + //MESSAGE("%f %f %f", _volume_dimensions_normalized.x, _volume_dimensions_normalized.y, _volume_dimensions_normalized.z); //getchar(); bounding_box_ = math::BoundingBox(math::vec3::zero(), _volume_dimensions_normalized); @@ -146,7 +146,7 @@ namespace gua { _volume_texture_ptr[ctx.id] = std::shared_ptr(new Texture3D(_volume_file_path));// scm_volume_loader.load_texture_3d(*(ctx.render_device.get()), _volume_file_path, false); _volume_texture_ptr[ctx.id]->upload_to(ctx); - MESSAGE("%s loaded!", _volume_file_path.c_str()); + //MESSAGE("%s loaded!", _volume_file_path.c_str()); //scm::gl::texture_loader scm_image_loader; _transfer_texture_ptr[ctx.id] = create_color_map(ctx, 255, _alpha_transfer, _color_transfer); From f6fecc243defe5c6da6ac839a6e0f039f2c3a93e Mon Sep 17 00:00:00 2001 From: gandideluxe Date: Sat, 7 Dec 2013 17:01:11 +0100 Subject: [PATCH 075/146] Output messages removed Even less output, yeah! --- src/gua/renderer/Volume.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/gua/renderer/Volume.cpp b/src/gua/renderer/Volume.cpp index de1168a80..7dc3e196a 100644 --- a/src/gua/renderer/Volume.cpp +++ b/src/gua/renderer/Volume.cpp @@ -209,7 +209,7 @@ namespace gua { return (std::shared_ptr(new Texture2D(in_size, 1))); } else{ - std::cout << "Volume::create_color_map(): color map texture generated." << std::endl; + //std::cout << "Volume::create_color_map(): color map texture generated." << std::endl; return (new_tex); } } @@ -246,14 +246,14 @@ namespace gua { combined_lut[i * 4 + 3] = alpha_lut[i]; } - MESSAGE("generating color map texture data done."); + //MESSAGE("generating color map texture data done."); - MESSAGE("uploading texture data ( size: %d KiB)...", static_cast(in_size * size_of_format(FORMAT_RGBA_32F)) / (1024.0)); + //MESSAGE("uploading texture data ( size: %d KiB)...", static_cast(in_size * size_of_format(FORMAT_RGBA_32F)) / (1024.0)); texture_region ur(vec3ui(0u), vec3ui(in_size, 1, 1)); bool res = ctx.render_context->update_sub_texture(transfer_texture_ptr->get_buffer(ctx), ur, 0u, FORMAT_RGBA_32F, combined_lut.get()); - MESSAGE("uploading texture data done."); + //MESSAGE("uploading texture data done."); if (!res) { MESSAGE("Volume::update_color_alpha_map(): error during color map texture generation."); From 1e1d4cc283423e5eee10bf4978d983dbd9bd55c1 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Tue, 10 Dec 2013 10:58:41 +0100 Subject: [PATCH 076/146] removed a message from find_boost.cmake --- cmake/modules/find_boost.cmake | 2 -- 1 file changed, 2 deletions(-) diff --git a/cmake/modules/find_boost.cmake b/cmake/modules/find_boost.cmake index 7fe440bbe..2b10e9404 100644 --- a/cmake/modules/find_boost.cmake +++ b/cmake/modules/find_boost.cmake @@ -139,8 +139,6 @@ IF ( BOOST_INCLUDE_DIRS AND NOT BOOST_LIBRARY_DIRS ) "${CMAKE_STATIC_LIBRARY_PREFIX}libboost_filesystem-mt${CMAKE_STATIC_LIBRARY_SUFFIX}") endif (UNIX) - MESSAGE(${_AVANGO_BOOST_FILESYSTEM_LIB}) - FOREACH(_SEARCH_DIR ${BOOST_LIBRARY_SEARCH_DIRS}) FIND_PATH(_CUR_SEARCH From 794b31487cdb5d6617a19eebc6d03fcc587ce10a Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Tue, 10 Dec 2013 14:22:09 +0100 Subject: [PATCH 077/146] fixed missing link libraries under linux --- plugins/guacamole-oculus/CMakeLists.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/guacamole-oculus/CMakeLists.txt b/plugins/guacamole-oculus/CMakeLists.txt index a7db83581..016b4603e 100644 --- a/plugins/guacamole-oculus/CMakeLists.txt +++ b/plugins/guacamole-oculus/CMakeLists.txt @@ -1,4 +1,4 @@ -# dependencies +# dependencies include(find_ovr) # determine source and header files @@ -12,9 +12,9 @@ SET(GUACAMOLE_OCULUS_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include) LINK_DIRECTORIES(${LIB_PATHS} ${OVR_LIBRARY_DIRS}) -INCLUDE_DIRECTORIES( ${INCLUDE_PATHS} - ${GUACAMOLE_SOURCE_DIR} - ${OVR_INCLUDE_DIRS} +INCLUDE_DIRECTORIES( ${INCLUDE_PATHS} + ${GUACAMOLE_SOURCE_DIR} + ${OVR_INCLUDE_DIRS} ${GUACAMOLE_OCULUS_SOURCE_DIR} ) @@ -29,7 +29,7 @@ IF (MSVC) ENDIF (MSVC) IF (UNIX) - LIST(APPEND LIBS udev Xinerama) + LIST(APPEND LIBS udev Xinerama guacamole ${OVR_LIBRARIES}) ELSEIF (MSVC) LIST(APPEND LIBS winmm.lib guacamole ${OVR_LIBRARIES}) ENDIF(UNIX) @@ -39,4 +39,4 @@ TARGET_LINK_LIBRARIES( guacamole-oculus debug ${LIBS} optimized ${LIBS}) # compile examples -ADD_SUBDIRECTORY(example-oculus) \ No newline at end of file +ADD_SUBDIRECTORY(example-oculus) From 355498e4bd62b1e3866f6c7fec7fa229d35ad14f Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Tue, 10 Dec 2013 14:39:05 +0100 Subject: [PATCH 078/146] removed shader debug output --- src/gua/renderer/CompositePass.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/gua/renderer/CompositePass.cpp b/src/gua/renderer/CompositePass.cpp index 22b49fd65..85a820c73 100644 --- a/src/gua/renderer/CompositePass.cpp +++ b/src/gua/renderer/CompositePass.cpp @@ -52,8 +52,6 @@ CompositePass::CompositePass(Pipeline* pipeline) : std::string ray_generation_fragment_shader(Resources::lookup_shader(Resources::shaders_uber_shaders_composite_ray_generation_frag)); ray_generation_shader_->create_from_sources(ray_generation_vertex_shader, ray_generation_fragment_shader); - - print_shaders("debug", "composite.txt"); } //////////////////////////////////////////////////////////////////////////////// @@ -104,9 +102,9 @@ void CompositePass::create(RenderContext const& ctx, /* virtual */ void CompositePass::rendering(SerializedScene const& scene, RenderContext const& ctx, - CameraMode eye, + CameraMode eye, Camera const& camera, - FrameBufferObject* target) + FrameBufferObject* target) { init_ressources(ctx); @@ -158,10 +156,10 @@ void CompositePass::create(RenderContext const& ctx, volume_raygeneration_buffer_->unbind(ctx); scm::gl::context_all_guard cug(ctx.render_context); - + // 2. render fullscreen quad for compositing and volume ray castinG Pass::set_camera_matrices(*composite_shader_, camera, pipeline_->get_current_scene(eye), eye, ctx); - + auto input_tex(inputs_[Pipeline::shading]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); auto normal_tex(inputs_[Pipeline::geometry]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_color_buffers(TYPE_FLOAT)[0]); auto depth_tex(inputs_[Pipeline::geometry]->get_eye_buffers()[eye == CameraMode::RIGHT ? 1 : 0]->get_depth_buffer()); From f8f0a9155c640687ef63bb0f9674bb3c9db1662f Mon Sep 17 00:00:00 2001 From: thelaui Date: Tue, 10 Dec 2013 14:53:32 +0100 Subject: [PATCH 079/146] merged new Oculus classes --- .../include/gua/OculusDeviceManager.hpp | 85 ++-- .../include/gua/OculusRift.hpp | 156 +++---- .../src/gua/OculusDeviceManager.cpp | 150 ++++--- .../guacamole-oculus/src/gua/OculusRift.cpp | 395 +++++++++--------- 4 files changed, 391 insertions(+), 395 deletions(-) diff --git a/plugins/guacamole-oculus/include/gua/OculusDeviceManager.hpp b/plugins/guacamole-oculus/include/gua/OculusDeviceManager.hpp index 3885d3645..ec9ee38b1 100644 --- a/plugins/guacamole-oculus/include/gua/OculusDeviceManager.hpp +++ b/plugins/guacamole-oculus/include/gua/OculusDeviceManager.hpp @@ -1,43 +1,44 @@ -#ifndef GUA_OCULUS_DEVICE_MANAGER_HPP -#define GUA_OCULUS_DEVICE_MANAGER_HPP - -#if defined (_MSC_VER) - #if defined (GUA_OCULUS_LIBRARY) - #define GUA_OCULUS_DLL __declspec( dllexport ) - #else -#define GUA_OCULUS_DLL __declspec( dllimport ) - #endif -#else - #define GUA_OCULUS_DLL -#endif // #if defined(_MSC_VER) - -namespace OVR { - class SensorFusion; - class DeviceManager; - class HMDDevice; - class SensorDevice; -} - -namespace gua -{ - class GUA_OCULUS_DLL OculusDeviceManager - { - public: - static OculusDeviceManager& getInstance(); - virtual ~OculusDeviceManager(); - - OVR::HMDDevice* getNextAvailableDevice(); - - protected: - OculusDeviceManager(); - - private: - OVR::DeviceManager* device_manager_; - static int numberOfDevicesCreated; - - OculusDeviceManager(OculusDeviceManager const& src); - OculusDeviceManager& operator=(OculusDeviceManager const& src); - }; -} - +#ifndef GUA_OCULUS_DEVICE_MANAGER_HPP +#define GUA_OCULUS_DEVICE_MANAGER_HPP + +#if defined (_MSC_VER) + #if defined (GUA_OCULUS_LIBRARY) + #define GUA_OCULUS_DLL __declspec( dllexport ) + #else +#define GUA_OCULUS_DLL __declspec( dllimport ) + #endif +#else + #define GUA_OCULUS_DLL +#endif // #if defined(_MSC_VER) + +namespace OVR { + class SensorFusion; + class DeviceManager; + class HMDDevice; + class SensorDevice; +} + +namespace gua +{ + class GUA_OCULUS_DLL OculusDeviceManager + { + public: + static OculusDeviceManager& getInstance(); + virtual ~OculusDeviceManager(); + + OVR::HMDDevice* getNextAvailableDevice(); + OVR::SensorDevice* getNextSensor(); + + protected: + OculusDeviceManager(); + + private: + OVR::DeviceManager* device_manager_; + static int numberOfDevicesCreated; + + OculusDeviceManager(OculusDeviceManager const& src); + OculusDeviceManager& operator=(OculusDeviceManager const& src); + }; +} + #endif // GUA_OCULUS_DEVICE_MANAGER_HPP \ No newline at end of file diff --git a/plugins/guacamole-oculus/include/gua/OculusRift.hpp b/plugins/guacamole-oculus/include/gua/OculusRift.hpp index 9809c77a9..c4c0aac61 100644 --- a/plugins/guacamole-oculus/include/gua/OculusRift.hpp +++ b/plugins/guacamole-oculus/include/gua/OculusRift.hpp @@ -1,78 +1,78 @@ -/****************************************************************************** - * guacamole - delicious VR * - * * - * Copyright: (c) 2011-2013 Bauhaus-Universität Weimar * - * Contact: felix.lauer@uni-weimar.de / simon.schneegans@uni-weimar.de * - * * - * This program is free software: you can redistribute it and/or modify it * - * under the terms of the GNU General Public License as published by the Free * - * Software Foundation, either version 3 of the License, or (at your option) * - * any later version. * - * * - * This program is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * - * for more details. * - * * - * You should have received a copy of the GNU General Public License along * - * with this program. If not, see . * - * * - ******************************************************************************/ - -#ifndef GUA_OCULUS_RIFT_HPP -#define GUA_OCULUS_RIFT_HPP - -#if defined (_MSC_VER) - #if defined (GUA_OCULUS_LIBRARY) - #define GUA_OCULUS_DLL __declspec( dllexport ) - #else -#define GUA_OCULUS_DLL __declspec( dllimport ) - #endif -#else - #define GUA_OCULUS_DLL -#endif // #if defined(_MSC_VER) - -// guacamole headers -#include - -namespace OVR { - class SensorFusion; - class DeviceManager; - class HMDDevice; - class SensorDevice; -} - -namespace gua { - -class GUA_OCULUS_DLL OculusRift : public Window { - public: - - static void init(); - - OculusRift(std::string const& display); - virtual ~OculusRift(); - - void create_shader(); - - // virtual - void display(std::shared_ptr const& left_texture, - std::shared_ptr const& right_texture); - - math::mat4 const get_transform() const; - - private: - void display(std::shared_ptr const& texture, - math::vec2ui const& size, - math::vec2ui const& position, - bool left); - - math::vec4 distortion_; - - OVR::SensorDevice* sensor_; - OVR::SensorFusion* sensor_fusion_; - OVR::HMDDevice* device_; -}; - -} - -#endif // GUA_OCULUS_RIFT_HPP +/****************************************************************************** + * guacamole - delicious VR * + * * + * Copyright: (c) 2011-2013 Bauhaus-Universität Weimar * + * Contact: felix.lauer@uni-weimar.de / simon.schneegans@uni-weimar.de * + * * + * This program is free software: you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the Free * + * Software Foundation, either version 3 of the License, or (at your option) * + * any later version. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * + * for more details. * + * * + * You should have received a copy of the GNU General Public License along * + * with this program. If not, see . * + * * + ******************************************************************************/ + +#ifndef GUA_OCULUS_RIFT_HPP +#define GUA_OCULUS_RIFT_HPP + +#if defined (_MSC_VER) + #if defined (GUA_OCULUS_LIBRARY) + #define GUA_OCULUS_DLL __declspec( dllexport ) + #else +#define GUA_OCULUS_DLL __declspec( dllimport ) + #endif +#else + #define GUA_OCULUS_DLL +#endif // #if defined(_MSC_VER) + +// guacamole headers +#include + +namespace OVR { + class SensorFusion; + class DeviceManager; + class HMDDevice; + class SensorDevice; +} + +namespace gua { + +class GUA_OCULUS_DLL OculusRift : public Window { + public: + + static void init(); + + OculusRift(std::string const& display); + virtual ~OculusRift(); + + void create_shader(); + + // virtual + void display(std::shared_ptr const& left_texture, + std::shared_ptr const& right_texture); + + math::mat4 const get_transform() const; + + private: + void display(std::shared_ptr const& texture, + math::vec2ui const& size, + math::vec2ui const& position, + bool left); + + math::vec4 distortion_; + + OVR::SensorDevice* sensor_; + OVR::SensorFusion* sensor_fusion_; + OVR::HMDDevice* device_; +}; + +} + +#endif // GUA_OCULUS_RIFT_HPP diff --git a/plugins/guacamole-oculus/src/gua/OculusDeviceManager.cpp b/plugins/guacamole-oculus/src/gua/OculusDeviceManager.cpp index 13ef3a749..6f90140b8 100644 --- a/plugins/guacamole-oculus/src/gua/OculusDeviceManager.cpp +++ b/plugins/guacamole-oculus/src/gua/OculusDeviceManager.cpp @@ -1,78 +1,72 @@ -// class header -#include - -// external headers -#include - -#include - -namespace gua -{ - int OculusDeviceManager::numberOfDevicesCreated(0); - - OculusDeviceManager& OculusDeviceManager::getInstance() - { - static OculusDeviceManager instance; - return instance; - } - - OculusDeviceManager::OculusDeviceManager() - { - device_manager_ = OVR::DeviceManager::Create(); - } - - /* virtual */ OculusDeviceManager::~OculusDeviceManager() - { - if (device_manager_) - delete device_manager_; - } - - OVR::HMDDevice* OculusDeviceManager::getNextAvailableDevice() - { - OVR::HMDDevice* deviceToReturn(nullptr); - - std::cout << "Getting some device" << std::endl; - - if (numberOfDevicesCreated == 0) - { - std::cout << "Getting first device" << std::endl; - deviceToReturn = device_manager_->EnumerateDevices().CreateDevice(); - } - else if (numberOfDevicesCreated == 1) - { - std::cout << "Getting second device" << std::endl; - auto enumerator = device_manager_->EnumerateDevices(); - enumerator.Next(); - deviceToReturn = enumerator.CreateDevice(); - } - - if (deviceToReturn != nullptr) - ++numberOfDevicesCreated; - - /* // Probably a better solution, to be used instead of the upper one as it supports more than 2 devices - auto enumerator = device_manager_->EnumerateDevices(); - int currentDevice(1); - bool deviceAvailable(true); - - while(currentDevice < numberOfDevicesCreated + 1) - { - if (enumerator.GetType() == Device_None) - { - deviceAvailable = false; - break; - } - - enumerator.Next(); - ++currentDevice; - } - - if (deviceAvailable) - { - deviceToReturn = enumerator.CreateDevice(); - ++numberOfDevicesCreated; - } - */ - - return deviceToReturn; - } -} +// class header +#include + +// external headers +#include + +#include + +namespace gua +{ + int OculusDeviceManager::numberOfDevicesCreated(0); + + OculusDeviceManager& OculusDeviceManager::getInstance() + { + static OculusDeviceManager instance; + return instance; + } + + OculusDeviceManager::OculusDeviceManager() + { + device_manager_ = OVR::DeviceManager::Create(); + } + + /* virtual */ OculusDeviceManager::~OculusDeviceManager() + { + if (device_manager_) + delete device_manager_; + } + + OVR::HMDDevice* OculusDeviceManager::getNextAvailableDevice() + { + OVR::HMDDevice* deviceToReturn(nullptr); + + auto enumerator = device_manager_->EnumerateDevices(); + //Surprisingly, it is not needed to call enumerator.Next() here + deviceToReturn = enumerator.CreateDevice(); + + if (deviceToReturn != nullptr) + { + ++numberOfDevicesCreated; + } + + std::cout << "Next device is number " << numberOfDevicesCreated << std::endl; + + return deviceToReturn; + } + + //Redefined function from the Oculus SDK in order to support multiple Oculus Rifts + OVR::SensorDevice* OculusDeviceManager::getNextSensor() + { + //Gets a sensor in order to attach it to a device + + OVR::SensorDevice* sensor(nullptr); + + auto enumerator = device_manager_->EnumerateDevices(); + + int i(1); + while (i < numberOfDevicesCreated) + { + enumerator.Next(); + ++i; + } + + sensor = enumerator.CreateDevice(); + + if (sensor) + sensor->SetCoordinateFrame(OVR::SensorDevice::Coord_HMD); + else + --numberOfDevicesCreated; // If no sensor was detected for a device, the number of devices created is decreased again + return sensor; + } +} diff --git a/plugins/guacamole-oculus/src/gua/OculusRift.cpp b/plugins/guacamole-oculus/src/gua/OculusRift.cpp index 5880c308c..ccdc450e9 100644 --- a/plugins/guacamole-oculus/src/gua/OculusRift.cpp +++ b/plugins/guacamole-oculus/src/gua/OculusRift.cpp @@ -1,197 +1,198 @@ -/****************************************************************************** - * guacamole - delicious VR * - * * - * Copyright: (c) 2011-2013 Bauhaus-Universität Weimar * - * Contact: felix.lauer@uni-weimar.de / simon.schneegans@uni-weimar.de * - * * - * This program is free software: you can redistribute it and/or modify it * - * under the terms of the GNU General Public License as published by the Free * - * Software Foundation, either version 3 of the License, or (at your option) * - * any later version. * - * * - * This program is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * - * for more details. * - * * - * You should have received a copy of the GNU General Public License along * - * with this program. If not, see . * - * * - ******************************************************************************/ - -// class header -#include -#include - -// external headers -#include - -namespace gua { - -void OculusRift::init() { - OVR::System::Init(OVR::Log::ConfigureDefaultLog(OVR::LogMask_All)); -} - -OculusRift::OculusRift(std::string const& display): - Window(), - distortion_(4), - sensor_fusion_(nullptr), - sensor_(nullptr), - device_(nullptr) { - - config.set_size(math::vec2ui(1280, 800)); - config.set_title("guacamole"); - config.set_display_name(display); - config.set_stereo_mode(StereoMode::SIDE_BY_SIDE); - config.set_left_resolution(math::vec2ui(1280/2, 800)); - config.set_left_position(math::vec2ui(0, 0)); - config.set_right_resolution(math::vec2ui(1280/2, 800)); - config.set_right_position(math::vec2ui(1280/2, 0)); - - //Every time an Oculus Rift Window is created, it is checked if an additional Oculus is connected - device_ = OculusDeviceManager::getInstance().getNextAvailableDevice(); - - if (!device_) { - WARNING("Failed to initialize an Oculus Rift device! Are enough Oculus Rifts attached?"); - return; - } - - OVR::HMDInfo hmd; - if (device_->GetDeviceInfo(&hmd)) { - MESSAGE("Oculus EyeDistance: %f", hmd.InterpupillaryDistance); - - distortion_[0] = hmd.DistortionK[0]; - distortion_[1] = hmd.DistortionK[1]; - distortion_[2] = hmd.DistortionK[2]; - distortion_[3] = hmd.DistortionK[3]; - } - - sensor_ = device_->GetSensor(); - sensor_fusion_ = new OVR::SensorFusion(); - - if (sensor_) { - sensor_fusion_->AttachToSensor(sensor_); - } else { - WARNING("Failed to get Oculus gyroskop data"); - } -} - -//////////////////////////////////////////////////////////////////////////////// - -OculusRift::~OculusRift() { - if (sensor_fusion_) - delete sensor_fusion_; -} - -//////////////////////////////////////////////////////////////////////////////// - -void OculusRift::create_shader() { - fullscreen_shader_.create_from_sources(R"( - #version 420 - #extension GL_NV_bindless_texture : require - - layout(location=0) in vec3 in_position; - - out vec2 tex_coord; - - void main() { - tex_coord = in_position.xy*0.5 + 0.5; - gl_Position = vec4(in_position, 1.0); - } - )", R"( - #version 420 - #extension GL_NV_bindless_texture : require - #extension GL_NV_gpu_shader5 : enable - - in vec2 tex_coord; - - uniform uvec2 sampler; - - // oculus parameters - uniform vec2 lens_center; - uniform vec2 scale; - uniform vec4 hmd_warp_param; - - layout (location = 0) out vec3 out_color; - - sampler2D get_tex(uvec2 handle) { - return sampler2D(uint64_t(handle.x) + uint64_t(handle.y) * 4294967295); - } - - vec2 hmd_warp(vec2 in_texcoord) { - vec2 theta = (in_texcoord - lens_center) * 2.0; // Scales to [-1, 1] - float rSq = theta.x * theta.x + theta.y * theta.y; - vec2 rvector = theta * (hmd_warp_param.x+hmd_warp_param.y*rSq - +hmd_warp_param.z*rSq*rSq - +hmd_warp_param.w*rSq*rSq*rSq); - return lens_center + scale * rvector; - } - - vec3 get_color() { - - vec2 tc = hmd_warp(tex_coord); - - if (tc.x < 0.0 || tc.y < 0.0 || tc.x > 1.0 || tc.y > 1.0 ) - return vec3(0); - - return vec3(texture2D( get_tex(sampler), tc).rgb); - } - - void main() { - out_color = get_color(); - } - )"); -} - -//////////////////////////////////////////////////////////////////////////////// - -void OculusRift::display(std::shared_ptr const& left_texture, - std::shared_ptr const& right_texture) { - - display(left_texture, config.get_left_resolution(), config.get_left_position(), true); - display(right_texture, config.get_right_resolution(), config.get_right_position(), false); -} - -//////////////////////////////////////////////////////////////////////////////// - -math::mat4 const OculusRift::get_transform() const { - if (sensor_fusion_) { - OVR::Quatf orient = sensor_fusion_->GetPredictedOrientation(); - OVR::Matrix4f ovr_mat(orient.Inverted()); - - return math::mat4(ovr_mat.M[0][0], ovr_mat.M[0][1], ovr_mat.M[0][2], ovr_mat.M[0][3], - ovr_mat.M[1][0], ovr_mat.M[1][1], ovr_mat.M[1][2], ovr_mat.M[1][3], - ovr_mat.M[2][0], ovr_mat.M[2][1], ovr_mat.M[2][2], ovr_mat.M[2][3], - ovr_mat.M[3][0], ovr_mat.M[3][1], ovr_mat.M[3][2], ovr_mat.M[3][3]); - } - - return math::mat4::identity(); -} - -//////////////////////////////////////////////////////////////////////////////// - -void OculusRift::display(std::shared_ptr const& texture, - math::vec2ui const& size, - math::vec2ui const& position, - bool left) { - - - fullscreen_shader_.use(*get_context()); - fullscreen_shader_.set_uniform(*get_context(), texture, "sampler"); - - if (left) fullscreen_shader_.set_uniform(*get_context(), math::vec2(0.6f, 0.5f), "lens_center"); - else fullscreen_shader_.set_uniform(*get_context(), math::vec2(0.4f, 0.5f), "lens_center"); - - fullscreen_shader_.set_uniform(*get_context(), math::vec2(0.4f, 0.4f), "scale"); - fullscreen_shader_.set_uniform(*get_context(), distortion_, "hmd_warp_param"); - - get_context()->render_context->set_viewport(scm::gl::viewport(position, size)); - get_context()->render_context->set_depth_stencil_state(depth_stencil_state_); - - fullscreen_quad_->draw(get_context()->render_context); - - get_context()->render_context->reset_state_objects(); - fullscreen_shader_.unuse(*get_context()); -} - -} +/****************************************************************************** + * guacamole - delicious VR * + * * + * Copyright: (c) 2011-2013 Bauhaus-Universität Weimar * + * Contact: felix.lauer@uni-weimar.de / simon.schneegans@uni-weimar.de * + * * + * This program is free software: you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the Free * + * Software Foundation, either version 3 of the License, or (at your option) * + * any later version. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * + * for more details. * + * * + * You should have received a copy of the GNU General Public License along * + * with this program. If not, see . * + * * + ******************************************************************************/ + +// class header +#include +#include + +// external headers +#include +#include + +namespace gua { + +void OculusRift::init() { + OVR::System::Init(OVR::Log::ConfigureDefaultLog(OVR::LogMask_All)); +} + +OculusRift::OculusRift(std::string const& display): + Window(), + distortion_(4), + sensor_fusion_(nullptr), + sensor_(nullptr), + device_(nullptr) { + + config.set_size(math::vec2ui(1280, 800)); + config.set_title("guacamole"); + config.set_display_name(display); + config.set_stereo_mode(StereoMode::SIDE_BY_SIDE); + config.set_left_resolution(math::vec2ui(1280/2, 800)); + config.set_left_position(math::vec2ui(0, 0)); + config.set_right_resolution(math::vec2ui(1280/2, 800)); + config.set_right_position(math::vec2ui(1280/2, 0)); + + //Every time an Oculus Rift Window is created, it is associated to the next available OR-device + device_ = OculusDeviceManager::getInstance().getNextAvailableDevice(); + + if (!device_) { + WARNING("Failed to initialize an Oculus Rift device! Are enough Oculus Rifts attached?"); + return; + } + + OVR::HMDInfo hmd; + if (device_->GetDeviceInfo(&hmd)) { + MESSAGE("Oculus EyeDistance: %f", hmd.InterpupillaryDistance); + + distortion_[0] = hmd.DistortionK[0]; + distortion_[1] = hmd.DistortionK[1]; + distortion_[2] = hmd.DistortionK[2]; + distortion_[3] = hmd.DistortionK[3]; + } + + sensor_ = OculusDeviceManager::getInstance().getNextSensor(); + sensor_fusion_ = new OVR::SensorFusion(); + + if (sensor_) { + sensor_fusion_->AttachToSensor(sensor_); + } else { + WARNING("Failed to get Oculus gyroskop data"); + } +} + +//////////////////////////////////////////////////////////////////////////////// + +OculusRift::~OculusRift() { + if (sensor_fusion_) + delete sensor_fusion_; +} + +//////////////////////////////////////////////////////////////////////////////// + +void OculusRift::create_shader() { + fullscreen_shader_.create_from_sources(R"( + #version 420 + #extension GL_NV_bindless_texture : require + + layout(location=0) in vec3 in_position; + + out vec2 tex_coord; + + void main() { + tex_coord = in_position.xy*0.5 + 0.5; + gl_Position = vec4(in_position, 1.0); + } + )", R"( + #version 420 + #extension GL_NV_bindless_texture : require + #extension GL_NV_gpu_shader5 : enable + + in vec2 tex_coord; + + uniform uvec2 sampler; + + // oculus parameters + uniform vec2 lens_center; + uniform vec2 scale; + uniform vec4 hmd_warp_param; + + layout (location = 0) out vec3 out_color; + + sampler2D get_tex(uvec2 handle) { + return sampler2D(uint64_t(handle.x) + uint64_t(handle.y) * 4294967295); + } + + vec2 hmd_warp(vec2 in_texcoord) { + vec2 theta = (in_texcoord - lens_center) * 2.0; // Scales to [-1, 1] + float rSq = theta.x * theta.x + theta.y * theta.y; + vec2 rvector = theta * (hmd_warp_param.x+hmd_warp_param.y*rSq + +hmd_warp_param.z*rSq*rSq + +hmd_warp_param.w*rSq*rSq*rSq); + return lens_center + scale * rvector; + } + + vec3 get_color() { + + vec2 tc = hmd_warp(tex_coord); + + if (tc.x < 0.0 || tc.y < 0.0 || tc.x > 1.0 || tc.y > 1.0 ) + return vec3(0); + + return vec3(texture2D( get_tex(sampler), tc).rgb); + } + + void main() { + out_color = get_color(); + } + )"); +} + +//////////////////////////////////////////////////////////////////////////////// + +void OculusRift::display(std::shared_ptr const& left_texture, + std::shared_ptr const& right_texture) { + + display(left_texture, config.get_left_resolution(), config.get_left_position(), true); + display(right_texture, config.get_right_resolution(), config.get_right_position(), false); +} + +//////////////////////////////////////////////////////////////////////////////// + +math::mat4 const OculusRift::get_transform() const { + if (sensor_fusion_) { + OVR::Quatf orient = sensor_fusion_->GetPredictedOrientation(); + OVR::Matrix4f ovr_mat(orient.Inverted()); + + return math::mat4(ovr_mat.M[0][0], ovr_mat.M[0][1], ovr_mat.M[0][2], ovr_mat.M[0][3], + ovr_mat.M[1][0], ovr_mat.M[1][1], ovr_mat.M[1][2], ovr_mat.M[1][3], + ovr_mat.M[2][0], ovr_mat.M[2][1], ovr_mat.M[2][2], ovr_mat.M[2][3], + ovr_mat.M[3][0], ovr_mat.M[3][1], ovr_mat.M[3][2], ovr_mat.M[3][3]); + } + + return math::mat4::identity(); +} + +//////////////////////////////////////////////////////////////////////////////// + +void OculusRift::display(std::shared_ptr const& texture, + math::vec2ui const& size, + math::vec2ui const& position, + bool left) { + + + fullscreen_shader_.use(*get_context()); + fullscreen_shader_.set_uniform(*get_context(), texture, "sampler"); + + if (left) fullscreen_shader_.set_uniform(*get_context(), math::vec2(0.6f, 0.5f), "lens_center"); + else fullscreen_shader_.set_uniform(*get_context(), math::vec2(0.4f, 0.5f), "lens_center"); + + fullscreen_shader_.set_uniform(*get_context(), math::vec2(0.4f, 0.4f), "scale"); + fullscreen_shader_.set_uniform(*get_context(), distortion_, "hmd_warp_param"); + + get_context()->render_context->set_viewport(scm::gl::viewport(position, size)); + get_context()->render_context->set_depth_stencil_state(depth_stencil_state_); + + fullscreen_quad_->draw(get_context()->render_context); + + get_context()->render_context->reset_state_objects(); + fullscreen_shader_.unuse(*get_context()); +} + +} \ No newline at end of file From c220357a52491d15f0afa4793908bf5813605095 Mon Sep 17 00:00:00 2001 From: thelaui Date: Thu, 12 Dec 2013 11:56:00 +0100 Subject: [PATCH 080/146] implemented flags to flip x and y coordinates on TexturedQuadNode --- include/gua/scenegraph/TexturedQuadNode.hpp | 2 ++ resources/materials/gua_textured_quad.gmd | 2 ++ resources/materials/gua_textured_quad.gsd | 18 ++++++++++-------- src/gua/renderer/GBufferPass.cpp | 14 ++++++++++++-- 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/include/gua/scenegraph/TexturedQuadNode.hpp b/include/gua/scenegraph/TexturedQuadNode.hpp index 9b5fb5748..f5617e97f 100644 --- a/include/gua/scenegraph/TexturedQuadNode.hpp +++ b/include/gua/scenegraph/TexturedQuadNode.hpp @@ -39,6 +39,8 @@ class GUA_DLL TexturedQuadNode : public Node { GUA_ADD_PROPERTY(std::string, texture, ""); GUA_ADD_PROPERTY(math::vec2, size, math::vec2(1.f, 1.f)); GUA_ADD_PROPERTY(bool, is_stereo_texture, false); + GUA_ADD_PROPERTY(bool, flip_x, false); + GUA_ADD_PROPERTY(bool, flip_y, false); }; Configuration data; diff --git a/resources/materials/gua_textured_quad.gmd b/resources/materials/gua_textured_quad.gmd index b63f91047..23e1955d3 100644 --- a/resources/materials/gua_textured_quad.gmd +++ b/resources/materials/gua_textured_quad.gmd @@ -3,6 +3,8 @@ "shading_model" : "gua_textured_quad", "uniforms" : { + "flip_x" : "0", + "flip_y" : "0", "texture" : "gua_default_texture" } } diff --git a/resources/materials/gua_textured_quad.gsd b/resources/materials/gua_textured_quad.gsd index 97f89673a..fbb4cf865 100644 --- a/resources/materials/gua_textured_quad.gsd +++ b/resources/materials/gua_textured_quad.gsd @@ -1,37 +1,39 @@ { - "final_shading_stage" : + "final_shading_stage" : { "body" : "gua_color = color;\n", "functions" : "", "outputs" : null, "uniforms" : null }, - "gbuffer_fragment_stage" : + "gbuffer_fragment_stage" : { - "body" : "gua_normal = varying_normal;\ncolor = texture2D(texture, varying_texcoord).rgb;", + "body" : "gua_normal = varying_normal;\nvec2 tex_coords = varying_texcoord;\n\nif (flip_x) tex_coords.x = 1.0 - varying_texcoord.x;\nif (flip_y) tex_coords.y = 1.0 - varying_texcoord.y;\n\ncolor = texture2D(texture, tex_coords).rgb;", "functions" : "", - "outputs" : + "outputs" : { "color" : "vec3" }, - "uniforms" : + "uniforms" : { + "flip_x" : "bool", + "flip_y" : "bool", "texture" : "sampler2D" } }, - "gbuffer_vertex_stage" : + "gbuffer_vertex_stage" : { "body" : "gua_position = gua_world_position;\nvarying_normal = gua_world_normal;\nvarying_texcoord = gua_texcoords;", "functions" : "", - "outputs" : + "outputs" : { "varying_normal" : "vec3", "varying_texcoord" : "vec2" }, "uniforms" : null }, - "lbuffer_stage" : + "lbuffer_stage" : { "body" : "", "functions" : "", diff --git a/src/gua/renderer/GBufferPass.cpp b/src/gua/renderer/GBufferPass.cpp index a2e247d8b..942483356 100644 --- a/src/gua/renderer/GBufferPass.cpp +++ b/src/gua/renderer/GBufferPass.cpp @@ -201,10 +201,20 @@ void GBufferPass::rendering(SerializedScene const& scene, if (TextureDatabase::instance()->is_supported(texture_name)) { auto texture = TextureDatabase::instance()->lookup(texture_name); - auto mapped( + auto mapped_texture( mesh_shader_->get_uniform_mapping()->get_mapping("gua_textured_quad", "texture")); - mesh_shader_->set_uniform(ctx, texture, mapped.first, mapped.second); + mesh_shader_->set_uniform(ctx, texture, mapped_texture.first, mapped_texture.second); + + auto mapped_flip_x( + mesh_shader_->get_uniform_mapping()->get_mapping("gua_textured_quad", "flip_x")); + + mesh_shader_->set_uniform(ctx, node.data.get_flip_x(), mapped_flip_x.first, mapped_flip_x.second); + + auto mapped_flip_y( + mesh_shader_->get_uniform_mapping()->get_mapping("gua_textured_quad", "flip_y")); + + mesh_shader_->set_uniform(ctx, node.data.get_flip_y(), mapped_flip_y.first, mapped_flip_y.second); if (material && geometry) { mesh_shader_->set_uniform( From a6fea27a6b72c767199b60cfb6dff367206fdbd1 Mon Sep 17 00:00:00 2001 From: thelaui Date: Thu, 12 Dec 2013 17:32:16 +0100 Subject: [PATCH 081/146] removed wrong if --- src/gua/renderer/GBufferPass.cpp | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/gua/renderer/GBufferPass.cpp b/src/gua/renderer/GBufferPass.cpp index 942483356..4ca4c5cff 100644 --- a/src/gua/renderer/GBufferPass.cpp +++ b/src/gua/renderer/GBufferPass.cpp @@ -132,19 +132,17 @@ void GBufferPass::rendering(SerializedScene const& scene, ctx.render_context->set_depth_stencil_state(depth_stencil_state_); - if (!scene.meshnodes_.empty() || scene.textured_quads_.empty() || (pipeline_->config.enable_bbox_display() && !scene.bounding_boxes_.empty())) { - mesh_shader_->set_material_uniforms( - scene.materials_, ShadingModel::GBUFFER_VERTEX_STAGE, ctx); - mesh_shader_->set_material_uniforms( - scene.materials_, ShadingModel::GBUFFER_FRAGMENT_STAGE, ctx); - - Pass::bind_inputs(*mesh_shader_, eye, ctx); - Pass::set_camera_matrices(*mesh_shader_, - camera, - pipeline_->get_current_scene(eye), - eye, - ctx); - } + mesh_shader_->set_material_uniforms( + scene.materials_, ShadingModel::GBUFFER_VERTEX_STAGE, ctx); + mesh_shader_->set_material_uniforms( + scene.materials_, ShadingModel::GBUFFER_FRAGMENT_STAGE, ctx); + + Pass::bind_inputs(*mesh_shader_, eye, ctx); + Pass::set_camera_matrices(*mesh_shader_, + camera, + pipeline_->get_current_scene(eye), + eye, + ctx); if (!scene.meshnodes_.empty()) { From 7b9362dab2e77144aed0cfafc19196600a290665 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Mon, 16 Dec 2013 16:16:05 +0100 Subject: [PATCH 082/146] added directional light --- include/gua/renderer/LightingPass.hpp | 4 +- include/gua/renderer/SerializedScene.hpp | 6 ++ include/gua/renderer/Serializer.hpp | 9 ++ include/gua/scenegraph/NodeVisitor.hpp | 10 +++ include/gua/scenegraph/SunLightNode.hpp | 80 +++++++++++++++++ .../uber_shaders/lighting/lighting.frag | 29 +++++- .../uber_shaders/lighting/lighting.vert | 20 ++++- .../shaders/uber_shaders/postfx/godrays.vert | 32 +++++-- src/gua/renderer/LightingPass.cpp | 90 ++++++++++++++++--- src/gua/renderer/PostFXPass.cpp | 33 ++++++- src/gua/renderer/Serializer.cpp | 19 +++- src/gua/scenegraph/SunLightNode.cpp | 53 +++++++++++ 12 files changed, 358 insertions(+), 27 deletions(-) create mode 100644 include/gua/scenegraph/SunLightNode.hpp create mode 100644 src/gua/scenegraph/SunLightNode.cpp diff --git a/include/gua/renderer/LightingPass.hpp b/include/gua/renderer/LightingPass.hpp index 395fc1dfb..020d4fabe 100644 --- a/include/gua/renderer/LightingPass.hpp +++ b/include/gua/renderer/LightingPass.hpp @@ -83,6 +83,7 @@ class LightingPass : public GeometryPass { LightingUberShader* shader_; std::shared_ptr light_sphere_; std::shared_ptr light_cone_; + scm::gl::quad_geometry_ptr fullscreen_quad_; Serializer* serializer_; @@ -94,7 +95,8 @@ class LightingPass : public GeometryPass { math::mat4 shadow_map_projection_view_matrix_; scm::gl::depth_stencil_state_ptr depth_stencil_state_; - scm::gl::rasterizer_state_ptr rasterizer_state_; + scm::gl::rasterizer_state_ptr rasterizer_state_front_; + scm::gl::rasterizer_state_ptr rasterizer_state_back_; scm::gl::blend_state_ptr blend_state_; }; diff --git a/include/gua/renderer/SerializedScene.hpp b/include/gua/renderer/SerializedScene.hpp index 6d5c8830f..da0e8ffbe 100644 --- a/include/gua/renderer/SerializedScene.hpp +++ b/include/gua/renderer/SerializedScene.hpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -75,6 +76,11 @@ struct SerializedScene { */ std::vector > spot_lights_; + /** + * All sun light nodes. + */ + std::vector > sun_lights_; + /** * The frustum. */ diff --git a/include/gua/renderer/Serializer.hpp b/include/gua/renderer/Serializer.hpp index fe03b2f2f..27e3caf73 100644 --- a/include/gua/renderer/Serializer.hpp +++ b/include/gua/renderer/Serializer.hpp @@ -111,6 +111,15 @@ class Serializer : public NodeVisitor { */ /* virtual */ void visit(SpotLightNode* spot); + /** + * Visits a SunLightNode + * + * This function provides the interface to visit a SunLightNode + * + * \param spot Pointer to SunLightNode + */ + /* virtual */ void visit(SunLightNode* sun); + /** * Visits a RayNode * diff --git a/include/gua/scenegraph/NodeVisitor.hpp b/include/gua/scenegraph/NodeVisitor.hpp index 9f9f2112e..a439781ff 100644 --- a/include/gua/scenegraph/NodeVisitor.hpp +++ b/include/gua/scenegraph/NodeVisitor.hpp @@ -35,6 +35,7 @@ class VolumeNode; class PointLightNode; class ScreenNode; class SpotLightNode; +class SunLightNode; class RayNode; class TexturedQuadNode; @@ -120,6 +121,15 @@ class NodeVisitor { */ virtual void visit(SpotLightNode* node) { visit(reinterpret_cast(node)); } + /** + * Visits a SunLightNode + * + * This function provides the interface to visit a SunLightNode + * + * \param cam Pointer to SunLightNode + */ + virtual void visit(SunLightNode* node) { visit(reinterpret_cast(node)); } + /** * Visits a SpotLightNode * diff --git a/include/gua/scenegraph/SunLightNode.hpp b/include/gua/scenegraph/SunLightNode.hpp new file mode 100644 index 000000000..cc6f876d7 --- /dev/null +++ b/include/gua/scenegraph/SunLightNode.hpp @@ -0,0 +1,80 @@ +/****************************************************************************** + * guacamole - delicious VR * + * * + * Copyright: (c) 2011-2013 Bauhaus-Universität Weimar * + * Contact: felix.lauer@uni-weimar.de / simon.schneegans@uni-weimar.de * + * * + * This program is free software: you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the Free * + * Software Foundation, either version 3 of the License, or (at your option) * + * any later version. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * + * for more details. * + * * + * You should have received a copy of the GNU General Public License along * + * with this program. If not, see . * + * * + ******************************************************************************/ + +#ifndef GUA_SUN_LIGHT_NODE_HPP +#define GUA_SUN_LIGHT_NODE_HPP + +#include +#include + +#include +#include + +#include + +/** + * This class is used to represent light in the SceneGraph. + * + */ + +namespace gua { + +class GUA_DLL SunLightNode : public Node { + public: + + struct Configuration { + GUA_ADD_PROPERTY(utils::Color3f, color, utils::Color3f(1.f, 1.f, 1.f)); + GUA_ADD_PROPERTY(bool, enable_shadows, false); + GUA_ADD_PROPERTY(bool, enable_godrays, false); + GUA_ADD_PROPERTY(bool, enable_diffuse_shading, true); + GUA_ADD_PROPERTY(bool, enable_specular_shading, true); + GUA_ADD_PROPERTY(unsigned, shadow_map_size, 512); + GUA_ADD_PROPERTY(float, shadow_offset, 0.001f); + }; + + Configuration data; + + SunLightNode() {} + + SunLightNode(std::string const& name, + Configuration const& configuration = Configuration(), + math::mat4 const& transform = math::mat4::identity()); + + /** + * Accepts a visitor and calls concrete visit method + * + * This method implements the visitor pattern for Nodes + * + */ + /* virtual */ void accept(NodeVisitor&); + + void update_bounding_box() const; + + private: + /** + * + */ + std::shared_ptr copy() const; +}; + +} + +#endif // GUA_SUN_LIGHT_NODE_HPP diff --git a/resources/shaders/uber_shaders/lighting/lighting.frag b/resources/shaders/uber_shaders/lighting/lighting.frag index 79d64720d..e3e9c1bc2 100644 --- a/resources/shaders/uber_shaders/lighting/lighting.frag +++ b/resources/shaders/uber_shaders/lighting/lighting.frag @@ -123,10 +123,10 @@ float gua_get_shadow() { return shadow; } -// base lighting calculations for point lights --------------------------------- subroutine void CalculateLightType(); subroutine uniform CalculateLightType compute_light; +// base lighting calculations for point lights --------------------------------- subroutine(CalculateLightType) void gua_calculate_point_light() { vec3 light_position = gua_lightinfo1; @@ -189,6 +189,33 @@ void gua_calculate_spot_light() { gua_light_intensity = radial_attenuation * length_attenuation * shadow; } +// base lighting calculations for point lights --------------------------------- +subroutine(CalculateLightType) +void gua_calculate_sun_light() { + vec3 light_direction = gua_lightinfo1; + vec3 gbuffer_normal = texture2D(gua_get_float_sampler(gua_float_gbuffer_in_1[0]), + gua_get_quad_coords()).xyz; + + gua_light_direction = light_direction; + gua_light_distance = 0.0; + + if (dot(gbuffer_normal, gua_light_direction) < 0) + discard; + + float shadow = 0.25; + float depth = gua_get_depth(); + + if (depth < 0.94) { + shadow = 1.0; + } else if (depth < 0.98) { + shadow = 0.75; + } else if (depth < 0.99) { + shadow = 0.5; + } + + gua_light_intensity = 1.0 * shadow; +} + // material specific methods @material_methods diff --git a/resources/shaders/uber_shaders/lighting/lighting.vert b/resources/shaders/uber_shaders/lighting/lighting.vert index 80bf3eaa3..cddb19470 100644 --- a/resources/shaders/uber_shaders/lighting/lighting.vert +++ b/resources/shaders/uber_shaders/lighting/lighting.vert @@ -39,10 +39,10 @@ out mat4 gua_lightinfo4; // BASE LIGHTING CALCULATIONS -------------------------------------------------- -// base lighting calculations for point lights subroutine void CalculateLightType(); subroutine uniform CalculateLightType compute_light; +// base lighting calculations for point lights subroutine( CalculateLightType ) void gua_calculate_point_light() { @@ -52,6 +52,9 @@ void gua_calculate_point_light() { gua_lightinfo1 = light_position; gua_lightinfo2 = vec3(0.0, 0.0, 0.0); gua_lightinfo3 = light_radius; + + vec3 position = (gua_model_matrix * vec4(gua_in_position, 1.0)).xyz; + gl_Position = gua_projection_matrix * gua_view_matrix * vec4(position, 1.0); } // base lighting calculations for spot lights @@ -72,11 +75,22 @@ void gua_calculate_spot_light() { gua_lightinfo2 = beam_direction; gua_lightinfo3 = half_beam_angle; gua_lightinfo4 = shadow_map_coords_mat; + + vec3 position = (gua_model_matrix * vec4(gua_in_position, 1.0)).xyz; + gl_Position = gua_projection_matrix * gua_view_matrix * vec4(position, 1.0); +} + +// base lighting calculations for sun lights +subroutine( CalculateLightType ) +void gua_calculate_sun_light() { + + vec3 light_direction = normalize((gua_model_matrix * vec4(0.0, 0.0, 1.0, 0.0)).xyz); + gua_lightinfo1 = light_direction; + + gl_Position = vec4(gua_in_position, 1.0); } // main ------------------------------------------------------------------------ void main() { compute_light(); - vec3 position = (gua_model_matrix * vec4(gua_in_position, 1.0)).xyz; - gl_Position = gua_projection_matrix * gua_view_matrix * vec4(position, 1.0); } diff --git a/resources/shaders/uber_shaders/postfx/godrays.vert b/resources/shaders/uber_shaders/postfx/godrays.vert index e4c7317e4..e6273100c 100644 --- a/resources/shaders/uber_shaders/postfx/godrays.vert +++ b/resources/shaders/uber_shaders/postfx/godrays.vert @@ -27,16 +27,38 @@ layout(location=2) in vec2 gua_in_texcoord; uniform mat4 gua_projection_matrix; uniform mat4 gua_view_matrix; -uniform vec3 gua_light_position; +uniform vec3 gua_light_position_direction; // output out vec3 gua_light_position_screen_space; out vec2 gua_quad_coords; +subroutine void CalculatePositionType(); +subroutine uniform CalculatePositionType compute_position; + +subroutine(CalculatePositionType) +void gua_calculate_by_direction() { + vec4 tmp = gua_view_matrix * vec4(gua_light_position_direction, 0.0); + + if (tmp.z > 0) { + // hide sun on wrong side + gua_light_position_screen_space = vec3(-10); + } else { + tmp = gua_projection_matrix * tmp; + gua_light_position_screen_space = (tmp/tmp.w).xyz; + gua_light_position_screen_space = gua_light_position_screen_space/gua_light_position_screen_space.z; + } +} + +subroutine(CalculatePositionType) +void gua_calculate_by_position() { + vec4 tmp = gua_projection_matrix * gua_view_matrix * vec4(gua_light_position_direction, 1.0); + gua_light_position_screen_space = (tmp/tmp.w).xyz; +} + // body void main() { - gua_quad_coords = gua_in_texcoord; - vec4 tmp = gua_projection_matrix * gua_view_matrix * vec4(gua_light_position, 1.0); - gua_light_position_screen_space = (tmp/tmp.w).xyz; - gl_Position = vec4(gua_in_position, 1.0); + gua_quad_coords = gua_in_texcoord; + compute_position(); + gl_Position = vec4(gua_in_position, 1.0); } diff --git a/src/gua/renderer/LightingPass.cpp b/src/gua/renderer/LightingPass.cpp index 1a2c3b56d..ce0ea3133 100644 --- a/src/gua/renderer/LightingPass.cpp +++ b/src/gua/renderer/LightingPass.cpp @@ -107,10 +107,14 @@ void LightingPass::rendering(SerializedScene const& scene, depth_stencil_state_ = ctx.render_device->create_depth_stencil_state(false, false); - if (!rasterizer_state_) - rasterizer_state_ = ctx.render_device + if (!rasterizer_state_front_) + rasterizer_state_front_ = ctx.render_device ->create_rasterizer_state(scm::gl::FILL_SOLID, scm::gl::CULL_FRONT); + if (!rasterizer_state_back_) + rasterizer_state_back_ = ctx.render_device + ->create_rasterizer_state(scm::gl::FILL_SOLID, scm::gl::CULL_BACK); + if (!blend_state_) blend_state_ = ctx.render_device->create_blend_state(true, scm::gl::FUNC_ONE, @@ -118,27 +122,87 @@ void LightingPass::rendering(SerializedScene const& scene, scm::gl::FUNC_ONE, scm::gl::FUNC_ONE); + if (!fullscreen_quad_) + fullscreen_quad_ = scm::gl::quad_geometry_ptr(new scm::gl::quad_geometry( + ctx.render_device, math::vec2(-1.f, -1.f), math::vec2(1.f, 1.f))); + ctx.render_context->set_depth_stencil_state(depth_stencil_state_); - ctx.render_context->set_rasterizer_state(rasterizer_state_); + ctx.render_context->set_rasterizer_state(rasterizer_state_back_); ctx.render_context->set_blend_state(blend_state_); shader_->set_material_uniforms( scene.materials_, ShadingModel::LIGHTING_STAGE, ctx); + shader_->use(ctx); + + Pass::bind_inputs(*shader_, eye, ctx); + Pass::set_camera_matrices( + *shader_, camera, pipeline_->get_current_scene(eye), eye, ctx); + + + // -------------------------- sun lights ----------------------------------- shader_->set_subroutine(ctx, scm::gl::STAGE_VERTEX_SHADER, "compute_light", - "gua_calculate_point_light"); + "gua_calculate_sun_light"); shader_->set_subroutine(ctx, scm::gl::STAGE_FRAGMENT_SHADER, "compute_light", + "gua_calculate_sun_light"); + + + for (auto const& light : scene.sun_lights_) { + + if (light.data.get_enable_shadows()) { + shader_->unuse(ctx); + target->unbind(ctx); + ctx.render_context->reset_state_objects(); + + render_shadow_map(ctx, camera, light.transform, light.data.get_shadow_map_size()); + + shader_->use(ctx); + target->bind(ctx); + + ctx.render_context->set_viewport(scm::gl::viewport( + math::vec2(0, 0), + math::vec2(float(target->width()), float(target->height())))); + + ctx.render_context->set_depth_stencil_state(depth_stencil_state_); + ctx.render_context->set_rasterizer_state(rasterizer_state_back_); + ctx.render_context->set_blend_state(blend_state_); + + shader_->set_uniform(ctx, shadow_map_->get_depth_buffer(), "gua_light_shadow_map"); + + float shadow_map_portion(1.f * light.data.get_shadow_map_size() / shadow_map_->width()); + shader_->set_uniform(ctx, shadow_map_portion, "gua_light_shadow_map_portion"); + shader_->set_uniform(ctx, shadow_map_projection_view_matrix_, "gua_light_shadow_map_projection_view_matrix"); + } + + shader_->set_uniform( + ctx, light.data.get_enable_diffuse_shading(), "gua_light_diffuse_enable"); + shader_->set_uniform(ctx, + light.data.get_enable_specular_shading(), + "gua_light_specular_enable"); + shader_->set_uniform(ctx, light.transform, "gua_model_matrix"); + shader_->set_uniform(ctx, light.data.get_color().vec3(), "gua_light_color"); + shader_->set_uniform(ctx, false, "gua_light_casts_shadow"); + fullscreen_quad_->draw(ctx.render_context); + } + + // ------------------------- point lights ---------------------------------- + + ctx.render_context->set_rasterizer_state(rasterizer_state_front_); + + shader_->set_subroutine(ctx, + scm::gl::STAGE_VERTEX_SHADER, + "compute_light", "gua_calculate_point_light"); - shader_->use(ctx); + shader_->set_subroutine(ctx, + scm::gl::STAGE_FRAGMENT_SHADER, + "compute_light", + "gua_calculate_point_light"); - Pass::bind_inputs(*shader_, eye, ctx); - Pass::set_camera_matrices( - *shader_, camera, pipeline_->get_current_scene(eye), eye, ctx); for (auto const& light : scene.point_lights_) { shader_->set_uniform( @@ -153,6 +217,8 @@ void LightingPass::rendering(SerializedScene const& scene, light_sphere_->draw(ctx); } + + // -------------------------- spot lights ---------------------------------- shader_->set_subroutine(ctx, scm::gl::STAGE_VERTEX_SHADER, "compute_light", @@ -179,7 +245,7 @@ void LightingPass::rendering(SerializedScene const& scene, math::vec2(float(target->width()), float(target->height())))); ctx.render_context->set_depth_stencil_state(depth_stencil_state_); - ctx.render_context->set_rasterizer_state(rasterizer_state_); + ctx.render_context->set_rasterizer_state(rasterizer_state_front_); ctx.render_context->set_blend_state(blend_state_); shader_->set_uniform( @@ -240,11 +306,7 @@ void LightingPass::render_shadow_map(RenderContext const & ctx, gbuffer_desc.push_back(std::make_pair(BufferComponent::DEPTH_16, state)); shadow_map_ = new GBuffer(gbuffer_desc, map_size, map_size); #else - shadow_map_ = new GBuffer({ - { BufferComponent::DEPTH_16, state } - }, - map_size, - map_size); + shadow_map_ = new GBuffer({{ BufferComponent::DEPTH_16, state }}, map_size, map_size); #endif shadow_map_->create(ctx); } diff --git a/src/gua/renderer/PostFXPass.cpp b/src/gua/renderer/PostFXPass.cpp index b967d424f..871e3df6b 100644 --- a/src/gua/renderer/PostFXPass.cpp +++ b/src/gua/renderer/PostFXPass.cpp @@ -444,6 +444,15 @@ bool PostFXPass::render_godrays(Camera const& camera, } } + if (!any_godrays) { + for (auto const& light: scene.sun_lights_) { + if (light.data.get_enable_godrays()) { + any_godrays = true; + break; + } + } + } + postfx_shaders_[0]->set_uniform(ctx, any_godrays, "gua_enable_godrays"); if (any_godrays) { @@ -491,12 +500,17 @@ bool PostFXPass::render_godrays(Camera const& camera, ctx.render_context->reset_state_objects(); }; + god_ray_shader_->set_subroutine(ctx, + scm::gl::STAGE_VERTEX_SHADER, + "compute_position", + "gua_calculate_by_position"); + for (auto const& light: scene.point_lights_) { if (light.data.get_enable_godrays()) { god_ray_shader_->set_uniform(ctx, light.data.get_color().vec3(), "gua_light_color"); god_ray_shader_->set_uniform(ctx, math::vec3(light.transform.column(3)[0], light.transform.column(3)[1], - light.transform.column(3)[2]), "gua_light_position"); + light.transform.column(3)[2]), "gua_light_position_direction"); render(); } } @@ -506,7 +520,22 @@ bool PostFXPass::render_godrays(Camera const& camera, god_ray_shader_->set_uniform(ctx, light.data.get_color().vec3(), "gua_light_color"); god_ray_shader_->set_uniform(ctx, math::vec3(light.transform.column(3)[0], light.transform.column(3)[1], - light.transform.column(3)[2]), "gua_light_position"); + light.transform.column(3)[2]), "gua_light_position_direction"); + render(); + } + } + + god_ray_shader_->set_subroutine(ctx, + scm::gl::STAGE_VERTEX_SHADER, + "compute_position", + "gua_calculate_by_direction"); + + for (auto const& light: scene.sun_lights_) { + if (light.data.get_enable_godrays()) { + god_ray_shader_->set_uniform(ctx, light.data.get_color().vec3(), "gua_light_color"); + math::vec3 direction(0, 0, 1); + direction = light.transform * direction; + god_ray_shader_->set_uniform(ctx, direction, "gua_light_position_direction"); render(); } } diff --git a/src/gua/renderer/Serializer.cpp b/src/gua/renderer/Serializer.cpp index 365d317c6..b2dfff504 100644 --- a/src/gua/renderer/Serializer.cpp +++ b/src/gua/renderer/Serializer.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -75,6 +76,7 @@ void Serializer::check(SerializedScene* output, std::size_t volume_count = data_->volumenodes_.size(); std::size_t point_light_count = data_->point_lights_.size(); std::size_t spot_light_count = data_->spot_lights_.size(); + std::size_t sun_light_count = data_->sun_lights_.size(); std::size_t ray_count = data_->rays_.size(); std::size_t textured_quad_count = data_->textured_quads_.size(); @@ -83,12 +85,14 @@ void Serializer::check(SerializedScene* output, data_->volumenodes_.clear(); data_->point_lights_.clear(); data_->spot_lights_.clear(); + data_->sun_lights_.clear(); data_->textured_quads_.clear(); data_->bounding_boxes_.clear(); data_->rays_.clear(); draw_bounding_boxes_ = draw_bounding_boxes; draw_rays_ = draw_rays; + if (draw_bounding_boxes_) { data_->materials_.insert("gua_bounding_box"); data_->bounding_boxes_ @@ -110,6 +114,7 @@ void Serializer::check(SerializedScene* output, data_->volumenodes_.reserve(nurbs_count); data_->point_lights_.reserve(point_light_count); data_->spot_lights_.reserve(spot_light_count); + data_->sun_lights_.reserve(sun_light_count); data_->textured_quads_.reserve(textured_quad_count); enable_frustum_culling_ = enable_frustum_culling; @@ -169,7 +174,7 @@ void Serializer::check(SerializedScene* output, if ( is_visible(node) ) { if ( !node->data.get_volume().empty() ) { add_bbox(node); - data_->volumenodes_.push_back(make_serialized_node(node->get_world_transform(), node->data)); + data_->volumenodes_.push_back(make_serialized_node(node->get_world_transform(), node->data)); } visit_children(node); @@ -207,6 +212,18 @@ void Serializer::check(SerializedScene* output, //////////////////////////////////////////////////////////////////////// +/* virtual */ void Serializer::visit(SunLightNode* node) { + + if (is_visible(node)) { + data_->sun_lights_ + .push_back(make_serialized_node(node->get_world_transform(), node->data)); + + visit_children(node); + } +} + +//////////////////////////////////////////////////////////////////////// + /* virtual */ void Serializer::visit(RayNode* node) { if (is_visible(node)) { diff --git a/src/gua/scenegraph/SunLightNode.cpp b/src/gua/scenegraph/SunLightNode.cpp new file mode 100644 index 000000000..730fb9fc9 --- /dev/null +++ b/src/gua/scenegraph/SunLightNode.cpp @@ -0,0 +1,53 @@ +/****************************************************************************** + * guacamole - delicious VR * + * * + * Copyright: (c) 2011-2013 Bauhaus-Universität Weimar * + * Contact: felix.lauer@uni-weimar.de / simon.schneegans@uni-weimar.de * + * * + * This program is free software: you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the Free * + * Software Foundation, either version 3 of the License, or (at your option) * + * any later version. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * + * for more details. * + * * + * You should have received a copy of the GNU General Public License along * + * with this program. If not, see . * + * * + ******************************************************************************/ + +// class header +#include + +// guacamole header +#include +#include +#include +#include + +namespace gua { + +SunLightNode::SunLightNode(std::string const& name, + Configuration const& configuration, + math::mat4 const& transform) + : Node(name, transform), data(configuration) {} + +/* virtual */ void SunLightNode::accept(NodeVisitor& visitor) { + visitor.visit(this); +} + +void SunLightNode::update_bounding_box() const { + bounding_box_ = math::BoundingBox( + math::vec3(std::numeric_limits::lowest()), + math::vec3(std::numeric_limits::max()) + ); +} + +std::shared_ptr SunLightNode::copy() const { + return std::make_shared(get_name(), data, get_transform()); +} + +} From bfe3bc0ef1b6974d848e185d483e9b935630104a Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Wed, 29 Jan 2014 03:03:15 +0100 Subject: [PATCH 083/146] added cascaded shadow maps --- include/gua/math/math.hpp | 7 +- include/gua/renderer/Frustum.hpp | 34 +- include/gua/renderer/LightingPass.hpp | 22 +- include/gua/renderer/Pipeline.hpp | 13 +- include/gua/renderer/ShadowMap.hpp | 93 +++++ .../uber_shaders/lighting/lighting.frag | 42 ++- .../uber_shaders/lighting/lighting.vert | 19 +- src/gua/math/math.cpp | 34 +- src/gua/renderer/Frustum.cpp | 172 ++++++--- src/gua/renderer/LightingPass.cpp | 147 ++------ src/gua/renderer/Pipeline.cpp | 8 +- src/gua/renderer/PostFXPass.cpp | 2 + src/gua/renderer/Serializer.cpp | 5 +- src/gua/renderer/ShadowMap.cpp | 330 ++++++++++++++++++ 14 files changed, 690 insertions(+), 238 deletions(-) create mode 100644 include/gua/renderer/ShadowMap.hpp create mode 100644 src/gua/renderer/ShadowMap.cpp diff --git a/include/gua/math/math.hpp b/include/gua/math/math.hpp index 1f95e3d04..a546022f1 100644 --- a/include/gua/math/math.hpp +++ b/include/gua/math/math.hpp @@ -67,7 +67,12 @@ typedef scm::math::quat quat; * * \return A frustum matrix. */ -math::mat4 const GUA_DLL compute_frustum(math::vec4 const& eye_position, +math::mat4 const GUA_DLL compute_perspective_frustum(math::vec4 const& eye_position, + math::mat4 const& screen_transform, + float near_plane, + float far_plane); + +math::mat4 const GUA_DLL compute_orthographic_frustum(math::vec4 const& eye_position, math::mat4 const& screen_transform, float near_plane, float far_plane); diff --git a/include/gua/renderer/Frustum.hpp b/include/gua/renderer/Frustum.hpp index 4dcfa39ad..2795a1b94 100644 --- a/include/gua/renderer/Frustum.hpp +++ b/include/gua/renderer/Frustum.hpp @@ -35,16 +35,29 @@ class Frustum { public: - Frustum() {} + Frustum(); - Frustum(math::mat4 const& camera_transform, - math::mat4 const& screen_transform, - float clip_near, - float clip_far); + static Frustum perspective(math::mat4 const& camera_transform, + math::mat4 const& screen_transform, + float clip_near, + float clip_far); - inline math::vec3 const& get_camera_position() const { - return camera_position_; + static Frustum orthographic(math::mat4 const& camera_transform, + math::mat4 const& screen_transform, + float clip_near, + float clip_far); + + std::vector get_corners() const; + + inline math::vec3 get_camera_position() const { + return math::vec3(camera_transform_.column(3)[0], + camera_transform_.column(3)[1], + camera_transform_.column(3)[2]);; } + + inline math::mat4 const& get_camera_transform() const { return camera_transform_; } + inline math::mat4 const& get_screen_transform() const { return screen_transform_; } + inline math::mat4 const& get_projection() const { return projection_; } inline math::mat4 const& get_view() const { return view_; } inline float get_clip_near() const { return clip_near_; } @@ -54,7 +67,12 @@ class Frustum { private: - math::vec3 camera_position_; + static void init_frustum_members(math::mat4 const& camera_transform, + math::mat4 const& screen_transform, + Frustum& frustum); + + math::mat4 camera_transform_; + math::mat4 screen_transform_; math::mat4 projection_; math::mat4 view_; std::vector planes_; diff --git a/include/gua/renderer/LightingPass.hpp b/include/gua/renderer/LightingPass.hpp index 020d4fabe..b2e06b701 100644 --- a/include/gua/renderer/LightingPass.hpp +++ b/include/gua/renderer/LightingPass.hpp @@ -24,10 +24,9 @@ // guacamole headers #include +#include #include #include -#include -#include namespace gua { @@ -64,9 +63,8 @@ class LightingPass : public GeometryPass { bool pre_compile_shaders(RenderContext const& ctx); - public: // attributes - - GBuffer* shadow_map_; +public: + ShadowMap shadow_map_; private: void rendering(SerializedScene const& scene, @@ -75,25 +73,11 @@ class LightingPass : public GeometryPass { Camera const& camera, FrameBufferObject* target); - void render_shadow_map(RenderContext const& ctx, - Camera const& scene_camera, - math::mat4 const& transform, - unsigned map_size); - LightingUberShader* shader_; std::shared_ptr light_sphere_; std::shared_ptr light_cone_; scm::gl::quad_geometry_ptr fullscreen_quad_; - Serializer* serializer_; - - ShadowMapMeshShader* shadow_map_mesh_shader_; - ShadowMapNURBSShader* shadow_map_nurbs_shader_; - - scm::gl::depth_stencil_state_ptr shadow_map_depth_stencil_state_; - scm::gl::rasterizer_state_ptr shadow_map_rasterizer_state_; - math::mat4 shadow_map_projection_view_matrix_; - scm::gl::depth_stencil_state_ptr depth_stencil_state_; scm::gl::rasterizer_state_ptr rasterizer_state_front_; scm::gl::rasterizer_state_ptr rasterizer_state_back_; diff --git a/include/gua/renderer/Pipeline.hpp b/include/gua/renderer/Pipeline.hpp index 727d2e779..4e1711a1e 100644 --- a/include/gua/renderer/Pipeline.hpp +++ b/include/gua/renderer/Pipeline.hpp @@ -168,14 +168,10 @@ class GUA_DLL Pipeline { inline float get_application_fps() const { return application_fps_; } inline float get_rendering_fps() const { return rendering_fps_; } + SerializedScene const& get_current_scene(CameraMode mode) const; + inline SceneGraph const* get_current_graph() const { return current_graph_; } + friend class Renderer; - friend class GBufferPass; - friend class LightingPass; - friend class FinalPass; - friend class CompositePass; - friend class PostFXPass; - friend class GeometryPass; - friend class FullscreenPass; private: void process(std::vector> const& scene_graphs, @@ -185,9 +181,6 @@ class GUA_DLL Pipeline { void create_passes(); void create_buffers(); - SerializedScene const& get_current_scene(CameraMode mode) const; - inline SceneGraph const* get_current_graph() const { return current_graph_; } - mutable std::mutex upload_mutex_; Window* window_; diff --git a/include/gua/renderer/ShadowMap.hpp b/include/gua/renderer/ShadowMap.hpp new file mode 100644 index 000000000..cbf842600 --- /dev/null +++ b/include/gua/renderer/ShadowMap.hpp @@ -0,0 +1,93 @@ +/****************************************************************************** + * guacamole - delicious VR * + * * + * Copyright: (c) 2011-2013 Bauhaus-Universität Weimar * + * Contact: felix.lauer@uni-weimar.de / simon.schneegans@uni-weimar.de * + * * + * This program is free software: you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the Free * + * Software Foundation, either version 3 of the License, or (at your option) * + * any later version. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * + * for more details. * + * * + * You should have received a copy of the GNU General Public License along * + * with this program. If not, see . * + * * + ******************************************************************************/ + +#ifndef GUA_SHADOW_MAP_HPP +#define GUA_SHADOW_MAP_HPP + +// guacamole headers +#include +#include + +namespace gua { + +class Serializer; +class GBuffer; +class Frustum; +class Camera; +class Pipeline; +class ShadowMapMeshShader; +class ShadowMapNURBSShader; + +/** + * + */ +class ShadowMap { + public: + + /** + * + */ + ShadowMap(Pipeline* pipeline); + + virtual ~ShadowMap(); + + void render(RenderContext const& ctx, + Camera const& scene_camera, + math::mat4 const& transform, + unsigned map_size); + + void render_cascaded(RenderContext const& ctx, + Frustum const& scene_frustum, + Camera const& scene_camera, + math::mat4 const& transform, + unsigned map_size, float split_1, + float split_2, float split_3); + + void print_shaders(std::string const& directory, + std::string const& name) const; + + bool pre_compile_shaders(RenderContext const& ctx); + + GBuffer* get_buffer() const {return buffer_;} + std::vector const& get_projection_view_matrices() const {return projection_view_matrices_;} + + private: + + void update_members(RenderContext const& ctx, unsigned map_size); + void render_geometry(RenderContext const & ctx, Frustum const& shadow_frustum, Camera const& scene_camera, unsigned cascade); + + GBuffer* buffer_; + + Serializer* serializer_; + + Pipeline* pipeline_; + + ShadowMapMeshShader* mesh_shader_; + ShadowMapNURBSShader* nurbs_shader_; + + scm::gl::depth_stencil_state_ptr depth_stencil_state_; + scm::gl::rasterizer_state_ptr rasterizer_state_; + std::vector projection_view_matrices_; +}; + +} + +#endif // GUA_SHADOW_MAP_HPP diff --git a/resources/shaders/uber_shaders/lighting/lighting.frag b/resources/shaders/uber_shaders/lighting/lighting.frag index e3e9c1bc2..d4ac2c839 100644 --- a/resources/shaders/uber_shaders/lighting/lighting.frag +++ b/resources/shaders/uber_shaders/lighting/lighting.frag @@ -28,6 +28,9 @@ in vec3 gua_lightinfo1; in vec3 gua_lightinfo2; in float gua_lightinfo3; in mat4 gua_lightinfo4; +in mat4 gua_lightinfo5; +in mat4 gua_lightinfo6; +in mat4 gua_lightinfo7; // input from gbuffer uniform uvec2 gua_depth_gbuffer_in; @@ -36,6 +39,11 @@ uniform uvec2 gua_depth_gbuffer_in; // uniforms @include "shaders/uber_shaders/common/gua_camera_uniforms.glsl" +uniform mat4 gua_light_shadow_map_projection_view_matrix_0; +uniform mat4 gua_light_shadow_map_projection_view_matrix_1; +uniform mat4 gua_light_shadow_map_projection_view_matrix_2; +uniform mat4 gua_light_shadow_map_projection_view_matrix_3; + uniform vec3 gua_light_color; uniform float gua_light_falloff; uniform float gua_light_softness; @@ -103,13 +111,12 @@ float gua_get_shadow(vec4 smap_coords) { ); } -float gua_get_shadow() { +float gua_get_shadow(mat4 shadow_map_coords_matrix, vec2 lookup_offset) { if(!gua_light_casts_shadow) return 1.0; - mat4 shadow_map_coords_matrix = gua_lightinfo4; vec3 position = gua_get_position(); - vec4 smap_coords = shadow_map_coords_matrix * vec4(position, 1.0); + vec4 smap_coords = shadow_map_coords_matrix * vec4(position, 1.0) + vec4(lookup_offset, 0, 0); float sum = 0; int x, y; @@ -123,6 +130,12 @@ float gua_get_shadow() { return shadow; } +bool gua_is_inside_frustum(mat4 frustum, vec3 position) { + vec4 proj = frustum * vec4(position, 1.0); + proj /= proj.w; + return (abs(proj.x) <= 1 && abs(proj.y) <= 1 && abs(proj.z) <= 1); +} + subroutine void CalculateLightType(); subroutine uniform CalculateLightType compute_light; @@ -172,7 +185,7 @@ void gua_calculate_spot_light() { if (dot(gbuffer_normal, gua_light_direction) < 0) discard; - float shadow = gua_get_shadow(); + float shadow = gua_get_shadow(gua_lightinfo4, vec2(0)); if(shadow <= 0.0) discard; @@ -202,17 +215,22 @@ void gua_calculate_sun_light() { if (dot(gbuffer_normal, gua_light_direction) < 0) discard; - float shadow = 0.25; - float depth = gua_get_depth(); - if (depth < 0.94) { - shadow = 1.0; - } else if (depth < 0.98) { - shadow = 0.75; - } else if (depth < 0.99) { - shadow = 0.5; + vec3 position = gua_get_position(); + + float shadow = 1.0; + + if (gua_is_inside_frustum(gua_light_shadow_map_projection_view_matrix_0, position)) { + shadow = gua_get_shadow(gua_lightinfo4, vec2(0, 0)); + } else if (gua_is_inside_frustum(gua_light_shadow_map_projection_view_matrix_1, position)) { + shadow = gua_get_shadow(gua_lightinfo5, vec2(1, 0)); + } else if (gua_is_inside_frustum(gua_light_shadow_map_projection_view_matrix_2, position)) { + shadow = gua_get_shadow(gua_lightinfo6, vec2(0, 1)); + } else { + shadow = gua_get_shadow(gua_lightinfo7, vec2(1, 1)); } + gua_light_intensity = 1.0 * shadow; } diff --git a/resources/shaders/uber_shaders/lighting/lighting.vert b/resources/shaders/uber_shaders/lighting/lighting.vert index cddb19470..d8e7d6383 100644 --- a/resources/shaders/uber_shaders/lighting/lighting.vert +++ b/resources/shaders/uber_shaders/lighting/lighting.vert @@ -29,13 +29,19 @@ layout(location=2) in vec3 gua_in_normal; // uniforms @include "shaders/uber_shaders/common/gua_camera_uniforms.glsl" -uniform mat4 gua_light_shadow_map_projection_view_matrix; +uniform mat4 gua_light_shadow_map_projection_view_matrix_0; +uniform mat4 gua_light_shadow_map_projection_view_matrix_1; +uniform mat4 gua_light_shadow_map_projection_view_matrix_2; +uniform mat4 gua_light_shadow_map_projection_view_matrix_3; // output out vec3 gua_lightinfo1; out vec3 gua_lightinfo2; out float gua_lightinfo3; out mat4 gua_lightinfo4; +out mat4 gua_lightinfo5; +out mat4 gua_lightinfo6; +out mat4 gua_lightinfo7; // BASE LIGHTING CALCULATIONS -------------------------------------------------- @@ -69,7 +75,7 @@ void gua_calculate_spot_light() { vec3 light_position = (gua_model_matrix * vec4(0.0, 0.0, 0.0, 1.0)).xyz; vec3 beam_direction = (gua_model_matrix * vec4(0.0, 0.0, -1.0, 1.0)).xyz - light_position; float half_beam_angle = dot(normalize((gua_model_matrix * vec4(0.0, 0.5, -1.0, 0.0)).xyz), normalize(beam_direction)); - mat4 shadow_map_coords_mat = bias * gua_light_shadow_map_projection_view_matrix; + mat4 shadow_map_coords_mat = bias * gua_light_shadow_map_projection_view_matrix_0; gua_lightinfo1 = light_position; gua_lightinfo2 = beam_direction; @@ -84,8 +90,17 @@ void gua_calculate_spot_light() { subroutine( CalculateLightType ) void gua_calculate_sun_light() { + const mat4 bias = mat4(0.5, 0.0, 0.0, 0.0, + 0.0, 0.5, 0.0, 0.0, + 0.0, 0.0, 0.5, 0.0, + 0.5, 0.5, 0.5, 1.0); + vec3 light_direction = normalize((gua_model_matrix * vec4(0.0, 0.0, 1.0, 0.0)).xyz); gua_lightinfo1 = light_direction; + gua_lightinfo4 = bias * gua_light_shadow_map_projection_view_matrix_0; + gua_lightinfo5 = bias * gua_light_shadow_map_projection_view_matrix_1; + gua_lightinfo6 = bias * gua_light_shadow_map_projection_view_matrix_2; + gua_lightinfo7 = bias * gua_light_shadow_map_projection_view_matrix_3; gl_Position = vec4(gua_in_position, 1.0); } diff --git a/src/gua/math/math.cpp b/src/gua/math/math.cpp index 737a40ecb..545563a21 100644 --- a/src/gua/math/math.cpp +++ b/src/gua/math/math.cpp @@ -36,11 +36,13 @@ namespace gua { //////////////////////////////////////////////////////////////////////////////// -math::mat4 const math::compute_frustum(math::vec4 const& eye_position, +math::mat4 const math::compute_perspective_frustum(math::vec4 const& eye_position, math::mat4 const& screen_transform, float near_plane, float far_plane) { + math::mat4 frustum(math::mat4::identity()); + math::vec4 relative_eye_position(scm::math::inverse(screen_transform) * eye_position); @@ -48,8 +50,6 @@ math::mat4 const math::compute_frustum(math::vec4 const& eye_position, float ox(-relative_eye_position[0]); float oy(-relative_eye_position[1]); - math::mat4 frustum(math::mat4::identity()); - frustum[0] = 2 * d; frustum[5] = 2 * d; frustum[8] = 2 * ox; @@ -64,6 +64,34 @@ math::mat4 const math::compute_frustum(math::vec4 const& eye_position, //////////////////////////////////////////////////////////////////////////////// +math::mat4 const math::compute_orthographic_frustum(math::vec4 const& eye_position, + math::mat4 const& screen_transform, + float near_plane, + float far_plane) { + + math::mat4 frustum(math::mat4::identity()); + + + math::vec4 relative_eye_position(scm::math::inverse(screen_transform) * + eye_position); + + // float d(relative_eye_position[2]); + float ox(-relative_eye_position[0]); + float oy(-relative_eye_position[1]); + + frustum[0] = 2.0f; + frustum[5] = 2.0f; + frustum[10] = 2.0f / (near_plane - far_plane); + frustum[12] = -2.0f * ox; + frustum[13] = -2.0f * oy; + frustum[14] = (far_plane + near_plane) / (near_plane - far_plane); + frustum[15] = 1.f; + + return frustum; +} + +//////////////////////////////////////////////////////////////////////////////// + math::mat4 const math::mat_ai_to_scm(aiMatrix4x4 const& ai_mat) { math::mat4 scm_mat; diff --git a/src/gua/renderer/Frustum.cpp b/src/gua/renderer/Frustum.cpp index 25cd3eddb..f6412ce82 100644 --- a/src/gua/renderer/Frustum.cpp +++ b/src/gua/renderer/Frustum.cpp @@ -26,77 +26,83 @@ namespace gua { -Frustum::Frustum(math::mat4 const& camera_transform, - math::mat4 const& screen_transform, - float clip_near, - float clip_far) - : camera_position_(), +Frustum::Frustum() + : camera_transform_(math::mat4::identity()), + screen_transform_(math::mat4::identity()), projection_(math::mat4::identity()), view_(math::mat4::identity()), planes_(6), - clip_near_(clip_near), - clip_far_(clip_far) { + clip_near_(0), + clip_far_(0) {} - projection_ = math::compute_frustum( - camera_transform.column(3), screen_transform, clip_near, clip_far); +//////////////////////////////////////////////////////////////////////////////// - math::mat4 view_transform(screen_transform); - view_transform[12] = 0.f; - view_transform[13] = 0.f; - view_transform[14] = 0.f; - view_transform[15] = 1.f; +Frustum Frustum::perspective(math::mat4 const& camera_transform, + math::mat4 const& screen_transform, + float clip_near, + float clip_far) { - camera_position_ = math::vec3(camera_transform.column(3)[0], - camera_transform.column(3)[1], - camera_transform.column(3)[2]); + auto projection = math::compute_perspective_frustum( + camera_transform.column(3), screen_transform, clip_near, clip_far); - view_transform = - scm::math::make_translation(camera_position_) * view_transform; + Frustum result; - view_ = scm::math::inverse(view_transform); + result.projection_ = projection; + result.clip_near_ = clip_near; + result.clip_far_ = clip_far; - auto frustum(projection_ * view_); + init_frustum_members(camera_transform, screen_transform, result); - //store normals + return result; +} - //left plane - planes_[0] = math::vec4(frustum[3] + frustum[0], - frustum[7] + frustum[4], - frustum[11] + frustum[8], - frustum[15] + frustum[12]); +//////////////////////////////////////////////////////////////////////////////// - //right plane - planes_[1] = math::vec4(frustum[3] - frustum[0], - frustum[7] - frustum[4], - frustum[11] - frustum[8], - frustum[15] - frustum[12]); +Frustum Frustum::orthographic(math::mat4 const& camera_transform, + math::mat4 const& screen_transform, + float clip_near, + float clip_far) { - //bottom plane - planes_[2] = math::vec4(frustum[3] + frustum[1], - frustum[7] + frustum[5], - frustum[11] + frustum[9], - frustum[15] + frustum[13]); + auto projection = math::compute_orthographic_frustum( + camera_transform.column(3), screen_transform, clip_near, clip_far); - //top plane - planes_[3] = math::vec4(frustum[3] - frustum[1], - frustum[7] - frustum[5], - frustum[11] - frustum[9], - frustum[15] - frustum[13]); + Frustum result; - //near plane - planes_[4] = math::vec4(frustum[3] + frustum[2], - frustum[7] + frustum[6], - frustum[11] + frustum[10], - frustum[15] + frustum[14]); + result.projection_ = projection; + result.clip_near_ = clip_near; + result.clip_far_ = clip_far; - //far plane - planes_[5] = math::vec4(frustum[3] - frustum[2], - frustum[7] - frustum[6], - frustum[11] - frustum[10], - frustum[15] - frustum[14]); + init_frustum_members(camera_transform, screen_transform, result); + return result; } +//////////////////////////////////////////////////////////////////////////////// + +std::vector Frustum::get_corners() const { + std::vector tmp(8); + std::vector result(8); + + auto inverse_transform(scm::math::inverse(projection_ * view_)); + + tmp[0] = inverse_transform * math::vec4(-1, -1, -1, 1); + tmp[1] = inverse_transform * math::vec4(-1, -1, 1, 1); + tmp[2] = inverse_transform * math::vec4(-1, 1, -1, 1); + tmp[3] = inverse_transform * math::vec4(-1, 1, 1, 1); + tmp[4] = inverse_transform * math::vec4( 1, -1, -1, 1); + tmp[5] = inverse_transform * math::vec4( 1, -1, 1, 1); + tmp[6] = inverse_transform * math::vec4( 1, 1, -1, 1); + tmp[7] = inverse_transform * math::vec4( 1, 1, 1, 1); + + for (int i(0); i<8; ++i) { + result[i] = tmp[i]/tmp[i][3]; + } + + return result; +} + +//////////////////////////////////////////////////////////////////////////////// + bool Frustum::is_inside(math::BoundingBox const& bbox) const { auto distance = [](math::vec4 const & plane, math::vec3 const & point) { @@ -123,4 +129,66 @@ bool Frustum::is_inside(math::BoundingBox const& bbox) const { return true; } +//////////////////////////////////////////////////////////////////////////////// + +void Frustum::init_frustum_members(math::mat4 const& camera_transform, + math::mat4 const& screen_transform, + Frustum& frustum) { + + math::mat4 view_transform(screen_transform); + view_transform[12] = 0.f; + view_transform[13] = 0.f; + view_transform[14] = 0.f; + view_transform[15] = 1.f; + + frustum.camera_transform_ = camera_transform; + frustum.screen_transform_ = screen_transform; + + view_transform = + scm::math::make_translation(frustum.get_camera_position()) * view_transform; + + frustum.view_ = scm::math::inverse(view_transform); + + auto projection_view(frustum.projection_ * frustum.view_); + + //store normals + + //left plane + frustum.planes_[0] = math::vec4(projection_view[3] + projection_view[0], + projection_view[7] + projection_view[4], + projection_view[11] + projection_view[8], + projection_view[15] + projection_view[12]); + + //right plane + frustum.planes_[1] = math::vec4(projection_view[3] - projection_view[0], + projection_view[7] - projection_view[4], + projection_view[11] - projection_view[8], + projection_view[15] - projection_view[12]); + + //bottom plane + frustum.planes_[2] = math::vec4(projection_view[3] + projection_view[1], + projection_view[7] + projection_view[5], + projection_view[11] + projection_view[9], + projection_view[15] + projection_view[13]); + + //top plane + frustum.planes_[3] = math::vec4(projection_view[3] - projection_view[1], + projection_view[7] - projection_view[5], + projection_view[11] - projection_view[9], + projection_view[15] - projection_view[13]); + + //near plane + frustum.planes_[4] = math::vec4(projection_view[3] + projection_view[2], + projection_view[7] + projection_view[6], + projection_view[11] + projection_view[10], + projection_view[15] + projection_view[14]); + + //far plane + frustum.planes_[5] = math::vec4(projection_view[3] - projection_view[2], + projection_view[7] - projection_view[6], + projection_view[11] - projection_view[10], + projection_view[15] - projection_view[14]); +} + + } diff --git a/src/gua/renderer/LightingPass.cpp b/src/gua/renderer/LightingPass.cpp index ce0ea3133..401327a75 100644 --- a/src/gua/renderer/LightingPass.cpp +++ b/src/gua/renderer/LightingPass.cpp @@ -40,11 +40,7 @@ LightingPass::LightingPass(Pipeline* pipeline) shader_(new LightingUberShader), light_sphere_(nullptr), light_cone_(nullptr), - serializer_(new Serializer), - shadow_map_mesh_shader_(new ShadowMapMeshShader), - shadow_map_nurbs_shader_(new ShadowMapNURBSShader), - shadow_map_(nullptr), - shadow_map_projection_view_matrix_(math::mat4::identity()) { + shadow_map_(pipeline) { light_sphere_ = GeometryDatabase::instance()->lookup("gua_light_sphere_proxy"); light_cone_ = GeometryDatabase::instance()->lookup("gua_light_cone_proxy"); } @@ -53,12 +49,6 @@ LightingPass::LightingPass(Pipeline* pipeline) //////////////////////////////////////////////////////////////////////////////// LightingPass::~LightingPass() { - if (shadow_map_mesh_shader_) - delete shadow_map_mesh_shader_; - if (shadow_map_nurbs_shader_) - delete shadow_map_nurbs_shader_; - if (serializer_) - delete serializer_; if (shader_) delete shader_; } @@ -83,17 +73,19 @@ LayerMapping const* LightingPass::get_gbuffer_mapping() const { /* virtual */ void LightingPass::print_shaders(std::string const& directory, std::string const& name) const { shader_->save_to_file(directory, name + "/lighting"); - shadow_map_mesh_shader_->save_to_file(directory, name + "/shadow/mesh"); - shadow_map_nurbs_shader_->save_to_file(directory, name + "/shadow/nurbs"); + shadow_map_.print_shaders(directory, name); } //////////////////////////////////////////////////////////////////////////////// bool LightingPass::pre_compile_shaders(RenderContext const& ctx) { - if (shader_) return shader_->upload_to(ctx); - if (shadow_map_mesh_shader_) return shadow_map_mesh_shader_->upload_to(ctx); - // if (shadow_map_nurbs_shader_) shadow_map_nurbs_shader_->upload_to(ctx); - return false; + + bool success(false); + + if (shader_) success = shader_->upload_to(ctx); + if (success) success = shadow_map_.pre_compile_shaders(ctx); + + return success; } //////////////////////////////////////////////////////////////////////////////// @@ -158,7 +150,7 @@ void LightingPass::rendering(SerializedScene const& scene, target->unbind(ctx); ctx.render_context->reset_state_objects(); - render_shadow_map(ctx, camera, light.transform, light.data.get_shadow_map_size()); + shadow_map_.render_cascaded(ctx, scene.frustum, camera, light.transform, light.data.get_shadow_map_size(), 2, 7, 50); shader_->use(ctx); target->bind(ctx); @@ -171,11 +163,14 @@ void LightingPass::rendering(SerializedScene const& scene, ctx.render_context->set_rasterizer_state(rasterizer_state_back_); ctx.render_context->set_blend_state(blend_state_); - shader_->set_uniform(ctx, shadow_map_->get_depth_buffer(), "gua_light_shadow_map"); + shader_->set_uniform(ctx, shadow_map_.get_buffer()->get_depth_buffer(), "gua_light_shadow_map"); - float shadow_map_portion(1.f * light.data.get_shadow_map_size() / shadow_map_->width()); + float shadow_map_portion(1.f * light.data.get_shadow_map_size() / shadow_map_.get_buffer()->width()); shader_->set_uniform(ctx, shadow_map_portion, "gua_light_shadow_map_portion"); - shader_->set_uniform(ctx, shadow_map_projection_view_matrix_, "gua_light_shadow_map_projection_view_matrix"); + + for (int i(0); i<4; ++i) { + shader_->set_uniform(ctx, shadow_map_.get_projection_view_matrices()[i], "gua_light_shadow_map_projection_view_matrix_" + string_utils::to_string(i)); + } } shader_->set_uniform( @@ -185,7 +180,9 @@ void LightingPass::rendering(SerializedScene const& scene, "gua_light_specular_enable"); shader_->set_uniform(ctx, light.transform, "gua_model_matrix"); shader_->set_uniform(ctx, light.data.get_color().vec3(), "gua_light_color"); - shader_->set_uniform(ctx, false, "gua_light_casts_shadow"); + shader_->set_uniform(ctx, light.data.get_enable_shadows(), "gua_light_casts_shadow"); + shader_->set_uniform( + ctx, light.data.get_shadow_offset(), "gua_shadow_offset"); fullscreen_quad_->draw(ctx.render_context); } @@ -235,7 +232,7 @@ void LightingPass::rendering(SerializedScene const& scene, target->unbind(ctx); ctx.render_context->reset_state_objects(); - render_shadow_map(ctx, camera, light.transform, light.data.get_shadow_map_size()); + shadow_map_.render(ctx, camera, light.transform, light.data.get_shadow_map_size()); shader_->use(ctx); target->bind(ctx); @@ -248,17 +245,11 @@ void LightingPass::rendering(SerializedScene const& scene, ctx.render_context->set_rasterizer_state(rasterizer_state_front_); ctx.render_context->set_blend_state(blend_state_); - shader_->set_uniform( - ctx, shadow_map_->get_depth_buffer(), "gua_light_shadow_map"); - - float shadow_map_portion(1.f * light.data.get_shadow_map_size() / shadow_map_->width()); - shader_->set_uniform( - ctx, shadow_map_portion, "gua_light_shadow_map_portion"); - - shader_->set_uniform(ctx, - shadow_map_projection_view_matrix_, - "gua_light_shadow_map_projection_view_matrix"); + shader_->set_uniform(ctx, shadow_map_.get_buffer()->get_depth_buffer(), "gua_light_shadow_map"); + float shadow_map_portion(1.f * light.data.get_shadow_map_size() / shadow_map_.get_buffer()->width()); + shader_->set_uniform(ctx, shadow_map_portion, "gua_light_shadow_map_portion"); + shader_->set_uniform(ctx, shadow_map_.get_projection_view_matrices()[0], "gua_light_shadow_map_projection_view_matrix_0"); } shader_->set_uniform( @@ -284,94 +275,4 @@ void LightingPass::rendering(SerializedScene const& scene, //////////////////////////////////////////////////////////////////////////////// -void LightingPass::render_shadow_map(RenderContext const & ctx, - Camera const& scene_camera, - math::mat4 const & transform, - unsigned map_size) { - - //check whether shadow map size is sufficient - if (shadow_map_ && shadow_map_->width() < map_size) { - shadow_map_->remove_buffers(ctx); - delete shadow_map_; - shadow_map_ = nullptr; - } - - if (!shadow_map_) { - scm::gl::sampler_state_desc state; - state._compare_mode = scm::gl::TEXCOMPARE_COMPARE_REF_TO_TEXTURE; - -#if GUA_COMPILER == GUA_COMPILER_MSVC&& SCM_COMPILER_VER <= 1700 - std::vector > - gbuffer_desc; - gbuffer_desc.push_back(std::make_pair(BufferComponent::DEPTH_16, state)); - shadow_map_ = new GBuffer(gbuffer_desc, map_size, map_size); -#else - shadow_map_ = new GBuffer({{ BufferComponent::DEPTH_16, state }}, map_size, map_size); -#endif - shadow_map_->create(ctx); - } - - shadow_map_->bind(ctx); - - ctx.render_context->set_viewport(scm::gl::viewport( - math::vec2(0, 0), - math::vec2(map_size, map_size))); - - shadow_map_->clear_depth_stencil_buffer(ctx); - - math::mat4 screen_transform(scm::math::make_translation(0.f, 0.f, -1.f)); - screen_transform = transform * screen_transform; - - Frustum shadow_frustum(transform, - screen_transform, - pipeline_->config.near_clip(), - pipeline_->config.far_clip()); - - SerializedScene scene; - scene.frustum = shadow_frustum; - serializer_->check(&scene, - pipeline_->get_current_graph(), - Camera("", "", scene_camera.render_mask), - false, - false, - true); - - shadow_map_projection_view_matrix_ = - shadow_frustum.get_projection() * shadow_frustum.get_view(); - - shadow_map_mesh_shader_->set_uniform( - ctx, shadow_map_projection_view_matrix_, "gua_projection_view_matrix"); - shadow_map_mesh_shader_->use(ctx); - - // let derived class render all geometries - if (!shadow_map_depth_stencil_state_) - shadow_map_depth_stencil_state_ = - ctx.render_device->create_depth_stencil_state(true, true); - - if (!shadow_map_rasterizer_state_) - shadow_map_rasterizer_state_ = ctx.render_device - ->create_rasterizer_state(scm::gl::FILL_SOLID, scm::gl::CULL_NONE); - - ctx.render_context->set_depth_stencil_state(shadow_map_depth_stencil_state_); - ctx.render_context->set_rasterizer_state(shadow_map_rasterizer_state_); - - for (auto const& node : scene.meshnodes_) { - auto geometry = GeometryDatabase::instance()->lookup(node.data.get_geometry()); - - if (geometry) { - shadow_map_mesh_shader_->set_uniform( - ctx, node.transform, "gua_model_matrix"); - geometry->draw(ctx); - } - } - - ctx.render_context->reset_state_objects(); - - shadow_map_mesh_shader_->unuse(ctx); - - shadow_map_->unbind(ctx); -} - -//////////////////////////////////////////////////////////////////////////////// - } diff --git a/src/gua/renderer/Pipeline.cpp b/src/gua/renderer/Pipeline.cpp index 135506947..9faef1562 100644 --- a/src/gua/renderer/Pipeline.cpp +++ b/src/gua/renderer/Pipeline.cpp @@ -232,7 +232,7 @@ void Pipeline::process(std::vector> const& sce return; } - current_scenes_[0].frustum = Frustum(eye->get_world_transform(), + current_scenes_[0].frustum = Frustum::perspective(eye->get_world_transform(), screen->get_scaled_world_transform(), config.near_clip(), config.far_clip()); @@ -273,11 +273,11 @@ void Pipeline::process(std::vector> const& sce } - current_scenes_[0].frustum = Frustum(eye_l->get_world_transform(), + current_scenes_[0].frustum = Frustum::perspective(eye_l->get_world_transform(), screen_l->get_scaled_world_transform(), config.near_clip(), config.far_clip()); - current_scenes_[1].frustum = Frustum(eye_r->get_world_transform(), + current_scenes_[1].frustum = Frustum::perspective(eye_r->get_world_transform(), screen_r->get_scaled_world_transform(), config.near_clip(), config.far_clip()); @@ -309,7 +309,7 @@ void Pipeline::process(std::vector> const& sce ->get_color_buffers(TYPE_FLOAT)[0]); } else { window_->display(passes_[PipelineStage::postfx]->get_gbuffer()->get_eye_buffers()[0] - ->get_color_buffers(TYPE_FLOAT)[0]); + ->get_color_buffers(TYPE_FLOAT)[0]); } window_->finish_frame(); diff --git a/src/gua/renderer/PostFXPass.cpp b/src/gua/renderer/PostFXPass.cpp index 871e3df6b..001608df6 100644 --- a/src/gua/renderer/PostFXPass.cpp +++ b/src/gua/renderer/PostFXPass.cpp @@ -31,6 +31,8 @@ #include #include +#include + #define LUMINANCE_MAP_SIZE 512 namespace gua { diff --git a/src/gua/renderer/Serializer.cpp b/src/gua/renderer/Serializer.cpp index b2dfff504..5392a7721 100644 --- a/src/gua/renderer/Serializer.cpp +++ b/src/gua/renderer/Serializer.cpp @@ -52,10 +52,7 @@ Serializer::Serializer() : data_(nullptr), current_camera_("", "", ""), current_render_mask_(""), - current_frustum_(math::mat4::identity(), - math::mat4::identity(), - 0.f, - 0.f), + current_frustum_(), draw_bounding_boxes_(false), draw_rays_(false), enable_frustum_culling_(false) {} diff --git a/src/gua/renderer/ShadowMap.cpp b/src/gua/renderer/ShadowMap.cpp new file mode 100644 index 000000000..bb66b12bc --- /dev/null +++ b/src/gua/renderer/ShadowMap.cpp @@ -0,0 +1,330 @@ +/****************************************************************************** + * guacamole - delicious VR * + * * + * Copyright: (c) 2011-2013 Bauhaus-Universität Weimar * + * Contact: felix.lauer@uni-weimar.de / simon.schneegans@uni-weimar.de * + * * + * This program is free software: you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the Free * + * Software Foundation, either version 3 of the License, or (at your option) * + * any later version. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * + * for more details. * + * * + * You should have received a copy of the GNU General Public License along * + * with this program. If not, see . * + * * + ******************************************************************************/ + +// class header +#include + +// guacamole headers +#include +#include +#include +#include +#include +#include +#include + +namespace gua { + +//////////////////////////////////////////////////////////////////////////////// + +ShadowMap::ShadowMap(Pipeline* pipeline) + : serializer_(new Serializer), + pipeline_(pipeline), + mesh_shader_(new ShadowMapMeshShader), + nurbs_shader_(nullptr /*new ShadowMapNURBSShader*/), + buffer_(nullptr), + projection_view_matrices_() { +} + + +//////////////////////////////////////////////////////////////////////////////// + +ShadowMap::~ShadowMap() { + if (mesh_shader_) + delete mesh_shader_; + if (nurbs_shader_) + delete nurbs_shader_; + if (serializer_) + delete serializer_; +} + +//////////////////////////////////////////////////////////////////////////////// + +void ShadowMap::print_shaders(std::string const& directory, + std::string const& name) const { + mesh_shader_->save_to_file(directory, name + "/shadow/mesh"); + nurbs_shader_->save_to_file(directory, name + "/shadow/nurbs"); +} + +//////////////////////////////////////////////////////////////////////////////// + +bool ShadowMap::pre_compile_shaders(RenderContext const& ctx) { + + bool success(false); + + if (mesh_shader_) success = mesh_shader_->upload_to(ctx); + if (success && nurbs_shader_) success = nurbs_shader_->upload_to(ctx); + + return success; +} + + +//////////////////////////////////////////////////////////////////////////////// + +void ShadowMap::update_members(RenderContext const & ctx, unsigned map_size) { + //check whether shadow map size is sufficient + if (buffer_ && buffer_->width() < map_size) { + buffer_->remove_buffers(ctx); + delete buffer_; + buffer_ = nullptr; + } + + if (!buffer_) { + scm::gl::sampler_state_desc state; + state._compare_mode = scm::gl::TEXCOMPARE_COMPARE_REF_TO_TEXTURE; + + buffer_ = new GBuffer({{ BufferComponent::DEPTH_16, state }}, map_size, map_size); + buffer_->create(ctx); + } + + // let derived class render all geometries + if (!depth_stencil_state_) + depth_stencil_state_ = + ctx.render_device->create_depth_stencil_state(true, true); + + if (!rasterizer_state_) + rasterizer_state_ = ctx.render_device + ->create_rasterizer_state(scm::gl::FILL_SOLID, scm::gl::CULL_NONE); +} + +//////////////////////////////////////////////////////////////////////////////// + +void ShadowMap::render_geometry(RenderContext const & ctx, Frustum const& shadow_frustum, Camera const& scene_camera, unsigned cascade) { + SerializedScene scene; + scene.frustum = shadow_frustum; + serializer_->check(&scene, + pipeline_->get_current_graph(), + Camera("", "", scene_camera.render_mask), + false, + false, + true); + + projection_view_matrices_[cascade] = + shadow_frustum.get_projection() * shadow_frustum.get_view(); + + mesh_shader_->set_uniform( + ctx, projection_view_matrices_[cascade], "gua_projection_view_matrix"); + + + + for (auto const& node : scene.meshnodes_) { + auto geometry = GeometryDatabase::instance()->lookup(node.data.get_geometry()); + + if (geometry) { + mesh_shader_->set_uniform( + ctx, node.transform, "gua_model_matrix"); + geometry->draw(ctx); + } + } +} + +//////////////////////////////////////////////////////////////////////////////// + +void ShadowMap::render(RenderContext const& ctx, + Camera const& scene_camera, + math::mat4 const& transform, + unsigned map_size) { + + // init members + update_members(ctx, map_size); + projection_view_matrices_ = std::vector(1); + + buffer_->bind(ctx); + buffer_->clear_depth_stencil_buffer(ctx); + + ctx.render_context->set_depth_stencil_state(depth_stencil_state_); + ctx.render_context->set_rasterizer_state(rasterizer_state_); + + + ctx.render_context->set_viewport(scm::gl::viewport( + math::vec2(0, 0), + math::vec2(map_size, map_size))); + + // calculate light frustum + math::mat4 screen_transform(scm::math::make_translation(0.f, 0.f, -1.f)); + screen_transform = transform * screen_transform; + + Frustum shadow_frustum = Frustum::perspective(transform, + screen_transform, + pipeline_->config.near_clip(), + pipeline_->config.far_clip()); + + + + // render geometries + mesh_shader_->use(ctx); + render_geometry(ctx, shadow_frustum, scene_camera, 0); + mesh_shader_->unuse(ctx); + + ctx.render_context->reset_state_objects(); + + buffer_->unbind(ctx); +} + +//////////////////////////////////////////////////////////////////////////////// + +void ShadowMap::render_cascaded(RenderContext const& ctx, + Frustum const& scene_frustum, + Camera const& scene_camera, + math::mat4 const& transform, + unsigned map_size, float split_1, + float split_2, float split_3) { + + update_members(ctx, map_size*2); + projection_view_matrices_ = std::vector(4); + + buffer_->bind(ctx); + buffer_->clear_depth_stencil_buffer(ctx); + + ctx.render_context->set_depth_stencil_state(depth_stencil_state_); + ctx.render_context->set_rasterizer_state(rasterizer_state_); + + std::vector splits({ + pipeline_->config.near_clip(), + split_1, split_2, split_3, + pipeline_->config.far_clip() + }); + + if (pipeline_->config.near_clip() >= split_1 || pipeline_->config.far_clip() <= split_3) { + WARNING("Splits of cascaded shadow maps are not inside clipping range! Fallback to equidistant splits used."); + float clipping_range(pipeline_->config.far_clip() - pipeline_->config.near_clip()); + splits = { + pipeline_->config.near_clip(), + pipeline_->config.near_clip() + clipping_range * 0.25f, + pipeline_->config.near_clip() + clipping_range * 0.5f, + pipeline_->config.near_clip() + clipping_range * 0.75f, + pipeline_->config.far_clip() + }; + } + + for (int y(0); y<2; ++y) { + for (int x(0); x<2; ++x) { + + int cascade(y*2 + x); + + ctx.render_context->set_viewport(scm::gl::viewport( + math::vec2(x * map_size, y * map_size), + math::vec2(map_size, map_size))); + + Frustum cropped_frustum(Frustum::perspective( + scene_frustum.get_camera_transform(), + scene_frustum.get_screen_transform(), + splits[cascade], splits[cascade+1] + )); + + auto cropped_frustum_corners(cropped_frustum.get_corners()); + math::BoundingBox extends_in_sun_space; + + auto inverse_sun_transform(scm::math::inverse(transform)); + for (auto const& corner: cropped_frustum_corners) { + auto corner_in_sun_space(inverse_sun_transform * corner); + extends_in_sun_space.expandBy(corner_in_sun_space); + } + + auto size(extends_in_sun_space.max - extends_in_sun_space.min); + + auto center(math::vec3((extends_in_sun_space.min[0] + extends_in_sun_space.max[0])/2, + (extends_in_sun_space.min[1] + extends_in_sun_space.max[1])/2, + extends_in_sun_space.max[2]+50)); + + auto screen_in_sun_space(scm::math::make_translation(center) * scm::math::make_scale(size[0], size[1], 1.0f)); + + auto sun_screen_transform(transform * screen_in_sun_space); + auto sun_camera_transform(scm::math::make_translation(sun_screen_transform.column(3)[0], sun_screen_transform.column(3)[1], sun_screen_transform.column(3)[2])); + auto sun_camera_depth(transform * math::vec4(0, 0, size[2]+50, 0.0f)); + + auto shadow_frustum( + Frustum::orthographic( + sun_camera_transform, + sun_screen_transform, + 0, + scm::math::length(sun_camera_depth) + ) + ); + + + // // render geometries + mesh_shader_->use(ctx); + render_geometry(ctx, shadow_frustum, scene_camera, cascade); + mesh_shader_->unuse(ctx); + } + } + + + + + // for (int y(0); y<2; ++y) { + // for (int x(0); x<2; ++x) { + + // int cascade(y*2 + x); + + // ctx.render_context->set_viewport(scm::gl::viewport( + // math::vec2(x * map_size, y * map_size), + // math::vec2(map_size, map_size))); + + // Frustum cropped_frustum(Frustum::perspective( + // scene_frustum.get_camera_transform(), + // scene_frustum.get_screen_transform(), + // splits[cascade], splits[cascade+1] + // )); + + // auto cropped_frustum_corners(cropped_frustum.get_corners()); + // math::BoundingBox extends_in_sun_space; + + // auto inverse_sun_transform(scm::math::inverse(transform)); + // for (auto const& corner: cropped_frustum_corners) { + // auto corner_in_sun_space(inverse_sun_transform * corner); + // extends_in_sun_space.expandBy(corner_in_sun_space); + // } + + // auto center((extends_in_sun_space.min + extends_in_sun_space.max)/2); + // auto size(extends_in_sun_space.max - extends_in_sun_space.min); + // auto screen_in_sun_space(scm::math::make_translation(center) * scm::math::make_scale(size[0], size[1], 1.0f)); + + // auto sun_screen_transform(transform * screen_in_sun_space); + // auto sun_camera_transform(transform * scm::math::make_translation(center)); + // auto sun_camera_depth(transform * math::vec4(0, 0, size[2], 0.0f)); + + // auto shadow_frustum( + // Frustum::orthographic( + // sun_camera_transform, + // sun_screen_transform, + // 0, + // scm::math::length(sun_camera_depth) + // ) + // ); + + // // // render geometries + // mesh_shader_->use(ctx); + // render_geometry(ctx, shadow_frustum, scene_camera, cascade); + // mesh_shader_->unuse(ctx); + // } + // } + + ctx.render_context->reset_state_objects(); + + buffer_->unbind(ctx); + +} + +//////////////////////////////////////////////////////////////////////////////// + +} From d83ac5457bcd06d50378d8d7475796410d6bb3e3 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Wed, 29 Jan 2014 13:43:53 +0100 Subject: [PATCH 084/146] removed some commtents --- include/gua/renderer/Frustum.hpp | 2 +- src/gua/renderer/ShadowMap.cpp | 52 -------------------------------- 2 files changed, 1 insertion(+), 53 deletions(-) diff --git a/include/gua/renderer/Frustum.hpp b/include/gua/renderer/Frustum.hpp index 2795a1b94..aaa425ead 100644 --- a/include/gua/renderer/Frustum.hpp +++ b/include/gua/renderer/Frustum.hpp @@ -52,7 +52,7 @@ class Frustum { inline math::vec3 get_camera_position() const { return math::vec3(camera_transform_.column(3)[0], camera_transform_.column(3)[1], - camera_transform_.column(3)[2]);; + camera_transform_.column(3)[2]); } inline math::mat4 const& get_camera_transform() const { return camera_transform_; } diff --git a/src/gua/renderer/ShadowMap.cpp b/src/gua/renderer/ShadowMap.cpp index bb66b12bc..09eb4da7f 100644 --- a/src/gua/renderer/ShadowMap.cpp +++ b/src/gua/renderer/ShadowMap.cpp @@ -260,7 +260,6 @@ void ShadowMap::render_cascaded(RenderContext const& ctx, ) ); - // // render geometries mesh_shader_->use(ctx); render_geometry(ctx, shadow_frustum, scene_camera, cascade); @@ -268,57 +267,6 @@ void ShadowMap::render_cascaded(RenderContext const& ctx, } } - - - - // for (int y(0); y<2; ++y) { - // for (int x(0); x<2; ++x) { - - // int cascade(y*2 + x); - - // ctx.render_context->set_viewport(scm::gl::viewport( - // math::vec2(x * map_size, y * map_size), - // math::vec2(map_size, map_size))); - - // Frustum cropped_frustum(Frustum::perspective( - // scene_frustum.get_camera_transform(), - // scene_frustum.get_screen_transform(), - // splits[cascade], splits[cascade+1] - // )); - - // auto cropped_frustum_corners(cropped_frustum.get_corners()); - // math::BoundingBox extends_in_sun_space; - - // auto inverse_sun_transform(scm::math::inverse(transform)); - // for (auto const& corner: cropped_frustum_corners) { - // auto corner_in_sun_space(inverse_sun_transform * corner); - // extends_in_sun_space.expandBy(corner_in_sun_space); - // } - - // auto center((extends_in_sun_space.min + extends_in_sun_space.max)/2); - // auto size(extends_in_sun_space.max - extends_in_sun_space.min); - // auto screen_in_sun_space(scm::math::make_translation(center) * scm::math::make_scale(size[0], size[1], 1.0f)); - - // auto sun_screen_transform(transform * screen_in_sun_space); - // auto sun_camera_transform(transform * scm::math::make_translation(center)); - // auto sun_camera_depth(transform * math::vec4(0, 0, size[2], 0.0f)); - - // auto shadow_frustum( - // Frustum::orthographic( - // sun_camera_transform, - // sun_screen_transform, - // 0, - // scm::math::length(sun_camera_depth) - // ) - // ); - - // // // render geometries - // mesh_shader_->use(ctx); - // render_geometry(ctx, shadow_frustum, scene_camera, cascade); - // mesh_shader_->unuse(ctx); - // } - // } - ctx.render_context->reset_state_objects(); buffer_->unbind(ctx); From bf12cbfabc9c6b36a7316cf8ca75c47fdd63a53e Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Wed, 29 Jan 2014 15:26:46 +0100 Subject: [PATCH 085/146] fixed sun godrays --- resources/shaders/uber_shaders/postfx/godrays.frag | 2 +- resources/shaders/uber_shaders/postfx/godrays.vert | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/shaders/uber_shaders/postfx/godrays.frag b/resources/shaders/uber_shaders/postfx/godrays.frag index 804cd330b..094092118 100644 --- a/resources/shaders/uber_shaders/postfx/godrays.frag +++ b/resources/shaders/uber_shaders/postfx/godrays.frag @@ -41,7 +41,7 @@ layout(location=0) out vec3 gua_out_color; subroutine( GetColorType ) float get_color_clamped(vec2 texcoords) { float depth = texture2D( gua_get_float_sampler(gua_ray_texture), texcoords).r * 2 -1; - float intensity = depth > gua_light_position_screen_space.z ? 1.0 : 0.0; + float intensity = depth >= gua_light_position_screen_space.z ? 1.0 : 0.0; intensity *= max(0.0, 1.0-length((gua_quad_coords - gua_light_position_screen_space.xy * 0.5 - 0.5)/vec2(1.0, gua_aspect_ratio))); return pow(intensity, 15.0); } diff --git a/resources/shaders/uber_shaders/postfx/godrays.vert b/resources/shaders/uber_shaders/postfx/godrays.vert index e6273100c..4189d257d 100644 --- a/resources/shaders/uber_shaders/postfx/godrays.vert +++ b/resources/shaders/uber_shaders/postfx/godrays.vert @@ -44,6 +44,7 @@ void gua_calculate_by_direction() { // hide sun on wrong side gua_light_position_screen_space = vec3(-10); } else { + tmp = gua_projection_matrix * tmp; gua_light_position_screen_space = (tmp/tmp.w).xyz; gua_light_position_screen_space = gua_light_position_screen_space/gua_light_position_screen_space.z; From 68567eb01d1d06b2c522631c93d7670c4acfa8b1 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Wed, 29 Jan 2014 15:59:25 +0100 Subject: [PATCH 086/146] added orthographic camera support --- include/gua/renderer/Camera.hpp | 20 ++++++++++++++------ src/gua/renderer/Pipeline.cpp | 15 +++++++++++---- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/include/gua/renderer/Camera.hpp b/include/gua/renderer/Camera.hpp index 7636ebe1c..bfea56921 100644 --- a/include/gua/renderer/Camera.hpp +++ b/include/gua/renderer/Camera.hpp @@ -36,13 +36,20 @@ namespace gua { */ struct GUA_DLL Camera { - Camera(std::string const& eye_l = "unknown_left_eye", - std::string const& eye_r = "unknown_right_eye", - std::string const& screen_l = "unknown_left_screen", - std::string const& screen_r = "unknown_right_screen", - std::string const& g = "scene_graph", std::string const& m = "") + + enum ProjectionMode { + PERSPECTIVE, + ORTHOGRAPHIC + }; + + Camera(std::string const& eye_l = "unknown_left_eye", + std::string const& eye_r = "unknown_right_eye", + std::string const& screen_l = "unknown_left_screen", + std::string const& screen_r = "unknown_right_screen", + std::string const& g = "scene_graph", std::string const& m = "", + ProjectionMode p = PERSPECTIVE) : eye_l(eye_l), eye_r(eye_r), screen_l(screen_l), screen_r(screen_r), - scene_graph(g), render_mask(m) {} + scene_graph(g), render_mask(m), mode(p) {} std::string eye_l; std::string eye_r; @@ -50,6 +57,7 @@ struct GUA_DLL Camera { std::string screen_r; std::string scene_graph; std::string render_mask; + ProjectionMode mode; }; } diff --git a/src/gua/renderer/Pipeline.cpp b/src/gua/renderer/Pipeline.cpp index 9faef1562..77a51c2a7 100644 --- a/src/gua/renderer/Pipeline.cpp +++ b/src/gua/renderer/Pipeline.cpp @@ -232,10 +232,17 @@ void Pipeline::process(std::vector> const& sce return; } - current_scenes_[0].frustum = Frustum::perspective(eye->get_world_transform(), - screen->get_scaled_world_transform(), - config.near_clip(), - config.far_clip()); + if (config.camera().mode == Camera::ProjectionMode::PERSPECTIVE) { + current_scenes_[0].frustum = Frustum::perspective(eye->get_world_transform(), + screen->get_scaled_world_transform(), + config.near_clip(), + config.far_clip()); + } else { + current_scenes_[0].frustum = Frustum::orthographic(eye->get_world_transform(), + screen->get_scaled_world_transform(), + config.near_clip(), + config.far_clip()); + } serializer_->check(¤t_scenes_[0], current_graph_, From 1a97f7f600d6e2914119c99cece853e447e22c1f Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Wed, 29 Jan 2014 16:07:05 +0100 Subject: [PATCH 087/146] added possiblity to disable pipelines --- include/gua/renderer/Pipeline.hpp | 3 +++ src/gua/renderer/Pipeline.cpp | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/include/gua/renderer/Pipeline.hpp b/include/gua/renderer/Pipeline.hpp index 4e1711a1e..886f7376d 100644 --- a/include/gua/renderer/Pipeline.hpp +++ b/include/gua/renderer/Pipeline.hpp @@ -50,6 +50,9 @@ struct PipelineConfiguration { // camera for this pipeline GUA_ADD_PROPERTY(Camera, camera, Camera()); + // if set to false, this pipeline won't render anything + GUA_ADD_PROPERTY(bool, enabled, true); + // the final image of this pipeline will be stored in the texture database // with this name. if enable_stereo is set to true, two images with postfixes // _left and _right will be stored diff --git a/src/gua/renderer/Pipeline.cpp b/src/gua/renderer/Pipeline.cpp index 77a51c2a7..998c42408 100644 --- a/src/gua/renderer/Pipeline.cpp +++ b/src/gua/renderer/Pipeline.cpp @@ -114,6 +114,10 @@ void Pipeline::process(std::vector> const& sce float application_fps, float rendering_fps) { + if (!config.get_enabled()) { + return; + } + std::unique_lock lock(upload_mutex_); if (ShadingModel::current_revision != last_shading_model_revision_) { From edf3c9594fbf48653cccacc182ce2d8eaaa5c009 Mon Sep 17 00:00:00 2001 From: thelaui Date: Wed, 29 Jan 2014 16:25:46 +0100 Subject: [PATCH 088/146] added new SunLightNode parameters --- include/gua/renderer/ShadowMap.hpp | 9 +++++++-- include/gua/scenegraph/SunLightNode.hpp | 16 +++++++++------- src/gua/renderer/LightingPass.cpp | 19 ++++++++++++++++++- src/gua/renderer/ShadowMap.cpp | 16 ++++++++-------- 4 files changed, 42 insertions(+), 18 deletions(-) diff --git a/include/gua/renderer/ShadowMap.hpp b/include/gua/renderer/ShadowMap.hpp index cbf842600..9694507f1 100644 --- a/include/gua/renderer/ShadowMap.hpp +++ b/include/gua/renderer/ShadowMap.hpp @@ -58,8 +58,13 @@ class ShadowMap { Frustum const& scene_frustum, Camera const& scene_camera, math::mat4 const& transform, - unsigned map_size, float split_1, - float split_2, float split_3); + unsigned map_size, + float split_0, + float split_1, + float split_2, + float split_3, + float split_4, + float near_clipping_in_sun_direction); void print_shaders(std::string const& directory, std::string const& name) const; diff --git a/include/gua/scenegraph/SunLightNode.hpp b/include/gua/scenegraph/SunLightNode.hpp index cc6f876d7..4fdfe0f96 100644 --- a/include/gua/scenegraph/SunLightNode.hpp +++ b/include/gua/scenegraph/SunLightNode.hpp @@ -41,13 +41,15 @@ class GUA_DLL SunLightNode : public Node { public: struct Configuration { - GUA_ADD_PROPERTY(utils::Color3f, color, utils::Color3f(1.f, 1.f, 1.f)); - GUA_ADD_PROPERTY(bool, enable_shadows, false); - GUA_ADD_PROPERTY(bool, enable_godrays, false); - GUA_ADD_PROPERTY(bool, enable_diffuse_shading, true); - GUA_ADD_PROPERTY(bool, enable_specular_shading, true); - GUA_ADD_PROPERTY(unsigned, shadow_map_size, 512); - GUA_ADD_PROPERTY(float, shadow_offset, 0.001f); + GUA_ADD_PROPERTY(utils::Color3f, color, utils::Color3f(1.f, 1.f, 1.f)); + GUA_ADD_PROPERTY(bool, enable_shadows, false); + GUA_ADD_PROPERTY(bool, enable_godrays, false); + GUA_ADD_PROPERTY(bool, enable_diffuse_shading, true); + GUA_ADD_PROPERTY(bool, enable_specular_shading, true); + GUA_ADD_PROPERTY(unsigned, shadow_map_size, 512); + GUA_ADD_PROPERTY(float, shadow_offset, 0.001f); + GUA_ADD_PROPERTY(std::vector, shadow_cascaded_splits, std::vector({0,0,0,0,0})); + GUA_ADD_PROPERTY(float, shadow_near_clipping_in_sun_direction, 0); }; Configuration data; diff --git a/src/gua/renderer/LightingPass.cpp b/src/gua/renderer/LightingPass.cpp index 401327a75..da64c6bc7 100644 --- a/src/gua/renderer/LightingPass.cpp +++ b/src/gua/renderer/LightingPass.cpp @@ -150,7 +150,24 @@ void LightingPass::rendering(SerializedScene const& scene, target->unbind(ctx); ctx.render_context->reset_state_objects(); - shadow_map_.render_cascaded(ctx, scene.frustum, camera, light.transform, light.data.get_shadow_map_size(), 2, 7, 50); + float split_0(0.f), split_1(0.f), split_2(0.f), split_3(0.f), split_4(0.f); + + if (light.data.get_shadow_cascaded_splits().size() == 5) { + + split_0 = light.data.get_shadow_cascaded_splits()[0]; + split_1 = light.data.get_shadow_cascaded_splits()[1]; + split_2 = light.data.get_shadow_cascaded_splits()[2]; + split_3 = light.data.get_shadow_cascaded_splits()[3]; + split_4 = light.data.get_shadow_cascaded_splits()[4]; + } else { + WARNING("Exactly 5 splits have to be defined for cascaded shadow maps!"); + } + + shadow_map_.render_cascaded(ctx, scene.frustum, camera, + light.transform, + light.data.get_shadow_map_size(), + split_0, split_1, split_2, split_3, split_4, + light.data.get_shadow_near_clipping_in_sun_direction()); shader_->use(ctx); target->bind(ctx); diff --git a/src/gua/renderer/ShadowMap.cpp b/src/gua/renderer/ShadowMap.cpp index 09eb4da7f..82133595c 100644 --- a/src/gua/renderer/ShadowMap.cpp +++ b/src/gua/renderer/ShadowMap.cpp @@ -185,8 +185,10 @@ void ShadowMap::render_cascaded(RenderContext const& ctx, Frustum const& scene_frustum, Camera const& scene_camera, math::mat4 const& transform, - unsigned map_size, float split_1, - float split_2, float split_3) { + unsigned map_size, float split_0, + float split_1, float split_2, + float split_3, float split_4, + float near_clipping_in_sun_direction) { update_members(ctx, map_size*2); projection_view_matrices_ = std::vector(4); @@ -198,12 +200,10 @@ void ShadowMap::render_cascaded(RenderContext const& ctx, ctx.render_context->set_rasterizer_state(rasterizer_state_); std::vector splits({ - pipeline_->config.near_clip(), - split_1, split_2, split_3, - pipeline_->config.far_clip() + split_0, split_1, split_2, split_3, split_4 }); - if (pipeline_->config.near_clip() >= split_1 || pipeline_->config.far_clip() <= split_3) { + if (pipeline_->config.near_clip() > split_0 || pipeline_->config.far_clip() < split_4) { WARNING("Splits of cascaded shadow maps are not inside clipping range! Fallback to equidistant splits used."); float clipping_range(pipeline_->config.far_clip() - pipeline_->config.near_clip()); splits = { @@ -243,13 +243,13 @@ void ShadowMap::render_cascaded(RenderContext const& ctx, auto center(math::vec3((extends_in_sun_space.min[0] + extends_in_sun_space.max[0])/2, (extends_in_sun_space.min[1] + extends_in_sun_space.max[1])/2, - extends_in_sun_space.max[2]+50)); + extends_in_sun_space.max[2] + near_clipping_in_sun_direction)); auto screen_in_sun_space(scm::math::make_translation(center) * scm::math::make_scale(size[0], size[1], 1.0f)); auto sun_screen_transform(transform * screen_in_sun_space); auto sun_camera_transform(scm::math::make_translation(sun_screen_transform.column(3)[0], sun_screen_transform.column(3)[1], sun_screen_transform.column(3)[2])); - auto sun_camera_depth(transform * math::vec4(0, 0, size[2]+50, 0.0f)); + auto sun_camera_depth(transform * math::vec4(0, 0, size[2] + near_clipping_in_sun_direction, 0.0f)); auto shadow_frustum( Frustum::orthographic( From 4e913136fcfc3c32c0c3991b0e20503dbc036abc Mon Sep 17 00:00:00 2001 From: thelaui Date: Wed, 29 Jan 2014 17:11:11 +0100 Subject: [PATCH 089/146] adapted defaults for SunLightNode --- include/gua/scenegraph/SunLightNode.hpp | 6 +++--- src/gua/renderer/LightingPass.cpp | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/include/gua/scenegraph/SunLightNode.hpp b/include/gua/scenegraph/SunLightNode.hpp index 4fdfe0f96..bfdea6f8a 100644 --- a/include/gua/scenegraph/SunLightNode.hpp +++ b/include/gua/scenegraph/SunLightNode.hpp @@ -46,10 +46,10 @@ class GUA_DLL SunLightNode : public Node { GUA_ADD_PROPERTY(bool, enable_godrays, false); GUA_ADD_PROPERTY(bool, enable_diffuse_shading, true); GUA_ADD_PROPERTY(bool, enable_specular_shading, true); - GUA_ADD_PROPERTY(unsigned, shadow_map_size, 512); + GUA_ADD_PROPERTY(unsigned, shadow_map_size, 1024); GUA_ADD_PROPERTY(float, shadow_offset, 0.001f); - GUA_ADD_PROPERTY(std::vector, shadow_cascaded_splits, std::vector({0,0,0,0,0})); - GUA_ADD_PROPERTY(float, shadow_near_clipping_in_sun_direction, 0); + GUA_ADD_PROPERTY(std::vector, shadow_cascaded_splits, std::vector({0.1f, 2, 10, 50, 100.f})); + GUA_ADD_PROPERTY(float, shadow_near_clipping_in_sun_direction, 100.f); }; Configuration data; diff --git a/src/gua/renderer/LightingPass.cpp b/src/gua/renderer/LightingPass.cpp index da64c6bc7..4143ddc04 100644 --- a/src/gua/renderer/LightingPass.cpp +++ b/src/gua/renderer/LightingPass.cpp @@ -159,6 +159,7 @@ void LightingPass::rendering(SerializedScene const& scene, split_2 = light.data.get_shadow_cascaded_splits()[2]; split_3 = light.data.get_shadow_cascaded_splits()[3]; split_4 = light.data.get_shadow_cascaded_splits()[4]; + } else { WARNING("Exactly 5 splits have to be defined for cascaded shadow maps!"); } From d16efcaea08004c38c03ff8ed125b601130b4efd Mon Sep 17 00:00:00 2001 From: Andreas Bernstein Date: Thu, 30 Jan 2014 23:13:34 +0100 Subject: [PATCH 090/146] We need remove_child for avango's distribution. --- include/gua/scenegraph/Node.hpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/include/gua/scenegraph/Node.hpp b/include/gua/scenegraph/Node.hpp index 9467b022a..ab13b8ab9 100644 --- a/include/gua/scenegraph/Node.hpp +++ b/include/gua/scenegraph/Node.hpp @@ -126,6 +126,15 @@ class GUA_DLL Node { return new_node; } + /** + * Removes a child. + * + * This removes a Node from the Node's children list. + * + * \param child The Node to be removed. + */ + void remove_child(std::shared_ptr const& child); + /** * Returns the Node's children list. * @@ -313,15 +322,6 @@ class GUA_DLL Node { private: - /** - * Removes a child. - * - * This removes a Node from the Node's children list. - * - * \param child The Node to be removed. - */ - void remove_child(std::shared_ptr const& child); - /** * Sets the Node's parent. * From f3475e9fa4836a540b8f9477ede631beaacacf5c Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Fri, 31 Jan 2014 10:58:33 +0100 Subject: [PATCH 091/146] shadows obey masks now --- include/gua/renderer/Serializer.hpp | 3 +-- resources/shaders/uber_shaders/lighting/lighting.frag | 2 +- src/gua/renderer/Pipeline.cpp | 6 +++--- src/gua/renderer/Serializer.cpp | 6 ++---- src/gua/renderer/ShadowMap.cpp | 2 +- 5 files changed, 8 insertions(+), 11 deletions(-) diff --git a/include/gua/renderer/Serializer.hpp b/include/gua/renderer/Serializer.hpp index 27e3caf73..412a177ff 100644 --- a/include/gua/renderer/Serializer.hpp +++ b/include/gua/renderer/Serializer.hpp @@ -61,7 +61,7 @@ class Serializer : public NodeVisitor { */ void check(SerializedScene* output, SceneGraph const* scene_graph, - Camera const& camera, + std::string const& render_mask, bool draw_bounding_boxes, bool draw_rays, bool enable_frustum_culling); @@ -163,7 +163,6 @@ class Serializer : public NodeVisitor { void visit_children(Node* node); Frustum current_frustum_; - Camera current_camera_; Mask current_render_mask_; SerializedScene* data_; diff --git a/resources/shaders/uber_shaders/lighting/lighting.frag b/resources/shaders/uber_shaders/lighting/lighting.frag index d4ac2c839..ff6b795a9 100644 --- a/resources/shaders/uber_shaders/lighting/lighting.frag +++ b/resources/shaders/uber_shaders/lighting/lighting.frag @@ -226,7 +226,7 @@ void gua_calculate_sun_light() { shadow = gua_get_shadow(gua_lightinfo5, vec2(1, 0)); } else if (gua_is_inside_frustum(gua_light_shadow_map_projection_view_matrix_2, position)) { shadow = gua_get_shadow(gua_lightinfo6, vec2(0, 1)); - } else { + } else if (gua_is_inside_frustum(gua_light_shadow_map_projection_view_matrix_3, position)) { shadow = gua_get_shadow(gua_lightinfo7, vec2(1, 1)); } diff --git a/src/gua/renderer/Pipeline.cpp b/src/gua/renderer/Pipeline.cpp index 998c42408..251d22de4 100644 --- a/src/gua/renderer/Pipeline.cpp +++ b/src/gua/renderer/Pipeline.cpp @@ -250,7 +250,7 @@ void Pipeline::process(std::vector> const& sce serializer_->check(¤t_scenes_[0], current_graph_, - config.camera(), + config.camera().render_mask, config.enable_bbox_display(), config.enable_ray_display(), config.enable_frustum_culling()); @@ -295,14 +295,14 @@ void Pipeline::process(std::vector> const& sce serializer_->check(¤t_scenes_[0], current_graph_, - config.camera(), + config.camera().render_mask, config.enable_bbox_display(), config.enable_ray_display(), config.enable_frustum_culling()); serializer_->check(¤t_scenes_[1], current_graph_, - config.camera(), + config.camera().render_mask, config.enable_bbox_display(), config.enable_ray_display(), config.enable_frustum_culling()); diff --git a/src/gua/renderer/Serializer.cpp b/src/gua/renderer/Serializer.cpp index 5392a7721..7b81c6fb4 100644 --- a/src/gua/renderer/Serializer.cpp +++ b/src/gua/renderer/Serializer.cpp @@ -50,7 +50,6 @@ namespace gua { Serializer::Serializer() : data_(nullptr), - current_camera_("", "", ""), current_render_mask_(""), current_frustum_(), draw_bounding_boxes_(false), @@ -61,7 +60,7 @@ Serializer::Serializer() void Serializer::check(SerializedScene* output, SceneGraph const* scene_graph, - Camera const& camera, + std::string const& render_mask, bool draw_bounding_boxes, bool draw_rays, bool enable_frustum_culling) { @@ -116,8 +115,7 @@ void Serializer::check(SerializedScene* output, enable_frustum_culling_ = enable_frustum_culling; - current_camera_ = camera; - current_render_mask_ = Mask(current_camera_.render_mask); + current_render_mask_ = Mask(render_mask); current_frustum_ = output->frustum; scene_graph->accept(*this); diff --git a/src/gua/renderer/ShadowMap.cpp b/src/gua/renderer/ShadowMap.cpp index 82133595c..59a53dc24 100644 --- a/src/gua/renderer/ShadowMap.cpp +++ b/src/gua/renderer/ShadowMap.cpp @@ -112,7 +112,7 @@ void ShadowMap::render_geometry(RenderContext const & ctx, Frustum const& shadow scene.frustum = shadow_frustum; serializer_->check(&scene, pipeline_->get_current_graph(), - Camera("", "", scene_camera.render_mask), + scene_camera.render_mask, false, false, true); From 7f2fc1dff585557c39e6d82fb1e87094f30f3d56 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Sat, 1 Feb 2014 23:58:44 +0100 Subject: [PATCH 092/146] added background texture modes --- include/gua/renderer/Pipeline.hpp | 20 +++++++++++++------ .../uber_shaders/common/get_depth.glsl | 9 +++++++++ .../uber_shaders/common/get_position.glsl | 12 +++++++++++ .../shaders/uber_shaders/final/final.frag | 20 ++++++++++++++----- src/gua/renderer/FinalPass.cpp | 6 +++--- src/gua/renderer/Pipeline.cpp | 3 +++ 6 files changed, 56 insertions(+), 14 deletions(-) diff --git a/include/gua/renderer/Pipeline.hpp b/include/gua/renderer/Pipeline.hpp index 886f7376d..1432d1e53 100644 --- a/include/gua/renderer/Pipeline.hpp +++ b/include/gua/renderer/Pipeline.hpp @@ -45,6 +45,12 @@ class FinalUberShader; class PostFXShader; class Serializer; +enum BackgroundMode { + COLOR = 0, + SKYMAP_TEXTURE = 1, + QUAD_TEXTURE = 2, +}; + struct PipelineConfiguration { // camera for this pipeline @@ -101,6 +107,7 @@ struct PipelineConfiguration { GUA_ADD_PROPERTY(utils::Color3f, fog_color, utils::Color3f()); // background image / color + GUA_ADD_PROPERTY(BackgroundMode, background_mode, BackgroundMode::SKYMAP_TEXTURE); GUA_ADD_PROPERTY(std::string, background_texture, ""); GUA_ADD_PROPERTY(utils::Color3f, background_color, utils::Color3f()); @@ -132,12 +139,13 @@ struct PipelineConfiguration { class GUA_DLL Pipeline { public: - enum PipelineStage { geometry = 0, - lighting = 1, - shading = 2, - compositing = 3, - postfx = 4 - }; + enum PipelineStage { + geometry = 0, + lighting = 1, + shading = 2, + compositing = 3, + postfx = 4 + }; public: diff --git a/resources/shaders/uber_shaders/common/get_depth.glsl b/resources/shaders/uber_shaders/common/get_depth.glsl index a0c4bba89..2cc88a159 100644 --- a/resources/shaders/uber_shaders/common/get_depth.glsl +++ b/resources/shaders/uber_shaders/common/get_depth.glsl @@ -6,3 +6,12 @@ float gua_get_depth() { vec2 frag_pos = gua_get_quad_coords(); return texture2D(gua_get_float_sampler(gua_depth_gbuffer_in), frag_pos).x * 2.0 - 1.0; } + +float gua_get_depth(sampler2D depth_texture, vec2 frag_pos) { + return texture2D(depth_texture, frag_pos).x * 2.0 - 1.0; +} + +float gua_get_depth(sampler2D depth_texture) { + vec2 frag_pos = gua_get_quad_coords(); + return texture2D(depth_texture, frag_pos).x * 2.0 - 1.0; +} diff --git a/resources/shaders/uber_shaders/common/get_position.glsl b/resources/shaders/uber_shaders/common/get_position.glsl index 1b06df21d..15275c817 100644 --- a/resources/shaders/uber_shaders/common/get_position.glsl +++ b/resources/shaders/uber_shaders/common/get_position.glsl @@ -8,3 +8,15 @@ vec3 gua_get_position(vec2 frag_pos) { vec3 gua_get_position() { return gua_get_position(gua_get_quad_coords()); } + +vec3 gua_get_position(sampler2D depth_texture, vec2 frag_pos) { + vec4 screen_space_pos = vec4(frag_pos * 2.0 - 1.0, gua_get_depth(depth_texture, frag_pos), 1.0); + vec4 h = gua_inverse_projection_view_matrix * screen_space_pos; + h /= h.w; + return h.xyz; +} + +vec3 gua_get_position(sampler2D depth_texture) { + return gua_get_position(depth_texture, gua_get_quad_coords()); +} + diff --git a/resources/shaders/uber_shaders/final/final.frag b/resources/shaders/uber_shaders/final/final.frag index aa6d723d3..d0a1a183a 100644 --- a/resources/shaders/uber_shaders/final/final.frag +++ b/resources/shaders/uber_shaders/final/final.frag @@ -34,8 +34,8 @@ uniform uvec2 gua_depth_gbuffer_in; @include "shaders/uber_shaders/common/gua_camera_uniforms.glsl" uniform vec3 gua_ambient_color; +uniform int gua_background_mode; uniform vec3 gua_background_color; -uniform bool gua_background_is_color; uniform uvec2 gua_background_texture; // material specific uniforms @@ -65,6 +65,11 @@ float gua_my_atan2(float a, float b) { } void gua_apply_background_texture() { + gua_float_gbuffer_out_0.rgb = texture2D( + gua_get_float_sampler(gua_background_texture), gua_quad_coords).xyz; +} + +void gua_apply_skymap_texture() { vec3 pos = gua_get_position(); vec3 view = normalize(pos - gua_camera_position); @@ -86,10 +91,15 @@ void gua_apply_background_color() { void main() { if (gua_get_material_id() == 0) { - if (gua_background_is_color) { - gua_apply_background_color(); - } else { - gua_apply_background_texture(); + switch (gua_background_mode) { + case 0: // color + gua_apply_background_color(); + break; + case 1: // skymap texture + gua_apply_skymap_texture(); + break; + default: // quad texture + gua_apply_background_texture(); } } else { diff --git a/src/gua/renderer/FinalPass.cpp b/src/gua/renderer/FinalPass.cpp index 33277d432..d85684056 100644 --- a/src/gua/renderer/FinalPass.cpp +++ b/src/gua/renderer/FinalPass.cpp @@ -69,10 +69,10 @@ void FinalPass::set_uniforms(SerializedScene const& scene, shader_->set_uniform( ctx, pipeline_->config.ambient_color(), "gua_ambient_color"); shader_->set_uniform(ctx, - pipeline_->config.background_texture().empty(), - "gua_background_is_color"); + static_cast(pipeline_->config.background_mode()), + "gua_background_mode"); - if (pipeline_->config.background_texture().empty()) + if (pipeline_->config.background_mode() == BackgroundMode::COLOR || pipeline_->config.background_texture() == "") shader_->set_uniform( ctx, pipeline_->config.background_color(), "gua_background_color"); else diff --git a/src/gua/renderer/Pipeline.cpp b/src/gua/renderer/Pipeline.cpp index 251d22de4..a6f464089 100644 --- a/src/gua/renderer/Pipeline.cpp +++ b/src/gua/renderer/Pipeline.cpp @@ -444,9 +444,12 @@ void Pipeline::create_buffers() { if (!config.get_enable_stereo()) { TextureDatabase::instance()->add(config.output_texture_name(), passes_[PipelineStage::postfx]->get_gbuffer()->get_eye_buffers()[0]->get_color_buffers(TYPE_FLOAT)[0]); + TextureDatabase::instance()->add(config.output_texture_name() + "_depth", passes_[PipelineStage::geometry]->get_gbuffer()->get_eye_buffers()[0]->get_depth_buffer()); } else { TextureDatabase::instance()->add(config.output_texture_name() + "_left", passes_[PipelineStage::postfx]->get_gbuffer()->get_eye_buffers()[0]->get_color_buffers(TYPE_FLOAT)[0]); TextureDatabase::instance()->add(config.output_texture_name() + "_right", passes_[PipelineStage::postfx]->get_gbuffer()->get_eye_buffers()[1]->get_color_buffers(TYPE_FLOAT)[0]); + TextureDatabase::instance()->add(config.output_texture_name() + "_depth_left", passes_[PipelineStage::geometry]->get_gbuffer()->get_eye_buffers()[0]->get_depth_buffer()); + TextureDatabase::instance()->add(config.output_texture_name() + "_depth_right", passes_[PipelineStage::geometry]->get_gbuffer()->get_eye_buffers()[1]->get_depth_buffer()); } From 0b796adfb64c38c978678b574f9a894aff39a942 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Tue, 4 Feb 2014 11:11:11 +0100 Subject: [PATCH 093/146] added global clipping plane option --- include/gua/renderer/Pipeline.hpp | 4 +++ include/gua/renderer/SerializedScene.hpp | 2 ++ .../uber_shaders/gbuffer/mesh/mesh.frag | 12 +++++++ src/gua/renderer/GBufferPass.cpp | 7 ++++ src/gua/renderer/Pipeline.cpp | 34 ++++++++++++++----- 5 files changed, 51 insertions(+), 8 deletions(-) diff --git a/include/gua/renderer/Pipeline.hpp b/include/gua/renderer/Pipeline.hpp index 1432d1e53..0b74c88a6 100644 --- a/include/gua/renderer/Pipeline.hpp +++ b/include/gua/renderer/Pipeline.hpp @@ -59,6 +59,10 @@ struct PipelineConfiguration { // if set to false, this pipeline won't render anything GUA_ADD_PROPERTY(bool, enabled, true); + // global clipping plane nothing + GUA_ADD_PROPERTY(bool, enable_global_clipping_plane, false); + GUA_ADD_PROPERTY(math::vec4, global_clipping_plane, math::vec4(0, 1, 0, 0)); + // the final image of this pipeline will be stored in the texture database // with this name. if enable_stereo is set to true, two images with postfixes // _left and _right will be stored diff --git a/include/gua/renderer/SerializedScene.hpp b/include/gua/renderer/SerializedScene.hpp index da0e8ffbe..404c82ab7 100644 --- a/include/gua/renderer/SerializedScene.hpp +++ b/include/gua/renderer/SerializedScene.hpp @@ -85,6 +85,8 @@ struct SerializedScene { * The frustum. */ Frustum frustum; + bool enable_global_clipping_plane; + math::vec4 global_clipping_plane; /** * All used materials. diff --git a/resources/shaders/uber_shaders/gbuffer/mesh/mesh.frag b/resources/shaders/uber_shaders/gbuffer/mesh/mesh.frag index 219b1e706..21ff03591 100644 --- a/resources/shaders/uber_shaders/gbuffer/mesh/mesh.frag +++ b/resources/shaders/uber_shaders/gbuffer/mesh/mesh.frag @@ -26,6 +26,8 @@ in vec3 gua_position_varying; @input_definition // uniforms +uniform bool gua_enable_global_clipping_plane; +uniform vec4 gua_global_clipping_plane; @include "shaders/uber_shaders/common/gua_camera_uniforms.glsl" // material specific uniforms @@ -37,6 +39,7 @@ in vec3 gua_position_varying; // methods --------------------------------------------------------------------- // global gua_* methods + vec2 gua_get_quad_coords() { return vec2(gl_FragCoord.x * gua_texel_width, gl_FragCoord.y * gua_texel_height); } @@ -57,11 +60,20 @@ void gua_set_position(vec3 world_position) { gl_FragDepth = (((gl_DepthRange.diff) * ndc) + gl_DepthRange.near + gl_DepthRange.far) / 2.0; } +void gua_clip_against_global_clipping_plane() { + if (gua_enable_global_clipping_plane) { + if (dot(gua_get_position(), gua_global_clipping_plane.xyz) + gua_global_clipping_plane.w < 0) { + discard; + } + } +} + // material specific methods @material_methods // main ------------------------------------------------------------------------ void main() { + gua_clip_against_global_clipping_plane(); gl_FragDepth = gl_FragCoord.z; diff --git a/src/gua/renderer/GBufferPass.cpp b/src/gua/renderer/GBufferPass.cpp index 4ca4c5cff..ddfdd619e 100644 --- a/src/gua/renderer/GBufferPass.cpp +++ b/src/gua/renderer/GBufferPass.cpp @@ -137,6 +137,9 @@ void GBufferPass::rendering(SerializedScene const& scene, mesh_shader_->set_material_uniforms( scene.materials_, ShadingModel::GBUFFER_FRAGMENT_STAGE, ctx); + mesh_shader_->set_uniform(ctx, scene.enable_global_clipping_plane, "gua_enable_global_clipping_plane"); + mesh_shader_->set_uniform(ctx, scene.global_clipping_plane, "gua_global_clipping_plane"); + Pass::bind_inputs(*mesh_shader_, eye, ctx); Pass::set_camera_matrices(*mesh_shader_, camera, @@ -253,6 +256,10 @@ void GBufferPass::rendering(SerializedScene const& scene, nurbs_shader_->set_material_uniforms( scene.materials_, ShadingModel::GBUFFER_FRAGMENT_STAGE, ctx); + // TODO: add this functionality to NURBS! + // nurbs_shader_->set_uniform(ctx, scene.enable_global_clipping_plane, "gua_enable_global_clipping_plane"); + // nurbs_shader_->set_uniform(ctx, scene.global_clipping_plane, "gua_global_clipping_plane"); + nurbs_shader_->set_uniform(ctx, pipeline_->config.get_max_tesselation(), "gua_max_tesselation"); diff --git a/src/gua/renderer/Pipeline.cpp b/src/gua/renderer/Pipeline.cpp index a6f464089..9b2119317 100644 --- a/src/gua/renderer/Pipeline.cpp +++ b/src/gua/renderer/Pipeline.cpp @@ -248,6 +248,9 @@ void Pipeline::process(std::vector> const& sce config.far_clip()); } + current_scenes_[0].enable_global_clipping_plane = config.get_enable_global_clipping_plane(); + current_scenes_[0].global_clipping_plane = config.get_global_clipping_plane(); + serializer_->check(¤t_scenes_[0], current_graph_, config.camera().render_mask, @@ -283,15 +286,30 @@ void Pipeline::process(std::vector> const& sce return; } + if (config.camera().mode == Camera::ProjectionMode::PERSPECTIVE) { + current_scenes_[0].frustum = Frustum::perspective(eye_l->get_world_transform(), + screen_l->get_scaled_world_transform(), + config.near_clip(), + config.far_clip()); + current_scenes_[1].frustum = Frustum::perspective(eye_r->get_world_transform(), + screen_r->get_scaled_world_transform(), + config.near_clip(), + config.far_clip()); + } else { + current_scenes_[0].frustum = Frustum::orthographic(eye_l->get_world_transform(), + screen_l->get_scaled_world_transform(), + config.near_clip(), + config.far_clip()); + current_scenes_[1].frustum = Frustum::orthographic(eye_r->get_world_transform(), + screen_r->get_scaled_world_transform(), + config.near_clip(), + config.far_clip()); + } - current_scenes_[0].frustum = Frustum::perspective(eye_l->get_world_transform(), - screen_l->get_scaled_world_transform(), - config.near_clip(), - config.far_clip()); - current_scenes_[1].frustum = Frustum::perspective(eye_r->get_world_transform(), - screen_r->get_scaled_world_transform(), - config.near_clip(), - config.far_clip()); + current_scenes_[0].enable_global_clipping_plane = config.get_enable_global_clipping_plane(); + current_scenes_[0].global_clipping_plane = config.get_global_clipping_plane(); + current_scenes_[1].enable_global_clipping_plane = config.get_enable_global_clipping_plane(); + current_scenes_[1].global_clipping_plane = config.get_global_clipping_plane(); serializer_->check(¤t_scenes_[0], current_graph_, From ab6a8bd55ccdb0e05eb19f3bf2e028d7334aab4c Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Tue, 4 Feb 2014 14:36:26 +0100 Subject: [PATCH 094/146] set output texture mode to repeat mirrored --- src/gua/renderer/GBufferPass.cpp | 4 ++-- src/gua/renderer/LayerMapping.cpp | 4 ++-- src/gua/renderer/Pipeline.cpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/gua/renderer/GBufferPass.cpp b/src/gua/renderer/GBufferPass.cpp index ddfdd619e..73938fb0c 100644 --- a/src/gua/renderer/GBufferPass.cpp +++ b/src/gua/renderer/GBufferPass.cpp @@ -71,8 +71,8 @@ void GBufferPass::create( layers) { scm::gl::sampler_state_desc state(scm::gl::FILTER_MIN_MAG_MIP_NEAREST, - scm::gl::WRAP_CLAMP_TO_EDGE, - scm::gl::WRAP_CLAMP_TO_EDGE); + scm::gl::WRAP_MIRRORED_REPEAT, + scm::gl::WRAP_MIRRORED_REPEAT); auto tmp(layers); tmp.insert(tmp.begin(), std::make_pair(BufferComponent::DEPTH_24, state)); diff --git a/src/gua/renderer/LayerMapping.cpp b/src/gua/renderer/LayerMapping.cpp index 3bbabb587..1a2686964 100644 --- a/src/gua/renderer/LayerMapping.cpp +++ b/src/gua/renderer/LayerMapping.cpp @@ -434,8 +434,8 @@ std::vector< } scm::gl::sampler_state_desc state(scm::gl::FILTER_ANISOTROPIC, - scm::gl::WRAP_CLAMP_TO_EDGE, - scm::gl::WRAP_CLAMP_TO_EDGE); + scm::gl::WRAP_MIRRORED_REPEAT, + scm::gl::WRAP_MIRRORED_REPEAT); result.push_back(std::make_pair(get_largest(output_sums), state)); } diff --git a/src/gua/renderer/Pipeline.cpp b/src/gua/renderer/Pipeline.cpp index 9b2119317..f53935620 100644 --- a/src/gua/renderer/Pipeline.cpp +++ b/src/gua/renderer/Pipeline.cpp @@ -454,8 +454,8 @@ void Pipeline::create_buffers() { stereobuffers.push_back(passes_[PipelineStage::compositing]->get_gbuffer()); scm::gl::sampler_state_desc state(scm::gl::FILTER_MIN_MAG_LINEAR, - scm::gl::WRAP_REPEAT, - scm::gl::WRAP_REPEAT); + scm::gl::WRAP_MIRRORED_REPEAT, + scm::gl::WRAP_MIRRORED_REPEAT); passes_[PipelineStage::postfx]->create(*context_, config, { { BufferComponent::F3, state } }); passes_[PipelineStage::postfx]->set_inputs(stereobuffers); From 9488675badbfd5e8cae920e6c55b420682c45dd6 Mon Sep 17 00:00:00 2001 From: thelaui Date: Thu, 6 Feb 2014 12:52:39 +0100 Subject: [PATCH 095/146] implemented LODNode --- include/gua/renderer/SerializedScene.hpp | 5 ++ include/gua/renderer/Serializer.hpp | 10 ++++ include/gua/renderer/ShadowMap.hpp | 8 ++- include/gua/scenegraph/LODNode.hpp | 75 ++++++++++++++++++++++++ include/gua/scenegraph/NodeVisitor.hpp | 10 ++++ src/gua/renderer/LightingPass.cpp | 4 +- src/gua/renderer/Pipeline.cpp | 3 + src/gua/renderer/Serializer.cpp | 30 ++++++++++ src/gua/renderer/ShadowMap.cpp | 13 +++- src/gua/scenegraph/LODNode.cpp | 44 ++++++++++++++ 10 files changed, 196 insertions(+), 6 deletions(-) create mode 100644 include/gua/scenegraph/LODNode.hpp create mode 100644 src/gua/scenegraph/LODNode.cpp diff --git a/include/gua/renderer/SerializedScene.hpp b/include/gua/renderer/SerializedScene.hpp index 404c82ab7..bc715fa1c 100644 --- a/include/gua/renderer/SerializedScene.hpp +++ b/include/gua/renderer/SerializedScene.hpp @@ -88,6 +88,11 @@ struct SerializedScene { bool enable_global_clipping_plane; math::vec4 global_clipping_plane; + /** + * The center of interest. + */ + math::vec3 center_of_interest; + /** * All used materials. */ diff --git a/include/gua/renderer/Serializer.hpp b/include/gua/renderer/Serializer.hpp index 412a177ff..215b2558d 100644 --- a/include/gua/renderer/Serializer.hpp +++ b/include/gua/renderer/Serializer.hpp @@ -75,6 +75,15 @@ class Serializer : public NodeVisitor { */ /* virtual */ void visit(Node* node); + /** + * Visits an LODNode + * + * This function provides the interface to visit an LODNode + * + * \param cam Pointer to LODNode + */ + /* virtual */ void visit(LODNode* lod); + /** * Visits a GeometryNode * @@ -164,6 +173,7 @@ class Serializer : public NodeVisitor { Frustum current_frustum_; Mask current_render_mask_; + math::vec3 current_center_of_interest_; SerializedScene* data_; bool draw_bounding_boxes_; diff --git a/include/gua/renderer/ShadowMap.hpp b/include/gua/renderer/ShadowMap.hpp index 9694507f1..24a08e509 100644 --- a/include/gua/renderer/ShadowMap.hpp +++ b/include/gua/renderer/ShadowMap.hpp @@ -50,11 +50,13 @@ class ShadowMap { virtual ~ShadowMap(); void render(RenderContext const& ctx, + math::vec3 const& center_of_interest, Camera const& scene_camera, math::mat4 const& transform, unsigned map_size); void render_cascaded(RenderContext const& ctx, + math::vec3 const& center_of_interest, Frustum const& scene_frustum, Camera const& scene_camera, math::mat4 const& transform, @@ -77,7 +79,11 @@ class ShadowMap { private: void update_members(RenderContext const& ctx, unsigned map_size); - void render_geometry(RenderContext const & ctx, Frustum const& shadow_frustum, Camera const& scene_camera, unsigned cascade); + void render_geometry(RenderContext const & ctx, + math::vec3 const& center_of_interest, + Frustum const& shadow_frustum, + Camera const& scene_camera, + unsigned cascade); GBuffer* buffer_; diff --git a/include/gua/scenegraph/LODNode.hpp b/include/gua/scenegraph/LODNode.hpp new file mode 100644 index 000000000..520478d2b --- /dev/null +++ b/include/gua/scenegraph/LODNode.hpp @@ -0,0 +1,75 @@ +/****************************************************************************** + * guacamole - delicious VR * + * * + * Copyright: (c) 2011-2013 Bauhaus-Universität Weimar * + * Contact: felix.lauer@uni-weimar.de / simon.schneegans@uni-weimar.de * + * * + * This program is free software: you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the Free * + * Software Foundation, either version 3 of the License, or (at your option) * + * any later version. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * + * for more details. * + * * + * You should have received a copy of the GNU General Public License along * + * with this program. If not, see . * + * * + ******************************************************************************/ + +#ifndef GUA_LOD_NODE_HPP +#define GUA_LOD_NODE_HPP + +#include +#include +#include + +/** + * This class is used to represent an empty node in the SceneGraph. + * + */ + +namespace gua { + +class GUA_DLL LODNode : public TransformNode { + public: + + struct Configuration { + GUA_ADD_PROPERTY(std::vector, lod_distances, std::vector()); + }; + + Configuration data; + + LODNode() {}; + + /** + * Constructor. + * + * This constructs a LODNode with the given parameters. + * + * \param name The Node's name + * \param transform The transformation of the object the Node contains. + */ + LODNode(std::string const& name, + Configuration const& configuration = Configuration(), + math::mat4 const& transform = math::mat4::identity()); + + + /** + * Accepts a visitor and calls concrete visit method + * + * This method implements the visitor pattern for Nodes + * + */ + /* virtual */ void accept(NodeVisitor&); + + private: + + std::shared_ptr copy() const; +}; + +} + +#endif // GUA_LOD_NODE_HPP diff --git a/include/gua/scenegraph/NodeVisitor.hpp b/include/gua/scenegraph/NodeVisitor.hpp index a439781ff..41416a863 100644 --- a/include/gua/scenegraph/NodeVisitor.hpp +++ b/include/gua/scenegraph/NodeVisitor.hpp @@ -30,6 +30,7 @@ namespace gua { class Node; class TransformNode; +class LODNode; class GeometryNode; class VolumeNode; class PointLightNode; @@ -85,6 +86,15 @@ class NodeVisitor { */ virtual void visit(TransformNode* node) { visit(reinterpret_cast(node)); } + /** + * Visits an LODNode + * + * This function provides the interface to visit an LODNode + * + * \param cam Pointer to LODNode + */ + virtual void visit(LODNode* node) { visit(reinterpret_cast(node)); } + /** * Visits a GeometryNode * diff --git a/src/gua/renderer/LightingPass.cpp b/src/gua/renderer/LightingPass.cpp index 4143ddc04..93a4ece78 100644 --- a/src/gua/renderer/LightingPass.cpp +++ b/src/gua/renderer/LightingPass.cpp @@ -164,7 +164,7 @@ void LightingPass::rendering(SerializedScene const& scene, WARNING("Exactly 5 splits have to be defined for cascaded shadow maps!"); } - shadow_map_.render_cascaded(ctx, scene.frustum, camera, + shadow_map_.render_cascaded(ctx, scene.center_of_interest, scene.frustum, camera, light.transform, light.data.get_shadow_map_size(), split_0, split_1, split_2, split_3, split_4, @@ -250,7 +250,7 @@ void LightingPass::rendering(SerializedScene const& scene, target->unbind(ctx); ctx.render_context->reset_state_objects(); - shadow_map_.render(ctx, camera, light.transform, light.data.get_shadow_map_size()); + shadow_map_.render(ctx, scene.center_of_interest, camera, light.transform, light.data.get_shadow_map_size()); shader_->use(ctx); target->bind(ctx); diff --git a/src/gua/renderer/Pipeline.cpp b/src/gua/renderer/Pipeline.cpp index f53935620..dfd924db4 100644 --- a/src/gua/renderer/Pipeline.cpp +++ b/src/gua/renderer/Pipeline.cpp @@ -248,6 +248,7 @@ void Pipeline::process(std::vector> const& sce config.far_clip()); } + current_scenes_[0].center_of_interest = eye->get_world_position(); current_scenes_[0].enable_global_clipping_plane = config.get_enable_global_clipping_plane(); current_scenes_[0].global_clipping_plane = config.get_global_clipping_plane(); @@ -306,8 +307,10 @@ void Pipeline::process(std::vector> const& sce config.far_clip()); } + current_scenes_[0].center_of_interest = eye_l->get_world_position(); current_scenes_[0].enable_global_clipping_plane = config.get_enable_global_clipping_plane(); current_scenes_[0].global_clipping_plane = config.get_global_clipping_plane(); + current_scenes_[1].center_of_interest = eye_r->get_world_position(); current_scenes_[1].enable_global_clipping_plane = config.get_enable_global_clipping_plane(); current_scenes_[1].global_clipping_plane = config.get_global_clipping_plane(); diff --git a/src/gua/renderer/Serializer.cpp b/src/gua/renderer/Serializer.cpp index 7b81c6fb4..202773176 100644 --- a/src/gua/renderer/Serializer.cpp +++ b/src/gua/renderer/Serializer.cpp @@ -32,6 +32,7 @@ #include #include +#include #include #include #include @@ -52,6 +53,7 @@ Serializer::Serializer() : data_(nullptr), current_render_mask_(""), current_frustum_(), + current_center_of_interest_(), draw_bounding_boxes_(false), draw_rays_(false), enable_frustum_culling_(false) {} @@ -117,6 +119,7 @@ void Serializer::check(SerializedScene* output, current_render_mask_ = Mask(render_mask); current_frustum_ = output->frustum; + current_center_of_interest_ = output->center_of_interest; scene_graph->accept(*this); } @@ -131,6 +134,33 @@ void Serializer::check(SerializedScene* output, //////////////////////////////////////////////////////////////////////// +/* virtual */ void Serializer::visit(LODNode* node) { + if (is_visible(node)) { + + float distance_to_camera(scm::math::length(node->get_world_position() - current_center_of_interest_)); + + unsigned child_index(0); + + if (!node->data.get_lod_distances().empty()) { + + child_index = node->get_children().size(); + + for (unsigned i(0); i < node->data.get_lod_distances().size(); ++i) { + if (node->data.get_lod_distances()[i] > distance_to_camera) { + child_index = i; + break; + } + } + } + + if (child_index < node->get_children().size()) { + node->get_children()[child_index]->accept(*this); + } + } +} + +//////////////////////////////////////////////////////////////////////// + /* virtual */ void Serializer::visit(GeometryNode* node) { if (is_visible(node)) { diff --git a/src/gua/renderer/ShadowMap.cpp b/src/gua/renderer/ShadowMap.cpp index 59a53dc24..fd5a9b58d 100644 --- a/src/gua/renderer/ShadowMap.cpp +++ b/src/gua/renderer/ShadowMap.cpp @@ -107,9 +107,14 @@ void ShadowMap::update_members(RenderContext const & ctx, unsigned map_size) { //////////////////////////////////////////////////////////////////////////////// -void ShadowMap::render_geometry(RenderContext const & ctx, Frustum const& shadow_frustum, Camera const& scene_camera, unsigned cascade) { +void ShadowMap::render_geometry(RenderContext const & ctx, + math::vec3 const& center_of_interest, + Frustum const& shadow_frustum, + Camera const& scene_camera, + unsigned cascade) { SerializedScene scene; scene.frustum = shadow_frustum; + scene.center_of_interest = center_of_interest; serializer_->check(&scene, pipeline_->get_current_graph(), scene_camera.render_mask, @@ -139,6 +144,7 @@ void ShadowMap::render_geometry(RenderContext const & ctx, Frustum const& shadow //////////////////////////////////////////////////////////////////////////////// void ShadowMap::render(RenderContext const& ctx, + math::vec3 const& center_of_interest, Camera const& scene_camera, math::mat4 const& transform, unsigned map_size) { @@ -171,7 +177,7 @@ void ShadowMap::render(RenderContext const& ctx, // render geometries mesh_shader_->use(ctx); - render_geometry(ctx, shadow_frustum, scene_camera, 0); + render_geometry(ctx, center_of_interest, shadow_frustum, scene_camera, 0); mesh_shader_->unuse(ctx); ctx.render_context->reset_state_objects(); @@ -182,6 +188,7 @@ void ShadowMap::render(RenderContext const& ctx, //////////////////////////////////////////////////////////////////////////////// void ShadowMap::render_cascaded(RenderContext const& ctx, + math::vec3 const& center_of_interest, Frustum const& scene_frustum, Camera const& scene_camera, math::mat4 const& transform, @@ -262,7 +269,7 @@ void ShadowMap::render_cascaded(RenderContext const& ctx, // // render geometries mesh_shader_->use(ctx); - render_geometry(ctx, shadow_frustum, scene_camera, cascade); + render_geometry(ctx, center_of_interest, shadow_frustum, scene_camera, cascade); mesh_shader_->unuse(ctx); } } diff --git a/src/gua/scenegraph/LODNode.cpp b/src/gua/scenegraph/LODNode.cpp new file mode 100644 index 000000000..d855e7a81 --- /dev/null +++ b/src/gua/scenegraph/LODNode.cpp @@ -0,0 +1,44 @@ +/****************************************************************************** + * guacamole - delicious VR * + * * + * Copyright: (c) 2011-2013 Bauhaus-Universität Weimar * + * Contact: felix.lauer@uni-weimar.de / simon.schneegans@uni-weimar.de * + * * + * This program is free software: you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the Free * + * Software Foundation, either version 3 of the License, or (at your option) * + * any later version. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * + * for more details. * + * * + * You should have received a copy of the GNU General Public License along * + * with this program. If not, see . * + * * + ******************************************************************************/ + +// class header +#include + +// guacamole headers +#include + +namespace gua { + +LODNode::LODNode(std::string const& name, Configuration const& configuration, math::mat4 const& transform) + : TransformNode(name, transform), data(configuration) {} + + +/* virtual */ void LODNode::accept(NodeVisitor& visitor) { + + visitor.visit(this); +} + +std::shared_ptr LODNode::copy() const { + return std::make_shared(get_name(), data, get_transform()); +} + + +} From d408debd88ae6cacfae84086f43c702c45274aa7 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Fri, 7 Feb 2014 09:58:03 +0100 Subject: [PATCH 096/146] fixed stencil map rendering of mipmaps --- src/gua/renderer/MaterialLoader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gua/renderer/MaterialLoader.cpp b/src/gua/renderer/MaterialLoader.cpp index 2e0a1b6fe..43c142bb3 100644 --- a/src/gua/renderer/MaterialLoader.cpp +++ b/src/gua/renderer/MaterialLoader.cpp @@ -270,7 +270,7 @@ std::string const MaterialLoader::load_shading_model( gbuffer_fragment_body += std::string( " \n\ if (texture2D(opacity_map, " - "varying_texcoords).r < 0.9) \n\ + "varying_texcoords).r < 0.5) \n\ discard; " " \n\ "); From fc8c8706e75b64970154447c20dadbf2be5730a3 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Tue, 11 Feb 2014 22:26:14 +0100 Subject: [PATCH 097/146] added new vignette --- .../shaders/uber_shaders/postfx/stage_03.frag | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/resources/shaders/uber_shaders/postfx/stage_03.frag b/resources/shaders/uber_shaders/postfx/stage_03.frag index 1a432df7b..2ca0a421c 100644 --- a/resources/shaders/uber_shaders/postfx/stage_03.frag +++ b/resources/shaders/uber_shaders/postfx/stage_03.frag @@ -106,14 +106,12 @@ void gua_apply_fxaa() { void apply_vignette() { - float hardness = gua_vignette_softness; - float offset = 0.5 - gua_vignette_coverage*0.5; - - float dist = length(gua_get_quad_coords() - vec2(0.5)); - float fac = (dist - offset)/hardness; - fac = 1.0 - pow(clamp(fac, 0.0, 1.0), 2); - - gua_out_color = gua_out_color * fac + (1.0 - fac) * gua_vignette_color; + // inigo quilez's great vigneting effect! + float a = -gua_vignette_coverage/gua_vignette_softness; + float b = 1.0/gua_vignette_softness; + vec2 q = gua_get_quad_coords(); + float fac = min(1, a + b*pow( 16.0*q.x*q.y*(1.0-q.x)*(1.0-q.y), 0.1 )); + gua_out_color = gua_out_color * fac + (1.0 - fac) * gua_vignette_color; } /////////////////////////////// main /////////////////////////////////// From 6982ac21aac930fefcef8944874fd15da7e52a8b Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Wed, 12 Feb 2014 17:21:04 +0100 Subject: [PATCH 098/146] fixed ambient color of generated materials --- src/gua/renderer/MaterialLoader.cpp | 32 +++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/src/gua/renderer/MaterialLoader.cpp b/src/gua/renderer/MaterialLoader.cpp index 43c142bb3..d8c5febdf 100644 --- a/src/gua/renderer/MaterialLoader.cpp +++ b/src/gua/renderer/MaterialLoader.cpp @@ -250,7 +250,7 @@ std::string const MaterialLoader::load_shading_model( "); if (capabilities & (DIFFUSE_MAP | SPECULAR_MAP | EMIT_MAP | NORMAL_MAP | - SHININESS_MAP)) { + SHININESS_MAP | AMBIENT_MAP)) { model->get_gbuffer_vertex_stage().get_outputs()["varying_texcoords"] = BufferComponent::F2; @@ -262,6 +262,20 @@ std::string const MaterialLoader::load_shading_model( "); } + if (capabilities & DIFFUSE_MAP) { + + model->get_gbuffer_fragment_stage().get_uniforms()["diffuse_map"] = + UniformType::SAMPLER2D; + + gbuffer_fragment_body += std::string( + " \n\ + if (texture2D(diffuse_map, " + "varying_texcoords).a < 0.5) \n\ + discard; " + " \n\ + "); + } + if (capabilities & OPACITY_MAP) { model->get_gbuffer_fragment_stage().get_uniforms()["opacity_map"] = @@ -372,6 +386,12 @@ std::string const MaterialLoader::load_shading_model( "r", BufferComponent::F1, UniformType::FLOAT); + add_output(capabilities & AMBIENT_MAP, + capabilities & AMBIENT_COLOR, + "ambient", + "r", + BufferComponent::F1, + UniformType::VEC3); model->get_gbuffer_vertex_stage().set_body(gbuffer_vertex_body); model->get_gbuffer_fragment_stage().set_body(gbuffer_fragment_body); @@ -429,9 +449,13 @@ std::string const MaterialLoader::load_shading_model( else final_body += "vec3 my_emit_color = vec3(0.0);"; - final_body += " gua_color = my_diffuse_color * gua_light_diffuse + " - "(vec3(1.0) / (vec3(1.0) + gua_light_diffuse)) * " - "gua_ambient_color * my_diffuse_color;"; + if (capabilities & (AMBIENT_MAP | AMBIENT_COLOR)) + final_body += "float my_ambient_color = gua_ambient;"; + else + final_body += "float my_ambient_color = 0.5;"; + + final_body += "gua_color = my_diffuse_color * gua_light_diffuse;"; + final_body += "gua_color += gua_ambient_color * my_diffuse_color * my_ambient_color * (1.0 - min(1, max(max(gua_light_diffuse.r, gua_light_diffuse.g), gua_light_diffuse.b)));"; if (capabilities & (SPECULAR_COLOR | SPECULAR_MAP)) final_body += "gua_color += gua_light_specular;"; From 093c9f0686cf9eee63fe22800c13929eca2cc829 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Thu, 13 Feb 2014 13:03:19 +0100 Subject: [PATCH 099/146] made skycolor default background mode --- include/gua/renderer/Pipeline.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/gua/renderer/Pipeline.hpp b/include/gua/renderer/Pipeline.hpp index 0b74c88a6..97e306d9b 100644 --- a/include/gua/renderer/Pipeline.hpp +++ b/include/gua/renderer/Pipeline.hpp @@ -111,7 +111,7 @@ struct PipelineConfiguration { GUA_ADD_PROPERTY(utils::Color3f, fog_color, utils::Color3f()); // background image / color - GUA_ADD_PROPERTY(BackgroundMode, background_mode, BackgroundMode::SKYMAP_TEXTURE); + GUA_ADD_PROPERTY(BackgroundMode, background_mode, BackgroundMode::COLOR); GUA_ADD_PROPERTY(std::string, background_texture, ""); GUA_ADD_PROPERTY(utils::Color3f, background_color, utils::Color3f()); From 4af6ff3f19229d52bdfd4393f7a2f5482f069a1b Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Thu, 13 Feb 2014 15:15:34 +0100 Subject: [PATCH 100/146] shadow map uses a meshubershader now --- include/gua/renderer/ShadowMap.hpp | 10 +++-- src/gua/renderer/LightingPass.cpp | 1 + src/gua/renderer/ShadowMap.cpp | 60 +++++++++++++++++++++++------- 3 files changed, 54 insertions(+), 17 deletions(-) diff --git a/include/gua/renderer/ShadowMap.hpp b/include/gua/renderer/ShadowMap.hpp index 24a08e509..5419b940a 100644 --- a/include/gua/renderer/ShadowMap.hpp +++ b/include/gua/renderer/ShadowMap.hpp @@ -33,8 +33,8 @@ class GBuffer; class Frustum; class Camera; class Pipeline; -class ShadowMapMeshShader; -class ShadowMapNURBSShader; +class GBufferMeshUberShader; +class GBufferNURBSUberShader; /** * @@ -73,6 +73,8 @@ class ShadowMap { bool pre_compile_shaders(RenderContext const& ctx); + void apply_material_mapping(std::set const& materials) const; + GBuffer* get_buffer() const {return buffer_;} std::vector const& get_projection_view_matrices() const {return projection_view_matrices_;} @@ -91,8 +93,8 @@ class ShadowMap { Pipeline* pipeline_; - ShadowMapMeshShader* mesh_shader_; - ShadowMapNURBSShader* nurbs_shader_; + GBufferMeshUberShader* mesh_shader_; + // GBufferNURBSUberShader* nurbs_shader_; scm::gl::depth_stencil_state_ptr depth_stencil_state_; scm::gl::rasterizer_state_ptr rasterizer_state_; diff --git a/src/gua/renderer/LightingPass.cpp b/src/gua/renderer/LightingPass.cpp index 93a4ece78..8108d114c 100644 --- a/src/gua/renderer/LightingPass.cpp +++ b/src/gua/renderer/LightingPass.cpp @@ -60,6 +60,7 @@ void LightingPass::apply_material_mapping( std::set const& material_names, std::vector const& inputs) const { shader_->create(material_names, inputs); + shadow_map_.apply_material_mapping(material_names); } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/gua/renderer/ShadowMap.cpp b/src/gua/renderer/ShadowMap.cpp index fd5a9b58d..a0b45c3e7 100644 --- a/src/gua/renderer/ShadowMap.cpp +++ b/src/gua/renderer/ShadowMap.cpp @@ -25,11 +25,11 @@ // guacamole headers #include #include -#include -#include +#include +#include #include #include -#include +#include namespace gua { @@ -38,8 +38,8 @@ namespace gua { ShadowMap::ShadowMap(Pipeline* pipeline) : serializer_(new Serializer), pipeline_(pipeline), - mesh_shader_(new ShadowMapMeshShader), - nurbs_shader_(nullptr /*new ShadowMapNURBSShader*/), + mesh_shader_(new GBufferMeshUberShader), + // nurbs_shader_(new GBufferNURBSUberShader), buffer_(nullptr), projection_view_matrices_() { } @@ -50,8 +50,8 @@ ShadowMap::ShadowMap(Pipeline* pipeline) ShadowMap::~ShadowMap() { if (mesh_shader_) delete mesh_shader_; - if (nurbs_shader_) - delete nurbs_shader_; + // if (nurbs_shader_) + // delete nurbs_shader_; if (serializer_) delete serializer_; } @@ -61,7 +61,7 @@ ShadowMap::~ShadowMap() { void ShadowMap::print_shaders(std::string const& directory, std::string const& name) const { mesh_shader_->save_to_file(directory, name + "/shadow/mesh"); - nurbs_shader_->save_to_file(directory, name + "/shadow/nurbs"); + // nurbs_shader_->save_to_file(directory, name + "/shadow/nurbs"); } //////////////////////////////////////////////////////////////////////////////// @@ -71,7 +71,7 @@ bool ShadowMap::pre_compile_shaders(RenderContext const& ctx) { bool success(false); if (mesh_shader_) success = mesh_shader_->upload_to(ctx); - if (success && nurbs_shader_) success = nurbs_shader_->upload_to(ctx); + // if (success && nurbs_shader_) success = nurbs_shader_->upload_to(ctx); return success; } @@ -105,6 +105,15 @@ void ShadowMap::update_members(RenderContext const & ctx, unsigned map_size) { ->create_rasterizer_state(scm::gl::FILL_SOLID, scm::gl::CULL_NONE); } +//////////////////////////////////////////////////////////////////////////////// + +void ShadowMap::apply_material_mapping(std::set const & + materials) const { + mesh_shader_->create(materials); + // nurbs_shader_->create(materials); +} + + //////////////////////////////////////////////////////////////////////////////// void ShadowMap::render_geometry(RenderContext const & ctx, @@ -115,6 +124,8 @@ void ShadowMap::render_geometry(RenderContext const & ctx, SerializedScene scene; scene.frustum = shadow_frustum; scene.center_of_interest = center_of_interest; + scene.enable_global_clipping_plane = pipeline_->config.get_enable_global_clipping_plane(); + scene.global_clipping_plane = pipeline_->config.get_global_clipping_plane(); serializer_->check(&scene, pipeline_->get_current_graph(), scene_camera.render_mask, @@ -125,17 +136,36 @@ void ShadowMap::render_geometry(RenderContext const & ctx, projection_view_matrices_[cascade] = shadow_frustum.get_projection() * shadow_frustum.get_view(); - mesh_shader_->set_uniform( - ctx, projection_view_matrices_[cascade], "gua_projection_view_matrix"); + mesh_shader_->set_material_uniforms( + scene.materials_, ShadingModel::GBUFFER_VERTEX_STAGE, ctx); + mesh_shader_->set_material_uniforms( + scene.materials_, ShadingModel::GBUFFER_FRAGMENT_STAGE, ctx); + + mesh_shader_->set_uniform(ctx, scene.enable_global_clipping_plane, "gua_enable_global_clipping_plane"); + mesh_shader_->set_uniform(ctx, scene.global_clipping_plane, "gua_global_clipping_plane"); + auto camera_position(scene.frustum.get_camera_position()); + auto projection(scene.frustum.get_projection()); + auto view_matrix(scene.frustum.get_view()); + mesh_shader_->set_uniform(ctx, camera_position, "gua_camera_position"); + mesh_shader_->set_uniform(ctx, projection, "gua_projection_matrix"); + mesh_shader_->set_uniform(ctx, view_matrix, "gua_view_matrix"); + mesh_shader_->set_uniform(ctx, scm::math::inverse(projection * view_matrix), "gua_inverse_projection_view_matrix"); for (auto const& node : scene.meshnodes_) { auto geometry = GeometryDatabase::instance()->lookup(node.data.get_geometry()); - + auto material = MaterialDatabase::instance()->lookup(node.data.get_material()); if (geometry) { + mesh_shader_->set_uniform( + ctx, material->get_id(), "gua_material_id"); mesh_shader_->set_uniform( ctx, node.transform, "gua_model_matrix"); + mesh_shader_->set_uniform( + ctx, + scm::math::transpose( + scm::math::inverse(node.transform)), + "gua_normal_matrix"); geometry->draw(ctx); } } @@ -173,7 +203,8 @@ void ShadowMap::render(RenderContext const& ctx, pipeline_->config.near_clip(), pipeline_->config.far_clip()); - + mesh_shader_->set_uniform(ctx, 1.0f / map_size, "gua_texel_width"); + mesh_shader_->set_uniform(ctx, 1.0f / map_size, "gua_texel_height"); // render geometries mesh_shader_->use(ctx); @@ -222,6 +253,9 @@ void ShadowMap::render_cascaded(RenderContext const& ctx, }; } + mesh_shader_->set_uniform(ctx, 1.0f / map_size, "gua_texel_width"); + mesh_shader_->set_uniform(ctx, 1.0f / map_size, "gua_texel_height"); + for (int y(0); y<2; ++y) { for (int x(0); x<2; ++x) { From 532d59688216a2835a1bfacbfb6d9cda5551538e Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Thu, 20 Feb 2014 11:01:52 +0100 Subject: [PATCH 101/146] fix crash upon usage of non-existent texture database entry --- include/gua/renderer/Uniform.hpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/gua/renderer/Uniform.hpp b/include/gua/renderer/Uniform.hpp index 4fd54e178..341f7f5a6 100644 --- a/include/gua/renderer/Uniform.hpp +++ b/include/gua/renderer/Uniform.hpp @@ -252,7 +252,9 @@ template <> class UniformValue : public UniformValueBase { unsigned position = 0) const { auto texture(TextureDatabase::instance()->lookup(value_)); - program->uniform(name, position, texture->get_handle(context)); + if (texture) { + program->uniform(name, position, texture->get_handle(context)); + } } std::string const& value() const { return value_; } From 961dc3eeff6231126d3d3d53a7421f6170b5c089 Mon Sep 17 00:00:00 2001 From: thelaui Date: Tue, 25 Feb 2014 11:20:44 +0100 Subject: [PATCH 102/146] udpated documentation for Node, GeometryNode and LODNode --- include/gua/scenegraph/GeometryNode.hpp | 26 ++- include/gua/scenegraph/LODNode.hpp | 22 ++- include/gua/scenegraph/Node.hpp | 214 +++++++++++++++++------- 3 files changed, 190 insertions(+), 72 deletions(-) diff --git a/include/gua/scenegraph/GeometryNode.hpp b/include/gua/scenegraph/GeometryNode.hpp index 41e253f8f..90e6a8d46 100644 --- a/include/gua/scenegraph/GeometryNode.hpp +++ b/include/gua/scenegraph/GeometryNode.hpp @@ -32,6 +32,9 @@ /** * This class is used to represent geometry in the SceneGraph. * + * A GeometryNode only stores references to existing rendering assets stored in + * guacamole's databases. GeometryNodes typically aren't instantiated directly + * but by utilizing guacamole's GeometryLoader. */ namespace gua { @@ -51,24 +54,33 @@ class GUA_DLL GeometryNode : public Node { /** * Constructor. * - * This constructs a GeometryNode with the given parameters and calls - * the constructor of base class Core with the type GEOMETRY. + * This constructs a GeometryNode with the given parameters. * - * \param geometry The name of the GeometryNode's geometry. - * \param material The name of the GeometryNodeCore's material. + * \param name The name of the new GeometryNode. + * \param configuration A configuration struct to define the GeometryNode's + * properties. + * \param transform A matrix to describe the GeometryNode's + * transformation. */ GeometryNode(std::string const& name, Configuration const& configuration = Configuration(), math::mat4 const& transform = math::mat4::identity()); /** - * Accepts a visitor and calls concrete visit method + * Accepts a visitor and calls concrete visit method. * - * This method implements the visitor pattern for Nodes + * This method implements the visitor pattern for Nodes. * + * \param visitor A visitor to process the GeometryNode's data. */ - /* virtual */ void accept(NodeVisitor&); + /* virtual */ void accept(NodeVisitor& visitor); + /** + * Updates a GeometryNode's BoundingBox. + * + * The bounding box is updated according to the transformation matrices of + * all children. + */ /*virtual*/ void update_bounding_box() const; /*virtual*/ void ray_test_impl(RayNode const& ray, PickResult::Options options, diff --git a/include/gua/scenegraph/LODNode.hpp b/include/gua/scenegraph/LODNode.hpp index 520478d2b..fc83ba9d9 100644 --- a/include/gua/scenegraph/LODNode.hpp +++ b/include/gua/scenegraph/LODNode.hpp @@ -27,7 +27,15 @@ #include /** - * This class is used to represent an empty node in the SceneGraph. + * This class is used to represent a level of detail node in the SceneGraph. + * + * While processing the LODNode, guacamole's renderer computes the distance + * between the LODNode's translation and the current camera's translation in + * world coordinates. The computed value is checked against a user-defined + * distances vector. That vector contains distance values which are being mapped + * to the LODNode's children sequentially. An entrance in the distances vector + * therefore describes upto which distance between the current camera and the + * LODNode the LODNode's child with the same index shall be visible. * */ @@ -49,8 +57,11 @@ class GUA_DLL LODNode : public TransformNode { * * This constructs a LODNode with the given parameters. * - * \param name The Node's name - * \param transform The transformation of the object the Node contains. + * \param name The name of the new LODNode. + * \param configuration A configuration struct to define the LODNode's + * properties. + * \param transform A matrix to describe the LODNode's + * transformation. */ LODNode(std::string const& name, Configuration const& configuration = Configuration(), @@ -58,10 +69,11 @@ class GUA_DLL LODNode : public TransformNode { /** - * Accepts a visitor and calls concrete visit method + * Accepts a visitor and calls concrete visit method. * - * This method implements the visitor pattern for Nodes + * This method implements the visitor pattern for Nodes. * + * \param visitor A visitor to process the LODNode's data. */ /* virtual */ void accept(NodeVisitor&); diff --git a/include/gua/scenegraph/Node.hpp b/include/gua/scenegraph/Node.hpp index ab13b8ab9..d50251f01 100644 --- a/include/gua/scenegraph/Node.hpp +++ b/include/gua/scenegraph/Node.hpp @@ -39,16 +39,12 @@ namespace gua { /** - * This class is used to build the internal structure of a SceneGraph. + * This class is used as a base class to provide basic node behaviour. * - * Nodes have a name and hold objects and there transforms. Furthermore they - * keep track of which Nodes are attached to them (children) an to which Node - * they are attached to (parent). Nodes can be assigned a group name which - * allows to group them concerning similar properties etc. - * - * NOTE: This class is ment to be used inside the SceneGraph class only! - * All interaction with the graph must be handled with Iterators and via - * the SceneGraph interface. + * A Node stores a name and, a tansformation matrix and an axis-aligned + * BoundingBox. Each of guacamole's Nodes may have multiple child Nodes and one + * parent Node. Furthermore, Nodes can be assigned a group name which allows for + * user-defined grouping concerning similar properties etc. * */ @@ -65,10 +61,8 @@ class GUA_DLL Node { * * This constructs a Node with the given parameters. * - * \param name The Node's name - * \param transform The transformation of the object the Node contains. - * \param core The Core of the node, representing its containing - * object. + * \param name The Node's name + * \param transform The Node's transformation. */ Node(std::string const& name = "", math::mat4 const& transform = math::mat4::identity()); @@ -81,32 +75,29 @@ class GUA_DLL Node { */ virtual ~Node(); - /** - * Accepts a visitor and calls concrete visit method - * - * This method implements the visitor pattern for Nodes - * - * \param visitor A concrete NodeVisitor - */ - virtual void accept(NodeVisitor& visitor) = 0; - - virtual void update_cache(); /** * Returns the Node's name. * - * \return string The Node's name. + * \return std::string The Node's name. */ inline std::string const& get_name() const { return name_; } + /** + * Sets the Node's name. + * + * \param name The Node's new name. + */ inline void set_name(std::string const& name) { name_ = name; } /** * Adds a child. * - * This adds a Node to the Node's children list. + * This adds a Node to the Node's children vector and returns a shared pointer + * to the new child. * - * \param child The Node to be added as a child. + * \param child The Node to be added as a child. + * \tparam T The type of the Node to be added as child. */ template std::shared_ptr add_child(std::string const& node_name) { @@ -129,19 +120,23 @@ class GUA_DLL Node { /** * Removes a child. * - * This removes a Node from the Node's children list. + * This removes a Node from the Node's children vector. * - * \param child The Node to be removed. + * \param child The Node to be removed. */ void remove_child(std::shared_ptr const& child); /** - * Returns the Node's children list. + * Returns the Node's children vector. * - * \return list The Node's children list. + * \return std::vector> The Node's children vector. */ inline std::vector> const& get_children() const { return children_; } + /** + * Clears the Node's children vector. Additionally, the parent node pointer of + * each child is set to nullptr. + */ void clear_children(); /** @@ -168,17 +163,17 @@ class GUA_DLL Node { /** * Checks whether the Node is in a certain group. * - * \param group The name of the group to be checked. + * \param group The name of the group to be checked. * - * \return is_in_group Returns true if the Node is in the given group, - * else false. + * \return bool Returns true if the Node is in the given group, + * else false. */ bool is_in_group(std::string const& group) const; /** - * Gets the groups the Node is in. + * Returns the groups the Node is in. * - * \return groups Returns all groups the Node is in. + * \return std::set Returns all groups the Node is in. */ inline std::set const& get_groups() const { return group_list_; @@ -186,50 +181,65 @@ class GUA_DLL Node { /** - * Returns the transformation of the object the Node contains. - * - * Returns the transformation accumulated with its parents so the - * resulting matrix represents the transformation in world - * coordinates. + * Returns the Node's transformation. * - * \return transform The Object's transformation. + * \return math::mat4 The Object's transformation. */ inline virtual math::mat4 get_transform() const { return transform_; } /** - * Returns the transformation of the object the Node contains. + * Returns the Node's world transformation. * - * Returns the transformation accumulated with its parents so the - * resulting matrix represents the transformation in world - * coordinates. + * Returns the Node's transformation accumulated with its parent's + * transformation * - * \return transform The Object's transformation. + * \return math::mat4 The Node's world transformation. */ math::mat4 get_world_transform() const; + /** + * Returns the Node's world postion. + * + * Accumulates the Node's transformation with its parent's transformation + * and returns the translational part of the resulting matrix. + * + * \return math::vec3 The Node's world position. + */ math::vec3 get_world_position() const; /** - * Sets the transformation of the object the Node contains. + * Sets the Node's transformation. * - * \param transform The new transformation of the Node's object. + * \param transform The Node's new transformation. */ virtual void set_transform(math::mat4 const& transform); /** - * Applies a scaling on the Node's object's transformation. + * Applies a scaling on the Node's transformation. * * \param x The x value of the scaling. * \param y The y value of the scaling. * \param z The z value of the scaling. */ - virtual void scale(float s); virtual void scale(float x, float y, float z); + + /** + * Applies a scaling on the Node's transformation. + * + * \param s The scaling vector to be used. + */ virtual void scale(math::vec3 const& s); /** - * Applies a rotation on the Node's object's transformation. + * Applies a uniform scaling on the Node's transformation. + * + * \param s The scaling value for all axes. + */ + virtual void scale(float s); + + /** + * Applies a rotation on the Node's transformation. * * \param angle The angle of the rotation in degrees. * \param x The x factor of the rotation. @@ -237,16 +247,29 @@ class GUA_DLL Node { * \param z The z factor of the rotation. */ virtual void rotate(float angle, float x, float y, float z); + + /** + * Applies a rotation on the Node's transformation. + * + * \param angle The angle of the rotation in degrees. + * \param axis A vector containing the rotation's axis values. + */ virtual void rotate(float angle, math::vec3 const& axis); /** - * Applies a translation on the Node's object's transformation. + * Applies a translation on the Node's transformation. * * \param x The x value of the translation. * \param y The y value of the translation. * \param z The z value of the translation. */ virtual void translate(float x, float y, float z); + + /** + * Applies a translation on the Node's transformation. + * + * \param offset The translation vector. + */ virtual void translate(math::vec3 const& offset); /** @@ -260,40 +283,112 @@ class GUA_DLL Node { int get_depth() const; /** - * Returns if the Node has Children + * Returns if the Node has children * - * \return bool Has the Node any children + * \return bool The return value is true if the Node has any children, else + * false. */ inline bool has_children() const { return !children_.empty(); } /** * Returns the full path to the node. * - * This function recursively computes the full path of the Node. + * This function recursively computes the full path of the Node. A Node's path + * contains the names of all of its parents, concatenated by "/". * - * \return path The full path to the Node. + * \return std::string The full path to the Node. */ std::string get_path() const; /** - * Returns the Node's parent. + * Returns a raw pointer to the Node's parent. * - * \return Node The Node's parent. + * \return Node* The Node's parent. */ inline Node* get_parent() const { return parent_; } + + /** + * Returns a shared pointer to the Node's parent. + * + * \return std::shared_ptr The Node's parent. + */ std::shared_ptr get_parent_shared() const; + /** + * Returns the Node's BoundingBox. + * + * \return math::BoundingBox The Node's BoundingBox. + */ virtual inline math::BoundingBox const& get_bounding_box() const { return bounding_box_; } + /** + * Updates a Node's BoundingBox. + * + * The bounding box is updated according to the transformation matrices of + * all children. + */ + virtual void update_bounding_box() const; + + /** + * Intersects a Node with a given RayNode. + * + * The function checks wheter a given RayNode intersects the Node or not. If + * an intersection was found, a std::set is returned, containing + * information about individual hits. The user may specify PickResult::Options + * and a mask (referring to Nodes' group names) to configure the intersection + * process. + * + * \param ray The RayNode used to check for intersections. + * \param options PickResult::Options to configure the intersection process. + * \param mask A mask to restrict the intersection to certain Nodes. + */ virtual std::set const ray_test(RayNode const& ray, PickResult::Options options = PickResult::PICK_ALL, std::string const& mask = ""); - void* get_user_data(unsigned handle) const; + /** + * Accepts a visitor and calls concrete visit method + * + * This method must be implemented by derived classes. + * + * \param visitor A visitor to process the Node's data + */ + virtual void accept(NodeVisitor& visitor) = 0; + + /** + * Updates the Node's cache. + * + * For optimization purposes, a Node caches data each time the rendering is + * being processed. This function computes and stores the Node's current + * BoundingBox and world transformation. + */ + virtual void update_cache(); + + /** + * Adds user-defined data to a Node. + * + * A user may store any data within one of guacamole's Nodes. This function + * returns a handle which can be used to regain the information later. + * + * \param data A pointer to the data to be stored. + * + * \return unsigned A handle for later access. + */ unsigned add_user_data(void* data); + /** + * Returns user-defined data for a given handle. + * + * + * \param handle The handle belonging to the wanted data. + * + * \return void* The user-defined data. This defaults to nullptr if the given + * handle is invalid. + */ + void* get_user_data(unsigned handle) const; + friend class SceneGraph; friend class GeometryLoader; friend class VolumeLoader; @@ -318,7 +413,6 @@ class GUA_DLL Node { */ inline bool is_root() const { return parent_ == nullptr; } - virtual void update_bounding_box() const; private: From fd02421ebf0550e818a63a2ecbfea26ddd35adc6 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Tue, 25 Feb 2014 11:21:28 +0100 Subject: [PATCH 103/146] added Doxyfile --- doc/Doxyfile | 1773 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1773 insertions(+) create mode 100644 doc/Doxyfile diff --git a/doc/Doxyfile b/doc/Doxyfile new file mode 100644 index 000000000..0128f85f0 --- /dev/null +++ b/doc/Doxyfile @@ -0,0 +1,1773 @@ +# Doxyfile 1.7.6.1 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or sequence of words) that should +# identify the project. Note that if you do not use Doxywizard you need +# to put quotes around the project name if it contains spaces. + +PROJECT_NAME = + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer +# a quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify an logo or icon that is +# included in the documentation. The maximum height of the logo should not +# exceed 55 pixels and the maximum width should not exceed 200 pixels. +# Doxygen will copy the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = doc + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = YES + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful if your file system +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 2 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding +# "class=itcl::class" will allow you to use the command class in the +# itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given extension. +# Doxygen has a built-in mapping, but you can override or extend it using this +# tag. The format is ext=language, where ext is a file extension, and language +# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, +# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make +# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C +# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions +# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also makes the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = YES + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter +# and setter methods for a property. Setting this option to YES (the default) +# will make doxygen replace the get and set methods by a property in the +# documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = NO + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = YES + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and +# unions are shown inside the group in which they are included (e.g. using +# @ingroup) instead of on a separate page (for HTML and Man pages) or +# section (for LaTeX and RTF). + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and +# unions with only public data fields will be shown inline in the documentation +# of the scope in which they are defined (i.e. file, namespace, or group +# documentation), provided this scope is documented. If set to NO (the default), +# structs, classes, and unions are shown on a separate page (for HTML and Man +# pages) or section (for LaTeX and RTF). + +INLINE_SIMPLE_STRUCTS = YES + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penalty. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will roughly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols. + +SYMBOL_CACHE_SIZE = 0 + +# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be +# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given +# their name and scope. Since this can be an expensive process and often the +# same symbol appear multiple times in the code, doxygen keeps a cache of +# pre-resolved symbols. If the cache is too small doxygen will become slower. +# If the cache is too large, memory is wasted. The cache size is given by this +# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespaces are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = YES + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = NO + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to +# do proper type resolution of all parameters of a function it will reject a +# match between the prototype and the implementation of a member function even +# if there is only one candidate or it is obvious which candidate to choose +# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen +# will still accept a match between prototype and implementation in such cases. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = NO + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = NO + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = NO + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= NO + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or macro consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and macros in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = NO + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = NO + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = NO + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. The create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted +# DoxygenLayout.xml will be used as the name of the layout file. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files +# containing the references data. This must be a list of .bib files. The +# .bib extension is automatically appended if omitted. Using this command +# requires the bibtex tool to be installed. See also +# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style +# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this +# feature you need bibtex and perl available in the search path. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = NO + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = NO + +# The WARN_NO_PARAMDOC option can be enabled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = ../include + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh +# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py +# *.f90 *.f *.for *.vhd *.vhdl + +FILE_PATTERNS = *.hpp \ + *.cpp + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = ../include/gua/renderer/nurbs_geometry + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty or if +# non of the patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) +# and it is also possible to disable source filtering for a specific pattern +# using *.ext= (so without naming a filter). This option only has effect when +# FILTER_SOURCE_FILES is enabled. + +FILTER_SOURCE_PATTERNS = + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. Note that when using a custom header you are responsible +# for the proper inclusion of any scripts and style sheets that doxygen +# needs, which is dependent on the configuration options used. +# It is advised to generate a default header using "doxygen -w html +# header.html footer.html stylesheet.css YourConfigFile" and then modify +# that header. Note that the header is subject to change so you typically +# have to redo this when upgrading to a newer version of doxygen or when +# changing the value of configuration settings such as GENERATE_TREEVIEW! + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# style sheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that +# the files will be copied as-is; there are no commands or markers available. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the style sheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The allowed range is 0 to 359. + +HTML_COLORSTYLE_HUE = 82 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use +# grayscales only. A value of 255 will produce the most vivid colors. + +HTML_COLORSTYLE_SAT = 98 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# and 100 does not change the gamma. + +HTML_COLORSTYLE_GAMMA = 70 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + +HTML_TIMESTAMP = YES + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = YES + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a +# Qt Compressed Help (.qch) of the generated HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# +# Qt Help Project / Filter Attributes. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) +# at top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. Since the tabs have the same information as the +# navigation tree you can set this option to NO if you already set +# GENERATE_TREEVIEW to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. +# Since the tree basically has the same information as the tab index you +# could consider to set DISABLE_INDEX to NO when enabling this option. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values +# (range [0,1..20]) that doxygen will group on one line in the generated HTML +# documentation. Note that a value of 0 will completely suppress the enum +# values from appearing in the overview section. + +ENUM_VALUES_PER_LINE = 4 + +# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, +# and Class Hierarchy pages using a tree view instead of an ordered list. + +USE_INLINE_TREES = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# links to external symbols imported via tag files in a separate window. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files +# in the HTML output before the changes have effect. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax +# (see http://www.mathjax.org) which uses client side Javascript for the +# rendering instead of using prerendered bitmaps. Use this if you do not +# have LaTeX installed or if you want to formulas look prettier in the HTML +# output. When enabled you also need to install MathJax separately and +# configure the path to it using the MATHJAX_RELPATH option. + +USE_MATHJAX = NO + +# When MathJax is enabled you need to specify the location relative to the +# HTML output directory using the MATHJAX_RELPATH option. The destination +# directory should contain the MathJax.js script. For instance, if the mathjax +# directory is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the +# mathjax.org site, so you can quickly see the result without installing +# MathJax, but it is strongly recommended to install a local copy of MathJax +# before deployment. + +MATHJAX_RELPATH = http://www.mathjax.org/mathjax + +# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension +# names that should be enabled during MathJax rendering. + +MATHJAX_EXTENSIONS = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a PHP enabled web server instead of at the web client +# using Javascript. Doxygen will generate the search PHP script and index +# file to put on the web server. The advantage of the server +# based approach is that it scales better to large projects and allows +# full text search. The disadvantages are that it is more difficult to setup +# and does not have live searching capabilities. + +SERVER_BASED_SEARCH = NO + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = YES + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4 + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for +# the generated latex document. The footer should contain everything after +# the last chapter. If it is left blank doxygen will generate a +# standard footer. Notice: only use this tag if you know what you are doing! + +LATEX_FOOTER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See +# http://en.wikipedia.org/wiki/BibTeX for more info. + +LATEX_BIB_STYLE = plain + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load style sheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = YES + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# pointed to by INCLUDE_PATH will be searched when a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = ../include + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition that +# overrules the definition found in the source code. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all references to function-like macros +# that are alone on a line, have an all uppercase name, and do not end with a +# semicolon, because these will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option also works with HAVE_DOT disabled, but it is recommended to +# install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = NO + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = YES + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 0 + +# By default doxygen will use the Helvetica font for all dot files that +# doxygen generates. When you want a differently looking font you can specify +# the font name using DOT_FONTNAME. You need to make sure dot is able to find +# the font, which can be done by putting it in a standard location or by setting +# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# directory containing the font. + +DOT_FONTNAME = Helvetica + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the Helvetica font. +# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to +# set the path where dot can find it. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = NO + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = NO + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = NO + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will generate a graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = NO + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = NO + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are svg, png, jpg, or gif. +# If left blank png will be used. If you choose svg you need to set +# HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible in IE 9+ (other browsers do not have this requirement). + +DOT_IMAGE_FORMAT = png + +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# Note that this requires a modern browser other than Internet Explorer. +# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you +# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible. Older versions of IE do not have SVG support. + +INTERACTIVE_SVG = NO + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = /usr/bin + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the +# \mscfile command). + +MSCFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES From 06f75304728fd768836e780cb256fb56120ef9ba Mon Sep 17 00:00:00 2001 From: thelaui Date: Tue, 25 Feb 2014 14:40:27 +0100 Subject: [PATCH 104/146] added build target for documentation in sublime project file and fixed typos in Node documentation --- .gitignore | 3 ++- include/gua/scenegraph/Node.hpp | 13 +++++++++++-- scripts/guacamole.sublime-project | 7 +++++++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index ba7977ed0..5f02af41f 100644 --- a/.gitignore +++ b/.gitignore @@ -21,7 +21,8 @@ build lib -doc/html +doc/doc/* +doc/libpeerconnection.log editor/guacamole_editor guarc/guarc guacamole.sublime-workspace diff --git a/include/gua/scenegraph/Node.hpp b/include/gua/scenegraph/Node.hpp index d50251f01..0a0a3d051 100644 --- a/include/gua/scenegraph/Node.hpp +++ b/include/gua/scenegraph/Node.hpp @@ -96,8 +96,8 @@ class GUA_DLL Node { * This adds a Node to the Node's children vector and returns a shared pointer * to the new child. * - * \param child The Node to be added as a child. - * \tparam T The type of the Node to be added as child. + * \param node_name The name of the new Node to be added as a child. + * \tparam T The type of the new Node to be added as a child. */ template std::shared_ptr add_child(std::string const& node_name) { @@ -106,6 +106,15 @@ class GUA_DLL Node { return add_child(new_node); } + /** + * Adds a child. + * + * This adds a Node to the Node's children vector and returns a shared pointer + * to the new child. + * + * \param new_node The new Node to be added as a child. + * \tparam T The type of the new Node to be added as a child. + */ template std::shared_ptr add_child(std::shared_ptr const& new_node) { diff --git a/scripts/guacamole.sublime-project b/scripts/guacamole.sublime-project index e0faec1a9..1fd880024 100644 --- a/scripts/guacamole.sublime-project +++ b/scripts/guacamole.sublime-project @@ -15,6 +15,13 @@ "file_regex": "^(..[^:]*):(.*)$", "working_dir": "${project_path}/..", "cmd": ["./scripts/make.sh"] + }, + { + "name": "guacamole-doc", + "file_regex": "^(..[^:]*):(.*)$", + "working_dir": "${project_path}/../doc/", + "shell" : "true", + "cmd": ["doxygen && xdg-open \"doc/html/index.html\""] } ] } From fb31e545651d677fd543f96a07d0292a9c4899c1 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Tue, 25 Feb 2014 15:09:46 +0100 Subject: [PATCH 105/146] moved background mode enum and PipelineConfiguration inside Pipeline class --- include/gua/renderer/CompositePass.hpp | 4 +- include/gua/renderer/GBufferPass.hpp | 1 - include/gua/renderer/Pass.hpp | 2 - include/gua/renderer/Pipeline.hpp | 178 ++++++++++++------------- include/gua/renderer/PostFXPass.hpp | 2 - include/gua/renderer/StereoBuffer.hpp | 4 +- src/gua/renderer/CompositePass.cpp | 7 +- src/gua/renderer/FinalPass.cpp | 2 +- src/gua/renderer/GBufferPass.cpp | 3 +- src/gua/renderer/Pass.cpp | 3 +- src/gua/renderer/Pipeline.cpp | 10 +- src/gua/renderer/PostFXPass.cpp | 29 ++-- src/gua/renderer/StereoBuffer.cpp | 2 +- 13 files changed, 118 insertions(+), 129 deletions(-) diff --git a/include/gua/renderer/CompositePass.hpp b/include/gua/renderer/CompositePass.hpp index e79b0c396..9446ac1b2 100644 --- a/include/gua/renderer/CompositePass.hpp +++ b/include/gua/renderer/CompositePass.hpp @@ -30,7 +30,6 @@ namespace gua { class GBuffer; -struct PipelineConfiguration; /** * @@ -44,12 +43,11 @@ class CompositePass : public Pass { CompositePass(Pipeline* pipeline); /** - * + * */ virtual ~CompositePass(); virtual void create(RenderContext const& ctx, - PipelineConfiguration const& config, std::vector > const& layers); diff --git a/include/gua/renderer/GBufferPass.hpp b/include/gua/renderer/GBufferPass.hpp index dc1965d0f..2c2a7c334 100644 --- a/include/gua/renderer/GBufferPass.hpp +++ b/include/gua/renderer/GBufferPass.hpp @@ -52,7 +52,6 @@ class GBufferPass : public GeometryPass { void create( RenderContext const& ctx, - PipelineConfiguration const& config, std::vector > const& layers); diff --git a/include/gua/renderer/Pass.hpp b/include/gua/renderer/Pass.hpp index f9d60d3be..2c92a6989 100644 --- a/include/gua/renderer/Pass.hpp +++ b/include/gua/renderer/Pass.hpp @@ -38,7 +38,6 @@ namespace gua { class Pipeline; -struct PipelineConfiguration; struct SerializedScene; struct Camera; class LayerMapping; @@ -76,7 +75,6 @@ class Pass { virtual void create( RenderContext const& ctx, - PipelineConfiguration const& config, std::vector > const& layers); diff --git a/include/gua/renderer/Pipeline.hpp b/include/gua/renderer/Pipeline.hpp index 0b74c88a6..4f47306eb 100644 --- a/include/gua/renderer/Pipeline.hpp +++ b/include/gua/renderer/Pipeline.hpp @@ -45,94 +45,6 @@ class FinalUberShader; class PostFXShader; class Serializer; -enum BackgroundMode { - COLOR = 0, - SKYMAP_TEXTURE = 1, - QUAD_TEXTURE = 2, -}; - -struct PipelineConfiguration { - - // camera for this pipeline - GUA_ADD_PROPERTY(Camera, camera, Camera()); - - // if set to false, this pipeline won't render anything - GUA_ADD_PROPERTY(bool, enabled, true); - - // global clipping plane nothing - GUA_ADD_PROPERTY(bool, enable_global_clipping_plane, false); - GUA_ADD_PROPERTY(math::vec4, global_clipping_plane, math::vec4(0, 1, 0, 0)); - - // the final image of this pipeline will be stored in the texture database - // with this name. if enable_stereo is set to true, two images with postfixes - // _left and _right will be stored - GUA_ADD_PROPERTY(std::string, output_texture_name, "gua_pipeline"); - - // stereo configuration - GUA_ADD_PROPERTY(math::vec2ui, left_resolution, math::vec2ui(800, 600)); - GUA_ADD_PROPERTY(math::vec2ui, right_resolution, math::vec2ui(800, 600)); - GUA_ADD_PROPERTY(bool, enable_stereo, false); - - // various display options - GUA_ADD_PROPERTY(bool, enable_preview_display, false); - GUA_ADD_PROPERTY(bool, enable_fps_display, false); - GUA_ADD_PROPERTY(bool, enable_ray_display, false); - GUA_ADD_PROPERTY(bool, enable_bbox_display, false); - GUA_ADD_PROPERTY(bool, enable_wireframe, false); - - // FXAA - GUA_ADD_PROPERTY(bool, enable_fxaa, false); - - // clipping - GUA_ADD_PROPERTY(float, near_clip, 0.1f); - GUA_ADD_PROPERTY(float, far_clip, 1000.0f); - - // culling - GUA_ADD_PROPERTY(bool, enable_frustum_culling, true); - GUA_ADD_PROPERTY(bool, enable_backface_culling, true); - - // screen space ambient occlusion - GUA_ADD_PROPERTY(bool, enable_ssao, false); - GUA_ADD_PROPERTY(float, ssao_radius, 2.0f); - GUA_ADD_PROPERTY(float, ssao_intensity, 1.0f); - GUA_ADD_PROPERTY(float, ssao_falloff, 1.0f); - - // bloom - GUA_ADD_PROPERTY(bool, enable_bloom, false); - GUA_ADD_PROPERTY(float, bloom_radius, 10.0f); - GUA_ADD_PROPERTY(float, bloom_threshold, 0.8f); - GUA_ADD_PROPERTY(float, bloom_intensity, 0.4f); - - // fog - GUA_ADD_PROPERTY(bool, enable_fog, false); - GUA_ADD_PROPERTY(float, fog_start, 100.0f); - GUA_ADD_PROPERTY(float, fog_end, 1000.0f); - GUA_ADD_PROPERTY(std::string, fog_texture, ""); - GUA_ADD_PROPERTY(utils::Color3f, fog_color, utils::Color3f()); - - // background image / color - GUA_ADD_PROPERTY(BackgroundMode, background_mode, BackgroundMode::SKYMAP_TEXTURE); - GUA_ADD_PROPERTY(std::string, background_texture, ""); - GUA_ADD_PROPERTY(utils::Color3f, background_color, utils::Color3f()); - - // ambient color - GUA_ADD_PROPERTY(utils::Color3f, ambient_color, utils::Color3f(0.1f, 0.1f, 0.1f)); - - // vignette - GUA_ADD_PROPERTY(bool, enable_vignette, false); - GUA_ADD_PROPERTY(utils::Color3f, vignette_color, utils::Color3f()); - GUA_ADD_PROPERTY(float, vignette_coverage, 0.3f); - GUA_ADD_PROPERTY(float, vignette_softness, 0.5f); - - // HDR - GUA_ADD_PROPERTY(bool, enable_hdr, false); - GUA_ADD_PROPERTY(float, hdr_key, 1.f); - - // NURBS tesselation - GUA_ADD_PROPERTY(int, max_tesselation, 4); - GUA_ADD_PROPERTY(float, tesselation_max_error, 8.0f); -}; - /** * A rendering pipeline describes how an image is generated. * @@ -143,6 +55,94 @@ struct PipelineConfiguration { class GUA_DLL Pipeline { public: + enum BackgroundMode { + COLOR = 0, + SKYMAP_TEXTURE = 1, + QUAD_TEXTURE = 2, + }; + + struct Configuration { + + // camera for this pipeline + GUA_ADD_PROPERTY(Camera, camera, Camera()); + + // if set to false, this pipeline won't render anything + GUA_ADD_PROPERTY(bool, enabled, true); + + // global clipping plane nothing + GUA_ADD_PROPERTY(bool, enable_global_clipping_plane, false); + GUA_ADD_PROPERTY(math::vec4, global_clipping_plane, math::vec4(0, 1, 0, 0)); + + // the final image of this pipeline will be stored in the texture database + // with this name. if enable_stereo is set to true, two images with postfixes + // _left and _right will be stored + GUA_ADD_PROPERTY(std::string, output_texture_name, "gua_pipeline"); + + // stereo configuration + GUA_ADD_PROPERTY(math::vec2ui, left_resolution, math::vec2ui(800, 600)); + GUA_ADD_PROPERTY(math::vec2ui, right_resolution, math::vec2ui(800, 600)); + GUA_ADD_PROPERTY(bool, enable_stereo, false); + + // various display options + GUA_ADD_PROPERTY(bool, enable_preview_display, false); + GUA_ADD_PROPERTY(bool, enable_fps_display, false); + GUA_ADD_PROPERTY(bool, enable_ray_display, false); + GUA_ADD_PROPERTY(bool, enable_bbox_display, false); + GUA_ADD_PROPERTY(bool, enable_wireframe, false); + + // FXAA + GUA_ADD_PROPERTY(bool, enable_fxaa, false); + + // clipping + GUA_ADD_PROPERTY(float, near_clip, 0.1f); + GUA_ADD_PROPERTY(float, far_clip, 1000.0f); + + // culling + GUA_ADD_PROPERTY(bool, enable_frustum_culling, true); + GUA_ADD_PROPERTY(bool, enable_backface_culling, true); + + // screen space ambient occlusion + GUA_ADD_PROPERTY(bool, enable_ssao, false); + GUA_ADD_PROPERTY(float, ssao_radius, 2.0f); + GUA_ADD_PROPERTY(float, ssao_intensity, 1.0f); + GUA_ADD_PROPERTY(float, ssao_falloff, 1.0f); + + // bloom + GUA_ADD_PROPERTY(bool, enable_bloom, false); + GUA_ADD_PROPERTY(float, bloom_radius, 10.0f); + GUA_ADD_PROPERTY(float, bloom_threshold, 0.8f); + GUA_ADD_PROPERTY(float, bloom_intensity, 0.4f); + + // fog + GUA_ADD_PROPERTY(bool, enable_fog, false); + GUA_ADD_PROPERTY(float, fog_start, 100.0f); + GUA_ADD_PROPERTY(float, fog_end, 1000.0f); + GUA_ADD_PROPERTY(std::string, fog_texture, ""); + GUA_ADD_PROPERTY(utils::Color3f, fog_color, utils::Color3f()); + + // background image / color + GUA_ADD_PROPERTY(BackgroundMode, background_mode, BackgroundMode::SKYMAP_TEXTURE); + GUA_ADD_PROPERTY(std::string, background_texture, ""); + GUA_ADD_PROPERTY(utils::Color3f, background_color, utils::Color3f()); + + // ambient color + GUA_ADD_PROPERTY(utils::Color3f, ambient_color, utils::Color3f(0.1f, 0.1f, 0.1f)); + + // vignette + GUA_ADD_PROPERTY(bool, enable_vignette, false); + GUA_ADD_PROPERTY(utils::Color3f, vignette_color, utils::Color3f()); + GUA_ADD_PROPERTY(float, vignette_coverage, 0.3f); + GUA_ADD_PROPERTY(float, vignette_softness, 0.5f); + + // HDR + GUA_ADD_PROPERTY(bool, enable_hdr, false); + GUA_ADD_PROPERTY(float, hdr_key, 1.f); + + // NURBS tesselation + GUA_ADD_PROPERTY(int, max_tesselation, 4); + GUA_ADD_PROPERTY(float, tesselation_max_error, 8.0f); + }; + enum PipelineStage { geometry = 0, lighting = 1, @@ -167,7 +167,7 @@ class GUA_DLL Pipeline { */ virtual ~Pipeline(); - PipelineConfiguration config; + Configuration config; void print_shaders(std::string const& directory) const; diff --git a/include/gua/renderer/PostFXPass.hpp b/include/gua/renderer/PostFXPass.hpp index c8e08bed0..a579247ec 100644 --- a/include/gua/renderer/PostFXPass.hpp +++ b/include/gua/renderer/PostFXPass.hpp @@ -30,7 +30,6 @@ namespace gua { class PostGBufferMeshUberShader; class GBuffer; -struct PipelineConfiguration; /** * @@ -52,7 +51,6 @@ class PostFXPass : public Pass { void create( RenderContext const& ctx, - PipelineConfiguration const& config, std::vector > const& layers); diff --git a/include/gua/renderer/StereoBuffer.hpp b/include/gua/renderer/StereoBuffer.hpp index 284f9124c..fe1929323 100644 --- a/include/gua/renderer/StereoBuffer.hpp +++ b/include/gua/renderer/StereoBuffer.hpp @@ -24,10 +24,10 @@ // guacamole headers #include +#include namespace gua { -struct PipelineConfiguration; /** * */ @@ -38,7 +38,7 @@ class StereoBuffer { */ StereoBuffer( RenderContext const& ctx, - PipelineConfiguration const& config, + Pipeline::Configuration const& config, std::vector > const& layers); diff --git a/src/gua/renderer/CompositePass.cpp b/src/gua/renderer/CompositePass.cpp index 85a820c73..1e652e6c6 100644 --- a/src/gua/renderer/CompositePass.cpp +++ b/src/gua/renderer/CompositePass.cpp @@ -73,8 +73,7 @@ CompositePass::~CompositePass() { //////////////////////////////////////////////////////////////////////////////// -void CompositePass::create(RenderContext const& ctx, - PipelineConfiguration const& config, std::vector> const& layers) { // reuse gbuffer from shading-pass @@ -93,8 +92,8 @@ void CompositePass::create(RenderContext const& ctx, layer_3f_desc.push_back(std::make_pair(BufferComponent::F3, state)); volume_raygeneration_buffer_ = new GBuffer(layer_3f_desc, - config.get_left_resolution()[0], - config.get_left_resolution()[1]); + pipeline_->config.get_left_resolution()[0], + pipeline_->config.get_left_resolution()[1]); volume_raygeneration_buffer_->create(ctx); } diff --git a/src/gua/renderer/FinalPass.cpp b/src/gua/renderer/FinalPass.cpp index d85684056..fa01bcd0e 100644 --- a/src/gua/renderer/FinalPass.cpp +++ b/src/gua/renderer/FinalPass.cpp @@ -72,7 +72,7 @@ void FinalPass::set_uniforms(SerializedScene const& scene, static_cast(pipeline_->config.background_mode()), "gua_background_mode"); - if (pipeline_->config.background_mode() == BackgroundMode::COLOR || pipeline_->config.background_texture() == "") + if (pipeline_->config.background_mode() == Pipeline::BackgroundMode::COLOR || pipeline_->config.background_texture() == "") shader_->set_uniform( ctx, pipeline_->config.background_color(), "gua_background_color"); else diff --git a/src/gua/renderer/GBufferPass.cpp b/src/gua/renderer/GBufferPass.cpp index 73938fb0c..c719cc63e 100644 --- a/src/gua/renderer/GBufferPass.cpp +++ b/src/gua/renderer/GBufferPass.cpp @@ -66,7 +66,6 @@ GBufferPass::~GBufferPass() { void GBufferPass::create( RenderContext const& ctx, - PipelineConfiguration const& config, std::vector > const& layers) { @@ -77,7 +76,7 @@ void GBufferPass::create( auto tmp(layers); tmp.insert(tmp.begin(), std::make_pair(BufferComponent::DEPTH_24, state)); - Pass::create(ctx, config, tmp); + Pass::create(ctx, tmp); } diff --git a/src/gua/renderer/Pass.cpp b/src/gua/renderer/Pass.cpp index efc0588bf..315a3b1f8 100644 --- a/src/gua/renderer/Pass.cpp +++ b/src/gua/renderer/Pass.cpp @@ -38,7 +38,6 @@ Pass::Pass(Pipeline* pipeline) : gbuffer_(nullptr), pipeline_(pipeline) {} void Pass::create( RenderContext const& ctx, - PipelineConfiguration const& config, std::vector > const& layers) { @@ -46,7 +45,7 @@ void Pass::create( gbuffer_->remove_buffers(ctx); } - gbuffer_ = std::make_shared(ctx, config, layers); + gbuffer_ = std::make_shared(ctx, pipeline_->config, layers); } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/gua/renderer/Pipeline.cpp b/src/gua/renderer/Pipeline.cpp index dfd924db4..dec7bfc71 100644 --- a/src/gua/renderer/Pipeline.cpp +++ b/src/gua/renderer/Pipeline.cpp @@ -441,26 +441,26 @@ void Pipeline::create_buffers() { std::vector> stereobuffers; - passes_[PipelineStage::geometry]->create(*context_, config, passes_[PipelineStage::geometry]->get_gbuffer_mapping()->get_layers()); + passes_[PipelineStage::geometry]->create(*context_, passes_[PipelineStage::geometry]->get_gbuffer_mapping()->get_layers()); stereobuffers.push_back(passes_[PipelineStage::geometry]->get_gbuffer()); - passes_[PipelineStage::lighting]->create(*context_, config, passes_[PipelineStage::lighting]->get_gbuffer_mapping()->get_layers()); + passes_[PipelineStage::lighting]->create(*context_, passes_[PipelineStage::lighting]->get_gbuffer_mapping()->get_layers()); passes_[PipelineStage::lighting]->set_inputs(stereobuffers); stereobuffers.push_back(passes_[PipelineStage::lighting]->get_gbuffer()); - passes_[PipelineStage::shading]->create(*context_, config, passes_[PipelineStage::shading]->get_gbuffer_mapping()->get_layers()); + passes_[PipelineStage::shading]->create(*context_, passes_[PipelineStage::shading]->get_gbuffer_mapping()->get_layers()); passes_[PipelineStage::shading]->set_inputs(stereobuffers); stereobuffers.push_back(passes_[PipelineStage::shading]->get_gbuffer()); passes_[PipelineStage::compositing]->set_inputs(stereobuffers); - passes_[PipelineStage::compositing]->create(*context_, config, {} ); + passes_[PipelineStage::compositing]->create(*context_, {} ); stereobuffers.push_back(passes_[PipelineStage::compositing]->get_gbuffer()); scm::gl::sampler_state_desc state(scm::gl::FILTER_MIN_MAG_LINEAR, scm::gl::WRAP_MIRRORED_REPEAT, scm::gl::WRAP_MIRRORED_REPEAT); - passes_[PipelineStage::postfx]->create(*context_, config, { { BufferComponent::F3, state } }); + passes_[PipelineStage::postfx]->create(*context_, { { BufferComponent::F3, state } }); passes_[PipelineStage::postfx]->set_inputs(stereobuffers); if (!config.get_enable_stereo()) { diff --git a/src/gua/renderer/PostFXPass.cpp b/src/gua/renderer/PostFXPass.cpp index 001608df6..b456d806f 100644 --- a/src/gua/renderer/PostFXPass.cpp +++ b/src/gua/renderer/PostFXPass.cpp @@ -168,10 +168,9 @@ PostFXPass::~PostFXPass() { //////////////////////////////////////////////////////////////////////////////// -void PostFXPass::create(RenderContext const& ctx, - PipelineConfiguration const& config, std::vector> const& layers) { - Pass::create(ctx, config, layers); + Pass::create(ctx, layers); for (auto p: godray_buffers_) { p->remove_buffers(ctx); @@ -202,8 +201,8 @@ void PostFXPass::create(RenderContext const& ctx, delete luminance_buffer_; } - ping_buffer_ = new StereoBuffer(ctx, config, layers); - pong_buffer_ = new StereoBuffer(ctx, config, layers); + ping_buffer_ = new StereoBuffer(ctx, pipeline_->config, layers); + pong_buffer_ = new StereoBuffer(ctx, pipeline_->config, layers); scm::gl::sampler_state_desc state(scm::gl::FILTER_MIN_MAG_LINEAR, scm::gl::WRAP_CLAMP_TO_EDGE, @@ -213,25 +212,25 @@ void PostFXPass::create(RenderContext const& ctx, layer_3f_desc.push_back(std::make_pair(BufferComponent::F3, state)); godray_buffers_.push_back(new GBuffer(layer_3f_desc, - config.get_left_resolution()[0]/2, - config.get_left_resolution()[1]/2)); + pipeline_->config.get_left_resolution()[0]/2, + pipeline_->config.get_left_resolution()[1]/2)); godray_buffers_.push_back(new GBuffer(layer_3f_desc, - config.get_left_resolution()[0]/2, - config.get_left_resolution()[1]/2)); + pipeline_->config.get_left_resolution()[0]/2, + pipeline_->config.get_left_resolution()[1]/2)); godray_buffers_.push_back(new GBuffer(layer_3f_desc, - config.get_left_resolution()[0]/2, - config.get_left_resolution()[1]/2)); + pipeline_->config.get_left_resolution()[0]/2, + pipeline_->config.get_left_resolution()[1]/2)); for (auto buffer: godray_buffers_) { buffer->create(ctx); } glow_buffers_.push_back(new GBuffer(layer_3f_desc, - config.get_left_resolution()[0]/2, - config.get_left_resolution()[1]/2)); + pipeline_->config.get_left_resolution()[0]/2, + pipeline_->config.get_left_resolution()[1]/2)); glow_buffers_.push_back(new GBuffer(layer_3f_desc, - config.get_left_resolution()[0]/2, - config.get_left_resolution()[1]/2)); + pipeline_->config.get_left_resolution()[0]/2, + pipeline_->config.get_left_resolution()[1]/2)); for (auto buffer: glow_buffers_) { buffer->create(ctx); diff --git a/src/gua/renderer/StereoBuffer.cpp b/src/gua/renderer/StereoBuffer.cpp index 469872706..c994ab69e 100644 --- a/src/gua/renderer/StereoBuffer.cpp +++ b/src/gua/renderer/StereoBuffer.cpp @@ -34,7 +34,7 @@ namespace gua { StereoBuffer::StereoBuffer( RenderContext const& ctx, - PipelineConfiguration const& config, + Pipeline::Configuration const& config, std::vector > const& layers) { From 44568f762c8a1e7bcbd96e90b7bf0ab369aa4af1 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Tue, 25 Feb 2014 15:11:00 +0100 Subject: [PATCH 106/146] added scenegraph and database modules in Doxygen documentation --- doc/Doxyfile | 1614 ++++++++--------- .../gua/databases/CollisionShapeDatabase.hpp | 2 + include/gua/databases/Database.hpp | 2 + include/gua/databases/Doxygen.hpp | 30 + include/gua/databases/GeometryDatabase.hpp | 2 + include/gua/databases/MaterialDatabase.hpp | 2 + include/gua/databases/Resources.hpp | 4 +- .../gua/databases/ShadingModelDatabase.hpp | 2 + include/gua/databases/TextureDatabase.hpp | 2 + include/gua/scenegraph/Doxygen.hpp | 30 + include/gua/scenegraph/GeometryNode.hpp | 7 +- include/gua/scenegraph/LODNode.hpp | 6 +- include/gua/scenegraph/Node.hpp | 12 +- include/gua/scenegraph/NodeVisitor.hpp | 1 + include/gua/scenegraph/PointLightNode.hpp | 17 +- include/gua/scenegraph/RayNode.hpp | 6 +- include/gua/scenegraph/SceneGraph.hpp | 2 + include/gua/scenegraph/ScreenNode.hpp | 6 +- include/gua/scenegraph/SpotLightNode.hpp | 6 +- include/gua/scenegraph/SunLightNode.hpp | 6 +- include/gua/scenegraph/TexturedQuadNode.hpp | 6 +- include/gua/scenegraph/TransformNode.hpp | 6 +- include/gua/scenegraph/VolumeNode.hpp | 10 +- include/gua/utils/configuration_macro.hpp | 29 +- 24 files changed, 950 insertions(+), 860 deletions(-) create mode 100644 include/gua/databases/Doxygen.hpp create mode 100644 include/gua/scenegraph/Doxygen.hpp diff --git a/doc/Doxyfile b/doc/Doxyfile index 0128f85f0..12c1beb60 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -14,325 +14,325 @@ # Project related configuration options #--------------------------------------------------------------------------- -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all -# text before the first occurrence of this tag. Doxygen uses libiconv (or the -# iconv built into libc) for the transcoding. See +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 -# The PROJECT_NAME tag is a single word (or sequence of words) that should -# identify the project. Note that if you do not use Doxywizard you need +# The PROJECT_NAME tag is a single word (or sequence of words) that should +# identify the project. Note that if you do not use Doxywizard you need # to put quotes around the project name if it contains spaces. -PROJECT_NAME = +PROJECT_NAME = "My Project" -# The PROJECT_NUMBER tag can be used to enter a project or revision number. -# This could be handy for archiving the generated documentation or +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or # if some version control system is used. -PROJECT_NUMBER = +PROJECT_NUMBER = -# Using the PROJECT_BRIEF tag one can provide an optional one line description -# for a project that appears at the top of each page and should give viewer +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer # a quick idea about the purpose of the project. Keep the description short. -PROJECT_BRIEF = +PROJECT_BRIEF = -# With the PROJECT_LOGO tag one can specify an logo or icon that is -# included in the documentation. The maximum height of the logo should not -# exceed 55 pixels and the maximum width should not exceed 200 pixels. +# With the PROJECT_LOGO tag one can specify an logo or icon that is +# included in the documentation. The maximum height of the logo should not +# exceed 55 pixels and the maximum width should not exceed 200 pixels. # Doxygen will copy the logo to the output directory. -PROJECT_LOGO = +PROJECT_LOGO = -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) -# base path where the generated documentation will be put. -# If a relative path is entered, it will be relative to the location +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = doc -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create -# 4096 sub-directories (in 2 levels) under the output directory of each output -# format and will distribute the generated files over these directories. -# Enabling this option can be useful when feeding doxygen a huge amount of -# source files, where putting all generated files in the same directory would +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = YES -# The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all constant output in the proper language. -# The default language is English, other supported languages are: -# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, -# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, -# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English -# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, -# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English -# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will -# include brief member descriptions after the members that are listed in -# the file and class documentation (similar to JavaDoc). +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES -# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend -# the brief description of a member or function before the detailed description. -# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES -# This tag implements a quasi-intelligent brief description abbreviator -# that is used to form the text in various listings. Each string -# in this list, if found as the leading text of the brief description, will be -# stripped from the text and the result after processing the whole list, is -# used as the annotated text. Otherwise, the brief description is used as-is. -# If left blank, the following values are used ("$name" is automatically -# replaced with the name of the entity): "The $name class" "The $name widget" -# "The $name file" "is" "provides" "specifies" "contains" +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" -ABBREVIATE_BRIEF = +ABBREVIATE_BRIEF = -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# Doxygen will generate a detailed section even if there is only a brief +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all -# inherited members of a class in the documentation of that class as if those -# members were ordinary class members. Constructors, destructors and assignment +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO -# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full -# path before files name in the file list and in the header files. If set +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES -# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag -# can be used to strip a user-defined part of the path. Stripping is -# only done if one of the specified strings matches the left-hand part of -# the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the # path to strip. -STRIP_FROM_PATH = +STRIP_FROM_PATH = -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of -# the path mentioned in the documentation of a class, which tells -# the reader which header file to include in order to use a class. -# If left blank only the name of the header file containing the class -# definition is used. Otherwise one should specify the include paths that +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. -STRIP_FROM_INC_PATH = +STRIP_FROM_INC_PATH = -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter -# (but less readable) file names. This can be useful if your file system +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful if your file system # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO -# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen -# will interpret the first line (until the first dot) of a JavaDoc-style -# comment as the brief description. If set to NO, the JavaDoc -# comments will behave just like regular Qt-style comments +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) -JAVADOC_AUTOBRIEF = NO +JAVADOC_AUTOBRIEF = YES -# If the QT_AUTOBRIEF tag is set to YES then Doxygen will -# interpret the first line (until the first dot) of a Qt-style -# comment as the brief description. If set to NO, the comments -# will behave just like regular Qt-style comments (thus requiring +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen -# treat a multi-line C++ special comment block (i.e. a block of //! or /// -# comments) as a brief description. This used to be the default behaviour. -# The new default is to treat a multi-line C++ comment block as a detailed +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO -# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented -# member inherits the documentation from any documented member that it +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce -# a new page for each member. If set to NO, the documentation of a member will +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO -# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 2 -# This tag can be used to specify a number of aliases that acts -# as commands in the documentation. An alias has the form "name=value". -# For example adding "sideeffect=\par Side Effects:\n" will allow you to -# put the command \sideeffect (or @sideeffect) in the documentation, which -# will result in a user-defined paragraph with heading "Side Effects:". +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. -ALIASES = +ALIASES = -# This tag can be used to specify a number of word-keyword mappings (TCL only). -# A mapping has the form "name=value". For example adding -# "class=itcl::class" will allow you to use the command class in the +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding +# "class=itcl::class" will allow you to use the command class in the # itcl::class meaning. -TCL_SUBST = +TCL_SUBST = -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C -# sources only. Doxygen will then generate output that is more tailored for C. -# For instance, some of the names that are used will be different. The list +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java -# sources only. Doxygen will then generate output that is more tailored for -# Java. For instance, namespaces will be presented as packages, qualified +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO -# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran -# sources only. Doxygen will then generate output that is more tailored for +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO -# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL -# sources. Doxygen will then generate output that is tailored for +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO -# Doxygen selects the parser to use depending on the extension of the files it -# parses. With this tag you can assign which parser to use for a given extension. -# Doxygen has a built-in mapping, but you can override or extend it using this -# tag. The format is ext=language, where ext is a file extension, and language -# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, -# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make -# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C -# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given extension. +# Doxygen has a built-in mapping, but you can override or extend it using this +# tag. The format is ext=language, where ext is a file extension, and language +# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, +# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make +# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C +# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions # you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. -EXTENSION_MAPPING = +EXTENSION_MAPPING = -# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want -# to include (a tag file for) the STL sources as input, then you should -# set this tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. -# func(std::string) {}). This also makes the inheritance and collaboration +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also makes the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = YES -# If you use Microsoft's C++/CLI language, you should set this option to YES to +# If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO -# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. -# Doxygen will parse them like normal C++ but will assume all classes use public +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO -# For Microsoft's IDL there are propget and propput attributes to indicate getter -# and setter methods for a property. Setting this option to YES (the default) -# will make doxygen replace the get and set methods by a property in the -# documentation. This will only work if the methods are indeed getting or -# setting a simple type. If this is not the case, or you want to show the +# For Microsoft's IDL there are propget and propput attributes to indicate getter +# and setter methods for a property. Setting this option to YES (the default) +# will make doxygen replace the get and set methods by a property in the +# documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = NO -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = YES -# Set the SUBGROUPING tag to YES (the default) to allow class member groups of -# the same type (for instance a group of public functions) to be put as a -# subgroup of that type (e.g. under the Public Functions section). Set it to -# NO to prevent subgrouping. Alternatively, this can be done per class using +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES -# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and -# unions are shown inside the group in which they are included (e.g. using -# @ingroup) instead of on a separate page (for HTML and Man pages) or +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and +# unions are shown inside the group in which they are included (e.g. using +# @ingroup) instead of on a separate page (for HTML and Man pages) or # section (for LaTeX and RTF). INLINE_GROUPED_CLASSES = NO -# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and -# unions with only public data fields will be shown inline in the documentation -# of the scope in which they are defined (i.e. file, namespace, or group -# documentation), provided this scope is documented. If set to NO (the default), -# structs, classes, and unions are shown on a separate page (for HTML and Man +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and +# unions with only public data fields will be shown inline in the documentation +# of the scope in which they are defined (i.e. file, namespace, or group +# documentation), provided this scope is documented. If set to NO (the default), +# structs, classes, and unions are shown on a separate page (for HTML and Man # pages) or section (for LaTeX and RTF). INLINE_SIMPLE_STRUCTS = YES -# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum -# is documented as struct, union, or enum with the name of the typedef. So -# typedef struct TypeS {} TypeT, will appear in the documentation as a struct -# with name TypeT. When disabled the typedef will appear as a member of a file, -# namespace, or class. And the struct will be named TypeS. This can typically -# be useful for C code in case the coding convention dictates that all compound +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO -# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to -# determine which symbols to keep in memory and which to flush to disk. -# When the cache is full, less often used symbols will be written to disk. -# For small to medium size projects (<1000 input files) the default value is -# probably good enough. For larger projects a too small cache size can cause -# doxygen to be busy swapping symbols to and from disk most of the time -# causing a significant performance penalty. -# If the system has enough physical memory increasing the cache will improve the -# performance by keeping more symbols in memory. Note that the value works on -# a logarithmic scale so increasing the size by one will roughly double the -# memory usage. The cache size is given by this formula: -# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penalty. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will roughly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols. SYMBOL_CACHE_SIZE = 0 -# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be -# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given -# their name and scope. Since this can be an expensive process and often the -# same symbol appear multiple times in the code, doxygen keeps a cache of -# pre-resolved symbols. If the cache is too small doxygen will become slower. -# If the cache is too large, memory is wasted. The cache size is given by this -# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, +# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be +# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given +# their name and scope. Since this can be an expensive process and often the +# same symbol appear multiple times in the code, doxygen keeps a cache of +# pre-resolved symbols. If the cache is too small doxygen will become slower. +# If the cache is too large, memory is wasted. The cache size is given by this +# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols. LOOKUP_CACHE_SIZE = 0 @@ -341,479 +341,479 @@ LOOKUP_CACHE_SIZE = 0 # Build related configuration options #--------------------------------------------------------------------------- -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in -# documentation are documented, even if no documentation was available. -# Private class members and static file members will be hidden unless +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO -# If the EXTRACT_STATIC tag is set to YES all static members of a file +# If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = YES -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) -# defined locally in source files will be included in the documentation. +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES -# This flag is only useful for Objective-C code. When set to YES local -# methods, which are defined in the implementation section but not in -# the interface are included in the documentation. +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO -# If this flag is set to YES, the members of anonymous namespaces will be -# extracted and appear in the documentation as a namespace called -# 'anonymous_namespace{file}', where file will be replaced with the base -# name of the file that contains the anonymous namespace. By default +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default # anonymous namespaces are hidden. EXTRACT_ANON_NSPACES = NO -# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all -# undocumented members of documented classes, files or namespaces. -# If set to NO (the default) these members will be included in the -# various overviews, but no documentation section is generated. +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO -# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. -# If set to NO (the default) these classes will be included in the various +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all -# friend (class|struct|union) declarations. -# If set to NO (the default) these declarations will be included in the +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO -# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any -# documentation blocks found inside the body of a function. -# If set to NO (the default) these blocks will be appended to the +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO -# The INTERNAL_DOCS tag determines if documentation -# that is typed after a \internal command is included. If the tag is set -# to NO (the default) then the documentation will be excluded. +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO -# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate -# file names in lower-case letters. If set to YES upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = NO -# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen -# will show members with their full class and namespace scopes in the +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = YES -# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen -# will put a list of the files that are included by a file in the documentation +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES -# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen -# will list include files with double quotes in the documentation +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO -# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES -# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen -# will sort the (detailed) documentation of file and class members -# alphabetically by member name. If set to NO the members will appear in +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in # declaration order. -SORT_MEMBER_DOCS = NO +SORT_MEMBER_DOCS = YES -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the -# brief documentation of file, namespace and class members alphabetically -# by member name. If set to NO (the default) the members will appear in +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO -# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen -# will sort the (brief and detailed) documentation of class members so that -# constructors and destructors are listed first. If set to NO (the default) -# the constructors will appear in the respective orders defined by -# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. -# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. -SORT_MEMBERS_CTORS_1ST = NO +SORT_MEMBERS_CTORS_1ST = YES -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the -# hierarchy of group names into alphabetical order. If set to NO (the default) +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be -# sorted by fully-qualified names, including namespaces. If set to -# NO (the default), the class list will be sorted only by class name, -# not including the namespace part. -# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO -# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to -# do proper type resolution of all parameters of a function it will reject a -# match between the prototype and the implementation of a member function even -# if there is only one candidate or it is obvious which candidate to choose -# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to +# do proper type resolution of all parameters of a function it will reject a +# match between the prototype and the implementation of a member function even +# if there is only one candidate or it is obvious which candidate to choose +# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen # will still accept a match between prototype and implementation in such cases. STRICT_PROTO_MATCHING = NO -# The GENERATE_TODOLIST tag can be used to enable (YES) or -# disable (NO) the todo list. This list is created by putting \todo +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = NO -# The GENERATE_TESTLIST tag can be used to enable (YES) or -# disable (NO) the test list. This list is created by putting \test +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = NO -# The GENERATE_BUGLIST tag can be used to enable (YES) or -# disable (NO) the bug list. This list is created by putting \bug +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = NO -# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or -# disable (NO) the deprecated list. This list is created by putting +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= NO -# The ENABLED_SECTIONS tag can be used to enable conditional +# The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. -ENABLED_SECTIONS = +ENABLED_SECTIONS = -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines -# the initial value of a variable or macro consists of for it to appear in -# the documentation. If the initializer consists of more lines than specified -# here it will be hidden. Use a value of 0 to hide initializers completely. -# The appearance of the initializer of individual variables and macros in the -# documentation can be controlled using \showinitializer or \hideinitializer +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or macro consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and macros in the +# documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated -# at the bottom of the documentation of classes and structs. If set to YES the +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = NO -# If the sources in your project are distributed over multiple directories -# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO -# Set the SHOW_FILES tag to NO to disable the generation of the Files page. -# This will remove the Files entry from the Quick Index and from the +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = NO -# Set the SHOW_NAMESPACES tag to NO to disable the generation of the -# Namespaces page. This will remove the Namespaces entry from the Quick Index +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = NO -# The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from -# the version control system). Doxygen will invoke the program by executing (via -# popen()) the command , where is the value of -# the FILE_VERSION_FILTER tag, and is the name of an input file -# provided by doxygen. Whatever the program writes to standard output +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. -FILE_VERSION_FILTER = +FILE_VERSION_FILTER = -# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed -# by doxygen. The layout file controls the global structure of the generated -# output files in an output format independent way. The create the layout file -# that represents doxygen's defaults, run doxygen with the -l option. -# You can optionally specify a file name after the option, if omitted +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. The create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted # DoxygenLayout.xml will be used as the name of the layout file. -LAYOUT_FILE = +LAYOUT_FILE = -# The CITE_BIB_FILES tag can be used to specify one or more bib files -# containing the references data. This must be a list of .bib files. The -# .bib extension is automatically appended if omitted. Using this command -# requires the bibtex tool to be installed. See also -# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style -# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this +# The CITE_BIB_FILES tag can be used to specify one or more bib files +# containing the references data. This must be a list of .bib files. The +# .bib extension is automatically appended if omitted. Using this command +# requires the bibtex tool to be installed. See also +# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style +# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this # feature you need bibtex and perl available in the search path. -CITE_BIB_FILES = +CITE_BIB_FILES = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- -# The QUIET tag can be used to turn on/off the messages that are generated +# The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO -# The WARNINGS tag can be used to turn on/off the warning messages that are -# generated by doxygen. Possible values are YES and NO. If left blank +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES -# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings -# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = NO -# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some -# parameters in a documented function, or documenting parameters that +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = NO -# The WARN_NO_PARAMDOC option can be enabled to get warnings for -# functions that are documented, but have no documentation for their parameters -# or return value. If set to NO (the default) doxygen will only warn about -# wrong or incomplete parameter documentation, but not about the absence of +# The WARN_NO_PARAMDOC option can be enabled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO -# The WARN_FORMAT tag determines the format of the warning messages that -# doxygen can produce. The string should contain the $file, $line, and $text -# tags, which will be replaced by the file and line number from which the -# warning originated and the warning text. Optionally the format may contain -# $version, which will be replaced by the version of the file (if it could +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" -# The WARN_LOGFILE tag can be used to specify a file to which warning -# and error messages should be written. If left blank the output is written +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written # to stderr. -WARN_LOGFILE = +WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- -# The INPUT tag can be used to specify the files and/or directories that contain -# documented source files. You may enter file names like "myfile.cpp" or -# directories like "/usr/src/myproject". Separate the files or directories +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = ../include -# This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is -# also the default input encoding. Doxygen uses libiconv (or the iconv built -# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 -# If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank the following patterns are tested: -# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh -# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh +# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py # *.f90 *.f *.for *.vhd *.vhdl FILE_PATTERNS = *.hpp \ *.cpp -# The RECURSIVE tag can be used to turn specify whether or not subdirectories -# should be searched for input files as well. Possible values are YES and NO. +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES -# The EXCLUDE tag can be used to specify files and/or directories that should be -# excluded from the INPUT source files. This way you can easily exclude a -# subdirectory from a directory tree whose root is specified with the INPUT tag. -# Note that relative paths are relative to the directory from which doxygen is +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = ../include/gua/renderer/nurbs_geometry -# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or -# directories that are symbolic links (a Unix file system feature) are excluded +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. Note that the wildcards are matched -# against the file with absolute path, so to exclude all test directories +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* -EXCLUDE_PATTERNS = +EXCLUDE_PATTERNS = -# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names -# (namespaces, classes, functions, etc.) that should be excluded from the -# output. The symbol name can be a fully qualified name, a word, or if the -# wildcard * is used, a substring. Examples: ANamespace, AClass, +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test -EXCLUDE_SYMBOLS = +EXCLUDE_SYMBOLS = -# The EXAMPLE_PATH tag can be used to specify one or more files or -# directories that contain example code fragments that are included (see +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see # the \include command). -EXAMPLE_PATH = +EXAMPLE_PATH = -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = * -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude -# commands irrespective of the value of the RECURSIVE tag. +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO -# The IMAGE_PATH tag can be used to specify one or more files or -# directories that contain image that are included in the documentation (see +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see # the \image command). -IMAGE_PATH = +IMAGE_PATH = -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command , where -# is the value of the INPUT_FILTER tag, and is the name of an -# input file. Doxygen will then use the output that the filter program writes -# to standard output. If FILTER_PATTERNS is specified, this tag will be +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be # ignored. -INPUT_FILTER = +INPUT_FILTER = -# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. The filters are a list of the form: -# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further -# info on how filters are used. If FILTER_PATTERNS is empty or if +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty or if # non of the patterns match the file name, INPUT_FILTER is applied. -FILTER_PATTERNS = +FILTER_PATTERNS = -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will be used to filter the input files when producing source +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO -# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file -# pattern. A pattern will override the setting for FILTER_PATTERN (if any) -# and it is also possible to disable source filtering for a specific pattern -# using *.ext= (so without naming a filter). This option only has effect when +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) +# and it is also possible to disable source filtering for a specific pattern +# using *.ext= (so without naming a filter). This option only has effect when # FILTER_SOURCE_FILES is enabled. -FILTER_SOURCE_PATTERNS = +FILTER_SOURCE_PATTERNS = #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- -# If the SOURCE_BROWSER tag is set to YES then a list of source files will -# be generated. Documented entities will be cross-referenced with these sources. -# Note: To get rid of all source code in the generated output, make sure also +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO -# Setting the INLINE_SOURCES tag to YES will include the body +# Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO -# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct -# doxygen to hide any special comment blocks from generated source code +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES -# If the REFERENCED_BY_RELATION tag is set to YES -# then for each documented function all documented +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = NO -# If the REFERENCES_RELATION tag is set to YES -# then for each documented function all documented entities +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = NO -# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) -# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from -# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES -# If the USE_HTAGS tag is set to YES then the references to source code -# will point to the HTML generated by the htags(1) tool instead of doxygen -# built-in source browser. The htags tool is part of GNU's global source -# tagging system (see http://www.gnu.org/software/global/global.html). You +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO -# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen -# will generate a verbatim copy of the header file for each class for +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES @@ -822,157 +822,157 @@ VERBATIM_HEADERS = YES # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index -# of all compounds will be generated. Enable this if the project +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO -# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then -# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 -# In case all classes in a project start with a common prefix, all -# classes will be put under the same header in the alphabetical index. -# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. -IGNORE_PREFIX = +IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- -# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for -# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html -# The HTML_HEADER tag can be used to specify a personal HTML header for -# each generated HTML page. If it is left blank doxygen will generate a -# standard header. Note that when using a custom header you are responsible -# for the proper inclusion of any scripts and style sheets that doxygen -# needs, which is dependent on the configuration options used. -# It is advised to generate a default header using "doxygen -w html -# header.html footer.html stylesheet.css YourConfigFile" and then modify -# that header. Note that the header is subject to change so you typically -# have to redo this when upgrading to a newer version of doxygen or when +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. Note that when using a custom header you are responsible +# for the proper inclusion of any scripts and style sheets that doxygen +# needs, which is dependent on the configuration options used. +# It is advised to generate a default header using "doxygen -w html +# header.html footer.html stylesheet.css YourConfigFile" and then modify +# that header. Note that the header is subject to change so you typically +# have to redo this when upgrading to a newer version of doxygen or when # changing the value of configuration settings such as GENERATE_TREEVIEW! -HTML_HEADER = +HTML_HEADER = -# The HTML_FOOTER tag can be used to specify a personal HTML footer for -# each generated HTML page. If it is left blank doxygen will generate a +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a # standard footer. -HTML_FOOTER = +HTML_FOOTER = -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading -# style sheet that is used by each HTML page. It can be used to -# fine-tune the look of the HTML output. If the tag is left blank doxygen -# will generate a default style sheet. Note that doxygen will try to copy -# the style sheet file to the HTML output directory, so don't put your own +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own # style sheet in the HTML output directory as well, or it will be erased! -HTML_STYLESHEET = +HTML_STYLESHEET = -# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or -# other source files which should be copied to the HTML output directory. Note -# that these files will be copied to the base HTML output directory. Use the -# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these -# files. In the HTML_STYLESHEET file, use the file name only. Also note that +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that # the files will be copied as-is; there are no commands or markers available. -HTML_EXTRA_FILES = +HTML_EXTRA_FILES = -# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. -# Doxygen will adjust the colors in the style sheet and background images -# according to this color. Hue is specified as an angle on a colorwheel, -# see http://en.wikipedia.org/wiki/Hue for more information. -# For instance the value 0 represents red, 60 is yellow, 120 is green, -# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the style sheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. HTML_COLORSTYLE_HUE = 82 -# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of -# the colors in the HTML output. For a value of 0 the output will use +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use # grayscales only. A value of 255 will produce the most vivid colors. -HTML_COLORSTYLE_SAT = 98 +HTML_COLORSTYLE_SAT = 0 -# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to -# the luminance component of the colors in the HTML output. Values below -# 100 gradually make the output lighter, whereas values above 100 make -# the output darker. The value divided by 100 is the actual gamma applied, -# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, # and 100 does not change the gamma. -HTML_COLORSTYLE_GAMMA = 70 +HTML_COLORSTYLE_GAMMA = 80 -# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES -# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, -# files or namespaces will be aligned in HTML using tables. If set to +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES -# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML -# documentation will contain sections that can be hidden and shown after the -# page has loaded. For this to work a browser that supports -# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = YES -# If the GENERATE_DOCSET tag is set to YES, additional index files -# will be generated that can be used as input for Apple's Xcode 3 -# integrated development environment, introduced with OSX 10.5 (Leopard). -# To create a documentation set, doxygen will generate a Makefile in the -# HTML output directory. Running make will produce the docset in that -# directory and running "make install" will install the docset in -# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find -# it at startup. -# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. GENERATE_DOCSET = NO -# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the -# feed. A documentation feed provides an umbrella under which multiple -# documentation sets from a single provider (such as a company or product suite) +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" -# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that -# should uniquely identify the documentation set bundle. This should be a -# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project -# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify -# the documentation publisher. This should be a reverse domain-name style +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. DOCSET_PUBLISHER_ID = org.doxygen.Publisher @@ -981,220 +981,220 @@ DOCSET_PUBLISHER_ID = org.doxygen.Publisher DOCSET_PUBLISHER_NAME = Publisher -# If the GENERATE_HTMLHELP tag is set to YES, additional index files -# will be generated that can be used as input for tools like the -# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can -# be used to specify the file name of the resulting .chm file. You -# can add a path in front of the file if the result should not be +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be # written to the html output directory. -CHM_FILE = +CHM_FILE = -# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can -# be used to specify the location (absolute path including file name) of -# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. -HHC_LOCATION = +HHC_LOCATION = -# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag -# controls if a separate .chi index file is generated (YES) or that +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING -# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. -CHM_INDEX_ENCODING = +CHM_INDEX_ENCODING = -# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag -# controls whether a binary table of contents is generated (YES) or a +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO -# The TOC_EXPAND flag can be set to YES to add extra items for group members +# The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO -# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and -# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated -# that can be used as input for Qt's qhelpgenerator to generate a +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a # Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = NO -# If the QHG_LOCATION tag is specified, the QCH_FILE tag can -# be used to specify the file name of the resulting .qch file. +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. -QCH_FILE = +QCH_FILE = -# The QHP_NAMESPACE tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project -# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc -# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to -# add. For more information please see +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters -QHP_CUST_FILTER_NAME = +QHP_CUST_FILTER_NAME = -# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the -# custom filter to add. For more information please see -# +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# # Qt Help Project / Custom Filters. -QHP_CUST_FILTER_ATTRS = +QHP_CUST_FILTER_ATTRS = -# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this -# project's -# filter section matches. -# +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# # Qt Help Project / Filter Attributes. -QHP_SECT_FILTER_ATTRS = +QHP_SECT_FILTER_ATTRS = -# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can -# be used to specify the location of Qt's qhelpgenerator. -# If non-empty doxygen will try to run qhelpgenerator on the generated +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. -QHG_LOCATION = +QHG_LOCATION = -# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files -# will be generated, which together with the HTML files, form an Eclipse help -# plugin. To install this plugin and make it available under the help contents -# menu in Eclipse, the contents of the directory containing the HTML and XML -# files needs to be copied into the plugins directory of eclipse. The name of -# the directory within the plugins directory should be the same as -# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before # the help appears. GENERATE_ECLIPSEHELP = NO -# A unique identifier for the eclipse help plugin. When installing the plugin -# the directory name containing the HTML and XML files should also have +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project -# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) -# at top of each HTML page. The value NO (the default) enables the index and -# the value YES disables it. Since the tabs have the same information as the -# navigation tree you can set this option to NO if you already set +# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) +# at top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. Since the tabs have the same information as the +# navigation tree you can set this option to NO if you already set # GENERATE_TREEVIEW to YES. DISABLE_INDEX = NO -# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index -# structure should be generated to display hierarchical information. -# If the tag value is set to YES, a side panel will be generated -# containing a tree-like index structure (just like the one that -# is generated for HTML Help). For this to work a browser that supports -# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). -# Windows users are probably better off using the HTML help feature. -# Since the tree basically has the same information as the tab index you +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. +# Since the tree basically has the same information as the tab index you # could consider to set DISABLE_INDEX to NO when enabling this option. GENERATE_TREEVIEW = NO -# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values -# (range [0,1..20]) that doxygen will group on one line in the generated HTML -# documentation. Note that a value of 0 will completely suppress the enum +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values +# (range [0,1..20]) that doxygen will group on one line in the generated HTML +# documentation. Note that a value of 0 will completely suppress the enum # values from appearing in the overview section. ENUM_VALUES_PER_LINE = 4 -# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, +# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, # and Class Hierarchy pages using a tree view instead of an ordered list. USE_INLINE_TREES = NO -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be -# used to set the initial width (in pixels) of the frame in which the tree +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 -# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open # links to external symbols imported via tag files in a separate window. EXT_LINKS_IN_WINDOW = NO -# Use this tag to change the font size of Latex formulas included -# as images in the HTML documentation. The default is 10. Note that -# when you change the font size after a successful doxygen run you need -# to manually remove any form_*.png images from the HTML output directory +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 -# Use the FORMULA_TRANPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are -# not supported properly for IE 6.0, but are supported on all modern browsers. -# Note that when changing this option you need to delete any form_*.png files +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files # in the HTML output before the changes have effect. FORMULA_TRANSPARENT = YES -# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax -# (see http://www.mathjax.org) which uses client side Javascript for the -# rendering instead of using prerendered bitmaps. Use this if you do not -# have LaTeX installed or if you want to formulas look prettier in the HTML -# output. When enabled you also need to install MathJax separately and +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax +# (see http://www.mathjax.org) which uses client side Javascript for the +# rendering instead of using prerendered bitmaps. Use this if you do not +# have LaTeX installed or if you want to formulas look prettier in the HTML +# output. When enabled you also need to install MathJax separately and # configure the path to it using the MATHJAX_RELPATH option. USE_MATHJAX = NO -# When MathJax is enabled you need to specify the location relative to the -# HTML output directory using the MATHJAX_RELPATH option. The destination -# directory should contain the MathJax.js script. For instance, if the mathjax -# directory is located at the same level as the HTML output directory, then -# MATHJAX_RELPATH should be ../mathjax. The default value points to the -# mathjax.org site, so you can quickly see the result without installing -# MathJax, but it is strongly recommended to install a local copy of MathJax +# When MathJax is enabled you need to specify the location relative to the +# HTML output directory using the MATHJAX_RELPATH option. The destination +# directory should contain the MathJax.js script. For instance, if the mathjax +# directory is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the +# mathjax.org site, so you can quickly see the result without installing +# MathJax, but it is strongly recommended to install a local copy of MathJax # before deployment. MATHJAX_RELPATH = http://www.mathjax.org/mathjax -# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension +# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension # names that should be enabled during MathJax rendering. -MATHJAX_EXTENSIONS = +MATHJAX_EXTENSIONS = -# When the SEARCHENGINE tag is enabled doxygen will generate a search box -# for the HTML output. The underlying search engine uses javascript -# and DHTML and should work on any modern browser. Note that when using -# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets -# (GENERATE_DOCSET) there is already a search function so this one should -# typically be disabled. For large projects the javascript based search engine +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = YES -# When the SERVER_BASED_SEARCH tag is enabled the search engine will be -# implemented using a PHP enabled web server instead of at the web client -# using Javascript. Doxygen will generate the search PHP script and index -# file to put on the web server. The advantage of the server -# based approach is that it scales better to large projects and allows -# full text search. The disadvantages are that it is more difficult to setup +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a PHP enabled web server instead of at the web client +# using Javascript. Doxygen will generate the search PHP script and index +# file to put on the web server. The advantage of the server +# based approach is that it scales better to large projects and allows +# full text search. The disadvantages are that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO @@ -1203,97 +1203,97 @@ SERVER_BASED_SEARCH = NO # configuration options related to the LaTeX output #--------------------------------------------------------------------------- -# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO -# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex -# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be -# invoked. If left blank `latex' will be used as the default command name. -# Note that when enabling USE_PDFLATEX this option is only used for -# generating bitmaps for formulas in the HTML output, but not in the +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex -# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to -# generate index for LaTeX. If left blank `makeindex' will be used as the +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex -# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact -# LaTeX documents. This may be useful for small projects and may help to +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = YES -# The PAPER_TYPE tag can be used to set the paper type that is used -# by the printer. Possible values are: a4, letter, legal and +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4 -# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. -EXTRA_PACKAGES = +EXTRA_PACKAGES = -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for -# the generated latex document. The header should contain everything until -# the first chapter. If it is left blank doxygen will generate a +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! -LATEX_HEADER = +LATEX_HEADER = -# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for -# the generated latex document. The footer should contain everything after -# the last chapter. If it is left blank doxygen will generate a +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for +# the generated latex document. The footer should contain everything after +# the last chapter. If it is left blank doxygen will generate a # standard footer. Notice: only use this tag if you know what you are doing! -LATEX_FOOTER = +LATEX_FOOTER = -# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated -# is prepared for conversion to pdf (using ps2pdf). The pdf file will -# contain links (just like the HTML output) instead of page references +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES -# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of -# plain latex in the generated Makefile. Set this option to YES to get a +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. -# command to the generated LaTeX files. This will instruct LaTeX to keep -# running if errors occur, instead of asking the user for help. +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO -# If LATEX_HIDE_INDICES is set to YES then doxygen will not -# include the index chapters (such as File Index, Compound Index, etc.) +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO -# If LATEX_SOURCE_CODE is set to YES then doxygen will include -# source code with syntax highlighting in the LaTeX output. -# Note that which sources are shown also depends on other settings +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO -# The LATEX_BIB_STYLE tag can be used to specify the style to use for the -# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See # http://en.wikipedia.org/wiki/BibTeX for more info. LATEX_BIB_STYLE = plain @@ -1302,68 +1302,68 @@ LATEX_BIB_STYLE = plain # configuration options related to the RTF output #--------------------------------------------------------------------------- -# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output -# The RTF output is optimized for Word 97 and may not look very pretty with +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO -# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf -# If the COMPACT_RTF tag is set to YES Doxygen generates more compact -# RTF documents. This may be useful for small projects and may help to +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO -# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated -# will contain hyperlink fields. The RTF file will -# contain links (just like the HTML output) instead of page references. -# This makes the output suitable for online browsing using WORD or other -# programs which support those fields. +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO -# Load style sheet definitions from file. Syntax is similar to doxygen's -# config file, i.e. a series of assignments. You only have to provide +# Load style sheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. -RTF_STYLESHEET_FILE = +RTF_STYLESHEET_FILE = -# Set optional variables used in the generation of an rtf document. +# Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. -RTF_EXTENSIONS_FILE = +RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- -# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO -# The MAN_OUTPUT tag is used to specify where the man pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man -# The MAN_EXTENSION tag determines the extension that is added to +# The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 -# If the MAN_LINKS tag is set to YES and Doxygen generates man output, -# then it will generate one additional man file for each entity -# documented in the real man page(s). These additional files -# only source the real man page, but without them the man command +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO @@ -1372,33 +1372,33 @@ MAN_LINKS = NO # configuration options related to the XML output #--------------------------------------------------------------------------- -# If the GENERATE_XML tag is set to YES Doxygen will -# generate an XML file that captures the structure of +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO -# The XML_OUTPUT tag is used to specify where the XML pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml -# The XML_SCHEMA tag can be used to specify an XML schema, -# which can be used by a validating XML parser to check the +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the # syntax of the XML files. -XML_SCHEMA = +XML_SCHEMA = -# The XML_DTD tag can be used to specify an XML DTD, -# which can be used by a validating XML parser to check the +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the # syntax of the XML files. -XML_DTD = +XML_DTD = -# If the XML_PROGRAMLISTING tag is set to YES Doxygen will -# dump the program listings (including syntax highlighting -# and cross-referencing information) to the XML output. Note that +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES @@ -1407,10 +1407,10 @@ XML_PROGRAMLISTING = YES # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- -# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will -# generate an AutoGen Definitions (see autogen.sf.net) file -# that captures the structure of the code including all -# documentation. Note that this feature is still experimental +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO @@ -1419,97 +1419,97 @@ GENERATE_AUTOGEN_DEF = NO # configuration options related to the Perl module output #--------------------------------------------------------------------------- -# If the GENERATE_PERLMOD tag is set to YES Doxygen will -# generate a Perl module file that captures the structure of -# the code including all documentation. Note that this -# feature is still experimental and incomplete at the +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO -# If the PERLMOD_LATEX tag is set to YES Doxygen will generate -# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO -# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be -# nicely formatted so it can be parsed by a human reader. This is useful -# if you want to understand what is going on. On the other hand, if this -# tag is set to NO the size of the Perl module output will be much smaller +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES -# The names of the make variables in the generated doxyrules.make file -# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. -# This is useful so different doxyrules.make files included by the same +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. -PERLMOD_MAKEVAR_PREFIX = +PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- -# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will -# evaluate all C-preprocessor directives found in the sources and include +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES -# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro -# names in the source code. If set to NO (the default) only conditional -# compilation will be performed. Macro expansion can be done in a controlled +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = YES -# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES -# then the macro expansion is limited to the macros specified with the +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO -# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # pointed to by INCLUDE_PATH will be searched when a #include is found. SEARCH_INCLUDES = YES -# The INCLUDE_PATH tag can be used to specify one or more directories that -# contain include files that are not input files but should be processed by +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = ../include -# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard -# patterns (like *.h and *.hpp) to filter out the header-files in the -# directories. If left blank, the patterns specified with FILE_PATTERNS will +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. -INCLUDE_FILE_PATTERNS = +INCLUDE_FILE_PATTERNS = -# The PREDEFINED tag can be used to specify one or more macro names that -# are defined before the preprocessor is started (similar to the -D option of -# gcc). The argument of the tag is a list of macros of the form: name -# or name=definition (no spaces). If the definition and the = are -# omitted =1 is assumed. To prevent a macro definition from being -# undefined via #undef or recursively expanded use the := operator +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator # instead of the = operator. -PREDEFINED = +PREDEFINED = -# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then -# this tag can be used to specify a list of macro names that should be expanded. -# The macro definition that is found in the sources will be used. -# Use the PREDEFINED tag if you want to use a different macro definition that +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition that # overrules the definition found in the source code. -EXPAND_AS_DEFINED = +EXPAND_AS_DEFINED = -# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then -# doxygen's preprocessor will remove all references to function-like macros -# that are alone on a line, have an all uppercase name, and do not end with a +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all references to function-like macros +# that are alone on a line, have an all uppercase name, and do not end with a # semicolon, because these will confuse the parser if not removed. SKIP_FUNCTION_MACROS = YES @@ -1518,41 +1518,41 @@ SKIP_FUNCTION_MACROS = YES # Configuration::additions related to external references #--------------------------------------------------------------------------- -# The TAGFILES option can be used to specify one or more tagfiles. -# Optionally an initial location of the external documentation -# can be added for each tagfile. The format of a tag file without -# this location is as follows: -# TAGFILES = file1 file2 ... -# Adding location for the tag files is done as follows: -# TAGFILES = file1=loc1 "file2 = loc2" ... -# where "loc1" and "loc2" can be relative or absolute paths or -# URLs. If a location is present for each tag, the installdox tool -# does not have to be run to correct the links. -# Note that each tag file must have a unique name -# (where the name does NOT include the path) -# If a tag file is not located in the directory in which doxygen +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. -TAGFILES = +TAGFILES = -# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. -GENERATE_TAGFILE = +GENERATE_TAGFILE = -# If the ALLEXTERNALS tag is set to YES all external classes will be listed -# in the class index. If set to NO only the inherited external classes +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO -# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed -# in the modules index. If set to NO, only the current project's groups will +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES -# The PERL_PATH should be the absolute path and name of the perl script +# The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl @@ -1561,213 +1561,213 @@ PERL_PATH = /usr/bin/perl # Configuration options related to the dot tool #--------------------------------------------------------------------------- -# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will -# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base -# or super classes. Setting the tag to NO turns the diagrams off. Note that -# this option also works with HAVE_DOT disabled, but it is recommended to +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option also works with HAVE_DOT disabled, but it is recommended to # install and use dot, since it yields more powerful graphs. CLASS_DIAGRAMS = YES -# You can define message sequence charts within doxygen comments using the \msc -# command. Doxygen will then run the mscgen tool (see -# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the -# documentation. The MSCGEN_PATH tag allows you to specify the directory where -# the mscgen tool resides. If left empty the tool is assumed to be found in the +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. -MSCGEN_PATH = +MSCGEN_PATH = -# If set to YES, the inheritance and collaboration graphs will hide -# inheritance and usage relations if the target is undocumented +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = NO -# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is -# available from the path. This tool is part of Graphviz, a graph visualization -# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = YES -# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is -# allowed to run in parallel. When set to 0 (the default) doxygen will -# base this on the number of processors available in the system. You can set it -# explicitly to a value larger than 0 to get control over the balance +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 0 -# By default doxygen will use the Helvetica font for all dot files that -# doxygen generates. When you want a differently looking font you can specify -# the font name using DOT_FONTNAME. You need to make sure dot is able to find -# the font, which can be done by putting it in a standard location or by setting -# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# By default doxygen will use the Helvetica font for all dot files that +# doxygen generates. When you want a differently looking font you can specify +# the font name using DOT_FONTNAME. You need to make sure dot is able to find +# the font, which can be done by putting it in a standard location or by setting +# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the # directory containing the font. DOT_FONTNAME = Helvetica -# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 -# By default doxygen will tell dot to use the Helvetica font. -# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to +# By default doxygen will tell dot to use the Helvetica font. +# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to # set the path where dot can find it. -DOT_FONTPATH = +DOT_FONTPATH = -# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect inheritance relations. Setting this tag to YES will force the +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the # CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES -# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect implementation dependencies (inheritance, containment, and +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = NO -# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = NO -# If the UML_LOOK tag is set to YES doxygen will generate inheritance and -# collaboration diagrams in a style similar to the OMG's Unified Modeling +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO -# If set to YES, the inheritance and collaboration graphs will show the +# If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = YES -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT -# tags are set to YES then doxygen will generate a graph for each documented -# file showing the direct and indirect include dependencies of the file with +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = NO -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and -# HAVE_DOT tags are set to YES then doxygen will generate a graph for each -# documented header file showing the documented files that directly or +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = NO -# If the CALL_GRAPH and HAVE_DOT options are set to YES then -# doxygen will generate a call dependency graph for every global function -# or class method. Note that enabling this option will significantly increase -# the time of a run. So in most cases it will be better to enable call graphs +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO -# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then -# doxygen will generate a caller dependency graph for every global function -# or class method. Note that enabling this option will significantly increase -# the time of a run. So in most cases it will be better to enable caller +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO -# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will generate a graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = NO -# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES -# then doxygen will show the dependencies a directory has on other directories -# in a graphical way. The dependency relations are determined by the #include +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = NO -# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. Possible values are svg, png, jpg, or gif. -# If left blank png will be used. If you choose svg you need to set -# HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are svg, png, jpg, or gif. +# If left blank png will be used. If you choose svg you need to set +# HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible in IE 9+ (other browsers do not have this requirement). DOT_IMAGE_FORMAT = png -# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to -# enable generation of interactive SVG images that allow zooming and panning. -# Note that this requires a modern browser other than Internet Explorer. -# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you -# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# Note that this requires a modern browser other than Internet Explorer. +# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you +# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible. Older versions of IE do not have SVG support. INTERACTIVE_SVG = NO -# The tag DOT_PATH can be used to specify the path where the dot tool can be +# The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = /usr/bin -# The DOTFILE_DIRS tag can be used to specify one or more directories that -# contain dot files that are included in the documentation (see the +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the # \dotfile command). -DOTFILE_DIRS = +DOTFILE_DIRS = -# The MSCFILE_DIRS tag can be used to specify one or more directories that -# contain msc files that are included in the documentation (see the +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the # \mscfile command). -MSCFILE_DIRS = +MSCFILE_DIRS = -# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of -# nodes that will be shown in the graph. If the number of nodes in a graph -# becomes larger than this value, doxygen will truncate the graph, which is -# visualized by representing a node as a red box. Note that doxygen if the -# number of direct children of the root node in a graph is already larger than -# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 -# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the -# graphs generated by dot. A depth value of 3 means that only nodes reachable -# from the root by following a path via at most 3 edges will be shown. Nodes -# that lay further from the root node will be omitted. Note that setting this -# option to 1 or 2 may greatly reduce the computation time needed for large -# code bases. Also note that the size of a graph can be further restricted by +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, because dot on Windows does not -# seem to support this out of the box. Warning: Depending on the platform used, -# enabling this option may lead to badly anti-aliased labels on the edges of +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO -# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output -# files in one run (i.e. multiple -o and -T options on the command line). This -# makes dot run faster, but since only newer versions of dot (>1.8.10) +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO -# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will -# generate a legend page explaining the meaning of the various boxes and +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES -# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will -# remove the intermediate dot files that are used to generate +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES diff --git a/include/gua/databases/CollisionShapeDatabase.hpp b/include/gua/databases/CollisionShapeDatabase.hpp index a27359959..ed89d5956 100644 --- a/include/gua/databases/CollisionShapeDatabase.hpp +++ b/include/gua/databases/CollisionShapeDatabase.hpp @@ -36,6 +36,8 @@ namespace physics { * * This Database stores collision shapes that can be shared among rigid bodies. * It can be accessed via string identifiers. + * + * \ingroup gua_databases */ class GUA_DLL CollisionShapeDatabase : public Database, public Singleton { diff --git a/include/gua/databases/Database.hpp b/include/gua/databases/Database.hpp index b96106425..8be82b93b 100644 --- a/include/gua/databases/Database.hpp +++ b/include/gua/databases/Database.hpp @@ -40,6 +40,8 @@ namespace gua { * * It can store any type of Data. The data is mapped on strings, * which then can be used to access this data. + * + * \ingroup gua_databases */ template class Database { public: diff --git a/include/gua/databases/Doxygen.hpp b/include/gua/databases/Doxygen.hpp new file mode 100644 index 000000000..7cbeb3618 --- /dev/null +++ b/include/gua/databases/Doxygen.hpp @@ -0,0 +1,30 @@ +/****************************************************************************** + * guacamole - delicious VR * + * * + * Copyright: (c) 2011-2013 Bauhaus-Universität Weimar * + * Contact: felix.lauer@uni-weimar.de / simon.schneegans@uni-weimar.de * + * * + * This program is free software: you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the Free * + * Software Foundation, either version 3 of the License, or (at your option) * + * any later version. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * + * for more details. * + * * + * You should have received a copy of the GNU General Public License along * + * with this program. If not, see . * + * * + ******************************************************************************/ + +#ifndef GUA_DATABASES_DOXYGEN_HPP +#define GUA_DATABASES_DOXYGEN_HPP + +/** + * \defgroup gua_databases guacamole asset databases + */ + +#endif // GUA_DATABASES_DOXYGEN_HPP + diff --git a/include/gua/databases/GeometryDatabase.hpp b/include/gua/databases/GeometryDatabase.hpp index 8a8e1c70c..c5c10c718 100644 --- a/include/gua/databases/GeometryDatabase.hpp +++ b/include/gua/databases/GeometryDatabase.hpp @@ -34,6 +34,8 @@ namespace gua { * * This Database stores geometry data. It can be accessed via string * identifiers. + * + * \ingroup gua_databases */ class GUA_DLL GeometryDatabase : public Database, public Singleton { diff --git a/include/gua/databases/MaterialDatabase.hpp b/include/gua/databases/MaterialDatabase.hpp index af83dd605..ee41347d2 100644 --- a/include/gua/databases/MaterialDatabase.hpp +++ b/include/gua/databases/MaterialDatabase.hpp @@ -35,6 +35,8 @@ namespace gua { * * This Database stores material data. It can be accessed via string * identifiers. + * + * \ingroup gua_databases */ class GUA_DLL MaterialDatabase : public Database, public Singleton { diff --git a/include/gua/databases/Resources.hpp b/include/gua/databases/Resources.hpp index fc13c0306..15bdad8b4 100644 --- a/include/gua/databases/Resources.hpp +++ b/include/gua/databases/Resources.hpp @@ -23,8 +23,8 @@ #define GUA_RESOURCES_HPP // external headers - #include - #include +#include +#include namespace gua { diff --git a/include/gua/databases/ShadingModelDatabase.hpp b/include/gua/databases/ShadingModelDatabase.hpp index 7ac1438a2..e4af311e3 100644 --- a/include/gua/databases/ShadingModelDatabase.hpp +++ b/include/gua/databases/ShadingModelDatabase.hpp @@ -34,6 +34,8 @@ namespace gua { * * This Database stores shading model data. It can be accessed via string * identifiers. + * + * \ingroup gua_databases */ class GUA_DLL ShadingModelDatabase : public Database, public Singleton { diff --git a/include/gua/databases/TextureDatabase.hpp b/include/gua/databases/TextureDatabase.hpp index b27362bfc..7d886ccea 100644 --- a/include/gua/databases/TextureDatabase.hpp +++ b/include/gua/databases/TextureDatabase.hpp @@ -35,6 +35,8 @@ namespace gua { * * This Database stores texture data. It can be accessed via string * identifiers. + * + * \ingroup gua_databases */ class GUA_DLL TextureDatabase : public Database, public Singleton { diff --git a/include/gua/scenegraph/Doxygen.hpp b/include/gua/scenegraph/Doxygen.hpp new file mode 100644 index 000000000..698e64656 --- /dev/null +++ b/include/gua/scenegraph/Doxygen.hpp @@ -0,0 +1,30 @@ +/****************************************************************************** + * guacamole - delicious VR * + * * + * Copyright: (c) 2011-2013 Bauhaus-Universität Weimar * + * Contact: felix.lauer@uni-weimar.de / simon.schneegans@uni-weimar.de * + * * + * This program is free software: you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the Free * + * Software Foundation, either version 3 of the License, or (at your option) * + * any later version. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * + * for more details. * + * * + * You should have received a copy of the GNU General Public License along * + * with this program. If not, see . * + * * + ******************************************************************************/ + +#ifndef GUA_SCENEGRAPH_DOXYGEN_HPP +#define GUA_SCENEGRAPH_DOXYGEN_HPP + +/** + * \defgroup gua_scenegraph guacamole scenegraph + */ + +#endif // GUA_SCENEGRAPH_DOXYGEN_HPP + diff --git a/include/gua/scenegraph/GeometryNode.hpp b/include/gua/scenegraph/GeometryNode.hpp index 90e6a8d46..bdc5f72d1 100644 --- a/include/gua/scenegraph/GeometryNode.hpp +++ b/include/gua/scenegraph/GeometryNode.hpp @@ -29,16 +29,17 @@ // external headers #include +namespace gua { + /** * This class is used to represent geometry in the SceneGraph. * * A GeometryNode only stores references to existing rendering assets stored in * guacamole's databases. GeometryNodes typically aren't instantiated directly * but by utilizing guacamole's GeometryLoader. + * + * \ingroup gua_scenegraph */ - -namespace gua { - class GUA_DLL GeometryNode : public Node { public: diff --git a/include/gua/scenegraph/LODNode.hpp b/include/gua/scenegraph/LODNode.hpp index fc83ba9d9..67d31a6d0 100644 --- a/include/gua/scenegraph/LODNode.hpp +++ b/include/gua/scenegraph/LODNode.hpp @@ -26,6 +26,8 @@ #include #include +namespace gua { + /** * This class is used to represent a level of detail node in the SceneGraph. * @@ -37,10 +39,8 @@ * therefore describes upto which distance between the current camera and the * LODNode the LODNode's child with the same index shall be visible. * + * \ingroup gua_scenegraph */ - -namespace gua { - class GUA_DLL LODNode : public TransformNode { public: diff --git a/include/gua/scenegraph/Node.hpp b/include/gua/scenegraph/Node.hpp index d50251f01..bd66af3a4 100644 --- a/include/gua/scenegraph/Node.hpp +++ b/include/gua/scenegraph/Node.hpp @@ -38,6 +38,11 @@ namespace gua { +class NodeVisitor; +class RayNode; + +namespace physics { class CollisionShapeNodeVisitor; } + /** * This class is used as a base class to provide basic node behaviour. * @@ -46,13 +51,8 @@ namespace gua { * parent Node. Furthermore, Nodes can be assigned a group name which allows for * user-defined grouping concerning similar properties etc. * + * \ingroup gua_scenegraph */ - -class NodeVisitor; -class RayNode; - -namespace physics { class CollisionShapeNodeVisitor; } - class GUA_DLL Node { public: diff --git a/include/gua/scenegraph/NodeVisitor.hpp b/include/gua/scenegraph/NodeVisitor.hpp index 41416a863..46ca5a1ff 100644 --- a/include/gua/scenegraph/NodeVisitor.hpp +++ b/include/gua/scenegraph/NodeVisitor.hpp @@ -50,6 +50,7 @@ class CollisionShapeNode; /** * This class is used to recursively visit nodes in a scenegraph * + * \ingroup gua_scenegraph */ class NodeVisitor { public: diff --git a/include/gua/scenegraph/PointLightNode.hpp b/include/gua/scenegraph/PointLightNode.hpp index b1b7dff95..6dfa3293c 100644 --- a/include/gua/scenegraph/PointLightNode.hpp +++ b/include/gua/scenegraph/PointLightNode.hpp @@ -30,18 +30,27 @@ #include +namespace gua { + /** * This class is used to represent light in the SceneGraph. * + * \ingroup gua_scenegraph */ - -namespace gua { - class GUA_DLL PointLightNode : public Node { public: struct Configuration { - GUA_ADD_PROPERTY(utils::Color3f, color, utils::Color3f(1.f, 1.f, 1.f)); + /** + * The color of the light source. + * It's possible to use negative values and values > 1. + */ + GUA_ADD_PROPERTY(utils::Color3f, color, utils::Color3f(1.f, 1.f, 1.f)); + + /** + * The exponent of distance attenuation. + * E.g. a value of 2 means quadratic falloff, 1 means linear falloff + */ GUA_ADD_PROPERTY(float, falloff, 1.f); GUA_ADD_PROPERTY(bool, enable_shadows, false); GUA_ADD_PROPERTY(bool, enable_godrays, false); diff --git a/include/gua/scenegraph/RayNode.hpp b/include/gua/scenegraph/RayNode.hpp index 6dc2f5fc2..55fc1b386 100644 --- a/include/gua/scenegraph/RayNode.hpp +++ b/include/gua/scenegraph/RayNode.hpp @@ -26,13 +26,13 @@ #include #include +namespace gua { + /** * This class is used to represent a camera in the SceneGraph. * + * \ingroup gua_scenegraph */ - -namespace gua { - class GUA_DLL RayNode : public Node { public: diff --git a/include/gua/scenegraph/SceneGraph.hpp b/include/gua/scenegraph/SceneGraph.hpp index 56a90bcc8..4c0d4bf79 100644 --- a/include/gua/scenegraph/SceneGraph.hpp +++ b/include/gua/scenegraph/SceneGraph.hpp @@ -42,6 +42,8 @@ class RayNode; * This class is used to build and structure a graph describing a scene with * all its contents. It provides an interface to set up and have access to * a graph consisting of several Nodes in order to build a scene abstraction. + * + * \ingroup gua_scenegraph */ class GUA_DLL SceneGraph { diff --git a/include/gua/scenegraph/ScreenNode.hpp b/include/gua/scenegraph/ScreenNode.hpp index 198020eac..e9a40541f 100644 --- a/include/gua/scenegraph/ScreenNode.hpp +++ b/include/gua/scenegraph/ScreenNode.hpp @@ -26,13 +26,13 @@ #include #include +namespace gua { + /** * This class is used to represent a screen in the SceneGraph. * + * \ingroup gua_scenegraph */ - -namespace gua { - class GUA_DLL ScreenNode : public Node { public: diff --git a/include/gua/scenegraph/SpotLightNode.hpp b/include/gua/scenegraph/SpotLightNode.hpp index 1b25510ee..85cc5d7c8 100644 --- a/include/gua/scenegraph/SpotLightNode.hpp +++ b/include/gua/scenegraph/SpotLightNode.hpp @@ -30,13 +30,13 @@ #include +namespace gua { + /** * This class is used to represent light in the SceneGraph. * + * \ingroup gua_scenegraph */ - -namespace gua { - class GUA_DLL SpotLightNode : public Node { public: diff --git a/include/gua/scenegraph/SunLightNode.hpp b/include/gua/scenegraph/SunLightNode.hpp index bfdea6f8a..480178301 100644 --- a/include/gua/scenegraph/SunLightNode.hpp +++ b/include/gua/scenegraph/SunLightNode.hpp @@ -30,13 +30,13 @@ #include +namespace gua { + /** * This class is used to represent light in the SceneGraph. * + * \ingroup gua_scenegraph */ - -namespace gua { - class GUA_DLL SunLightNode : public Node { public: diff --git a/include/gua/scenegraph/TexturedQuadNode.hpp b/include/gua/scenegraph/TexturedQuadNode.hpp index f5617e97f..ff478cc19 100644 --- a/include/gua/scenegraph/TexturedQuadNode.hpp +++ b/include/gua/scenegraph/TexturedQuadNode.hpp @@ -25,13 +25,13 @@ #include #include +namespace gua { + /** * This class is used to represent a screen in the SceneGraph. * + * \ingroup gua_scenegraph */ - -namespace gua { - class GUA_DLL TexturedQuadNode : public Node { public: diff --git a/include/gua/scenegraph/TransformNode.hpp b/include/gua/scenegraph/TransformNode.hpp index ebdd566ed..b9c1c12d0 100644 --- a/include/gua/scenegraph/TransformNode.hpp +++ b/include/gua/scenegraph/TransformNode.hpp @@ -25,13 +25,13 @@ #include #include +namespace gua { + /** * This class is used to represent an empty node in the SceneGraph. * + * \ingroup gua_scenegraph */ - -namespace gua { - class GUA_DLL TransformNode : public Node { public: diff --git a/include/gua/scenegraph/VolumeNode.hpp b/include/gua/scenegraph/VolumeNode.hpp index 81d58e2cd..d9f1e22fc 100644 --- a/include/gua/scenegraph/VolumeNode.hpp +++ b/include/gua/scenegraph/VolumeNode.hpp @@ -29,13 +29,13 @@ // external headers #include +namespace gua { + /** * This class is used to represent a volume in the SceneGraph. * + * \ingroup gua_scenegraph */ - -namespace gua { - class GUA_DLL VolumeNode : public Node { public: @@ -55,9 +55,9 @@ class GUA_DLL VolumeNode : public Node { /*virtual*/ void update_bounding_box() const; - /*virtual*/ void ray_test_impl(RayNode const& ray, + /*virtual*/ void ray_test_impl(RayNode const& ray, PickResult::Options options, - Mask const& mask, + Mask const& mask, std::set& hits); private: diff --git a/include/gua/utils/configuration_macro.hpp b/include/gua/utils/configuration_macro.hpp index 8858563b1..8766b27a7 100644 --- a/include/gua/utils/configuration_macro.hpp +++ b/include/gua/utils/configuration_macro.hpp @@ -22,18 +22,23 @@ #ifndef GUA_ADD_PROPERTY_HPP #define GUA_ADD_PROPERTY_HPP -#define GUA_ADD_PROPERTY(TYPE, NAME, VALUE) \ - struct NAME##_struct { \ - public: \ - NAME##_struct() : val_(VALUE) {} \ - NAME##_struct(TYPE const& val) : val_(val) {} \ - \ - TYPE& operator()() { return val_; } \ - TYPE const& operator()() const { return val_; } \ - private: \ - TYPE val_; \ - } NAME; \ - void set_##NAME(TYPE const & val) { NAME() = val; } \ +#define GUA_ADD_PROPERTY(TYPE, NAME, VALUE) \ + /** \cond */ \ + struct NAME##_struct { \ + public: \ + NAME##_struct() : val_(VALUE) {} \ + NAME##_struct(TYPE const& val) : val_(val) {} \ + \ + TYPE& operator()() { return val_; } \ + TYPE const& operator()() const { return val_; } \ + private: \ + TYPE val_; \ + } NAME; \ + /** \endcond */ \ + Configuration& set_##NAME(TYPE const & val) { NAME() = val; return *this; } \ + /** + The default value is VALUE. + */ \ TYPE const& get_##NAME() const { return NAME(); } #endif // GUA_ADD_PROPERTY_HPP From bb79b832d2be08237656cb03e57cad0a1fbb4fb7 Mon Sep 17 00:00:00 2001 From: thelaui Date: Tue, 25 Feb 2014 15:13:30 +0100 Subject: [PATCH 107/146] updated documentation for NodeVisitor, started to document PickResult and removed opening browser command from documentation build target in sublime project file --- include/gua/scenegraph/NodeVisitor.hpp | 93 +++++++++++++++----------- include/gua/scenegraph/PickResult.hpp | 36 +++++++++- scripts/guacamole.sublime-project | 2 +- 3 files changed, 89 insertions(+), 42 deletions(-) diff --git a/include/gua/scenegraph/NodeVisitor.hpp b/include/gua/scenegraph/NodeVisitor.hpp index 41416a863..717d485a4 100644 --- a/include/gua/scenegraph/NodeVisitor.hpp +++ b/include/gua/scenegraph/NodeVisitor.hpp @@ -48,7 +48,7 @@ class CollisionShapeNode; } /** - * This class is used to recursively visit nodes in a scenegraph + * This class is used to recursively visit nodes in a SceneGraph * */ class NodeVisitor { @@ -69,119 +69,132 @@ class NodeVisitor { virtual ~NodeVisitor(); /** - * Visits a TransformNode + * Visits a Node. * - * This function provides the interface to visit a TransformNode + * This function provides the interface to visit a Node. + * Unless overwritten by derived classes, this defaults to visit(Node*). * - * \param cam Pointer to TransformNode + * \param cam Pointer to a Node. */ virtual void visit(Node* node) {}; /** - * Visits a TransformNode + * Visits a TransformNode. * - * This function provides the interface to visit a TransformNode + * This function provides the interface to visit a TransformNode. + * Unless overwritten by derived classes, this defaults to visit(Node*). * - * \param cam Pointer to TransformNode + * \param cam Pointer to a TransformNode. */ virtual void visit(TransformNode* node) { visit(reinterpret_cast(node)); } /** - * Visits an LODNode + * Visits an LODNode. * - * This function provides the interface to visit an LODNode + * This function provides the interface to visit an LODNode. + * Unless overwritten by derived classes, this defaults to visit(Node*). * - * \param cam Pointer to LODNode + * \param cam Pointer to a LODNode. */ virtual void visit(LODNode* node) { visit(reinterpret_cast(node)); } /** - * Visits a GeometryNode + * Visits a GeometryNode. * - * This function provides the interface to visit a GeometryNode + * This function provides the interface to visit a GeometryNode. + * Unless overwritten by derived classes, this defaults to visit(Node*). * - * \param cam Pointer to GeometryNode + * \param cam Pointer to a GeometryNode. */ virtual void visit(GeometryNode* node) { visit(reinterpret_cast(node)); } /** - * Visits a GeometryNode + * Visits a GeometryNode. * - * This function provides the interface to visit a GeometryNode + * This function provides the interface to visit a GeometryNode. + * Unless overwritten by derived classes, this defaults to visit(Node*). * - * \param cam Pointer to GeometryNode + * \param cam Pointer to a GeometryNode. */ virtual void visit(VolumeNode* node) { visit(reinterpret_cast(node)); } /** - * Visits a PointLightNode + * Visits a PointLightNode. * - * This function provides the interface to visit a PointLightNode + * This function provides the interface to visit a PointLightNode. + * Unless overwritten by derived classes, this defaults to visit(Node*). * - * \param cam Pointer to PointLightNode + * \param cam Pointer to a PointLightNode. */ virtual void visit(PointLightNode* node) { visit(reinterpret_cast(node)); } /** - * Visits a SpotLightNode + * Visits a SpotLightNode. * - * This function provides the interface to visit a SpotLightNode + * This function provides the interface to visit a SpotLightNode. + * Unless overwritten by derived classes, this defaults to visit(Node*). * - * \param cam Pointer to SpotLightNode + * \param cam Pointer to a SpotLightNode. */ virtual void visit(SpotLightNode* node) { visit(reinterpret_cast(node)); } /** - * Visits a SunLightNode + * Visits a SunLightNode. * - * This function provides the interface to visit a SunLightNode + * This function provides the interface to visit a SunLightNode. + * Unless overwritten by derived classes, this defaults to visit(Node*). * - * \param cam Pointer to SunLightNode + * \param cam Pointer to a SunLightNode. */ virtual void visit(SunLightNode* node) { visit(reinterpret_cast(node)); } /** - * Visits a SpotLightNode + * Visits a SpotLightNode. * - * This function provides the interface to visit a ScreenNode + * This function provides the interface to visit a ScreenNode. + * Unless overwritten by derived classes, this defaults to visit(Node*). * - * \param cam Pointer to ScreenNode + * \param cam Pointer to a ScreenNode. */ virtual void visit(ScreenNode* node) { visit(reinterpret_cast(node)); } /** - * Visits a RayNode + * Visits a RayNode. * - * This function provides the interface to visit a ScreenNode + * This function provides the interface to visit a ScreenNode. + * Unless overwritten by derived classes, this defaults to visit(Node*). * - * \param cam Pointer to ScreenNode + * \param cam Pointer to a ScreenNode. */ virtual void visit(RayNode* node) { visit(reinterpret_cast(node)); } /** - * Visits a RigidBodyNode + * Visits a RigidBodyNode. * - * This function provides the interface to visit a RigidBodyNode + * This function provides the interface to visit a RigidBodyNode. + * Unless overwritten by derived classes, this defaults to visit(Node*). * - * \param cam Pointer to RigidBodyNode + * \param cam Pointer to a RigidBodyNode. */ virtual void visit(physics::RigidBodyNode* node) { visit(reinterpret_cast(node)); } /** - * Visits a CollisionShapeNode + * Visits a CollisionShapeNode. * - * This function provides the interface to visit a CollisionShapeNode + * This function provides the interface to visit a CollisionShapeNode. + * Unless overwritten by derived classes, this defaults to visit(Node*). * - * \param cam Pointer to CollisionShapeNode + * \param cam Pointer to a CollisionShapeNode. */ virtual void visit(physics::CollisionShapeNode* node) { visit(reinterpret_cast(node)); } /** - * Visits a TexturedQuadNode + * Visits a TexturedQuadNode. * - * This function provides the interface to visit a TexturedQuadNode + * This function provides the interface to visit a TexturedQuadNode. + * Unless overwritten by derived classes, this defaults to visit(Node*). * - * \param cam Pointer to TexturedQuadNode + * \param cam Pointer to a TexturedQuadNode. */ virtual void visit(TexturedQuadNode* node) { visit(reinterpret_cast(node)); } diff --git a/include/gua/scenegraph/PickResult.hpp b/include/gua/scenegraph/PickResult.hpp index d73fe960c..2922fe9a9 100644 --- a/include/gua/scenegraph/PickResult.hpp +++ b/include/gua/scenegraph/PickResult.hpp @@ -33,9 +33,19 @@ class Node; struct GUA_DLL PickResult { - enum Options { PICK_ALL = 0, + enum Options { + /// A PickResult is returned for each hit Node along a RayNode's + /// length. This is the default value for picking. + PICK_ALL = 0, + /// Only the first hit Node is used as a PickResult. The + /// intersection process is terminated after hitting one Node. PICK_ONLY_FIRST_OBJECT = 1<<1, + /// Only the first face of each hit Node is used as a + /// PickResult. PICK_ONLY_FIRST_FACE = 1<<2, + /// If set, the positions of all intersection points in the hit + /// Nodes' object coordinates are written to the respective + /// PickResult. GET_POSITIONS = 1<<3, GET_WORLD_POSITIONS = 1<<4, GET_NORMALS = 1<<5, @@ -44,6 +54,23 @@ struct GUA_DLL PickResult { GET_TEXTURE_COORDS = 1<<8 }; + /** + * Constructor. + * + * This constructs a PickResult with the given parameters. A PickResult is + * instantiated for each successful intersection of a RayNode with a + * GeometryNode. + * + * \param d The distance between the RayNode's origin and the + * intersection point. + * \param o The Node beeing hit. + * \param p The hit's position in the hit Node's object coordinates. + * \param wp The hit's position in world coordinates. + * \param n The surface normal at the hit's position the hit Node's object + * coordinates. + * \param wn The surface normal at the hit's position in world coordinates. + * \param t The hit object's texture coordinates at the hit's position. + */ PickResult(float d, Node* o, math::vec3 const& p, math::vec3 const& wp, math::vec3 const& n, math::vec3 const& wn, @@ -53,12 +80,19 @@ struct GUA_DLL PickResult { normal(n), world_normal(wn), texture_coords(t) {} + /// The distance between a RayNode's origin and the intersection point. float distance; + /// The Node beeing hit. Node* object; + /// The Node beeing hit. mutable math::vec3 position; + /// The hit's position in the hit Node's object coordinates. mutable math::vec3 world_position; + /// The surface normal at the hit's position the hit Node's object coordinates. mutable math::vec3 normal; + /// The surface normal at the hit's position in world coordinates. mutable math::vec3 world_normal; + /// The hit object's texture coordinates at the hit's position. mutable math::vec2 texture_coords; bool operator<(PickResult const& lhs) const { diff --git a/scripts/guacamole.sublime-project b/scripts/guacamole.sublime-project index 1fd880024..f9df3120e 100644 --- a/scripts/guacamole.sublime-project +++ b/scripts/guacamole.sublime-project @@ -21,7 +21,7 @@ "file_regex": "^(..[^:]*):(.*)$", "working_dir": "${project_path}/../doc/", "shell" : "true", - "cmd": ["doxygen && xdg-open \"doc/html/index.html\""] + "cmd": ["doxygen"] } ] } From 26b350837b94ec17b5c15452d5e6aa0caf4ec3d5 Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Tue, 25 Feb 2014 15:11:37 +0100 Subject: [PATCH 108/146] Add CMakeLists for tests subdirectory. --- tests/CMakeLists.txt | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 tests/CMakeLists.txt diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 000000000..ab8e993c1 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,8 @@ +include_directories ( ../include ${BOOST_INCLUDE_DIRS} ) + +add_executable( runTests main.cpp testBoundingBox.cpp ) + +target_link_libraries( runTests + unittest++ + ${BOOST_LIBRARIES} + ) From 23c39f83627369a85ef18d44c4f4fa4890fd67d3 Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Tue, 25 Feb 2014 15:13:29 +0100 Subject: [PATCH 109/146] Add test target. Set GUACAMOLE_TESTS to ON during configuration. This will add an additional test target to make. --- CMakeLists.txt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9bc2e0f88..fd914e2c0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -157,6 +157,18 @@ add_subdirectory(src) add_subdirectory(plugins) +################################################################ +# Testing +################################################################ + +set (GUACAMOLE_TESTS "false" CACHE BOOL "Set to enable testing.") + +if (GUACAMOLE_TESTS) + add_subdirectory(tests) + enable_testing() + add_test( NAME testGUA COMMAND runTests ) +endif (GUACAMOLE_TESTS) + ################################################################ # Summary ################################################################ From 58988f3fdec478deec4af5812f83799ff9edd414 Mon Sep 17 00:00:00 2001 From: thelaui Date: Tue, 25 Feb 2014 15:29:27 +0100 Subject: [PATCH 110/146] finished documentation of PickResult --- include/gua/scenegraph/PickResult.hpp | 85 +++++++++++++++++++++------ 1 file changed, 66 insertions(+), 19 deletions(-) diff --git a/include/gua/scenegraph/PickResult.hpp b/include/gua/scenegraph/PickResult.hpp index 2922fe9a9..f79a20d2c 100644 --- a/include/gua/scenegraph/PickResult.hpp +++ b/include/gua/scenegraph/PickResult.hpp @@ -34,23 +34,62 @@ class Node; struct GUA_DLL PickResult { enum Options { - /// A PickResult is returned for each hit Node along a RayNode's - /// length. This is the default value for picking. + /** + * A PickResult is returned for each hit Node along a RayNode's + * length. This is the default value for picking. + */ PICK_ALL = 0, - /// Only the first hit Node is used as a PickResult. The - /// intersection process is terminated after hitting one Node. + + /** + * Only the first hit Node is used as a PickResult. + */ PICK_ONLY_FIRST_OBJECT = 1<<1, - /// Only the first face of each hit Node is used as a - /// PickResult. + + /** + * Only the first face of each hit Node is used as a + * PickResult. + */ PICK_ONLY_FIRST_FACE = 1<<2, - /// If set, the positions of all intersection points in the hit - /// Nodes' object coordinates are written to the respective - /// PickResult. + + /** + * If set, the positions of all intersection points in the hit + * Nodes' object coordinates are written to the respective + * PickResult. + */ GET_POSITIONS = 1<<3, + + /** + * If set, the world positions of all intersection points in + * are written to the respective PickResult. + */ GET_WORLD_POSITIONS = 1<<4, + + /** + * If set, the normals of the surfaces at all intersection + * points in the hit Nodes' object coordinates are written to + * the respective PickResult. + */ GET_NORMALS = 1<<5, + + /** + * If set, the normals of the surfaces at all intersection + * points in world coordinates are written to the respective + * PickResult. + */ GET_WORLD_NORMALS = 1<<6, + + /** + * If set, all obtained normals are interpolated according to + * the hit Node's vertex normals and written to the respective + * PickResult. + */ INTERPOLATE_NORMALS = 1<<7, + + /** + * If set, the hit Node's texture coordinates at the hit's + * position are interpolated and written to the respective + * PickResult. + */ GET_TEXTURE_COORDS = 1<<8 }; @@ -69,7 +108,7 @@ struct GUA_DLL PickResult { * \param n The surface normal at the hit's position the hit Node's object * coordinates. * \param wn The surface normal at the hit's position in world coordinates. - * \param t The hit object's texture coordinates at the hit's position. + * \param t The hit Node's texture coordinates at the hit's position. */ PickResult(float d, Node* o, math::vec3 const& p, math::vec3 const& wp, @@ -80,23 +119,31 @@ struct GUA_DLL PickResult { normal(n), world_normal(wn), texture_coords(t) {} - /// The distance between a RayNode's origin and the intersection point. + /** The distance between a RayNode's origin and the intersection point.*/ float distance; - /// The Node beeing hit. + /** The Node beeing hit.*/ Node* object; - /// The Node beeing hit. + /** The Node beeing hit.*/ mutable math::vec3 position; - /// The hit's position in the hit Node's object coordinates. + /** The hit's position in the hit Node's object coordinates.*/ mutable math::vec3 world_position; - /// The surface normal at the hit's position the hit Node's object coordinates. + /** The surface normal at the hit's position the hit Node's object coordinates.*/ mutable math::vec3 normal; - /// The surface normal at the hit's position in world coordinates. + /** The surface normal at the hit's position in world coordinates.*/ mutable math::vec3 world_normal; - /// The hit object's texture coordinates at the hit's position. + /** The hit Node's texture coordinates at the hit's position.*/ mutable math::vec2 texture_coords; - bool operator<(PickResult const& lhs) const { - return distance < lhs.distance; + /** + * Compares two PickResults. + * + * As comparison criterion, the PickResults' distance to the respective + * RayNode's origin is used. + * + * \param rhs The PickResult to be compared with. + */ + bool operator<(PickResult const& rhs) const { + return distance < rhs.distance; } }; From 330aef59836b96cae109dc4d5f276b888132108c Mon Sep 17 00:00:00 2001 From: thelaui Date: Tue, 25 Feb 2014 15:38:44 +0100 Subject: [PATCH 111/146] finished documentation of PickResult, this time for real --- include/gua/scenegraph/PickResult.hpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/include/gua/scenegraph/PickResult.hpp b/include/gua/scenegraph/PickResult.hpp index f79a20d2c..2ebfb49fb 100644 --- a/include/gua/scenegraph/PickResult.hpp +++ b/include/gua/scenegraph/PickResult.hpp @@ -31,8 +31,24 @@ namespace gua { class Node; +/** + * This class is used to encapsulate properties of intersections generated by + * SceneGraph::ray_test(). + * + * A PickResult contains several data defining an intersection, such as distance + * of hit position to a RayNode's origin, a pointer to the hit Node itself, + * the hit position and corresponding surface normal in object and world + * coordinates and texture coordinates. + * + * \ingroup gua_scenegraph + */ + struct GUA_DLL PickResult { + /** + * These options are used by SceneGraph::ray_test() to configure the + * intersection. + */ enum Options { /** * A PickResult is returned for each hit Node along a RayNode's From 2a026cbcf39266352f72933251f880bb7d246931 Mon Sep 17 00:00:00 2001 From: thelaui Date: Tue, 25 Feb 2014 15:55:19 +0100 Subject: [PATCH 112/146] completed documentation of GeometryNode and LODNode and added documentation for PointLightNode --- include/gua/scenegraph/GeometryNode.hpp | 16 +++++++ include/gua/scenegraph/LODNode.hpp | 13 ++++++ include/gua/scenegraph/PointLightNode.hpp | 53 ++++++++++++++++++++--- 3 files changed, 76 insertions(+), 6 deletions(-) diff --git a/include/gua/scenegraph/GeometryNode.hpp b/include/gua/scenegraph/GeometryNode.hpp index bdc5f72d1..2bb6ad6ef 100644 --- a/include/gua/scenegraph/GeometryNode.hpp +++ b/include/gua/scenegraph/GeometryNode.hpp @@ -44,12 +44,28 @@ class GUA_DLL GeometryNode : public Node { public: struct Configuration { + /** + * A string referring to an entry in GeometryDatabase. + */ GUA_ADD_PROPERTY(std::string, geometry, "gua_default_geometry"); + + /** + * A string referring to an entry in MaterialDatabase. + */ GUA_ADD_PROPERTY(std::string, material, "gua_default_material"); }; + /** + * The GeometryNode's configuration. + */ Configuration data; + /** + * Constructor. + * + * This constructs an empty GeometryNode. + * + */ GeometryNode() {}; /** diff --git a/include/gua/scenegraph/LODNode.hpp b/include/gua/scenegraph/LODNode.hpp index 67d31a6d0..a2888fabb 100644 --- a/include/gua/scenegraph/LODNode.hpp +++ b/include/gua/scenegraph/LODNode.hpp @@ -45,11 +45,24 @@ class GUA_DLL LODNode : public TransformNode { public: struct Configuration { + /** + * A vector storing distances. Indices are mapped to the indices of the + * LODNode's children vector. + */ GUA_ADD_PROPERTY(std::vector, lod_distances, std::vector()); }; + /** + * The LODNode's configuration. + */ Configuration data; + /** + * Constructor. + * + * This constructs an empty LODNode. + * + */ LODNode() {}; /** diff --git a/include/gua/scenegraph/PointLightNode.hpp b/include/gua/scenegraph/PointLightNode.hpp index 6dfa3293c..928d5d861 100644 --- a/include/gua/scenegraph/PointLightNode.hpp +++ b/include/gua/scenegraph/PointLightNode.hpp @@ -52,38 +52,79 @@ class GUA_DLL PointLightNode : public Node { * E.g. a value of 2 means quadratic falloff, 1 means linear falloff */ GUA_ADD_PROPERTY(float, falloff, 1.f); + + /** + * Triggers if the light casts shadows. NOTE: Not implemented yet! + */ GUA_ADD_PROPERTY(bool, enable_shadows, false); + + /** + * Triggers volumetric screen-space effects for the light source. + */ GUA_ADD_PROPERTY(bool, enable_godrays, false); + + /** + * Triggers whether or not the light source has influence on objects' + * diffuse shading. + */ GUA_ADD_PROPERTY(bool, enable_diffuse_shading, true); + + /** + * Triggers whether or not the light source has influence on objects' + * specular shading. + */ GUA_ADD_PROPERTY(bool, enable_specular_shading, true); + + /** + * Sets the size in pixel of the texture used for shadow map generation. + * Choose wisely! + */ GUA_ADD_PROPERTY(unsigned, shadow_map_size, 512); }; + /** + * The PointLightNode's configuration. + */ Configuration data; /** * Constructor. * - * This constructs a PointLightNode with the given parameters and calls - * the constructor of base class Core with the type LIGHT. + * This constructs an empty PointLightNode. * - * \param color The light's color. */ - PointLightNode() {} + /** + * Constructor. + * + * This constructs a PointLightNode with the given parameters. + * + * \param name The name of the new PointLightNode. + * \param configuration A configuration struct to define the PointLightNode's + * properties. + * \param transform A matrix to describe the PointLightNode's + * transformation. + */ PointLightNode(std::string const& name, Configuration const& configuration = Configuration(), math::mat4 const& transform = math::mat4::identity()); /** - * Accepts a visitor and calls concrete visit method + * Accepts a visitor and calls concrete visit method. * - * This method implements the visitor pattern for Nodes + * This method implements the visitor pattern for Nodes. * + * \param visitor A visitor to process the PointLightNode's data. */ /* virtual */ void accept(NodeVisitor&); + /** + * Updates a PointLightNode's BoundingBox. + * + * The bounding box is updated according to the transformation matrices of + * all children. + */ void update_bounding_box() const; private: From 54351731da9b62895b00c7f91e88edde542af532 Mon Sep 17 00:00:00 2001 From: thelaui Date: Tue, 25 Feb 2014 16:10:23 +0100 Subject: [PATCH 113/146] documented RayNode --- include/gua/scenegraph/RayNode.hpp | 56 +++++++++++++++++++++++------- src/gua/scenegraph/RayNode.cpp | 2 +- 2 files changed, 45 insertions(+), 13 deletions(-) diff --git a/include/gua/scenegraph/RayNode.hpp b/include/gua/scenegraph/RayNode.hpp index 55fc1b386..2b611b665 100644 --- a/include/gua/scenegraph/RayNode.hpp +++ b/include/gua/scenegraph/RayNode.hpp @@ -29,48 +29,80 @@ namespace gua { /** - * This class is used to represent a camera in the SceneGraph. + * This class is used to represent a ray in the SceneGraph. + * + * A RayNode is used to intersect scene geometry and utilized by + * SceneGraph::ray_test(). * * \ingroup gua_scenegraph */ class GUA_DLL RayNode : public Node { public: + /** + * Constructor. + * + * This constructs an empty RayNode. + * + */ RayNode() {} /** * Constructor. * - * This constructs a RayNode with the given parameters and calls - * the constructor of base class Core with the type CAMERA. + * This constructs a RayNode with the given parameters. * - * \param stereo_width The gap between the eyes. + * \param name The name of the new RayNode. + * \param transform A matrix to describe the RayNode's transformation. */ RayNode(std::string const& name, math::mat4 const& transform = math::mat4::identity()); - /** - * Accepts a visitor and calls concrete visit method + /** + * Accepts a visitor and calls concrete visit method. * - * This method implements the visitor pattern for Nodes + * This method implements the visitor pattern for Nodes. * + * \param visitor A visitor to process the RayNode's data. */ /* virtual */ void accept(NodeVisitor&); + /** + * Updates a RayNode's BoundingBox. + * + * The bounding box is updated according to the transformation matrices of + * all children. + */ + void update_bounding_box() const; + + /** + * Used internally to check whether a RayNode hits a given BoundingBox. + * + * \param box The box to be checked against. + * + * \return std::pair A pair of parameters which describe the + * to potential intersecition points with a + * BoundingBox in ray space. + */ std::pair intersect( math::BoundingBox const& box) const; + /** + * Used internally to get a world-transformed mathematical representation of a + * ray. + * + * \return Ray A mathematical representation (origin, direction, length) of + * the RayNode. + */ Ray const get_world_ray() const; - void update_bounding_box() const; - + /** + * Used internally to check whether or not intersections occured. + */ static const float END; private: - /** - * - */ std::shared_ptr copy() const; }; diff --git a/src/gua/scenegraph/RayNode.cpp b/src/gua/scenegraph/RayNode.cpp index e1b5fac2f..9d96ef99e 100644 --- a/src/gua/scenegraph/RayNode.cpp +++ b/src/gua/scenegraph/RayNode.cpp @@ -43,7 +43,7 @@ RayNode::RayNode(std::string const& name, math::mat4 const& transform) std::pair RayNode::intersect( math::BoundingBox const& box) const { - return ::gua::intersect(get_world_ray(),box); + return ::gua::intersect(get_world_ray(), box); } Ray const RayNode::get_world_ray() const { From 4917e5440ee599a2acb632f5760fa0a10393ef6f Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Tue, 25 Feb 2014 16:29:08 +0100 Subject: [PATCH 114/146] Add cmake find script for unittest++. --- cmake/modules/FindUnitTest++.cmake | 31 ++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 cmake/modules/FindUnitTest++.cmake diff --git a/cmake/modules/FindUnitTest++.cmake b/cmake/modules/FindUnitTest++.cmake new file mode 100644 index 000000000..9b08c4a96 --- /dev/null +++ b/cmake/modules/FindUnitTest++.cmake @@ -0,0 +1,31 @@ +# - Try to find UnitTest++ +# Once done this will define +# UNITTEST++_FOUND - System has UnitTest++ +# UNITTEST++_INCLUDE_DIRS - The UnitTest++ include directories +# UNITTEST++_LIBRARIES - The libraries needed to use UnitTest++ +find_path(UNITTEST++_INCLUDE_DIR UnitTest++.h + HINTS ${CMAKE_SOURCE_DIR}/externals/UnitTest++/include + PATHS /usr/include/unittest++ + PATH_SUFFIXES UnitTest++ unittest++ + ) + +find_library(UNITTEST++_LIBRARY + NAMES UnitTest++ UnitTest++.vsnet2005 UnitTest++.vsnet2008 + HINTS ${CMAKE_SOURCE_DIR}/externals/UnitTest++/lib + PATHS /usr/lib + ) + +#find_library(UNITTEST++_LIBRARY_DEBUG +# NAMES UnitTest++D UnitTest++.vsnet2005 UnitTest++.vsnet2008 +# HINTS ${CMAKE_SOURCE_DIR}/externals/UnitTest++/lib +# PATHS /usr/lib +# ) + +set(UNITTEST++_LIBRARIES ${UNITTEST++_LIBRARY} ) +set(UNITTEST++_INCLUDE_DIRS ${UNITTEST++_INCLUDE_DIR} ) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(UnitTest++ DEFAULT_MSG + UNITTEST++_LIBRARY UNITTEST++_INCLUDE_DIR) + +mark_as_advanced(UNITTEST++_INCLUDE_DIR UNITTEST++_LIBRARY ) From fd6b4356a32607be25ddf2ae990f1a7b834c58fa Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Tue, 25 Feb 2014 16:29:37 +0100 Subject: [PATCH 115/146] Check for Unittest++. --- tests/CMakeLists.txt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ab8e993c1..fcff287cb 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,8 +1,14 @@ -include_directories ( ../include ${BOOST_INCLUDE_DIRS} ) +find_package( UnitTest++ REQUIRED ) +include_directories ( + ../include + ${BOOST_INCLUDE_DIRS} + ${UNITTEST++_INCLUDE_DIR} + ) add_executable( runTests main.cpp testBoundingBox.cpp ) target_link_libraries( runTests unittest++ ${BOOST_LIBRARIES} + ${UNITTEST++_LIBRARIES} ) From 80b41927d63da9577e6049cb37fcb03027cc1547 Mon Sep 17 00:00:00 2001 From: thelaui Date: Tue, 25 Feb 2014 17:07:55 +0100 Subject: [PATCH 116/146] documented SceneGraph --- include/gua/scenegraph/SceneGraph.hpp | 148 ++++++++++++++++++++++---- 1 file changed, 126 insertions(+), 22 deletions(-) diff --git a/include/gua/scenegraph/SceneGraph.hpp b/include/gua/scenegraph/SceneGraph.hpp index 4c0d4bf79..b11130c89 100644 --- a/include/gua/scenegraph/SceneGraph.hpp +++ b/include/gua/scenegraph/SceneGraph.hpp @@ -52,7 +52,9 @@ class GUA_DLL SceneGraph { /** * Constructor. * - * This constructs an empty SceneGraph. + * This constructs a new SceneGraph. + * + * \param name The new SceneGraph's name. */ SceneGraph(std::string const& name = "scenegraph"); @@ -68,21 +70,17 @@ class GUA_DLL SceneGraph { /** * Adds a new Node. * - * This function adds a new Node to the graph. If the given path to a - * parent Node is invalid (this means this Node doesn't exist), an - * Node on the SceneGraph's "end" is returned. Otherwise an - * Node on the added Node is given back. + * This function adds a new Node to the SceneGraph. If the given path to a + * parent Node is invalid (this means this Node doesn't exist), a new Node is + * returned but not added to any parent. + * + * \param path_to_parent The location of the Node the new Node will be + * attached to. + * \param node_name The name of the new Node. * - * \param path_to_parent The location of the Node the new Node will be - * attached to. - * \param node_name The name of the new Node. - * \param core The core the new Node shall refer to. - * \param transform The transformation of the object the new Node - * carries. + * \tparam tparam The type of the Node to be added. * - * \return Iterator An Iterator on the recently added Node. If the - * path to the parent was invalid, the returned - * Iterator points to the SceneGraph's "end". + * \return std::shared_ptr A shared pointer to the recently added Node. */ template std::shared_ptr add_node(std::string const& path_to_parent, std::string const& node_name) { @@ -99,6 +97,18 @@ class GUA_DLL SceneGraph { return add_node(parent, new_node); } + /** + * Adds a new Node. + * + * This function adds a new Node to the SceneGraph. + * + * \param parent The Node the new Node will be attached to. + * \param node_name The name of the new Node. + * + * \tparam tparam The type of the Node to be added. + * + * \return std::shared_ptr A shared pointer to the recently added Node. + */ template std::shared_ptr add_node(std::shared_ptr const& parent, std::string const& node_name) { @@ -108,6 +118,21 @@ class GUA_DLL SceneGraph { return new_node; } + /** + * Adds a new Node. + * + * This function adds a new Node to the SceneGraph. If the given path to a + * parent Node is invalid (this means this Node doesn't exist), a new Node is + * returned but not added to any parent. + * + * \param path_to_parent The location of the Node the new Node will be + * attached to. + * \param new_node The Node to be attached. + * + * \tparam tparam The type of the Node to be added. + * + * \return std::shared_ptr A shared pointer to the recently added Node. + */ template std::shared_ptr add_node(std::string const& path_to_parent, std::shared_ptr const& new_node) { @@ -121,6 +146,18 @@ class GUA_DLL SceneGraph { return add_node(parent, new_node); } + /** + * Adds a new Node. + * + * This function adds a new Node to the SceneGraph. + * + * \param parent The Node the new Node will be attached to. + * \param new_node The Node to be attached. + * + * \tparam tparam The type of the Node to be added. + * + * \return std::shared_ptr A shared pointer to the recently added Node. + */ template std::shared_ptr add_node(std::shared_ptr const& parent, std::shared_ptr const& new_node) { parent->add_child(new_node); @@ -130,41 +167,108 @@ class GUA_DLL SceneGraph { /** * Removes a Node. * - * This function removes a Node from the graph and returnes an Iterator - * on the next Node with respect to the Iterator's traversion style. + * This function removes a Node from the SceneGraph. * * \param path_to_node The location of the Node to be removed. - * - * \return Iterator An Iterator on the next Node. */ void remove_node(std::string const& path_to_node); + + /** + * Removes a Node. + * + * This function removes a Node from the SceneGraph. + * + * \param to_remove The Node to be removed. + */ void remove_node(std::shared_ptr const& to_remove); + /** + * Sets the SceneGraph's name. + * + * \param name The SceneGraph's new name. + */ void set_name(std::string const& name); + + /** + * Returns the SceneGraph's name. + * + * \return std::string The SceneGraph's name. + */ std::string const& get_name() const; + /** + * Sets the SceneGraph's root Node. + * + * \param root The SceneGraph's new root Node. + */ void set_root(std::shared_ptr const& root); + + /** + * Returns the SceneGraph's root Node. + * + * \return std::shared_ptr The SceneGraph's root Node. + */ std::shared_ptr const& get_root() const; /** * Allows to access nodes via the index operator. * * This operator allows to access nodes via the index operator. If a - * given path doesn't refer to an existing set of nodes, all missing - * nodes in the path are added to the SceneGraph. + * given path doesn't refer to an existing set of nodes, a nullptr is returned. * - * \param path_to_node The path to the Node you want to access. + * \param path_to_node The path to the wanted Node. * - * \return Iterator An Iterator on the given Node. + * \return std::shared_ptr The wanted Node. */ std::shared_ptr operator[](std::string const& path_to_node) const; + + /** + * Assignment operator. + * + * This operator deep copies a given SceneGraph, assignes the copy's values to + * the existing graph and returns a reference to it. + * + * \param rhs The SceneGraph to be copied. + * + * \return SceneGraph const& The SceneGraph containing the copied values. + */ SceneGraph const& operator=(SceneGraph const& rhs); + /** + * Prints the SceneGraph to a file in GraphViz' dot format. + * + * Serializes the graph and writes all its Nodes to a file. + * + * \param file Complete path to the output file. The file doesn't need to + * exist. The directory structure, however, does. + */ void to_dot_file(std::string const& file) const; + /** + * Updates the cache of all SceneGraph Nodes. + * + * Calls Node::update_cache() on the root Node. + */ void update_cache() const; + + /** + * Accepts a NodeVisitor to process all SceneGraph Nodes. + * + * Calls Node::accept() on the root Node. + * + * \param visitor The NodeVisitor to pe accepted. + */ void accept(NodeVisitor& visitor) const; + /** + * Intersects a SceneGraph with a given RayNode. + * + * Calls Node::ray_test() on the root Node. + * + * \param ray The RayNode used to check for intersections. + * \param options PickResult::Options to configure the intersection process. + * \param mask A mask to restrict the intersection to certain Nodes. + */ std::set const ray_test(RayNode const& ray, PickResult::Options options = PickResult::PICK_ALL, std::string const& mask = ""); From 053151089892ffc7f5ef4e156e35cb72d8f535d1 Mon Sep 17 00:00:00 2001 From: thelaui Date: Tue, 25 Feb 2014 17:23:04 +0100 Subject: [PATCH 117/146] documented ScreenNode and SpotLightNode --- include/gua/scenegraph/PointLightNode.hpp | 2 +- include/gua/scenegraph/ScreenNode.hpp | 55 ++++++++++++++--- include/gua/scenegraph/SpotLightNode.hpp | 75 ++++++++++++++++++++++- 3 files changed, 120 insertions(+), 12 deletions(-) diff --git a/include/gua/scenegraph/PointLightNode.hpp b/include/gua/scenegraph/PointLightNode.hpp index 928d5d861..2f5f0e7a6 100644 --- a/include/gua/scenegraph/PointLightNode.hpp +++ b/include/gua/scenegraph/PointLightNode.hpp @@ -33,7 +33,7 @@ namespace gua { /** - * This class is used to represent light in the SceneGraph. + * This class is used to represent a point light in the SceneGraph. * * \ingroup gua_scenegraph */ diff --git a/include/gua/scenegraph/ScreenNode.hpp b/include/gua/scenegraph/ScreenNode.hpp index e9a40541f..cf3190eb9 100644 --- a/include/gua/scenegraph/ScreenNode.hpp +++ b/include/gua/scenegraph/ScreenNode.hpp @@ -19,8 +19,8 @@ * * ******************************************************************************/ -#ifndef GUA_SCREEN_CORE_HPP -#define GUA_SCREEN_CORE_HPP +#ifndef GUA_SCREEN_NODE_HPP +#define GUA_SCREEN_NODE_HPP #include #include @@ -31,42 +31,81 @@ namespace gua { /** * This class is used to represent a screen in the SceneGraph. * + * A ScreenNode is used to specify arbitrary viewing frustra. For that purpose, + * additionally any of guacamole's Nodes may serve as eye representation. To + * combine ScreenNode and eye, a Camera has to be specified. + * * \ingroup gua_scenegraph */ class GUA_DLL ScreenNode : public Node { public: struct Configuration { + /** + * A vector containing width and height of the ScreenNode. + */ GUA_ADD_PROPERTY(math::vec2, size, math::vec2(1.f, 1.f)); }; + /** + * The ScreenNode's configuration. + */ Configuration data; + /** + * Constructor. + * + * This constructs an empty ScreenNode. + * + */ ScreenNode() {} + /** + * Constructor. + * + * This constructs a ScreenNode with the given parameters. + * + * \param name The name of the new ScreenNode. + * \param configuration A configuration struct to define the ScreenNode's + * properties. + * \param transform A matrix to describe the ScreenNode's + * transformation. + */ ScreenNode(std::string const& name, Configuration const& configuration = Configuration(), math::mat4 const& transform = math::mat4::identity()); + /** + * Returns the ScreenNode's transformation, considering the scaling specified + * in the Configuration. + * + * \return math::mat4 The ScreenNode's scaled transformation. + */ math::mat4 get_scaled_transform() const; + + /** + * Returns the ScreenNode's world transformation, considering the scaling + * specified in the Configuration. + * + * \return math::mat4 The ScreenNode's scaled world transformation. + */ math::mat4 get_scaled_world_transform() const; /** - * Accepts a visitor and calls concrete visit method + * Accepts a visitor and calls concrete visit method. * - * This method implements the visitor pattern for Nodes + * This method implements the visitor pattern for Nodes. * + * \param visitor A visitor to process the ScreenNode's data. */ /* virtual */ void accept(NodeVisitor&); private: - /** - * - */ + std::shared_ptr copy() const; }; } -#endif // GUA_SCREEN_CORE_HPP +#endif // GUA_SCREEN_NODE_HPP diff --git a/include/gua/scenegraph/SpotLightNode.hpp b/include/gua/scenegraph/SpotLightNode.hpp index 85cc5d7c8..9fc061e51 100644 --- a/include/gua/scenegraph/SpotLightNode.hpp +++ b/include/gua/scenegraph/SpotLightNode.hpp @@ -33,7 +33,7 @@ namespace gua { /** - * This class is used to represent light in the SceneGraph. + * This class is used to represent spot light in the SceneGraph. * * \ingroup gua_scenegraph */ @@ -41,33 +41,102 @@ class GUA_DLL SpotLightNode : public Node { public: struct Configuration { + /** + * The color of the light source. + * It's possible to use negative values and values > 1. + */ GUA_ADD_PROPERTY(utils::Color3f, color, utils::Color3f(1.f, 1.f, 1.f)); + + /** + * The exponent of distance attenuation. + * E.g. a value of 2 means quadratic falloff, 1 means linear falloff + */ GUA_ADD_PROPERTY(float, falloff, 1.f); + + /** + * The exponent of radial attenuation. + * E.g. a value of 2 means quadratic falloff, 1 means linear falloff + */ GUA_ADD_PROPERTY(float, softness, 0.5f); + + /** + * Triggers if the light casts shadows. + */ GUA_ADD_PROPERTY(bool, enable_shadows, false); + + /** + * Triggers volumetric screen-space effects for the light source. + */ GUA_ADD_PROPERTY(bool, enable_godrays, false); + + /** + * Triggers whether or not the light source has influence on objects' + * diffuse shading. + */ GUA_ADD_PROPERTY(bool, enable_diffuse_shading, true); + + /** + * Triggers whether or not the light source has influence on objects' + * specular shading. + */ GUA_ADD_PROPERTY(bool, enable_specular_shading, true); + + /** + * Sets the size in pixel of the texture used for shadow map generation. + * Choose wisely! + */ GUA_ADD_PROPERTY(unsigned, shadow_map_size, 512); + + /** + * Sets the distance between shadow casting object and shadow edge. + * Increase this value to reduce artefacts (especially shadow acne). + */ GUA_ADD_PROPERTY(float, shadow_offset, 0.001f); }; + /** + * The SpotLightNode's configuration. + */ Configuration data; + /** + * Constructor. + * + * This constructs an empty SpotLightNode. + * + */ SpotLightNode() {} + /** + * Constructor. + * + * This constructs a SpotLightNode with the given parameters. + * + * \param name The name of the new SpotLightNode. + * \param configuration A configuration struct to define the SpotLightNode's + * properties. + * \param transform A matrix to describe the SpotLightNode's + * transformation. + */ SpotLightNode(std::string const& name, Configuration const& configuration = Configuration(), math::mat4 const& transform = math::mat4::identity()); /** - * Accepts a visitor and calls concrete visit method + * Accepts a visitor and calls concrete visit method. * - * This method implements the visitor pattern for Nodes + * This method implements the visitor pattern for Nodes. * + * \param visitor A visitor to process the SpotLightNode's data. */ /* virtual */ void accept(NodeVisitor&); + /** + * Updates a SpotLightNode's BoundingBox. + * + * The bounding box is updated according to the transformation matrices of + * all children. + */ void update_bounding_box() const; private: From abd03b1b115167ffdc10271530bbdf5783ceffe5 Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Tue, 25 Feb 2014 17:25:55 +0100 Subject: [PATCH 118/146] Add get_rotation. Add function to extract the rotational part of an affine transformation. --- include/gua/math/math.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/gua/math/math.hpp b/include/gua/math/math.hpp index a546022f1..f7891611c 100644 --- a/include/gua/math/math.hpp +++ b/include/gua/math/math.hpp @@ -102,6 +102,11 @@ inline math::vec3 get_translation(math::mat4 const& m) { return math::vec3(m[12], m[13], m[14]); } +inline math::mat4 get_rotation(math::mat4 const& m) { + math::quat q = ::scm::math::quat::from_matrix(m); + return q.to_matrix(); +} + std::tuple GUA_DLL barycentric(math::vec3 const& a, math::vec3 const& b, math::vec3 const& c, From 275dc270d2a52e75f3397ccdfc6596d89d2ea3b1 Mon Sep 17 00:00:00 2001 From: thelaui Date: Tue, 25 Feb 2014 17:31:49 +0100 Subject: [PATCH 119/146] documented SunLightNode --- include/gua/scenegraph/SpotLightNode.hpp | 4 +- include/gua/scenegraph/SunLightNode.hpp | 82 ++++++++++++++++++++++-- 2 files changed, 78 insertions(+), 8 deletions(-) diff --git a/include/gua/scenegraph/SpotLightNode.hpp b/include/gua/scenegraph/SpotLightNode.hpp index 9fc061e51..e19145691 100644 --- a/include/gua/scenegraph/SpotLightNode.hpp +++ b/include/gua/scenegraph/SpotLightNode.hpp @@ -60,7 +60,7 @@ class GUA_DLL SpotLightNode : public Node { GUA_ADD_PROPERTY(float, softness, 0.5f); /** - * Triggers if the light casts shadows. + * Triggers whether the light casts shadows. */ GUA_ADD_PROPERTY(bool, enable_shadows, false); @@ -88,7 +88,7 @@ class GUA_DLL SpotLightNode : public Node { GUA_ADD_PROPERTY(unsigned, shadow_map_size, 512); /** - * Sets the distance between shadow casting object and shadow edge. + * Sets the offset between a shadow casting object and the shadow's edge. * Increase this value to reduce artefacts (especially shadow acne). */ GUA_ADD_PROPERTY(float, shadow_offset, 0.001f); diff --git a/include/gua/scenegraph/SunLightNode.hpp b/include/gua/scenegraph/SunLightNode.hpp index 480178301..45c2e610c 100644 --- a/include/gua/scenegraph/SunLightNode.hpp +++ b/include/gua/scenegraph/SunLightNode.hpp @@ -33,7 +33,8 @@ namespace gua { /** - * This class is used to represent light in the SceneGraph. + * This class is used to represent directional and parallel light in the + * SceneGraph. * * \ingroup gua_scenegraph */ @@ -41,39 +42,108 @@ class GUA_DLL SunLightNode : public Node { public: struct Configuration { + /** + * The color of the light source. + * It's possible to use negative values and values > 1. + */ GUA_ADD_PROPERTY(utils::Color3f, color, utils::Color3f(1.f, 1.f, 1.f)); + + /** + * Triggers whether the light casts shadows. + */ GUA_ADD_PROPERTY(bool, enable_shadows, false); + + /** + * Triggers volumetric screen-space effects for the light source. + */ GUA_ADD_PROPERTY(bool, enable_godrays, false); + + /** + * Triggers whether or not the light source has influence on objects' + * diffuse shading. + */ GUA_ADD_PROPERTY(bool, enable_diffuse_shading, true); + + /** + * Triggers whether or not the light source has influence on objects' + * specular shading. + */ GUA_ADD_PROPERTY(bool, enable_specular_shading, true); + + /** + * Sets the size in pixel of the texture used for shadow map generation. + * Choose wisely! + */ GUA_ADD_PROPERTY(unsigned, shadow_map_size, 1024); + + /** + * Sets the offset between a shadow casting object and the shadow's edge. + * Increase this value to reduce artefacts (especially shadow acne). + */ GUA_ADD_PROPERTY(float, shadow_offset, 0.001f); + + /** + * Sets the split distance-to-camera values for rendering cascaded shadow + * maps in world coordinates. + */ GUA_ADD_PROPERTY(std::vector, shadow_cascaded_splits, std::vector({0.1f, 2, 10, 50, 100.f})); + + /** + * Sets the value used for near clipping when renering cascaded shadow maps. + */ GUA_ADD_PROPERTY(float, shadow_near_clipping_in_sun_direction, 100.f); }; + /** + * The SunLightNode's configuration. + */ Configuration data; + /** + * Constructor. + * + * This constructs an empty SunLightNode. + * + */ SunLightNode() {} + /** + * Constructor. + * + * This constructs a SunLightNode with the given parameters. + * + * \param name The name of the new SunLightNode. + * \param configuration A configuration struct to define the SunLightNode's + * properties. + * \param transform A matrix to describe the SunLightNode's + * transformation. The default light direction is -y. + * NOTE: Since a SunLightNode is a directional light + * source, only rotations are needed to describe its + * transformation. + */ SunLightNode(std::string const& name, Configuration const& configuration = Configuration(), math::mat4 const& transform = math::mat4::identity()); /** - * Accepts a visitor and calls concrete visit method + * Accepts a visitor and calls concrete visit method. * - * This method implements the visitor pattern for Nodes + * This method implements the visitor pattern for Nodes. * + * \param visitor A visitor to process the SunLightNode's data. */ /* virtual */ void accept(NodeVisitor&); - void update_bounding_box() const; - - private: /** + * Updates a SunLightNode's BoundingBox. * + * The bounding box is updated according to the transformation matrices of + * all children. */ + void update_bounding_box() const; + + private: + std::shared_ptr copy() const; }; From 174270d7ba9abb7e4b26a12b0ab04a19245d757b Mon Sep 17 00:00:00 2001 From: thelaui Date: Tue, 25 Feb 2014 17:45:40 +0100 Subject: [PATCH 120/146] documented TexturedQuadNode and added some missing parameter names --- include/gua/scenegraph/LODNode.hpp | 2 +- include/gua/scenegraph/PointLightNode.hpp | 4 +- include/gua/scenegraph/RayNode.hpp | 2 +- include/gua/scenegraph/ScreenNode.hpp | 5 +- include/gua/scenegraph/SpotLightNode.hpp | 4 +- include/gua/scenegraph/SunLightNode.hpp | 2 +- include/gua/scenegraph/TexturedQuadNode.hpp | 66 +++++++++++++++++++-- 7 files changed, 72 insertions(+), 13 deletions(-) diff --git a/include/gua/scenegraph/LODNode.hpp b/include/gua/scenegraph/LODNode.hpp index a2888fabb..ee77be54d 100644 --- a/include/gua/scenegraph/LODNode.hpp +++ b/include/gua/scenegraph/LODNode.hpp @@ -88,7 +88,7 @@ class GUA_DLL LODNode : public TransformNode { * * \param visitor A visitor to process the LODNode's data. */ - /* virtual */ void accept(NodeVisitor&); + /* virtual */ void accept(NodeVisitor& visitor); private: diff --git a/include/gua/scenegraph/PointLightNode.hpp b/include/gua/scenegraph/PointLightNode.hpp index 2f5f0e7a6..468882e85 100644 --- a/include/gua/scenegraph/PointLightNode.hpp +++ b/include/gua/scenegraph/PointLightNode.hpp @@ -54,7 +54,7 @@ class GUA_DLL PointLightNode : public Node { GUA_ADD_PROPERTY(float, falloff, 1.f); /** - * Triggers if the light casts shadows. NOTE: Not implemented yet! + * Triggers whether the light casts shadows. NOTE: Not implemented yet! */ GUA_ADD_PROPERTY(bool, enable_shadows, false); @@ -117,7 +117,7 @@ class GUA_DLL PointLightNode : public Node { * * \param visitor A visitor to process the PointLightNode's data. */ - /* virtual */ void accept(NodeVisitor&); + /* virtual */ void accept(NodeVisitor& visitor); /** * Updates a PointLightNode's BoundingBox. diff --git a/include/gua/scenegraph/RayNode.hpp b/include/gua/scenegraph/RayNode.hpp index 2b611b665..cd18c8134 100644 --- a/include/gua/scenegraph/RayNode.hpp +++ b/include/gua/scenegraph/RayNode.hpp @@ -65,7 +65,7 @@ class GUA_DLL RayNode : public Node { * * \param visitor A visitor to process the RayNode's data. */ - /* virtual */ void accept(NodeVisitor&); + /* virtual */ void accept(NodeVisitor& visitor); /** * Updates a RayNode's BoundingBox. diff --git a/include/gua/scenegraph/ScreenNode.hpp b/include/gua/scenegraph/ScreenNode.hpp index cf3190eb9..108c15bd2 100644 --- a/include/gua/scenegraph/ScreenNode.hpp +++ b/include/gua/scenegraph/ScreenNode.hpp @@ -69,7 +69,8 @@ class GUA_DLL ScreenNode : public Node { * \param configuration A configuration struct to define the ScreenNode's * properties. * \param transform A matrix to describe the ScreenNode's - * transformation. + * transformation. By default, the ScreenNode is aligned + * with the xy-plane and facing in +z direction. */ ScreenNode(std::string const& name, Configuration const& configuration = Configuration(), @@ -98,7 +99,7 @@ class GUA_DLL ScreenNode : public Node { * * \param visitor A visitor to process the ScreenNode's data. */ - /* virtual */ void accept(NodeVisitor&); + /* virtual */ void accept(NodeVisitor& visitor); private: diff --git a/include/gua/scenegraph/SpotLightNode.hpp b/include/gua/scenegraph/SpotLightNode.hpp index e19145691..9721c4a45 100644 --- a/include/gua/scenegraph/SpotLightNode.hpp +++ b/include/gua/scenegraph/SpotLightNode.hpp @@ -116,7 +116,7 @@ class GUA_DLL SpotLightNode : public Node { * \param configuration A configuration struct to define the SpotLightNode's * properties. * \param transform A matrix to describe the SpotLightNode's - * transformation. + * transformation. The default light direction is -y. */ SpotLightNode(std::string const& name, Configuration const& configuration = Configuration(), @@ -129,7 +129,7 @@ class GUA_DLL SpotLightNode : public Node { * * \param visitor A visitor to process the SpotLightNode's data. */ - /* virtual */ void accept(NodeVisitor&); + /* virtual */ void accept(NodeVisitor& visitor); /** * Updates a SpotLightNode's BoundingBox. diff --git a/include/gua/scenegraph/SunLightNode.hpp b/include/gua/scenegraph/SunLightNode.hpp index 45c2e610c..2d8f7a842 100644 --- a/include/gua/scenegraph/SunLightNode.hpp +++ b/include/gua/scenegraph/SunLightNode.hpp @@ -132,7 +132,7 @@ class GUA_DLL SunLightNode : public Node { * * \param visitor A visitor to process the SunLightNode's data. */ - /* virtual */ void accept(NodeVisitor&); + /* virtual */ void accept(NodeVisitor& visitor); /** * Updates a SunLightNode's BoundingBox. diff --git a/include/gua/scenegraph/TexturedQuadNode.hpp b/include/gua/scenegraph/TexturedQuadNode.hpp index ff478cc19..4b82164b7 100644 --- a/include/gua/scenegraph/TexturedQuadNode.hpp +++ b/include/gua/scenegraph/TexturedQuadNode.hpp @@ -28,7 +28,10 @@ namespace gua { /** - * This class is used to represent a screen in the SceneGraph. + * This class is used to represent a textured quad in the SceneGraph. + * + * A TexturedQuadNode is capable of displaying any texture and even Pipeline + * output. It may therefore be used to implement portals, mirrors etc. * * \ingroup gua_scenegraph */ @@ -36,31 +39,86 @@ class GUA_DLL TexturedQuadNode : public Node { public: struct Configuration { + /** + * A string referring to an entry in guacamole's TextureDatabase. + */ GUA_ADD_PROPERTY(std::string, texture, ""); + + /** + * A vector containing width and height of the TexturedQuadNode. + */ GUA_ADD_PROPERTY(math::vec2, size, math::vec2(1.f, 1.f)); + + /** + * Sets wheter the given texture is a stereo texture. Set this to true, if + * you want to display a texture rendered by a Pipeline with stereo enabled. + */ GUA_ADD_PROPERTY(bool, is_stereo_texture, false); + + /** + * Triggers whether the given texture's x coordinates are flipped. + */ GUA_ADD_PROPERTY(bool, flip_x, false); + + /** + * Triggers whether the given texture's y coordinates are flipped. + */ GUA_ADD_PROPERTY(bool, flip_y, false); }; + /** + * The TexturedQuadNode's configuration. + */ Configuration data; + /** + * Constructor. + * + * This constructs an empty TexturedQuadNode. + * + */ TexturedQuadNode() {} + /** + * Constructor. + * + * This constructs a TexturedQuadNode with the given parameters. + * + * \param name The name of the new TexturedQuadNode. + * \param configuration A configuration struct to define the + * TexturedQuadNode's properties. + * \param transform A matrix to describe the TexturedQuadNode's + * transformation. By default, the TexturedQuadNode is + * aligned with the xy-plane and facing in +z direction. + */ TexturedQuadNode(std::string const& name, Configuration const& configuration = Configuration(), math::mat4 const& transform = math::mat4::identity()); + /** + * Returns the TexturedQuadNode's transformation, considering the scaling + * specified in the Configuration. + * + * \return math::mat4 The TexturedQuadNode's scaled transformation. + */ math::mat4 get_scaled_transform() const; + + /** + * Returns the TexturedQuadNode's world transformation, considering the + * scaling specified in the Configuration. + * + * \return math::mat4 The TexturedQuadNode's scaled world transformation. + */ math::mat4 get_scaled_world_transform() const; /** - * Accepts a visitor and calls concrete visit method + * Accepts a visitor and calls concrete visit method. * - * This method implements the visitor pattern for Nodes + * This method implements the visitor pattern for Nodes. * + * \param visitor A visitor to process the TexturedQuadNode's data. */ - /* virtual */ void accept(NodeVisitor&); + /* virtual */ void accept(NodeVisitor& visitor); /*virtual*/ void update_bounding_box() const; From e36cab6e12ee53b31b27c243638e5487f1158f6e Mon Sep 17 00:00:00 2001 From: thelaui Date: Tue, 25 Feb 2014 17:57:50 +0100 Subject: [PATCH 121/146] documented TransformNode and VolumeNode --- include/gua/scenegraph/GeometryNode.hpp | 4 +-- include/gua/scenegraph/TransformNode.hpp | 24 ++++++++++---- include/gua/scenegraph/VolumeNode.hpp | 40 ++++++++++++++++++++++-- 3 files changed, 58 insertions(+), 10 deletions(-) diff --git a/include/gua/scenegraph/GeometryNode.hpp b/include/gua/scenegraph/GeometryNode.hpp index 2bb6ad6ef..1cccde639 100644 --- a/include/gua/scenegraph/GeometryNode.hpp +++ b/include/gua/scenegraph/GeometryNode.hpp @@ -45,12 +45,12 @@ class GUA_DLL GeometryNode : public Node { struct Configuration { /** - * A string referring to an entry in GeometryDatabase. + * A string referring to an entry in guacamole's GeometryDatabase. */ GUA_ADD_PROPERTY(std::string, geometry, "gua_default_geometry"); /** - * A string referring to an entry in MaterialDatabase. + * A string referring to an entry in guacamole's MaterialDatabase. */ GUA_ADD_PROPERTY(std::string, material, "gua_default_material"); }; diff --git a/include/gua/scenegraph/TransformNode.hpp b/include/gua/scenegraph/TransformNode.hpp index b9c1c12d0..64e57950e 100644 --- a/include/gua/scenegraph/TransformNode.hpp +++ b/include/gua/scenegraph/TransformNode.hpp @@ -28,13 +28,23 @@ namespace gua { /** - * This class is used to represent an empty node in the SceneGraph. + * This class is used to represent an transformation node in the SceneGraph. + * + * Because any of guacamole's Nodes stores children and transformation, the + * TransformationNode only exists for the convenience of explicitly limiting a + * Node's purpose to the transformation of several children. * * \ingroup gua_scenegraph */ class GUA_DLL TransformNode : public Node { public: + /** + * Constructor. + * + * This constructs an empty TransformNode. + * + */ TransformNode() {}; /** @@ -42,19 +52,21 @@ class GUA_DLL TransformNode : public Node { * * This constructs a TransformNode with the given parameters. * - * \param name The Node's name - * \param transform The transformation of the object the Node contains. + * \param name The name of the new TransformNode. + * \param transform A matrix to describe the TransformNode's + * transformation. */ TransformNode(std::string const& name, math::mat4 const& transform = math::mat4::identity()); /** - * Accepts a visitor and calls concrete visit method + * Accepts a visitor and calls concrete visit method. * - * This method implements the visitor pattern for Nodes + * This method implements the visitor pattern for Nodes. * + * \param visitor A visitor to process the TransformNode's data. */ - /* virtual */ void accept(NodeVisitor&); + /* virtual */ void accept(NodeVisitor& visitor); private: diff --git a/include/gua/scenegraph/VolumeNode.hpp b/include/gua/scenegraph/VolumeNode.hpp index d9f1e22fc..be77f84bc 100644 --- a/include/gua/scenegraph/VolumeNode.hpp +++ b/include/gua/scenegraph/VolumeNode.hpp @@ -40,19 +40,55 @@ class GUA_DLL VolumeNode : public Node { public: struct Configuration { + /** + * A string referring to an entry in guacamole's GeometryDatabase. + */ GUA_ADD_PROPERTY(std::string, volume, "gua_volume_default"); }; + /** + * The VolumeNode's configuration. + */ Configuration data; + /** + * Constructor. + * + * This constructs an empty VolumeNode. + * + */ VolumeNode() {}; + /** + * Constructor. + * + * This constructs a VolumeNode with the given parameters. + * + * \param name The name of the new VolumeNode. + * \param configuration A configuration struct to define the VolumeNode's + * properties. + * \param transform A matrix to describe the VolumeNode's + * transformation. + */ VolumeNode(std::string const& name, Configuration const& configuration = Configuration(), math::mat4 const& transform = math::mat4::identity()); - /*virtual*/ void accept(NodeVisitor&); - + /** + * Accepts a visitor and calls concrete visit method. + * + * This method implements the visitor pattern for Nodes. + * + * \param visitor A visitor to process the VolumeNode's data. + */ + /*virtual*/ void accept(NodeVisitor& visitor); + + /** + * Updates a VolumeNode's BoundingBox. + * + * The bounding box is updated according to the transformation matrices of + * all children. + */ /*virtual*/ void update_bounding_box() const; /*virtual*/ void ray_test_impl(RayNode const& ray, From 2d797e7fd5700b3b52130d977ebc94f473c7f518 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Tue, 25 Feb 2014 18:22:28 +0100 Subject: [PATCH 122/146] styled Doxygen output according to website style --- doc/Doxyfile | 4 +- doc/body.png | Bin 0 -> 31838 bytes doc/bootstrap.min.css | 9 + doc/darkstrap.css | 430 ++++++++++++ doc/header.html | 129 ++++ doc/style.css | 1471 +++++++++++++++++++++++++++++++++++++++++ scripts/make_doc.sh | 10 + 7 files changed, 2051 insertions(+), 2 deletions(-) create mode 100644 doc/body.png create mode 100644 doc/bootstrap.min.css create mode 100644 doc/darkstrap.css create mode 100644 doc/header.html create mode 100644 doc/style.css create mode 100755 scripts/make_doc.sh diff --git a/doc/Doxyfile b/doc/Doxyfile index 12c1beb60..b885839a1 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -26,7 +26,7 @@ DOXYFILE_ENCODING = UTF-8 # identify the project. Note that if you do not use Doxywizard you need # to put quotes around the project name if it contains spaces. -PROJECT_NAME = "My Project" +PROJECT_NAME = # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or @@ -873,7 +873,7 @@ HTML_FILE_EXTENSION = .html # have to redo this when upgrading to a newer version of doxygen or when # changing the value of configuration settings such as GENERATE_TREEVIEW! -HTML_HEADER = +HTML_HEADER = header.html # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a diff --git a/doc/body.png b/doc/body.png new file mode 100644 index 0000000000000000000000000000000000000000..d98f86684b3d0e3c272fa34cc631f3220b6de92b GIT binary patch literal 31838 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.radio:first-child,.controls>.checkbox:first-child{padding-top:5px}.radio.inline,.checkbox.inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle}.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px}.input-mini{width:60px}.input-small{width:90px}.input-medium{width:150px}.input-large{width:210px}.input-xlarge{width:270px}.input-xxlarge{width:530px}input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"]{float:none;margin-left:0}.input-append input[class*="span"],.input-append .uneditable-input[class*="span"],.input-prepend input[class*="span"],.input-prepend .uneditable-input[class*="span"],.row-fluid .input-prepend [class*="span"],.row-fluid .input-append [class*="span"]{display:inline-block}input,textarea,.uneditable-input{margin-left:0}input.span12,textarea.span12,.uneditable-input.span12{width:930px}input.span11,textarea.span11,.uneditable-input.span11{width:850px}input.span10,textarea.span10,.uneditable-input.span10{width:770px}input.span9,textarea.span9,.uneditable-input.span9{width:690px}input.span8,textarea.span8,.uneditable-input.span8{width:610px}input.span7,textarea.span7,.uneditable-input.span7{width:530px}input.span6,textarea.span6,.uneditable-input.span6{width:450px}input.span5,textarea.span5,.uneditable-input.span5{width:370px}input.span4,textarea.span4,.uneditable-input.span4{width:290px}input.span3,textarea.span3,.uneditable-input.span3{width:210px}input.span2,textarea.span2,.uneditable-input.span2{width:130px}input.span1,textarea.span1,.uneditable-input.span1{width:50px}input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#eee;border-color:#ddd}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"][readonly],input[type="checkbox"][readonly]{background-color:transparent}.control-group.warning>label,.control-group.warning .help-block,.control-group.warning .help-inline{color:#c09853}.control-group.warning .checkbox,.control-group.warning .radio,.control-group.warning input,.control-group.warning select,.control-group.warning textarea{color:#c09853;border-color:#c09853}.control-group.warning .checkbox:focus,.control-group.warning .radio:focus,.control-group.warning input:focus,.control-group.warning select:focus,.control-group.warning textarea:focus{border-color:#a47e3c;-webkit-box-shadow:0 0 6px #dbc59e;-moz-box-shadow:0 0 6px #dbc59e;box-shadow:0 0 6px #dbc59e}.control-group.warning .input-prepend .add-on,.control-group.warning .input-append .add-on{color:#c09853;background-color:#fcf8e3;border-color:#c09853}.control-group.error>label,.control-group.error .help-block,.control-group.error .help-inline{color:#b94a48}.control-group.error .checkbox,.control-group.error .radio,.control-group.error input,.control-group.error select,.control-group.error textarea{color:#b94a48;border-color:#b94a48}.control-group.error .checkbox:focus,.control-group.error .radio:focus,.control-group.error input:focus,.control-group.error select:focus,.control-group.error textarea:focus{border-color:#953b39;-webkit-box-shadow:0 0 6px #d59392;-moz-box-shadow:0 0 6px #d59392;box-shadow:0 0 6px #d59392}.control-group.error .input-prepend .add-on,.control-group.error .input-append .add-on{color:#b94a48;background-color:#f2dede;border-color:#b94a48}.control-group.success>label,.control-group.success .help-block,.control-group.success .help-inline{color:#468847}.control-group.success .checkbox,.control-group.success .radio,.control-group.success input,.control-group.success select,.control-group.success textarea{color:#468847;border-color:#468847}.control-group.success .checkbox:focus,.control-group.success .radio:focus,.control-group.success input:focus,.control-group.success select:focus,.control-group.success textarea:focus{border-color:#356635;-webkit-box-shadow:0 0 6px #7aba7b;-moz-box-shadow:0 0 6px #7aba7b;box-shadow:0 0 6px #7aba7b}.control-group.success .input-prepend .add-on,.control-group.success .input-append .add-on{color:#468847;background-color:#dff0d8;border-color:#468847}input:focus:required:invalid,textarea:focus:required:invalid,select:focus:required:invalid{color:#b94a48;border-color:#ee5f5b}input:focus:required:invalid:focus,textarea:focus:required:invalid:focus,select:focus:required:invalid:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7}.form-actions{padding:17px 20px 18px;margin-top:18px;margin-bottom:18px;background-color:#f5f5f5;border-top:1px solid #e5e5e5;*zoom:1}.form-actions:before,.form-actions:after{display:table;content:""}.form-actions:after{clear:both}.uneditable-input{overflow:hidden;white-space:nowrap;cursor:not-allowed;background-color:#fff;border-color:#eee;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.025);box-shadow:inset 0 1px 2px rgba(0,0,0,0.025)}:-moz-placeholder{color:#999}:-ms-input-placeholder{color:#999}::-webkit-input-placeholder{color:#999}.help-block,.help-inline{color:#555}.help-block{display:block;margin-bottom:9px}.help-inline{display:inline-block;*display:inline;padding-left:5px;vertical-align:middle;*zoom:1}.input-prepend,.input-append{margin-bottom:5px}.input-prepend input,.input-append input,.input-prepend select,.input-append select,.input-prepend .uneditable-input,.input-append .uneditable-input{position:relative;margin-bottom:0;*margin-left:0;vertical-align:middle;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.input-prepend input:focus,.input-append input:focus,.input-prepend select:focus,.input-append select:focus,.input-prepend .uneditable-input:focus,.input-append .uneditable-input:focus{z-index:2}.input-prepend .uneditable-input,.input-append .uneditable-input{border-left-color:#ccc}.input-prepend .add-on,.input-append .add-on{display:inline-block;width:auto;height:18px;min-width:16px;padding:4px 5px;font-weight:normal;line-height:18px;text-align:center;text-shadow:0 1px 0 #fff;vertical-align:middle;background-color:#eee;border:1px solid #ccc}.input-prepend .add-on,.input-append .add-on,.input-prepend .btn,.input-append .btn{margin-left:-1px;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-prepend .active,.input-append .active{background-color:#a9dba9;border-color:#46a546}.input-prepend .add-on,.input-prepend .btn{margin-right:-1px}.input-prepend .add-on:first-child,.input-prepend .btn:first-child{-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.input-append input,.input-append select,.input-append .uneditable-input{-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.input-append .uneditable-input{border-right-color:#ccc;border-left-color:#eee}.input-append .add-on:last-child,.input-append .btn:last-child{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.input-prepend.input-append input,.input-prepend.input-append select,.input-prepend.input-append .uneditable-input{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-prepend.input-append .add-on:first-child,.input-prepend.input-append .btn:first-child{margin-right:-1px;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.input-prepend.input-append .add-on:last-child,.input-prepend.input-append .btn:last-child{margin-left:-1px;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.search-query{padding-right:14px;padding-right:4px \9;padding-left:14px;padding-left:4px \9;margin-bottom:0;-webkit-border-radius:14px;-moz-border-radius:14px;border-radius:14px}.form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input,.form-search .input-prepend,.form-inline .input-prepend,.form-horizontal .input-prepend,.form-search .input-append,.form-inline .input-append,.form-horizontal .input-append{display:inline-block;*display:inline;margin-bottom:0;*zoom:1}.form-search .hide,.form-inline .hide,.form-horizontal .hide{display:none}.form-search label,.form-inline label{display:inline-block}.form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{margin-bottom:0}.form-search .radio,.form-search .checkbox,.form-inline .radio,.form-inline .checkbox{padding-left:0;margin-bottom:0;vertical-align:middle}.form-search .radio input[type="radio"],.form-search .checkbox input[type="checkbox"],.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:left;margin-right:3px;margin-left:0}.control-group{margin-bottom:9px}legend+.control-group{margin-top:18px;-webkit-margin-top-collapse:separate}.form-horizontal .control-group{margin-bottom:18px;*zoom:1}.form-horizontal .control-group:before,.form-horizontal .control-group:after{display:table;content:""}.form-horizontal .control-group:after{clear:both}.form-horizontal .control-label{float:left;width:140px;padding-top:5px;text-align:right}.form-horizontal .controls{*display:inline-block;*padding-left:20px;margin-left:160px;*margin-left:0}.form-horizontal .controls:first-child{*padding-left:160px}.form-horizontal .help-block{margin-top:9px;margin-bottom:0}.form-horizontal .form-actions{padding-left:160px}table{max-width:100%;background-color:transparent;border-collapse:collapse;border-spacing:0}.table{width:100%;margin-bottom:18px}.table th,.table td{padding:8px;line-height:18px;text-align:left;vertical-align:top;border-top:1px solid #ddd}.table th{font-weight:bold}.table thead th{vertical-align:bottom}.table caption+thead tr:first-child th,.table caption+thead tr:first-child td,.table colgroup+thead tr:first-child th,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child th,.table thead:first-child tr:first-child td{border-top:0}.table tbody+tbody{border-top:2px solid #ddd}.table-condensed th,.table-condensed td{padding:4px 5px}.table-bordered{border:1px solid #ddd;border-collapse:separate;*border-collapse:collapsed;border-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.table-bordered th,.table-bordered td{border-left:1px solid #ddd}.table-bordered caption+thead tr:first-child th,.table-bordered caption+tbody tr:first-child th,.table-bordered caption+tbody tr:first-child td,.table-bordered colgroup+thead tr:first-child th,.table-bordered colgroup+tbody tr:first-child th,.table-bordered colgroup+tbody tr:first-child td,.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0}.table-bordered thead:first-child tr:first-child th:first-child,.table-bordered tbody:first-child tr:first-child td:first-child{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}.table-bordered thead:first-child tr:first-child th:last-child,.table-bordered tbody:first-child tr:first-child td:last-child{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px}.table-bordered thead:last-child tr:last-child th:first-child,.table-bordered tbody:last-child tr:last-child td:first-child{-webkit-border-radius:0 0 0 4px;-moz-border-radius:0 0 0 4px;border-radius:0 0 0 4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px}.table-bordered thead:last-child tr:last-child th:last-child,.table-bordered tbody:last-child tr:last-child td:last-child{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px}.table-striped tbody tr:nth-child(odd) td,.table-striped tbody tr:nth-child(odd) th{background-color:#f9f9f9}.table tbody tr:hover td,.table tbody tr:hover th{background-color:#f5f5f5}table .span1{float:none;width:44px;margin-left:0}table .span2{float:none;width:124px;margin-left:0}table .span3{float:none;width:204px;margin-left:0}table .span4{float:none;width:284px;margin-left:0}table .span5{float:none;width:364px;margin-left:0}table .span6{float:none;width:444px;margin-left:0}table .span7{float:none;width:524px;margin-left:0}table .span8{float:none;width:604px;margin-left:0}table .span9{float:none;width:684px;margin-left:0}table .span10{float:none;width:764px;margin-left:0}table .span11{float:none;width:844px;margin-left:0}table .span12{float:none;width:924px;margin-left:0}table .span13{float:none;width:1004px;margin-left:0}table .span14{float:none;width:1084px;margin-left:0}table .span15{float:none;width:1164px;margin-left:0}table .span16{float:none;width:1244px;margin-left:0}table .span17{float:none;width:1324px;margin-left:0}table .span18{float:none;width:1404px;margin-left:0}table .span19{float:none;width:1484px;margin-left:0}table .span20{float:none;width:1564px;margin-left:0}table .span21{float:none;width:1644px;margin-left:0}table .span22{float:none;width:1724px;margin-left:0}table .span23{float:none;width:1804px;margin-left:0}table .span24{float:none;width:1884px;margin-left:0}[class^="icon-"],[class*=" icon-"]{display:inline-block;width:14px;height:14px;*margin-right:.3em;line-height:14px;vertical-align:text-top;background-image:url("../img/glyphicons-halflings.png");background-position:14px 14px;background-repeat:no-repeat}[class^="icon-"]:last-child,[class*=" icon-"]:last-child{*margin-left:0}.icon-white{background-image:url("../img/glyphicons-halflings-white.png")}.icon-glass{background-position:0 0}.icon-music{background-position:-24px 0}.icon-search{background-position:-48px 0}.icon-envelope{background-position:-72px 0}.icon-heart{background-position:-96px 0}.icon-star{background-position:-120px 0}.icon-star-empty{background-position:-144px 0}.icon-user{background-position:-168px 0}.icon-film{background-position:-192px 0}.icon-th-large{background-position:-216px 0}.icon-th{background-position:-240px 0}.icon-th-list{background-position:-264px 0}.icon-ok{background-position:-288px 0}.icon-remove{background-position:-312px 0}.icon-zoom-in{background-position:-336px 0}.icon-zoom-out{background-position:-360px 0}.icon-off{background-position:-384px 0}.icon-signal{background-position:-408px 0}.icon-cog{background-position:-432px 0}.icon-trash{background-position:-456px 0}.icon-home{background-position:0 -24px}.icon-file{background-position:-24px -24px}.icon-time{background-position:-48px -24px}.icon-road{background-position:-72px -24px}.icon-download-alt{background-position:-96px -24px}.icon-download{background-position:-120px -24px}.icon-upload{background-position:-144px -24px}.icon-inbox{background-position:-168px -24px}.icon-play-circle{background-position:-192px -24px}.icon-repeat{background-position:-216px -24px}.icon-refresh{background-position:-240px -24px}.icon-list-alt{background-position:-264px -24px}.icon-lock{background-position:-287px -24px}.icon-flag{background-position:-312px -24px}.icon-headphones{background-position:-336px -24px}.icon-volume-off{background-position:-360px -24px}.icon-volume-down{background-position:-384px -24px}.icon-volume-up{background-position:-408px -24px}.icon-qrcode{background-position:-432px -24px}.icon-barcode{background-position:-456px -24px}.icon-tag{background-position:0 -48px}.icon-tags{background-position:-25px -48px}.icon-book{background-position:-48px -48px}.icon-bookmark{background-position:-72px -48px}.icon-print{background-position:-96px -48px}.icon-camera{background-position:-120px -48px}.icon-font{background-position:-144px -48px}.icon-bold{background-position:-167px -48px}.icon-italic{background-position:-192px -48px}.icon-text-height{background-position:-216px -48px}.icon-text-width{background-position:-240px -48px}.icon-align-left{background-position:-264px -48px}.icon-align-center{background-position:-288px -48px}.icon-align-right{background-position:-312px -48px}.icon-align-justify{background-position:-336px -48px}.icon-list{background-position:-360px -48px}.icon-indent-left{background-position:-384px -48px}.icon-indent-right{background-position:-408px -48px}.icon-facetime-video{background-position:-432px -48px}.icon-picture{background-position:-456px -48px}.icon-pencil{background-position:0 -72px}.icon-map-marker{background-position:-24px -72px}.icon-adjust{background-position:-48px -72px}.icon-tint{background-position:-72px -72px}.icon-edit{background-position:-96px -72px}.icon-share{background-position:-120px -72px}.icon-check{background-position:-144px -72px}.icon-move{background-position:-168px -72px}.icon-step-backward{background-position:-192px -72px}.icon-fast-backward{background-position:-216px -72px}.icon-backward{background-position:-240px -72px}.icon-play{background-position:-264px -72px}.icon-pause{background-position:-288px -72px}.icon-stop{background-position:-312px -72px}.icon-forward{background-position:-336px -72px}.icon-fast-forward{background-position:-360px -72px}.icon-step-forward{background-position:-384px -72px}.icon-eject{background-position:-408px -72px}.icon-chevron-left{background-position:-432px -72px}.icon-chevron-right{background-position:-456px -72px}.icon-plus-sign{background-position:0 -96px}.icon-minus-sign{background-position:-24px -96px}.icon-remove-sign{background-position:-48px -96px}.icon-ok-sign{background-position:-72px -96px}.icon-question-sign{background-position:-96px -96px}.icon-info-sign{background-position:-120px -96px}.icon-screenshot{background-position:-144px -96px}.icon-remove-circle{background-position:-168px -96px}.icon-ok-circle{background-position:-192px -96px}.icon-ban-circle{background-position:-216px -96px}.icon-arrow-left{background-position:-240px -96px}.icon-arrow-right{background-position:-264px -96px}.icon-arrow-up{background-position:-289px -96px}.icon-arrow-down{background-position:-312px -96px}.icon-share-alt{background-position:-336px -96px}.icon-resize-full{background-position:-360px -96px}.icon-resize-small{background-position:-384px -96px}.icon-plus{background-position:-408px -96px}.icon-minus{background-position:-433px -96px}.icon-asterisk{background-position:-456px -96px}.icon-exclamation-sign{background-position:0 -120px}.icon-gift{background-position:-24px -120px}.icon-leaf{background-position:-48px -120px}.icon-fire{background-position:-72px -120px}.icon-eye-open{background-position:-96px -120px}.icon-eye-close{background-position:-120px -120px}.icon-warning-sign{background-position:-144px -120px}.icon-plane{background-position:-168px -120px}.icon-calendar{background-position:-192px -120px}.icon-random{background-position:-216px -120px}.icon-comment{background-position:-240px -120px}.icon-magnet{background-position:-264px -120px}.icon-chevron-up{background-position:-288px -120px}.icon-chevron-down{background-position:-313px -119px}.icon-retweet{background-position:-336px -120px}.icon-shopping-cart{background-position:-360px -120px}.icon-folder-close{background-position:-384px -120px}.icon-folder-open{background-position:-408px -120px}.icon-resize-vertical{background-position:-432px -119px}.icon-resize-horizontal{background-position:-456px -118px}.icon-hdd{background-position:0 -144px}.icon-bullhorn{background-position:-24px -144px}.icon-bell{background-position:-48px -144px}.icon-certificate{background-position:-72px -144px}.icon-thumbs-up{background-position:-96px -144px}.icon-thumbs-down{background-position:-120px -144px}.icon-hand-right{background-position:-144px -144px}.icon-hand-left{background-position:-168px -144px}.icon-hand-up{background-position:-192px -144px}.icon-hand-down{background-position:-216px -144px}.icon-circle-arrow-right{background-position:-240px -144px}.icon-circle-arrow-left{background-position:-264px -144px}.icon-circle-arrow-up{background-position:-288px -144px}.icon-circle-arrow-down{background-position:-312px -144px}.icon-globe{background-position:-336px -144px}.icon-wrench{background-position:-360px -144px}.icon-tasks{background-position:-384px -144px}.icon-filter{background-position:-408px -144px}.icon-briefcase{background-position:-432px -144px}.icon-fullscreen{background-position:-456px -144px}.dropup,.dropdown{position:relative}.dropdown-toggle{*margin-bottom:-3px}.dropdown-toggle:active,.open .dropdown-toggle{outline:0}.caret{display:inline-block;width:0;height:0;vertical-align:top;border-top:4px solid #000;border-right:4px solid transparent;border-left:4px solid transparent;content:"";opacity:.3;filter:alpha(opacity=30)}.dropdown .caret{margin-top:8px;margin-left:2px}.dropdown:hover .caret,.open .caret{opacity:1;filter:alpha(opacity=100)}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:4px 0;margin:1px 0 0;list-style:none;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);*border-right-width:2px;*border-bottom-width:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{*width:100%;height:1px;margin:8px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.dropdown-menu a{display:block;padding:3px 15px;clear:both;font-weight:normal;line-height:18px;color:#333;white-space:nowrap}.dropdown-menu li>a:hover,.dropdown-menu .active>a,.dropdown-menu .active>a:hover{color:#fff;text-decoration:none;background-color:#08c}.open{*z-index:1000}.open>.dropdown-menu{display:block}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid #000;content:"\2191"}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}.typeahead{margin-top:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #eee;border:1px solid rgba(0,0,0,0.05);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-large{padding:24px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.well-small{padding:9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.fade{opacity:0;-webkit-transition:opacity .15s linear;-moz-transition:opacity .15s linear;-ms-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{position:relative;height:0;overflow:hidden;-webkit-transition:height .35s ease;-moz-transition:height .35s ease;-ms-transition:height .35s ease;-o-transition:height .35s ease;transition:height .35s ease}.collapse.in{height:auto}.close{float:right;font-size:20px;font-weight:bold;line-height:18px;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.close:hover{color:#000;text-decoration:none;cursor:pointer;opacity:.4;filter:alpha(opacity=40)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.btn{display:inline-block;*display:inline;padding:4px 10px 4px;margin-bottom:0;*margin-left:.3em;font-size:13px;line-height:18px;*line-height:20px;color:#333;text-align:center;text-shadow:0 1px 1px rgba(255,255,255,0.75);vertical-align:middle;cursor:pointer;background-color:#f5f5f5;*background-color:#e6e6e6;background-image:-ms-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(top,#fff,#e6e6e6);background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-repeat:repeat-x;border:1px solid #ccc;*border:0;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ffffff',endColorstr='#e6e6e6',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false);*zoom:1;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn:hover,.btn:active,.btn.active,.btn.disabled,.btn[disabled]{background-color:#e6e6e6;*background-color:#d9d9d9}.btn:active,.btn.active{background-color:#ccc \9}.btn:first-child{*margin-left:0}.btn:hover{color:#333;text-decoration:none;background-color:#e6e6e6;*background-color:#d9d9d9;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-ms-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.active,.btn:active{background-color:#e6e6e6;background-color:#d9d9d9 \9;background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn.disabled,.btn[disabled]{cursor:default;background-color:#e6e6e6;background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-large{padding:9px 14px;font-size:15px;line-height:normal;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.btn-large [class^="icon-"]{margin-top:1px}.btn-small{padding:5px 9px;font-size:11px;line-height:16px}.btn-small [class^="icon-"]{margin-top:-1px}.btn-mini{padding:2px 6px;font-size:11px;line-height:14px}.btn-primary,.btn-primary:hover,.btn-warning,.btn-warning:hover,.btn-danger,.btn-danger:hover,.btn-success,.btn-success:hover,.btn-info,.btn-info:hover,.btn-inverse,.btn-inverse:hover{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active,.btn-inverse.active{color:rgba(255,255,255,0.75)}.btn{border-color:#ccc;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25)}.btn-primary{background-color:#0074cc;*background-color:#05c;background-image:-ms-linear-gradient(top,#08c,#05c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#05c));background-image:-webkit-linear-gradient(top,#08c,#05c);background-image:-o-linear-gradient(top,#08c,#05c);background-image:-moz-linear-gradient(top,#08c,#05c);background-image:linear-gradient(top,#08c,#05c);background-repeat:repeat-x;border-color:#05c #05c #003580;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#0088cc',endColorstr='#0055cc',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.btn-primary:hover,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{background-color:#05c;*background-color:#004ab3}.btn-primary:active,.btn-primary.active{background-color:#004099 \9}.btn-warning{background-color:#faa732;*background-color:#f89406;background-image:-ms-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(top,#fbb450,#f89406);background-repeat:repeat-x;border-color:#f89406 #f89406 #ad6704;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#fbb450',endColorstr='#f89406',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.btn-warning:hover,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{background-color:#f89406;*background-color:#df8505}.btn-warning:active,.btn-warning.active{background-color:#c67605 \9}.btn-danger{background-color:#da4f49;*background-color:#bd362f;background-image:-ms-linear-gradient(top,#ee5f5b,#bd362f);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#bd362f));background-image:-webkit-linear-gradient(top,#ee5f5b,#bd362f);background-image:-o-linear-gradient(top,#ee5f5b,#bd362f);background-image:-moz-linear-gradient(top,#ee5f5b,#bd362f);background-image:linear-gradient(top,#ee5f5b,#bd362f);background-repeat:repeat-x;border-color:#bd362f #bd362f #802420;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ee5f5b',endColorstr='#bd362f',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.btn-danger:hover,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{background-color:#bd362f;*background-color:#a9302a}.btn-danger:active,.btn-danger.active{background-color:#942a25 \9}.btn-success{background-color:#5bb75b;*background-color:#51a351;background-image:-ms-linear-gradient(top,#62c462,#51a351);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#51a351));background-image:-webkit-linear-gradient(top,#62c462,#51a351);background-image:-o-linear-gradient(top,#62c462,#51a351);background-image:-moz-linear-gradient(top,#62c462,#51a351);background-image:linear-gradient(top,#62c462,#51a351);background-repeat:repeat-x;border-color:#51a351 #51a351 #387038;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#62c462',endColorstr='#51a351',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.btn-success:hover,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{background-color:#51a351;*background-color:#499249}.btn-success:active,.btn-success.active{background-color:#408140 \9}.btn-info{background-color:#49afcd;*background-color:#2f96b4;background-image:-ms-linear-gradient(top,#5bc0de,#2f96b4);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#2f96b4));background-image:-webkit-linear-gradient(top,#5bc0de,#2f96b4);background-image:-o-linear-gradient(top,#5bc0de,#2f96b4);background-image:-moz-linear-gradient(top,#5bc0de,#2f96b4);background-image:linear-gradient(top,#5bc0de,#2f96b4);background-repeat:repeat-x;border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#5bc0de',endColorstr='#2f96b4',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.btn-info:hover,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{background-color:#2f96b4;*background-color:#2a85a0}.btn-info:active,.btn-info.active{background-color:#24748c \9}.btn-inverse{background-color:#414141;*background-color:#222;background-image:-ms-linear-gradient(top,#555,#222);background-image:-webkit-gradient(linear,0 0,0 100%,from(#555),to(#222));background-image:-webkit-linear-gradient(top,#555,#222);background-image:-o-linear-gradient(top,#555,#222);background-image:-moz-linear-gradient(top,#555,#222);background-image:linear-gradient(top,#555,#222);background-repeat:repeat-x;border-color:#222 #222 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#555555',endColorstr='#222222',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false)}.btn-inverse:hover,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{background-color:#222;*background-color:#151515}.btn-inverse:active,.btn-inverse.active{background-color:#080808 \9}button.btn,input[type="submit"].btn{*padding-top:2px;*padding-bottom:2px}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0}button.btn.btn-large,input[type="submit"].btn.btn-large{*padding-top:7px;*padding-bottom:7px}button.btn.btn-small,input[type="submit"].btn.btn-small{*padding-top:3px;*padding-bottom:3px}button.btn.btn-mini,input[type="submit"].btn.btn-mini{*padding-top:1px;*padding-bottom:1px}.btn-group{position:relative;*margin-left:.3em;*zoom:1}.btn-group:before,.btn-group:after{display:table;content:""}.btn-group:after{clear:both}.btn-group:first-child{*margin-left:0}.btn-group+.btn-group{margin-left:5px}.btn-toolbar{margin-top:9px;margin-bottom:9px}.btn-toolbar .btn-group{display:inline-block;*display:inline;*zoom:1}.btn-group>.btn{position:relative;float:left;margin-left:-1px;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-group>.btn:first-child{margin-left:0;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:4px}.btn-group>.btn:last-child,.btn-group>.dropdown-toggle{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:4px}.btn-group>.btn.large:first-child{margin-left:0;-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topleft:6px}.btn-group>.btn.large:last-child,.btn-group>.large.dropdown-toggle{-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px}.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active{z-index:2}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.dropdown-toggle{*padding-top:4px;padding-right:8px;*padding-bottom:4px;padding-left:8px;-webkit-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn-group>.btn-mini.dropdown-toggle{padding-right:5px;padding-left:5px}.btn-group>.btn-small.dropdown-toggle{*padding-top:4px;*padding-bottom:4px}.btn-group>.btn-large.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn-group.open .btn.dropdown-toggle{background-color:#e6e6e6}.btn-group.open .btn-primary.dropdown-toggle{background-color:#05c}.btn-group.open .btn-warning.dropdown-toggle{background-color:#f89406}.btn-group.open .btn-danger.dropdown-toggle{background-color:#bd362f}.btn-group.open .btn-success.dropdown-toggle{background-color:#51a351}.btn-group.open .btn-info.dropdown-toggle{background-color:#2f96b4}.btn-group.open .btn-inverse.dropdown-toggle{background-color:#222}.btn .caret{margin-top:7px;margin-left:0}.btn:hover .caret,.open.btn-group .caret{opacity:1;filter:alpha(opacity=100)}.btn-mini .caret{margin-top:5px}.btn-small .caret{margin-top:6px}.btn-large .caret{margin-top:6px;border-top-width:5px;border-right-width:5px;border-left-width:5px}.dropup .btn-large .caret{border-top:0;border-bottom:5px solid #000}.btn-primary .caret,.btn-warning .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret,.btn-inverse .caret{border-top-color:#fff;border-bottom-color:#fff;opacity:.75;filter:alpha(opacity=75)}.alert{padding:8px 35px 8px 14px;margin-bottom:18px;color:#c09853;text-shadow:0 1px 0 rgba(255,255,255,0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.alert-heading{color:inherit}.alert .close{position:relative;top:-2px;right:-21px;line-height:18px}.alert-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.alert-danger,.alert-error{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.alert-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.alert-block{padding-top:14px;padding-bottom:14px}.alert-block>p,.alert-block>ul{margin-bottom:0}.alert-block p+p{margin-top:5px}.nav{margin-bottom:18px;margin-left:0;list-style:none}.nav>li>a{display:block}.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>.pull-right{float:right}.nav .nav-header{display:block;padding:3px 15px;font-size:11px;font-weight:bold;line-height:18px;color:#999;text-shadow:0 1px 0 rgba(255,255,255,0.5);text-transform:uppercase}.nav li+.nav-header{margin-top:9px}.nav-list{padding-right:15px;padding-left:15px;margin-bottom:0}.nav-list>li>a,.nav-list .nav-header{margin-right:-15px;margin-left:-15px;text-shadow:0 1px 0 rgba(255,255,255,0.5)}.nav-list>li>a{padding:3px 15px}.nav-list>.active>a,.nav-list>.active>a:hover{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.2);background-color:#08c}.nav-list [class^="icon-"]{margin-right:2px}.nav-list .divider{*width:100%;height:1px;margin:8px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.nav-tabs,.nav-pills{*zoom:1}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;content:""}.nav-tabs:after,.nav-pills:after{clear:both}.nav-tabs>li,.nav-pills>li{float:left}.nav-tabs>li>a,.nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{margin-bottom:-1px}.nav-tabs>li>a{padding-top:8px;padding-bottom:8px;line-height:18px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>.active>a,.nav-tabs>.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.nav-pills>.active>a,.nav-pills>.active>a:hover{color:#fff;background-color:#08c}.nav-stacked>li{float:none}.nav-stacked>li>a{margin-right:0}.nav-tabs.nav-stacked{border-bottom:0}.nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.nav-tabs.nav-stacked>li:first-child>a{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.nav-tabs.nav-stacked>li:last-child>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.nav-tabs.nav-stacked>li>a:hover{z-index:2;border-color:#ddd}.nav-pills.nav-stacked>li>a{margin-bottom:3px}.nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px}.nav-tabs .dropdown-menu{-webkit-border-radius:0 0 5px 5px;-moz-border-radius:0 0 5px 5px;border-radius:0 0 5px 5px}.nav-pills .dropdown-menu{-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.nav-tabs .dropdown-toggle .caret,.nav-pills .dropdown-toggle .caret{margin-top:6px;border-top-color:#08c;border-bottom-color:#08c}.nav-tabs .dropdown-toggle:hover .caret,.nav-pills .dropdown-toggle:hover .caret{border-top-color:#005580;border-bottom-color:#005580}.nav-tabs .active .dropdown-toggle .caret,.nav-pills .active .dropdown-toggle .caret{border-top-color:#333;border-bottom-color:#333}.nav>.dropdown.active>a:hover{color:#000;cursor:pointer}.nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>li.dropdown.open.active>a:hover{color:#fff;background-color:#999;border-color:#999}.nav li.dropdown.open .caret,.nav li.dropdown.open.active .caret,.nav li.dropdown.open a:hover .caret{border-top-color:#fff;border-bottom-color:#fff;opacity:1;filter:alpha(opacity=100)}.tabs-stacked .open>a:hover{border-color:#999}.tabbable{*zoom:1}.tabbable:before,.tabbable:after{display:table;content:""}.tabbable:after{clear:both}.tab-content{overflow:auto}.tabs-below>.nav-tabs,.tabs-right>.nav-tabs,.tabs-left>.nav-tabs{border-bottom:0}.tab-content>.tab-pane,.pill-content>.pill-pane{display:none}.tab-content>.active,.pill-content>.active{display:block}.tabs-below>.nav-tabs{border-top:1px solid #ddd}.tabs-below>.nav-tabs>li{margin-top:-1px;margin-bottom:0}.tabs-below>.nav-tabs>li>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.tabs-below>.nav-tabs>li>a:hover{border-top-color:#ddd;border-bottom-color:transparent}.tabs-below>.nav-tabs>.active>a,.tabs-below>.nav-tabs>.active>a:hover{border-color:transparent #ddd #ddd #ddd}.tabs-left>.nav-tabs>li,.tabs-right>.nav-tabs>li{float:none}.tabs-left>.nav-tabs>li>a,.tabs-right>.nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px}.tabs-left>.nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd}.tabs-left>.nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.tabs-left>.nav-tabs>li>a:hover{border-color:#eee #ddd #eee #eee}.tabs-left>.nav-tabs .active>a,.tabs-left>.nav-tabs .active>a:hover{border-color:#ddd transparent #ddd #ddd;*border-right-color:#fff}.tabs-right>.nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd}.tabs-right>.nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.tabs-right>.nav-tabs>li>a:hover{border-color:#eee #eee #eee #ddd}.tabs-right>.nav-tabs .active>a,.tabs-right>.nav-tabs .active>a:hover{border-color:#ddd #ddd #ddd transparent;*border-left-color:#fff}.navbar{*position:relative;*z-index:2;margin-bottom:18px;overflow:visible}.navbar-inner{min-height:40px;padding-right:20px;padding-left:20px;background-color:#2c2c2c;background-image:-moz-linear-gradient(top,#333,#222);background-image:-ms-linear-gradient(top,#333,#222);background-image:-webkit-gradient(linear,0 0,0 100%,from(#333),to(#222));background-image:-webkit-linear-gradient(top,#333,#222);background-image:-o-linear-gradient(top,#333,#222);background-image:linear-gradient(top,#333,#222);background-repeat:repeat-x;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#333333',endColorstr='#222222',GradientType=0);-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.25),inset 0 -1px 0 rgba(0,0,0,0.1);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.25),inset 0 -1px 0 rgba(0,0,0,0.1);box-shadow:0 1px 3px rgba(0,0,0,0.25),inset 0 -1px 0 rgba(0,0,0,0.1)}.navbar .container{width:auto}.nav-collapse.collapse{height:auto}.navbar{color:#999}.navbar .brand:hover{text-decoration:none}.navbar .brand{display:block;float:left;padding:8px 20px 12px;margin-left:-20px;font-size:20px;font-weight:200;line-height:1;color:#999}.navbar .navbar-text{margin-bottom:0;line-height:40px}.navbar .navbar-link{color:#999}.navbar .navbar-link:hover{color:#fff}.navbar .btn,.navbar .btn-group{margin-top:5px}.navbar .btn-group .btn{margin:0}.navbar-form{margin-bottom:0;*zoom:1}.navbar-form:before,.navbar-form:after{display:table;content:""}.navbar-form:after{clear:both}.navbar-form input,.navbar-form select,.navbar-form .radio,.navbar-form .checkbox{margin-top:5px}.navbar-form input,.navbar-form select{display:inline-block;margin-bottom:0}.navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px}.navbar-form .input-append,.navbar-form .input-prepend{margin-top:6px;white-space:nowrap}.navbar-form .input-append input,.navbar-form .input-prepend input{margin-top:0}.navbar-search{position:relative;float:left;margin-top:6px;margin-bottom:0}.navbar-search .search-query{padding:4px 9px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;color:#fff;background-color:#626262;border:1px solid #151515;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-webkit-transition:none;-moz-transition:none;-ms-transition:none;-o-transition:none;transition:none}.navbar-search .search-query:-moz-placeholder{color:#ccc}.navbar-search .search-query:-ms-input-placeholder{color:#ccc}.navbar-search .search-query::-webkit-input-placeholder{color:#ccc}.navbar-search .search-query:focus,.navbar-search .search-query.focused{padding:5px 10px;color:#333;text-shadow:0 1px 0 #fff;background-color:#fff;border:0;outline:0;-webkit-box-shadow:0 0 3px rgba(0,0,0,0.15);-moz-box-shadow:0 0 3px rgba(0,0,0,0.15);box-shadow:0 0 3px rgba(0,0,0,0.15)}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;margin-bottom:0}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding-right:0;padding-left:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.navbar-fixed-top{top:0}.navbar-fixed-bottom{bottom:0}.navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0}.navbar .nav.pull-right{float:right}.navbar .nav>li{display:block;float:left}.navbar .nav>li>a{float:none;padding:9px 10px 11px;line-height:19px;color:#999;text-decoration:none;text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.navbar .btn{display:inline-block;padding:4px 10px 4px;margin:5px 5px 6px;line-height:18px}.navbar .btn-group{padding:5px 5px 6px;margin:0}.navbar .nav>li>a:hover{color:#fff;text-decoration:none;background-color:transparent}.navbar .nav .active>a,.navbar .nav .active>a:hover{color:#fff;text-decoration:none;background-color:#222}.navbar .divider-vertical{width:1px;height:40px;margin:0 9px;overflow:hidden;background-color:#222;border-right:1px solid #333}.navbar .nav.pull-right{margin-right:0;margin-left:10px}.navbar .btn-navbar{display:none;float:right;padding:7px 10px;margin-right:5px;margin-left:5px;background-color:#2c2c2c;*background-color:#222;background-image:-ms-linear-gradient(top,#333,#222);background-image:-webkit-gradient(linear,0 0,0 100%,from(#333),to(#222));background-image:-webkit-linear-gradient(top,#333,#222);background-image:-o-linear-gradient(top,#333,#222);background-image:linear-gradient(top,#333,#222);background-image:-moz-linear-gradient(top,#333,#222);background-repeat:repeat-x;border-color:#222 #222 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:dximagetransform.microsoft.gradient(startColorstr='#333333',endColorstr='#222222',GradientType=0);filter:progid:dximagetransform.microsoft.gradient(enabled=false);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075)}.navbar .btn-navbar:hover,.navbar .btn-navbar:active,.navbar .btn-navbar.active,.navbar .btn-navbar.disabled,.navbar .btn-navbar[disabled]{background-color:#222;*background-color:#151515}.navbar .btn-navbar:active,.navbar .btn-navbar.active{background-color:#080808 \9}.navbar .btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,0.25);-moz-box-shadow:0 1px 0 rgba(0,0,0,0.25);box-shadow:0 1px 0 rgba(0,0,0,0.25)}.btn-navbar .icon-bar+.icon-bar{margin-top:3px}.navbar .dropdown-menu:before{position:absolute;top:-7px;left:9px;display:inline-block;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-left:7px solid transparent;border-bottom-color:rgba(0,0,0,0.2);content:''}.navbar .dropdown-menu:after{position:absolute;top:-6px;left:10px;display:inline-block;border-right:6px solid transparent;border-bottom:6px solid #fff;border-left:6px solid transparent;content:''}.navbar-fixed-bottom .dropdown-menu:before{top:auto;bottom:-7px;border-top:7px solid #ccc;border-bottom:0;border-top-color:rgba(0,0,0,0.2)}.navbar-fixed-bottom .dropdown-menu:after{top:auto;bottom:-6px;border-top:6px solid #fff;border-bottom:0}.navbar .nav li.dropdown .dropdown-toggle .caret,.navbar .nav li.dropdown.open .caret{border-top-color:#fff;border-bottom-color:#fff}.navbar .nav li.dropdown.active .caret{opacity:1;filter:alpha(opacity=100)}.navbar .nav li.dropdown.open>.dropdown-toggle,.navbar .nav li.dropdown.active>.dropdown-toggle,.navbar .nav li.dropdown.open.active>.dropdown-toggle{background-color:transparent}.navbar .nav li.dropdown.active>.dropdown-toggle:hover{color:#fff}.navbar .pull-right .dropdown-menu,.navbar .dropdown-menu.pull-right{right:0;left:auto}.navbar .pull-right .dropdown-menu:before,.navbar .dropdown-menu.pull-right:before{right:12px;left:auto}.navbar .pull-right .dropdown-menu:after,.navbar .dropdown-menu.pull-right:after{right:13px;left:auto}.breadcrumb{padding:7px 14px;margin:0 0 18px;list-style:none;background-color:#fbfbfb;background-image:-moz-linear-gradient(top,#fff,#f5f5f5);background-image:-ms-linear-gradient(top,#fff,#f5f5f5);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#f5f5f5));background-image:-webkit-linear-gradient(top,#fff,#f5f5f5);background-image:-o-linear-gradient(top,#fff,#f5f5f5);background-image:linear-gradient(top,#fff,#f5f5f5);background-repeat:repeat-x;border:1px solid #ddd;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ffffff',endColorstr='#f5f5f5',GradientType=0);-webkit-box-shadow:inset 0 1px 0 #fff;-moz-box-shadow:inset 0 1px 0 #fff;box-shadow:inset 0 1px 0 #fff}.breadcrumb li{display:inline-block;*display:inline;text-shadow:0 1px 0 #fff;*zoom:1}.breadcrumb .divider{padding:0 5px;color:#999}.breadcrumb .active a{color:#333}.pagination{height:36px;margin:18px 0}.pagination ul{display:inline-block;*display:inline;margin-bottom:0;margin-left:0;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;*zoom:1;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.pagination li{display:inline}.pagination a{float:left;padding:0 14px;line-height:34px;text-decoration:none;border:1px solid #ddd;border-left-width:0}.pagination a:hover,.pagination .active a{background-color:#f5f5f5}.pagination .active a{color:#999;cursor:default}.pagination .disabled span,.pagination .disabled a,.pagination .disabled a:hover{color:#999;cursor:default;background-color:transparent}.pagination li:first-child a{border-left-width:1px;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.pagination li:last-child a{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.pagination-centered{text-align:center}.pagination-right{text-align:right}.pager{margin-bottom:18px;margin-left:0;text-align:center;list-style:none;*zoom:1}.pager:before,.pager:after{display:table;content:""}.pager:after{clear:both}.pager li{display:inline}.pager a{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.pager a:hover{text-decoration:none;background-color:#f5f5f5}.pager .next a{float:right}.pager .previous a{float:left}.pager .disabled a,.pager .disabled a:hover{color:#999;cursor:default;background-color:#fff}.modal-open .dropdown-menu{z-index:2050}.modal-open .dropdown.open{*z-index:2050}.modal-open .popover{z-index:2060}.modal-open .tooltip{z-index:2070}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop,.modal-backdrop.fade.in{opacity:.8;filter:alpha(opacity=80)}.modal{position:fixed;top:50%;left:50%;z-index:1050;width:560px;margin:-250px 0 0 -280px;overflow:auto;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0,0,0,0.3);-moz-box-shadow:0 3px 7px rgba(0,0,0,0.3);box-shadow:0 3px 7px rgba(0,0,0,0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box}.modal.fade{top:-25%;-webkit-transition:opacity .3s linear,top .3s ease-out;-moz-transition:opacity .3s linear,top .3s ease-out;-ms-transition:opacity .3s linear,top .3s ease-out;-o-transition:opacity .3s linear,top .3s ease-out;transition:opacity .3s linear,top .3s ease-out}.modal.fade.in{top:50%}.modal-header{padding:9px 15px;border-bottom:1px solid #eee}.modal-header .close{margin-top:2px}.modal-body{max-height:400px;padding:15px;overflow-y:auto}.modal-form{margin-bottom:0}.modal-footer{padding:14px 15px 15px;margin-bottom:0;text-align:right;background-color:#f5f5f5;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;*zoom:1;-webkit-box-shadow:inset 0 1px 0 #fff;-moz-box-shadow:inset 0 1px 0 #fff;box-shadow:inset 0 1px 0 #fff}.modal-footer:before,.modal-footer:after{display:table;content:""}.modal-footer:after{clear:both}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.tooltip{position:absolute;z-index:1020;display:block;padding:5px;font-size:11px;opacity:0;filter:alpha(opacity=0);visibility:visible}.tooltip.in{opacity:.8;filter:alpha(opacity=80)}.tooltip.top{margin-top:-2px}.tooltip.right{margin-left:2px}.tooltip.bottom{margin-top:2px}.tooltip.left{margin-left:-2px}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top:5px solid #000;border-right:5px solid transparent;border-left:5px solid transparent}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-right:5px solid transparent;border-bottom:5px solid #000;border-left:5px solid transparent}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-right:5px solid #000;border-bottom:5px solid transparent}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0}.popover{position:absolute;top:0;left:0;z-index:1010;display:none;padding:5px}.popover.top{margin-top:-5px}.popover.right{margin-left:5px}.popover.bottom{margin-top:5px}.popover.left{margin-left:-5px}.popover.top .arrow{bottom:0;left:50%;margin-left:-5px;border-top:5px solid #000;border-right:5px solid transparent;border-left:5px solid transparent}.popover.right .arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-right:5px solid #000;border-bottom:5px solid transparent}.popover.bottom .arrow{top:0;left:50%;margin-left:-5px;border-right:5px solid transparent;border-bottom:5px solid #000;border-left:5px solid transparent}.popover.left .arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000}.popover .arrow{position:absolute;width:0;height:0}.popover-inner{width:280px;padding:3px;overflow:hidden;background:#000;background:rgba(0,0,0,0.8);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0,0,0,0.3);-moz-box-shadow:0 3px 7px rgba(0,0,0,0.3);box-shadow:0 3px 7px rgba(0,0,0,0.3)}.popover-title{padding:9px 15px;line-height:1;background-color:#f5f5f5;border-bottom:1px solid #eee;-webkit-border-radius:3px 3px 0 0;-moz-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0}.popover-content{padding:14px;background-color:#fff;-webkit-border-radius:0 0 3px 3px;-moz-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px;-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box}.popover-content p,.popover-content ul,.popover-content ol{margin-bottom:0}.thumbnails{margin-left:-20px;list-style:none;*zoom:1}.thumbnails:before,.thumbnails:after{display:table;content:""}.thumbnails:after{clear:both}.row-fluid .thumbnails{margin-left:0}.thumbnails>li{float:left;margin-bottom:18px;margin-left:20px}.thumbnail{display:block;padding:4px;line-height:1;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:0 1px 1px rgba(0,0,0,0.075);box-shadow:0 1px 1px rgba(0,0,0,0.075)}a.thumbnail:hover{border-color:#08c;-webkit-box-shadow:0 1px 4px rgba(0,105,214,0.25);-moz-box-shadow:0 1px 4px rgba(0,105,214,0.25);box-shadow:0 1px 4px rgba(0,105,214,0.25)}.thumbnail>img{display:block;max-width:100%;margin-right:auto;margin-left:auto}.thumbnail .caption{padding:9px}.label,.badge{font-size:10.998px;font-weight:bold;line-height:14px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);white-space:nowrap;vertical-align:baseline;background-color:#999}.label{padding:1px 4px 2px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.badge{padding:1px 9px 2px;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px}a.label:hover,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.label-important,.badge-important{background-color:#b94a48}.label-important[href],.badge-important[href]{background-color:#953b39}.label-warning,.badge-warning{background-color:#f89406}.label-warning[href],.badge-warning[href]{background-color:#c67605}.label-success,.badge-success{background-color:#468847}.label-success[href],.badge-success[href]{background-color:#356635}.label-info,.badge-info{background-color:#3a87ad}.label-info[href],.badge-info[href]{background-color:#2d6987}.label-inverse,.badge-inverse{background-color:#333}.label-inverse[href],.badge-inverse[href]{background-color:#1a1a1a}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-moz-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-ms-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:0 0}to{background-position:40px 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:18px;margin-bottom:18px;overflow:hidden;background-color:#f7f7f7;background-image:-moz-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-ms-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f5f5f5),to(#f9f9f9));background-image:-webkit-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-o-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:linear-gradient(top,#f5f5f5,#f9f9f9);background-repeat:repeat-x;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#f5f5f5',endColorstr='#f9f9f9',GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress .bar{width:0;height:18px;font-size:12px;color:#fff;text-align:center;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e90d2;background-image:-moz-linear-gradient(top,#149bdf,#0480be);background-image:-webkit-gradient(linear,0 0,0 100%,from(#149bdf),to(#0480be));background-image:-webkit-linear-gradient(top,#149bdf,#0480be);background-image:-o-linear-gradient(top,#149bdf,#0480be);background-image:linear-gradient(top,#149bdf,#0480be);background-image:-ms-linear-gradient(top,#149bdf,#0480be);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#149bdf',endColorstr='#0480be',GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;-webkit-transition:width .6s ease;-moz-transition:width .6s ease;-ms-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-striped .bar{background-color:#149bdf;background-image:-o-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-webkit-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-ms-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;-moz-background-size:40px 40px;-o-background-size:40px 40px;background-size:40px 40px}.progress.active .bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;-ms-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-danger .bar{background-color:#dd514c;background-image:-moz-linear-gradient(top,#ee5f5b,#c43c35);background-image:-ms-linear-gradient(top,#ee5f5b,#c43c35);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#c43c35));background-image:-webkit-linear-gradient(top,#ee5f5b,#c43c35);background-image:-o-linear-gradient(top,#ee5f5b,#c43c35);background-image:linear-gradient(top,#ee5f5b,#c43c35);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#ee5f5b',endColorstr='#c43c35',GradientType=0)}.progress-danger.progress-striped .bar{background-color:#ee5f5b;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-ms-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-success .bar{background-color:#5eb95e;background-image:-moz-linear-gradient(top,#62c462,#57a957);background-image:-ms-linear-gradient(top,#62c462,#57a957);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#57a957));background-image:-webkit-linear-gradient(top,#62c462,#57a957);background-image:-o-linear-gradient(top,#62c462,#57a957);background-image:linear-gradient(top,#62c462,#57a957);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#62c462',endColorstr='#57a957',GradientType=0)}.progress-success.progress-striped .bar{background-color:#62c462;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-ms-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-info .bar{background-color:#4bb1cf;background-image:-moz-linear-gradient(top,#5bc0de,#339bb9);background-image:-ms-linear-gradient(top,#5bc0de,#339bb9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#339bb9));background-image:-webkit-linear-gradient(top,#5bc0de,#339bb9);background-image:-o-linear-gradient(top,#5bc0de,#339bb9);background-image:linear-gradient(top,#5bc0de,#339bb9);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#5bc0de',endColorstr='#339bb9',GradientType=0)}.progress-info.progress-striped .bar{background-color:#5bc0de;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-ms-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-warning .bar{background-color:#faa732;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-ms-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(top,#fbb450,#f89406);background-repeat:repeat-x;filter:progid:dximagetransform.microsoft.gradient(startColorstr='#fbb450',endColorstr='#f89406',GradientType=0)}.progress-warning.progress-striped .bar{background-color:#fbb450;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-ms-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.accordion{margin-bottom:18px}.accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.accordion-heading{border-bottom:0}.accordion-heading .accordion-toggle{display:block;padding:8px 15px}.accordion-toggle{cursor:pointer}.accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5}.carousel{position:relative;margin-bottom:18px;line-height:1}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel .item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-moz-transition:.6s ease-in-out left;-ms-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel .item>img{display:block;line-height:1}.carousel .active,.carousel .next,.carousel .prev{display:block}.carousel .active{left:0}.carousel .next,.carousel .prev{position:absolute;top:0;width:100%}.carousel .next{left:100%}.carousel .prev{left:-100%}.carousel .next.left,.carousel .prev.right{left:0}.carousel .active.left{left:-100%}.carousel .active.right{left:100%}.carousel-control{position:absolute;top:40%;left:15px;width:40px;height:40px;margin-top:-20px;font-size:60px;font-weight:100;line-height:30px;color:#fff;text-align:center;background:#222;border:3px solid #fff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:.5;filter:alpha(opacity=50)}.carousel-control.right{right:15px;left:auto}.carousel-control:hover{color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}.carousel-caption{position:absolute;right:0;bottom:0;left:0;padding:10px 15px 5px;background:#333;background:rgba(0,0,0,0.75)}.carousel-caption h4,.carousel-caption p{color:#fff}.hero-unit{padding:60px;margin-bottom:30px;background-color:#eee;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;letter-spacing:-1px;color:inherit}.hero-unit p{font-size:18px;font-weight:200;line-height:27px;color:inherit}.pull-right{float:right}.pull-left{float:left}.hide{display:none}.show{display:block}.invisible{visibility:hidden} diff --git a/doc/darkstrap.css b/doc/darkstrap.css new file mode 100644 index 000000000..2f0e3ec4f --- /dev/null +++ b/doc/darkstrap.css @@ -0,0 +1,430 @@ +/* line 31, ../sass/darkstrap.scss */ +.well, .breadcrumb, input, +input[type="file"], +select, +textarea, .progress, code, pre { + -moz-box-shadow: rgba(255, 255, 255, 0.1) 0 1px 0, rgba(0, 0, 0, 0.8) 0 1px 7px 0px inset; + -webkit-box-shadow: rgba(255, 255, 255, 0.1) 0 1px 0, rgba(0, 0, 0, 0.8) 0 1px 7px 0px inset; + -o-box-shadow: rgba(255, 255, 255, 0.1) 0 1px 0, rgba(0, 0, 0, 0.8) 0 1px 7px 0px inset; + box-shadow: rgba(255, 255, 255, 0.1) 0 1px 0, rgba(0, 0, 0, 0.8) 0 1px 7px 0px inset; + background: #202020; + background-color: rgba(0, 0, 0, 0.3); +} + +/* line 33, ../sass/darkstrap.scss */ +body { + background: #2f2f2f; + color: #c6c6c6; +} + +/* line 39, ../sass/darkstrap.scss */ +.breadcrumb { + border: 0; +} +/* line 42, ../sass/darkstrap.scss */ +.breadcrumb li { + text-shadow: 0 1px 0 #000; +} + +/* line 48, ../sass/darkstrap.scss */ +.page-header { + -moz-box-shadow: rgba(255, 255, 255, 0.07) 0 1px 0; + -webkit-box-shadow: rgba(255, 255, 255, 0.07) 0 1px 0; + -o-box-shadow: rgba(255, 255, 255, 0.07) 0 1px 0; + box-shadow: rgba(255, 255, 255, 0.07) 0 1px 0; + border-bottom: 1px solid #121212; +} + +/* line 49, ../sass/darkstrap.scss */ +h1, h2, h3, h4, h5, h6 { + color: white; +} + +/* line 50, ../sass/darkstrap.scss */ +h6 { + color: #999; +} + +/* line 57, ../sass/darkstrap.scss */ +.nav-tabs .open .dropdown-toggle, +.nav-pills .open .dropdown-toggle, +.nav > .open.active > a:hover { + background-color: #222222; + border-color: #2f2f2f #2f2f2f transparent #2f2f2f; +} + +/* line 61, ../sass/darkstrap.scss */ +.nav > .dropdown.active > a:hover { + color: #fff; +} + +/* line 65, ../sass/darkstrap.scss */ +.nav-tabs .active .dropdown-toggle .caret, +.nav-pills .active .dropdown-toggle .caret { + border-top-color: #fff; +} + +/* line 68, ../sass/darkstrap.scss */ +.nav-tabs { + border-bottom: 1px solid #666; +} +/* line 71, ../sass/darkstrap.scss */ +.nav-tabs > .active > a, .nav-tabs > .active > a:hover { + background-color: #2f2f2f; + color: #fff; + border-color: #666 #666 transparent #666; +} +/* line 76, ../sass/darkstrap.scss */ +.nav-tabs > li > a:hover { + border-color: #2f2f2f #2f2f2f #666666 #2f2f2f; + background-color: #222222; + color: #00aaff; +} +/* line 83, ../sass/darkstrap.scss */ +.nav-tabs.nav-stacked > li > a, .nav-tabs.nav-stacked > li > a:hover { + border-color: #666; +} + +/* line 89, ../sass/darkstrap.scss */ +.nav-pills > li > a:hover { + background-color: #222222; + color: #00aaff; +} + +/* line 95, ../sass/darkstrap.scss */ +.nav-list > li > a, +.nav-list .nav-header { + text-shadow: 0 1px 0 #000; +} + +/* line 98, ../sass/darkstrap.scss */ +.nav-list > li > a:hover { + background-color: #161616; + color: #00aaff; +} + +/* line 103, ../sass/darkstrap.scss */ +.nav-list .active > a:hover { + background-color: #0088cc; + color: white; +} + +/* line 109, ../sass/darkstrap.scss */ +.tabs-below .nav-tabs { + border-top: 1px solid #666; +} + +/* line 110, ../sass/darkstrap.scss */ +.tabs-left .nav-tabs { + border-right: 1px solid #666; +} + +/* line 111, ../sass/darkstrap.scss */ +.tabs-right .nav-tabs { + border-left: 1px solid #666; +} + +/* line 113, ../sass/darkstrap.scss */ +.tabs-below .nav-tabs > li > a:hover { + border-top: 1px solid #666; +} + +/* line 116, ../sass/darkstrap.scss */ +.tabs-left .nav-tabs > li > a:hover { + border-color: transparent #666 transparent transparent; +} + +/* line 119, ../sass/darkstrap.scss */ +.tabs-right .nav-tabs > li > a:hover { + border-color: transparent transparent transparent #666; +} + +/* line 124, ../sass/darkstrap.scss */ +.tabs-below .nav-tabs .active > a, +.tabs-below .nav-tabs .active > a:hover { + border-color: transparent #666 #666 #666; +} + +/* line 128, ../sass/darkstrap.scss */ +.tabs-left .nav-tabs .active > a, +.tabs-left .nav-tabs .active > a:hover { + border-color: #666 transparent #666 #666; +} + +/* line 132, ../sass/darkstrap.scss */ +.tabs-right .nav-tabs .active > a, +.tabs-right .nav-tabs .active > a:hover { + border-color: #666 #666 #666 transparent; +} + +/* line 2, ../sass/mixins.scss */ +:-moz-placeholder { + color: #666666; +} + +/* line 5, ../sass/mixins.scss */ +::-webkit-input-placeholder { + color: #666666; +} + +/* line 140, ../sass/darkstrap.scss */ +.input-prepend .add-on, +.input-append .add-on { + background: #444; + color: #c6c6c6; + border-color: #111; + text-shadow: 0 1px 0 black; +} + +/* line 147, ../sass/darkstrap.scss */ +label { + color: #c6c6c6; +} + +/* line 154, ../sass/darkstrap.scss */ +input, +input[type="file"], +select, +textarea { + color: white; + border-color: #111111; +} + +/* line 160, ../sass/darkstrap.scss */ +.search-query { + -webkit-box-shadow: rgba(255, 255, 255, 0.1) 0 1px 0, rgba(0, 0, 0, 0) 0 1px 7px 0px inset; +} + +/* line 163, ../sass/darkstrap.scss */ +legend { + color: white; + -moz-box-shadow: rgba(255, 255, 255, 0.07) 0 1px 0; + -webkit-box-shadow: rgba(255, 255, 255, 0.07) 0 1px 0; + -o-box-shadow: rgba(255, 255, 255, 0.07) 0 1px 0; + box-shadow: rgba(255, 255, 255, 0.07) 0 1px 0; + border-bottom: 1px solid #121212; +} + +/* line 167, ../sass/darkstrap.scss */ +.form-actions { + border-top-color: #222; + background-color: #444; +} + +/* line 172, ../sass/darkstrap.scss */ +.help-inline { + color: #999; +} + +/* line 12, ../sass/mixins.scss */ +.control-group.warning > label, +.control-group.warning .help-block, +.control-group.warning .help-inline { + color: #faa732; +} +/* line 13, ../sass/mixins.scss */ +.control-group.warning input, .control-group.warning select, .control-group.warning textarea { + color: #faa732; + border-color: #faa732; +} +/* line 20, ../sass/mixins.scss */ +.control-group.warning input:focus, +.control-group.warning select:focus, +.control-group.warning textarea:focus { + border-color: #faa732; + -moz-box-shadow: 0 0 6px #faa732; + -webkit-box-shadow: 0 0 6px #faa732; + -o-box-shadow: 0 0 6px #faa732; + box-shadow: 0 0 6px #faa732; +} +/* line 12, ../sass/mixins.scss */ +.control-group.success > label, .control-group.success .help-block, .control-group.success .help-inline { + color: #5bb75b; +} +/* line 13, ../sass/mixins.scss */ +.control-group.success input, .control-group.success select, .control-group.success textarea { + color: #5bb75b; + border-color: #5bb75b; +} +/* line 20, ../sass/mixins.scss */ +.control-group.success input:focus, .control-group.success select:focus, .control-group.success textarea:focus { + border-color: #5bb75b; + -moz-box-shadow: 0 0 6px #5bb75b; + -webkit-box-shadow: 0 0 6px #5bb75b; + -o-box-shadow: 0 0 6px #5bb75b; + box-shadow: 0 0 6px #5bb75b; +} +/* line 12, ../sass/mixins.scss */ +.control-group.error > label, .control-group.error .help-block, .control-group.error .help-inline { + color: #fc5b5e; +} +/* line 13, ../sass/mixins.scss */ +.control-group.error input, .control-group.error select, .control-group.error textarea { + color: #fc5b5e; + border-color: #fc5b5e; +} +/* line 20, ../sass/mixins.scss */ +.control-group.error input:focus, .control-group.error select:focus, .control-group.error textarea:focus { + border-color: #fc5b5e; + -moz-box-shadow: 0 0 6px #fc5b5e; + -webkit-box-shadow: 0 0 6px #fc5b5e; + -o-box-shadow: 0 0 6px #fc5b5e; + box-shadow: 0 0 6px #fc5b5e; +} + +/* line 182, ../sass/darkstrap.scss */ +.table thead { + color: white; +} +/* line 185, ../sass/darkstrap.scss */ +.table td { + border-top-color: #666; +} + +/* line 191, ../sass/darkstrap.scss */ +.table-striped tbody tr:nth-child(2n+1) td, .table-striped tbody tr:nth-child(2n+1) th { + background-color: #444; +} + +/* line 197, ../sass/darkstrap.scss */ +.table-bordered { + border: 1px solid #666; +} +/* line 202, ../sass/darkstrap.scss */ +.table-bordered th + th, +.table-bordered td + td, +.table-bordered th + td, +.table-bordered td + th { + border-left: 1px solid #666; +} + +/* line 208, ../sass/darkstrap.scss */ +.pagination a:hover { + color: #00aaff; + background-color: #222222; +} + +/* line 212, ../sass/darkstrap.scss */ +.pagination .active a { + color: #fff; + background-color: #222222; +} + +/* line 216, ../sass/darkstrap.scss */ +.pagination a { + border-color: #666; +} + +/* line 219, ../sass/darkstrap.scss */ +.pager a { + background-color: #2f2f2f; + border-color: #666; +} +/* line 222, ../sass/darkstrap.scss */ +.pager a:hover { + background-color: #222222; + color: #00aaff; +} + +/* line 238, ../sass/darkstrap.scss */ +.alert { + color: #fff; + background-color: #faa732; + text-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); + border-color: #a86404; +} +/* line 234, ../sass/darkstrap.scss */ +.alert h4 { + color: #c17305; +} + +/* line 239, ../sass/darkstrap.scss */ +.alert-success { + color: #fff; + background-color: #5bb75b; + text-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); + border-color: #2d662d; +} +/* line 234, ../sass/darkstrap.scss */ +.alert-success h4 { + color: #347834; +} + +/* line 240, ../sass/darkstrap.scss */ +.alert-error { + color: #fff; + background-color: #fc5b5e; + text-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); + border-color: #d40408; +} +/* line 234, ../sass/darkstrap.scss */ +.alert-error h4 { + color: #ed0409; +} + +/* line 241, ../sass/darkstrap.scss */ +.alert-info { + color: #fff; + background-color: #3a87ad; + text-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); + border-color: #1a3c4e; +} +/* line 234, ../sass/darkstrap.scss */ +.alert-info h4 { + color: #204b61; +} + +/* line 245, ../sass/darkstrap.scss */ +.modal { + background-color: #444; +} + +/* line 248, ../sass/darkstrap.scss */ +.modal-header { + border-bottom: 1px solid #222; +} + +/* line 251, ../sass/darkstrap.scss */ +.modal-body p { + color: #c6c6c6; +} + +/* line 254, ../sass/darkstrap.scss */ +.modal-footer { + background-color: #373737; + border-top: 1px solid #222; + -moz-box-shadow: 0 1px 0 #333333 inset; + -webkit-box-shadow: 0 1px 0 #333333 inset; + -o-box-shadow: 0 1px 0 #333333 inset; + box-shadow: 0 1px 0 #333333 inset; +} + +/* line 266, ../sass/darkstrap.scss */ +blockquote { + border-left-color: #111; +} +/* line 268, ../sass/darkstrap.scss */ +blockquote.pull-right { + border-right-color: #111; +} + +/* line 271, ../sass/darkstrap.scss */ +hr { + -moz-box-shadow: rgba(255, 255, 255, 0.07) 0 1px 0; + -webkit-box-shadow: rgba(255, 255, 255, 0.07) 0 1px 0; + -o-box-shadow: rgba(255, 255, 255, 0.07) 0 1px 0; + box-shadow: rgba(255, 255, 255, 0.07) 0 1px 0; + border-bottom: 1px solid #121212; + border-top: none; +} + +/* line 277, ../sass/darkstrap.scss */ +code { + border: none; +} + +/* line 281, ../sass/darkstrap.scss */ +pre { + border: none; + color: #c6c6c6; +} \ No newline at end of file diff --git a/doc/header.html b/doc/header.html new file mode 100644 index 000000000..8f5e4d6c7 --- /dev/null +++ b/doc/header.html @@ -0,0 +1,129 @@ + + + + + +$projectname: $title +$title + + + + + + + + + +$treeview +$search +$mathjax + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + +
+
$projectname +  $projectnumber +
+
$projectbrief
+
+
$projectbrief
+
$searchbox
+
+ diff --git a/doc/style.css b/doc/style.css new file mode 100644 index 000000000..928f6a6d6 --- /dev/null +++ b/doc/style.css @@ -0,0 +1,1471 @@ +/* The standard CSS for doxygen */ + +body, table, div, p, dl { + font-family: Lucida Grande, Verdana, Geneva, Arial, sans-serif; + font-size: 13px; + line-height: 1.3; +} + +/* @group Heading Levels */ + +h1 { + font-size: 150%; +} + +.title { + font-size: 150%; + font-weight: bold; + margin: 10px 2px; +} + +h2 { + font-size: 120%; +} + +h3 { + font-size: 100%; +} + +dt { + font-weight: bold; +} + +div.multicol { + -moz-column-gap: 1em; + -webkit-column-gap: 1em; + -moz-column-count: 3; + -webkit-column-count: 3; +} + +p.startli, p.startdd, p.starttd { + margin-top: 2px; +} + +p.endli { + margin-bottom: 0px; +} + +p.enddd { + margin-bottom: 4px; +} + +p.endtd { + margin-bottom: 2px; +} + +/* @end */ + +caption { + font-weight: bold; +} + +span.legend { + font-size: 70%; + text-align: center; +} + +h3.version { + font-size: 90%; + text-align: center; +} + +div.qindex, div.navtab{ + background-color: #F1F1F1; + border: 1px solid #BDBDBD; + text-align: center; +} + +div.qindex, div.navpath { + width: 100%; + line-height: 140%; +} + +div.navtab { + margin-right: 15px; +} + +/* @group Link Styling */ + +a { + color: #646464; + font-weight: normal; + text-decoration: none; +} + +.contents a:visited { + color: #747474; +} + +a:hover { + text-decoration: underline; +} + +a.qindex { + font-weight: bold; +} + +a.qindexHL { + font-weight: bold; + background-color: #B8B8B8; + color: #ffffff; + border: 1px double #A8A8A8; +} + +.contents a.qindexHL:visited { + color: #ffffff; +} + +a.el { + font-weight: bold; +} + +a.elRef { +} + +a.code, a.code:visited { + color: #4665A2; +} + +a.codeRef, a.codeRef:visited { + color: #4665A2; +} + +/* @end */ + +dl.el { + margin-left: -1cm; +} + +.fragment { + font-family: monospace, fixed; + font-size: 105%; +} + +pre.fragment { + border: 1px solid #D5D5D5; + background-color: #FCFCFC; + padding: 4px 6px; + margin: 4px 8px 4px 2px; + overflow: auto; + word-wrap: break-word; + font-size: 9pt; + line-height: 125%; +} + +div.ah { + background-color: black; + font-weight: bold; + color: #ffffff; + margin-bottom: 3px; + margin-top: 3px; + padding: 0.2em; + border: solid thin #333; + border-radius: 0.5em; + -webkit-border-radius: .5em; + -moz-border-radius: .5em; + box-shadow: 2px 2px 3px #999; + -webkit-box-shadow: 2px 2px 3px #999; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; + background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#000),color-stop(0.3, #444)); + background-image: -moz-linear-gradient(center top, #eee 0%, #444 40%, #000); +} + +div.groupHeader { + margin-left: 16px; + margin-top: 12px; + font-weight: bold; +} + +div.groupText { + margin-left: 16px; + font-style: italic; +} + +body { + background-color: white; + color: black; + margin: 0; +} + +div.contents { + margin-top: 10px; + margin-left: 8px; + margin-right: 8px; +} + +td.indexkey { + background-color: #F1F1F1; + font-weight: bold; + border: 1px solid #D5D5D5; + margin: 2px 0px 2px 0; + padding: 2px 10px; + white-space: nowrap; + vertical-align: top; +} + +td.indexvalue { + background-color: #F1F1F1; + border: 1px solid #D5D5D5; + padding: 2px 10px; + margin: 2px 0px; +} + +tr.memlist { + background-color: #F2F2F2; +} + +p.formulaDsp { + text-align: center; +} + +img.formulaDsp { + +} + +img.formulaInl { + vertical-align: middle; +} + +div.center { + text-align: center; + margin-top: 0px; + margin-bottom: 0px; + padding: 0px; +} + +div.center img { + border: 0px; +} + +address.footer { + text-align: right; + padding-right: 12px; +} + +img.footer { + border: 0px; + vertical-align: middle; +} + +/* @group Code Colorization */ + +span.keyword { + color: #008000 +} + +span.keywordtype { + color: #604020 +} + +span.keywordflow { + color: #e08000 +} + +span.comment { + color: #800000 +} + +span.preprocessor { + color: #806020 +} + +span.stringliteral { + color: #002080 +} + +span.charliteral { + color: #008080 +} + +span.vhdldigit { + color: #ff00ff +} + +span.vhdlchar { + color: #000000 +} + +span.vhdlkeyword { + color: #700070 +} + +span.vhdllogic { + color: #ff0000 +} + +/* @end */ + +/* +.search { + color: #003399; + font-weight: bold; +} + +form.search { + margin-bottom: 0px; + margin-top: 0px; +} + +input.search { + font-size: 75%; + color: #000080; + font-weight: normal; + background-color: #e8eef2; +} +*/ + +td.tiny { + font-size: 75%; +} + +.dirtab { + padding: 4px; + border-collapse: collapse; + border: 1px solid #BDBDBD; +} + +th.dirtab { + background: #F1F1F1; + font-weight: bold; +} + +hr { + height: 0px; + border: none; + border-top: 1px solid #7A7A7A; +} + +hr.footer { + height: 1px; +} + +/* @group Member Descriptions */ + +table.memberdecls { + border-spacing: 0px; + padding: 0px; +} + +.mdescLeft, .mdescRight, +.memItemLeft, .memItemRight, +.memTemplItemLeft, .memTemplItemRight, .memTemplParams { + background-color: #FAFAFA; + border: none; + margin: 4px; + padding: 1px 0 0 8px; +} + +.mdescLeft, .mdescRight { + padding: 0px 8px 4px 8px; + color: #555; +} + +.memItemLeft, .memItemRight, .memTemplParams { + border-top: 1px solid #D5D5D5; +} + +.memItemLeft, .memTemplItemLeft { + white-space: nowrap; +} + +.memItemRight { + width: 100%; +} + +.memTemplParams { + color: #747474; + white-space: nowrap; +} + +/* @end */ + +/* @group Member Details */ + +/* Styles for detailed member documentation */ + +.memtemplate { + font-size: 80%; + color: #747474; + font-weight: normal; + margin-left: 9px; +} + +.memnav { + background-color: #F1F1F1; + border: 1px solid #BDBDBD; + text-align: center; + margin: 2px; + margin-right: 15px; + padding: 2px; +} + +.mempage { + width: 100%; +} + +.memitem { + padding: 0; + margin-bottom: 10px; + margin-right: 5px; +} + +.memname { + white-space: nowrap; + font-weight: bold; + margin-left: 6px; +} + +.memproto, dl.reflist dt { + border-top: 1px solid #C0C0C0; + border-left: 1px solid #C0C0C0; + border-right: 1px solid #C0C0C0; + padding: 6px 0px 6px 0px; + color: #3D3D3D; + font-weight: bold; + text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); + /* opera specific markup */ + box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + border-top-right-radius: 8px; + border-top-left-radius: 8px; + /* firefox specific markup */ + -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; + -moz-border-radius-topright: 8px; + -moz-border-radius-topleft: 8px; + /* webkit specific markup */ + -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + -webkit-border-top-right-radius: 8px; + -webkit-border-top-left-radius: 8px; + background-image:url('nav_f.png'); + background-repeat:repeat-x; + background-color: #EAEAEA; + +} + +.memdoc, dl.reflist dd { + border-bottom: 1px solid #C0C0C0; + border-left: 1px solid #C0C0C0; + border-right: 1px solid #C0C0C0; + padding: 2px 5px; + background-color: #FCFCFC; + border-top-width: 0; + /* opera specific markup */ + border-bottom-left-radius: 8px; + border-bottom-right-radius: 8px; + box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + /* firefox specific markup */ + -moz-border-radius-bottomleft: 8px; + -moz-border-radius-bottomright: 8px; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; + background-image: -moz-linear-gradient(center top, #FFFFFF 0%, #FFFFFF 60%, #F9F9F9 95%, #F2F2F2); + /* webkit specific markup */ + -webkit-border-bottom-left-radius: 8px; + -webkit-border-bottom-right-radius: 8px; + -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + background-image: -webkit-gradient(linear,center top,center bottom,from(#FFFFFF), color-stop(0.6,#FFFFFF), color-stop(0.60,#FFFFFF), color-stop(0.95,#F9F9F9), to(#F2F2F2)); +} + +dl.reflist dt { + padding: 5px; +} + +dl.reflist dd { + margin: 0px 0px 10px 0px; + padding: 5px; +} + +.paramkey { + text-align: right; +} + +.paramtype { + white-space: nowrap; +} + +.paramname { + color: #602020; + white-space: nowrap; +} +.paramname em { + font-style: normal; +} + +.params, .retval, .exception, .tparams { + border-spacing: 6px 2px; +} + +.params .paramname, .retval .paramname { + font-weight: bold; + vertical-align: top; +} + +.params .paramtype { + font-style: italic; + vertical-align: top; +} + +.params .paramdir { + font-family: "courier new",courier,monospace; + vertical-align: top; +} + + + + +/* @end */ + +/* @group Directory (tree) */ + +/* for the tree view */ + +.ftvtree { + font-family: sans-serif; + margin: 0px; +} + +/* these are for tree view when used as main index */ + +.directory { + font-size: 9pt; + font-weight: bold; + margin: 5px; +} + +.directory h3 { + margin: 0px; + margin-top: 1em; + font-size: 11pt; +} + +/* +The following two styles can be used to replace the root node title +with an image of your choice. Simply uncomment the next two styles, +specify the name of your image and be sure to set 'height' to the +proper pixel height of your image. +*/ + +/* +.directory h3.swap { + height: 61px; + background-repeat: no-repeat; + background-image: url("yourimage.gif"); +} +.directory h3.swap span { + display: none; +} +*/ + +.directory > h3 { + margin-top: 0; +} + +.directory p { + margin: 0px; + white-space: nowrap; +} + +.directory div { + display: none; + margin: 0px; +} + +.directory img { + vertical-align: -30%; +} + +/* these are for tree view when not used as main index */ + +.directory-alt { + font-size: 100%; + font-weight: bold; +} + +.directory-alt h3 { + margin: 0px; + margin-top: 1em; + font-size: 11pt; +} + +.directory-alt > h3 { + margin-top: 0; +} + +.directory-alt p { + margin: 0px; + white-space: nowrap; +} + +.directory-alt div { + display: none; + margin: 0px; +} + +.directory-alt img { + vertical-align: -30%; +} + +/* @end */ + +div.dynheader { + margin-top: 8px; +} + +address { + font-style: normal; + color: #464646; +} + +table.doxtable { + border-collapse:collapse; +} + +table.doxtable td, table.doxtable th { + border: 1px solid #4A4A4A; + padding: 3px 7px 2px; +} + +table.doxtable th { + background-color: #5B5B5B; + color: #FFFFFF; + font-size: 110%; + padding-bottom: 4px; + padding-top: 5px; + text-align:left; +} + +table.fieldtable { + width: 100%; + margin-bottom: 10px; + border: 1px solid #C0C0C0; + border-spacing: 0px; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; + -webkit-box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); + box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); +} + +.fieldtable td, .fieldtable th { + padding: 3px 7px 2px; +} + +.fieldtable td.fieldtype, .fieldtable td.fieldname { + white-space: nowrap; + border-right: 1px solid #C0C0C0; + border-bottom: 1px solid #C0C0C0; + vertical-align: top; +} + +.fieldtable td.fielddoc { + border-bottom: 1px solid #C0C0C0; + width: 100%; +} + +.fieldtable tr:last-child td { + border-bottom: none; +} + +.fieldtable th { + background-image:url('nav_f.png'); + background-repeat:repeat-x; + background-color: #EAEAEA; + font-size: 90%; + color: #3D3D3D; + padding-bottom: 4px; + padding-top: 5px; + text-align:left; + -moz-border-radius-topleft: 4px; + -moz-border-radius-topright: 4px; + -webkit-border-top-left-radius: 4px; + -webkit-border-top-right-radius: 4px; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom: 1px solid #C0C0C0; +} + + +.tabsearch { + top: 0px; + left: 10px; + height: 36px; + background-image: url('tab_b.png'); + z-index: 101; + overflow: hidden; + font-size: 13px; +} + +.navpath ul +{ + font-size: 11px; + background-image:url('tab_b.png'); + background-repeat:repeat-x; + height:30px; + line-height:30px; + color:#ABABAB; + border:solid 1px #D3D3D3; + overflow:hidden; + margin:0px; + padding:0px; +} + +.navpath li +{ + list-style-type:none; + float:left; + padding-left:10px; + padding-right:15px; + background-image:url('bc_s.png'); + background-repeat:no-repeat; + background-position:right; + color:#595959; +} + +.navpath li.navelem a +{ + height:32px; + display:block; + text-decoration: none; + outline: none; +} + +.navpath li.navelem a:hover +{ + color:#929292; +} + +.navpath li.footer +{ + list-style-type:none; + float:right; + padding-left:10px; + padding-right:15px; + background-image:none; + background-repeat:no-repeat; + background-position:right; + color:#595959; + font-size: 8pt; +} + + +div.summary +{ + float: right; + font-size: 8pt; + padding-right: 5px; + width: 50%; + text-align: right; +} + +div.summary a +{ + white-space: nowrap; +} + +div.ingroups +{ + margin-left: 5px; + font-size: 8pt; + padding-left: 5px; + width: 50%; + text-align: left; +} + +div.ingroups a +{ + white-space: nowrap; +} + +div.header +{ + background-image:url('nav_h.png'); + background-repeat:repeat-x; + background-color: #FAFAFA; + margin: 0px; + border-bottom: 1px solid #D5D5D5; +} + +div.headertitle +{ + padding: 5px 5px 5px 7px; +} + +dl +{ + padding: 0 0 0 10px; +} + +dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug +{ + border-left:4px solid; + padding: 0 0 0 6px; +} + +dl.note +{ + border-color: #D0C000; +} + +dl.warning, dl.attention +{ + border-color: #FF0000; +} + +dl.pre, dl.post, dl.invariant +{ + border-color: #00D000; +} + +dl.deprecated +{ + border-color: #505050; +} + +dl.todo +{ + border-color: #00C0E0; +} + +dl.test +{ + border-color: #3030E0; +} + +dl.bug +{ + border-color: #C08050; +} + +#projectlogo +{ + text-align: center; + vertical-align: bottom; + border-collapse: separate; +} + +#projectlogo img +{ + border: 0px none; +} + +#projectname +{ + font: 300% Tahoma, Arial,sans-serif; + margin: 0px; + padding: 2px 0px; +} + +#projectbrief +{ + font: 120% Tahoma, Arial,sans-serif; + margin: 0px; + padding: 0px; +} + +#projectnumber +{ + font: 50% Tahoma, Arial,sans-serif; + margin: 0px; + padding: 0px; +} + +#titlearea +{ + padding: 0px; + margin: 0px; + width: 100%; + border-bottom: 1px solid #848484; +} + +.image +{ + text-align: center; +} + +.dotgraph +{ + text-align: center; +} + +.mscgraph +{ + text-align: center; +} + +.caption +{ + font-weight: bold; +} + +div.zoom +{ + border: 1px solid #AFAFAF; +} + +dl.citelist { + margin-bottom:50px; +} + +dl.citelist dt { + color:#545454; + float:left; + font-weight:bold; + margin-right:10px; + padding:5px; +} + +dl.citelist dd { + margin:2px 0; + padding:5px 0; +} + +@media print +{ + #top { display: none; } + #side-nav { display: none; } + #nav-path { display: none; } + body { overflow:visible; } + h1, h2, h3, h4, h5, h6 { page-break-after: avoid; } + .summary { display: none; } + .memitem { page-break-inside: avoid; } + #doc-content + { + margin-left:0 !important; + height:auto !important; + width:auto !important; + overflow:inherit; + display:inline; + } + pre.fragment + { + overflow: visible; + text-wrap: unrestricted; + white-space: -moz-pre-wrap; /* Moz */ + white-space: -pre-wrap; /* Opera 4-6 */ + white-space: -o-pre-wrap; /* Opera 7 */ + white-space: pre-wrap; /* CSS3 */ + word-wrap: break-word; /* IE 5.5+ */ + } +} + + +/* +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +custom css + +*/ + + +.mdescLeft, .mdescRight, .memItemLeft, .memItemRight, .memTemplItemLeft, .memTemplItemRight, .memTemplParams { + background: none !important; +} + +.mdescLeft, .mdescRight { + border-top: none !important; + color: #858585; +} + + +.table tbody tr:hover td, .table tbody tr:hover th { + background-color: rgba(255, 255, 255, 0.1); +} + +/* +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +guacamole css + +*/ + + +/* Partials */ +@import url(http://fonts.googleapis.com/css?family=Oxygen:100,300,400,700,900,100italic,300italic,400italic,700italic,900italic); +/* Theme Vars */ +/* Compass Vars */ +/* Override some defaults */ +/* line 12, ../_sass/_base.scss */ +html, body { + background-color: #2a2a2a; + background: url(body.png) repeat; +} + +/* line 17, ../_sass/_base.scss */ +body { + color: #bbb; + text-shadow: 1px 1px 1px #000; + height: 100%; + font-family: 'Oxygen', sans-serif; + font-size: 14px; +} + +/* line 25, ../_sass/_base.scss */ +h1, h2, h3, h4, h5, h6 { + margin: 0; + font-weight: normal; + color: #999; + text-rendering: optimizelegibility; +} + +/* line 32, ../_sass/_base.scss */ +a { + color: #a9c161; + -webkit-transition: all 0.3s; + -moz-transition: all 0.3s; + -o-transition: all 0.3s; + transition: all 0.3s; +} +/* line 34, ../_sass/_base.scss */ +a:hover { + color: #e5eccf; + text-decoration: none; +} + +/* line 41, ../_sass/_base.scss */ +h1 small, h2 small, h3 small, h4 small, h5 small, h6 small { + font-weight: normal; + color: #999999; +} + +/* line 46, ../_sass/_base.scss */ +h1, h2 { + margin: 0 0 15px; +} + +/* line 50, ../_sass/_base.scss */ +h1 { + font-size: 40px; +} + +/* line 54, ../_sass/_base.scss */ +h2 { + font-size: 30px; +} + +/* line 58, ../_sass/_base.scss */ +pre, code { + color: #ccc; +} + +/* line 62, ../_sass/_base.scss */ +pre > code { + /* Reset box-shadow for pygments */ + background: none; + box-shadow: none; + -moz-box-shadow: none; + -webkit-box-shadow: none; + -o-box-shadow: none; +} + +/* line 71, ../_sass/_base.scss */ +hr { + border: none; + background: rgba(0, 0, 0, 0.7); + height: 1px; +} + +/* Layout */ +/* line 78, ../_sass/_base.scss */ +.navbar { + margin-bottom: 0; +} +/* line 80, ../_sass/_base.scss */ +.navbar div.navbar-inner { + -webkit-border-radius: 0; + -moz-border-radius: 0; + -ms-border-radius: 0; + -o-border-radius: 0; + border-radius: 0; +} +/* line 82, ../_sass/_base.scss */ +.navbar div.navbar-inner a.brand { + padding: 10px 20px; +} +/* line 87, ../_sass/_base.scss */ +.navbar div.navbar-inner ul.nav:first-child li a { + padding: 12px 10px 8px; +} + +/* line 96, ../_sass/_base.scss */ +.container > footer { + margin-top: 20px; + clear: both; +} + +/* Content */ +/* line 103, ../_sass/_base.scss */ +.content { + /* when there's no page-header */ + /* Wide column content */ +} +/* line 104, ../_sass/_base.scss */ +.content img { + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.8); +} +/* line 108, ../_sass/_base.scss */ +.content.no-header { + padding-top: 40px; +} +/* line 115, ../_sass/_base.scss */ +.content div.row div.span8 > img { + display: block; + margin: 10px auto; +} +/* line 121, ../_sass/_base.scss */ +.content div.row div.span4 h4 { + margin-top: 10px; +} +/* line 123, ../_sass/_base.scss */ +.content div.row div.span4 h4:first { + margin-top: 0; +} +/* line 127, ../_sass/_base.scss */ +.content p { + padding-bottom: 15px; + line-height: 25px; + margin: 0; +} + +/* Page header tweaks */ +/* line 135, ../_sass/_base.scss */ +.page-header { + -webkit-box-shadow: rgba(255, 255, 255, 0.1) 0 1px 0, rgba(0, 0, 0, 0.8) 0 1px 7px 0px inset; + -moz-box-shadow: rgba(255, 255, 255, 0.1) 0 1px 0, rgba(0, 0, 0, 0.8) 0 1px 7px 0px inset; + box-shadow: rgba(255, 255, 255, 0.1) 0 1px 0, rgba(0, 0, 0, 0.8) 0 1px 7px 0px inset; + background: #202020; + background-color: rgba(0, 0, 0, 0.3); + border: none; + padding: 20px 20px 10px; + margin: 0px -20px 20px; + z-index: 0; + -moz-border-radius-bottomleft: 5px; + -webkit-border-bottom-left-radius: 5px; + border-bottom-left-radius: 5px; + -moz-border-radius-bottomright: 5px; + -webkit-border-bottom-right-radius: 5px; + border-bottom-right-radius: 5px; +} +/* line 148, ../_sass/_base.scss */ +.page-header h1 { + color: #eee; + margin-bottom: 0; +} + +/* line 154, ../_sass/_base.scss */ +.topbar .btn { + border: 0; +} + +/* Pagination */ +/* line 161, ../_sass/_base.scss */ +div.pagination li a { + -webkit-box-shadow: 0px 1px 1px black; + -moz-box-shadow: 0px 1px 1px black; + box-shadow: 0px 1px 1px black; +} + +/* Buttons */ +/* line 169, ../_sass/_base.scss */ +a.btn, a.btn.disabled { + color: rgba(255, 255, 255, 0.7); + text-shadow: 1px 1px 2px #000; + background: rgba(0, 0, 0, 0.2); + border: none; + -moz-box-shadow: rgba(255, 255, 255, 0.1) 0 1px 0, rgba(0, 0, 0, 0.8) 0 1px 7px 0px inset; + -webkit-box-shadow: rgba(0, 0, 0, 0.8) 0 1px 0, rgba(255, 255, 255, 0.5) 0 1px 1px 0px inset; + -o-box-shadow: rgba(255, 255, 255, 0.1) 0 1px 0, rgba(0, 0, 0, 0.8) 0 1px 7px 0px inset; +} +/* line 180, ../_sass/_base.scss */ +a.btn:hover, a.btn.disabled:hover { + background: rgba(255, 255, 255, 0.05); +} +/* line 183, ../_sass/_base.scss */ +a.btn.disabled, a.btn.disabled.disabled { + color: rgba(255, 255, 255, 0.3); +} + +/* Tags */ +/* line 189, ../_sass/_base.scss */ +.tag_box { + list-style: none; + margin: 0; + padding: 5px 0; + overflow: hidden; +} +/* line 195, ../_sass/_base.scss */ +.tag_box.inline li { + float: left; +} +/* line 199, ../_sass/_base.scss */ +.tag_box li { + line-height: 28px; +} +/* line 203, ../_sass/_base.scss */ +.tag_box a.active { + background: #57A957; + border: 1px solid #4C964D; + color: #FFF; +} +/* line 208, ../_sass/_base.scss */ +.tag_box a span { + vertical-align: super; + font-size: 0.8em; +} + +/* line 216, ../_sass/_base.scss */ +.tag_box.inline a, +.tag_box a { + padding: 3px 6px; + margin: 2px; + background: rgba(255, 255, 255, 0.2); + color: white; + border-radius: 3px; + text-decoration: none; + -moz-box-shadow: rgba(255, 255, 255, 0.1) 0 1px 0, rgba(0, 0, 0, 0.8) 0 1px 7px 0px inset; + -webkit-box-shadow: rgba(0, 0, 0, 0.8) 0 1px 0, rgba(255, 255, 255, 0.5) 0 1px 1px 0px inset; + -o-box-shadow: rgba(255, 255, 255, 0.1) 0 1px 0, rgba(0, 0, 0, 0.8) 0 1px 7px 0px inset; +} +/* line 228, ../_sass/_base.scss */ +.tag_box.inline a:hover, +.tag_box a:hover { + background: rgba(255, 255, 255, 0.3); +} + +/* Page-specific */ +/* line 235, ../_sass/_base.scss */ +div.content div#page-index { + padding-top: 30px !important; +} + +/* Zocial Overrides */ +/* line 241, ../_sass/_base.scss */ +.navbar a.zocial { + padding-left: 0 !important; + padding-right: 0 !important; +} + +/* line 247, ../_sass/_base.scss */ +.zocial.googleplus:focus, +.zocial.googleplus:hover { + color: #e01d30 !important; +} + +/* line 252, ../_sass/_base.scss */ +.zocial.twitter:focus, +.zocial.twitter:hover { + color: #46c0fb !important; +} + +/* line 257, ../_sass/_base.scss */ +.zocial.linkedin:focus, +.zocial.linkedin:hover { + color: #50a1cb !important; +} + +/* line 262, ../_sass/_base.scss */ +.zocial.rss:focus, +.zocial.rss:hover { + color: #fb9d3a !important; +} + +/* Responsive Overrides */ +@media (max-width: 767px) { + /* line 3, ../_sass/_responsive.scss */ + body { + padding-right: 0px; + padding-left: 0px; + } + + /* line 8, ../_sass/_responsive.scss */ + div.container { + padding: 0px 20px; + } + /* line 10, ../_sass/_responsive.scss */ + div.container div.page-header { + -webkit-border-radius: 0; + -moz-border-radius: 0; + -ms-border-radius: 0; + -o-border-radius: 0; + border-radius: 0; + } +} +/* Posts */ +/* line 3, ../_sass/_post.scss */ +article.post header { + margin-bottom: 10px; +} +/* line 5, ../_sass/_post.scss */ +article.post header h1, article.post header h2 { + margin: 0; +} +/* line 8, ../_sass/_post.scss */ +article.post header .category { + display: inline-block; + color: white; + border-left: 1px solid rgba(255, 255, 255, 0.2); + padding-left: 6px; + margin-left: 3px; +} + +/* line 25, ../_sass/_post.scss */ +div.posts article div.date { + font-size: 10px; + padding: 10px 0; +} +/* line 28, ../_sass/_post.scss */ +div.posts article div.date time { + -webkit-box-shadow: rgba(255, 255, 255, 0.1) 0 1px 0, rgba(0, 0, 0, 0.8) 0 1px 7px 0px inset; + -moz-box-shadow: rgba(255, 255, 255, 0.1) 0 1px 0, rgba(0, 0, 0, 0.8) 0 1px 7px 0px inset; + box-shadow: rgba(255, 255, 255, 0.1) 0 1px 0, rgba(0, 0, 0, 0.8) 0 1px 7px 0px inset; + background: #202020; + background-color: rgba(0, 0, 0, 0.3); + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + -ms-border-radius: 5px; + -o-border-radius: 5px; + border-radius: 5px; + padding: 5px 10px; +} + +/* line 4, ../_sass/_custom.scss */ +html, body { + height: 100%; +} + +/* line 8, ../_sass/_custom.scss */ +html > body { + min-height: 100%; + height: auto; + position: relative; +} + +/* line 14, ../_sass/_custom.scss */ +.content { + margin-bottom: 180px; +} + +/* line 18, ../_sass/_custom.scss */ +.text-right { + text-align: right; +} + +/* line 22, ../_sass/_custom.scss */ +.text-left { + text-align: left; +} + +/* line 26, ../_sass/_custom.scss */ +.text-center { + text-align: center; +} + +/* line 30, ../_sass/_custom.scss */ +.page-header { + -webkit-background-size: cover; + -moz-background-size: cover; + -o-background-size: cover; + background-size: cover; + position: relative; +} +/* line 35, ../_sass/_custom.scss */ +.page-header h1 { + font-size: 3.2em; + line-height: 1.2em; + bottom: 15px; + text-shadow: 1px 1px 15px #000; + padding-bottom: 10px; + font-weight: 300; +} +/* line 46, ../_sass/_custom.scss */ +.page-header.teaser h1 { + margin-top: 30px; + margin-bottom: 50px; +} +@media (max-width: 767px) { + /* line 53, ../_sass/_custom.scss */ + .page-header h1 { + font-size: 2.2em; + } +} +/* line 58, ../_sass/_custom.scss */ +.page-header small { + color: #fff; +} + +/* line 63, ../_sass/_custom.scss */ +.teaser-button { + margin-top: 20px; + margin-bottom: 20px; + font-weight: 900; + font-size: 1.2em; + line-height: 1em; +} +/* line 71, ../_sass/_custom.scss */ +.teaser-button i { + color: #fff; +} + +/* line 76, ../_sass/_custom.scss */ +.footer-wrapper { + position: absolute; + left: 0; + bottom: 0; + min-width: 100%; + line-height: 25px; +} +/* line 84, ../_sass/_custom.scss */ +.footer-wrapper .page-footer { + padding: 20px 20px 20px; + margin: 20px -20px 0px; + -moz-border-radius-topleft: 5px; + -webkit-border-top-left-radius: 5px; + border-top-left-radius: 5px; + -moz-border-radius-topright: 5px; + -webkit-border-top-right-radius: 5px; + border-top-right-radius: 5px; + -webkit-box-shadow: rgba(255, 255, 255, 0.1) 0 1px 0, rgba(0, 0, 0, 0.8) 0 1px 7px 0px inset; + -moz-box-shadow: rgba(255, 255, 255, 0.1) 0 1px 0, rgba(0, 0, 0, 0.8) 0 1px 7px 0px inset; + box-shadow: rgba(255, 255, 255, 0.1) 0 1px 0, rgba(0, 0, 0, 0.8) 0 1px 7px 0px inset; + background: #202020; + background-color: rgba(0, 0, 0, 0.3); + color: #888; +} +/* line 99, ../_sass/_custom.scss */ +.footer-wrapper .page-footer a { + color: #bbb; +} +/* line 102, ../_sass/_custom.scss */ +.footer-wrapper .page-footer a:hover { + color: #fff; +} +/* line 107, ../_sass/_custom.scss */ +.footer-wrapper .page-footer .text-right { + margin-top: 12px; +} +/* line 111, ../_sass/_custom.scss */ +.footer-wrapper .page-footer img { + position: relative; + top: -4px; + margin-left: 5px; + width: 100px; + height: 30px; + opacity: .8; + padding: 1px; + -webkit-transition: opacity 0.2s; + -moz-transition: opacity 0.2s; + -o-transition: opacity 0.2s; + transition: opacity 0.2s; +} +/* line 121, ../_sass/_custom.scss */ +.footer-wrapper .page-footer img:hover { + opacity: 1; +} diff --git a/scripts/make_doc.sh b/scripts/make_doc.sh new file mode 100755 index 000000000..db908d9f9 --- /dev/null +++ b/scripts/make_doc.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +# get directory of script and cd to doc folder +DIR="$( cd "$( dirname "$0" )" && pwd )" +cd $DIR/../doc + +doxygen + +cp *.css doc/html +cp *.png doc/html From e9a05e551c49c245507a01d9a6b52d90b8e0f07d Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Wed, 26 Feb 2014 13:26:41 +0100 Subject: [PATCH 123/146] Rename find_json.cmake to FindJson.cmake. --- CMakeLists.txt | 2 +- cmake/modules/{find_json.cmake => FindJson.cmake} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename cmake/modules/{find_json.cmake => FindJson.cmake} (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index fd914e2c0..0dac6b1c2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,7 +43,7 @@ include(find_compiler) include(find_schism) include(find_boost) include(find_bullet) -include(find_json) +include(FindJson) set(LIBS ${SCHISM_LIBRARIES} diff --git a/cmake/modules/find_json.cmake b/cmake/modules/FindJson.cmake similarity index 100% rename from cmake/modules/find_json.cmake rename to cmake/modules/FindJson.cmake From c07878b6a35c64d53ba0a29693cd9b2cfe0d952c Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Wed, 26 Feb 2014 15:22:20 +0100 Subject: [PATCH 124/146] Use find_package(Json) to search for jsoncpp. --- CMakeLists.txt | 2 +- cmake/modules/FindJson.cmake | 162 ++++++++--------------------------- 2 files changed, 38 insertions(+), 126 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0dac6b1c2..2dc1fb5d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,7 +43,7 @@ include(find_compiler) include(find_schism) include(find_boost) include(find_bullet) -include(FindJson) +find_package(Json) set(LIBS ${SCHISM_LIBRARIES} diff --git a/cmake/modules/FindJson.cmake b/cmake/modules/FindJson.cmake index 0c06a1526..3744dd0bf 100644 --- a/cmake/modules/FindJson.cmake +++ b/cmake/modules/FindJson.cmake @@ -1,125 +1,37 @@ -############################################################################## -# pre-defined search paths -############################################################################## -SET(JSON_INCLUDE_SEARCH_DIRS - ${GLOBAL_EXT_DIR}/inc/json - ${GUACAMOLE_EXT_DIR}/inc/json - /usr/include -) - -SET(JSON_LIBRARY_SEARCH_DIRS - ${GLOBAL_EXT_DIR}/lib - ${GUACAMOLE_EXT_DIR}/lib - /usr/lib -) - -############################################################################## -# feedback to provide user-defined paths to search for json -############################################################################## -MACRO (request_json_search_directories) - - IF ( NOT JSON_INCLUDE_DIRS AND NOT JSON_LIBRARY_DIRS ) - SET(JSON_INCLUDE_SEARCH_DIR "Please provide json include path." CACHE PATH "path to json headers.") - SET(JSON_LIBRARY_SEARCH_DIR "Please provide json library path." CACHE PATH "path to json libraries.") - MESSAGE(FATAL_ERROR "find_json.cmake: unable to find json.") - ENDIF ( NOT JSON_INCLUDE_DIRS AND NOT JSON_LIBRARY_DIRS ) - - IF ( NOT JSON_INCLUDE_DIRS ) - SET(JSON_INCLUDE_SEARCH_DIR "Please provide json include path." CACHE PATH "path to json headers.") - MESSAGE(FATAL_ERROR "find_json.cmake: unable to find json headers.") - ELSE ( NOT JSON_INCLUDE_DIRS ) - UNSET(JSON_INCLUDE_SEARCH_DIR CACHE) - ENDIF ( NOT JSON_INCLUDE_DIRS ) - - IF ( NOT JSON_LIBRARY_DIRS ) - SET(JSON_LIBRARY_SEARCH_DIR "Please provide json library path." CACHE PATH "path to json libraries.") - MESSAGE(FATAL_ERROR "find_json.cmake: unable to find json libraries.") - ELSE ( NOT JSON_LIBRARY_DIRS ) - UNSET(JSON_LIBRARY_SEARCH_DIR CACHE) - ENDIF ( NOT JSON_LIBRARY_DIRS ) - -ENDMACRO (request_json_search_directories) - -############################################################################## -# search -############################################################################## -message(STATUS "-- checking for JSON") - -IF (NOT JSON_INCLUDE_DIRS) - - SET(_JSON_FOUND_INC_DIRS "") - - FOREACH(_SEARCH_DIR ${JSON_INCLUDE_SEARCH_DIRS}) - FIND_PATH(_CUR_SEARCH - NAMES jsoncpp/json/json.h - PATHS ${_SEARCH_DIR} - NO_DEFAULT_PATH) - IF (_CUR_SEARCH) - LIST(APPEND _JSON_FOUND_INC_DIRS ${_CUR_SEARCH}) - ENDIF(_CUR_SEARCH) - SET(_CUR_SEARCH _CUR_SEARCH-NOTFOUND CACHE INTERNAL "internal use") - ENDFOREACH(_SEARCH_DIR ${JSON_INCLUDE_SEARCH_DIRS}) - - IF (NOT _JSON_FOUND_INC_DIRS) - request_json_search_directories() - ENDIF (NOT _JSON_FOUND_INC_DIRS) - - FOREACH(_INC_DIR ${_JSON_FOUND_INC_DIRS}) - LIST(APPEND _JSON_INCLUDE_DIRS ${_INC_DIR}) - ENDFOREACH(_INC_DIR ${_BOOST_FOUND_INC_DIRS}) - - IF (_JSON_FOUND_INC_DIRS) - SET(JSON_INCLUDE_DIRS ${_JSON_INCLUDE_DIRS} CACHE PATH "path to json headers.") - ENDIF (_JSON_FOUND_INC_DIRS) - -ENDIF(NOT JSON_INCLUDE_DIRS) - -IF(UNIX) - SET(JSON_LIB_FILENAME "libjsoncpp.so") -ELSEIF(WIN32) - SET(JSON_LIB_FILENAME "json.lib") -ENDIF(UNIX) - -IF ( JSON_INCLUDE_DIRS AND NOT JSON_LIBRARY_DIRS ) - - SET(_JSON_FOUND_LIB_DIR "") - SET(_JSON_POSTFIX "") - - FOREACH(_SEARCH_DIR ${JSON_LIBRARY_SEARCH_DIRS}) - FIND_PATH(_CUR_SEARCH - NAMES ${JSON_LIB_FILENAME} - PATHS ${_SEARCH_DIR} - PATH_SUFFIXES debug release - NO_DEFAULT_PATH) - IF (_CUR_SEARCH) - LIST(APPEND _JSON_FOUND_LIB_DIR ${_SEARCH_DIR}) - ENDIF(_CUR_SEARCH) - SET(_CUR_SEARCH _CUR_SEARCH-NOTFOUND CACHE INTERNAL "internal use") - ENDFOREACH(_SEARCH_DIR ${JSON_LIBRARY_SEARCH_DIRS}) - - IF (NOT _JSON_FOUND_LIB_DIR) - request_json_search_directories() - ELSE (NOT _JSON_FOUND_LIB_DIR) - SET(JSON_LIBRARY_DIRS ${_JSON_FOUND_LIB_DIR}) - SET(JSON_LIBRARY_DIRS ${JSON_LIBRARY_DIRS} CACHE STRING "The json library directory.") - ENDIF (NOT _JSON_FOUND_LIB_DIR) - - IF (_JSON_FOUND_LIB_DIR) - SET(JSON_LIBRARIES ${JSON_LIB_FILENAME} CACHE STRING "The json library.") - ENDIF (_JSON_FOUND_LIB_DIR) - -ENDIF( JSON_INCLUDE_DIRS AND NOT JSON_LIBRARY_DIRS ) - -############################################################################## -# verify -############################################################################## -IF ( NOT JSON_INCLUDE_DIRS OR NOT JSON_LIBRARY_DIRS ) - request_json_search_directories() -ELSE ( NOT JSON_INCLUDE_DIRS OR NOT JSON_LIBRARY_DIRS ) - UNSET(JSON_INCLUDE_SEARCH_DIR CACHE) - UNSET(JSON_LIBRARY_SEARCH_DIR CACHE) - MESSAGE(STATUS "-- found matching json version") -ENDIF ( NOT JSON_INCLUDE_DIRS OR NOT JSON_LIBRARY_DIRS ) - - - +# - Try to find UnitTest++ +# Once done this will define +# JSON_FOUND - System has json +# JSON_INCLUDE_DIRS - The json include directories +# JSON_LIBRARIES - The libraries needed to use json +find_path(JSON_INCLUDE_DIR + NAMES json/features.h + PATHS /usr/include/jsoncpp/json + ${GLOBAL_EXT_DIR}/inc/json + ${GUACAMOLE_EXT_DIR}/inc/json + PATH_SUFFIXES jsoncpp json + ) + +find_library(JSON_LIBRARY + NAMES jsoncpp json + PATHS /usr/lib + ${GLOBAL_EXT_DIR}/lib + ${GUACAMOLE_EXT_DIR}/lib + PATH_SUFFIXES release + ) + +#find_library(JSON_LIBRARY_DEBUG +# NAMES jsoncpp json +# PATHS /usr/lib +# ${GLOBAL_EXT_DIR}/lib +# ${GUACAMOLE_EXT_DIR}/lib +# PATH_SUFFIXES debug +# ) + +set(JSON_LIBRARIES ${JSON_LIBRARY} ) +set(JSON_INCLUDE_DIRS ${JSON_INCLUDE_DIR} ) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Json DEFAULT_MSG + JSON_LIBRARY JSON_INCLUDE_DIR) + +mark_as_advanced(JSON_INCLUDE_DIR JSON_LIBRARY ) From 26db23df5eb9ca405341a34da01a8563d8c93003 Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Thu, 27 Feb 2014 10:22:42 +0100 Subject: [PATCH 125/146] Rename scripts for boost, bullet, schism, assimp. --- CMakeLists.txt | 10 +++++----- cmake/modules/{find_assimp.cmake => FindAssimp.cmake} | 0 cmake/modules/{find_boost.cmake => FindBoost.cmake} | 0 cmake/modules/{find_bullet.cmake => FindBullet.cmake} | 0 cmake/modules/{find_schism.cmake => FindSchism.cmake} | 0 5 files changed, 5 insertions(+), 5 deletions(-) rename cmake/modules/{find_assimp.cmake => FindAssimp.cmake} (100%) rename cmake/modules/{find_boost.cmake => FindBoost.cmake} (100%) rename cmake/modules/{find_bullet.cmake => FindBullet.cmake} (100%) rename cmake/modules/{find_schism.cmake => FindSchism.cmake} (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2dc1fb5d6..f59e79c21 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,15 +35,15 @@ if (UNIX) pkg_check_modules(GL REQUIRED gl) pkg_check_modules(GLEW REQUIRED glew) elseif (WIN32) - include(find_assimp) + include(FindAssimp) endif (UNIX) include(define_macros) include(find_compiler) -include(find_schism) -include(find_boost) -include(find_bullet) -find_package(Json) +include(FindSchism) +include(FindBoost) +include(FindBullet) +find_package(Json REQUIRED) set(LIBS ${SCHISM_LIBRARIES} diff --git a/cmake/modules/find_assimp.cmake b/cmake/modules/FindAssimp.cmake similarity index 100% rename from cmake/modules/find_assimp.cmake rename to cmake/modules/FindAssimp.cmake diff --git a/cmake/modules/find_boost.cmake b/cmake/modules/FindBoost.cmake similarity index 100% rename from cmake/modules/find_boost.cmake rename to cmake/modules/FindBoost.cmake diff --git a/cmake/modules/find_bullet.cmake b/cmake/modules/FindBullet.cmake similarity index 100% rename from cmake/modules/find_bullet.cmake rename to cmake/modules/FindBullet.cmake diff --git a/cmake/modules/find_schism.cmake b/cmake/modules/FindSchism.cmake similarity index 100% rename from cmake/modules/find_schism.cmake rename to cmake/modules/FindSchism.cmake From b3c215b4964e7c4af03ebee028a8c89bb8846bbc Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Thu, 27 Feb 2014 16:21:31 +0100 Subject: [PATCH 126/146] added possibility to adjust Ticker's tick time to other values on the fly --- include/gua/events/Ticker.hpp | 3 + scripts/website.sublime-workspace | 1823 +++++++++++++++++++++++++++++ src/gua/events/Ticker.cpp | 8 + 3 files changed, 1834 insertions(+) create mode 100644 scripts/website.sublime-workspace diff --git a/include/gua/events/Ticker.hpp b/include/gua/events/Ticker.hpp index bca76e40a..a20a55ea7 100644 --- a/include/gua/events/Ticker.hpp +++ b/include/gua/events/Ticker.hpp @@ -38,6 +38,9 @@ namespace gua { Ticker(MainLoop& mainloop, double tick_time); ~Ticker(); + void set_tick_time(double tick_time); + double get_tick_time() const; + Signal<> on_tick; private: diff --git a/scripts/website.sublime-workspace b/scripts/website.sublime-workspace new file mode 100644 index 000000000..9349f1b76 --- /dev/null +++ b/scripts/website.sublime-workspace @@ -0,0 +1,1823 @@ +{ + "auto_complete": + { + "selected_items": + [ + [ + "bac", + "background-color" + ], + [ + "nav", + "navpath" + ], + [ + "re", + "removeClass" + ], + [ + "paddin", + "padding-bottom" + ], + [ + "marg", + "margin-bottom" + ], + [ + "pos", + "position" + ], + [ + "wid", + "width" + ], + [ + "fon", + "font-size" + ], + [ + "sha", + "shadow_map_" + ], + [ + "mes", + "mesh_shader_" + ], + [ + "pr", + "projection_mode" + ], + [ + "vec", + "vec3" + ], + [ + "ve", + "vec3" + ], + [ + "gu", + "gua_out_color" + ], + [ + "sh", + "shadow" + ], + [ + "gua_ge", + "gua_get_position" + ], + [ + "in", + "inputs_" + ], + [ + "ex", + "extends_in_sun_space" + ], + [ + "ca", + "cascade" + ], + [ + "co", + "corner_in_sun_space" + ], + [ + "cr", + "cropped_frustum_corners" + ], + [ + "inv", + "inverse_transform" + ], + [ + "spl", + "split_1" + ], + [ + "crop", + "cropped_clip_near" + ], + [ + "render_", + "render_geometry" + ], + [ + "prin", + "print_shaders" + ], + [ + "n", + "near_plane" + ], + [ + "tp", + "top" + ], + [ + "cl", + "clip_far" + ], + [ + "com", + "compute_position" + ], + [ + "lensE", + "lensEyeR statement" + ], + [ + "len", + "lens" + ], + [ + "le", + "lens" + ], + [ + "fil", + "file" + ], + [ + "file", + "filename" + ], + [ + "new", + "new_passes" + ], + [ + "lo", + "loading_texture_height" + ], + [ + "loa", + "loading_texture_width" + ], + [ + "mon", + "monitor_count" + ], + [ + "las", + "last_left_resolution_" + ], + [ + "on", + "on_window_resize" + ], + [ + "scm", + "scm_core" + ], + [ + "gl", + "glfw_window_" + ], + [ + "mo", + "monitor" + ], + [ + "moi", + "monitor_count" + ], + [ + "mouse", + "mouseMoveEvent function" + ], + [ + "te", + "test_btn statement" + ], + [ + "update", + "updateScene function" + ], + [ + "get_sc", + "get_scaled_world_transform" + ], + [ + "__n", + "__node statement" + ], + [ + "G", + "GuiElement class" + ], + [ + "Tex", + "TexturedQuadNode" + ], + [ + "geom", + "geometry_bbox" + ], + [ + "tra", + "transform" + ], + [ + "dat", + "data" + ], + [ + "da", + "data_" + ], + [ + "de", + "depth_" + ], + [ + "h", + "height_" + ], + [ + "i", + "id" + ], + [ + "di", + "dirty_flags_" + ], + [ + "fal", + "fallback_material" + ], + [ + "nex", + "next_node" + ], + [ + "CMA", + "CMAKE_SOURCE_DIR" + ], + [ + "fi", + "file_name_" + ], + [ + "ma", + "max" + ], + [ + "vis", + "visit_children" + ], + [ + "is", + "is_visible" + ], + [ + "tr", + "transform" + ], + [ + "set", + "set_dirty" + ], + [ + "no", + "node" + ], + [ + "add", + "add_all_geometries" + ], + [ + "chi", + "child_dirty_" + ], + [ + "spo", + "sponza_rb" + ], + [ + "Ro", + "RigidBodyNode" + ], + [ + "dire", + "direction" + ], + [ + "get_", + "get_size" + ], + [ + "node", + "node_name" + ], + [ + "a", + "auto" + ], + [ + "to", + "to_remove" + ], + [ + "ser", + "searched_node" + ], + [ + "po", + "positions" + ], + [ + "st", + "state" + ], + [ + "obe", + "objects" + ], + [ + "fa", + "fail_result" + ], + [ + "set_s", + "set_size" + ], + [ + "pip", + "pipeline_" + ], + [ + "li", + "light_rb" + ], + [ + "make_", + "make_shared" + ], + [ + "con", + "config" + ], + [ + "pa", + "parent" + ], + [ + "dynamic_", + "dynamic_pointer_cast" + ], + [ + "Geometry", + "GeometryNode" + ], + [ + "look", + "lookup_string" + ], + [ + "en", + "endl" + ], + [ + "un", + "unordered_map" + ], + [ + "rea", + "read_directory" + ], + [ + "Oc", + "OculusRift" + ], + [ + "inc", + "include" + ], + [ + "scre", + "screen" + ], + [ + "screen_", + "screen_right" + ], + [ + "screen_r", + "screen_right_it" + ], + [ + "screen_l", + "screen_left_it" + ], + [ + "cre", + "create" + ], + [ + "OCULUS_RIFT_", + "OCULUS_RIFT_RIGHT" + ], + [ + "ret", + "return" + ], + [ + "Con", + "Configuration" + ], + [ + "cur", + "current_directoy_" + ], + [ + "get_main", + "get_main_methods" + ], + [ + "as", + "asString" + ], + [ + "fun", + "functions" + ], + [ + "with", + "with_top_code" + ], + [ + "e", + "endl" + ], + [ + "gua_in", + "gua_in_position" + ], + [ + "gua_in_t", + "gua_in_tangent" + ], + [ + "GB", + "GBUFFER_FRAGMENT_STAGE" + ], + [ + "str", + "string" + ], + [ + "UNI", + "UNIFORM_CUBEMAP" + ], + [ + "main", + "main_pos" + ], + [ + "ge", + "get_name" + ], + [ + "_ta", + "_targetCameraPos" + ], + [ + "_in", + "_initMembers" + ], + [ + "han", + "handles" + ], + [ + "Dis", + "Display3D" + ], + [ + "marke", + "markerInterval" + ] + ] + }, + "buffers": + [ + { + "contents": "#!/bin/sh\n\n# get directory of script and cd to doc folder\nDIR=\"$( cd \"$( dirname \"$0\" )\" && pwd )\"\ncd $DIR/../doc\n\ndoxygen\n\ncp *.css doc/html\ncp *.png doc/html\n", + "file": "make_doc.sh", + "file_size": 160, + "file_write_time": 130379623100925502, + "settings": + { + "buffer_size": 160, + "line_ending": "Unix" + } + }, + { + "file": "/home/rufu1194/Desktop/guacamole/doc/style.css", + "settings": + { + "buffer_size": 27215, + "line_ending": "Unix" + } + }, + { + "contents": "\n\n\n\n\n$projectname: $title\n$title\n\n\n\n\n\n\n\n\n\n$treeview\n$search\n$mathjax\n\n\n
\n\n\n
\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\"Logo\"\n
$projectname\n  $projectnumber\n
\n
$projectbrief
\n
\n
$projectbrief
\n
$searchbox
\n
\n\n", + "file": "/home/rufu1194/Desktop/guacamole/doc/header.html", + "file_size": 4822, + "file_write_time": 130379623093685261, + "settings": + { + "buffer_size": 4822, + "line_ending": "Unix" + } + }, + { + "file": "/home/rufu1194/Desktop/avango/examples/touch_example/main.py", + "settings": + { + "buffer_size": 2963, + "line_ending": "Unix" + } + }, + { + "file": "make.sh", + "settings": + { + "buffer_size": 284, + "line_ending": "Unix" + } + } + ], + "build_system": "", + "command_palette": + { + "height": 375.0, + "selected_items": + [ + [ + "color", + "Color picker" + ], + [ + "colo", + "Color picker" + ], + [ + "ins", + "Package Control: Install Package" + ], + [ + "inst", + "Package Control: Install Package" + ], + [ + "view", + "View: Toggle Menu" + ], + [ + "new", + "Git: New Branch" + ], + [ + "pu", + "Git: Push Current Branch" + ], + [ + "cha", + "Git: Change Branch" + ], + [ + "com", + "Git: Commit" + ], + [ + "ad", + "Git: Add..." + ], + [ + "push", + "Git: Push Current Branch" + ], + [ + "mer", + "Git: Merge Branch" + ], + [ + "chan", + "Git: Change Branch" + ], + [ + "add", + "Git: Add..." + ], + [ + "comm", + "Git: Commit" + ], + [ + "git add", + "Git: Add..." + ], + [ + "git che", + "Git: Checkout Current File" + ], + [ + "git chec", + "Git: Checkout Current File" + ], + [ + "git res", + "Git: Reset (unstage) Current File" + ], + [ + "git a", + "Git: Add..." + ], + [ + "pus", + "Git: Push Current Branch" + ], + [ + "pul", + "Git: Pull Current Branch" + ], + [ + "git:", + "Git: Gui" + ], + [ + "pull", + "Git: Pull Current Branch" + ], + [ + "stas", + "Git: Stash Changes" + ], + [ + "in", + "Package Control: Install Package" + ], + [ + "rem", + "Package Control: Remove Package" + ], + [ + "pac", + "Package Control: Install Package" + ], + [ + "syn co", + "Set Syntax: CoffeeScript" + ], + [ + "pack", + "Package Control: Remove Package" + ], + [ + "re", + "Package Control: Remove Package" + ], + [ + "pa", + "Package Control: Remove Package" + ], + [ + "ena", + "Package Control: Enable Package" + ], + [ + "en", + "Package Control: Enable Package" + ], + [ + "dis", + "Package Control: Disable Package" + ], + [ + "enab", + "Package Control: Enable Package" + ], + [ + "the", + "QuickThemes: next" + ], + [ + "them", + "QuickThemes: next" + ], + [ + "Package Control: ", + "Package Control: Enable Package" + ], + [ + "key", + "Preferences: Key Bindings - User" + ], + [ + "remo", + "Package Control: Remove Package" + ], + [ + "gi", + "Git: Status" + ], + [ + "select", + "Bookmarks: Select All" + ], + [ + "git", + "Git: Status" + ], + [ + "packa", + "Package Control: Remove Package" + ], + [ + "sublmi", + "Preferences: SublimeLinter Settings – Default" + ] + ], + "width": 435.0 + }, + "console": + { + "height": 139.0, + "history": + [ + "a", + "a = 5", + "env", + "ls", + "bash", + "ls" + ] + }, + "distraction_free": + { + "menu_visible": true, + "show_minimap": false, + "show_open_files": false, + "show_tabs": false, + "side_bar_visible": false, + "status_bar_visible": false + }, + "file_history": + [ + "/home/rufu1194/Desktop/guacamole/include/gua/databases/Database.hpp", + "/home/rufu1194/Desktop/guacamole/src/gua/databases/MaterialDatabase.cpp", + "/home/rufu1194/Desktop/guacamole/documentation.md", + "/home/rufu1194/Desktop/guacamole/scripts/start_localhost.sh", + "/home/rufu1194/Desktop/guacamole/_config.yml", + "/opt/guacamole/examples/cluster/multi-user/lib/Navigation.py", + "/opt/guacamole/examples/cluster/multi-user/lib/Device.py", + "/home/rufu1194/simmesimme.github.com/index.md", + "/home/rufu1194/simmesimme.github.com/_config.yml", + "/home/rufu1194/simmesimme.github.com/features.md", + "/home/rufu1194/simmesimme.github.com/installation.md", + "/home/rufu1194/simmesimme.github.com/documentation.md", + "/home/rufu1194/simmesimme.github.com/news.html", + "/home/rufu1194/simmesimme.github.com/_includes/themes/hooligan/page.html", + "/home/rufu1194/Desktop/avango/avango-gua/include/avango/gua/scenegraph/Node.hpp", + "/home/rufu1194/simmesimme.github.com/README.md", + "/home/rufu1194/simmesimme.github.com/.gitignore", + "/home/rufu1194/Desktop/guacamole/doc/Doxyfile", + "/home/rufu1194/simmesimme.github.com/_includes/themes/hooligan/default.html", + "/home/rufu1194/simmesimme.github.com/scripts/start_localhost.sh", + "/home/rufu1194/simmesimme.github.com/assets/themes/hooligan/_sass/style.scss", + "/home/rufu1194/simmesimme.github.com/scripts/website.sublime-project", + "/home/rufu1194/Desktop/guacamole/scripts/guacamole.sublime-project", + "/home/rufu1194/simmesimme.github.com/assets/themes/hooligan/_sass/_base.scss", + "/home/rufu1194/simmesimme.github.com/assets/themes/hooligan/config.rb", + "/home/rufu1194/simmesimme.github.com/scripts/make.sh", + "/home/rufu1194/simmesimme.github.com/assets/themes/hooligan/_sass/config.rb", + "/home/rufu1194/simmesimme.github.com/assets/themes/hooligan/css/darkstrap.css", + "/home/rufu1194/simmesimme.github.com/assets/themes/hooligan/css/pygments.css", + "/home/rufu1194/simmesimme.github.com/assets/themes/hooligan/css/style.css", + "/home/rufu1194/simmesimme.github.com/_theme_packages/hooligan/README.md", + "/home/rufu1194/simmesimme.github.com/_theme_packages/hooligan/assets/themes/hooligan/css/style.css", + "/home/rufu1194/simmesimme.github.com/_theme_packages/hooligan/manifest.yml", + "/home/rufu1194/simmesimme.github.com/_theme_packages/hooligan/packager.yml", + "/home/rufu1194/simmesimme.github.com/_includes/themes/hooligan/settings.yml", + "/home/rufu1194/simmesimme.github.com/_includes/themes/hooligan/post.html", + "/home/rufu1194/simmesimme.github.com/_theme_packages/hooligan/_includes/themes/hooligan/page.html", + "/home/rufu1194/simmesimme.github.com/_theme_packages/hooligan/_includes/themes/hooligan/default.html", + "/home/rufu1194/simmesimme.github.com/_theme_packages/hooligan/_includes/themes/hooligan/post.html", + "/home/rufu1194/simmesimme.github.com/_posts/2014-02-20-hello-world.md", + "/home/rufu1194/simmesimme.github.com/_posts/core-samples/test.md", + "/home/rufu1194/Desktop/guacamole/stylesheets/stylesheet.css", + "/home/rufu1194/Desktop/guacamole/index.html", + "/home/rufu1194/.config/sublime-text-3/Packages/User/Default (Linux).sublime-keymap", + "/home/rufu1194/.config/sublime-text-3/Packages/User/Preferences.sublime-settings", + "/home/rufu1194/.config/sublime-text-3/Packages/Schemr/Default (Linux).sublime-keymap", + "/home/rufu1194/Desktop/guacamole/resources/shaders/uber_shaders/lighting/mesh/shadow.vert", + "/home/rufu1194/Desktop/guacamole/resources/shaders/uber_shaders/lighting/mesh/shadow.frag", + "/home/rufu1194/Desktop/guacamole/src/gua/renderer/LightingPass.cpp", + "/home/rufu1194/Desktop/guacamole/include/gua/renderer/LightingPass.hpp", + "/home/rufu1194/Desktop/guacamole/include/gua/renderer/ShadowMap.hpp", + "/home/rufu1194/Desktop/guacamole/include/gua/renderer/Uniform.hpp", + "/home/rufu1194/Desktop/guacamole/src/gua/renderer/UberShader.cpp", + "/home/rufu1194/Desktop/guacamole/src/gua/renderer/GBufferPass.cpp", + "/home/rufu1194/Desktop/guacamole/src/gua/renderer/Pass.cpp", + "/home/rufu1194/Desktop/guacamole/src/gua/renderer/Pipeline.cpp", + "/home/rufu1194/Desktop/guacamole/include/gua/renderer/DisplayData.hpp", + "/home/rufu1194/Desktop/guacamole/src/gua/renderer/DisplayData.cpp", + "/home/rufu1194/Desktop/guacamole/src/gua/utils/DotGenerator.cpp", + "/home/rufu1194/Desktop/guacamole/include/gua/renderer/Pass.hpp", + "/home/rufu1194/Desktop/guacamole/include/gua/renderer/GBufferPass.hpp", + "/home/rufu1194/Desktop/guacamole/include/gua/renderer/Renderer.hpp", + "/home/rufu1194/Desktop/guacamole/include/gua/renderer.hpp", + "/home/rufu1194/Desktop/guacamole/src/gua/renderer/Renderer.cpp", + "/home/rufu1194/Desktop/guacamole/include/gua/renderer/BufferDescriptions.hpp", + "/home/rufu1194/Desktop/guacamole/include/gua/renderer/nurbs_geometry/tml/oriented_boundingbox_partial_derivative_policy.hpp", + "/home/rufu1194/Desktop/guacamole/src/gua/databases/Resources.cpp", + "/home/rufu1194/Desktop/guacamole/resources/shaders/uber_shaders/gbuffer/mesh/mesh.frag", + "/home/rufu1194/Desktop/guacamole/resources/shaders/uber_shaders/gbuffer/mesh/mesh.vert", + "/home/rufu1194/Desktop/guacamole/resources/shaders/uber_shaders/lighting/lighting.vert", + "/home/rufu1194/Desktop/guacamole/resources/shaders/uber_shaders/lighting/lighting.frag", + "/home/rufu1194/Desktop/guacamole/include/gua/renderer/Pipeline.hpp", + "/home/rufu1194/Desktop/guacamole/include/gua/renderer/ShadowMapNURBSShader.hpp", + "/home/rufu1194/Desktop/guacamole/src/gua/renderer/MeshLoader.cpp", + "/home/rufu1194/Desktop/guacamole/include/gua/renderer/MeshLoader.hpp", + "/home/rufu1194/Desktop/guacamole/src/gua/renderer/Mesh.cpp", + "/home/rufu1194/Desktop/guacamole/include/gua/renderer/Mesh.hpp", + "/home/rufu1194/Desktop/guacamole/src/gua/math/math.cpp", + "/home/rufu1194/Desktop/guacamole/include/gua/utils/Mask.hpp", + "/home/rufu1194/Desktop/guacamole/include/gua/memory.hpp", + "/home/rufu1194/Desktop/guacamole/include/gua/renderer/ShadingModel.hpp", + "/home/rufu1194/Desktop/guacamole/include/gua/renderer/ShaderStage.hpp", + "/home/rufu1194/Desktop/guacamole/include/gua/renderer/ShaderProgram.hpp", + "/home/rufu1194/Desktop/guacamole/resources/shaders/uber_shaders/common/get_position.glsl", + "/home/rufu1194/Desktop/guacamole/resources/shaders/uber_shaders/common/get_depth.glsl", + "/home/rufu1194/Desktop/guacamole/src/gua/renderer/Frustum.cpp", + "/home/rufu1194/Desktop/guacamole/src/gua/renderer/Serializer.cpp", + "/home/rufu1194/Desktop/guacamole/src/gua/renderer/ShadowMap.cpp", + "/home/rufu1194/Desktop/guacamole/src/gua/renderer/LayerMapping.cpp", + "/home/rufu1194/Desktop/guacamole/resources/shaders/uber_shaders/final/final.frag", + "/home/rufu1194/Desktop/guacamole/src/gua/renderer/MaterialLoader.cpp", + "/home/rufu1194/Desktop/guacamole/include/gua/renderer/SerializedScene.hpp", + "/home/rufu1194/Desktop/guacamole/include/gua/scenegraph/Node.hpp", + "/home/rufu1194/Desktop/guacamole/src/gua/renderer/ShadingModel.cpp", + "/home/rufu1194/Desktop/guacamole/src/gua/scenegraph/SceneGraph.cpp", + "/home/rufu1194/Desktop/guacamole/src/gua/renderer/MaterialDescription.cpp", + "/home/rufu1194/Desktop/guacamole/include/gua/renderer/MaterialLoader.hpp", + "/home/rufu1194/Desktop/guacamole/include/gua/renderer/LayerMapping.hpp", + "/home/rufu1194/Desktop/guacamole/include/gua/renderer/Frustum.hpp", + "/home/rufu1194/Desktop/guacamole/src/gua/renderer/GBuffer.cpp", + "/home/rufu1194/Desktop/guacamole/include/gua/renderer/Serializer.hpp", + "/home/rufu1194/Desktop/guacamole/include/gua/renderer/SerializedNode.hpp", + "/home/rufu1194/Desktop/avango/examples/zmq_distribution/Readme.txt", + "/home/rufu1194/Desktop/avango/examples/zmq_distribution/start-server.sh", + "/home/rufu1194/Desktop/avango/examples/zmq_distribution/start-client.sh", + "/home/rufu1194/Desktop/avango/examples/zmq_distribution/simpleviewer-srv.py", + "/home/rufu1194/Desktop/avango/examples/zmq_distribution/simpleviewer-clnt.py", + "/home/rufu1194/Desktop/guacamole/src/gua/renderer/PostFXPass.cpp", + "/home/rufu1194/Desktop/guacamole/include/gua/renderer/Camera.hpp", + "/home/rufu1194/Desktop/guacamole/resources/shaders/uber_shaders/postfx/godrays.vert", + "/home/rufu1194/Desktop/guacamole/src/gua/physics/CollisionShapeNode.cpp", + "/home/rufu1194/Desktop/guacamole/resources/shaders/uber_shaders/postfx/godrays.frag", + "/home/rufu1194/Desktop/guacamole/include/gua/renderer/PostFXPass.hpp", + "/home/rufu1194/Desktop/guacamole/include/gua/math/math.hpp", + "/home/rufu1194/Desktop/guacamole/plugins/guacamole-oculus/include/gua/OculusRift.hpp", + "/home/rufu1194/Desktop/guacamole/resources/shaders/uber_shaders/common/get_material_id.glsl", + "/home/rufu1194/Desktop/guacamole/include/gua/utils/Directory.hpp", + "/home/rufu1194/Desktop/guacamole/include/gua/scenegraph/PointLightNode.hpp", + "/home/rufu1194/Desktop/guacamole/src/gua/renderer/LightingUberShader.cpp", + "/home/rufu1194/Desktop/guacamole/include/gua/renderer/LightingUberShader.hpp", + "/home/rufu1194/Desktop/guacamole/include/gua/databases/Resources.hpp", + "/home/rufu1194/Desktop/guacamole/include/gua/renderer/LoaderBase.hpp", + "/home/rufu1194/Desktop/guacamole/src/gua/math/BoundingBoxAlgo.cpp", + "/home/rufu1194/Desktop/guacamole/include/gua/renderer/GBuffer.hpp", + "/home/rufu1194/Desktop/guacamole/src/gua/renderer/ShadowMapMeshShader.cpp", + "/home/rufu1194/Desktop/guacamole/include/gua/renderer/ShadowMapMeshShader.hpp", + "/home/rufu1194/Desktop/guacamole/src/gua/renderer/ShadowMapNURBSShader.cpp", + "/home/rufu1194/Desktop/guacamole/make" + ], + "find": + { + "height": 35.0 + }, + "find_in_files": + { + "height": 93.0, + "where_history": + [ + "", + "/home/rufu1194/Desktop/guacamole/include/gua", + "", + "/home/rufu1194/Desktop/guacamole/src", + "/home/rufu1194/Desktop/guacamole", + "/home/rufu1194/Desktop/guacamole/src", + "/home/rufu1194/Desktop/guacamole/include", + "/home/rufu1194/Desktop/guacamole/src" + ] + }, + "find_state": + { + "case_sensitive": false, + "find_history": + [ + "indexvalue", + "memitem", + "'", + "indexkey", + "key", + "pills", + "body", + "incl", + "News", + "Advent", + "header", + "head", + "page-h", + "alt", + "shader.", + "rendering", + "current_scenes_[0]", + "mesh_s", + "global_clipping_plane", + "enable_global_clipping_plane", + "/sc", + "screen", + "Screen", + "screen", + "render_go", + "gua_get_position()", + "shadow_pos", + "gua_get_shadow", + "render_godrays", + "godr", + "result", + "vec3", + "0", + "vec3", + "push_back", + "bbox.max", + "min", + ".z", + ".y", + ".x", + "tmp", + "camera", + "camera_transform", + "gua_light_shadow_map_projection_view_matrix", + "shadow_map_", + "LightingPass", + "shadow_map_", + "frustum[", + "result", + "compute_frustum", + "frustum", + "Frustum", + "camera_position_", + "view_", + "planes_", + "scal_type", + "update_b", + "update", + "spot", + "render_godrays", + "CalculateLightType", + "godra", + "rasterizer_state_", + "spot_light_count", + "SpotLightNode", + "gua_calculate_point_light", + "SpotLightNode", + "GUA_SPOT_LIGHT_NODE_HPP", + "= 0", + "head", + "236", + "GroupNode", + "convert_osg_to_gua", + "camera", + "Camera", + "ViewNode", + "gua_textured_quad", + "new_passes", + "passes_", + "reload_all", + "samplerBuffer", + "trim_pointdata", + "trim_curvedata", + "trim_curvelist", + "trim_contourlist", + "trim_partition", + "uniform", + "trim_partition", + "attribute_texture", + "parameter_texture", + "nurbs", + "cout", + "max_screen_error", + "new NURBS", + "left", + "resolution", + "left", + "glfw_LIBRARIES", + "glfw_INCLUDE_DIRS", + "GLFW", + "CUDA", + "GLFW_INCLUDE_DIR", + "GLFW_LIBRARY", + "filena", + "request_GLFW_search_directories", + "find_GLFW", + "GUACAMOLE", + "my_constructor", + "Pipeline", + "floo", + "Stones", + "scene", + "__view", + "size", + "btn", + "__view", + "__image", + "initUI", + "__updateTexture", + "__scene", + "navigation", + "\"\\n", + "needs_update", + "textures_.size() <= context.id || textures_[context.id] == 0 || dirty_flags_[context.id]", + "cout", + "####", + "GroupNode", + ");\n" + ], + "highlight": true, + "in_selection": false, + "preserve_case": false, + "regex": false, + "replace_history": + [ + "TransformNode", + "math::mat4::Zero()", + "@_", + " @", + "", + "# ===========", + "this.", + "@", + "----------------------------------------------------------------------------", + "this.", + "@_my", + "" + ], + "reverse": false, + "show_context": true, + "use_buffer2": true, + "whole_word": false, + "wrap": true + }, + "groups": + [ + { + "selected": 3, + "sheets": + [ + { + "buffer": 0, + "file": "make_doc.sh", + "semi_transient": false, + "settings": + { + "buffer_size": 160, + "regions": + { + }, + "selection": + [ + [ + 142, + 142 + ] + ], + "settings": + { + "syntax": "Packages/ShellScript/Shell-Unix-Generic.tmLanguage" + }, + "translation.x": 0.0, + "translation.y": 0.0, + "zoom_level": 1.0 + }, + "stack_index": 2, + "type": "text" + }, + { + "buffer": 1, + "file": "/home/rufu1194/Desktop/guacamole/doc/style.css", + "semi_transient": false, + "settings": + { + "buffer_size": 27215, + "regions": + { + }, + "selection": + [ + [ + 15630, + 15630 + ] + ], + "settings": + { + "syntax": "Packages/CSS/CSS.tmLanguage" + }, + "translation.x": 0.0, + "translation.y": 15832.0, + "zoom_level": 1.0 + }, + "stack_index": 4, + "type": "text" + }, + { + "buffer": 2, + "file": "/home/rufu1194/Desktop/guacamole/doc/header.html", + "semi_transient": false, + "settings": + { + "buffer_size": 4822, + "regions": + { + }, + "selection": + [ + [ + 758, + 758 + ] + ], + "settings": + { + "syntax": "Packages/HTML/HTML.tmLanguage" + }, + "translation.x": 0.0, + "translation.y": 258.0, + "zoom_level": 1.0 + }, + "stack_index": 1, + "type": "text" + }, + { + "buffer": 3, + "file": "/home/rufu1194/Desktop/avango/examples/touch_example/main.py", + "semi_transient": false, + "settings": + { + "buffer_size": 2963, + "regions": + { + }, + "selection": + [ + [ + 0, + 0 + ] + ], + "settings": + { + "syntax": "Packages/Python/Python.tmLanguage" + }, + "translation.x": 0.0, + "translation.y": 596.0, + "zoom_level": 1.0 + }, + "stack_index": 0, + "type": "text" + }, + { + "buffer": 4, + "file": "make.sh", + "semi_transient": false, + "settings": + { + "buffer_size": 284, + "regions": + { + }, + "selection": + [ + [ + 0, + 0 + ] + ], + "settings": + { + "syntax": "Packages/ShellScript/Shell-Unix-Generic.tmLanguage" + }, + "translation.x": 0.0, + "translation.y": 0.0, + "zoom_level": 1.0 + }, + "stack_index": 3, + "type": "text" + } + ] + } + ], + "incremental_find": + { + "height": 25.0 + }, + "input": + { + "height": 31.0 + }, + "layout": + { + "cells": + [ + [ + 0, + 0, + 1, + 1 + ] + ], + "cols": + [ + 0.0, + 1.0 + ], + "rows": + [ + 0.0, + 1.0 + ] + }, + "menu_visible": true, + "output.exec": + { + "height": 112.0 + }, + "output.find_results": + { + "height": 0.0 + }, + "project": "website.sublime-project", + "replace": + { + "height": 46.0 + }, + "save_all_on_build": true, + "select_file": + { + "height": 0.0, + "selected_items": + [ + [ + "data", + "include/gua/databases/Database.hpp" + ], + [ + "unifo", + "include/gua/renderer/Uniform.hpp" + ], + [ + "uber", + "src/gua/renderer/UberShader.cpp" + ], + [ + "pass", + "src/gua/renderer/Pass.cpp" + ], + [ + "gbu", + "src/gua/renderer/GBufferPass.cpp" + ], + [ + "renderer", + "src/gua/renderer/Renderer.cpp" + ], + [ + "pip", + "src/gua/renderer/Pipeline.cpp" + ], + [ + "passc", + "src/gua/renderer/Pass.cpp" + ], + [ + "light", + "src/gua/renderer/LightingPass.cpp" + ], + [ + "shadoe", + "resources/shaders/uber_shaders/lighting/mesh/shadow.vert" + ], + [ + "shadow", + "resources/shaders/uber_shaders/lighting/mesh/shadow.frag" + ], + [ + "mesh", + "resources/shaders/uber_shaders/gbuffer/mesh/mesh.vert" + ], + [ + "mehs", + "resources/shaders/uber_shaders/gbuffer/mesh/mesh.frag" + ], + [ + "pas", + "include/gua/renderer/Pass.hpp" + ], + [ + "sha", + "include/gua/renderer/ShadowMap.hpp" + ], + [ + "gbuffer", + "src/gua/renderer/GBufferPass.cpp" + ], + [ + "mater", + "src/gua/renderer/MaterialLoader.cpp" + ], + [ + "mappin", + "src/gua/renderer/LayerMapping.cpp" + ], + [ + "frus", + "src/gua/renderer/Frustum.cpp" + ], + [ + "seria", + "src/gua/renderer/Serializer.cpp" + ], + [ + "node", + "include/gua/scenegraph/Node.hpp" + ], + [ + "gbuf", + "include/gua/renderer/GBufferPass.hpp" + ], + [ + "camer", + "include/gua/renderer/Camera.hpp" + ], + [ + "shamap", + "src/gua/renderer/ShadowMap.cpp" + ], + [ + "cam", + "include/gua/renderer/Camera.hpp" + ], + [ + "math", + "src/gua/math/math.cpp" + ], + [ + "fru", + "include/gua/renderer/Frustum.hpp" + ], + [ + "gord", + "resources/shaders/uber_shaders/postfx/godrays.vert" + ], + [ + "godr", + "resources/shaders/uber_shaders/postfx/godrays.frag" + ], + [ + "post", + "src/gua/renderer/PostFXPass.cpp" + ], + [ + "p", + "include/gua/renderer/Pass.hpp" + ], + [ + "res", + "src/gua/databases/Resources.cpp" + ], + [ + "ligh", + "src/gua/renderer/LightingPass.cpp" + ], + [ + "messhadow", + "resources/shaders/uber_shaders/lighting/mesh/shadow.vert" + ], + [ + "bound", + "src/gua/math/BoundingBoxAlgo.cpp" + ], + [ + "fr", + "src/gua/renderer/Frustum.cpp" + ], + [ + "gb", + "src/gua/renderer/GBufferPass.cpp" + ], + [ + "seri", + "src/gua/renderer/Serializer.cpp" + ], + [ + "sun", + "include/gua/scenegraph/SunLightNode.hpp" + ], + [ + "node.cpp", + "src/gua/scenegraph/Node.cpp" + ], + [ + "tran", + "include/gua/scenegraph/TransformNode.hpp" + ], + [ + "tra", + "src/gua/scenegraph/TransformNode.cpp" + ], + [ + "sce", + "src/gua/scenegraph/SceneGraph.cpp" + ], + [ + "vis", + "include/gua/scenegraph/NodeVisitor.hpp" + ], + [ + "serial", + "include/gua/renderer/Serializer.hpp" + ], + [ + "fina", + "src/gua/renderer/FinalPass.cpp" + ], + [ + "fulls", + "src/gua/renderer/FullscreenPass.cpp" + ], + [ + "full", + "include/gua/renderer/FullscreenPass.hpp" + ], + [ + "spo", + "include/gua/scenegraph/SpotLightNode.hpp" + ], + [ + "node.h", + "include/gua/scenegraph/Node.hpp" + ], + [ + "tick", + "include/gua/events/Ticker.hpp" + ], + [ + "ticker", + "src/gua/events/Ticker.cpp" + ], + [ + "sign", + "include/gua/events/Signal.hpp" + ], + [ + "unif", + "include/gua/renderer/Uniform.hpp" + ], + [ + "singl", + "include/gua/utils/Singleton.hpp" + ], + [ + "geom", + "include/gua/databases/GeometryDatabase.hpp" + ], + [ + "geome", + "include/gua/scenegraph/GeometryNode.hpp" + ], + [ + "pick", + "include/gua/scenegraph/PickResult.hpp" + ], + [ + "scen", + "include/gua/scenegraph/SceneGraph.hpp" + ], + [ + "no", + "include/gua/scenegraph/Node.hpp" + ], + [ + "node.c", + "src/gua/scenegraph/Node.cpp" + ], + [ + "gbuff", + "src/gua/renderer/GBufferPass.cpp" + ], + [ + "pipe", + "src/gua/renderer/Pipeline.cpp" + ], + [ + "datab", + "include/gua/databases.hpp" + ], + [ + "materia", + "src/gua/renderer/Material.cpp" + ], + [ + "nursh", + "src/gua/renderer/GBufferNURBSUberShader.cpp" + ], + [ + "nuubers", + "src/gua/renderer/GBufferNURBSUberShader.cpp" + ], + [ + "nurb", + "include/gua/renderer/NURBS.hpp" + ], + [ + "igslo", + "src/gua/renderer/nurbs_geometry/import/igs/igs_loader.cpp" + ], + [ + "nudata", + "include/gua/renderer/nurbs_geometry/NURBSData.hpp" + ], + [ + "grap", + "include/gua/scenegraph/SceneGraph.hpp" + ], + [ + "nurbs", + "src/gua/renderer/NURBSLoader.cpp" + ], + [ + "bui", + "src/gua/renderer/BuiltInTextures.cpp" + ], + [ + "buil", + "include/gua/renderer/BuiltInTextures.hpp" + ], + [ + "tic", + "include/gua/events/Ticker.hpp" + ], + [ + "renc", + "include/gua/renderer/RenderClient.hpp" + ], + [ + "ren", + "src/gua/renderer/Renderer.cpp" + ], + [ + "render", + "include/gua/renderer/Renderer.hpp" + ], + [ + "gua", + "src/gua/guacamole.cpp" + ], + [ + "win", + "src/gua/renderer/Window.cpp" + ], + [ + "text2d", + "src/gua/renderer/Texture2D.cpp" + ], + [ + "texc", + "src/gua/renderer/Texture.cpp" + ], + [ + "texture", + "include/gua/renderer/Texture.hpp" + ], + [ + "te", + "src/gua/scenegraph/TexturedQuadNode.cpp" + ], + [ + "mathcp", + "src/gua/math/math.cpp" + ], + [ + "scre", + "include/gua/scenegraph/ScreenNode.hpp" + ], + [ + "nodhpp", + "include/gua/scenegraph/Node.hpp" + ], + [ + "gecp", + "src/gua/scenegraph/GeometryNode.cpp" + ], + [ + "texq", + "src/gua/scenegraph/TexturedQuadNode.cpp" + ], + [ + "textqu", + "include/gua/scenegraph/TexturedQuadNode.hpp" + ], + [ + "built", + "src/gua/renderer/BuiltInTextures.cpp" + ], + [ + "3d", + "src/gua/renderer/Texture3D.cpp" + ], + [ + "text", + "src/gua/renderer/Texture2D.cpp" + ], + [ + "textu", + "src/gua/renderer/Texture.cpp" + ], + [ + "ser", + "include/gua/renderer/Serializer.hpp" + ], + [ + "iter", + "include/gua/scenegraph/Iterator.hpp" + ], + [ + "bou", + "include/gua/math/BoundingBox.hpp" + ], + [ + "rigi", + "src/gua/physics/RigidBodyNode.cpp" + ], + [ + "main", + "examples/big_scenegraph_example/main.cpp" + ], + [ + "lgeomoader", + "include/gua/renderer/GeometryLoader.hpp" + ], + [ + "tria", + "src/gua/physics/TriangleMeshShape.cpp" + ], + [ + "meshlo", + "src/gua/renderer/MeshLoader.cpp" + ], + [ + "trian", + "src/gua/physics/TriangleMeshShape.cpp" + ], + [ + "geomnode", + "src/gua/scenegraph/GeometryNode.cpp" + ], + [ + "coll", + "src/gua/physics/CollisionShapeNode.cpp" + ], + [ + "ubersh", + "src/gua/renderer/UberShaderFactory.cpp" + ], + [ + "ubers", + "src/gua/renderer/UberShader.cpp" + ], + [ + "meshub", + "src/gua/renderer/GBufferMeshUberShader.cpp" + ], + [ + "nurbsu", + "src/gua/renderer/GBufferNURBSUberShader.cpp" + ], + [ + "collisionshapenodevisitor", + "include/gua/physics/CollisionShapeNodeVisitor.hpp" + ], + [ + "phy", + "src/gua/physics/Physics.cpp" + ], + [ + "oilr", + "examples/multipipe_example/OilrigScene.cpp" + ], + [ + "tex", + "include/gua/scenegraph/TexturedQuadNode.hpp" + ], + [ + "scree", + "include/gua/scenegraph/ScreenNode.hpp" + ], + [ + "colli", + "include/gua/physics/CollisionShapeNode.hpp" + ], + [ + "group", + "include/gua/scenegraph/GroupNode.hpp" + ], + [ + "windo", + "include/gua/renderer/Window.hpp" + ], + [ + "point", + "include/gua/scenegraph/PointLightNode.hpp" + ], + [ + "rayn", + "include/gua/scenegraph/RayNode.hpp" + ], + [ + "view", + "include/gua/scenegraph/ViewNode.hpp" + ], + [ + "spot", + "src/gua/scenegraph/SpotLightNode.cpp" + ], + [ + "load", + "include/gua/renderer/LoaderBase.hpp" + ], + [ + "scene", + "include/gua/scenegraph/SceneGraph.hpp" + ], + [ + "inner", + "src/gua/scenegraph/InnerNode.cpp" + ], + [ + "grou", + "src/gua/scenegraph/GroupNode.cpp" + ], + [ + "trav", + "include/gua/scenegraph/TraversalStateStack.hpp" + ], + [ + "dot", + "src/gua/utils/DotGenerator.cpp" + ], + [ + "geomno", + "include/gua/scenegraph/GeometryNode.hpp" + ] + ], + "width": 0.0 + }, + "select_project": + { + "height": 500.0, + "selected_items": + [ + [ + "", + "~/Desktop/avango/scripts/avango.sublime-project" + ] + ], + "width": 380.0 + }, + "select_symbol": + { + "height": 0.0, + "selected_items": + [ + ], + "width": 0.0 + }, + "settings": + { + }, + "show_minimap": true, + "show_open_files": true, + "show_tabs": true, + "side_bar_visible": true, + "side_bar_width": 248.0, + "status_bar_visible": true, + "template_settings": + { + } +} diff --git a/src/gua/events/Ticker.cpp b/src/gua/events/Ticker.cpp index 7d180f1e3..a1da667b7 100644 --- a/src/gua/events/Ticker.cpp +++ b/src/gua/events/Ticker.cpp @@ -38,6 +38,14 @@ namespace gua { delete timer_; } + void Ticker::set_tick_time(double tick_time) { + tick_time_ = tick_time; + } + + double Ticker::get_tick_time() const { + return tick_time_; + } + void Ticker::self_callback(int revents) { async_wait(); From fcfed7c360735dab7fa19fba2d58ade655f5be08 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Thu, 27 Feb 2014 16:22:12 +0100 Subject: [PATCH 127/146] removed obsolete file --- scripts/website.sublime-workspace | 1823 ----------------------------- 1 file changed, 1823 deletions(-) delete mode 100644 scripts/website.sublime-workspace diff --git a/scripts/website.sublime-workspace b/scripts/website.sublime-workspace deleted file mode 100644 index 9349f1b76..000000000 --- a/scripts/website.sublime-workspace +++ /dev/null @@ -1,1823 +0,0 @@ -{ - "auto_complete": - { - "selected_items": - [ - [ - "bac", - "background-color" - ], - [ - "nav", - "navpath" - ], - [ - "re", - "removeClass" - ], - [ - "paddin", - "padding-bottom" - ], - [ - "marg", - "margin-bottom" - ], - [ - "pos", - "position" - ], - [ - "wid", - "width" - ], - [ - "fon", - "font-size" - ], - [ - "sha", - "shadow_map_" - ], - [ - "mes", - "mesh_shader_" - ], - [ - "pr", - "projection_mode" - ], - [ - "vec", - "vec3" - ], - [ - "ve", - "vec3" - ], - [ - "gu", - "gua_out_color" - ], - [ - "sh", - "shadow" - ], - [ - "gua_ge", - "gua_get_position" - ], - [ - "in", - "inputs_" - ], - [ - "ex", - "extends_in_sun_space" - ], - [ - "ca", - "cascade" - ], - [ - "co", - "corner_in_sun_space" - ], - [ - "cr", - "cropped_frustum_corners" - ], - [ - "inv", - "inverse_transform" - ], - [ - "spl", - "split_1" - ], - [ - "crop", - "cropped_clip_near" - ], - [ - "render_", - "render_geometry" - ], - [ - "prin", - "print_shaders" - ], - [ - "n", - "near_plane" - ], - [ - "tp", - "top" - ], - [ - "cl", - "clip_far" - ], - [ - "com", - "compute_position" - ], - [ - "lensE", - "lensEyeR statement" - ], - [ - "len", - "lens" - ], - [ - "le", - "lens" - ], - [ - "fil", - "file" - ], - [ - "file", - "filename" - ], - [ - "new", - "new_passes" - ], - [ - "lo", - "loading_texture_height" - ], - [ - "loa", - "loading_texture_width" - ], - [ - "mon", - "monitor_count" - ], - [ - "las", - "last_left_resolution_" - ], - [ - "on", - "on_window_resize" - ], - [ - "scm", - "scm_core" - ], - [ - "gl", - "glfw_window_" - ], - [ - "mo", - "monitor" - ], - [ - "moi", - "monitor_count" - ], - [ - "mouse", - "mouseMoveEvent function" - ], - [ - "te", - "test_btn statement" - ], - [ - "update", - "updateScene function" - ], - [ - "get_sc", - "get_scaled_world_transform" - ], - [ - "__n", - "__node statement" - ], - [ - "G", - "GuiElement class" - ], - [ - "Tex", - "TexturedQuadNode" - ], - [ - "geom", - "geometry_bbox" - ], - [ - "tra", - "transform" - ], - [ - "dat", - "data" - ], - [ - "da", - "data_" - ], - [ - "de", - "depth_" - ], - [ - "h", - "height_" - ], - [ - "i", - "id" - ], - [ - "di", - "dirty_flags_" - ], - [ - "fal", - "fallback_material" - ], - [ - "nex", - "next_node" - ], - [ - "CMA", - "CMAKE_SOURCE_DIR" - ], - [ - "fi", - "file_name_" - ], - [ - "ma", - "max" - ], - [ - "vis", - "visit_children" - ], - [ - "is", - "is_visible" - ], - [ - "tr", - "transform" - ], - [ - "set", - "set_dirty" - ], - [ - "no", - "node" - ], - [ - "add", - "add_all_geometries" - ], - [ - "chi", - "child_dirty_" - ], - [ - "spo", - "sponza_rb" - ], - [ - "Ro", - "RigidBodyNode" - ], - [ - "dire", - "direction" - ], - [ - "get_", - "get_size" - ], - [ - "node", - "node_name" - ], - [ - "a", - "auto" - ], - [ - "to", - "to_remove" - ], - [ - "ser", - "searched_node" - ], - [ - "po", - "positions" - ], - [ - "st", - "state" - ], - [ - "obe", - "objects" - ], - [ - "fa", - "fail_result" - ], - [ - "set_s", - "set_size" - ], - [ - "pip", - "pipeline_" - ], - [ - "li", - "light_rb" - ], - [ - "make_", - "make_shared" - ], - [ - "con", - "config" - ], - [ - "pa", - "parent" - ], - [ - "dynamic_", - "dynamic_pointer_cast" - ], - [ - "Geometry", - "GeometryNode" - ], - [ - "look", - "lookup_string" - ], - [ - "en", - "endl" - ], - [ - "un", - "unordered_map" - ], - [ - "rea", - "read_directory" - ], - [ - "Oc", - "OculusRift" - ], - [ - "inc", - "include" - ], - [ - "scre", - "screen" - ], - [ - "screen_", - "screen_right" - ], - [ - "screen_r", - "screen_right_it" - ], - [ - "screen_l", - "screen_left_it" - ], - [ - "cre", - "create" - ], - [ - "OCULUS_RIFT_", - "OCULUS_RIFT_RIGHT" - ], - [ - "ret", - "return" - ], - [ - "Con", - "Configuration" - ], - [ - "cur", - "current_directoy_" - ], - [ - "get_main", - "get_main_methods" - ], - [ - "as", - "asString" - ], - [ - "fun", - "functions" - ], - [ - "with", - "with_top_code" - ], - [ - "e", - "endl" - ], - [ - "gua_in", - "gua_in_position" - ], - [ - "gua_in_t", - "gua_in_tangent" - ], - [ - "GB", - "GBUFFER_FRAGMENT_STAGE" - ], - [ - "str", - "string" - ], - [ - "UNI", - "UNIFORM_CUBEMAP" - ], - [ - "main", - "main_pos" - ], - [ - "ge", - "get_name" - ], - [ - "_ta", - "_targetCameraPos" - ], - [ - "_in", - "_initMembers" - ], - [ - "han", - "handles" - ], - [ - "Dis", - "Display3D" - ], - [ - "marke", - "markerInterval" - ] - ] - }, - "buffers": - [ - { - "contents": "#!/bin/sh\n\n# get directory of script and cd to doc folder\nDIR=\"$( cd \"$( dirname \"$0\" )\" && pwd )\"\ncd $DIR/../doc\n\ndoxygen\n\ncp *.css doc/html\ncp *.png doc/html\n", - "file": "make_doc.sh", - "file_size": 160, - "file_write_time": 130379623100925502, - "settings": - { - "buffer_size": 160, - "line_ending": "Unix" - } - }, - { - "file": "/home/rufu1194/Desktop/guacamole/doc/style.css", - "settings": - { - "buffer_size": 27215, - "line_ending": "Unix" - } - }, - { - "contents": "\n\n\n\n\n$projectname: $title\n$title\n\n\n\n\n\n\n\n\n\n$treeview\n$search\n$mathjax\n\n\n
\n\n\n
\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\"Logo\"\n
$projectname\n  $projectnumber\n
\n
$projectbrief
\n
\n
$projectbrief
\n
$searchbox
\n
\n\n", - "file": "/home/rufu1194/Desktop/guacamole/doc/header.html", - "file_size": 4822, - "file_write_time": 130379623093685261, - "settings": - { - "buffer_size": 4822, - "line_ending": "Unix" - } - }, - { - "file": "/home/rufu1194/Desktop/avango/examples/touch_example/main.py", - "settings": - { - "buffer_size": 2963, - "line_ending": "Unix" - } - }, - { - "file": "make.sh", - "settings": - { - "buffer_size": 284, - "line_ending": "Unix" - } - } - ], - "build_system": "", - "command_palette": - { - "height": 375.0, - "selected_items": - [ - [ - "color", - "Color picker" - ], - [ - "colo", - "Color picker" - ], - [ - "ins", - "Package Control: Install Package" - ], - [ - "inst", - "Package Control: Install Package" - ], - [ - "view", - "View: Toggle Menu" - ], - [ - "new", - "Git: New Branch" - ], - [ - "pu", - "Git: Push Current Branch" - ], - [ - "cha", - "Git: Change Branch" - ], - [ - "com", - "Git: Commit" - ], - [ - "ad", - "Git: Add..." - ], - [ - "push", - "Git: Push Current Branch" - ], - [ - "mer", - "Git: Merge Branch" - ], - [ - "chan", - "Git: Change Branch" - ], - [ - "add", - "Git: Add..." - ], - [ - "comm", - "Git: Commit" - ], - [ - "git add", - "Git: Add..." - ], - [ - "git che", - "Git: Checkout Current File" - ], - [ - "git chec", - "Git: Checkout Current File" - ], - [ - "git res", - "Git: Reset (unstage) Current File" - ], - [ - "git a", - "Git: Add..." - ], - [ - "pus", - "Git: Push Current Branch" - ], - [ - "pul", - "Git: Pull Current Branch" - ], - [ - "git:", - "Git: Gui" - ], - [ - "pull", - "Git: Pull Current Branch" - ], - [ - "stas", - "Git: Stash Changes" - ], - [ - "in", - "Package Control: Install Package" - ], - [ - "rem", - "Package Control: Remove Package" - ], - [ - "pac", - "Package Control: Install Package" - ], - [ - "syn co", - "Set Syntax: CoffeeScript" - ], - [ - "pack", - "Package Control: Remove Package" - ], - [ - "re", - "Package Control: Remove Package" - ], - [ - "pa", - "Package Control: Remove Package" - ], - [ - "ena", - "Package Control: Enable Package" - ], - [ - "en", - "Package Control: Enable Package" - ], - [ - "dis", - "Package Control: Disable Package" - ], - [ - "enab", - "Package Control: Enable Package" - ], - [ - "the", - "QuickThemes: next" - ], - [ - "them", - "QuickThemes: next" - ], - [ - "Package Control: ", - "Package Control: Enable Package" - ], - [ - "key", - "Preferences: Key Bindings - User" - ], - [ - "remo", - "Package Control: Remove Package" - ], - [ - "gi", - "Git: Status" - ], - [ - "select", - "Bookmarks: Select All" - ], - [ - "git", - "Git: Status" - ], - [ - "packa", - "Package Control: Remove Package" - ], - [ - "sublmi", - "Preferences: SublimeLinter Settings – Default" - ] - ], - "width": 435.0 - }, - "console": - { - "height": 139.0, - "history": - [ - "a", - "a = 5", - "env", - "ls", - "bash", - "ls" - ] - }, - "distraction_free": - { - "menu_visible": true, - "show_minimap": false, - "show_open_files": false, - "show_tabs": false, - "side_bar_visible": false, - "status_bar_visible": false - }, - "file_history": - [ - "/home/rufu1194/Desktop/guacamole/include/gua/databases/Database.hpp", - "/home/rufu1194/Desktop/guacamole/src/gua/databases/MaterialDatabase.cpp", - "/home/rufu1194/Desktop/guacamole/documentation.md", - "/home/rufu1194/Desktop/guacamole/scripts/start_localhost.sh", - "/home/rufu1194/Desktop/guacamole/_config.yml", - "/opt/guacamole/examples/cluster/multi-user/lib/Navigation.py", - "/opt/guacamole/examples/cluster/multi-user/lib/Device.py", - "/home/rufu1194/simmesimme.github.com/index.md", - "/home/rufu1194/simmesimme.github.com/_config.yml", - "/home/rufu1194/simmesimme.github.com/features.md", - "/home/rufu1194/simmesimme.github.com/installation.md", - "/home/rufu1194/simmesimme.github.com/documentation.md", - "/home/rufu1194/simmesimme.github.com/news.html", - "/home/rufu1194/simmesimme.github.com/_includes/themes/hooligan/page.html", - "/home/rufu1194/Desktop/avango/avango-gua/include/avango/gua/scenegraph/Node.hpp", - "/home/rufu1194/simmesimme.github.com/README.md", - "/home/rufu1194/simmesimme.github.com/.gitignore", - "/home/rufu1194/Desktop/guacamole/doc/Doxyfile", - "/home/rufu1194/simmesimme.github.com/_includes/themes/hooligan/default.html", - "/home/rufu1194/simmesimme.github.com/scripts/start_localhost.sh", - "/home/rufu1194/simmesimme.github.com/assets/themes/hooligan/_sass/style.scss", - "/home/rufu1194/simmesimme.github.com/scripts/website.sublime-project", - "/home/rufu1194/Desktop/guacamole/scripts/guacamole.sublime-project", - "/home/rufu1194/simmesimme.github.com/assets/themes/hooligan/_sass/_base.scss", - "/home/rufu1194/simmesimme.github.com/assets/themes/hooligan/config.rb", - "/home/rufu1194/simmesimme.github.com/scripts/make.sh", - "/home/rufu1194/simmesimme.github.com/assets/themes/hooligan/_sass/config.rb", - "/home/rufu1194/simmesimme.github.com/assets/themes/hooligan/css/darkstrap.css", - "/home/rufu1194/simmesimme.github.com/assets/themes/hooligan/css/pygments.css", - "/home/rufu1194/simmesimme.github.com/assets/themes/hooligan/css/style.css", - "/home/rufu1194/simmesimme.github.com/_theme_packages/hooligan/README.md", - "/home/rufu1194/simmesimme.github.com/_theme_packages/hooligan/assets/themes/hooligan/css/style.css", - "/home/rufu1194/simmesimme.github.com/_theme_packages/hooligan/manifest.yml", - "/home/rufu1194/simmesimme.github.com/_theme_packages/hooligan/packager.yml", - "/home/rufu1194/simmesimme.github.com/_includes/themes/hooligan/settings.yml", - "/home/rufu1194/simmesimme.github.com/_includes/themes/hooligan/post.html", - "/home/rufu1194/simmesimme.github.com/_theme_packages/hooligan/_includes/themes/hooligan/page.html", - "/home/rufu1194/simmesimme.github.com/_theme_packages/hooligan/_includes/themes/hooligan/default.html", - "/home/rufu1194/simmesimme.github.com/_theme_packages/hooligan/_includes/themes/hooligan/post.html", - "/home/rufu1194/simmesimme.github.com/_posts/2014-02-20-hello-world.md", - "/home/rufu1194/simmesimme.github.com/_posts/core-samples/test.md", - "/home/rufu1194/Desktop/guacamole/stylesheets/stylesheet.css", - "/home/rufu1194/Desktop/guacamole/index.html", - "/home/rufu1194/.config/sublime-text-3/Packages/User/Default (Linux).sublime-keymap", - "/home/rufu1194/.config/sublime-text-3/Packages/User/Preferences.sublime-settings", - "/home/rufu1194/.config/sublime-text-3/Packages/Schemr/Default (Linux).sublime-keymap", - "/home/rufu1194/Desktop/guacamole/resources/shaders/uber_shaders/lighting/mesh/shadow.vert", - "/home/rufu1194/Desktop/guacamole/resources/shaders/uber_shaders/lighting/mesh/shadow.frag", - "/home/rufu1194/Desktop/guacamole/src/gua/renderer/LightingPass.cpp", - "/home/rufu1194/Desktop/guacamole/include/gua/renderer/LightingPass.hpp", - "/home/rufu1194/Desktop/guacamole/include/gua/renderer/ShadowMap.hpp", - "/home/rufu1194/Desktop/guacamole/include/gua/renderer/Uniform.hpp", - "/home/rufu1194/Desktop/guacamole/src/gua/renderer/UberShader.cpp", - "/home/rufu1194/Desktop/guacamole/src/gua/renderer/GBufferPass.cpp", - "/home/rufu1194/Desktop/guacamole/src/gua/renderer/Pass.cpp", - "/home/rufu1194/Desktop/guacamole/src/gua/renderer/Pipeline.cpp", - "/home/rufu1194/Desktop/guacamole/include/gua/renderer/DisplayData.hpp", - "/home/rufu1194/Desktop/guacamole/src/gua/renderer/DisplayData.cpp", - "/home/rufu1194/Desktop/guacamole/src/gua/utils/DotGenerator.cpp", - "/home/rufu1194/Desktop/guacamole/include/gua/renderer/Pass.hpp", - "/home/rufu1194/Desktop/guacamole/include/gua/renderer/GBufferPass.hpp", - "/home/rufu1194/Desktop/guacamole/include/gua/renderer/Renderer.hpp", - "/home/rufu1194/Desktop/guacamole/include/gua/renderer.hpp", - "/home/rufu1194/Desktop/guacamole/src/gua/renderer/Renderer.cpp", - "/home/rufu1194/Desktop/guacamole/include/gua/renderer/BufferDescriptions.hpp", - "/home/rufu1194/Desktop/guacamole/include/gua/renderer/nurbs_geometry/tml/oriented_boundingbox_partial_derivative_policy.hpp", - "/home/rufu1194/Desktop/guacamole/src/gua/databases/Resources.cpp", - "/home/rufu1194/Desktop/guacamole/resources/shaders/uber_shaders/gbuffer/mesh/mesh.frag", - "/home/rufu1194/Desktop/guacamole/resources/shaders/uber_shaders/gbuffer/mesh/mesh.vert", - "/home/rufu1194/Desktop/guacamole/resources/shaders/uber_shaders/lighting/lighting.vert", - "/home/rufu1194/Desktop/guacamole/resources/shaders/uber_shaders/lighting/lighting.frag", - "/home/rufu1194/Desktop/guacamole/include/gua/renderer/Pipeline.hpp", - "/home/rufu1194/Desktop/guacamole/include/gua/renderer/ShadowMapNURBSShader.hpp", - "/home/rufu1194/Desktop/guacamole/src/gua/renderer/MeshLoader.cpp", - "/home/rufu1194/Desktop/guacamole/include/gua/renderer/MeshLoader.hpp", - "/home/rufu1194/Desktop/guacamole/src/gua/renderer/Mesh.cpp", - "/home/rufu1194/Desktop/guacamole/include/gua/renderer/Mesh.hpp", - "/home/rufu1194/Desktop/guacamole/src/gua/math/math.cpp", - "/home/rufu1194/Desktop/guacamole/include/gua/utils/Mask.hpp", - "/home/rufu1194/Desktop/guacamole/include/gua/memory.hpp", - "/home/rufu1194/Desktop/guacamole/include/gua/renderer/ShadingModel.hpp", - "/home/rufu1194/Desktop/guacamole/include/gua/renderer/ShaderStage.hpp", - "/home/rufu1194/Desktop/guacamole/include/gua/renderer/ShaderProgram.hpp", - "/home/rufu1194/Desktop/guacamole/resources/shaders/uber_shaders/common/get_position.glsl", - "/home/rufu1194/Desktop/guacamole/resources/shaders/uber_shaders/common/get_depth.glsl", - "/home/rufu1194/Desktop/guacamole/src/gua/renderer/Frustum.cpp", - "/home/rufu1194/Desktop/guacamole/src/gua/renderer/Serializer.cpp", - "/home/rufu1194/Desktop/guacamole/src/gua/renderer/ShadowMap.cpp", - "/home/rufu1194/Desktop/guacamole/src/gua/renderer/LayerMapping.cpp", - "/home/rufu1194/Desktop/guacamole/resources/shaders/uber_shaders/final/final.frag", - "/home/rufu1194/Desktop/guacamole/src/gua/renderer/MaterialLoader.cpp", - "/home/rufu1194/Desktop/guacamole/include/gua/renderer/SerializedScene.hpp", - "/home/rufu1194/Desktop/guacamole/include/gua/scenegraph/Node.hpp", - "/home/rufu1194/Desktop/guacamole/src/gua/renderer/ShadingModel.cpp", - "/home/rufu1194/Desktop/guacamole/src/gua/scenegraph/SceneGraph.cpp", - "/home/rufu1194/Desktop/guacamole/src/gua/renderer/MaterialDescription.cpp", - "/home/rufu1194/Desktop/guacamole/include/gua/renderer/MaterialLoader.hpp", - "/home/rufu1194/Desktop/guacamole/include/gua/renderer/LayerMapping.hpp", - "/home/rufu1194/Desktop/guacamole/include/gua/renderer/Frustum.hpp", - "/home/rufu1194/Desktop/guacamole/src/gua/renderer/GBuffer.cpp", - "/home/rufu1194/Desktop/guacamole/include/gua/renderer/Serializer.hpp", - "/home/rufu1194/Desktop/guacamole/include/gua/renderer/SerializedNode.hpp", - "/home/rufu1194/Desktop/avango/examples/zmq_distribution/Readme.txt", - "/home/rufu1194/Desktop/avango/examples/zmq_distribution/start-server.sh", - "/home/rufu1194/Desktop/avango/examples/zmq_distribution/start-client.sh", - "/home/rufu1194/Desktop/avango/examples/zmq_distribution/simpleviewer-srv.py", - "/home/rufu1194/Desktop/avango/examples/zmq_distribution/simpleviewer-clnt.py", - "/home/rufu1194/Desktop/guacamole/src/gua/renderer/PostFXPass.cpp", - "/home/rufu1194/Desktop/guacamole/include/gua/renderer/Camera.hpp", - "/home/rufu1194/Desktop/guacamole/resources/shaders/uber_shaders/postfx/godrays.vert", - "/home/rufu1194/Desktop/guacamole/src/gua/physics/CollisionShapeNode.cpp", - "/home/rufu1194/Desktop/guacamole/resources/shaders/uber_shaders/postfx/godrays.frag", - "/home/rufu1194/Desktop/guacamole/include/gua/renderer/PostFXPass.hpp", - "/home/rufu1194/Desktop/guacamole/include/gua/math/math.hpp", - "/home/rufu1194/Desktop/guacamole/plugins/guacamole-oculus/include/gua/OculusRift.hpp", - "/home/rufu1194/Desktop/guacamole/resources/shaders/uber_shaders/common/get_material_id.glsl", - "/home/rufu1194/Desktop/guacamole/include/gua/utils/Directory.hpp", - "/home/rufu1194/Desktop/guacamole/include/gua/scenegraph/PointLightNode.hpp", - "/home/rufu1194/Desktop/guacamole/src/gua/renderer/LightingUberShader.cpp", - "/home/rufu1194/Desktop/guacamole/include/gua/renderer/LightingUberShader.hpp", - "/home/rufu1194/Desktop/guacamole/include/gua/databases/Resources.hpp", - "/home/rufu1194/Desktop/guacamole/include/gua/renderer/LoaderBase.hpp", - "/home/rufu1194/Desktop/guacamole/src/gua/math/BoundingBoxAlgo.cpp", - "/home/rufu1194/Desktop/guacamole/include/gua/renderer/GBuffer.hpp", - "/home/rufu1194/Desktop/guacamole/src/gua/renderer/ShadowMapMeshShader.cpp", - "/home/rufu1194/Desktop/guacamole/include/gua/renderer/ShadowMapMeshShader.hpp", - "/home/rufu1194/Desktop/guacamole/src/gua/renderer/ShadowMapNURBSShader.cpp", - "/home/rufu1194/Desktop/guacamole/make" - ], - "find": - { - "height": 35.0 - }, - "find_in_files": - { - "height": 93.0, - "where_history": - [ - "", - "/home/rufu1194/Desktop/guacamole/include/gua", - "", - "/home/rufu1194/Desktop/guacamole/src", - "/home/rufu1194/Desktop/guacamole", - "/home/rufu1194/Desktop/guacamole/src", - "/home/rufu1194/Desktop/guacamole/include", - "/home/rufu1194/Desktop/guacamole/src" - ] - }, - "find_state": - { - "case_sensitive": false, - "find_history": - [ - "indexvalue", - "memitem", - "'", - "indexkey", - "key", - "pills", - "body", - "incl", - "News", - "Advent", - "header", - "head", - "page-h", - "alt", - "shader.", - "rendering", - "current_scenes_[0]", - "mesh_s", - "global_clipping_plane", - "enable_global_clipping_plane", - "/sc", - "screen", - "Screen", - "screen", - "render_go", - "gua_get_position()", - "shadow_pos", - "gua_get_shadow", - "render_godrays", - "godr", - "result", - "vec3", - "0", - "vec3", - "push_back", - "bbox.max", - "min", - ".z", - ".y", - ".x", - "tmp", - "camera", - "camera_transform", - "gua_light_shadow_map_projection_view_matrix", - "shadow_map_", - "LightingPass", - "shadow_map_", - "frustum[", - "result", - "compute_frustum", - "frustum", - "Frustum", - "camera_position_", - "view_", - "planes_", - "scal_type", - "update_b", - "update", - "spot", - "render_godrays", - "CalculateLightType", - "godra", - "rasterizer_state_", - "spot_light_count", - "SpotLightNode", - "gua_calculate_point_light", - "SpotLightNode", - "GUA_SPOT_LIGHT_NODE_HPP", - "= 0", - "head", - "236", - "GroupNode", - "convert_osg_to_gua", - "camera", - "Camera", - "ViewNode", - "gua_textured_quad", - "new_passes", - "passes_", - "reload_all", - "samplerBuffer", - "trim_pointdata", - "trim_curvedata", - "trim_curvelist", - "trim_contourlist", - "trim_partition", - "uniform", - "trim_partition", - "attribute_texture", - "parameter_texture", - "nurbs", - "cout", - "max_screen_error", - "new NURBS", - "left", - "resolution", - "left", - "glfw_LIBRARIES", - "glfw_INCLUDE_DIRS", - "GLFW", - "CUDA", - "GLFW_INCLUDE_DIR", - "GLFW_LIBRARY", - "filena", - "request_GLFW_search_directories", - "find_GLFW", - "GUACAMOLE", - "my_constructor", - "Pipeline", - "floo", - "Stones", - "scene", - "__view", - "size", - "btn", - "__view", - "__image", - "initUI", - "__updateTexture", - "__scene", - "navigation", - "\"\\n", - "needs_update", - "textures_.size() <= context.id || textures_[context.id] == 0 || dirty_flags_[context.id]", - "cout", - "####", - "GroupNode", - ");\n" - ], - "highlight": true, - "in_selection": false, - "preserve_case": false, - "regex": false, - "replace_history": - [ - "TransformNode", - "math::mat4::Zero()", - "@_", - " @", - "", - "# ===========", - "this.", - "@", - "----------------------------------------------------------------------------", - "this.", - "@_my", - "" - ], - "reverse": false, - "show_context": true, - "use_buffer2": true, - "whole_word": false, - "wrap": true - }, - "groups": - [ - { - "selected": 3, - "sheets": - [ - { - "buffer": 0, - "file": "make_doc.sh", - "semi_transient": false, - "settings": - { - "buffer_size": 160, - "regions": - { - }, - "selection": - [ - [ - 142, - 142 - ] - ], - "settings": - { - "syntax": "Packages/ShellScript/Shell-Unix-Generic.tmLanguage" - }, - "translation.x": 0.0, - "translation.y": 0.0, - "zoom_level": 1.0 - }, - "stack_index": 2, - "type": "text" - }, - { - "buffer": 1, - "file": "/home/rufu1194/Desktop/guacamole/doc/style.css", - "semi_transient": false, - "settings": - { - "buffer_size": 27215, - "regions": - { - }, - "selection": - [ - [ - 15630, - 15630 - ] - ], - "settings": - { - "syntax": "Packages/CSS/CSS.tmLanguage" - }, - "translation.x": 0.0, - "translation.y": 15832.0, - "zoom_level": 1.0 - }, - "stack_index": 4, - "type": "text" - }, - { - "buffer": 2, - "file": "/home/rufu1194/Desktop/guacamole/doc/header.html", - "semi_transient": false, - "settings": - { - "buffer_size": 4822, - "regions": - { - }, - "selection": - [ - [ - 758, - 758 - ] - ], - "settings": - { - "syntax": "Packages/HTML/HTML.tmLanguage" - }, - "translation.x": 0.0, - "translation.y": 258.0, - "zoom_level": 1.0 - }, - "stack_index": 1, - "type": "text" - }, - { - "buffer": 3, - "file": "/home/rufu1194/Desktop/avango/examples/touch_example/main.py", - "semi_transient": false, - "settings": - { - "buffer_size": 2963, - "regions": - { - }, - "selection": - [ - [ - 0, - 0 - ] - ], - "settings": - { - "syntax": "Packages/Python/Python.tmLanguage" - }, - "translation.x": 0.0, - "translation.y": 596.0, - "zoom_level": 1.0 - }, - "stack_index": 0, - "type": "text" - }, - { - "buffer": 4, - "file": "make.sh", - "semi_transient": false, - "settings": - { - "buffer_size": 284, - "regions": - { - }, - "selection": - [ - [ - 0, - 0 - ] - ], - "settings": - { - "syntax": "Packages/ShellScript/Shell-Unix-Generic.tmLanguage" - }, - "translation.x": 0.0, - "translation.y": 0.0, - "zoom_level": 1.0 - }, - "stack_index": 3, - "type": "text" - } - ] - } - ], - "incremental_find": - { - "height": 25.0 - }, - "input": - { - "height": 31.0 - }, - "layout": - { - "cells": - [ - [ - 0, - 0, - 1, - 1 - ] - ], - "cols": - [ - 0.0, - 1.0 - ], - "rows": - [ - 0.0, - 1.0 - ] - }, - "menu_visible": true, - "output.exec": - { - "height": 112.0 - }, - "output.find_results": - { - "height": 0.0 - }, - "project": "website.sublime-project", - "replace": - { - "height": 46.0 - }, - "save_all_on_build": true, - "select_file": - { - "height": 0.0, - "selected_items": - [ - [ - "data", - "include/gua/databases/Database.hpp" - ], - [ - "unifo", - "include/gua/renderer/Uniform.hpp" - ], - [ - "uber", - "src/gua/renderer/UberShader.cpp" - ], - [ - "pass", - "src/gua/renderer/Pass.cpp" - ], - [ - "gbu", - "src/gua/renderer/GBufferPass.cpp" - ], - [ - "renderer", - "src/gua/renderer/Renderer.cpp" - ], - [ - "pip", - "src/gua/renderer/Pipeline.cpp" - ], - [ - "passc", - "src/gua/renderer/Pass.cpp" - ], - [ - "light", - "src/gua/renderer/LightingPass.cpp" - ], - [ - "shadoe", - "resources/shaders/uber_shaders/lighting/mesh/shadow.vert" - ], - [ - "shadow", - "resources/shaders/uber_shaders/lighting/mesh/shadow.frag" - ], - [ - "mesh", - "resources/shaders/uber_shaders/gbuffer/mesh/mesh.vert" - ], - [ - "mehs", - "resources/shaders/uber_shaders/gbuffer/mesh/mesh.frag" - ], - [ - "pas", - "include/gua/renderer/Pass.hpp" - ], - [ - "sha", - "include/gua/renderer/ShadowMap.hpp" - ], - [ - "gbuffer", - "src/gua/renderer/GBufferPass.cpp" - ], - [ - "mater", - "src/gua/renderer/MaterialLoader.cpp" - ], - [ - "mappin", - "src/gua/renderer/LayerMapping.cpp" - ], - [ - "frus", - "src/gua/renderer/Frustum.cpp" - ], - [ - "seria", - "src/gua/renderer/Serializer.cpp" - ], - [ - "node", - "include/gua/scenegraph/Node.hpp" - ], - [ - "gbuf", - "include/gua/renderer/GBufferPass.hpp" - ], - [ - "camer", - "include/gua/renderer/Camera.hpp" - ], - [ - "shamap", - "src/gua/renderer/ShadowMap.cpp" - ], - [ - "cam", - "include/gua/renderer/Camera.hpp" - ], - [ - "math", - "src/gua/math/math.cpp" - ], - [ - "fru", - "include/gua/renderer/Frustum.hpp" - ], - [ - "gord", - "resources/shaders/uber_shaders/postfx/godrays.vert" - ], - [ - "godr", - "resources/shaders/uber_shaders/postfx/godrays.frag" - ], - [ - "post", - "src/gua/renderer/PostFXPass.cpp" - ], - [ - "p", - "include/gua/renderer/Pass.hpp" - ], - [ - "res", - "src/gua/databases/Resources.cpp" - ], - [ - "ligh", - "src/gua/renderer/LightingPass.cpp" - ], - [ - "messhadow", - "resources/shaders/uber_shaders/lighting/mesh/shadow.vert" - ], - [ - "bound", - "src/gua/math/BoundingBoxAlgo.cpp" - ], - [ - "fr", - "src/gua/renderer/Frustum.cpp" - ], - [ - "gb", - "src/gua/renderer/GBufferPass.cpp" - ], - [ - "seri", - "src/gua/renderer/Serializer.cpp" - ], - [ - "sun", - "include/gua/scenegraph/SunLightNode.hpp" - ], - [ - "node.cpp", - "src/gua/scenegraph/Node.cpp" - ], - [ - "tran", - "include/gua/scenegraph/TransformNode.hpp" - ], - [ - "tra", - "src/gua/scenegraph/TransformNode.cpp" - ], - [ - "sce", - "src/gua/scenegraph/SceneGraph.cpp" - ], - [ - "vis", - "include/gua/scenegraph/NodeVisitor.hpp" - ], - [ - "serial", - "include/gua/renderer/Serializer.hpp" - ], - [ - "fina", - "src/gua/renderer/FinalPass.cpp" - ], - [ - "fulls", - "src/gua/renderer/FullscreenPass.cpp" - ], - [ - "full", - "include/gua/renderer/FullscreenPass.hpp" - ], - [ - "spo", - "include/gua/scenegraph/SpotLightNode.hpp" - ], - [ - "node.h", - "include/gua/scenegraph/Node.hpp" - ], - [ - "tick", - "include/gua/events/Ticker.hpp" - ], - [ - "ticker", - "src/gua/events/Ticker.cpp" - ], - [ - "sign", - "include/gua/events/Signal.hpp" - ], - [ - "unif", - "include/gua/renderer/Uniform.hpp" - ], - [ - "singl", - "include/gua/utils/Singleton.hpp" - ], - [ - "geom", - "include/gua/databases/GeometryDatabase.hpp" - ], - [ - "geome", - "include/gua/scenegraph/GeometryNode.hpp" - ], - [ - "pick", - "include/gua/scenegraph/PickResult.hpp" - ], - [ - "scen", - "include/gua/scenegraph/SceneGraph.hpp" - ], - [ - "no", - "include/gua/scenegraph/Node.hpp" - ], - [ - "node.c", - "src/gua/scenegraph/Node.cpp" - ], - [ - "gbuff", - "src/gua/renderer/GBufferPass.cpp" - ], - [ - "pipe", - "src/gua/renderer/Pipeline.cpp" - ], - [ - "datab", - "include/gua/databases.hpp" - ], - [ - "materia", - "src/gua/renderer/Material.cpp" - ], - [ - "nursh", - "src/gua/renderer/GBufferNURBSUberShader.cpp" - ], - [ - "nuubers", - "src/gua/renderer/GBufferNURBSUberShader.cpp" - ], - [ - "nurb", - "include/gua/renderer/NURBS.hpp" - ], - [ - "igslo", - "src/gua/renderer/nurbs_geometry/import/igs/igs_loader.cpp" - ], - [ - "nudata", - "include/gua/renderer/nurbs_geometry/NURBSData.hpp" - ], - [ - "grap", - "include/gua/scenegraph/SceneGraph.hpp" - ], - [ - "nurbs", - "src/gua/renderer/NURBSLoader.cpp" - ], - [ - "bui", - "src/gua/renderer/BuiltInTextures.cpp" - ], - [ - "buil", - "include/gua/renderer/BuiltInTextures.hpp" - ], - [ - "tic", - "include/gua/events/Ticker.hpp" - ], - [ - "renc", - "include/gua/renderer/RenderClient.hpp" - ], - [ - "ren", - "src/gua/renderer/Renderer.cpp" - ], - [ - "render", - "include/gua/renderer/Renderer.hpp" - ], - [ - "gua", - "src/gua/guacamole.cpp" - ], - [ - "win", - "src/gua/renderer/Window.cpp" - ], - [ - "text2d", - "src/gua/renderer/Texture2D.cpp" - ], - [ - "texc", - "src/gua/renderer/Texture.cpp" - ], - [ - "texture", - "include/gua/renderer/Texture.hpp" - ], - [ - "te", - "src/gua/scenegraph/TexturedQuadNode.cpp" - ], - [ - "mathcp", - "src/gua/math/math.cpp" - ], - [ - "scre", - "include/gua/scenegraph/ScreenNode.hpp" - ], - [ - "nodhpp", - "include/gua/scenegraph/Node.hpp" - ], - [ - "gecp", - "src/gua/scenegraph/GeometryNode.cpp" - ], - [ - "texq", - "src/gua/scenegraph/TexturedQuadNode.cpp" - ], - [ - "textqu", - "include/gua/scenegraph/TexturedQuadNode.hpp" - ], - [ - "built", - "src/gua/renderer/BuiltInTextures.cpp" - ], - [ - "3d", - "src/gua/renderer/Texture3D.cpp" - ], - [ - "text", - "src/gua/renderer/Texture2D.cpp" - ], - [ - "textu", - "src/gua/renderer/Texture.cpp" - ], - [ - "ser", - "include/gua/renderer/Serializer.hpp" - ], - [ - "iter", - "include/gua/scenegraph/Iterator.hpp" - ], - [ - "bou", - "include/gua/math/BoundingBox.hpp" - ], - [ - "rigi", - "src/gua/physics/RigidBodyNode.cpp" - ], - [ - "main", - "examples/big_scenegraph_example/main.cpp" - ], - [ - "lgeomoader", - "include/gua/renderer/GeometryLoader.hpp" - ], - [ - "tria", - "src/gua/physics/TriangleMeshShape.cpp" - ], - [ - "meshlo", - "src/gua/renderer/MeshLoader.cpp" - ], - [ - "trian", - "src/gua/physics/TriangleMeshShape.cpp" - ], - [ - "geomnode", - "src/gua/scenegraph/GeometryNode.cpp" - ], - [ - "coll", - "src/gua/physics/CollisionShapeNode.cpp" - ], - [ - "ubersh", - "src/gua/renderer/UberShaderFactory.cpp" - ], - [ - "ubers", - "src/gua/renderer/UberShader.cpp" - ], - [ - "meshub", - "src/gua/renderer/GBufferMeshUberShader.cpp" - ], - [ - "nurbsu", - "src/gua/renderer/GBufferNURBSUberShader.cpp" - ], - [ - "collisionshapenodevisitor", - "include/gua/physics/CollisionShapeNodeVisitor.hpp" - ], - [ - "phy", - "src/gua/physics/Physics.cpp" - ], - [ - "oilr", - "examples/multipipe_example/OilrigScene.cpp" - ], - [ - "tex", - "include/gua/scenegraph/TexturedQuadNode.hpp" - ], - [ - "scree", - "include/gua/scenegraph/ScreenNode.hpp" - ], - [ - "colli", - "include/gua/physics/CollisionShapeNode.hpp" - ], - [ - "group", - "include/gua/scenegraph/GroupNode.hpp" - ], - [ - "windo", - "include/gua/renderer/Window.hpp" - ], - [ - "point", - "include/gua/scenegraph/PointLightNode.hpp" - ], - [ - "rayn", - "include/gua/scenegraph/RayNode.hpp" - ], - [ - "view", - "include/gua/scenegraph/ViewNode.hpp" - ], - [ - "spot", - "src/gua/scenegraph/SpotLightNode.cpp" - ], - [ - "load", - "include/gua/renderer/LoaderBase.hpp" - ], - [ - "scene", - "include/gua/scenegraph/SceneGraph.hpp" - ], - [ - "inner", - "src/gua/scenegraph/InnerNode.cpp" - ], - [ - "grou", - "src/gua/scenegraph/GroupNode.cpp" - ], - [ - "trav", - "include/gua/scenegraph/TraversalStateStack.hpp" - ], - [ - "dot", - "src/gua/utils/DotGenerator.cpp" - ], - [ - "geomno", - "include/gua/scenegraph/GeometryNode.hpp" - ] - ], - "width": 0.0 - }, - "select_project": - { - "height": 500.0, - "selected_items": - [ - [ - "", - "~/Desktop/avango/scripts/avango.sublime-project" - ] - ], - "width": 380.0 - }, - "select_symbol": - { - "height": 0.0, - "selected_items": - [ - ], - "width": 0.0 - }, - "settings": - { - }, - "show_minimap": true, - "show_open_files": true, - "show_tabs": true, - "side_bar_visible": true, - "side_bar_width": 248.0, - "status_bar_visible": true, - "template_settings": - { - } -} From a53c7e572826c2ba777227448b1e17c16b80f5e0 Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Mon, 3 Mar 2014 13:14:29 +0100 Subject: [PATCH 128/146] Fix include path. --- src/gua/physics/TriangleMeshShape.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gua/physics/TriangleMeshShape.cpp b/src/gua/physics/TriangleMeshShape.cpp index 1f77a89e2..85efc951e 100644 --- a/src/gua/physics/TriangleMeshShape.cpp +++ b/src/gua/physics/TriangleMeshShape.cpp @@ -32,7 +32,7 @@ // external headers #include -#include +#include namespace gua { namespace physics { From f8905e7f453d197e699ea2adef2090b242d6b6a2 Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Mon, 3 Mar 2014 13:15:16 +0100 Subject: [PATCH 129/146] Use find_package to search for bullet. --- CMakeLists.txt | 4 +- cmake/modules/FindBullet.cmake | 148 --------------------------------- 2 files changed, 3 insertions(+), 149 deletions(-) delete mode 100644 cmake/modules/FindBullet.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index f59e79c21..1d2931e3d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,7 +42,9 @@ include(define_macros) include(find_compiler) include(FindSchism) include(FindBoost) -include(FindBullet) + +set (BULLET_ROOT "/opt/bullet/dist-2.81" CACHE PATH "Set to your bullet install path.") +find_package(Bullet) find_package(Json REQUIRED) set(LIBS diff --git a/cmake/modules/FindBullet.cmake b/cmake/modules/FindBullet.cmake deleted file mode 100644 index 4971205ed..000000000 --- a/cmake/modules/FindBullet.cmake +++ /dev/null @@ -1,148 +0,0 @@ -SET(BULLET_INCLUDE_SEARCH_DIRS - ${GLOBAL_EXT_DIR}/inc/bullet - ${BULLET_INCLUDE_SEARCH_DIR} - ${BULLET_INCLUDE_DIRS} - /opt/bullet/current -) - -SET(BULLET_LIBRARY_SEARCH_DIRS - ${GLOBAL_EXT_DIR}/lib - ${BULLET_LIBRARY_SEARCH_DIR} - ${BULLET_LIBRARY_DIRS} - /opt/bullet/current/bullet-build -) - -############################################################################## -# feedback to provide user-defined paths to search for bullet -############################################################################## -MACRO (request_bullet_search_directories) - - IF ( NOT BULLET_INCLUDE_DIRS AND NOT BULLET_LIBRARY_DIRS ) - SET(BULLET_INCLUDE_SEARCH_DIR "Please provide bullet include path." CACHE PATH "path to bullet headers.") - SET(BULLET_LIBRARY_SEARCH_DIR "Please provide bullet library path." CACHE PATH "path to bullet libraries.") - MESSAGE(FATAL_ERROR "find_bullet.cmake: unable to find bullet.") - ENDIF ( NOT BULLET_INCLUDE_DIRS AND NOT BULLET_LIBRARY_DIRS ) - - IF ( NOT BULLET_INCLUDE_DIRS ) - SET(BULLET_INCLUDE_SEARCH_DIR "Please provide bullet include path." CACHE PATH "path to bullet headers.") - MESSAGE(FATAL_ERROR "find_bullet.cmake: unable to find bullet headers.") - ELSE ( NOT BULLET_INCLUDE_DIRS ) - UNSET(BULLET_INCLUDE_SEARCH_DIR CACHE) - ENDIF ( NOT BULLET_INCLUDE_DIRS ) - - IF ( NOT BULLET_LIBRARY_DIRS ) - SET(BULLET_LIBRARY_SEARCH_DIR "Please provide bullet library path." CACHE PATH "path to bullet libraries.") - MESSAGE(FATAL_ERROR "find_bullet.cmake: unable to find bullet libraries.") - ELSE ( NOT BULLET_LIBRARY_DIRS ) - UNSET(BULLET_LIBRARY_SEARCH_DIR CACHE) - ENDIF ( NOT BULLET_LIBRARY_DIRS ) - -ENDMACRO (request_bullet_search_directories) - -############################################################################## -# search -############################################################################## -message(STATUS "-- checking for BULLET") - -IF (NOT BULLET_INCLUDE_DIRS) - - SET(_BULLET_FOUND_INC_DIRS "") - - FOREACH(_SEARCH_DIR ${BULLET_INCLUDE_SEARCH_DIRS}) - FIND_PATH(_CUR_SEARCH - NAMES src/btBulletDynamicsCommon.h - PATHS ${_SEARCH_DIR} - NO_DEFAULT_PATH) - IF (_CUR_SEARCH) - LIST(APPEND _BULLET_FOUND_INC_DIRS ${_CUR_SEARCH}) - ENDIF(_CUR_SEARCH) - SET(_CUR_SEARCH _CUR_SEARCH-NOTFOUND CACHE INTERNAL "internal use") - ENDFOREACH(_SEARCH_DIR ${BULLET_INCLUDE_SEARCH_DIRS}) - - IF (NOT _BULLET_FOUND_INC_DIRS) - request_bullet_search_directories() - ENDIF (NOT _BULLET_FOUND_INC_DIRS) - - FOREACH(_INC_DIR ${_BULLET_FOUND_INC_DIRS}) - LIST(APPEND _BULLET_INCLUDE_DIRS ${_INC_DIR}/src) - LIST(APPEND _BULLET_INCLUDE_DIRS ${_INC_DIR}/Extras/HACD) - ENDFOREACH(_INC_DIR ${_BOOST_FOUND_INC_DIRS}) - - IF (_BULLET_FOUND_INC_DIRS) - SET(BULLET_INCLUDE_DIRS ${_BULLET_INCLUDE_DIRS} CACHE PATH "paths to bullet headers.") - ENDIF (_BULLET_FOUND_INC_DIRS) - -ENDIF(NOT BULLET_INCLUDE_DIRS) - - -IF(UNIX) - SET(BULLET_DYNAMICS_LIB_SUFFIX "src/BulletDynamics/") - SET(BULLET_COLLISION_LIB_SUFFIX "src/BulletCollision/") - SET(BULLET_LINEAR_MATH_LIB_SUFFIX "src/LinearMath/") - SET(BULLET_HACD_LIB_SUFFIX "Extras/HACD/") - SET(BULLET_DYNAMICS_LIB "libBulletDynamics.so") - SET(BULLET_COLLISION_LIB "libBulletCollision.so") - SET(BULLET_LINEAR_MATH_LIB "libLinearMath.so") - SET(BULLET_HACD_LIB "libHACD.so") -ELSEIF(WIN32) - SET(BULLET_DYNAMICS_LIB_SUFFIX "") - SET(BULLET_COLLISION_LIB_SUFFIX "") - SET(BULLET_LINEAR_MATH_LIB_SUFFIX "") - SET(BULLET_HACD_LIB_SUFFIX "") - SET(BULLET_DYNAMICS_LIB "BulletDynamics.lib") - SET(BULLET_COLLISION_LIB "BulletCollision.lib") - SET(BULLET_LINEAR_MATH_LIB "LinearMath.lib") - SET(BULLET_HACD_LIB "HACD.lib") -ENDIF(UNIX) - - -IF ( BULLET_INCLUDE_DIRS - AND NOT BULLET_LIBRARIES) - - SET(_BULLET_FOUND_LIB_DIR "") - SET(_BULLET_POSTFIX "") - - FOREACH(_SEARCH_DIR ${BULLET_LIBRARY_SEARCH_DIRS}) - FIND_PATH(_CUR_SEARCH - NAMES ${BULLET_DYNAMICS_LIB} - PATHS ${_SEARCH_DIR} - PATH_SUFFIXES debug release ${BULLET_DYNAMICS_LIB_SUFFIX} - NO_DEFAULT_PATH) - IF (_CUR_SEARCH) - LIST(APPEND _BULLET_FOUND_LIB_DIR ${_SEARCH_DIR}) - ENDIF(_CUR_SEARCH) - SET(_CUR_SEARCH _CUR_SEARCH-NOTFOUND CACHE INTERNAL "internal use") - ENDFOREACH(_SEARCH_DIR ${BULLET_LIBRARY_SEARCH_DIRS}) - - IF (NOT _BULLET_FOUND_LIB_DIR) - request_bullet_search_directories() - ELSE (NOT _BULLET_FOUND_LIB_DIR) - SET(BULLET_LIBRARY_DIRS ${_BULLET_FOUND_LIB_DIR} CACHE STRING "The bullet library directory") - ENDIF (NOT _BULLET_FOUND_LIB_DIR) - - FOREACH(_LIB_DIR ${_BULLET_FOUND_LIB_DIR}) - LIST(APPEND BULLET_LIBRARY_DIRS ${_LIB_DIR}/${BULLET_DYNAMICS_LIB_SUFFIX}) - LIST(APPEND BULLET_LIBRARY_DIRS ${_LIB_DIR}/${BULLET_COLLISION_LIB_SUFFIX}) - LIST(APPEND BULLET_LIBRARY_DIRS ${_LIB_DIR}/${BULLET_LINEAR_MATH_LIB_SUFFIX}) - LIST(APPEND BULLET_LIBRARY_DIRS ${_LIB_DIR}/${BULLET_HACD_LIB_SUFFIX}) - ENDFOREACH(_LIB_DIR ${_BULLET_FOUND_INC_DIRS}) - - LIST(APPEND BULLET_LIBRARIES ${BULLET_DYNAMICS_LIB}) - LIST(APPEND BULLET_LIBRARIES ${BULLET_COLLISION_LIB}) - LIST(APPEND BULLET_LIBRARIES ${BULLET_LINEAR_MATH_LIB}) - LIST(APPEND BULLET_LIBRARIES ${BULLET_HACD_LIB}) - -ENDIF( BULLET_INCLUDE_DIRS - AND NOT BULLET_LIBRARIES) - -############################################################################## -# verify -############################################################################## -IF ( NOT BULLET_INCLUDE_DIRS OR NOT BULLET_LIBRARY_DIRS ) - request_bullet_search_directories() -ELSE ( NOT BULLET_INCLUDE_DIRS OR NOT BULLET_LIBRARY_DIRS ) - UNSET(BULLET_INCLUDE_SEARCH_DIR CACHE) - UNSET(BULLET_LIBRARY_SEARCH_DIR CACHE) - MESSAGE(STATUS "-- found matching bullet version") -ENDIF ( NOT BULLET_INCLUDE_DIRS OR NOT BULLET_LIBRARY_DIRS ) - From 98b0f3f2d845490ea89b57242b65ee0314652b87 Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Mon, 3 Mar 2014 14:33:00 +0100 Subject: [PATCH 130/146] Change bullet path. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1d2931e3d..2ce81adb7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,7 +43,7 @@ include(find_compiler) include(FindSchism) include(FindBoost) -set (BULLET_ROOT "/opt/bullet/dist-2.81" CACHE PATH "Set to your bullet install path.") +set (BULLET_ROOT "/opt/bullet/default" CACHE PATH "Set to your bullet install path.") find_package(Bullet) find_package(Json REQUIRED) From 287c00ea57f3324bff5eded8635fb5e6a6bbcaaa Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Mon, 3 Mar 2014 14:53:08 +0100 Subject: [PATCH 131/146] Version bump. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ce81adb7..e4cc81a27 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ PROJECT(GUACAMOLE CXX) # version number set(GUACAMOLE_MAJOR 0) -set(GUACAMOLE_MINOR 1) +set(GUACAMOLE_MINOR 4) set(GUACAMOLE_PATCH 0) set(GUACAMOLE_VERSION ${GUACAMOLE_MAJOR}.${GUACAMOLE_MINOR}.${GUACAMOLE_PATCH}) set(GUACAMOLE_DESCRIPTION "GUACAMOLE - an astonishing virtual reality engine") From 10cc2de4e213719bdde549d5a279d43d7f16ec7a Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Mon, 3 Mar 2014 16:03:20 +0100 Subject: [PATCH 132/146] Use custom FindBullet.cmake. --- cmake/modules/FindBullet.cmake | 88 ++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 cmake/modules/FindBullet.cmake diff --git a/cmake/modules/FindBullet.cmake b/cmake/modules/FindBullet.cmake new file mode 100644 index 000000000..c152d46d0 --- /dev/null +++ b/cmake/modules/FindBullet.cmake @@ -0,0 +1,88 @@ +# - Try to find the Bullet physics engine +# +# This module defines the following variables +# +# BULLET_FOUND - Was bullet found +# BULLET_INCLUDE_DIRS - the Bullet include directories +# BULLET_LIBRARIES - Link to this, by default it includes +# all bullet components (Dynamics, +# Collision, LinearMath, & SoftBody) +# +# This module accepts the following variables +# +# BULLET_ROOT - Can be set to bullet install path or Windows build path +# + +#============================================================================= +# Copyright 2009 Kitware, Inc. +# Copyright 2009 Philip Lowman +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) + +macro(_FIND_BULLET_LIBRARY _var) + find_library(${_var} + NAMES + ${ARGN} + HINTS + ${BULLET_ROOT} + ${BULLET_ROOT}/lib/Release + ${BULLET_ROOT}/lib/Debug + ${BULLET_ROOT}/out/release8/libs + ${BULLET_ROOT}/out/debug8/libs + PATH_SUFFIXES lib + ) + mark_as_advanced(${_var}) +endmacro() + +macro(_BULLET_APPEND_LIBRARIES _list _release) + set(_debug ${_release}_DEBUG) + if(${_debug}) + set(${_list} ${${_list}} optimized ${${_release}} debug ${${_debug}}) + else() + set(${_list} ${${_list}} ${${_release}}) + endif() +endmacro() + +find_path(BULLET_INCLUDE_DIR NAMES btBulletCollisionCommon.h + HINTS + ${BULLET_ROOT}/include + ${BULLET_ROOT}/src + PATH_SUFFIXES bullet +) + +# Find the libraries + +_FIND_BULLET_LIBRARY(BULLET_DYNAMICS_LIBRARY BulletDynamics) +_FIND_BULLET_LIBRARY(BULLET_DYNAMICS_LIBRARY_DEBUG BulletDynamics_Debug BulletDynamics_d) +_FIND_BULLET_LIBRARY(BULLET_COLLISION_LIBRARY BulletCollision) +_FIND_BULLET_LIBRARY(BULLET_COLLISION_LIBRARY_DEBUG BulletCollision_Debug BulletCollision_d) +_FIND_BULLET_LIBRARY(BULLET_MATH_LIBRARY BulletMath LinearMath) +_FIND_BULLET_LIBRARY(BULLET_MATH_LIBRARY_DEBUG BulletMath_Debug BulletMath_d LinearMath_Debug LinearMath_d) +_FIND_BULLET_LIBRARY(BULLET_SOFTBODY_LIBRARY BulletSoftBody) +_FIND_BULLET_LIBRARY(BULLET_HACD_LIBRARY HACD) +_FIND_BULLET_LIBRARY(BULLET_SOFTBODY_LIBRARY_DEBUG BulletSoftBody_Debug BulletSoftBody_d) + + +# handle the QUIETLY and REQUIRED arguments and set BULLET_FOUND to TRUE if +# all listed variables are TRUE +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Bullet DEFAULT_MSG + BULLET_DYNAMICS_LIBRARY BULLET_COLLISION_LIBRARY BULLET_MATH_LIBRARY + BULLET_SOFTBODY_LIBRARY BULLET_HACD_LIBRARY BULLET_INCLUDE_DIR) + +set(BULLET_INCLUDE_DIRS ${BULLET_INCLUDE_DIR}) +if(BULLET_FOUND) + _BULLET_APPEND_LIBRARIES(BULLET_LIBRARIES BULLET_DYNAMICS_LIBRARY) + _BULLET_APPEND_LIBRARIES(BULLET_LIBRARIES BULLET_COLLISION_LIBRARY) + _BULLET_APPEND_LIBRARIES(BULLET_LIBRARIES BULLET_MATH_LIBRARY) + _BULLET_APPEND_LIBRARIES(BULLET_LIBRARIES BULLET_SOFTBODY_LIBRARY) + _BULLET_APPEND_LIBRARIES(BULLET_LIBRARIES BULLET_HACD_LIBRARY) +endif() From c2abbeca5f498a7aebfe04a22781433a42dd51a2 Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Tue, 4 Mar 2014 13:59:08 +0100 Subject: [PATCH 133/146] Add comment to function call. --- cmake/modules/FindJson.cmake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmake/modules/FindJson.cmake b/cmake/modules/FindJson.cmake index 3744dd0bf..a6d97a8ac 100644 --- a/cmake/modules/FindJson.cmake +++ b/cmake/modules/FindJson.cmake @@ -34,4 +34,6 @@ include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Json DEFAULT_MSG JSON_LIBRARY JSON_INCLUDE_DIR) +# Mark the named cached variables as advanced. An advanced variable will not be +# displayed in any of the cmake GUIs unless the show advanced option is on. mark_as_advanced(JSON_INCLUDE_DIR JSON_LIBRARY ) From 95985115261a8d56e173db64b314b08be930d1f5 Mon Sep 17 00:00:00 2001 From: Joshua Reibert Date: Wed, 5 Mar 2014 15:36:49 +0100 Subject: [PATCH 134/146] rename files from OculusRift to OculusWindow --- .../include/gua/{OculusRift.hpp => OculusWindow.hpp} | 0 .../guacamole-oculus/src/gua/{OculusRift.cpp => OculusWindow.cpp} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename plugins/guacamole-oculus/include/gua/{OculusRift.hpp => OculusWindow.hpp} (100%) rename plugins/guacamole-oculus/src/gua/{OculusRift.cpp => OculusWindow.cpp} (100%) diff --git a/plugins/guacamole-oculus/include/gua/OculusRift.hpp b/plugins/guacamole-oculus/include/gua/OculusWindow.hpp similarity index 100% rename from plugins/guacamole-oculus/include/gua/OculusRift.hpp rename to plugins/guacamole-oculus/include/gua/OculusWindow.hpp diff --git a/plugins/guacamole-oculus/src/gua/OculusRift.cpp b/plugins/guacamole-oculus/src/gua/OculusWindow.cpp similarity index 100% rename from plugins/guacamole-oculus/src/gua/OculusRift.cpp rename to plugins/guacamole-oculus/src/gua/OculusWindow.cpp From 6bbae0425fb20872db5a9b4263f210ba5b9c46c0 Mon Sep 17 00:00:00 2001 From: Joshua Reibert Date: Wed, 5 Mar 2014 15:37:20 +0100 Subject: [PATCH 135/146] remove OculusDeviceManager --- .../include/gua/OculusDeviceManager.hpp | 44 ------------ .../src/gua/OculusDeviceManager.cpp | 72 ------------------- 2 files changed, 116 deletions(-) delete mode 100644 plugins/guacamole-oculus/include/gua/OculusDeviceManager.hpp delete mode 100644 plugins/guacamole-oculus/src/gua/OculusDeviceManager.cpp diff --git a/plugins/guacamole-oculus/include/gua/OculusDeviceManager.hpp b/plugins/guacamole-oculus/include/gua/OculusDeviceManager.hpp deleted file mode 100644 index ec9ee38b1..000000000 --- a/plugins/guacamole-oculus/include/gua/OculusDeviceManager.hpp +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef GUA_OCULUS_DEVICE_MANAGER_HPP -#define GUA_OCULUS_DEVICE_MANAGER_HPP - -#if defined (_MSC_VER) - #if defined (GUA_OCULUS_LIBRARY) - #define GUA_OCULUS_DLL __declspec( dllexport ) - #else -#define GUA_OCULUS_DLL __declspec( dllimport ) - #endif -#else - #define GUA_OCULUS_DLL -#endif // #if defined(_MSC_VER) - -namespace OVR { - class SensorFusion; - class DeviceManager; - class HMDDevice; - class SensorDevice; -} - -namespace gua -{ - class GUA_OCULUS_DLL OculusDeviceManager - { - public: - static OculusDeviceManager& getInstance(); - virtual ~OculusDeviceManager(); - - OVR::HMDDevice* getNextAvailableDevice(); - OVR::SensorDevice* getNextSensor(); - - protected: - OculusDeviceManager(); - - private: - OVR::DeviceManager* device_manager_; - static int numberOfDevicesCreated; - - OculusDeviceManager(OculusDeviceManager const& src); - OculusDeviceManager& operator=(OculusDeviceManager const& src); - }; -} - -#endif // GUA_OCULUS_DEVICE_MANAGER_HPP \ No newline at end of file diff --git a/plugins/guacamole-oculus/src/gua/OculusDeviceManager.cpp b/plugins/guacamole-oculus/src/gua/OculusDeviceManager.cpp deleted file mode 100644 index 6f90140b8..000000000 --- a/plugins/guacamole-oculus/src/gua/OculusDeviceManager.cpp +++ /dev/null @@ -1,72 +0,0 @@ -// class header -#include - -// external headers -#include - -#include - -namespace gua -{ - int OculusDeviceManager::numberOfDevicesCreated(0); - - OculusDeviceManager& OculusDeviceManager::getInstance() - { - static OculusDeviceManager instance; - return instance; - } - - OculusDeviceManager::OculusDeviceManager() - { - device_manager_ = OVR::DeviceManager::Create(); - } - - /* virtual */ OculusDeviceManager::~OculusDeviceManager() - { - if (device_manager_) - delete device_manager_; - } - - OVR::HMDDevice* OculusDeviceManager::getNextAvailableDevice() - { - OVR::HMDDevice* deviceToReturn(nullptr); - - auto enumerator = device_manager_->EnumerateDevices(); - //Surprisingly, it is not needed to call enumerator.Next() here - deviceToReturn = enumerator.CreateDevice(); - - if (deviceToReturn != nullptr) - { - ++numberOfDevicesCreated; - } - - std::cout << "Next device is number " << numberOfDevicesCreated << std::endl; - - return deviceToReturn; - } - - //Redefined function from the Oculus SDK in order to support multiple Oculus Rifts - OVR::SensorDevice* OculusDeviceManager::getNextSensor() - { - //Gets a sensor in order to attach it to a device - - OVR::SensorDevice* sensor(nullptr); - - auto enumerator = device_manager_->EnumerateDevices(); - - int i(1); - while (i < numberOfDevicesCreated) - { - enumerator.Next(); - ++i; - } - - sensor = enumerator.CreateDevice(); - - if (sensor) - sensor->SetCoordinateFrame(OVR::SensorDevice::Coord_HMD); - else - --numberOfDevicesCreated; // If no sensor was detected for a device, the number of devices created is decreased again - return sensor; - } -} From 5654e6ca61e931531cf78e5a7602b028cb45e941 Mon Sep 17 00:00:00 2001 From: Joshua Reibert Date: Wed, 5 Mar 2014 15:45:43 +0100 Subject: [PATCH 136/146] reflect name change internally in files --- .../include/gua/OculusWindow.hpp | 144 ++++--- .../guacamole-oculus/src/gua/OculusWindow.cpp | 360 ++++++++---------- 2 files changed, 229 insertions(+), 275 deletions(-) diff --git a/plugins/guacamole-oculus/include/gua/OculusWindow.hpp b/plugins/guacamole-oculus/include/gua/OculusWindow.hpp index c4c0aac61..f321bf5ec 100644 --- a/plugins/guacamole-oculus/include/gua/OculusWindow.hpp +++ b/plugins/guacamole-oculus/include/gua/OculusWindow.hpp @@ -1,78 +1,66 @@ -/****************************************************************************** - * guacamole - delicious VR * - * * - * Copyright: (c) 2011-2013 Bauhaus-Universität Weimar * - * Contact: felix.lauer@uni-weimar.de / simon.schneegans@uni-weimar.de * - * * - * This program is free software: you can redistribute it and/or modify it * - * under the terms of the GNU General Public License as published by the Free * - * Software Foundation, either version 3 of the License, or (at your option) * - * any later version. * - * * - * This program is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * - * for more details. * - * * - * You should have received a copy of the GNU General Public License along * - * with this program. If not, see . * - * * - ******************************************************************************/ - -#ifndef GUA_OCULUS_RIFT_HPP -#define GUA_OCULUS_RIFT_HPP - -#if defined (_MSC_VER) - #if defined (GUA_OCULUS_LIBRARY) - #define GUA_OCULUS_DLL __declspec( dllexport ) - #else -#define GUA_OCULUS_DLL __declspec( dllimport ) - #endif -#else - #define GUA_OCULUS_DLL -#endif // #if defined(_MSC_VER) - -// guacamole headers -#include - -namespace OVR { - class SensorFusion; - class DeviceManager; - class HMDDevice; - class SensorDevice; -} - -namespace gua { - -class GUA_OCULUS_DLL OculusRift : public Window { - public: - - static void init(); - - OculusRift(std::string const& display); - virtual ~OculusRift(); - - void create_shader(); - - // virtual - void display(std::shared_ptr const& left_texture, - std::shared_ptr const& right_texture); - - math::mat4 const get_transform() const; - - private: - void display(std::shared_ptr const& texture, - math::vec2ui const& size, - math::vec2ui const& position, - bool left); - - math::vec4 distortion_; - - OVR::SensorDevice* sensor_; - OVR::SensorFusion* sensor_fusion_; - OVR::HMDDevice* device_; -}; - -} - -#endif // GUA_OCULUS_RIFT_HPP +/****************************************************************************** + * guacamole - delicious VR * + * * + * Copyright: (c) 2011-2013 Bauhaus-Universität Weimar * + * Contact: felix.lauer@uni-weimar.de / simon.schneegans@uni-weimar.de * + * * + * This program is free software: you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the Free * + * Software Foundation, either version 3 of the License, or (at your option) * + * any later version. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * + * for more details. * + * * + * You should have received a copy of the GNU General Public License along * + * with this program. If not, see . * + * * + ******************************************************************************/ + +#ifndef GUA_OCULUS_WINDOW_HPP +#define GUA_OCULUS_WINDOW_HPP + +#if defined (_MSC_VER) + #if defined (GUA_OCULUS_LIBRARY) + #define GUA_OCULUS_DLL __declspec( dllexport ) + #else +#define GUA_OCULUS_DLL __declspec( dllimport ) + #endif +#else + #define GUA_OCULUS_DLL +#endif // #if defined(_MSC_VER) + +// guacamole headers +#include + +namespace gua { + +class GUA_OCULUS_DLL OculusWindow : public Window { + public: + + OculusWindow(std::string const& display); + virtual ~OculusWindow(); + + void create_shader(); + + void set_distortion(math::vec4 const& distortion); + void set_distortion(float distortion0, float distortion1, float distortion2, float distortion3); + + // virtual + void display(std::shared_ptr const& left_texture, + std::shared_ptr const& right_texture); + + private: + void display(std::shared_ptr const& texture, + math::vec2ui const& size, + math::vec2ui const& position, + bool left); + + math::vec4 distortion_; +}; + +} + +#endif // GUA_OCULUS_WINDOW_HPP diff --git a/plugins/guacamole-oculus/src/gua/OculusWindow.cpp b/plugins/guacamole-oculus/src/gua/OculusWindow.cpp index ccdc450e9..52db7326c 100644 --- a/plugins/guacamole-oculus/src/gua/OculusWindow.cpp +++ b/plugins/guacamole-oculus/src/gua/OculusWindow.cpp @@ -1,198 +1,164 @@ -/****************************************************************************** - * guacamole - delicious VR * - * * - * Copyright: (c) 2011-2013 Bauhaus-Universität Weimar * - * Contact: felix.lauer@uni-weimar.de / simon.schneegans@uni-weimar.de * - * * - * This program is free software: you can redistribute it and/or modify it * - * under the terms of the GNU General Public License as published by the Free * - * Software Foundation, either version 3 of the License, or (at your option) * - * any later version. * - * * - * This program is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * - * for more details. * - * * - * You should have received a copy of the GNU General Public License along * - * with this program. If not, see . * - * * - ******************************************************************************/ - -// class header -#include -#include - -// external headers -#include -#include - -namespace gua { - -void OculusRift::init() { - OVR::System::Init(OVR::Log::ConfigureDefaultLog(OVR::LogMask_All)); -} - -OculusRift::OculusRift(std::string const& display): - Window(), - distortion_(4), - sensor_fusion_(nullptr), - sensor_(nullptr), - device_(nullptr) { - - config.set_size(math::vec2ui(1280, 800)); - config.set_title("guacamole"); - config.set_display_name(display); - config.set_stereo_mode(StereoMode::SIDE_BY_SIDE); - config.set_left_resolution(math::vec2ui(1280/2, 800)); - config.set_left_position(math::vec2ui(0, 0)); - config.set_right_resolution(math::vec2ui(1280/2, 800)); - config.set_right_position(math::vec2ui(1280/2, 0)); - - //Every time an Oculus Rift Window is created, it is associated to the next available OR-device - device_ = OculusDeviceManager::getInstance().getNextAvailableDevice(); - - if (!device_) { - WARNING("Failed to initialize an Oculus Rift device! Are enough Oculus Rifts attached?"); - return; - } - - OVR::HMDInfo hmd; - if (device_->GetDeviceInfo(&hmd)) { - MESSAGE("Oculus EyeDistance: %f", hmd.InterpupillaryDistance); - - distortion_[0] = hmd.DistortionK[0]; - distortion_[1] = hmd.DistortionK[1]; - distortion_[2] = hmd.DistortionK[2]; - distortion_[3] = hmd.DistortionK[3]; - } - - sensor_ = OculusDeviceManager::getInstance().getNextSensor(); - sensor_fusion_ = new OVR::SensorFusion(); - - if (sensor_) { - sensor_fusion_->AttachToSensor(sensor_); - } else { - WARNING("Failed to get Oculus gyroskop data"); - } -} - -//////////////////////////////////////////////////////////////////////////////// - -OculusRift::~OculusRift() { - if (sensor_fusion_) - delete sensor_fusion_; -} - -//////////////////////////////////////////////////////////////////////////////// - -void OculusRift::create_shader() { - fullscreen_shader_.create_from_sources(R"( - #version 420 - #extension GL_NV_bindless_texture : require - - layout(location=0) in vec3 in_position; - - out vec2 tex_coord; - - void main() { - tex_coord = in_position.xy*0.5 + 0.5; - gl_Position = vec4(in_position, 1.0); - } - )", R"( - #version 420 - #extension GL_NV_bindless_texture : require - #extension GL_NV_gpu_shader5 : enable - - in vec2 tex_coord; - - uniform uvec2 sampler; - - // oculus parameters - uniform vec2 lens_center; - uniform vec2 scale; - uniform vec4 hmd_warp_param; - - layout (location = 0) out vec3 out_color; - - sampler2D get_tex(uvec2 handle) { - return sampler2D(uint64_t(handle.x) + uint64_t(handle.y) * 4294967295); - } - - vec2 hmd_warp(vec2 in_texcoord) { - vec2 theta = (in_texcoord - lens_center) * 2.0; // Scales to [-1, 1] - float rSq = theta.x * theta.x + theta.y * theta.y; - vec2 rvector = theta * (hmd_warp_param.x+hmd_warp_param.y*rSq - +hmd_warp_param.z*rSq*rSq - +hmd_warp_param.w*rSq*rSq*rSq); - return lens_center + scale * rvector; - } - - vec3 get_color() { - - vec2 tc = hmd_warp(tex_coord); - - if (tc.x < 0.0 || tc.y < 0.0 || tc.x > 1.0 || tc.y > 1.0 ) - return vec3(0); - - return vec3(texture2D( get_tex(sampler), tc).rgb); - } - - void main() { - out_color = get_color(); - } - )"); -} - -//////////////////////////////////////////////////////////////////////////////// - -void OculusRift::display(std::shared_ptr const& left_texture, - std::shared_ptr const& right_texture) { - - display(left_texture, config.get_left_resolution(), config.get_left_position(), true); - display(right_texture, config.get_right_resolution(), config.get_right_position(), false); -} - -//////////////////////////////////////////////////////////////////////////////// - -math::mat4 const OculusRift::get_transform() const { - if (sensor_fusion_) { - OVR::Quatf orient = sensor_fusion_->GetPredictedOrientation(); - OVR::Matrix4f ovr_mat(orient.Inverted()); - - return math::mat4(ovr_mat.M[0][0], ovr_mat.M[0][1], ovr_mat.M[0][2], ovr_mat.M[0][3], - ovr_mat.M[1][0], ovr_mat.M[1][1], ovr_mat.M[1][2], ovr_mat.M[1][3], - ovr_mat.M[2][0], ovr_mat.M[2][1], ovr_mat.M[2][2], ovr_mat.M[2][3], - ovr_mat.M[3][0], ovr_mat.M[3][1], ovr_mat.M[3][2], ovr_mat.M[3][3]); - } - - return math::mat4::identity(); -} - -//////////////////////////////////////////////////////////////////////////////// - -void OculusRift::display(std::shared_ptr const& texture, - math::vec2ui const& size, - math::vec2ui const& position, - bool left) { - - - fullscreen_shader_.use(*get_context()); - fullscreen_shader_.set_uniform(*get_context(), texture, "sampler"); - - if (left) fullscreen_shader_.set_uniform(*get_context(), math::vec2(0.6f, 0.5f), "lens_center"); - else fullscreen_shader_.set_uniform(*get_context(), math::vec2(0.4f, 0.5f), "lens_center"); - - fullscreen_shader_.set_uniform(*get_context(), math::vec2(0.4f, 0.4f), "scale"); - fullscreen_shader_.set_uniform(*get_context(), distortion_, "hmd_warp_param"); - - get_context()->render_context->set_viewport(scm::gl::viewport(position, size)); - get_context()->render_context->set_depth_stencil_state(depth_stencil_state_); - - fullscreen_quad_->draw(get_context()->render_context); - - get_context()->render_context->reset_state_objects(); - fullscreen_shader_.unuse(*get_context()); -} - +/****************************************************************************** + * guacamole - delicious VR * + * * + * Copyright: (c) 2011-2013 Bauhaus-Universität Weimar * + * Contact: felix.lauer@uni-weimar.de / simon.schneegans@uni-weimar.de * + * * + * This program is free software: you can redistribute it and/or modify it * + * under the terms of the GNU General Public License as published by the Free * + * Software Foundation, either version 3 of the License, or (at your option) * + * any later version. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * + * for more details. * + * * + * You should have received a copy of the GNU General Public License along * + * with this program. If not, see . * + * * + ******************************************************************************/ + +// class header +#include + +// external headers +#include + +namespace gua { + +OculusWindow::OculusWindow(std::string const& display): + Window(), + distortion_(4) { + + config.set_size(math::vec2ui(1280, 800)); + config.set_title("guacamole"); + config.set_display_name(display); + config.set_stereo_mode(StereoMode::SIDE_BY_SIDE); + config.set_left_resolution(math::vec2ui(1280/2, 800)); + config.set_left_position(math::vec2ui(0, 0)); + config.set_right_resolution(math::vec2ui(1280/2, 800)); + config.set_right_position(math::vec2ui(1280/2, 0)); + + // for now fixed distortion values TODO should be set dynamically by OVR + set_distortion(1.0, 0.22, 0.24, 0.0); + +} + +//////////////////////////////////////////////////////////////////////////////// + +OculusWindow::~OculusWindow() { + +} + +//////////////////////////////////////////////////////////////////////////////// + +void OculusWindow::create_shader() { + fullscreen_shader_.create_from_sources(R"( + #version 420 + #extension GL_NV_bindless_texture : require + + layout(location=0) in vec3 in_position; + + out vec2 tex_coord; + + void main() { + tex_coord = in_position.xy*0.5 + 0.5; + gl_Position = vec4(in_position, 1.0); + } + )", R"( + #version 420 + #extension GL_NV_bindless_texture : require + #extension GL_NV_gpu_shader5 : enable + + in vec2 tex_coord; + + uniform uvec2 sampler; + + // oculus parameters + uniform vec2 lens_center; + uniform vec2 scale; + uniform vec4 hmd_warp_param; + + layout (location = 0) out vec3 out_color; + + sampler2D get_tex(uvec2 handle) { + return sampler2D(uint64_t(handle.x) + uint64_t(handle.y) * 4294967295); + } + + vec2 hmd_warp(vec2 in_texcoord) { + vec2 theta = (in_texcoord - lens_center) * 2.0; // Scales to [-1, 1] + float rSq = theta.x * theta.x + theta.y * theta.y; + vec2 rvector = theta * (hmd_warp_param.x+hmd_warp_param.y*rSq + +hmd_warp_param.z*rSq*rSq + +hmd_warp_param.w*rSq*rSq*rSq); + return lens_center + scale * rvector; + } + + vec3 get_color() { + + vec2 tc = hmd_warp(tex_coord); + + if (tc.x < 0.0 || tc.y < 0.0 || tc.x > 1.0 || tc.y > 1.0 ) + return vec3(0); + + return vec3(texture2D( get_tex(sampler), tc).rgb); + } + + void main() { + out_color = get_color(); + } + )"); +} + +//////////////////////////////////////////////////////////////////////////////// + +void OculusWindow::set_distortion(math::vec4 const& distortion) { + distortion_ = distortion; +} + +//////////////////////////////////////////////////////////////////////////////// + +void OculusWindow::set_distortion(float distortion0, float distortion1, float distortion2, float distortion3) { + distortion_[0] = distortion0; + distortion_[1] = distortion1; + distortion_[2] = distortion2; + distortion_[3] = distortion3; +} + +//////////////////////////////////////////////////////////////////////////////// + +void OculusWindow::display(std::shared_ptr const& left_texture, + std::shared_ptr const& right_texture) { + + display(left_texture, config.get_left_resolution(), config.get_left_position(), true); + display(right_texture, config.get_right_resolution(), config.get_right_position(), false); +} + +//////////////////////////////////////////////////////////////////////////////// + +void OculusWindow::display(std::shared_ptr const& texture, + math::vec2ui const& size, + math::vec2ui const& position, + bool left) { + + + fullscreen_shader_.use(*get_context()); + fullscreen_shader_.set_uniform(*get_context(), texture, "sampler"); + + if (left) fullscreen_shader_.set_uniform(*get_context(), math::vec2(0.6f, 0.5f), "lens_center"); + else fullscreen_shader_.set_uniform(*get_context(), math::vec2(0.4f, 0.5f), "lens_center"); + + fullscreen_shader_.set_uniform(*get_context(), math::vec2(0.4f, 0.4f), "scale"); + fullscreen_shader_.set_uniform(*get_context(), distortion_, "hmd_warp_param"); + + get_context()->render_context->set_viewport(scm::gl::viewport(position, size)); + get_context()->render_context->set_depth_stencil_state(depth_stencil_state_); + + fullscreen_quad_->draw(get_context()->render_context); + + get_context()->render_context->reset_state_objects(); + fullscreen_shader_.unuse(*get_context()); +} + } \ No newline at end of file From fe1680656611d6e225f12c7417a888ad15ededbc Mon Sep 17 00:00:00 2001 From: Joshua Reibert Date: Wed, 5 Mar 2014 16:13:50 +0100 Subject: [PATCH 137/146] remove oculus rotation from oculus-example --- plugins/guacamole-oculus/example-oculus/main.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/guacamole-oculus/example-oculus/main.cpp b/plugins/guacamole-oculus/example-oculus/main.cpp index eb35bd491..5ab9c7791 100644 --- a/plugins/guacamole-oculus/example-oculus/main.cpp +++ b/plugins/guacamole-oculus/example-oculus/main.cpp @@ -20,7 +20,7 @@ ******************************************************************************/ #include -#include +#include const std::string geometry("data/objects/monkey.obj"); // const std::string geometry("data/objects/cube.obj"); @@ -94,7 +94,7 @@ int main(int argc, char** argv) { // initialize guacamole gua::init(argc, argv); - gua::OculusRift::init(); + //gua::OculusRift::init(); gua::ShadingModelDatabase::load_shading_models_from("data/materials/"); gua::MaterialDatabase::load_materials_from("data/materials/"); @@ -164,8 +164,8 @@ int main(int argc, char** argv) { pipe->config.set_bloom_intensity(0.8f); - auto oculus_rift(new gua::OculusRift(":0.0")); - pipe->set_window(oculus_rift); + auto oculus_window(new gua::OculusWindow(":0.0")); + pipe->set_window(oculus_window); gua::Renderer renderer({ pipe @@ -204,7 +204,7 @@ int main(int argc, char** argv) { graph["/root_ape"]->rotate(15 * frame_time, 0, 1, 0); //graph["/screen"]->rotate(20*frame_time, 0, 1, 0); - nav->set_transform(oculus_rift->get_transform()); + //nav->set_transform(oculus_rift->get_transform()); renderer.queue_draw({&graph}); }); From 1ae4a8d855726551d715000c8cd4033d69e82b47 Mon Sep 17 00:00:00 2001 From: Joshua Reibert Date: Wed, 5 Mar 2014 17:21:08 +0100 Subject: [PATCH 138/146] change oculus-example to use OculusSDK to fetch rotation matrix. --- .../guacamole-oculus/example-oculus/main.cpp | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/plugins/guacamole-oculus/example-oculus/main.cpp b/plugins/guacamole-oculus/example-oculus/main.cpp index 5ab9c7791..e4f8e9cc2 100644 --- a/plugins/guacamole-oculus/example-oculus/main.cpp +++ b/plugins/guacamole-oculus/example-oculus/main.cpp @@ -22,6 +22,8 @@ #include #include +#include + const std::string geometry("data/objects/monkey.obj"); // const std::string geometry("data/objects/cube.obj"); @@ -90,15 +92,39 @@ void setup_scene(gua::SceneGraph& graph, } } +OVR::SensorFusion* init_oculus() { + OVR::System::Init(OVR::Log::ConfigureDefaultLog(OVR::LogMask_All)); + OVR::DeviceManager* device_manager = OVR::DeviceManager::Create(); + OVR::SensorDevice* sensor_device = device_manager->EnumerateDevices().CreateDevice(); + if (sensor_device) { + OVR::SensorFusion* sensor_fusion = new OVR::SensorFusion(); + sensor_fusion->AttachToSensor(sensor_device); + return sensor_fusion; + } + return nullptr; +} + +gua::math::mat4 const get_oculus_transform(OVR::SensorFusion* sensor) { + OVR::Quatf orient = sensor->GetPredictedOrientation(); + OVR::Matrix4f mat(orient.Inverted()); + return gua::math::mat4( mat.M[0][0], mat.M[0][1], mat.M[0][2], mat.M[0][3], + mat.M[1][0], mat.M[1][1], mat.M[1][2], mat.M[1][3], + mat.M[2][0], mat.M[2][1], mat.M[2][2], mat.M[2][3], + mat.M[3][0], mat.M[3][1], mat.M[3][2], mat.M[3][3]); +} + int main(int argc, char** argv) { // initialize guacamole gua::init(argc, argv); - //gua::OculusRift::init(); gua::ShadingModelDatabase::load_shading_models_from("data/materials/"); gua::MaterialDatabase::load_materials_from("data/materials/"); + // initialize Oculus SDK + OVR::SensorFusion* oculus_sensor = init_oculus(); + if (!oculus_sensor) return 1; // no oculus sensor found + // setup scene gua::SceneGraph graph("main_scenegraph"); @@ -204,7 +230,7 @@ int main(int argc, char** argv) { graph["/root_ape"]->rotate(15 * frame_time, 0, 1, 0); //graph["/screen"]->rotate(20*frame_time, 0, 1, 0); - //nav->set_transform(oculus_rift->get_transform()); + nav->set_transform(get_oculus_transform(oculus_sensor)); renderer.queue_draw({&graph}); }); From eaa026c9429ff0bc14bb9a936e271b7d82c3b770 Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Thu, 6 Mar 2014 10:21:06 +0100 Subject: [PATCH 139/146] Bullet is required. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e4cc81a27..b1cca6df2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,7 +44,7 @@ include(FindSchism) include(FindBoost) set (BULLET_ROOT "/opt/bullet/default" CACHE PATH "Set to your bullet install path.") -find_package(Bullet) +find_package(Bullet REQUIRED) find_package(Json REQUIRED) set(LIBS From 12ee74611732d1fd1ccf832536837b82477648d7 Mon Sep 17 00:00:00 2001 From: Andreas-Christoph Bernstein Date: Thu, 6 Mar 2014 10:29:08 +0100 Subject: [PATCH 140/146] Bump version. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b1cca6df2..4e0db37d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ PROJECT(GUACAMOLE CXX) # version number set(GUACAMOLE_MAJOR 0) set(GUACAMOLE_MINOR 4) -set(GUACAMOLE_PATCH 0) +set(GUACAMOLE_PATCH 2) set(GUACAMOLE_VERSION ${GUACAMOLE_MAJOR}.${GUACAMOLE_MINOR}.${GUACAMOLE_PATCH}) set(GUACAMOLE_DESCRIPTION "GUACAMOLE - an astonishing virtual reality engine") set(GUACAMOLE_HOMEPAGE "http://www.GUACAMOLE.org") From 1ad52c33eeaa17af5292045bcc4dd3ed5a83c1b8 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Thu, 6 Mar 2014 13:31:06 +0100 Subject: [PATCH 141/146] serialized scene stores now pointers only --- include/gua/renderer/MaterialLoader.hpp | 2 +- include/gua/renderer/MeshLoader.hpp | 2 +- include/gua/renderer/SerializedNode.hpp | 67 ------------------------ include/gua/renderer/SerializedScene.hpp | 17 +++--- src/gua/databases/MaterialDatabase.cpp | 2 +- src/gua/renderer/CompositePass.cpp | 8 +-- src/gua/renderer/GBufferPass.cpp | 39 +++++++------- src/gua/renderer/LightingPass.cpp | 66 +++++++++++------------ src/gua/renderer/MaterialLoader.cpp | 6 +-- src/gua/renderer/MeshLoader.cpp | 11 ++-- src/gua/renderer/PostFXPass.cpp | 32 +++++------ src/gua/renderer/Serializer.cpp | 23 +++----- src/gua/renderer/ShadowMap.cpp | 8 +-- 13 files changed, 104 insertions(+), 179 deletions(-) delete mode 100644 include/gua/renderer/SerializedNode.hpp diff --git a/include/gua/renderer/MaterialLoader.hpp b/include/gua/renderer/MaterialLoader.hpp index e19abfcc1..5b95ea1ed 100644 --- a/include/gua/renderer/MaterialLoader.hpp +++ b/include/gua/renderer/MaterialLoader.hpp @@ -73,7 +73,7 @@ class GUA_DLL MaterialLoader { }; std::string const load_material(aiMaterial const* material, - std::string const& name_prefix) const; + std::string const& file_name) const; private: std::string const load_shading_model(unsigned capabilities) const; diff --git a/include/gua/renderer/MeshLoader.hpp b/include/gua/renderer/MeshLoader.hpp index 19f282f87..d8d0ab49c 100644 --- a/include/gua/renderer/MeshLoader.hpp +++ b/include/gua/renderer/MeshLoader.hpp @@ -97,7 +97,7 @@ class MeshLoader : public LoaderBase { aiScene const* ai_scene, aiNode* ai_root, std::string const& file_name, - unsigned flags); + unsigned flags, unsigned& mesh_count); unsigned node_counter_; diff --git a/include/gua/renderer/SerializedNode.hpp b/include/gua/renderer/SerializedNode.hpp deleted file mode 100644 index fd3dec3e4..000000000 --- a/include/gua/renderer/SerializedNode.hpp +++ /dev/null @@ -1,67 +0,0 @@ -/****************************************************************************** - * guacamole - delicious VR * - * * - * Copyright: (c) 2011-2013 Bauhaus-Universität Weimar * - * Contact: felix.lauer@uni-weimar.de / simon.schneegans@uni-weimar.de * - * * - * This program is free software: you can redistribute it and/or modify it * - * under the terms of the GNU General Public License as published by the Free * - * Software Foundation, either version 3 of the License, or (at your option) * - * any later version. * - * * - * This program is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * - * for more details. * - * * - * You should have received a copy of the GNU General Public License along * - * with this program. If not, see . * - * * - ******************************************************************************/ - -#ifndef GUA_SERIALIZED_NODE_HPP -#define GUA_SERIALIZED_NODE_HPP - -// guacamole headers -#include - -namespace gua { - -/** - * Stores information on a light for rendering. - * - * This is a struct used for serializing the graph. - * - * essentially the same as a std::pair - */ -template struct SerializedNode { - - SerializedNode() : transform(math::mat4::identity()), data() {} - - /** - * Constructor. - * - * This creates a new serialized node. - * - * \param transform The global transformation of this node. - * \param color The color of the light. - */ - SerializedNode(math::mat4 const& t, configuration_type const& d) - : transform(t), data(d) {} - - /** - * The global transformation of this node. - */ - math::mat4 transform; - configuration_type data; -}; - -template -inline SerializedNode make_serialized_node(math::mat4 const& t, T const& d) -{ - return SerializedNode(t, d); -} - -} - -#endif // GUA_SERIALIZED_NODE_HPP diff --git a/include/gua/renderer/SerializedScene.hpp b/include/gua/renderer/SerializedScene.hpp index bc715fa1c..40455aa82 100644 --- a/include/gua/renderer/SerializedScene.hpp +++ b/include/gua/renderer/SerializedScene.hpp @@ -31,7 +31,6 @@ #include #include #include -#include #include #include @@ -54,32 +53,32 @@ struct SerializedScene { /** * All geometry nodes. */ - std::vector > meshnodes_; + std::vector meshnodes_; /** * All NURBS nodes. */ - std::vector > nurbsnodes_; + std::vector nurbsnodes_; /** * All Volume nodes. */ - std::vector > volumenodes_; + std::vector volumenodes_; /** * All point light nodes. */ - std::vector > point_lights_; + std::vector point_lights_; /** * All spot light nodes. */ - std::vector > spot_lights_; + std::vector spot_lights_; /** * All sun light nodes. */ - std::vector > sun_lights_; + std::vector sun_lights_; /** * The frustum. @@ -106,12 +105,12 @@ struct SerializedScene { /** * All bounding boxes. */ - std::vector > rays_; + std::vector rays_; /** * All textured quads. */ - std::vector > textured_quads_; + std::vector textured_quads_; }; } diff --git a/src/gua/databases/MaterialDatabase.cpp b/src/gua/databases/MaterialDatabase.cpp index 010fba9d4..654db5ebe 100644 --- a/src/gua/databases/MaterialDatabase.cpp +++ b/src/gua/databases/MaterialDatabase.cpp @@ -43,7 +43,7 @@ void MaterialDatabase::load_materials_from( unsigned suffix_pos = unsigned(parse_string.find(".gmd")); if (parse_string.length() - suffix_pos == 4) { - auto name(parse_string.substr(0, suffix_pos)); + auto name(dir.get_directory_name() + parse_string); auto mat = std::make_shared( name, MaterialDescription(dir.get_directory_name() + parse_string)); diff --git a/src/gua/renderer/CompositePass.cpp b/src/gua/renderer/CompositePass.cpp index 1e652e6c6..6789fb9b7 100644 --- a/src/gua/renderer/CompositePass.cpp +++ b/src/gua/renderer/CompositePass.cpp @@ -134,11 +134,11 @@ void CompositePass::create(RenderContext const& ctx, std::vector(GeometryDatabase::instance()->lookup(node.data.get_volume())); + std::static_pointer_cast(GeometryDatabase::instance()->lookup(node->data.get_volume())); if (volume) { ray_generation_shader_->set_uniform( - ctx, node.transform, "gua_model_matrix"); + ctx, node->get_world_transform(), "gua_model_matrix"); ray_generation_shader_->set_uniform( ctx, 0, "volume_frag_id"); @@ -184,11 +184,11 @@ void CompositePass::create(RenderContext const& ctx, std::vector(GeometryDatabase::instance()->lookup(node.data.get_volume())); + std::static_pointer_cast(GeometryDatabase::instance()->lookup(node->data.get_volume())); if (volume) { composite_shader_->set_uniform( - ctx, node.transform, "gua_model_matrix"); + ctx, node->get_world_transform(), "gua_model_matrix"); volume->set_uniforms(ctx, composite_shader_); diff --git a/src/gua/renderer/GBufferPass.cpp b/src/gua/renderer/GBufferPass.cpp index c719cc63e..0f71a0b11 100644 --- a/src/gua/renderer/GBufferPass.cpp +++ b/src/gua/renderer/GBufferPass.cpp @@ -154,19 +154,19 @@ void GBufferPass::rendering(SerializedScene const& scene, for (auto const& node : scene.meshnodes_) { auto geometry = - GeometryDatabase::instance()->lookup(node.data.get_geometry()); + GeometryDatabase::instance()->lookup(node->data.get_geometry()); auto material = - MaterialDatabase::instance()->lookup(node.data.get_material()); + MaterialDatabase::instance()->lookup(node->data.get_material()); if (material && geometry) { mesh_shader_->set_uniform( ctx, material->get_id(), "gua_material_id"); mesh_shader_->set_uniform( - ctx, node.transform, "gua_model_matrix"); + ctx, node->get_world_transform(), "gua_model_matrix"); mesh_shader_->set_uniform( ctx, scm::math::transpose( - scm::math::inverse(node.transform)), + scm::math::inverse(node->get_world_transform())), "gua_normal_matrix"); geometry->draw(ctx); @@ -188,8 +188,8 @@ void GBufferPass::rendering(SerializedScene const& scene, auto material = MaterialDatabase::instance()->lookup("gua_textured_quad"); - std::string texture_name(node.data.get_texture()); - if (node.data.get_is_stereo_texture()) { + std::string texture_name(node->data.get_texture()); + if (node->data.get_is_stereo_texture()) { if (eye == CameraMode::LEFT) { texture_name += "_left"; @@ -209,22 +209,22 @@ void GBufferPass::rendering(SerializedScene const& scene, auto mapped_flip_x( mesh_shader_->get_uniform_mapping()->get_mapping("gua_textured_quad", "flip_x")); - mesh_shader_->set_uniform(ctx, node.data.get_flip_x(), mapped_flip_x.first, mapped_flip_x.second); + mesh_shader_->set_uniform(ctx, node->data.get_flip_x(), mapped_flip_x.first, mapped_flip_x.second); auto mapped_flip_y( mesh_shader_->get_uniform_mapping()->get_mapping("gua_textured_quad", "flip_y")); - mesh_shader_->set_uniform(ctx, node.data.get_flip_y(), mapped_flip_y.first, mapped_flip_y.second); + mesh_shader_->set_uniform(ctx, node->data.get_flip_y(), mapped_flip_y.first, mapped_flip_y.second); if (material && geometry) { mesh_shader_->set_uniform( ctx, material->get_id(), "gua_material_id"); mesh_shader_->set_uniform( - ctx, node.transform, "gua_model_matrix"); + ctx, node->get_scaled_world_transform(), "gua_model_matrix"); mesh_shader_->set_uniform( ctx, scm::math::transpose( - scm::math::inverse(node.transform)), + scm::math::inverse(node->get_scaled_world_transform())), "gua_normal_matrix"); geometry->draw(ctx); @@ -266,9 +266,9 @@ void GBufferPass::rendering(SerializedScene const& scene, for (auto const& node : scene.nurbsnodes_) { auto geometry = - GeometryDatabase::instance()->lookup(node.data.get_geometry()); + GeometryDatabase::instance()->lookup(node->data.get_geometry()); auto material = - MaterialDatabase::instance()->lookup(node.data.get_material()); + MaterialDatabase::instance()->lookup(node->data.get_material()); #ifdef DEBUG_XFB_OUTPUT scm::gl::transform_feedback_statistics_query_ptr q = ctx @@ -279,10 +279,10 @@ void GBufferPass::rendering(SerializedScene const& scene, nurbs_shader_->get_pre_shader().use(ctx); { nurbs_shader_->get_pre_shader() - .set_uniform(ctx, node.transform, "gua_model_matrix"); + .set_uniform(ctx, node->get_world_transform(), "gua_model_matrix"); nurbs_shader_->get_pre_shader().set_uniform( ctx, - scm::math::transpose(scm::math::inverse(node.transform)), + scm::math::transpose(scm::math::inverse(node->get_world_transform())), "gua_normal_matrix"); ctx.render_context->apply(); @@ -304,11 +304,11 @@ void GBufferPass::rendering(SerializedScene const& scene, nurbs_shader_->set_uniform( ctx, material->get_id(), "gua_material_id"); nurbs_shader_->set_uniform( - ctx, node.transform, "gua_model_matrix"); + ctx, node->get_world_transform(), "gua_model_matrix"); nurbs_shader_->set_uniform( ctx, scm::math::transpose( - scm::math::inverse(node.transform)), + scm::math::inverse(node->get_world_transform())), "gua_normal_matrix"); geometry->draw(ctx); @@ -358,15 +358,14 @@ void GBufferPass::rendering(SerializedScene const& scene, mesh_shader_->use(ctx); // re-use mesh_shader for (auto const& ray : scene.rays_) { - auto geometry = - GeometryDatabase::instance()->lookup(ray.data.get_geometry()); + auto geometry = GeometryDatabase::instance()->lookup("gua_ray_geometry"); mesh_shader_->set_uniform( ctx, bbox_material->get_id(), "gua_material_id"); - mesh_shader_->set_uniform(ctx, ray.transform, "gua_model_matrix"); + mesh_shader_->set_uniform(ctx, ray->get_world_transform(), "gua_model_matrix"); mesh_shader_->set_uniform( ctx, - scm::math::transpose(scm::math::inverse(ray.transform)), + scm::math::transpose(scm::math::inverse(ray->get_world_transform())), "gua_normal_matrix"); geometry->draw(ctx); diff --git a/src/gua/renderer/LightingPass.cpp b/src/gua/renderer/LightingPass.cpp index 8108d114c..6155c3151 100644 --- a/src/gua/renderer/LightingPass.cpp +++ b/src/gua/renderer/LightingPass.cpp @@ -146,30 +146,30 @@ void LightingPass::rendering(SerializedScene const& scene, for (auto const& light : scene.sun_lights_) { - if (light.data.get_enable_shadows()) { + if (light->data.get_enable_shadows()) { shader_->unuse(ctx); target->unbind(ctx); ctx.render_context->reset_state_objects(); float split_0(0.f), split_1(0.f), split_2(0.f), split_3(0.f), split_4(0.f); - if (light.data.get_shadow_cascaded_splits().size() == 5) { + if (light->data.get_shadow_cascaded_splits().size() == 5) { - split_0 = light.data.get_shadow_cascaded_splits()[0]; - split_1 = light.data.get_shadow_cascaded_splits()[1]; - split_2 = light.data.get_shadow_cascaded_splits()[2]; - split_3 = light.data.get_shadow_cascaded_splits()[3]; - split_4 = light.data.get_shadow_cascaded_splits()[4]; + split_0 = light->data.get_shadow_cascaded_splits()[0]; + split_1 = light->data.get_shadow_cascaded_splits()[1]; + split_2 = light->data.get_shadow_cascaded_splits()[2]; + split_3 = light->data.get_shadow_cascaded_splits()[3]; + split_4 = light->data.get_shadow_cascaded_splits()[4]; } else { WARNING("Exactly 5 splits have to be defined for cascaded shadow maps!"); } shadow_map_.render_cascaded(ctx, scene.center_of_interest, scene.frustum, camera, - light.transform, - light.data.get_shadow_map_size(), + light->get_world_transform(), + light->data.get_shadow_map_size(), split_0, split_1, split_2, split_3, split_4, - light.data.get_shadow_near_clipping_in_sun_direction()); + light->data.get_shadow_near_clipping_in_sun_direction()); shader_->use(ctx); target->bind(ctx); @@ -184,7 +184,7 @@ void LightingPass::rendering(SerializedScene const& scene, shader_->set_uniform(ctx, shadow_map_.get_buffer()->get_depth_buffer(), "gua_light_shadow_map"); - float shadow_map_portion(1.f * light.data.get_shadow_map_size() / shadow_map_.get_buffer()->width()); + float shadow_map_portion(1.f * light->data.get_shadow_map_size() / shadow_map_.get_buffer()->width()); shader_->set_uniform(ctx, shadow_map_portion, "gua_light_shadow_map_portion"); for (int i(0); i<4; ++i) { @@ -193,15 +193,15 @@ void LightingPass::rendering(SerializedScene const& scene, } shader_->set_uniform( - ctx, light.data.get_enable_diffuse_shading(), "gua_light_diffuse_enable"); + ctx, light->data.get_enable_diffuse_shading(), "gua_light_diffuse_enable"); shader_->set_uniform(ctx, - light.data.get_enable_specular_shading(), + light->data.get_enable_specular_shading(), "gua_light_specular_enable"); - shader_->set_uniform(ctx, light.transform, "gua_model_matrix"); - shader_->set_uniform(ctx, light.data.get_color().vec3(), "gua_light_color"); - shader_->set_uniform(ctx, light.data.get_enable_shadows(), "gua_light_casts_shadow"); + shader_->set_uniform(ctx, light->get_world_transform(), "gua_model_matrix"); + shader_->set_uniform(ctx, light->data.get_color().vec3(), "gua_light_color"); + shader_->set_uniform(ctx, light->data.get_enable_shadows(), "gua_light_casts_shadow"); shader_->set_uniform( - ctx, light.data.get_shadow_offset(), "gua_shadow_offset"); + ctx, light->data.get_shadow_offset(), "gua_shadow_offset"); fullscreen_quad_->draw(ctx.render_context); } @@ -222,13 +222,13 @@ void LightingPass::rendering(SerializedScene const& scene, for (auto const& light : scene.point_lights_) { shader_->set_uniform( - ctx, light.data.get_enable_diffuse_shading(), "gua_light_diffuse_enable"); + ctx, light->data.get_enable_diffuse_shading(), "gua_light_diffuse_enable"); shader_->set_uniform(ctx, - light.data.get_enable_specular_shading(), + light->data.get_enable_specular_shading(), "gua_light_specular_enable"); - shader_->set_uniform(ctx, light.transform, "gua_model_matrix"); - shader_->set_uniform(ctx, light.data.get_color().vec3(), "gua_light_color"); - shader_->set_uniform(ctx, light.data.get_falloff(), "gua_light_falloff"); + shader_->set_uniform(ctx, light->get_world_transform(), "gua_model_matrix"); + shader_->set_uniform(ctx, light->data.get_color().vec3(), "gua_light_color"); + shader_->set_uniform(ctx, light->data.get_falloff(), "gua_light_falloff"); shader_->set_uniform(ctx, false, "gua_light_casts_shadow"); light_sphere_->draw(ctx); } @@ -246,12 +246,12 @@ void LightingPass::rendering(SerializedScene const& scene, "gua_calculate_spot_light"); for (auto const& light : scene.spot_lights_) { - if (light.data.get_enable_shadows()) { + if (light->data.get_enable_shadows()) { shader_->unuse(ctx); target->unbind(ctx); ctx.render_context->reset_state_objects(); - shadow_map_.render(ctx, scene.center_of_interest, camera, light.transform, light.data.get_shadow_map_size()); + shadow_map_.render(ctx, scene.center_of_interest, camera, light->get_world_transform(), light->data.get_shadow_map_size()); shader_->use(ctx); target->bind(ctx); @@ -266,24 +266,24 @@ void LightingPass::rendering(SerializedScene const& scene, shader_->set_uniform(ctx, shadow_map_.get_buffer()->get_depth_buffer(), "gua_light_shadow_map"); - float shadow_map_portion(1.f * light.data.get_shadow_map_size() / shadow_map_.get_buffer()->width()); + float shadow_map_portion(1.f * light->data.get_shadow_map_size() / shadow_map_.get_buffer()->width()); shader_->set_uniform(ctx, shadow_map_portion, "gua_light_shadow_map_portion"); shader_->set_uniform(ctx, shadow_map_.get_projection_view_matrices()[0], "gua_light_shadow_map_projection_view_matrix_0"); } shader_->set_uniform( - ctx, light.data.get_enable_shadows(), "gua_light_casts_shadow"); + ctx, light->data.get_enable_shadows(), "gua_light_casts_shadow"); shader_->set_uniform( - ctx, light.data.get_enable_diffuse_shading(), "gua_light_diffuse_enable"); + ctx, light->data.get_enable_diffuse_shading(), "gua_light_diffuse_enable"); shader_->set_uniform(ctx, - light.data.get_enable_specular_shading(), + light->data.get_enable_specular_shading(), "gua_light_specular_enable"); - shader_->set_uniform(ctx, light.transform, "gua_model_matrix"); - shader_->set_uniform(ctx, light.data.get_color().vec3(), "gua_light_color"); - shader_->set_uniform(ctx, light.data.get_falloff(), "gua_light_falloff"); - shader_->set_uniform(ctx, light.data.get_softness(), "gua_light_softness"); + shader_->set_uniform(ctx, light->get_world_transform(), "gua_model_matrix"); + shader_->set_uniform(ctx, light->data.get_color().vec3(), "gua_light_color"); + shader_->set_uniform(ctx, light->data.get_falloff(), "gua_light_falloff"); + shader_->set_uniform(ctx, light->data.get_softness(), "gua_light_softness"); shader_->set_uniform( - ctx, light.data.get_shadow_offset(), "gua_shadow_offset"); + ctx, light->data.get_shadow_offset(), "gua_shadow_offset"); light_cone_->draw(ctx); } diff --git a/src/gua/renderer/MaterialLoader.cpp b/src/gua/renderer/MaterialLoader.cpp index d8c5febdf..28bc87eab 100644 --- a/src/gua/renderer/MaterialLoader.cpp +++ b/src/gua/renderer/MaterialLoader.cpp @@ -39,7 +39,7 @@ namespace gua { std::string const MaterialLoader::load_material( aiMaterial const* ai_material, - std::string const& name_prefix) const { + std::string const& file_name) const { auto get_color = [&](const char * pKey, unsigned int type, unsigned int idx)->std::string { @@ -71,12 +71,12 @@ std::string const MaterialLoader::load_material( aiString ai_material_name; ai_material->Get(AI_MATKEY_NAME, ai_material_name); - std::string material_name(name_prefix + "/" + ai_material_name.data); + std::string material_name("type='generated'&source='" + file_name + "'&name='" + ai_material_name.data + "'"); if (!MaterialDatabase::instance()->is_supported(material_name)) { PathParser path; - path.parse(name_prefix); + path.parse(file_name); std::string assets_directory(path.get_path(true)); unsigned capabilities = 0; diff --git a/src/gua/renderer/MeshLoader.cpp b/src/gua/renderer/MeshLoader.cpp index 2caa061f2..9734c1802 100644 --- a/src/gua/renderer/MeshLoader.cpp +++ b/src/gua/renderer/MeshLoader.cpp @@ -92,7 +92,8 @@ std::shared_ptr MeshLoader::load(std::string const& file_name, // new_node = std::make_shared(new GeometryNode("unnamed", // GeometryNode::Configuration("", ""), // math::mat4::identity())); - new_node = get_tree(importer, scene, scene->mRootNode, file_name, flags); + unsigned count(0); + new_node = get_tree(importer, scene, scene->mRootNode, file_name, flags, count); } else { WARNING("Failed to load object \"%s\": No valid root node contained!", @@ -144,12 +145,12 @@ std::shared_ptr MeshLoader::get_tree(std::shared_ptr con aiScene const* ai_scene, aiNode* ai_root, std::string const& file_name, - unsigned flags) { + unsigned flags, unsigned& mesh_count) { // creates a geometry node and returns it auto load_geometry = [&](int i) { // load geometry - std::string mesh_name("mesh_" + string_utils::to_string(mesh_counter_++)); + std::string mesh_name("type='file'&file='" + file_name + "'&id=" + string_utils::to_string(mesh_count++) + "&flags=" + string_utils::to_string(flags)); GeometryDatabase::instance()->add(mesh_name, std::make_shared(ai_scene->mMeshes[ai_root->mMeshes[i]], importer, flags & GeometryLoader::MAKE_PICKABLE)); // load material @@ -173,7 +174,7 @@ std::shared_ptr MeshLoader::get_tree(std::shared_ptr con if (ai_root->mNumChildren == 1 && ai_root->mNumMeshes == 0) { return get_tree( importer, ai_scene, ai_root->mChildren[0], - file_name, flags + file_name, flags, mesh_count ); } @@ -193,7 +194,7 @@ std::shared_ptr MeshLoader::get_tree(std::shared_ptr con group->add_child( get_tree( importer, ai_scene, ai_root->mChildren[i], - file_name, flags + file_name, flags, mesh_count ) ); } diff --git a/src/gua/renderer/PostFXPass.cpp b/src/gua/renderer/PostFXPass.cpp index b456d806f..211a3819c 100644 --- a/src/gua/renderer/PostFXPass.cpp +++ b/src/gua/renderer/PostFXPass.cpp @@ -430,7 +430,7 @@ bool PostFXPass::render_godrays(Camera const& camera, bool any_godrays(false); for (auto const& light: scene.point_lights_) { - if (light.data.get_enable_godrays()) { + if (light->data.get_enable_godrays()) { any_godrays = true; break; } @@ -438,7 +438,7 @@ bool PostFXPass::render_godrays(Camera const& camera, if (!any_godrays) { for (auto const& light: scene.spot_lights_) { - if (light.data.get_enable_godrays()) { + if (light->data.get_enable_godrays()) { any_godrays = true; break; } @@ -447,7 +447,7 @@ bool PostFXPass::render_godrays(Camera const& camera, if (!any_godrays) { for (auto const& light: scene.sun_lights_) { - if (light.data.get_enable_godrays()) { + if (light->data.get_enable_godrays()) { any_godrays = true; break; } @@ -507,21 +507,21 @@ bool PostFXPass::render_godrays(Camera const& camera, "gua_calculate_by_position"); for (auto const& light: scene.point_lights_) { - if (light.data.get_enable_godrays()) { - god_ray_shader_->set_uniform(ctx, light.data.get_color().vec3(), "gua_light_color"); - god_ray_shader_->set_uniform(ctx, math::vec3(light.transform.column(3)[0], - light.transform.column(3)[1], - light.transform.column(3)[2]), "gua_light_position_direction"); + if (light->data.get_enable_godrays()) { + god_ray_shader_->set_uniform(ctx, light->data.get_color().vec3(), "gua_light_color"); + god_ray_shader_->set_uniform(ctx, math::vec3(light->get_world_transform().column(3)[0], + light->get_world_transform().column(3)[1], + light->get_world_transform().column(3)[2]), "gua_light_position_direction"); render(); } } for (auto const& light: scene.spot_lights_) { - if (light.data.get_enable_godrays()) { - god_ray_shader_->set_uniform(ctx, light.data.get_color().vec3(), "gua_light_color"); - god_ray_shader_->set_uniform(ctx, math::vec3(light.transform.column(3)[0], - light.transform.column(3)[1], - light.transform.column(3)[2]), "gua_light_position_direction"); + if (light->data.get_enable_godrays()) { + god_ray_shader_->set_uniform(ctx, light->data.get_color().vec3(), "gua_light_color"); + god_ray_shader_->set_uniform(ctx, math::vec3(light->get_world_transform().column(3)[0], + light->get_world_transform().column(3)[1], + light->get_world_transform().column(3)[2]), "gua_light_position_direction"); render(); } } @@ -532,10 +532,10 @@ bool PostFXPass::render_godrays(Camera const& camera, "gua_calculate_by_direction"); for (auto const& light: scene.sun_lights_) { - if (light.data.get_enable_godrays()) { - god_ray_shader_->set_uniform(ctx, light.data.get_color().vec3(), "gua_light_color"); + if (light->data.get_enable_godrays()) { + god_ray_shader_->set_uniform(ctx, light->data.get_color().vec3(), "gua_light_color"); math::vec3 direction(0, 0, 1); - direction = light.transform * direction; + direction = light->get_world_transform() * direction; god_ray_shader_->set_uniform(ctx, direction, "gua_light_position_direction"); render(); } diff --git a/src/gua/renderer/Serializer.cpp b/src/gua/renderer/Serializer.cpp index 202773176..6e9af431d 100644 --- a/src/gua/renderer/Serializer.cpp +++ b/src/gua/renderer/Serializer.cpp @@ -24,7 +24,6 @@ // guacamole headers #include -#include #include #include @@ -173,7 +172,7 @@ void Serializer::check(SerializedScene* output, if (mesh_ptr) { - data_->meshnodes_.push_back(make_serialized_node(node->get_world_transform(), node->data)); + data_->meshnodes_.push_back(node); } else { @@ -181,7 +180,7 @@ void Serializer::check(SerializedScene* output, gua::GeometryDatabase::instance()->lookup(node->data.get_geometry())); if (nurbs_ptr) { - data_->nurbsnodes_.push_back(make_serialized_node(node->get_world_transform(), node->data)); + data_->nurbsnodes_.push_back(node); } } } @@ -199,7 +198,7 @@ void Serializer::check(SerializedScene* output, if ( is_visible(node) ) { if ( !node->data.get_volume().empty() ) { add_bbox(node); - data_->volumenodes_.push_back(make_serialized_node(node->get_world_transform(), node->data)); + data_->volumenodes_.push_back(node); } visit_children(node); @@ -214,7 +213,7 @@ void Serializer::check(SerializedScene* output, add_bbox(node); - data_->point_lights_.push_back(make_serialized_node(node->get_world_transform(), node->data)); + data_->point_lights_.push_back(node); visit_children(node); } @@ -228,8 +227,7 @@ void Serializer::check(SerializedScene* output, add_bbox(node); - data_->spot_lights_ - .push_back(make_serialized_node(node->get_world_transform(), node->data)); + data_->spot_lights_.push_back(node); visit_children(node); } @@ -240,8 +238,7 @@ void Serializer::check(SerializedScene* output, /* virtual */ void Serializer::visit(SunLightNode* node) { if (is_visible(node)) { - data_->sun_lights_ - .push_back(make_serialized_node(node->get_world_transform(), node->data)); + data_->sun_lights_.push_back(node); visit_children(node); } @@ -254,10 +251,7 @@ void Serializer::check(SerializedScene* output, if (is_visible(node)) { if (draw_rays_) { - GeometryNode::Configuration config; - config.set_geometry("gua_ray_geometry"); - config.set_material("gua_bounding_box"); - data_->rays_.push_back(make_serialized_node(node->get_world_transform(), config)); + data_->rays_.push_back(node); } visit_children(node); @@ -272,8 +266,7 @@ void Serializer::check(SerializedScene* output, add_bbox(node); - data_->textured_quads_ - .push_back(make_serialized_node(node->get_scaled_world_transform(), node->data)); + data_->textured_quads_.push_back(node); visit_children(node); } diff --git a/src/gua/renderer/ShadowMap.cpp b/src/gua/renderer/ShadowMap.cpp index a0b45c3e7..3e354c7a1 100644 --- a/src/gua/renderer/ShadowMap.cpp +++ b/src/gua/renderer/ShadowMap.cpp @@ -154,17 +154,17 @@ void ShadowMap::render_geometry(RenderContext const & ctx, mesh_shader_->set_uniform(ctx, scm::math::inverse(projection * view_matrix), "gua_inverse_projection_view_matrix"); for (auto const& node : scene.meshnodes_) { - auto geometry = GeometryDatabase::instance()->lookup(node.data.get_geometry()); - auto material = MaterialDatabase::instance()->lookup(node.data.get_material()); + auto geometry = GeometryDatabase::instance()->lookup(node->data.get_geometry()); + auto material = MaterialDatabase::instance()->lookup(node->data.get_material()); if (geometry) { mesh_shader_->set_uniform( ctx, material->get_id(), "gua_material_id"); mesh_shader_->set_uniform( - ctx, node.transform, "gua_model_matrix"); + ctx, node->get_world_transform(), "gua_model_matrix"); mesh_shader_->set_uniform( ctx, scm::math::transpose( - scm::math::inverse(node.transform)), + scm::math::inverse(node->get_world_transform())), "gua_normal_matrix"); geometry->draw(ctx); } From 4921639eabd117a707b435c600bdeee5459d73f3 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Thu, 6 Mar 2014 16:47:34 +0100 Subject: [PATCH 142/146] geometries and materials are loaded automatically now, if not present in the databases --- include/gua/databases/GeometryDatabase.hpp | 10 --- include/gua/renderer/GeometryLoader.hpp | 2 + include/gua/renderer/MaterialDescription.hpp | 3 + include/gua/scenegraph/GeometryNode.hpp | 34 ++++---- src/gua/databases/GeometryDatabase.cpp | 5 -- src/gua/databases/ShadingModelDatabase.cpp | 4 +- src/gua/physics/ConvexHullShape.cpp | 4 +- src/gua/physics/TriangleMeshShape.cpp | 2 +- src/gua/renderer/GBufferPass.cpp | 8 +- src/gua/renderer/GeometryLoader.cpp | 26 ++++--- src/gua/renderer/Material.cpp | 13 +++- src/gua/renderer/MaterialDescription.cpp | 4 + src/gua/renderer/MeshLoader.cpp | 6 +- src/gua/renderer/NURBSLoader.cpp | 4 +- src/gua/renderer/Serializer.cpp | 8 +- src/gua/renderer/ShadowMap.cpp | 4 +- src/gua/renderer/UberShaderFactory.cpp | 22 +++++- src/gua/scenegraph/GeometryNode.cpp | 82 ++++++++++++++++++-- src/gua/utils/DotGenerator.cpp | 8 +- 19 files changed, 174 insertions(+), 75 deletions(-) diff --git a/include/gua/databases/GeometryDatabase.hpp b/include/gua/databases/GeometryDatabase.hpp index c5c10c718..f7bdcf20b 100644 --- a/include/gua/databases/GeometryDatabase.hpp +++ b/include/gua/databases/GeometryDatabase.hpp @@ -41,16 +41,6 @@ class GUA_DLL GeometryDatabase : public Database, public Singleton { public: - /** - * Loads a geometry file to the database. - * - * This method loads a geometry to the data base. - * - * \param id An absolute or relative path to the - * geometry file. - */ - void load(std::string const& id); - friend class Singleton; private: diff --git a/include/gua/renderer/GeometryLoader.hpp b/include/gua/renderer/GeometryLoader.hpp index 8a02b4a9d..d2069436f 100644 --- a/include/gua/renderer/GeometryLoader.hpp +++ b/include/gua/renderer/GeometryLoader.hpp @@ -54,6 +54,8 @@ class GUA_DLL GeometryLoader { virtual ~GeometryLoader(); + std::shared_ptr load_geometry(std::string const& file_name, unsigned flags = DEFAULTS); + std::shared_ptr create_geometry_from_file(std::string const& node_name, std::string const& file_name, std::string const& fallback_material, diff --git a/include/gua/renderer/MaterialDescription.hpp b/include/gua/renderer/MaterialDescription.hpp index efde2317f..51471ff4c 100644 --- a/include/gua/renderer/MaterialDescription.hpp +++ b/include/gua/renderer/MaterialDescription.hpp @@ -78,6 +78,7 @@ class MaterialDescription { inline void set_shading_model(std::string const& s) { shading_model_ = s; } inline std::string const& get_shading_model() const { return shading_model_; } + inline std::unordered_map& get_uniforms() { return uniforms_; } @@ -86,6 +87,8 @@ class MaterialDescription { return uniforms_; } + std::string const& get_filename() const {return file_name_;} + void save_to_file(std::string const& file_name) const; private: diff --git a/include/gua/scenegraph/GeometryNode.hpp b/include/gua/scenegraph/GeometryNode.hpp index 1cccde639..5cea0f6cd 100644 --- a/include/gua/scenegraph/GeometryNode.hpp +++ b/include/gua/scenegraph/GeometryNode.hpp @@ -43,22 +43,17 @@ namespace gua { class GUA_DLL GeometryNode : public Node { public: - struct Configuration { - /** - * A string referring to an entry in guacamole's GeometryDatabase. - */ - GUA_ADD_PROPERTY(std::string, geometry, "gua_default_geometry"); - - /** - * A string referring to an entry in guacamole's MaterialDatabase. - */ - GUA_ADD_PROPERTY(std::string, material, "gua_default_material"); - }; + /** + * A string referring to an entry in guacamole's GeometryDatabase. + */ + std::string const& get_geometry() const { return geometry_; } + void set_geometry(std::string const& v) { geometry_ = v; geometry_changed_ = self_dirty_ = true; } /** - * The GeometryNode's configuration. - */ - Configuration data; + * A string referring to an entry in guacamole's MaterialDatabase. + */ + std::string const& get_material() const { return material_; } + void set_material(std::string const& v) { material_ = v; material_changed_ = self_dirty_ = true; } /** * Constructor. @@ -80,7 +75,8 @@ class GUA_DLL GeometryNode : public Node { * transformation. */ GeometryNode(std::string const& name, - Configuration const& configuration = Configuration(), + std::string const& geometry = "gua_default_geometry", + std::string const& material = "gua_default_material", math::mat4 const& transform = math::mat4::identity()); /** @@ -100,12 +96,20 @@ class GUA_DLL GeometryNode : public Node { */ /*virtual*/ void update_bounding_box() const; + /*virtual*/ void update_cache(); + /*virtual*/ void ray_test_impl(RayNode const& ray, PickResult::Options options, Mask const& mask, std::set& hits); private: std::shared_ptr copy() const; + + std::string geometry_; + std::string material_; + + bool geometry_changed_; + bool material_changed_; }; } diff --git a/src/gua/databases/GeometryDatabase.cpp b/src/gua/databases/GeometryDatabase.cpp index 691ca4a87..b36e8582f 100644 --- a/src/gua/databases/GeometryDatabase.cpp +++ b/src/gua/databases/GeometryDatabase.cpp @@ -32,11 +32,6 @@ namespace gua { //////////////////////////////////////////////////////////////////////////////// -void GeometryDatabase::load(std::string const& id) { - // auto geo(new Geometry(id)); - // instance()->add(id, std::shared_ptr(geo)); -} - //////////////////////////////////////////////////////////////////////////////// } diff --git a/src/gua/databases/ShadingModelDatabase.cpp b/src/gua/databases/ShadingModelDatabase.cpp index cc4633f31..9fba3433c 100644 --- a/src/gua/databases/ShadingModelDatabase.cpp +++ b/src/gua/databases/ShadingModelDatabase.cpp @@ -43,8 +43,8 @@ void ShadingModelDatabase::load_shading_models_from( unsigned suffix_pos = unsigned(parse_string.find(".gsd")); if (parse_string.length() - suffix_pos == 4) { - auto name(parse_string.substr(0, suffix_pos)); - auto mod = std::make_shared(name, dir.get_directory_name() + parse_string); + auto name(dir.get_directory_name() + parse_string); + auto mod = std::make_shared(name, name); instance()->add(name, mod); } diff --git a/src/gua/physics/ConvexHullShape.cpp b/src/gua/physics/ConvexHullShape.cpp index 6acbb536d..d940f2be6 100644 --- a/src/gua/physics/ConvexHullShape.cpp +++ b/src/gua/physics/ConvexHullShape.cpp @@ -152,13 +152,13 @@ void ConvexHullShape::build_from_geometry( for (auto const& n : node->get_children()) { auto gnode = std::dynamic_pointer_cast(n); if (gnode) { - geom_list.push_back(gnode->data.get_geometry()); + geom_list.push_back(gnode->get_geometry()); } } } else { auto gnode = std::dynamic_pointer_cast(node); if (gnode) { - geom_list.push_back(gnode->data.get_geometry()); + geom_list.push_back(gnode->get_geometry()); } } diff --git a/src/gua/physics/TriangleMeshShape.cpp b/src/gua/physics/TriangleMeshShape.cpp index 1f77a89e2..9d4b130c0 100644 --- a/src/gua/physics/TriangleMeshShape.cpp +++ b/src/gua/physics/TriangleMeshShape.cpp @@ -169,7 +169,7 @@ void TriangleMeshShape::set_scaling(const math::vec3 & scaling) { auto gnode = std::dynamic_pointer_cast(node); if (gnode) { - geom_list.push_back(gnode->data.get_geometry()); + geom_list.push_back(gnode->get_geometry()); } for (auto const& n: node->get_children()) { diff --git a/src/gua/renderer/GBufferPass.cpp b/src/gua/renderer/GBufferPass.cpp index 0f71a0b11..04fcc2c28 100644 --- a/src/gua/renderer/GBufferPass.cpp +++ b/src/gua/renderer/GBufferPass.cpp @@ -154,9 +154,9 @@ void GBufferPass::rendering(SerializedScene const& scene, for (auto const& node : scene.meshnodes_) { auto geometry = - GeometryDatabase::instance()->lookup(node->data.get_geometry()); + GeometryDatabase::instance()->lookup(node->get_geometry()); auto material = - MaterialDatabase::instance()->lookup(node->data.get_material()); + MaterialDatabase::instance()->lookup(node->get_material()); if (material && geometry) { mesh_shader_->set_uniform( @@ -266,9 +266,9 @@ void GBufferPass::rendering(SerializedScene const& scene, for (auto const& node : scene.nurbsnodes_) { auto geometry = - GeometryDatabase::instance()->lookup(node->data.get_geometry()); + GeometryDatabase::instance()->lookup(node->get_geometry()); auto material = - MaterialDatabase::instance()->lookup(node->data.get_material()); + MaterialDatabase::instance()->lookup(node->get_material()); #ifdef DEBUG_XFB_OUTPUT scm::gl::transform_feedback_statistics_query_ptr q = ctx diff --git a/src/gua/renderer/GeometryLoader.cpp b/src/gua/renderer/GeometryLoader.cpp index cd19fa2d2..d7f942784 100644 --- a/src/gua/renderer/GeometryLoader.cpp +++ b/src/gua/renderer/GeometryLoader.cpp @@ -63,12 +63,7 @@ GeometryLoader::~GeometryLoader() { //////////////////////////////////////////////////////////////////////////////// -std::shared_ptr GeometryLoader::create_geometry_from_file - (std::string const& node_name, - std::string const& file_name, - std::string const& fallback_material, - unsigned flags) { - +std::shared_ptr GeometryLoader::load_geometry(std::string const& file_name, unsigned flags) { std::shared_ptr cached_node; std::string key(file_name + "_" + string_utils::to_string(flags)); @@ -116,6 +111,19 @@ std::shared_ptr GeometryLoader::create_geometry_from_file } } + return cached_node; +} + +//////////////////////////////////////////////////////////////////////////////// + +std::shared_ptr GeometryLoader::create_geometry_from_file + (std::string const& node_name, + std::string const& file_name, + std::string const& fallback_material, + unsigned flags) { + + auto cached_node(load_geometry(file_name, flags)); + if (cached_node) { auto copy(cached_node->deep_copy()); @@ -150,7 +158,7 @@ std::shared_ptr GeometryLoader::create_volume_from_file(std::string const& loaded_files_.insert(std::make_pair(key, cached_node)); // normalize volume position and rotation - if ( flags & VolumeLoader::NORMALIZE_POSITION + if ( flags & VolumeLoader::NORMALIZE_POSITION || flags & VolumeLoader::NORMALIZE_SCALE) { auto bbox = cached_node->get_bounding_box(); @@ -196,8 +204,8 @@ void GeometryLoader::apply_fallback_material(std::shared_ptr const& root, auto g_node(std::dynamic_pointer_cast(root)); if (g_node) { - if (g_node->data.get_material().empty()) { - g_node->data.set_material(fallback_material); + if (g_node->get_material().empty()) { + g_node->set_material(fallback_material); } } diff --git a/src/gua/renderer/Material.cpp b/src/gua/renderer/Material.cpp index 650d7243c..9470f33f4 100644 --- a/src/gua/renderer/Material.cpp +++ b/src/gua/renderer/Material.cpp @@ -114,10 +114,17 @@ void Material::reload() { void Material::load_description() { uniform_values_.clear(); - auto shading_model(ShadingModelDatabase::instance()->lookup( - description_.get_shading_model())); + std::string shading_model(description_.get_shading_model()); + std::shared_ptr mod; + + if (!ShadingModelDatabase::instance()->is_supported(shading_model)) { + mod = std::make_shared(shading_model, shading_model); + ShadingModelDatabase::instance()->add(shading_model, mod); + } else { + mod = ShadingModelDatabase::instance()->lookup(shading_model); + } - for (auto& stage : shading_model->get_stages()) { + for (auto& stage : mod->get_stages()) { for (auto const& uniform : stage.get_uniforms()) { std::string value(description_.get_uniforms()[uniform.first]); uniform_values_[uniform.first] = diff --git a/src/gua/renderer/MaterialDescription.cpp b/src/gua/renderer/MaterialDescription.cpp index 1df1467ea..03de5805f 100644 --- a/src/gua/renderer/MaterialDescription.cpp +++ b/src/gua/renderer/MaterialDescription.cpp @@ -70,6 +70,10 @@ void MaterialDescription::reload() { "File does not exist!", file_name_.c_str()); } + + PathParser p; + p.parse(file_name_); + shading_model_ = p.get_path(true) + shading_model_; } } diff --git a/src/gua/renderer/MeshLoader.cpp b/src/gua/renderer/MeshLoader.cpp index 9734c1802..59380850d 100644 --- a/src/gua/renderer/MeshLoader.cpp +++ b/src/gua/renderer/MeshLoader.cpp @@ -150,7 +150,7 @@ std::shared_ptr MeshLoader::get_tree(std::shared_ptr con // creates a geometry node and returns it auto load_geometry = [&](int i) { // load geometry - std::string mesh_name("type='file'&file='" + file_name + "'&id=" + string_utils::to_string(mesh_count++) + "&flags=" + string_utils::to_string(flags)); + std::string mesh_name("type=file&file=" + file_name + "&id=" + string_utils::to_string(mesh_count++) + "&flags=" + string_utils::to_string(flags)); GeometryDatabase::instance()->add(mesh_name, std::make_shared(ai_scene->mMeshes[ai_root->mMeshes[i]], importer, flags & GeometryLoader::MAKE_PICKABLE)); // load material @@ -164,8 +164,8 @@ std::shared_ptr MeshLoader::get_tree(std::shared_ptr con } auto result(std::make_shared(mesh_name)); - result->data.set_geometry(mesh_name); - result->data.set_material(material_name); + result->set_geometry(mesh_name); + result->set_material(material_name); return result; }; diff --git a/src/gua/renderer/NURBSLoader.cpp b/src/gua/renderer/NURBSLoader.cpp index 644ad86e4..efd565cf8 100644 --- a/src/gua/renderer/NURBSLoader.cpp +++ b/src/gua/renderer/NURBSLoader.cpp @@ -64,8 +64,8 @@ std::shared_ptr NURBSLoader::load(std::string const& file_name, file_name, std::make_shared(bezier_object)); auto result = std::make_shared("unnamed_nurbs"); - result->data.set_geometry(file_name); - result->data.set_material(""); + result->set_geometry(file_name); + result->set_material(""); return result; diff --git a/src/gua/renderer/Serializer.cpp b/src/gua/renderer/Serializer.cpp index 6e9af431d..0f911d35e 100644 --- a/src/gua/renderer/Serializer.cpp +++ b/src/gua/renderer/Serializer.cpp @@ -163,12 +163,12 @@ void Serializer::check(SerializedScene* output, /* virtual */ void Serializer::visit(GeometryNode* node) { if (is_visible(node)) { - if (!node->data.get_geometry().empty() && !node->data.get_material().empty()) { + if (!node->get_geometry().empty() && !node->get_material().empty()) { add_bbox(node); std::shared_ptr mesh_ptr = std::dynamic_pointer_cast( - gua::GeometryDatabase::instance()->lookup(node->data.get_geometry())); + gua::GeometryDatabase::instance()->lookup(node->get_geometry())); if (mesh_ptr) { @@ -177,7 +177,7 @@ void Serializer::check(SerializedScene* output, } else { std::shared_ptr nurbs_ptr = std::dynamic_pointer_cast( - gua::GeometryDatabase::instance()->lookup(node->data.get_geometry())); + gua::GeometryDatabase::instance()->lookup(node->get_geometry())); if (nurbs_ptr) { data_->nurbsnodes_.push_back(node); @@ -185,7 +185,7 @@ void Serializer::check(SerializedScene* output, } } - data_->materials_.insert(node->data.get_material()); + data_->materials_.insert(node->get_material()); visit_children(node); } diff --git a/src/gua/renderer/ShadowMap.cpp b/src/gua/renderer/ShadowMap.cpp index 3e354c7a1..954ad6f43 100644 --- a/src/gua/renderer/ShadowMap.cpp +++ b/src/gua/renderer/ShadowMap.cpp @@ -154,8 +154,8 @@ void ShadowMap::render_geometry(RenderContext const & ctx, mesh_shader_->set_uniform(ctx, scm::math::inverse(projection * view_matrix), "gua_inverse_projection_view_matrix"); for (auto const& node : scene.meshnodes_) { - auto geometry = GeometryDatabase::instance()->lookup(node->data.get_geometry()); - auto material = MaterialDatabase::instance()->lookup(node->data.get_material()); + auto geometry = GeometryDatabase::instance()->lookup(node->get_geometry()); + auto material = MaterialDatabase::instance()->lookup(node->get_material()); if (geometry) { mesh_shader_->set_uniform( ctx, material->get_id(), "gua_material_id"); diff --git a/src/gua/renderer/UberShaderFactory.cpp b/src/gua/renderer/UberShaderFactory.cpp index 6946f68ae..6edd0bb4d 100644 --- a/src/gua/renderer/UberShaderFactory.cpp +++ b/src/gua/renderer/UberShaderFactory.cpp @@ -65,7 +65,16 @@ UberShaderFactory::UberShaderFactory( // store material specific main method calls std::stringstream call; call << "/* " << mat->get_name() << " */ "; - call << shading_model->get_name() << "_main("; + + auto name(shading_model->get_name()); + std::replace(name.begin(), name.end(), '/', '_'); + std::replace(name.begin(), name.end(), '\\', '_'); + std::replace(name.begin(), name.end(), '.', '_'); + std::replace(name.begin(), name.end(), '(', '_'); + std::replace(name.begin(), name.end(), ')', '_'); + std::replace(name.begin(), name.end(), ' ', '_'); + + call << name << "_main("; auto uniform(shading_model->get_stages()[stage].get_uniforms().begin()); @@ -198,7 +207,16 @@ void UberShaderFactory::load_main_functions(std::shared_ptr const& // write main() method _main() ---------------------------- std::stringstream stream; - stream << "void " << model->get_name() << "_main("; + + auto name(model->get_name()); + std::replace(name.begin(), name.end(), '/', '_'); + std::replace(name.begin(), name.end(), '\\', '_'); + std::replace(name.begin(), name.end(), '.', '_'); + std::replace(name.begin(), name.end(), '(', '_'); + std::replace(name.begin(), name.end(), ')', '_'); + std::replace(name.begin(), name.end(), ' ', '_'); + + stream << "void " << name << "_main("; auto uniform(model->get_stages()[stage_].get_uniforms().begin()); diff --git a/src/gua/scenegraph/GeometryNode.cpp b/src/gua/scenegraph/GeometryNode.cpp index c709977ce..fda38f939 100644 --- a/src/gua/scenegraph/GeometryNode.cpp +++ b/src/gua/scenegraph/GeometryNode.cpp @@ -25,6 +25,7 @@ // guacamole headers #include #include +#include #include #include #include @@ -33,19 +34,23 @@ namespace gua { GeometryNode::GeometryNode(std::string const& name, - Configuration const& configuration, + std::string const& geometry, + std::string const& material, math::mat4 const& transform) - : Node(name, transform), data(configuration) {} + : Node(name, transform), geometry_(geometry), material_(material), + geometry_changed_(false), material_changed_(false) {} /* virtual */ void GeometryNode::accept(NodeVisitor& visitor) { visitor.visit(this); } +//////////////////////////////////////////////////////////////////////////////// + void GeometryNode::update_bounding_box() const { - if (data.get_geometry() != "") { - auto geometry_bbox(GeometryDatabase::instance()->lookup(data.get_geometry())->get_bounding_box()); + if (get_geometry() != "") { + auto geometry_bbox(GeometryDatabase::instance()->lookup(get_geometry())->get_bounding_box()); bounding_box_ = transform(geometry_bbox, world_transform_); for (auto child : get_children()) { @@ -57,6 +62,69 @@ void GeometryNode::update_bounding_box() const { } } +//////////////////////////////////////////////////////////////////////////////// + +void GeometryNode::update_cache() { + + // The code below auto-loads a geometry if it's not already supported by + // the GeometryDatabase. It expects a geometry name like + // + // "type='file'&file='data/objects/monkey.obj'&id=0&flags=0" + + if (geometry_changed_) { + if (geometry_ != "") { + if (!GeometryDatabase::instance()->is_supported(geometry_)) { + auto params(string_utils::split(geometry_, '&')); + if (params.size() == 4) { + if (params[0] == "type=file") { + auto tmp_filename(string_utils::split(params[1], '=')); + auto tmp_flags(string_utils::split(params[3], '=')); + if (tmp_filename.size() == 2 && tmp_flags.size() == 2) { + std::string filename(tmp_filename[1]); + std::string flags_string(tmp_flags[1]); + unsigned flags(0); + std::stringstream sstr(flags_string); + sstr >> flags; + + GeometryLoader loader; + loader.load_geometry(filename, flags); + + } else { + WARNING("Failed to auto-load geometry %s: Failed to extract filename and/or loading flags!", geometry_.c_str()); + } + } else { + WARNING("Failed to auto-load geometry %s: Type is not supported!", geometry_.c_str()); + } + } else { + WARNING("Failed to auto-load geometry %s: The name does not contain a type, file, id and flag parameter!", geometry_.c_str()); + } + } + } + + geometry_changed_ = false; + } + + // The code below auto-loads a material if it's not already supported by + // the MaterialDatabase. It expects a material name like + // + // data/materials/Stones.gmd + + if (material_changed_) { + if (material_ != "") { + if (!MaterialDatabase::instance()->is_supported(material_)) { + auto mat = std::make_shared(material_, MaterialDescription(material_)); + MaterialDatabase::instance()->add(material_, mat); + } + } + + material_changed_ = false; + } + + Node::update_cache(); +} + +//////////////////////////////////////////////////////////////////////////////// + void GeometryNode::ray_test_impl(RayNode const& ray, PickResult::Options options, Mask const& mask, std::set& hits) { @@ -79,9 +147,9 @@ void GeometryNode::ray_test_impl(RayNode const& ray, PickResult::Options options } // bbox is intersected, but check geometry only if mask tells us to check - if (data.get_geometry() != "" && mask.check(get_groups())) { + if (get_geometry() != "" && mask.check(get_groups())) { - auto geometry(GeometryDatabase::instance()->lookup(data.get_geometry())); + auto geometry(GeometryDatabase::instance()->lookup(get_geometry())); if (geometry) { @@ -183,7 +251,7 @@ void GeometryNode::ray_test_impl(RayNode const& ray, PickResult::Options options } std::shared_ptr GeometryNode::copy() const { - return std::make_shared(get_name(), data, get_transform()); + return std::make_shared(get_name(), geometry_, material_, get_transform()); } } diff --git a/src/gua/utils/DotGenerator.cpp b/src/gua/utils/DotGenerator.cpp index 6f78ec0b0..83c8fdb83 100644 --- a/src/gua/utils/DotGenerator.cpp +++ b/src/gua/utils/DotGenerator.cpp @@ -101,10 +101,10 @@ void DotGenerator::parse_graph(SceneGraph const* graph) { std::string fillcolor("[fillcolor ="); fillcolor += " \"#CCCCCC\""; - if (geometry->data.get_geometry() != "") - parse_data_ += "| geometry: " + geometry->data.get_geometry(); - if (geometry->data.get_material() != "") - parse_data_ += "| material: " + geometry->data.get_material(); + if (geometry->get_geometry() != "") + parse_data_ += "| geometry: " + geometry->get_geometry(); + if (geometry->get_material() != "") + parse_data_ += "| material: " + geometry->get_material(); fillcolor += "]"; From b2386e838c211b8ba3e4ccd117c27f5d838a5fde Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Fri, 7 Mar 2014 11:23:18 +0100 Subject: [PATCH 143/146] added explicit loading functions to databases --- include/gua/databases/MaterialDatabase.hpp | 8 +++++--- include/gua/databases/ShadingModelDatabase.hpp | 9 +++++---- src/gua/databases/MaterialDatabase.cpp | 18 +++++++++++------- src/gua/databases/ShadingModelDatabase.cpp | 17 ++++++++++++----- 4 files changed, 33 insertions(+), 19 deletions(-) diff --git a/include/gua/databases/MaterialDatabase.hpp b/include/gua/databases/MaterialDatabase.hpp index ee41347d2..bdf0ac681 100644 --- a/include/gua/databases/MaterialDatabase.hpp +++ b/include/gua/databases/MaterialDatabase.hpp @@ -47,10 +47,12 @@ class GUA_DLL MaterialDatabase : public Database, * * This method loads gmd materials to the data base. * - * \param path_to_materials An absolute or relative path to the - * directory containing gmd files. + * \param directory An absolute or relative path to the + * directory containing gmd files. */ - static void load_materials_from(std::string const& path_to_materials); + static void load_materials_from(std::string const& directory); + + static void load_material(std::string const& filename); void reload_all(); diff --git a/include/gua/databases/ShadingModelDatabase.hpp b/include/gua/databases/ShadingModelDatabase.hpp index e4af311e3..91966db36 100644 --- a/include/gua/databases/ShadingModelDatabase.hpp +++ b/include/gua/databases/ShadingModelDatabase.hpp @@ -46,11 +46,12 @@ class GUA_DLL ShadingModelDatabase : public Database, * * This method loads gsd shading models to the data base. * - * \param path_to_shading_models An absolute or relative path to the - * directory containing gsd files. + * \param directory An absolute or relative path to the + * directory containing gsd files. */ - static void load_shading_models_from( - std::string const& path_to_shading_models); + static void load_shading_models_from(std::string const& directory); + + static void load_shading_model(std::string const& filename); void reload_all(); diff --git a/src/gua/databases/MaterialDatabase.cpp b/src/gua/databases/MaterialDatabase.cpp index 654db5ebe..1b9ccfe07 100644 --- a/src/gua/databases/MaterialDatabase.cpp +++ b/src/gua/databases/MaterialDatabase.cpp @@ -32,10 +32,9 @@ namespace gua { //////////////////////////////////////////////////////////////////////////////// -void MaterialDatabase::load_materials_from( - std::string const& path_to_materials) { +void MaterialDatabase::load_materials_from(std::string const& directory) { - gua::Directory dir(path_to_materials); + gua::Directory dir(directory); std::stringstream content(dir.get_content()); std::string parse_string; @@ -44,12 +43,17 @@ void MaterialDatabase::load_materials_from( if (parse_string.length() - suffix_pos == 4) { auto name(dir.get_directory_name() + parse_string); + load_material(name); + } + } +} - auto mat = std::make_shared( - name, MaterialDescription(dir.get_directory_name() + parse_string)); +//////////////////////////////////////////////////////////////////////////////// - instance()->add(name, mat); - } +void MaterialDatabase::load_material(std::string const& filename) { + if (!instance()->is_supported(filename)) { + auto mat = std::make_shared(filename, filename); + instance()->add(filename, mat); } } diff --git a/src/gua/databases/ShadingModelDatabase.cpp b/src/gua/databases/ShadingModelDatabase.cpp index 9fba3433c..cec1dffaa 100644 --- a/src/gua/databases/ShadingModelDatabase.cpp +++ b/src/gua/databases/ShadingModelDatabase.cpp @@ -33,9 +33,9 @@ namespace gua { //////////////////////////////////////////////////////////////////////////////// void ShadingModelDatabase::load_shading_models_from( - std::string const& path_to_shading_models) { + std::string const& directory) { - gua::Directory dir(path_to_shading_models); + gua::Directory dir(directory); std::stringstream content(dir.get_content()); std::string parse_string; @@ -44,15 +44,22 @@ void ShadingModelDatabase::load_shading_models_from( if (parse_string.length() - suffix_pos == 4) { auto name(dir.get_directory_name() + parse_string); - auto mod = std::make_shared(name, name); - - instance()->add(name, mod); + load_shading_model(name); } } } //////////////////////////////////////////////////////////////////////////////// +void ShadingModelDatabase::load_shading_model(std::string const& filename) { + if (!instance()->is_supported(filename)) { + auto mod = std::make_shared(filename, filename); + instance()->add(filename, mod); + } +} + +//////////////////////////////////////////////////////////////////////////////// + void ShadingModelDatabase::reload_all() { for (auto const& date: data_) { date.second->reload(); From d8f809e90826c19b7c98c56e21181099a374b6f9 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Fri, 7 Mar 2014 13:54:23 +0100 Subject: [PATCH 144/146] Ignore executable. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 5f02af41f..a103ccdce 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,4 @@ tags .nfs* .vim.custom tests/tests +plugins/guacamole-oculus/example-oculus/example-oculus From 117639aa216c31379923fc8c3fe15734689dc826 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Fri, 7 Mar 2014 13:57:37 +0100 Subject: [PATCH 145/146] Change to absolute database keys. --- .../example-oculus/data/materials/Stones.gmd | 4 ++-- .../example-oculus/data/materials/White.gmd | 4 ++-- plugins/guacamole-oculus/example-oculus/main.cpp | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/guacamole-oculus/example-oculus/data/materials/Stones.gmd b/plugins/guacamole-oculus/example-oculus/data/materials/Stones.gmd index f574e692f..66a05f4cf 100644 --- a/plugins/guacamole-oculus/example-oculus/data/materials/Stones.gmd +++ b/plugins/guacamole-oculus/example-oculus/data/materials/Stones.gmd @@ -1,7 +1,7 @@ { - "shading_model" : "ComplexTexture", - "uniforms" : + "shading_model" : "ComplexTexture.gsd", + "uniforms" : { "diffuse_map" : "data/textures/stones_diffuse.jpg", "emit" : "0.1", diff --git a/plugins/guacamole-oculus/example-oculus/data/materials/White.gmd b/plugins/guacamole-oculus/example-oculus/data/materials/White.gmd index b85505972..16b84b973 100644 --- a/plugins/guacamole-oculus/example-oculus/data/materials/White.gmd +++ b/plugins/guacamole-oculus/example-oculus/data/materials/White.gmd @@ -1,7 +1,7 @@ { - "shading_model" : "Shadeless", - "uniforms" : + "shading_model" : "Shadeless.gsd", + "uniforms" : { "diffuse_color" : "(1.000 1.000 1.000)" } diff --git a/plugins/guacamole-oculus/example-oculus/main.cpp b/plugins/guacamole-oculus/example-oculus/main.cpp index e4f8e9cc2..85ac64b99 100644 --- a/plugins/guacamole-oculus/example-oculus/main.cpp +++ b/plugins/guacamole-oculus/example-oculus/main.cpp @@ -43,7 +43,7 @@ std::vector> add_lights(gua::SceneGraph& gra loader.create_geometry_from_file( "sphere" + gua::string_utils::to_string(i), "data/objects/light_sphere.obj", - "White" + "data/materials/White.gmd" )); sphere_geometry->scale(0.04, 0.04, 0.04); @@ -80,7 +80,7 @@ void setup_scene(gua::SceneGraph& graph, auto monkey_geometry(loader.create_geometry_from_file( "monkey", geometry, - "Stones" + "data/materials/Stones.gmd" )); auto monkey = root_monkey->add_child(monkey_geometry); @@ -133,7 +133,7 @@ int main(int argc, char** argv) { auto monkey_geometry(loader.create_geometry_from_file( "root_ape", geometry, - "Stones" + "data/materials/Stones.gmd" )); auto root_monkey = graph.add_node("/", monkey_geometry); From 755f61937cd2bc15e0a4b571c450b8529562a834 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Fri, 7 Mar 2014 13:57:54 +0100 Subject: [PATCH 146/146] Bump version. --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4e0db37d6..823cf7f19 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,8 +6,8 @@ PROJECT(GUACAMOLE CXX) # version number set(GUACAMOLE_MAJOR 0) -set(GUACAMOLE_MINOR 4) -set(GUACAMOLE_PATCH 2) +set(GUACAMOLE_MINOR 5) +set(GUACAMOLE_PATCH 0) set(GUACAMOLE_VERSION ${GUACAMOLE_MAJOR}.${GUACAMOLE_MINOR}.${GUACAMOLE_PATCH}) set(GUACAMOLE_DESCRIPTION "GUACAMOLE - an astonishing virtual reality engine") set(GUACAMOLE_HOMEPAGE "http://www.GUACAMOLE.org")