commit 9fbddb6ffed2d778999747ef31b77a512d0ce216 Author: stujones11 Date: Thu May 24 22:28:00 2018 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8c5eec1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +## Generic ignorable patterns and files +*~ +.*.swp +*bak* +tags +*.vim diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..0447e25 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,42 @@ +cmake_minimum_required(VERSION 2.6) +project(samviewer) + +set(CMAKE_CXX_STANDARD 11) + +set(PROJECT_NAME_CAPITALIZED "SAM-Viewer") +set(PROJECT_LINK_URL "https://github.com/stujones11/SAM-Viewer") +set(PROJECT_LINK_TEXT "github.com/stujones11/SAM-Viewer") + +set(VERSION_MAJOR 0) +set(VERSION_MINOR 1) +set(VERSION_PATCH 0) +set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}") + +add_definitions(-DUSE_CMAKE_CONFIG_H) + +configure_file ( + "${PROJECT_SOURCE_DIR}/src/cmake_config.h.in" + "${PROJECT_SOURCE_DIR}/src/cmake_config.h" +) + +if(NOT IRRLICHT_INCLUDE_DIR) + set(IRRLICHT_INCLUDE_DIR "/usr/include/irrlicht") +endif() + +if(NOT IRRLICHT_LIBRARY) + set(IRRLICHT_LIBRARY "/usr/local/lib/libIrrlicht.so") +endif() + +include_directories( + ${IRRLICHT_INCLUDE_DIR} + ${PROJECT_SOURCE_DIR}/src +) +file(GLOB SRCS src/*.cpp) +file(MAKE_DIRECTORY "bin") + +set(EXECUTABLE_OUTPUT_PATH "${CMAKE_SOURCE_DIR}/bin") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3") + +add_executable(${PROJECT_NAME} ${SRCS}) +target_link_libraries(${PROJECT_NAME} ${IRRLICHT_LIBRARY}) + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ab60297 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..1c31c9d --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,57 @@ +SAM-Viewer +========== + +License of SAM-Viewer source code +--------------------------------- + +Copyright (C) 2018, Stuart Jones - (MIT License) + +License of SAM-Viewer media +--------------------------- + +Includes all textures and models contained in this distribution. + +Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0) + +Minetest media files: + + character.b3d + character.png + pickaxe.png + blank.png + +Copyright (C) 2010-2018 celeron55, Perttu Ahola + +Irrlicht media files: + + fontlucida.png + +All other media files: + +Copyright (C) 2018, Stuart Jones - (CC BY-SA 3.0) + +Irrlicht +-------- + +This program uses the Irrlicht Engine. http://irrlicht.sourceforge.net/ + +The Irrlicht Engine License: + + Copyright (C) 2002-2015 Nikolaus Gebhardt + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgement in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be clearly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..003727b --- /dev/null +++ b/README.md @@ -0,0 +1,78 @@ +SAM-Viewer +========== + +**Skin & Model Viewer - Version 0.1.0** + +A simple 3d mesh viewer built with Irrlicht rendering engine. + +Features +-------- + +* Wielded item or 'attachment' model support. +* Multiple textures, up to 6 material layers. +* Mesh debug view. (wire-frame, skeleton and normals) +* Animation playback amd frame controls. + +Supported Formats +----------------- + +Compatible with all mesh formats supported by Irrlicht engine. + +* B3D files (.b3d) +* DirectX (.x) +* Alias Wavefront Maya (.obj) +* 3D Studio meshes (.3ds) +* Lightwave Objects (.lwo) +* COLLADA 1.4 (.xml, .dae) + +Installation +------------ + +For now this assumes you are using some sane linux distro and have +a c++11 compliant compiler, although I am pretty sure it could be +made to work on any platform or device that meets the requirements. + +**Requirements:** cmake, opengl, Irrlicht +``` +cmake . +make -j2 +``` + +**CMake options:** (defaults) +``` +IRRLICHT_INCLUDE_DIR=/usr/include/irrlicht +IRRLICHT_LIBRARY="/usr/local/lib/libIrrlicht.so" +``` + +**Example:** +``` +cmake . -DIRRLICHT_LIBRARY="/usr/lib/libIrrlicht.so"` +``` + +Controls +-------- + +| Control | Action | +|-------------------------------|----------------------------------------------------------------| +| Left mouse button + move | Trackball style rotation | +| Mouse wheel | Zoom | +| + | Zoom in | +| - | Zoom out | +| Arrow keys | Rotate around X and Y axes in 15 degree steps | +| Z, X | Rotate around Z axis in 15 degree steps | +| Home | Reset zoom and rotation | +| F5 | Reload textures | +| Space | Jump (experimental) | + +To Do +----- + +* Improve file-browser. +* Basic lighting. +* Image capture tools. + +Screenshot +---------- + +![Imgur](https://i.imgur.com/xIS7pRj.png) + diff --git a/assets/browse.png b/assets/browse.png new file mode 100644 index 0000000..1286a5e Binary files /dev/null and b/assets/browse.png differ diff --git a/assets/fontlucida.png b/assets/fontlucida.png new file mode 100644 index 0000000..b81ec9f Binary files /dev/null and b/assets/fontlucida.png differ diff --git a/assets/pause.png b/assets/pause.png new file mode 100644 index 0000000..14b0b24 Binary files /dev/null and b/assets/pause.png differ diff --git a/assets/play_fwd.png b/assets/play_fwd.png new file mode 100644 index 0000000..6c4118b Binary files /dev/null and b/assets/play_fwd.png differ diff --git a/assets/play_rev.png b/assets/play_rev.png new file mode 100644 index 0000000..4d75bac Binary files /dev/null and b/assets/play_rev.png differ diff --git a/assets/sam_icon.svg b/assets/sam_icon.svg new file mode 100644 index 0000000..1642241 --- /dev/null +++ b/assets/sam_icon.svg @@ -0,0 +1,414 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/sam_icon_128.png b/assets/sam_icon_128.png new file mode 100644 index 0000000..9578987 Binary files /dev/null and b/assets/sam_icon_128.png differ diff --git a/assets/sam_icon_16.png b/assets/sam_icon_16.png new file mode 100644 index 0000000..9f55eed Binary files /dev/null and b/assets/sam_icon_16.png differ diff --git a/assets/skip_fwd.png b/assets/skip_fwd.png new file mode 100644 index 0000000..1c5bb6a Binary files /dev/null and b/assets/skip_fwd.png differ diff --git a/assets/skip_rev.png b/assets/skip_rev.png new file mode 100644 index 0000000..68bb1d1 Binary files /dev/null and b/assets/skip_rev.png differ diff --git a/assets/title.png b/assets/title.png new file mode 100644 index 0000000..953a1ab Binary files /dev/null and b/assets/title.png differ diff --git a/media/blank.png b/media/blank.png new file mode 100644 index 0000000..85e0250 Binary files /dev/null and b/media/blank.png differ diff --git a/media/character.b3d b/media/character.b3d new file mode 100644 index 0000000..5b1616b Binary files /dev/null and b/media/character.b3d differ diff --git a/media/character.png b/media/character.png new file mode 100644 index 0000000..0502178 Binary files /dev/null and b/media/character.png differ diff --git a/media/pickaxe.obj b/media/pickaxe.obj new file mode 100644 index 0000000..de05eae --- /dev/null +++ b/media/pickaxe.obj @@ -0,0 +1,881 @@ +# Blender v2.78 (sub 0) OBJ File: 'pickaxe.blend' +# www.blender.org +o Cube +v 0.062500 0.353553 1.285178 +v 0.062500 -0.353554 1.285177 +v 0.062500 -0.000000 1.638731 +v 0.062500 0.176777 1.108401 +v 0.062500 -0.530330 1.461954 +v 0.062500 0.176777 0.754847 +v 0.062500 -0.176777 0.754847 +v 0.062500 -0.176777 1.108401 +v 0.062500 -0.883883 1.108400 +v 0.062500 0.883883 1.108401 +v 0.062500 -0.176776 0.047741 +v 0.062500 0.176777 0.401294 +v 0.062500 -0.176776 0.401294 +v 0.062500 0.176777 0.047741 +v 0.062500 -0.353554 1.638731 +v 0.062500 0.088388 1.020012 +v 0.062500 -0.618719 1.550342 +v 0.062500 0.088388 0.843236 +v 0.062500 -0.265165 1.196789 +v 0.062500 -0.972272 1.196789 +v 0.062500 0.088388 1.727119 +v 0.062500 0.441942 1.196789 +v 0.062500 0.088389 0.312906 +v 0.062500 0.265165 1.196789 +v 0.062500 -0.088388 0.843236 +v 0.062500 -0.088388 1.020012 +v 0.062500 -0.441942 1.196789 +v 0.062500 -0.088389 1.727119 +v 0.062500 0.972272 1.196789 +v 0.062500 -0.088388 0.136129 +v 0.062500 -0.088388 0.312906 +v 0.062500 -0.088388 0.489682 +v 0.062500 0.088388 0.666459 +v 0.062500 -0.088388 0.666459 +v 0.062500 0.088389 0.489682 +v 0.062500 0.972272 1.020013 +v 0.062500 0.618718 1.196789 +v 0.062500 0.795495 1.373566 +v 0.062500 0.795495 1.196789 +v 0.062500 0.441941 1.550343 +v 0.062500 -0.441942 1.550342 +v 0.062500 -0.265165 1.550342 +v 0.062500 -0.795495 1.196789 +v 0.062500 -0.795495 1.373565 +v 0.062500 -0.618718 1.196789 +v 0.062500 -0.088388 1.196789 +v 0.062500 0.088388 1.196789 +v 0.062500 -0.972272 1.020012 +v 0.062500 -0.088388 -0.040648 +v 0.062500 0.088389 -0.217424 +v 0.062500 0.176777 -0.129036 +v 0.062500 -0.176776 -0.129036 +v 0.062500 -1.060660 1.108400 +v 0.062500 0.176777 1.285177 +v 0.062500 -0.176777 1.285177 +v 0.062500 -0.530330 1.285177 +v 0.062500 -0.707107 1.461954 +v 0.062500 -0.883884 1.285177 +v 0.062500 -0.176777 0.931624 +v 0.062500 -0.176777 1.638731 +v 0.062500 0.176777 0.931624 +v 0.062500 0.883883 1.285178 +v 0.062500 0.530330 1.285178 +v 0.062500 -0.176776 0.224517 +v 0.062500 0.176777 0.578071 +v 0.062500 -0.176776 0.578071 +v 0.062500 0.707107 1.108401 +v 0.062500 -0.707107 1.108400 +v 0.062500 -0.000000 0.931624 +v 0.062500 0.000000 0.224518 +v 0.062500 0.530330 1.461954 +v 0.062500 0.176777 1.461954 +v 0.062500 -0.176777 1.461954 +v 0.062500 0.000000 0.578071 +v 0.062500 0.707107 1.285178 +v 0.062500 0.353553 1.638731 +v 0.062500 -0.707107 1.285177 +v 0.062500 -0.000000 1.285177 +v 0.062500 0.000000 -0.129036 +v 0.062500 0.618718 1.550343 +v 0.062500 -0.441942 1.373566 +v 0.062500 0.265165 1.373566 +v 0.062500 -0.088388 1.550342 +v 0.062500 0.441942 1.373566 +v 0.062500 0.088388 1.550343 +v 0.062500 -0.265165 1.373566 +v 0.062500 0.088388 0.136129 +v 0.062500 0.618718 1.373566 +v 0.062500 0.265165 1.550343 +v 0.062500 -0.618718 1.373566 +v 0.062500 -0.088388 1.373566 +v 0.062500 0.088388 1.373566 +v 0.062500 0.088388 -0.040648 +v 0.062500 -0.088388 -0.217424 +v 0.062500 0.000000 0.047741 +v 0.062500 -0.000000 1.461954 +v 0.062500 0.176777 1.638731 +v 0.062500 1.060660 1.108401 +v 0.062500 0.707107 1.461954 +v 0.062500 0.176777 0.224517 +v 0.062500 0.000000 0.754847 +v 0.062500 0.000000 0.401294 +v 0.062500 -0.353553 1.461954 +v 0.062500 0.353553 1.461954 +v 0.062500 -0.000000 1.108401 +v 0.062500 0.000000 -0.305812 +v -0.062500 0.088388 1.020012 +v -0.062500 -0.000000 0.931624 +v -0.062500 0.088388 0.843236 +v -0.062500 0.441941 1.373566 +v -0.062500 0.353553 1.285178 +v -0.062500 -0.265165 1.196789 +v -0.062500 -0.353554 1.285177 +v -0.062500 -0.265165 1.373566 +v -0.062500 0.088388 1.727119 +v -0.062500 -0.000000 1.638731 +v -0.062500 0.088388 1.550343 +v -0.062500 0.441942 1.196789 +v -0.062500 0.088389 0.312906 +v -0.062500 0.000000 0.224517 +v -0.062500 0.088389 0.136129 +v -0.062500 -0.441942 1.373566 +v -0.062500 -0.530330 1.461954 +v -0.062500 -0.088388 0.843236 +v -0.062500 -0.176777 0.754847 +v -0.062500 0.618718 1.550343 +v -0.062500 0.530330 1.461954 +v -0.062500 -0.088388 1.020012 +v -0.062500 -0.176777 1.108401 +v -0.062500 -0.088389 1.550342 +v -0.062500 -0.176777 1.461954 +v -0.062500 0.265165 1.373566 +v -0.062500 0.176776 1.461954 +v -0.062500 0.972272 1.196789 +v -0.062500 0.883883 1.108401 +v -0.062500 -0.088388 0.136129 +v -0.062500 -0.176776 0.047741 +v -0.062500 -0.088388 0.312906 +v -0.062500 -0.176776 0.401294 +v -0.062500 -0.088388 0.489682 +v -0.062500 0.088388 0.666459 +v -0.062500 0.000000 0.578071 +v -0.062500 -0.088388 0.666459 +v -0.062500 0.088389 0.489682 +v -0.062500 0.972272 1.020013 +v -0.062500 0.795495 1.373566 +v -0.062500 0.707107 1.285178 +v -0.062500 0.618718 1.373566 +v -0.062500 0.795495 1.196789 +v -0.062500 0.265165 1.550343 +v -0.062500 0.441941 1.550343 +v -0.062500 0.353553 1.638731 +v -0.062500 -0.441942 1.550342 +v -0.062500 -0.265165 1.550342 +v -0.062500 -0.353554 1.638731 +v -0.062500 -0.795495 1.196789 +v -0.062500 -0.883883 1.108400 +v -0.062500 -0.618719 1.373566 +v -0.062500 -0.707107 1.285177 +v -0.062500 -0.618718 1.196789 +v -0.062500 -0.088388 1.196789 +v -0.062500 0.088388 1.373566 +v -0.062500 -0.000000 1.285177 +v -0.062500 -0.088388 1.373566 +v -0.062500 0.088388 1.196789 +v -0.062500 0.088389 -0.040647 +v -0.062500 0.000000 -0.129036 +v -0.062500 -0.088388 -0.040648 +v -0.062500 0.088389 -0.217424 +v -0.062500 0.176777 1.108401 +v -0.062500 -0.618719 1.550342 +v -0.062500 0.176777 0.754847 +v -0.062500 -0.972272 1.196789 +v -0.062500 0.176777 0.401294 +v -0.062500 0.176777 0.047741 +v -0.062500 0.265165 1.196789 +v -0.062500 -0.441942 1.196789 +v -0.062500 -0.088389 1.727119 +v -0.062500 0.618718 1.196789 +v -0.062500 -0.795495 1.373565 +v -0.062500 -0.972272 1.020012 +v -0.062500 -0.088388 -0.217424 +v -0.062500 0.176777 -0.129036 +v -0.062500 0.000000 0.047741 +v -0.062500 -0.176776 -0.129036 +v -0.062500 -1.060660 1.108400 +v -0.062500 0.176777 1.285177 +v -0.062500 -0.000000 1.461954 +v -0.062500 -0.176777 1.285177 +v -0.062500 -0.530330 1.285177 +v -0.062500 -0.707107 1.461954 +v -0.062500 -0.883884 1.285177 +v -0.062500 -0.176777 0.931624 +v -0.062500 -0.176777 1.638731 +v -0.062500 0.176776 1.638731 +v -0.062500 0.176777 0.931624 +v -0.062500 0.883883 1.285178 +v -0.062500 0.707107 1.461954 +v -0.062500 0.530330 1.285178 +v -0.062500 1.060660 1.108401 +v -0.062500 -0.176776 0.224517 +v -0.062500 0.176777 0.578071 +v -0.062500 0.000000 0.754847 +v -0.062500 -0.176776 0.578071 +v -0.062500 0.176777 0.224518 +v -0.062500 0.000000 0.401294 +v -0.062500 0.707107 1.108401 +v -0.062500 0.353553 1.461954 +v -0.062500 -0.353554 1.461954 +v -0.062500 -0.707107 1.108401 +v -0.062500 -0.000000 1.108401 +v -0.062500 0.000001 -0.305812 +vt 0.4375 0.7500 +vt 0.5000 0.7500 +vt 0.5000 0.8125 +vt 0.4375 0.8125 +vt 0.4375 0.8750 +vt 0.5000 0.8750 +vt 0.5000 0.9375 +vt 0.4375 0.9375 +vt 0.4375 0.7500 +vt 0.5000 0.7500 +vt 0.5000 0.8125 +vt 0.4375 0.8125 +vt 0.3750 0.8750 +vt 0.4375 0.8750 +vt 0.4375 0.9375 +vt 0.3750 0.9375 +vt 0.5000 0.4375 +vt 0.4375 0.4375 +vt 0.4375 0.3750 +vt 0.5000 0.3750 +vt 0.4375 0.3750 +vt 0.5000 0.3750 +vt 0.5000 0.4375 +vt 0.4375 0.4375 +vt 0.8125 0.3750 +vt 0.8750 0.3750 +vt 0.8750 0.4375 +vt 0.8125 0.4375 +vt 0.2500 0.8750 +vt 0.3125 0.8750 +vt 0.3125 0.9375 +vt 0.2500 0.9375 +vt 0.8125 0.5000 +vt 0.8750 0.5000 +vt 0.8750 0.5625 +vt 0.8125 0.5625 +vt 0.4375 0.8750 +vt 0.5000 0.8750 +vt 0.5000 0.9375 +vt 0.4375 0.9375 +vt 0.1875 0.2500 +vt 0.2500 0.2500 +vt 0.2500 0.3125 +vt 0.1875 0.3125 +vt 0.7500 0.6875 +vt 0.8125 0.6875 +vt 0.8125 0.7500 +vt 0.7500 0.7500 +vt 0.3750 0.3125 +vt 0.3125 0.3125 +vt 0.3125 0.2500 +vt 0.3750 0.2500 +vt 0.7500 0.6250 +vt 0.8125 0.6250 +vt 0.8125 0.6875 +vt 0.7500 0.6875 +vt 0.7500 0.6875 +vt 0.8125 0.6875 +vt 0.8125 0.7500 +vt 0.7500 0.7500 +vt 0.1875 0.2500 +vt 0.2500 0.2500 +vt 0.2500 0.3125 +vt 0.1875 0.3125 +vt 0.2500 0.8750 +vt 0.3125 0.8750 +vt 0.3125 0.9375 +vt 0.2500 0.9375 +vt 0.6875 0.7500 +vt 0.7500 0.7500 +vt 0.7500 0.8125 +vt 0.6875 0.8125 +vt 0.3125 0.8750 +vt 0.3750 0.8750 +vt 0.3750 0.9375 +vt 0.3125 0.9375 +vt 0.5625 0.8125 +vt 0.6250 0.8125 +vt 0.6250 0.8750 +vt 0.5625 0.8750 +vt 0.0625 0.1250 +vt 0.1250 0.1250 +vt 0.1250 0.1875 +vt 0.0625 0.1875 +vt 0.2500 0.1875 +vt 0.3125 0.1875 +vt 0.3125 0.2500 +vt 0.2500 0.2500 +vt 0.3125 0.8125 +vt 0.3750 0.8125 +vt 0.3750 0.8750 +vt 0.3125 0.8750 +vt 0.0625 0.1250 +vt 0.1250 0.1250 +vt 0.1250 0.1875 +vt 0.0625 0.1875 +vt 0.1250 0.1875 +vt 0.1875 0.1875 +vt 0.1875 0.2500 +vt 0.1250 0.2500 +vt 0.3125 0.2500 +vt 0.2500 0.2500 +vt 0.2500 0.1875 +vt 0.3125 0.1875 +vt 0.7500 0.4375 +vt 0.8125 0.4375 +vt 0.8125 0.5000 +vt 0.7500 0.5000 +vt 0.3125 0.2500 +vt 0.3750 0.2500 +vt 0.3750 0.3125 +vt 0.3125 0.3125 +vt 0.3125 0.8125 +vt 0.3750 0.8125 +vt 0.3750 0.8750 +vt 0.3125 0.8750 +vt 0.4375 0.5000 +vt 0.5000 0.5000 +vt 0.5000 0.5625 +vt 0.4375 0.5625 +vt 0.8125 0.3125 +vt 0.8750 0.3125 +vt 0.8750 0.3750 +vt 0.8125 0.3750 +vt 0.3750 0.4375 +vt 0.4375 0.4375 +vt 0.4375 0.5000 +vt 0.3750 0.5000 +vt 0.2500 0.3125 +vt 0.3125 0.3125 +vt 0.3125 0.3750 +vt 0.2500 0.3750 +vt 0.2500 0.1875 +vt 0.1875 0.1875 +vt 0.1875 0.1250 +vt 0.2500 0.1250 +vt 0.6250 0.5625 +vt 0.5625 0.5625 +vt 0.5625 0.5000 +vt 0.6250 0.5000 +vt 0.8125 0.3125 +vt 0.8750 0.3125 +vt 0.8750 0.3750 +vt 0.8125 0.3750 +vt 0.8750 0.4375 +vt 0.9375 0.4375 +vt 0.9375 0.5000 +vt 0.8750 0.5000 +vt 0.1250 0.1875 +vt 0.1875 0.1875 +vt 0.1875 0.2500 +vt 0.1250 0.2500 +vt 0.4375 0.5000 +vt 0.5000 0.5000 +vt 0.5000 0.5625 +vt 0.4375 0.5625 +vt 0.5000 0.4375 +vt 0.5625 0.4375 +vt 0.5625 0.5000 +vt 0.5000 0.5000 +vt 0.5000 0.5625 +vt 0.5625 0.5625 +vt 0.5625 0.6250 +vt 0.5000 0.6250 +vt 0.6875 0.5000 +vt 0.7500 0.5000 +vt 0.7500 0.5625 +vt 0.6875 0.5625 +vt 0.3750 0.3125 +vt 0.4375 0.3125 +vt 0.4375 0.3750 +vt 0.3750 0.3750 +vt 0.2500 0.3125 +vt 0.3125 0.3125 +vt 0.3125 0.3750 +vt 0.2500 0.3750 +vt 0.6250 0.5625 +vt 0.6875 0.5625 +vt 0.6875 0.6250 +vt 0.6250 0.6250 +vt 0.5625 0.5000 +vt 0.5000 0.5000 +vt 0.5000 0.4375 +vt 0.5625 0.4375 +vt 0.8125 0.5625 +vt 0.8750 0.5625 +vt 0.8750 0.6250 +vt 0.8125 0.6250 +vt 0.4375 0.3750 +vt 0.3750 0.3750 +vt 0.3750 0.3125 +vt 0.4375 0.3125 +vt 0.3750 0.4375 +vt 0.4375 0.4375 +vt 0.4375 0.5000 +vt 0.3750 0.5000 +vt 0.8750 0.1875 +vt 0.9375 0.1875 +vt 0.9375 0.2500 +vt 0.8750 0.2500 +vt 0.8750 0.1875 +vt 0.9375 0.1875 +vt 0.9375 0.2500 +vt 0.8750 0.2500 +vt 0.1875 0.8750 +vt 0.2500 0.8750 +vt 0.2500 0.9375 +vt 0.1875 0.9375 +vt 0.0625 0.0625 +vt 0.1250 0.0625 +vt 0.1250 0.1250 +vt 0.0625 0.1250 +vt 0.6875 0.5000 +vt 0.7500 0.5000 +vt 0.7500 0.5625 +vt 0.6875 0.5625 +vt 0.0625 0.0625 +vt 0.1250 0.0625 +vt 0.1250 0.1250 +vt 0.0625 0.1250 +vt 0.8750 0.1875 +vt 0.9375 0.1875 +vt 0.9375 0.2500 +vt 0.8750 0.2500 +vt 0.1875 0.8750 +vt 0.2500 0.8750 +vt 0.2500 0.9375 +vt 0.1875 0.9375 +vt 0.8750 0.3125 +vt 0.9375 0.3125 +vt 0.9375 0.3750 +vt 0.8750 0.3750 +vt 0.8750 0.2500 +vt 0.9375 0.2500 +vt 0.9375 0.3125 +vt 0.8750 0.3125 +vt 0.1875 0.8750 +vt 0.1875 0.9375 +vt 0.5000 0.8125 +vt 0.5625 0.8125 +vt 0.5625 0.8750 +vt 0.5000 0.8750 +vt 0.8750 0.2500 +vt 0.9375 0.2500 +vt 0.9375 0.3125 +vt 0.8750 0.3125 +vt 0.6250 0.7500 +vt 0.6875 0.7500 +vt 0.6875 0.8125 +vt 0.6250 0.8125 +vt 0.5000 0.5625 +vt 0.5625 0.5625 +vt 0.5625 0.6250 +vt 0.5000 0.6250 +vt 0.6875 0.7500 +vt 0.7500 0.7500 +vt 0.7500 0.8125 +vt 0.6875 0.8125 +vt 0.5625 0.5625 +vt 0.5625 0.5000 +vt 0.5000 0.5000 +vt 0.5000 0.5625 +vt 0.5625 0.8125 +vt 0.5625 0.7500 +vt 0.5000 0.8125 +vt 0.8125 0.5625 +vt 0.8125 0.5000 +vt 0.7500 0.5625 +vt 0.3125 0.3125 +vt 0.2500 0.2500 +vt 0.2500 0.3125 +vt 0.4375 0.4375 +vt 0.3750 0.3750 +vt 0.3750 0.4375 +vt 0.3125 0.9375 +vt 0.2500 0.9375 +vt 0.4375 0.9375 +vt 0.4375 0.8750 +vt 0.3750 0.8750 +vt 0.3750 0.9375 +vt 0.8125 0.6875 +vt 0.8125 0.6250 +vt 0.7500 0.6250 +vt 0.7500 0.6875 +vt 0.6875 0.8125 +vt 0.6875 0.7500 +vt 0.6250 0.7500 +vt 0.6250 0.8125 +vt 0.9375 0.4375 +vt 0.9375 0.3750 +vt 0.8750 0.3750 +vt 0.8750 0.4375 +vt 0.9375 0.3125 +vt 0.9375 0.2500 +vt 0.8750 0.2500 +vt 0.6875 0.6875 +vt 0.6875 0.6250 +vt 0.6250 0.6250 +vt 0.6250 0.6875 +vt 0.1875 0.1875 +vt 0.1875 0.1250 +vt 0.1250 0.1250 +vt 0.1250 0.1875 +vt 0.1875 0.0625 +vt 0.1875 0.2500 +vt 0.1250 0.2500 +vt 0.2500 0.1250 +vt 0.0625 0.1250 +vt 0.0625 0.1875 +vt 0.7500 0.7500 +vt 0.5625 0.6875 +vt 0.5000 0.6875 +vt 0.5625 0.6250 +vt 0.9375 0.5000 +vt 0.8750 0.5000 +vt 0.8125 0.4375 +vt 0.7500 0.4375 +vt 0.8125 0.3750 +vt 0.7500 0.8125 +vt 0.8125 0.7500 +vt 0.1875 0.9375 +vt 0.4375 0.8125 +vt 0.5000 0.8750 +vt 0.5000 0.9375 +vt 0.4375 0.5000 +vt 0.4375 0.5625 +vt 0.1875 0.3125 +vt 0.3750 0.5000 +vt 0.3125 0.3750 +vt 0.3125 0.4375 +vt 0.2500 0.3750 +vt 0.8750 0.6250 +vt 0.8750 0.5625 +vt 0.5625 0.8750 +vt 0.6250 0.8750 +vt 0.5000 0.6250 +vt 0.6250 0.5000 +vt 0.5625 0.5625 +vt 0.5000 0.5625 +vt 0.5000 0.5000 +vt 0.5625 0.5000 +vt 0.8125 0.5625 +vt 0.7500 0.5625 +vt 0.8125 0.5000 +vt 0.5625 0.8125 +vt 0.5000 0.8125 +vt 0.5000 0.7500 +vt 0.5625 0.7500 +vt 0.3125 0.3125 +vt 0.2500 0.3125 +vt 0.2500 0.2500 +vt 0.3125 0.2500 +vt 0.4375 0.4375 +vt 0.3750 0.4375 +vt 0.3750 0.3750 +vt 0.4375 0.3750 +vt 0.4375 0.9375 +vt 0.3750 0.9375 +vt 0.3750 0.8750 +vt 0.4375 0.8750 +vt 0.3125 0.9375 +vt 0.2500 0.9375 +vt 0.2500 0.8750 +vt 0.6875 0.8125 +vt 0.6250 0.8125 +vt 0.6250 0.7500 +vt 0.6875 0.7500 +vt 0.7500 0.6875 +vt 0.7500 0.6250 +vt 0.8125 0.6250 +vt 0.9375 0.4375 +vt 0.8750 0.4375 +vt 0.8750 0.3750 +vt 0.6875 0.6875 +vt 0.6250 0.6875 +vt 0.6250 0.6250 +vt 0.6875 0.6250 +vt 0.1875 0.1875 +vt 0.1250 0.1875 +vt 0.1250 0.1250 +vt 0.1875 0.1250 +vt 0.2500 0.1875 +vt 0.1875 0.2500 +vt 0.1250 0.0625 +vt 0.1875 0.0625 +vt 0.8750 0.1875 +vt 0.9375 0.1875 +vt 0.5625 0.6875 +vt 0.5625 0.6250 +vt 0.5000 0.6875 +vt 0.7500 0.7500 +vt 0.6875 0.5000 +vt 0.6250 0.5625 +vt 0.8750 0.5000 +vt 0.7500 0.4375 +vt 0.9375 0.5000 +vt 0.5000 0.4375 +vt 0.4375 0.5000 +vt 0.5000 0.8750 +vt 0.4375 0.7500 +vt 0.3750 0.8125 +vt 0.1875 0.8750 +vt 0.3125 0.4375 +vt 0.3125 0.3750 +vt 0.3750 0.3125 +vt 0.3125 0.8125 +vt 0.6250 0.8750 +vt 0.5625 0.8750 +vt 0.8125 0.3125 +vt 0.0625 0.0625 +vt 0.1875 0.1250 +vt 0.1250 0.1250 +vt 0.1250 0.0625 +vt 0.5625 0.6250 +vt 0.6250 0.6250 +vt 0.6250 0.6875 +vt 0.6250 0.5625 +vt 0.5625 0.5625 +vt 0.5000 0.6875 +vt 0.5625 0.6875 +vt 0.5625 0.7500 +vt 0.8125 0.5625 +vt 0.8750 0.5625 +vt 0.8750 0.6250 +vt 0.8125 0.6250 +vt 0.8125 0.5000 +vt 0.7500 0.5000 +vt 0.3125 0.3750 +vt 0.3750 0.3750 +vt 0.3750 0.4375 +vt 0.1875 0.1250 +vt 0.2500 0.1875 +vt 0.1875 0.1875 +vt 0.8750 0.3750 +vt 0.9375 0.3750 +vt 0.8750 0.4375 +vt 0.4375 0.8750 +vt 0.5625 0.8125 +vt 0.6250 0.8125 +vt 0.5625 0.8750 +vt 0.8750 0.4375 +vt 0.9375 0.4375 +vt 0.8750 0.5000 +vt 0.3125 0.3750 +vt 0.3750 0.3750 +vt 0.3750 0.4375 +vt 0.3125 0.4375 +vt 0.5625 0.7500 +vt 0.5000 0.7500 +vn 0.0000 0.7071 -0.7071 +vn 0.0000 -0.7071 0.7071 +vn 0.0000 -0.7071 -0.7071 +vn 0.0000 0.7071 0.7071 +vn 1.0000 0.0000 -0.0000 +vn -1.0000 -0.0000 0.0000 +s off +f 63/1/1 22/2/1 118/3/1 199/4/1 +f 71/5/2 80/6/2 126/7/2 127/8/2 +f 22/9/3 1/10/3 111/11/3 118/12/3 +f 99/13/4 38/14/4 146/15/4 198/16/4 +f 7/17/2 25/18/2 124/19/2 125/20/2 +f 34/21/3 7/22/3 125/23/3 143/24/3 +f 56/25/1 45/26/1 160/27/1 190/28/1 +f 10/29/3 39/30/3 149/31/3 135/32/3 +f 5/33/2 41/34/2 153/35/2 123/36/2 +f 80/37/4 99/38/4 198/39/4 126/40/4 +f 23/41/4 100/42/4 205/43/4 119/44/4 +f 60/45/2 28/46/2 178/47/2 194/48/2 +f 13/49/2 32/50/2 140/51/2 139/52/2 +f 42/53/2 60/54/2 194/55/2 154/56/2 +f 28/57/4 3/58/4 116/59/4 178/60/4 +f 100/61/1 87/62/1 121/63/1 205/64/1 +f 62/65/4 29/66/4 134/67/4 197/68/4 +f 3/69/2 21/70/2 115/71/2 116/72/2 +f 38/73/4 62/74/4 197/75/4 146/76/4 +f 76/77/4 40/78/4 151/79/4 152/80/4 +f 93/81/4 51/82/4 183/83/4 166/84/4 +f 30/85/3 64/86/3 201/87/3 136/88/3 +f 67/89/3 37/90/3 179/91/3 207/92/3 +f 51/93/1 50/94/1 169/95/1 183/96/1 +f 87/97/4 14/98/4 175/99/4 121/100/4 +f 64/101/2 31/102/2 138/103/2 201/104/2 +f 2/105/1 27/106/1 177/107/1 113/108/1 +f 31/109/3 13/110/3 139/111/3 138/112/3 +f 39/113/1 67/114/1 207/115/1 149/116/1 +f 16/117/4 61/118/4 196/119/4 107/120/4 +f 68/121/3 43/122/3 156/123/3 210/124/3 +f 18/125/4 6/126/4 172/127/4 109/128/4 +f 12/129/1 23/130/1 119/131/1 174/132/1 +f 11/133/2 30/134/2 136/135/2 137/136/2 +f 8/137/2 46/138/2 161/139/2 129/140/2 +f 45/141/1 68/142/1 210/143/1 160/144/1 +f 17/145/4 5/146/4 123/147/4 171/148/4 +f 14/149/1 93/150/1 166/151/1 175/152/1 +f 61/153/1 18/154/1 109/155/1 196/156/1 +f 25/157/3 59/158/3 193/159/3 124/160/3 +f 4/161/1 16/162/1 107/163/1 170/164/1 +f 19/165/3 2/166/3 113/167/3 112/168/3 +f 32/169/3 66/170/3 204/171/3 140/172/3 +f 35/173/4 12/174/4 174/175/4 144/176/4 +f 46/177/3 55/178/3 189/179/3 161/180/3 +f 59/181/2 26/182/2 128/183/2 193/184/2 +f 41/185/2 15/186/2 155/187/2 153/188/2 +f 66/189/2 34/190/2 143/191/2 204/192/2 +f 6/193/1 33/194/1 141/195/1 172/196/1 +f 9/197/1 48/198/1 181/199/1 157/200/1 +f 48/201/3 53/202/3 186/203/3 181/204/3 +f 29/205/4 98/206/4 200/207/4 134/208/4 +f 106/209/3 94/210/3 182/211/3 212/212/3 +f 55/213/1 19/214/1 112/215/1 189/216/1 +f 50/217/1 106/218/1 212/219/1 169/220/1 +f 53/221/2 20/222/2 173/223/2 186/224/2 +f 98/225/1 36/226/1 145/227/1 200/228/1 +f 58/229/2 44/230/2 180/231/2 192/232/2 +f 20/233/2 58/234/2 192/235/2 173/236/2 +f 36/237/3 10/29/3 135/32/3 145/238/3 +f 40/239/4 71/240/4 127/241/4 151/242/4 +f 43/243/1 9/244/1 157/245/1 156/246/1 +f 97/247/4 89/248/4 150/249/4 195/250/4 +f 47/251/4 4/252/4 170/253/4 165/254/4 +f 21/255/4 97/256/4 195/257/4 115/258/4 +f 105/259/5 26/260/5 69/261/5 16/262/5 +f 104/263/5 82/264/5 1/10/5 84/265/5 +f 103/266/5 81/267/5 2/166/5 86/268/5 +f 102/269/5 31/109/5 70/270/5 23/271/5 +f 101/272/5 34/21/5 74/273/5 33/274/5 +f 62/275/5 39/30/5 10/29/5 29/276/5 +f 99/277/5 88/278/5 75/279/5 38/280/5 +f 60/281/5 42/282/5 73/283/5 83/284/5 +f 97/285/5 85/286/5 72/287/5 89/288/5 +f 57/289/5 44/290/5 77/291/5 90/292/5 +f 58/293/5 20/294/5 9/295/5 43/122/5 +f 96/296/5 91/297/5 78/298/5 92/299/5 +f 95/300/5 49/301/5 79/302/5 93/303/5 +f 49/301/5 52/304/5 94/210/5 79/302/5 +f 87/305/5 95/300/5 93/303/5 14/306/5 +f 70/270/5 30/85/5 95/300/5 87/305/5 +f 30/85/5 11/307/5 49/301/5 95/300/5 +f 93/303/5 79/302/5 50/308/5 51/309/5 +f 91/297/5 55/178/5 46/177/5 78/298/5 +f 73/283/5 86/268/5 55/178/5 91/297/5 +f 86/268/5 2/166/5 19/165/5 55/178/5 +f 85/286/5 96/296/5 92/299/5 72/287/5 +f 3/310/5 83/284/5 96/296/5 85/286/5 +f 83/284/5 73/283/5 91/297/5 96/296/5 +f 82/264/5 54/311/5 24/312/5 1/10/5 +f 72/287/5 92/299/5 54/311/5 82/264/5 +f 92/299/5 78/298/5 47/313/5 54/311/5 +f 26/260/5 59/158/5 25/157/5 69/261/5 +f 44/290/5 58/293/5 43/122/5 77/291/5 +f 17/314/5 57/289/5 90/292/5 5/315/5 +f 81/267/5 56/316/5 27/317/5 2/166/5 +f 5/315/5 90/292/5 56/316/5 81/267/5 +f 90/292/5 77/291/5 45/318/5 56/316/5 +f 20/294/5 53/202/5 48/201/5 9/295/5 +f 21/319/5 3/310/5 85/286/5 97/285/5 +f 28/320/5 60/281/5 83/284/5 3/310/5 +f 29/276/5 10/29/5 36/237/5 98/321/5 +f 88/278/5 63/322/5 37/90/5 75/279/5 +f 71/323/5 84/265/5 63/322/5 88/278/5 +f 84/265/5 1/10/5 22/9/5 63/322/5 +f 80/324/5 71/323/5 88/278/5 99/277/5 +f 38/280/5 75/279/5 39/30/5 62/275/5 +f 16/262/5 69/261/5 18/325/5 61/326/5 +f 23/271/5 70/270/5 87/305/5 100/327/5 +f 34/21/5 66/170/5 32/169/5 74/273/5 +f 18/325/5 101/272/5 33/274/5 6/328/5 +f 69/261/5 25/157/5 101/272/5 18/325/5 +f 25/157/5 7/22/5 34/21/5 101/272/5 +f 33/274/5 74/273/5 35/329/5 65/330/5 +f 31/109/5 64/86/5 30/85/5 70/270/5 +f 35/329/5 102/269/5 23/271/5 12/331/5 +f 74/273/5 32/169/5 102/269/5 35/329/5 +f 32/169/5 13/110/5 31/109/5 102/269/5 +f 75/279/5 37/90/5 67/89/5 39/30/5 +f 42/282/5 103/266/5 86/268/5 73/283/5 +f 15/332/5 41/333/5 103/266/5 42/282/5 +f 41/333/5 5/315/5 81/267/5 103/266/5 +f 40/334/5 104/263/5 84/265/5 71/323/5 +f 76/335/5 89/288/5 104/263/5 40/334/5 +f 89/288/5 72/287/5 82/264/5 104/263/5 +f 77/291/5 43/122/5 68/121/5 45/318/5 +f 47/313/5 105/259/5 16/262/5 4/336/5 +f 78/298/5 46/177/5 105/259/5 47/313/5 +f 46/177/5 8/337/5 26/260/5 105/259/5 +f 79/302/5 94/210/5 106/209/5 50/308/5 +f 211/338/6 107/339/6 108/340/6 128/341/6 +f 209/342/6 114/343/6 113/108/6 122/344/6 +f 208/345/6 110/346/6 111/347/6 132/348/6 +f 206/349/6 119/350/6 120/351/6 138/352/6 +f 203/353/6 141/354/6 142/355/6 143/356/6 +f 198/357/6 146/358/6 147/359/6 148/360/6 +f 197/361/6 134/362/6 135/363/6 149/116/6 +f 195/364/6 150/365/6 133/366/6 117/367/6 +f 194/55/6 130/368/6 131/369/6 154/370/6 +f 192/235/6 156/246/6 157/200/6 173/223/6 +f 191/371/6 158/372/6 159/373/6 180/231/6 +f 188/374/6 162/375/6 163/376/6 164/377/6 +f 184/378/6 166/379/6 167/380/6 168/381/6 +f 166/379/6 183/96/6 169/220/6 167/380/6 +f 136/382/6 184/378/6 168/381/6 137/136/6 +f 120/351/6 121/383/6 184/378/6 136/382/6 +f 121/383/6 175/152/6 166/379/6 184/378/6 +f 168/381/6 167/380/6 182/384/6 185/385/6 +f 173/223/6 157/200/6 181/386/6 186/387/6 +f 162/375/6 187/388/6 165/389/6 163/376/6 +f 133/366/6 132/348/6 187/388/6 162/375/6 +f 132/348/6 111/347/6 176/390/6 187/388/6 +f 130/368/6 188/374/6 164/377/6 131/369/6 +f 116/391/6 117/367/6 188/374/6 130/368/6 +f 117/367/6 133/366/6 162/375/6 188/374/6 +f 114/343/6 189/216/6 112/392/6 113/108/6 +f 131/369/6 164/377/6 189/216/6 114/343/6 +f 164/377/6 163/376/6 161/393/6 189/216/6 +f 158/372/6 190/28/6 160/144/6 159/373/6 +f 123/394/6 122/344/6 190/28/6 158/372/6 +f 122/344/6 113/108/6 177/395/6 190/28/6 +f 171/396/6 123/394/6 158/372/6 191/371/6 +f 180/231/6 159/373/6 156/246/6 192/235/6 +f 128/341/6 108/340/6 124/397/6 193/184/6 +f 178/47/6 116/391/6 130/368/6 194/55/6 +f 115/71/6 195/364/6 117/367/6 116/391/6 +f 107/339/6 196/156/6 109/398/6 108/340/6 +f 146/358/6 197/361/6 149/116/6 147/359/6 +f 126/7/6 198/357/6 148/360/6 127/399/6 +f 110/346/6 199/4/6 118/400/6 111/347/6 +f 127/399/6 148/360/6 199/4/6 110/346/6 +f 148/360/6 147/359/6 179/401/6 199/4/6 +f 134/362/6 200/228/6 145/402/6 135/363/6 +f 138/352/6 120/351/6 136/382/6 201/104/6 +f 141/354/6 202/403/6 144/404/6 142/355/6 +f 124/397/6 203/353/6 143/356/6 125/20/6 +f 108/340/6 109/398/6 203/353/6 124/397/6 +f 109/398/6 172/196/6 141/354/6 203/353/6 +f 143/356/6 142/355/6 140/405/6 204/192/6 +f 119/350/6 205/64/6 121/383/6 120/351/6 +f 140/405/6 206/349/6 138/352/6 139/52/6 +f 142/355/6 144/404/6 206/349/6 140/405/6 +f 144/404/6 174/132/6 119/350/6 206/349/6 +f 147/359/6 149/116/6 207/406/6 179/401/6 +f 150/365/6 208/345/6 132/348/6 133/366/6 +f 152/407/6 151/408/6 208/345/6 150/365/6 +f 151/408/6 127/399/6 110/346/6 208/345/6 +f 153/35/6 209/342/6 122/344/6 123/394/6 +f 155/187/6 154/370/6 209/342/6 153/35/6 +f 154/370/6 131/369/6 114/343/6 209/342/6 +f 159/373/6 160/144/6 210/409/6 156/246/6 +f 161/393/6 211/338/6 128/341/6 129/140/6 +f 163/376/6 165/389/6 211/338/6 161/393/6 +f 165/389/6 170/164/6 107/339/6 211/338/6 +f 167/380/6 169/220/6 212/410/6 182/384/6 +f 52/411/2 49/412/2 168/413/2 185/385/2 +f 54/414/1 47/415/1 165/416/1 187/388/1 +f 26/260/3 8/337/3 129/417/3 128/418/3 +f 1/419/1 24/420/1 176/421/1 111/347/1 +f 15/422/4 42/423/4 154/424/4 155/425/4 +f 27/317/3 56/316/3 190/426/3 177/427/3 +f 65/428/1 35/429/1 144/430/1 202/403/1 +f 94/210/3 52/304/3 185/431/3 182/211/3 +f 49/301/3 11/307/3 137/432/3 168/433/3 +f 44/434/2 57/435/2 191/371/2 180/436/2 +f 37/90/3 63/322/3 199/437/3 179/91/3 +f 89/438/2 76/439/2 152/407/2 150/440/2 +f 57/441/2 17/442/2 171/396/2 191/443/2 +f 33/444/4 65/445/4 202/446/4 141/447/4 +f 24/312/3 54/311/3 187/448/3 176/449/3 diff --git a/media/pickaxe.png b/media/pickaxe.png new file mode 100644 index 0000000..f9883c6 Binary files /dev/null and b/media/pickaxe.png differ diff --git a/screenshot.png b/screenshot.png new file mode 100644 index 0000000..78c75be Binary files /dev/null and b/screenshot.png differ diff --git a/src/cmake_config.h.in b/src/cmake_config.h.in new file mode 100644 index 0000000..c5452a3 --- /dev/null +++ b/src/cmake_config.h.in @@ -0,0 +1,5 @@ +// Template for cmake_config.h + +#define D_ABOUT_LINK_URL "@PROJECT_LINK_URL@" +#define D_ABOUT_LINK_TEXT "@PROJECT_LINK_TEXT@" +#define D_VERSION "@VERSION_STRING@" diff --git a/src/config.cpp b/src/config.cpp new file mode 100644 index 0000000..9b28cfb --- /dev/null +++ b/src/config.cpp @@ -0,0 +1,82 @@ +#include +#include + +#include "config.h" + +Vector::Vector(const std::string &str) +{ + sscanf(str.c_str(), "%f,%f,%f", &x, &y, &z); +} + +bool Config::load() +{ + std::ifstream file(filename.c_str()); + if (!file) + return false; + + std::string line; + while (std::getline(file, line)) + { + auto index = line.find("="); + std::string key = line.substr(0, index); + std::string value = line.substr(index + 1); + config[key] = value; + } + file.close(); + return true; +} + +bool Config::save() +{ + std::ofstream file(filename.c_str()); + if (!file) + return false; + + for(std::map::iterator it = config.begin(); + it != config.end(); it++) + { + file << it->first << "=" << it->second << "\n"; + } + file.close(); + return true; +} + +bool Config::hasKey(const std::string &key) const +{ + return config.find(key) != config.end(); +} + +void Config::set(const std::string &key, const std::string &value) +{ + config[key] = value; +} + +const std::string &Config::get(const std::string &key) const +{ + return config.find(key)->second; +} + +const char *Config::getCStr(const std::string &key) const +{ + return get(key).c_str(); +} + +int Config::getInt(const std::string &key) const +{ + return atoi(getCStr(key)); +} + +int Config::getHex(const std::string &key) const +{ + return std::stoul(get(key), nullptr, 16); +} + +bool Config::getBool(const std::string &key) const +{ + return get(key) == "true"; +} + +Vector Config::getVector(const std::string &key) const +{ + return Vector(get(key)); +} \ No newline at end of file diff --git a/src/config.h b/src/config.h new file mode 100644 index 0000000..49bc918 --- /dev/null +++ b/src/config.h @@ -0,0 +1,37 @@ +#ifndef D_CONFIG_H +#define D_CONFIG_H + +#include + +class Vector +{ +public: + Vector() : x(0), y(0), z(0) {} + Vector(float x, float y, float z) : x(x), y(y), z(z) {} + Vector(const std::string &str); + float x; + float y; + float z; +}; + +class Config +{ +public: + Config(const std::string &filename) : filename(filename) {} + bool load(); + bool save(); + bool hasKey(const std::string &key) const; + void set(const std::string &key, const std::string &value); + const std::string &get(const std::string &key) const; + const char *getCStr(const std::string &key) const; + int getInt(const std::string &key) const; + int getHex(const std::string &key) const; + bool getBool(const std::string &key) const; + Vector getVector(const std::string &key) const; + +private: + std::map config; + std::string filename; +}; + +#endif // D_CONFIG_H diff --git a/src/dialog.cpp b/src/dialog.cpp new file mode 100644 index 0000000..6a383c9 --- /dev/null +++ b/src/dialog.cpp @@ -0,0 +1,492 @@ +#include +#include +#include + +#ifdef USE_CMAKE_CONFIG_H +#include "cmake_config.h" +#else +#define D_ABOUT_LINK_URL "https://github.com/stujones11/SAM-Viewer" +#define D_ABOUT_LINK_TEXT "github.com/stujones11/SAM-Viewer" +#define D_VERSION "0.0.0" +#endif + +#include "config.h" +#include "scene.h" +#include "dialog.h" + +static inline void open_url(std::string url) +{ + system((std::string("xdg-open \"") + url + std::string("\"")).c_str()); +} + +HyperlinkCtrl::HyperlinkCtrl(IGUIEnvironment *env, IGUIElement *parent, s32 id, + const rect &rectangle, const wchar_t *title, std::string url) : + IGUIElement(EGUIET_ELEMENT, env, parent, id, rectangle), + url(url), + is_active(false) +{ + IGUIStaticText *text = env->addStaticText(title, + rect(0,0,rectangle.getWidth(),20), false, false, this); + text->setOverrideColor(SColor(255,0,0,255)); + text->setTextAlignment(EGUIA_CENTER, EGUIA_CENTER); +} + +void HyperlinkCtrl::draw() +{ + if (is_active) + { + IVideoDriver *driver = Environment->getVideoDriver(); + rect pos = getAbsolutePosition(); + vector2di end = pos.LowerRightCorner; + vector2di start = end - vector2di(pos.getWidth(), 0); + driver->draw2DLine(start, end, SColor(255,0,0,255)); + } + IGUIElement::draw(); +} + +bool HyperlinkCtrl::OnEvent(const SEvent &event) +{ + if (event.EventType == EET_GUI_EVENT) + { + if (event.GUIEvent.EventType == EGET_ELEMENT_HOVERED) + is_active = true; + else if (event.GUIEvent.EventType == EGET_ELEMENT_LEFT) + is_active = false; + } + else if (is_active && event.EventType == EET_MOUSE_INPUT_EVENT && + event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) + { + open_url(url); + } + return IGUIElement::OnEvent(event); +} + +static inline bool isValidHexString(std::string hex) +{ + return (hex.length() == 6 && + hex.find_first_not_of("0123456789abcdefABCDEF") == std::string::npos); +} + +ColorCtrl::ColorCtrl(IGUIEnvironment *env, IGUIElement *parent, s32 id, + const rect &rectangle, const wchar_t *label) : + IGUIElement(EGUIET_ELEMENT, env, parent, id, rectangle) +{ + IVideoDriver *driver = env->getVideoDriver(); + IGUIStaticText *text = env->addStaticText(label, rect(0,0,160,20), + false, false, this); + IGUIEditBox *edit = env->addEditBox(L"", rect(180,0,250,20), true, + this, E_DIALOG_ID_COLOR_EDIT); + edit->setMax(6); + edit->setToolTipText(L"Hex color string RRGGBB"); + + ITexture *texture = driver->findTexture("color_preview"); + if (!texture) + { + IImage *image = driver->createImage(ECF_A8R8G8B8, dimension2du(30,20)); + image->fill(SColor(255,255,255,255)); + texture = driver->addTexture("color_preview", image); + image->drop(); + } + IGUIImage *preview = env->addImage(rect(270,0,300,20), this, + E_DIALOG_ID_COLOR_PREVIEW); + preview->setImage(texture); +} + +void ColorCtrl::setColor(const std::string &hex) +{ + if (!isValidHexString(hex)) + return; + + stringw text = hex.c_str(); + IGUIEditBox *edit = (IGUIEditBox*) + getElementFromId(E_DIALOG_ID_COLOR_EDIT); + if (edit) + edit->setText(text.c_str()); + IGUIImage *preview = (IGUIImage*) + getElementFromId(E_DIALOG_ID_COLOR_PREVIEW); + if (preview) + { + SColor color; + color.color = std::stoul(hex, nullptr, 16); + color.setAlpha(255); + preview->setColor(color); + } +} + +std::string ColorCtrl::getColor() const +{ + std::string hex = ""; + IGUIEditBox *edit = (IGUIEditBox*) + getElementFromId(E_DIALOG_ID_COLOR_EDIT); + if (edit) + hex = stringc(edit->getText()).c_str(); + return hex; +} + +bool ColorCtrl::OnEvent(const SEvent &event) +{ + if (event.EventType == EET_GUI_EVENT && + event.GUIEvent.EventType == EGET_EDITBOX_CHANGED && + event.GUIEvent.Caller->getID() == E_DIALOG_ID_COLOR_EDIT) + { + IGUIEditBox *edit = (IGUIEditBox*)event.GUIEvent.Caller; + std::string hex = stringc(edit->getText()).c_str(); + setColor(hex); + } + return IGUIElement::OnEvent(event); +} + +AboutDialog::AboutDialog(IGUIEnvironment *env, IGUIElement *parent, + s32 id, const rect &rectangle) : + IGUIElement(EGUIET_ELEMENT, env, parent, id, rectangle) +{ + IVideoDriver *driver = env->getVideoDriver(); + ITexture *icon = driver->findTexture("sam_icon_128.png"); + if (!icon) + icon = driver->getTexture("sam_icon_128.png"); + if (icon) + { + IGUIImage *image = env->addImage(rect(86,10,214,138), this); + image->setImage(icon); + + } + ITexture *title = driver->findTexture("title.png"); + if (!title) + title = driver->getTexture("title.png"); + if (title) + { + IGUIImage *image = env->addImage(rect(50,140,250,170), this); + image->setImage(title); + } + stringw desc = stringw("Skin & Model Viewer - Version ") + D_VERSION; + IGUIStaticText *text; + text = env->addStaticText(desc.c_str(), rect(20,175,280,195), + false, false, this); + text->setTextAlignment(EGUIA_CENTER, EGUIA_CENTER); + + HyperlinkCtrl *link = new HyperlinkCtrl(env, this, + E_DIALOG_ID_ABOUT_LINK, rect(32,200,268,216), + stringw(D_ABOUT_LINK_TEXT).c_str(), D_ABOUT_LINK_URL); + link->drop(); + + IGUIButton *button = env->addButton(rect(110,235,190,265), this, + E_DIALOG_ID_ABOUT_OK, L"OK"); +} + + +SettingsDialog::SettingsDialog(IGUIEnvironment *env, IGUIElement *parent, + s32 id, const rect &rectangle, Config *conf) : + IGUIElement(EGUIET_ELEMENT, env, parent, id, rectangle), + conf(conf) +{ + IGUITabControl *tabs = env->addTabControl(rect(2,2,398,250), + this, true, true); + + IGUITab *tab_general = tabs->addTab(L"General"); + IGUITab *tab_debug = tabs->addTab(L"Debug"); + IGUIStaticText *text; + IGUIEditBox *edit; + IGUISpinBox *spin; + IGUICheckBox *check; + IGUIButton *button; + ColorCtrl *color; + + color = new ColorCtrl(env, tab_general, E_DIALOG_ID_BG_COLOR, + rect(20,20,320,40), L"Background Color:"); + color->setColor(conf->get("bg_color")); + color->drop(); + + color = new ColorCtrl(env, tab_general, E_DIALOG_ID_GRID_COLOR, + rect(20,50,320,70), L"Grid Color:"); + color->setColor(conf->get("grid_color")); + color->drop(); + + text = env->addStaticText(L"Wield Attachment Bone:", rect(20,80,180,100), + false, false, tab_general, -1); + stringw bone_name = conf->getCStr("wield_bone"); + edit = env->addEditBox(bone_name.c_str(), rect(200,80,320,100), + true, tab_general, E_DIALOG_ID_WIELD_BONE); + + text = env->addStaticText(L"Default Screen Width:", + rect(20,110,180,130), false, false, tab_general, -1); + spin = env->addSpinBox(L"", rect(200,110,270,130), + true, tab_general, E_DIALOG_ID_SCREEN_WIDTH); + spin->setValue(conf->getInt("screen_width")); + spin->setDecimalPlaces(0); + + text = env->addStaticText(L"Default Screen Height:", + rect(20,140,180,160), false, false, tab_general, -1); + spin = env->addSpinBox(L"", rect(200,140,270,160), + true, tab_general, E_DIALOG_ID_SCREEN_HEIGHT); + spin->setValue(conf->getInt("screen_height")); + spin->setDecimalPlaces(0); + + check = env->addCheckBox(false, rect(20,20,380,40), tab_debug, + E_DIALOG_ID_DEBUG_BBOX, L"Show bounding boxes"); + check->setChecked(conf->getInt("debug_flags") & EDS_BBOX); + check = env->addCheckBox(false, rect(20,50,380,70), tab_debug, + E_DIALOG_ID_DEBUG_NORMALS, L"Show vertex normals"); + check->setChecked(conf->getInt("debug_flags") & EDS_NORMALS); + check = env->addCheckBox(false, rect(20,80,380,100), tab_debug, + E_DIALOG_ID_DEBUG_SKELETON, L"Show skeleton"); + check->setChecked(conf->getInt("debug_flags") & EDS_SKELETON); + check = env->addCheckBox(false, rect(20,110,380,130), tab_debug, + E_DIALOG_ID_DEBUG_WIREFRANE, L"Wireframe overaly"); + check->setChecked(conf->getInt("debug_flags") & EDS_MESH_WIRE_OVERLAY); + check = env->addCheckBox(false, rect(20,140,380,160), tab_debug, + E_DIALOG_ID_DEBUG_ALPHA, L"Use transparent material"); + check->setChecked(conf->getInt("debug_flags") & EDS_HALF_TRANSPARENCY); + check = env->addCheckBox(false, rect(20,170,380,190), tab_debug, + E_DIALOG_ID_DEBUG_BUFFERS, L"Show all mesh buffers"); + check->setChecked(conf->getInt("debug_flags") & EDS_BBOX_BUFFERS); + + button = env->addButton(rect(315,255,395,285), this, + E_DIALOG_ID_SETTINGS_OK, L"OK"); + button = env->addButton(rect(230,255,310,285), this, + E_DIALOG_ID_SETTINGS_CANCEL, L"Cancel"); +} + +bool SettingsDialog::isBoxChecked(s32 id) const +{ + IGUICheckBox *check = (IGUICheckBox*)getElementFromId(id, true); + if (check) + return check->isChecked(); + + return false; +} + +bool SettingsDialog::OnEvent(const SEvent &event) +{ + if (event.EventType == EET_GUI_EVENT && + event.GUIEvent.EventType == EGET_BUTTON_CLICKED && + event.GUIEvent.Caller->getID() == E_DIALOG_ID_SETTINGS_OK) + { + IGUIEditBox *edit; + IGUISpinBox *spin; + ColorCtrl *color; + + color = (ColorCtrl*)getElementFromId(E_DIALOG_ID_BG_COLOR, true); + if (color) + { + const std::string hex = color->getColor(); + if (isValidHexString(hex)) + conf->set("bg_color", hex); + } + color = (ColorCtrl*)getElementFromId(E_DIALOG_ID_GRID_COLOR, true); + if (color) + { + const std::string hex = color->getColor(); + if (isValidHexString(hex)) + conf->set("grid_color", hex); + } + edit = (IGUIEditBox*) + getElementFromId(E_DIALOG_ID_WIELD_BONE, true); + std::string bone = stringc(edit->getText()).c_str(); + conf->set("wield_bone", bone); + + spin = (IGUISpinBox*) + getElementFromId(E_DIALOG_ID_SCREEN_WIDTH, true); + u32 width = spin->getValue(); + conf->set("screen_width", std::to_string(width)); + spin = (IGUISpinBox*) + getElementFromId(E_DIALOG_ID_SCREEN_HEIGHT, true); + u32 height = spin->getValue(); + conf->set("screen_height", std::to_string(height)); + + u32 flags = 0; + if (isBoxChecked(E_DIALOG_ID_DEBUG_BBOX)) + flags |= EDS_BBOX; + if (isBoxChecked(E_DIALOG_ID_DEBUG_NORMALS)) + flags |= EDS_NORMALS; + if (isBoxChecked(E_DIALOG_ID_DEBUG_SKELETON)) + flags |= EDS_SKELETON; + if (isBoxChecked(E_DIALOG_ID_DEBUG_WIREFRANE)) + flags |= EDS_MESH_WIRE_OVERLAY; + if (isBoxChecked(E_DIALOG_ID_DEBUG_ALPHA)) + flags |= EDS_HALF_TRANSPARENCY; + if (isBoxChecked(E_DIALOG_ID_DEBUG_BUFFERS)) + flags |= EDS_BBOX_BUFFERS; + conf->set("debug_flags", std::to_string(flags)); + } + return IGUIElement::OnEvent(event); +} + +TexturesDialog::TexturesDialog(IGUIEnvironment *env, IGUIElement *parent, + s32 id, const rect &rectangle, Config *conf, ISceneManager *smgr) : + IGUIElement(EGUIET_ELEMENT, env, parent, id, rectangle), + conf(conf), + smgr(smgr) +{ + IGUITabControl *tabs = env->addTabControl(rect(2,2,398,250), this, + true, true); + + IGUITab *tab_model = tabs->addTab(L"Model"); + IGUITab *tab_wield = tabs->addTab(L"Wield"); + IGUIStaticText *text; + IGUIEditBox *edit; + IGUIButton *button; + stringw fn; + std::string key; + + ITexture *image = getTexture("browse.png"); + ISceneNode *model = smgr->getSceneNodeFromId(E_SCENE_ID_MODEL); + ISceneNode *wield = smgr->getSceneNodeFromId(E_SCENE_ID_WIELD); + u32 mc_model = (model) ? model->getMaterialCount() : 0; + u32 mc_wield = (wield) ? wield->getMaterialCount() : 0; + + for (s32 i = 0; i < 6; ++i) + { + s32 top = i * 30 + 20; + stringw num = stringw(i + 1); + + key = "model_texture_" + std::to_string(i + 1); + fn = conf->getCStr(key); + text = env->addStaticText(num.c_str(), rect(15,top,25,top+20), + false, false, tab_model, -1); + edit = env->addEditBox(fn.c_str(), rect(35,top,350,top+20), + true, tab_model, E_TEXTURE_ID_MODEL + i); + edit->setEnabled(i < mc_model); + edit->setOverrideColor(SColor(255,255,0,0)); + edit->enableOverrideColor(false); + button = env->addButton(rect(360,top,380,top+20), tab_model, + E_BUTTON_ID_MODEL + i); + button->setToolTipText(L"Browse"); + if (image) + { + button->setImage(image); + button->setUseAlphaChannel(true); + button->setDrawBorder(false); + } + button->setEnabled(i < mc_model); + + key = "wield_texture_" + std::to_string(i + 1); + fn = conf->getCStr(key); + text = env->addStaticText(num.c_str(), rect(15,top,25,top+20), + false, false, tab_wield, -1); + edit = env->addEditBox(fn.c_str(), rect(35,top,350,top+20), + true, tab_wield, E_TEXTURE_ID_WIELD + i); + edit->setEnabled(i < mc_wield); + edit->setOverrideColor(SColor(255,255,0,0)); + edit->enableOverrideColor(false); + button = env->addButton(rect(360,top,380,top+20), tab_wield, + E_BUTTON_ID_WIELD + i); + if (image) + { + button->setImage(image); + button->setUseAlphaChannel(true); + button->setDrawBorder(false); + } + button->setEnabled(i < mc_wield); + } + button = env->addButton(rect(315,255,395,285), this, + E_DIALOG_ID_TEXTURES_OK, L"OK"); + button = env->addButton(rect(230,255,310,285), this, + E_DIALOG_ID_TEXTURES_CANCEL, L"Cancel"); +} + +ITexture *TexturesDialog::getTexture(const io::path &filename) +{ + IVideoDriver *driver = Environment->getVideoDriver(); + ITexture *texture = driver->findTexture(filename); + if (!texture) + texture = driver->getTexture(filename); + return texture; +} + +bool TexturesDialog::OnEvent(const SEvent &event) +{ + if (event.EventType == EET_GUI_EVENT) + { + s32 id = event.GUIEvent.Caller->getID(); + if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUS_LOST) + { + if (event.GUIEvent.Caller->getType() == EGUIET_EDIT_BOX) + { + IGUIEditBox *edit = (IGUIEditBox*)event.GUIEvent.Caller; + if (edit) + { + stringc fn = stringc(edit->getText()).c_str(); + edit->enableOverrideColor(!(getTexture(fn))); + } + } + } + else if (event.GUIEvent.EventType == EGET_BUTTON_CLICKED) + { + if (id == E_DIALOG_ID_TEXTURES_OK) + { + ISceneNode *model = smgr->getSceneNodeFromId(E_SCENE_ID_MODEL); + ISceneNode *wield = smgr->getSceneNodeFromId(E_SCENE_ID_WIELD); + IGUIEditBox *edit; + + for (s32 i = 0; i < 6; ++i) + { + std::string idx = std::to_string(i + 1); + edit = (IGUIEditBox*) + getElementFromId(E_TEXTURE_ID_MODEL + i, true); + if (edit && model && i < model->getMaterialCount()) + { + stringc fn = stringc(edit->getText()).c_str(); + ITexture *texture = getTexture(fn); + if (texture) + { + std::string key = "model_texture_" + idx; + conf->set(key, fn.c_str()); + SMaterial &material = model->getMaterial(i); + material.TextureLayer[0].Texture = texture; + } + } + edit = (IGUIEditBox*) + getElementFromId(E_TEXTURE_ID_WIELD + i, true); + if (edit && wield && i < wield->getMaterialCount()) + { + stringc fn = stringc(edit->getText()).c_str(); + ITexture *texture = getTexture(fn); + if (texture) + { + std::string key = "wield_texture_" + idx; + conf->set(key, fn.c_str()); + SMaterial &material = wield->getMaterial(i); + material.TextureLayer[0].Texture = texture; + } + } + } + } + else + { + for (s32 i = 0; i < 6; ++i) + { + if (id == E_BUTTON_ID_MODEL + i) + { + Environment->addFileOpenDialog(L"Open Image File", + true, this, E_TEXTURE_ID_MODEL + i); + break; + } + else if (id == E_BUTTON_ID_WIELD + i) + { + Environment->addFileOpenDialog(L"Open Image File", + true, this, E_TEXTURE_ID_WIELD + i); + break; + } + } + } + } + else if (event.GUIEvent.EventType == EGET_FILE_SELECTED) + { + IGUIFileOpenDialog *dialog = + (IGUIFileOpenDialog*)event.GUIEvent.Caller; + + stringw fn = stringw(dialog->getFileName()); + if (!fn.empty()) + { + s32 id = dialog->getID(); + IGUIEditBox *edit = (IGUIEditBox*)getElementFromId(id, true); + if (edit) + { + edit->setText(fn.c_str()); + edit->enableOverrideColor(!(getTexture(fn))); + } + } + } + } + return IGUIElement::OnEvent(event); +} diff --git a/src/dialog.h b/src/dialog.h new file mode 100644 index 0000000..5eeba62 --- /dev/null +++ b/src/dialog.h @@ -0,0 +1,109 @@ +#ifndef D_DIALOG_H +#define D_DIALOG_H + +using namespace irr; +using namespace core; +using namespace scene; +using namespace gui; +using namespace video; + +enum +{ + E_DIALOG_ID_ABOUT = 0x1000, + E_DIALOG_ID_SETTINGS, + E_DIALOG_ID_BG_COLOR, + E_DIALOG_ID_GRID_COLOR, + E_DIALOG_ID_COLOR_EDIT, + E_DIALOG_ID_COLOR_PREVIEW, + E_DIALOG_ID_WIELD_BONE, + E_DIALOG_ID_SCREEN_WIDTH, + E_DIALOG_ID_SCREEN_HEIGHT, + E_DIALOG_ID_DEBUG_BBOX, + E_DIALOG_ID_DEBUG_NORMALS, + E_DIALOG_ID_DEBUG_SKELETON, + E_DIALOG_ID_DEBUG_WIREFRANE, + E_DIALOG_ID_DEBUG_ALPHA, + E_DIALOG_ID_DEBUG_BUFFERS, + E_DIALOG_ID_ABOUT_OK, + E_DIALOG_ID_ABOUT_LINK, + E_DIALOG_ID_SETTINGS_OK, + E_DIALOG_ID_SETTINGS_CANCEL, + E_DIALOG_ID_TEXTURES, + E_DIALOG_ID_TEXTURES_OK, + E_DIALOG_ID_TEXTURES_CANCEL +}; + +enum +{ + E_TEXTURE_ID_MODEL = 0x2000, + E_TEXTURE_ID_WIELD = 0x2010, + E_BUTTON_ID_MODEL = 0x2020, + E_BUTTON_ID_WIELD = 0x2030 +}; + +class Config; + +class HyperlinkCtrl : public IGUIElement +{ +public: + HyperlinkCtrl(IGUIEnvironment *env, IGUIElement *parent, s32 id, + const rect &rectangle, const wchar_t *title, std::string url); + virtual ~HyperlinkCtrl() {} + virtual void draw(); + virtual bool OnEvent(const SEvent &event); + +private: + std::string url; + bool is_active; +}; + +class ColorCtrl : public IGUIElement +{ +public: + ColorCtrl(IGUIEnvironment *env, IGUIElement *parent, s32 id, + const rect &rectangle, const wchar_t *label); + virtual ~ColorCtrl() {} + virtual bool OnEvent(const SEvent &event); + void setColor(const std::string &hex); + std::string getColor() const; +}; + +class AboutDialog : public IGUIElement +{ +public: + AboutDialog(IGUIEnvironment *env, IGUIElement *parent, s32 id, + const rect &rectangle); + virtual ~AboutDialog() {} +}; + +class SettingsDialog : public IGUIElement +{ +public: + SettingsDialog(IGUIEnvironment *env, IGUIElement *parent, s32 id, + const rect &rectangle, Config *conf); + virtual ~SettingsDialog() {} + virtual bool OnEvent(const SEvent &event); + +private: + bool isBoxChecked(s32 id) const; + void colorFromHexStr(const std::string &hex); + + Config *conf; +}; + +class TexturesDialog : public IGUIElement +{ +public: + TexturesDialog(IGUIEnvironment *env, IGUIElement *parent, s32 id, + const rect &rectangle, Config *conf, ISceneManager *smgr); + virtual ~TexturesDialog() {} + virtual bool OnEvent(const SEvent &event); + +private: + ITexture *getTexture(const io::path &filename); + + Config *conf; + ISceneManager *smgr; +}; + +#endif // D_DIALOG_H \ No newline at end of file diff --git a/src/gui.cpp b/src/gui.cpp new file mode 100644 index 0000000..a909bd2 --- /dev/null +++ b/src/gui.cpp @@ -0,0 +1,573 @@ +#include +#include +#include + +#include "config.h" +#include "scene.h" +#include "dialog.h" +#include "gui.h" + +VertexCtrl::VertexCtrl(IGUIEnvironment *env, IGUIElement *parent, s32 id, + const rect &rectangle, f32 step, const wchar_t *label) : + IGUIElement(EGUIET_ELEMENT, env, parent, id, rectangle), + vertex(0) +{ + IGUIStaticText *text = env->addStaticText(label, + rect(0,0,20,20), false, false, this); + + IGUISpinBox *spin = env->addSpinBox(L"", rect(20,0,120,20), + true, this, E_GUI_ID_VERTEX); + spin->setDecimalPlaces(2); + spin->setValue(0); + spin->setStepSize(step); + spin->setRange(-1000, 1000); +} + +void VertexCtrl::setValue(const f32 &value) +{ + IGUISpinBox *spin = (IGUISpinBox*)getElementFromId(E_GUI_ID_VERTEX); + if (spin) + { + spin->setValue(value); + vertex = value; + } +} + +bool VertexCtrl::OnEvent(const SEvent &event) +{ + if (event.EventType == EET_GUI_EVENT) + { + if (event.GUIEvent.EventType == EGET_SPINBOX_CHANGED) + { + IGUISpinBox *spin = (IGUISpinBox*)event.GUIEvent.Caller; + if (spin) + vertex = spin->getValue(); + } + } + else if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUS_LOST) + { + setValue(vertex); + } + return IGUIElement::OnEvent(event); +} + +VectorCtrl::VectorCtrl(IGUIEnvironment *env, IGUIElement *parent, s32 id, + const rect &rectangle, f32 step, const wchar_t *label) : + IGUIElement(EGUIET_ELEMENT, env, parent, id, rectangle), + vector(vector3df(0,0,0)) +{ + IGUIStaticText *text = env->addStaticText(label, + rect(10,0,150,20), false, false, this); + + VertexCtrl *x = new VertexCtrl(env, this, E_GUI_ID_VECTOR_X, + rect(10,30,150,50), step, L"X"); + x->drop(); + VertexCtrl *y = new VertexCtrl(env, this, E_GUI_ID_VECTOR_Y, + rect(10,60,150,80), step, L"Y"); + y->drop(); + VertexCtrl *z = new VertexCtrl(env, this, E_GUI_ID_VECTOR_Z, + rect(10,90,150,110), step, L"Z"); + z->drop(); +} + +void VectorCtrl::setVector(const vector3df &vec) +{ + vector = vec; + VertexCtrl *vertex; + vertex = (VertexCtrl*)getElementFromId(E_GUI_ID_VECTOR_X); + if (vertex) + vertex->setValue(vector.X); + vertex = (VertexCtrl*)getElementFromId(E_GUI_ID_VECTOR_Y); + if (vertex) + vertex->setValue(vector.Y); + vertex = (VertexCtrl*)getElementFromId(E_GUI_ID_VECTOR_Z); + if (vertex) + vertex->setValue(vector.Z); +} + +bool VectorCtrl::OnEvent(const SEvent &event) +{ + if (event.EventType == EET_GUI_EVENT && + event.GUIEvent.EventType == EGET_SPINBOX_CHANGED) + { + VertexCtrl *vertex = (VertexCtrl*)event.GUIEvent.Caller->getParent(); + if (vertex) + { + switch (vertex->getID()) + { + case E_GUI_ID_VECTOR_X: + vector.X = vertex->getValue(); + break; + case E_GUI_ID_VECTOR_Y: + vector.Y = vertex->getValue(); + break; + case E_GUI_ID_VECTOR_Z: + vector.Z = vertex->getValue(); + break; + default: + break; + } + SEvent new_event = event; + new_event.GUIEvent.Caller = this; + return IGUIElement::OnEvent(new_event); + } + } + return IGUIElement::OnEvent(event); +} + +AnimCtrl::AnimCtrl(IGUIEnvironment *env, IGUIElement *parent, s32 id, + const rect &rectangle) : + IGUIElement(EGUIET_ELEMENT, env, parent, id, rectangle), + button_id(E_GUI_ID_PAUSE) +{ + IVideoDriver *driver = env->getVideoDriver(); + ITexture *image; + IGUIButton *button; + + image = driver->getTexture("skip_rev.png"); + button = env->addButton(rect(0,4,23,27), this, + E_GUI_ID_SKIP_REV); + button->setImage(image); + button->setUseAlphaChannel(true); + + image = driver->getTexture("play_rev.png"); + button = env->addButton(rect(24,4,47,27), this, + E_GUI_ID_PLAY_REV); + button->setImage(image); + button->setUseAlphaChannel(true); + + image = driver->getTexture("pause.png"); + button = env->addButton(rect(48,4,71,27), this, + E_GUI_ID_PAUSE); + button->setImage(image); + button->setUseAlphaChannel(true); + + image = driver->getTexture("play_fwd.png"); + button = env->addButton(rect(72,4,95,27), this, + E_GUI_ID_PLAY_FWD); + button->setImage(image); + button->setUseAlphaChannel(true); + + image = driver->getTexture("skip_fwd.png"); + button = env->addButton(rect(96,4,119,27), this, + E_GUI_ID_SKIP_FWD); + button->setImage(image); + button->setUseAlphaChannel(true); +} + +bool AnimCtrl::OnEvent(const SEvent &event) +{ + if (event.EventType == EET_GUI_EVENT) + { + if (event.GUIEvent.EventType == EGET_BUTTON_CLICKED) + { + reset(true); + IGUIButton *button = (IGUIButton*)event.GUIEvent.Caller; + button->setEnabled(false); + button->setPressed(true); + button_id = button->getID(); + } + else if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUS_LOST) + { + if (button_id == E_GUI_ID_SKIP_FWD || + button_id == E_GUI_ID_SKIP_REV) + { + reset(true); + IGUIButton *button = (IGUIButton*) + getElementFromId(E_GUI_ID_PAUSE); + if (button) + button->setPressed(true); + button_id = E_GUI_ID_PAUSE; + } + } + } + return IGUIElement::OnEvent(event); +} + +void AnimCtrl::reset(bool enabled) +{ + const list &children = getChildren(); + list::ConstIterator iter = children.begin(); + while (iter != children.end()) + { + if ((*iter)->getType() == EGUIET_BUTTON) + { + IGUIButton *button = (IGUIButton*)(*iter); + button->setEnabled(enabled); + button->setPressed(false); + } + ++iter; + } +} + +ToolBox::ToolBox(IGUIEnvironment *env, IGUIElement *parent, s32 id, + const rect &rectangle, ISceneNode *node) : + IGUIElement(EGUIET_ELEMENT, env, parent, id, rectangle) +{ + smgr = node->getSceneManager(); + node_id = node->getID(); + + IGUIWindow *dialog = (IGUIWindow*)parent; + dialog->setDraggable(false); + VectorCtrl *position = new VectorCtrl(env, this, E_GUI_ID_POSITION, + rect(10,0,150,120), 1.0, L"Position:"); + + VectorCtrl *rotation = new VectorCtrl(env, this, E_GUI_ID_ROTATION, + rect(10,130,150,250), 15.0, L"Rotation:"); + + IGUIStaticText *text; + text = env->addStaticText(L"Scale:", + rect(20,260,150,280), false, false, this); + + text = env->addStaticText(L"%", + rect(20,290,40,310), false, false, this); + + IGUISpinBox *scale = env->addSpinBox(L"", rect(40,290,140,310), + true, this, E_GUI_ID_SCALE); + + scale->setDecimalPlaces(0); + scale->setStepSize(10.0); + scale->setRange(0, 1000); + + position->setVector(node->getPosition()); + position->drop(); + rotation->setVector(node->getRotation()); + rotation->drop(); + scale->setValue(node->getScale().Y * 100); + + text = env->addStaticText(L"Transparency:", + rect(20,330,150,350), false, false, this); + + IGUIComboBox *combo = env->addComboBox(rect(20,360,140,380), + this, E_GUI_ID_MATERIAL); + combo->addItem(L"Opaque"); + combo->addItem(L"Alpha Channel"); + combo->addItem(L"Alpha Test"); + + switch (node->getMaterial(0).MaterialType) + { + case EMT_TRANSPARENT_ALPHA_CHANNEL: + combo->setSelected(1); + break; + case EMT_TRANSPARENT_ALPHA_CHANNEL_REF: + combo->setSelected(2); + break; + default: + break; + } +} + +bool ToolBox::OnEvent(const SEvent &event) +{ + if (event.EventType == EET_GUI_EVENT) + { + s32 id = event.GUIEvent.Caller->getID(); + if (event.GUIEvent.EventType == EGET_SPINBOX_CHANGED) + { + ISceneNode *node = smgr->getSceneNodeFromId(node_id); + if (node) + { + IGUIElement *elem = (IGUIElement*)event.GUIEvent.Caller; + switch(id) + { + case E_GUI_ID_POSITION: + { + VectorCtrl *position = (VectorCtrl*)elem; + node->setPosition(position->getVector()); + break; + } + case E_GUI_ID_ROTATION: + { + VectorCtrl *rotation = (VectorCtrl*)elem; + node->setRotation(rotation->getVector()); + break; + } + case E_GUI_ID_SCALE: + { + IGUISpinBox *spin = (IGUISpinBox*)elem; + f32 s = spin->getValue() / 100; + node->setScale(vector3df(s,s,s)); + break; + } + default: + break; + } + } + } + else if(event.GUIEvent.EventType == EGET_COMBO_BOX_CHANGED) + { + ISceneNode *node = smgr->getSceneNodeFromId(node_id); + if (node) + { + IGUIComboBox *combo = (IGUIComboBox*)event.GUIEvent.Caller; + switch (combo->getSelected()) + { + case 0: + node->setMaterialType(EMT_SOLID); + break; + case 1: + node->setMaterialType(EMT_TRANSPARENT_ALPHA_CHANNEL); + break; + case 2: + node->setMaterialType(EMT_TRANSPARENT_ALPHA_CHANNEL_REF); + break; + } + } + } + } + return IGUIElement::OnEvent(event); +} + +GUI::GUI(IrrlichtDevice *device, Config *config) : + device(device), + conf(config), + has_focus(false) +{ + IGUIEnvironment *env = device->getGUIEnvironment(); + IGUISkin *skin = env->getSkin(); + IGUIFont *font = env->getFont("fontlucida.png"); + if (font) + skin->setFont(font); + + skin->setColor(EGDC_3D_FACE, SColor(255,232,232,232)); + skin->setColor(EGDC_3D_DARK_SHADOW, SColor(255,160,160,160)); + skin->setColor(EGDC_3D_HIGH_LIGHT, SColor(255,248,248,248)); + skin->setColor(EGDC_3D_LIGHT, SColor(255,255,255,255)); + skin->setColor(EGDC_3D_SHADOW, SColor(255,196,196,196)); + skin->setColor(EGDC_ACTIVE_BORDER, SColor(255,232,232,232)); + skin->setColor(EGDC_INACTIVE_BORDER, SColor(255,232,232,232)); + skin->setColor(EGDC_GRAY_EDITABLE, SColor(255,172,172,172)); + skin->setColor(EGDC_GRAY_TEXT, SColor(255,96,96,96)); + skin->setColor(EGDC_ACTIVE_CAPTION, SColor(255,16,16,16)); + skin->setColor(EGDC_INACTIVE_CAPTION, SColor(255,64,64,64)); + + for (s32 i=0; i < EGDC_COUNT; ++i) + { + SColor col = skin->getColor((EGUI_DEFAULT_COLOR)i); + col.setAlpha(255); + skin->setColor((EGUI_DEFAULT_COLOR)i, col); + } +} + +void GUI::initMenu() +{ + IGUIEnvironment *env = device->getGUIEnvironment(); + IGUIContextMenu *menu = env->addMenu(0, E_GUI_ID_MENU); + menu->addItem(L"File", -1, true, true); + menu->addItem(L"Edit", -1, true, true); + menu->addItem(L"View", -1, true, true); + menu->addItem(L"Help", -1, true, true); + + IGUIContextMenu *submenu; + submenu = menu->getSubMenu(0); + submenu->addItem(L"Load Model Mesh", E_GUI_ID_LOAD_MODEL_MESH); + submenu->addItem(L"Load Wield Mesh", E_GUI_ID_LOAD_WIELD_MESH); + submenu->addSeparator(); + submenu->addItem(L"Save Configuration", E_GUI_ID_SAVE_CONFIG); + submenu->addSeparator(); + submenu->addItem(L"Quit", E_GUI_ID_QUIT); + + submenu = menu->getSubMenu(1); + submenu->addItem(L"Textures", E_GUI_ID_TEXTURES_DIALOG); + submenu->addSeparator(); + submenu->addItem(L"Preferences", E_GUI_ID_SETTINGS_DIALOG); + + submenu = menu->getSubMenu(2); + submenu->addItem(L"Model Toolbox", E_GUI_ID_TOOLBOX_MODEL, true, false, + false, true); + submenu->addItem(L"Wield Toolbox", E_GUI_ID_TOOLBOX_WIELD, true, false, + false, true); + submenu->addSeparator(); + submenu->addItem(L"Show Grid", E_GUI_ID_SHOW_GRID, true, false, + true, true); + submenu->addItem(L"Show Axes", E_GUI_ID_SHOW_AXES, true, false, + true, true); + submenu->addSeparator(); + submenu->addItem(L"Projection", -1, true, true); + submenu->addItem(L"Filters", -1, true, true); + submenu->addSeparator(); + submenu->addItem(L"Show Wield Item", E_GUI_ID_ENABLE_WIELD, true, false, + conf->getBool("wield_show"), true); + submenu->addItem(L"Backface Culling", E_GUI_ID_BACK_FACE_CULL, true, false, + conf->getBool("backface_cull"), true); + submenu->addSeparator(); + submenu->addItem(L"Model Debug Info", E_GUI_ID_DEBUG_INFO, true, false, + conf->getBool("debug_info"), true); + + submenu = menu->getSubMenu(2)->getSubMenu(6); + submenu->addItem(L"Perspective", E_GUI_ID_PERSPECTIVE, true, false, + !conf->getBool("ortho"), true); + submenu->addItem(L"Orthogonal", E_GUI_ID_ORTHOGONAL, true, false, + conf->getBool("ortho"), true); + + submenu = menu->getSubMenu(2)->getSubMenu(7); + submenu->addItem(L"Bilinear", E_GUI_ID_BILINEAR, true, false, + conf->getBool("bilinear"), true); + submenu->addItem(L"Trilinear", E_GUI_ID_TRILINEAR, true, false, + conf->getBool("trilinear"), true); + submenu->addItem(L"Anisotropic", E_GUI_ID_ANISOTROPIC, true, false, + conf->getBool("anisotropic"), true); + + submenu = menu->getSubMenu(3); + submenu->addItem(L"About", E_DIALOG_ID_ABOUT); +} + +void GUI::initToolBar() +{ + IVideoDriver *driver = device->getVideoDriver(); + IGUIEnvironment *env = device->getGUIEnvironment(); + IGUIStaticText *text; + IGUISpinBox *spin; + + IGUIToolBar *toolbar = env->addToolBar(0, E_GUI_ID_TOOLBAR); + text = env->addStaticText(L"Animation:", + rect(20,5,120,25), false, false, toolbar); + + text = env->addStaticText(L"Start", + rect(130,5,160,25), false, false, toolbar); + spin = env->addSpinBox(L"", rect(170,5,230,25), + true, toolbar, E_GUI_ID_ANIM_START); + spin->setDecimalPlaces(0); + spin->setRange(0, 10000); + + text = env->addStaticText(L"End", + rect(255,5,280,25), false, false, toolbar); + spin = env->addSpinBox(L"", rect(290,5,350,25), + true, toolbar, E_GUI_ID_ANIM_END); + spin->setDecimalPlaces(0); + spin->setRange(0, 10000); + + text = env->addStaticText(L"Speed", + rect(370,5,410,25), false, false, toolbar); + spin = env->addSpinBox(L"", rect(420,5,480,25), + true, toolbar, E_GUI_ID_ANIM_SPEED); + spin->setDecimalPlaces(0); + spin->setRange(0, 10000); + + text = env->addStaticText(L"Frame", + rect(495,5,535,25), false, false, toolbar); + spin = env->addSpinBox(L"", rect(550,5,610,25), + true, toolbar, E_GUI_ID_ANIM_FRAME); + spin->setDecimalPlaces(0); + spin->setRange(0, 10000); + + s32 w = driver->getScreenSize().Width; + AnimCtrl *anim = new AnimCtrl(env, toolbar, E_GUI_ID_ANIM_CTRL, + rect(w-120,0,w,30)); + anim->drop(); +} + +void GUI::showToolBox(s32 id) +{ + ISceneManager *smgr = device->getSceneManager(); + IGUIEnvironment *env = device->getGUIEnvironment(); + IGUIElement *root = env->getRootGUIElement(); + if (root->getElementFromId(id, true)) + return; + + switch (id) + { + case E_GUI_ID_TOOLBOX_MODEL: + { + IVideoDriver *driver = device->getVideoDriver(); + s32 w = driver->getScreenSize().Width; + ISceneNode *node = smgr->getSceneNodeFromId(E_SCENE_ID_MODEL); + IGUIWindow *window = env->addWindow(rect(w-160,54,w,490), + false, L"Model Properties", root, id); + ToolBox *toolbox = new ToolBox(env, window, id, + rect(0,30,160,800), node); + toolbox->drop(); + env->setFocus(window); + break; + } + case E_GUI_ID_TOOLBOX_WIELD: + { + ISceneNode *node = smgr->getSceneNodeFromId(E_SCENE_ID_WIELD); + IGUIWindow *window = env->addWindow(rect(0,54,160,490), + false, L"Wield Properties", root, id); + ToolBox *toolbox = new ToolBox(env, window, id, + rect(0,30,160,800), node); + toolbox->drop(); + env->setFocus(window); + break; + } + default: + break; + } +} + +void GUI::closeToolBox(s32 id) +{ + IGUIElement *elem = getElement(id); + if (elem) + elem->remove(); +} + +void GUI::reloadToolBox(s32 id) +{ + IGUIElement *elem = getElement(id); + if (elem) + { + elem->remove(); + showToolBox(id); + } +} + +void GUI::moveElement(s32 id, const vector2di &move) +{ + IGUIElement *elem = getElement(id); + if (elem) + { + vector2di pos = elem->getRelativePosition().UpperLeftCorner + move; + elem->setRelativePosition(position2di(pos.X, pos.Y)); + } +} + +void GUI::showTexturesDialog() +{ + IGUIEnvironment *env = device->getGUIEnvironment(); + ISceneManager *smgr = device->getSceneManager(); + IGUIWindow *window = env->addWindow(getWindowRect(400, 310), + true, L"Textures"); + + TexturesDialog *dialog = new TexturesDialog(env, window, + E_DIALOG_ID_TEXTURES, rect(0,20,400,310), conf, smgr); + dialog->drop(); +} + +void GUI::showSettingsDialog() +{ + IGUIEnvironment *env = device->getGUIEnvironment(); + IGUIWindow *window = env->addWindow(getWindowRect(400, 310), + true, L"Settings"); + + SettingsDialog *dialog = new SettingsDialog(env, window, + E_DIALOG_ID_SETTINGS, rect(0,20,400,310), conf); + dialog->drop(); +} + +void GUI::showAboutDialog() +{ + IGUIEnvironment *env = device->getGUIEnvironment(); + IGUIWindow *window = env->addWindow(getWindowRect(300, 300), + true, L"About"); + + AboutDialog *dialog = new AboutDialog(env, window, + E_DIALOG_ID_ABOUT, rect(0,20,300,300)); + dialog->drop(); +} + +IGUIElement *GUI::getElement(s32 id) +{ + IGUIEnvironment *env = device->getGUIEnvironment(); + IGUIElement *root = env->getRootGUIElement(); + return root->getElementFromId(id, true); +} + +const rect GUI::getWindowRect(const u32 &width, const u32 &height) const +{ + IVideoDriver *driver = device->getVideoDriver(); + dimension2du s = driver->getScreenSize(); + vector2di pos = vector2di((s.Width - width) / 2, (s.Height - height) / 2); + return rect(pos, pos + vector2di(width, height)); +} \ No newline at end of file diff --git a/src/gui.h b/src/gui.h new file mode 100644 index 0000000..89104ea --- /dev/null +++ b/src/gui.h @@ -0,0 +1,133 @@ +#ifndef D_GUI_H +#define D_GUI_H + +using namespace irr; +using namespace core; +using namespace scene; +using namespace gui; +using namespace video; + +enum +{ + E_GUI_ID_MENU, + E_GUI_ID_TOOLBAR, + E_GUI_ID_LOAD_MODEL_MESH, + E_GUI_ID_LOAD_WIELD_MESH, + E_GUI_ID_SAVE_CONFIG, + E_GUI_ID_QUIT, + E_GUI_ID_TEXTURES_DIALOG, + E_GUI_ID_SETTINGS_DIALOG, + E_GUI_ID_TOOLBOX_MODEL, + E_GUI_ID_TOOLBOX_WIELD, + E_GUI_ID_SHOW_GRID, + E_GUI_ID_SHOW_AXES, + E_GUI_ID_ENABLE_WIELD, + E_GUI_ID_BACK_FACE_CULL, + E_GUI_ID_ORTHOGONAL, + E_GUI_ID_PERSPECTIVE, + E_GUI_ID_BILINEAR, + E_GUI_ID_TRILINEAR, + E_GUI_ID_ANISOTROPIC, + E_GUI_ID_DEBUG_INFO, + E_GUI_ID_VERTEX, + E_GUI_ID_VECTOR_X, + E_GUI_ID_VECTOR_Y, + E_GUI_ID_VECTOR_Z, + E_GUI_ID_POSITION, + E_GUI_ID_ROTATION, + E_GUI_ID_SCALE, + E_GUI_ID_MATERIAL, + E_GUI_ID_ANIM_CTRL, + E_GUI_ID_ANIM_START, + E_GUI_ID_ANIM_END, + E_GUI_ID_ANIM_FRAME, + E_GUI_ID_ANIM_SPEED, + E_GUI_ID_SKIP_REV, + E_GUI_ID_PLAY_REV, + E_GUI_ID_PAUSE, + E_GUI_ID_PLAY_FWD, + E_GUI_ID_SKIP_FWD +}; + +class Config; + +class VertexCtrl : public IGUIElement +{ +public: + VertexCtrl(IGUIEnvironment *env, IGUIElement *parent, s32 id, + const rect &rectangle, f32 step, const wchar_t *label); + virtual ~VertexCtrl() {} + virtual bool OnEvent(const SEvent &event); + f32 getValue() const { return vertex; } + void setValue(const f32 &value); + +private: + f32 vertex; +}; + +class VectorCtrl : public IGUIElement +{ +public: + VectorCtrl(IGUIEnvironment *env, IGUIElement *parent, s32 id, + const rect &rectangle, f32 step, const wchar_t *label); + virtual ~VectorCtrl() {} + virtual bool OnEvent(const SEvent &event); + vector3df getVector() const { return vector; } + void setVector(const vector3df &vec); + +private: + vector3df vector; +}; + +class AnimCtrl : public IGUIElement +{ +public: + AnimCtrl(IGUIEnvironment *env, IGUIElement *parent, s32 id, + const rect &rectangle); + virtual ~AnimCtrl() {} + virtual bool OnEvent(const SEvent &event); + void reset(bool enabled); + +private: + s32 button_id; +}; + +class ToolBox : public IGUIElement +{ +public: + ToolBox(IGUIEnvironment *env, IGUIElement *parent, s32 id, + const rect &rectangle, ISceneNode *node); + virtual ~ToolBox() {} + virtual bool OnEvent(const SEvent &event); + +private: + s32 node_id; + ISceneManager *smgr; +}; + +class GUI +{ +public: + GUI(IrrlichtDevice *device, Config *config); + void initMenu(); + void initToolBar(); + void showToolBox(s32 id); + void closeToolBox(s32 id); + void reloadToolBox(s32 id); + IGUIElement *getElement(s32 id); + bool getFocused() const { return has_focus; } + void setFocused(const bool &focus) { has_focus = focus; } + void moveElement(s32 id, const vector2di &move); + void showTexturesDialog(); + void showSettingsDialog(); + void showAboutDialog(); + +private: + const rect getWindowRect(const u32 &width, const u32 &height) const; + + IrrlichtDevice *device; + Config *conf; + bool has_focus; +}; + +#endif // D_GUI_H \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..871c663 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,74 @@ +#include +#include +#include + +#include "config.h" +#include "viewer.h" + +int main() +{ + Config *conf = new Config("../bin/config.ini"); + std::map defaults = { + {"model_mesh", "character.b3d"}, + {"model_position", "0,-10,0"}, + {"model_rotation", "0,0,0"}, + {"model_scale", "100"}, + {"model_material", "14"}, + {"model_texture_1", "character.png"}, + {"model_texture_2", "blank.png"}, + {"model_texture_3", "blank.png"}, + {"model_texture_4", "blank.png"}, + {"model_texture_5", "blank.png"}, + {"model_texture_6", "blank.png"}, + {"wield_mesh", "pickaxe.obj"}, + {"wield_position", "0,5,0"}, + {"wield_rotation", "0,0,0"}, + {"wield_scale", "400"}, + {"wield_material", "14"}, + {"wield_show", "true"}, + {"wield_bone", "Arm_Right"}, + {"wield_texture_1", "pickaxe.png"}, + {"wield_texture_2", "blank.png"}, + {"wield_texture_3", "blank.png"}, + {"wield_texture_4", "blank.png"}, + {"wield_texture_5", "blank.png"}, + {"wield_texture_6", "blank.png"}, + {"anim_start", "168"}, + {"anim_end", "187"}, + {"anim_speed", "15"}, + {"ortho", "false"}, + {"bilinear", "false"}, + {"trilinear", "false"}, + {"anisotropic", "false"}, + {"backface_cull", "true"}, + {"bg_color", "808080"}, + {"grid_color", "404040"}, + {"screen_width", "800"}, + {"screen_height","600"}, + {"debug_info", "false"}, + {"debug_flags", "1"}, + }; + conf->load(); + for (std::map::iterator it = defaults.begin(); + it != defaults.end(); it++) + { + if (!conf->hasKey(it->first)) + conf->set(it->first, it->second); + } + conf->save(); + + u32 width = conf->getInt("screen_width"); + u32 height = conf->getInt("screen_height"); + IrrlichtDevice *device = createDevice(EDT_OPENGL, + dimension2d(width, height), 16, false, false, false); + + if (device && conf) + { + Viewer *viewer = new Viewer(conf); + viewer->run(device); + device->drop(); + delete viewer; + delete conf; + } + return 1; +} diff --git a/src/scene.cpp b/src/scene.cpp new file mode 100644 index 0000000..3ff2303 --- /dev/null +++ b/src/scene.cpp @@ -0,0 +1,320 @@ +#include +#include +#include + +#include "config.h" +#include "scene.h" + +Scene::Scene(ISceneNode *parent, ISceneManager *smgr, s32 id) : + ISceneNode(parent, smgr, id), + conf(0), + show_grid(true), + show_axes(true) +{ + material.Lighting = false; + material.MaterialType = EMT_TRANSPARENT_ALPHA_CHANNEL; + material.BackfaceCulling = false; + grid_color = SColor(64,128,128,128); +} + +bool Scene::load(Config *config) +{ + conf = config; + if (!loadModelMesh(conf->getCStr("model_mesh"))) + return false; + + setAttachment(); + loadWieldMesh(conf->getCStr("wield_mesh")); + setBackFaceCulling(conf->getBool("backface_cull")); + setGridColor(conf->getHex("grid_color")); + return true; +} + +bool Scene::loadModelMesh(const io::path &filename) +{ + if (!conf) + return false; + + IAnimatedMesh *mesh = SceneManager->getMesh(filename); + if (!mesh) + return false; + + ISceneNode *wield = getNode(E_SCENE_ID_WIELD); + if (wield) + wield->setParent(this); + + ISceneNode *model = getNode(E_SCENE_ID_MODEL); + if (model) + { + model->remove(); + model = 0; + } + model = SceneManager->addAnimatedMeshSceneNode(mesh, this, + E_SCENE_ID_MODEL); + if (!model) + return false; + + Vector pos = conf->getVector("model_position"); + Vector rot = conf->getVector("model_rotation"); + f32 s = (f32)conf->getInt("model_scale") / 100; + u32 mat = conf->getInt("model_material"); + + model->setMaterialFlag(EMF_LIGHTING, false); + model->setPosition(vector3df(pos.x, pos.y, pos.z)); + model->setRotation(vector3df(rot.x, rot.y, rot.z)); + model->setScale(vector3df(s,s,s)); + model->setMaterialType((E_MATERIAL_TYPE)mat); + model->setMaterialFlag(EMF_BILINEAR_FILTER, + conf->getBool("bilinear")); + model->setMaterialFlag(EMF_TRILINEAR_FILTER, + conf->getBool("trilinear")); + model->setMaterialFlag(EMF_ANISOTROPIC_FILTER, + conf->getBool("anisotropic")); + + setDebugInfo(conf->getBool("debug_info")); + if (wield) + setAttachment(); + + loadTextures(model, "model"); + return true; +} + +bool Scene::loadWieldMesh(const io::path &filename) +{ + if (!conf) + return false; + + IMesh *mesh = SceneManager->getMesh(filename); + if (!mesh) + return false; + + ISceneNode *wield = getNode(E_SCENE_ID_WIELD); + if (wield) + { + wield->remove(); + wield = 0; + } + wield = SceneManager->addMeshSceneNode(mesh, this, + E_SCENE_ID_WIELD); + + if (!wield) + return false; + + Vector pos = conf->getVector("wield_position"); + Vector rot = conf->getVector("wield_rotation"); + f32 s = (f32)conf->getInt("wield_scale") / 100; + u32 mat = conf->getInt("wield_material"); + + wield->setMaterialFlag(EMF_LIGHTING, false); + wield->setPosition(vector3df(pos.x, pos.y, pos.z)); + wield->setRotation(vector3df(rot.x, rot.y, rot.z)); + wield->setScale(vector3df(s,s,s)); + wield->setVisible(conf->getBool("wield_show")); + wield->setMaterialType((E_MATERIAL_TYPE)mat); + wield->setMaterialFlag(EMF_BILINEAR_FILTER, + conf->getBool("bilinear")); + wield->setMaterialFlag(EMF_TRILINEAR_FILTER, + conf->getBool("trilinear")); + wield->setMaterialFlag(EMF_ANISOTROPIC_FILTER, + conf->getBool("anisotropic")); + wield->setVisible(conf->getBool("wield_show")); + + setAttachment(); + loadTextures(wield, "wield"); + return true; +} + +void Scene::loadTextures(ISceneNode *node, const std::string &prefix) +{ + IVideoDriver *driver = SceneManager->getVideoDriver(); + for (u32 i = 0; i < node->getMaterialCount(); ++i) + { + std::string key = prefix + "_texture_" + std::to_string(i + 1); + io::path fn = conf->getCStr(key); + ITexture *texture = driver->getTexture(fn); + if (texture) + driver->removeTexture(texture); + texture = driver->getTexture(fn); + if (texture) + { + SMaterial &material = node->getMaterial(i); + material.TextureLayer[0].Texture = texture; + } + } +} + +ISceneNode *Scene::getNode(s32 id) +{ + return SceneManager->getSceneNodeFromId(id); +} + +void Scene::setAttachment() +{ + if (!conf) + return; + + IAnimatedMeshSceneNode *model = + (IAnimatedMeshSceneNode*)getNode(E_SCENE_ID_MODEL); + ISceneNode *wield = getNode(E_SCENE_ID_WIELD); + if (!model || !wield) + return; + + IBoneSceneNode *bone = 0; + if (model->getJointCount() > 0) + { + stringc wield_bone = conf->getCStr("wield_bone"); + bone = model->getJointNode(wield_bone.c_str()); + } + if (bone) + wield->setParent(bone); + else + wield->setParent(model); +} + +void Scene::setAnimation(const u32 &start, const u32 &end, const s32 &speed) +{ + if (!conf) + return; + + IAnimatedMeshSceneNode *model = + (IAnimatedMeshSceneNode*)getNode(E_SCENE_ID_MODEL); + + if (model) + { + model->setFrameLoop(start, end); + model->setAnimationSpeed(speed); + } +} + +void Scene::setFilter(E_MATERIAL_FLAG flag, const bool &is_enabled) +{ + ISceneNode *model = getNode(E_SCENE_ID_MODEL); + if (model) + model->setMaterialFlag(flag, is_enabled); + + ISceneNode *wield = getNode(E_SCENE_ID_WIELD); + if (wield) + wield->setMaterialFlag(flag, is_enabled); +} + +void Scene::setBackFaceCulling(const bool &is_enabled) +{ + ISceneNode *model = getNode(E_SCENE_ID_MODEL); + if (model) + model->setMaterialFlag(EMF_BACK_FACE_CULLING, is_enabled); + + ISceneNode *wield = getNode(E_SCENE_ID_WIELD); + if (wield) + wield->setMaterialFlag(EMF_BACK_FACE_CULLING, is_enabled); +} + +void Scene::setDebugInfo(const bool &is_visible) +{ + ISceneNode *model = getNode(E_SCENE_ID_MODEL); + if (model) + { + u32 state = (is_visible) ? conf->getInt("debug_flags") : EDS_OFF; + model->setDebugDataVisible(state); + } +} + +void Scene::rotate(s32 axis, const s32 &step) +{ + vector3df rot = getRotation(); + switch (axis) + { + case E_SCENE_AXIS_X: + rot.X = int(rot.X / step) * step + step; + break; + case E_SCENE_AXIS_Y: + rot.Y = int(rot.Y / step) * step + step; + break; + case E_SCENE_AXIS_Z: + rot.Z = int(rot.Z / step) * step + step; + break; + } + setRotation(rot); +} + +void Scene::refresh() +{ + ISceneNode *model = getNode(E_SCENE_ID_MODEL); + if (model) + loadTextures(model, "model"); + ISceneNode *wield = getNode(E_SCENE_ID_WIELD); + if (wield) + loadTextures(wield, "wield"); +} + +void Scene::jump() +{ + // quick and dirty jump animation to test attachment inertia + // this should eventually be improved or removed + ISceneNode *model = getNode(E_SCENE_ID_MODEL); + if (!model) + return; + + Vector p = conf->getVector("model_position"); + vector3df start = vector3df(p.x,p.y,p.z); + vector3df end = start + vector3df(0,10,0); + ISceneNodeAnimator *anim = + SceneManager->createFlyStraightAnimator(start, end, 380, false, true); + model->removeAnimators(); + model->addAnimator(anim); + anim->drop(); +} + +void Scene::OnRegisterSceneNode() +{ + if (IsVisible) + SceneManager->registerNodeForRendering(this); + + ISceneNode::OnRegisterSceneNode(); +} + +void Scene::render() +{ + if (!show_grid) + return; + + IVideoDriver *driver = SceneManager->getVideoDriver(); + driver->setMaterial(material); + driver->setTransform(ETS_WORLD, AbsoluteTransformation); + + SColor grid = grid_color; + grid.setAlpha(64); + for (f32 n = -10; n <= 10; n += 2) + { + driver->draw3DLine(vector3df(n,0,-10), vector3df(n,-0,10), grid); + driver->draw3DLine(vector3df(-10,0,n), vector3df(10,-0,n), grid); + } + if (!show_axes) + return; + + SColor red(128,255,0,0); + SColor green(128,0,255,0); + SColor blue(128,0,0,255); + + driver->draw3DLine(vector3df(-10,0,-10), vector3df(-8,0,-10), red); + driver->draw3DLine(vector3df(-8,0,-10), vector3df(-8.5,0,-9.8), red); + driver->draw3DLine(vector3df(-8,0,-10), vector3df(-8.5,0,-10.2), red); + + driver->draw3DLine(vector3df(-10,0,-10), vector3df(-10,2,-10), green); + driver->draw3DLine(vector3df(-10,2,-10), vector3df(-9.8,1.5,-10), green); + driver->draw3DLine(vector3df(-10,2,-10), vector3df(-10.2,1.5,-10), green); + + driver->draw3DLine(vector3df(-10,0,-10), vector3df(-10,0,-8), blue); + driver->draw3DLine(vector3df(-10,0,-8), vector3df(-9.8,0,-8.5), blue); + driver->draw3DLine(vector3df(-10,0,-8), vector3df(-10.2,0,-8.5), blue); + + IGUIEnvironment *env = SceneManager->getGUIEnvironment(); + IGUIFont *font = env->getFont("fontlucida.png"); + if (font) + { + s32 btm = driver->getScreenSize().Height; + s32 top = btm - 20; + font->draw(L"X", rect(5,top,15,btm), red); + font->draw(L"Y", rect(15,top,25,btm), green); + font->draw(L"Z", rect(25,top,35,btm), blue); + } +} diff --git a/src/scene.h b/src/scene.h new file mode 100644 index 0000000..d7bc4d0 --- /dev/null +++ b/src/scene.h @@ -0,0 +1,64 @@ +#ifndef D_SCENE_H +#define D_SCENE_H + +enum +{ + E_SCENE_ID, + E_SCENE_ID_MODEL, + E_SCENE_ID_WIELD +}; + +enum +{ + E_SCENE_AXIS_X, + E_SCENE_AXIS_Y, + E_SCENE_AXIS_Z +}; + +using namespace irr; +using namespace core; +using namespace scene; +using namespace gui; +using namespace video; + +class Config; + +class Scene : public ISceneNode +{ +public: + Scene(ISceneNode *parent, ISceneManager *mgr, s32 id); + ~Scene() {} + bool load(Config *config); + bool loadModelMesh(const io::path &filename); + bool loadWieldMesh(const io::path &filename); + ISceneNode *getNode(s32 id); + void setAttachment(); + void setAnimation(const u32 &start, const u32 &end, const s32 &speed); + void setFilter(E_MATERIAL_FLAG flag, const bool &is_enabled); + void setBackFaceCulling(const bool &is_enabled); + void setGridColor(SColor color) { grid_color = color; } + void setGridVisible(const bool &is_visible) { show_grid = is_visible; } + void setAxesVisible(const bool &is_visible) { show_axes = is_visible; } + void setDebugInfo(const bool &is_visible); + void rotate(s32 axis, const s32 &step); + void refresh(); + void jump(); + + virtual void OnRegisterSceneNode(); + virtual const aabbox3d &getBoundingBox() const { return box; } + virtual SMaterial &getMaterial(u32 i) { return material; } + virtual u32 getMaterialCount() const { return 1; } + virtual void render(); + +private: + void loadTextures(ISceneNode *node, const std::string &prefix); + + Config *conf; + bool show_grid; + bool show_axes; + SColor grid_color; + aabbox3d box; + SMaterial material; +}; + +#endif // D_SCENE_H diff --git a/src/trackball.cpp b/src/trackball.cpp new file mode 100644 index 0000000..4c51926 --- /dev/null +++ b/src/trackball.cpp @@ -0,0 +1,84 @@ +#include +#include + +#include "trackball.h" + +Trackball::Trackball(const u32 &width, const u32 &height) : + is_clicked(false), + is_moving(false) +{ + transform.makeIdentity(); + setBounds(width, height); +} + +vector3df Trackball::getVector(const position2df &pos) const +{ + vector3df vect; + vect.X = 1.0 * pos.X / screen.Width * 2 - 1.0; + vect.Y = 1.0 * pos.Y / screen.Height * 2 - 1.0; + vect.Z = 0; + + f32 len = vect.X * vect.X + vect.Y * vect.Y; + if (len > 1.0f) + vect.normalize(); + else + vect.Z = -sqrt(1.0f - len); + + return vect; +} + +void Trackball::setBounds(const u32 &width, const u32 &height) +{ + screen = dimension2di(width, height); +} + +void Trackball::setDragPos(const s32 &x, const s32 &y) +{ + drag_pos = position2df(x, y); +} + +void Trackball::animateNode(ISceneNode *node, u32 timeMs) +{ + if (!node) + return; + + if (!is_moving) + { + if (is_clicked) + { + is_moving = true; + transform = node->getAbsoluteTransformation(); + drag_start = drag_pos; + } + } + else + { + if (is_clicked) + { + matrix4 rotation; + quaternion quat; + vector3df start = getVector(drag_start); + vector3df end = getVector(drag_pos); + vector3df perp = start.crossProduct(end); + if (iszero(perp.getLength())) + { + quat.makeIdentity(); + } + else + { + quat.X = perp.X; + quat.Y = perp.Y; + quat.Z = perp.Z; + quat.W = start.dotProduct(end); + } + quat.getMatrix(rotation); + rotation *= transform; + node->setRotation(rotation.getRotationDegrees()); + node->updateAbsolutePosition(); + } + else + { + is_moving = false; + } + } +} diff --git a/src/trackball.h b/src/trackball.h new file mode 100644 index 0000000..3a42fbb --- /dev/null +++ b/src/trackball.h @@ -0,0 +1,33 @@ +#ifndef D_TRACKBALL_H +#define D_TRACKBALL_H + +using namespace irr; +using namespace core; +using namespace scene; + +class Trackball : public ISceneNodeAnimator +{ +public: + Trackball(const u32 &width, const u32 &height); + ~Trackball(void) {} + void setBounds(const u32 &width, const u32 &height); + void setDragPos(const s32 &x, const s32 &y); + void click() { is_clicked = true; } + void release() { is_clicked = false; } + bool isClicked() const { return is_clicked; } + virtual void animateNode(ISceneNode *node, u32 timeMs); + virtual ISceneNodeAnimator *createClone(ISceneNode *node, + ISceneManager *newManager = 0) { return 0; } + +private: + vector3df getVector(const position2df &pos) const; + + bool is_clicked; + bool is_moving; + position2df drag_start; + position2df drag_pos; + dimension2di screen; + matrix4 transform; +}; + +#endif // D_TRACKBALL_H diff --git a/src/viewer.cpp b/src/viewer.cpp new file mode 100644 index 0000000..e179ae4 --- /dev/null +++ b/src/viewer.cpp @@ -0,0 +1,640 @@ +#include +#include +#include +#include + +#include "config.h" +#include "scene.h" +#include "trackball.h" +#include "gui.h" +#include "dialog.h" +#include "viewer.h" + +#define M_ZOOM_IN(fov) std::max(fov - DEGTORAD * 2, PI * 0.0125f) +#define M_ZOOM_OUT(fov) std::min(fov + DEGTORAD * 2, PI * 0.5f) + +Viewer::Viewer(Config *conf) : + conf(conf), + device(0), + camera(0), + scene(0), + trackball(0), + gui(0), + animation(0) +{} + +Viewer::~Viewer() +{ + if (scene) + scene->drop(); + if (trackball) + delete trackball; + if (gui) + delete gui; + if (animation) + delete animation; +} + +bool Viewer::run(IrrlichtDevice *irr_device) +{ + device = irr_device; + device->getFileSystem()->addFileArchive("../assets/"); + device->getFileSystem()->addFileArchive("../media/"); + device->getFileSystem()->changeWorkingDirectoryTo("../media/"); + device->setEventReceiver(this); + + IVideoDriver *driver = device->getVideoDriver(); + ISceneManager *smgr = device->getSceneManager(); + IGUIEnvironment *env = device->getGUIEnvironment(); + + screen = driver->getScreenSize(); + trackball = new Trackball(screen.Width, screen.Height); + scene = new Scene(smgr->getRootSceneNode(), smgr, E_SCENE_ID); + scene->addAnimator(trackball); + + gui = new GUI(device, conf); + gui->initMenu(); + gui->initToolBar(); + + if (!scene->load(conf)) + return false; + + animation = new AnimState(env); + animation->load(scene->getNode(E_SCENE_ID_MODEL)); + animation->setField(E_GUI_ID_ANIM_START, conf->getInt("anim_start")); + animation->setField(E_GUI_ID_ANIM_END, conf->getInt("anim_end")); + animation->setField(E_GUI_ID_ANIM_SPEED, conf->getInt("anim_speed")); + animation->setField(E_GUI_ID_ANIM_FRAME, conf->getInt("anim_start")); + scene->setAnimation(conf->getInt("anim_start"), + conf->getInt("anim_start"), conf->getInt("anim_speed")); + + camera = smgr->addCameraSceneNode(0, vector3df(0,0,30), vector3df(0,0,0)); + fov = camera->getFOV(); + fov_home = fov; + jump_time = 0; + + setCaptionFileName(conf->getCStr("model_mesh")); + setBackgroundColor(conf->getHex("bg_color")); + setProjection(); + + while (device->run()) + { + resize(); + driver->beginScene(true, true, bg_color); + smgr->drawAll(); + env->drawAll(); + driver->endScene(); + animation->update(scene->getNode(E_SCENE_ID_MODEL)); + } + return true; +} + +void Viewer::resize() +{ + IVideoDriver *driver = device->getVideoDriver(); + dimension2du dim = driver->getScreenSize(); + if (screen == dim) + return; + + const vector2di move = vector2di(dim.Width - screen.Width, 0); + gui->moveElement(E_GUI_ID_TOOLBOX_MODEL, move); + gui->moveElement(E_GUI_ID_ANIM_CTRL, move); + + screen = dim; + trackball->setBounds(screen.Width, screen.Height); + camera->setAspectRatio((f32)screen.Width / (f32)screen.Height); + setProjection(); +} + +void Viewer::setProjection() +{ + f32 width = (f32)screen.Width * fov / 20.0f; + f32 height = (f32)screen.Height * fov / 20.0f; + ortho.buildProjectionMatrixOrthoLH(width, height, 1.0f, 1000.f); + + if (conf->getBool("ortho")) + camera->setProjectionMatrix(ortho, true); + else + camera->setFOV(fov); +} + +void Viewer::setBackgroundColor(const u32 &color) +{ + bg_color.color = color; + bg_color.setAlpha(255); +} + +void Viewer::setCaptionFileName(const io::path &filename) +{ + io::IFileSystem *fs = device->getFileSystem(); + stringw caption = fs->getFileBasename(filename) + L" - SAM-Viewer"; + device->setWindowCaption(caption.c_str()); +} + +static inline std::string boolToString(bool b) +{ + return (b) ? "true" : "false"; +} + +static inline std::string vectorToString(vector3df v) +{ + std::ostringstream ss; + ss << v.X << "," << v.Y << "," << v.Z; + std::string str(ss.str()); + return str; +} + +bool Viewer::OnEvent(const SEvent &event) +{ + if (event.EventType == EET_GUI_EVENT) + { + if (event.GUIEvent.EventType == EGET_MENU_ITEM_SELECTED) + { + IGUIContextMenu *menu = (IGUIContextMenu*)event.GUIEvent.Caller; + s32 item = menu->getSelectedItem(); + s32 id = menu->getItemCommandId(item); + IGUIEnvironment *env = device->getGUIEnvironment(); + switch (id) + { + case E_GUI_ID_LOAD_MODEL_MESH: + env->addFileOpenDialog(L"Open main model file", true, 0, + E_GUI_ID_LOAD_MODEL_MESH); + break; + case E_GUI_ID_LOAD_WIELD_MESH: + env->addFileOpenDialog(L"Open wield model file", true, 0, + E_GUI_ID_LOAD_WIELD_MESH); + break; + case E_GUI_ID_ENABLE_WIELD: + { + ISceneNode *wield = scene->getNode(E_SCENE_ID_WIELD); + if (wield) + { + wield->setVisible(menu->isItemChecked(item)); + conf->set("wield_show", + boolToString(menu->isItemChecked(item))); + } + break; + } + case E_GUI_ID_QUIT: + device->closeDevice(); + break; + case E_GUI_ID_TEXTURES_DIALOG: + gui->showTexturesDialog(); + break; + case E_GUI_ID_SETTINGS_DIALOG: + gui->showSettingsDialog(); + break; + case E_GUI_ID_TOOLBOX_MODEL: + { + if (menu->isItemChecked(item)) + gui->showToolBox(E_GUI_ID_TOOLBOX_MODEL); + else + gui->closeToolBox(E_GUI_ID_TOOLBOX_MODEL); + break; + } + case E_GUI_ID_TOOLBOX_WIELD: + { + if (menu->isItemChecked(item)) + gui->showToolBox(E_GUI_ID_TOOLBOX_WIELD); + else + gui->closeToolBox(E_GUI_ID_TOOLBOX_WIELD); + break; + } + case E_GUI_ID_SHOW_GRID: + scene->setGridVisible(menu->isItemChecked(item)); + menu->setItemEnabled(item + 1, menu->isItemChecked(item)); + break; + case E_GUI_ID_SHOW_AXES: + scene->setAxesVisible(menu->isItemChecked(item)); + break; + case E_GUI_ID_BILINEAR: + scene->setFilter(EMF_BILINEAR_FILTER, + menu->isItemChecked(item)); + conf->set("bilinear", + boolToString(menu->isItemChecked(item))); + break; + case E_GUI_ID_TRILINEAR: + scene->setFilter(EMF_TRILINEAR_FILTER, + menu->isItemChecked(item)); + conf->set("trilinear", + boolToString(menu->isItemChecked(item))); + break; + case E_GUI_ID_ANISOTROPIC: + scene->setFilter(EMF_ANISOTROPIC_FILTER, + menu->isItemChecked(item)); + conf->set("anisotropic", + boolToString(menu->isItemChecked(item))); + break; + case E_GUI_ID_PERSPECTIVE: + menu->setItemChecked(item + 1, !menu->isItemChecked(item)); + conf->set("ortho", boolToString(!menu->isItemChecked(item))); + setProjection(); + break; + case E_GUI_ID_ORTHOGONAL: + menu->setItemChecked(item - 1, !menu->isItemChecked(item)); + conf->set("ortho", boolToString(menu->isItemChecked(item))); + setProjection(); + break; + case E_GUI_ID_BACK_FACE_CULL: + scene->setBackFaceCulling(menu->isItemChecked(item)); + conf->set("backface_cull", + boolToString(menu->isItemChecked(item))); + break; + case E_GUI_ID_DEBUG_INFO: + scene->setDebugInfo(menu->isItemChecked(item)); + conf->set("debug_info", + boolToString(menu->isItemChecked(item))); + break; + case E_DIALOG_ID_ABOUT: + gui->showAboutDialog(); + break; + case E_GUI_ID_SAVE_CONFIG: + { + ISceneNode *model = scene->getNode(E_SCENE_ID_MODEL); + if (model) + { + conf->set("model_position", + vectorToString(model->getPosition())); + conf->set("model_rotation", + vectorToString(model->getRotation())); + conf->set("model_scale", + std::to_string(model->getScale().Y * 100)); + conf->set("model_material", + std::to_string(model->getMaterial(0).MaterialType)); + } + ISceneNode *wield = scene->getNode(E_SCENE_ID_WIELD); + if (wield) + { + conf->set("wield_position", + vectorToString(wield->getPosition())); + conf->set("wield_rotation", + vectorToString(wield->getRotation())); + conf->set("wield_scale", + std::to_string(wield->getScale().Y * 100)); + conf->set("wield_material", + std::to_string(wield->getMaterial(0).MaterialType)); + } + conf->set("anim_start", + std::to_string(animation->getField(E_GUI_ID_ANIM_START))); + conf->set("anim_end", + std::to_string(animation->getField(E_GUI_ID_ANIM_END))); + conf->set("anim_speed", + std::to_string(animation->getField(E_GUI_ID_ANIM_SPEED))); + conf->save(); + break; + } + default: + break; + } + } + else if (event.GUIEvent.EventType == EGET_FILE_SELECTED) + { + IGUIFileOpenDialog *dialog = + (IGUIFileOpenDialog*)event.GUIEvent.Caller; + + stringc fn = stringc(dialog->getFileName()).c_str(); + if (!fn.empty()) + { + s32 id = dialog->getID(); + switch (id) + { + case E_GUI_ID_LOAD_MODEL_MESH: + { + if (scene->loadModelMesh(fn)) + { + ISceneNode *model = scene->getNode(E_SCENE_ID_MODEL); + if (model) + { + animation->load(model); + setCaptionFileName(fn); + gui->reloadToolBox(E_GUI_ID_TOOLBOX_MODEL); + conf->set("model_mesh", fn.c_str()); + } + } + break; + } + case E_GUI_ID_LOAD_WIELD_MESH: + { + if (scene->loadWieldMesh(fn)) + { + gui->reloadToolBox(E_GUI_ID_TOOLBOX_WIELD); + conf->set("wield_mesh", fn.c_str()); + } + break; + } + default: + break; + } + } + gui->setFocused(false); + } + else if (event.GUIEvent.EventType == EGET_FILE_CHOOSE_DIALOG_CANCELLED) + { + gui->setFocused(false); + } + else if (event.GUIEvent.EventType == EGET_ELEMENT_CLOSED) + { + IGUIContextMenu *menu = + (IGUIContextMenu*)gui->getElement(E_GUI_ID_MENU); + if (menu) + { + s32 id = event.GUIEvent.Caller->getID(); + if (id == E_GUI_ID_TOOLBOX_MODEL) + menu->getSubMenu(2)->setItemChecked(0, false); + else if (id == E_GUI_ID_TOOLBOX_WIELD) + menu->getSubMenu(2)->setItemChecked(1, false); + } + gui->setFocused(false); + } + else if (event.GUIEvent.EventType == EGET_BUTTON_CLICKED) + { + s32 id = event.GUIEvent.Caller->getID(); + switch (id) + { + case E_GUI_ID_SKIP_REV: + scene->setAnimation( + animation->getField(E_GUI_ID_ANIM_START), + animation->getField(E_GUI_ID_ANIM_START), + animation->getField(E_GUI_ID_ANIM_SPEED)); + animation->setState(E_ANIM_STATE_PAUSED); + gui->setFocused(false); + break; + case E_GUI_ID_PLAY_REV: + scene->setAnimation( + animation->getField(E_GUI_ID_ANIM_START), + animation->getField(E_GUI_ID_ANIM_END), + -animation->getField(E_GUI_ID_ANIM_SPEED)); + animation->setState(E_ANIM_STATE_PLAY_REV); + gui->setFocused(false); + break; + case E_GUI_ID_PAUSE: + scene->setAnimation( + animation->getFrame(), + animation->getFrame(), + animation->getField(E_GUI_ID_ANIM_SPEED)); + animation->setState(E_ANIM_STATE_PAUSED); + gui->setFocused(false); + break; + case E_GUI_ID_PLAY_FWD: + scene->setAnimation( + animation->getField(E_GUI_ID_ANIM_START), + animation->getField(E_GUI_ID_ANIM_END), + animation->getField(E_GUI_ID_ANIM_SPEED)); + animation->setState(E_ANIM_STATE_PLAY_FWD); + gui->setFocused(false); + break; + case E_GUI_ID_SKIP_FWD: + scene->setAnimation( + animation->getField(E_GUI_ID_ANIM_END), + animation->getField(E_GUI_ID_ANIM_END), + animation->getField(E_GUI_ID_ANIM_SPEED)); + animation->setState(E_ANIM_STATE_PAUSED); + gui->setFocused(false); + break; + case E_DIALOG_ID_SETTINGS_OK: + event.GUIEvent.Caller->getParent()->getParent()->remove(); + gui->setFocused(false); + setBackgroundColor(conf->getHex("bg_color")); + scene->setGridColor(conf->getHex("grid_color")); + scene->setAttachment(); + scene->setDebugInfo(conf->getBool("debug_info")); + break; + case E_DIALOG_ID_SETTINGS_CANCEL: + event.GUIEvent.Caller->getParent()->getParent()->remove(); + gui->setFocused(false); + break; + case E_DIALOG_ID_ABOUT_OK: + case E_DIALOG_ID_TEXTURES_OK: + case E_DIALOG_ID_TEXTURES_CANCEL: + event.GUIEvent.Caller->getParent()->getParent()->remove(); + gui->setFocused(false); + break; + default: + break; + } + } + else if (event.GUIEvent.EventType == EGET_SPINBOX_CHANGED) + { + s32 id = event.GUIEvent.Caller->getID(); + switch (id) + { + case E_GUI_ID_ANIM_START: + case E_GUI_ID_ANIM_END: + { + if (animation->getState() != E_ANIM_STATE_PAUSED) + { + IAnimatedMeshSceneNode *model = (IAnimatedMeshSceneNode*) + scene->getNode(E_SCENE_ID_MODEL); + scene->setAnimation( + animation->getField(E_GUI_ID_ANIM_START), + animation->getField(E_GUI_ID_ANIM_END), + model->getAnimationSpeed()); + } + break; + } + case E_GUI_ID_ANIM_SPEED: + { + IAnimatedMeshSceneNode *model = (IAnimatedMeshSceneNode*) + scene->getNode(E_SCENE_ID_MODEL); + s32 speed = animation->getField(E_GUI_ID_ANIM_SPEED); + if (animation->getState() == E_ANIM_STATE_PLAY_REV) + speed = -speed; + + model->setAnimationSpeed(speed); + break; + } + case E_GUI_ID_ANIM_FRAME: + { + if (animation->getState() == E_ANIM_STATE_PAUSED) + { + u32 frame = animation->getField(E_GUI_ID_ANIM_FRAME); + scene->setAnimation(frame, frame, + animation->getField(E_GUI_ID_ANIM_SPEED)); + } + break; + } + default: + break; + } + } + else if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUS_LOST) + { + gui->setFocused(false); + } + else if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUSED) + { + gui->setFocused(true); + trackball->release(); + } + } + else if (event.EventType == EET_KEY_INPUT_EVENT && + event.KeyInput.PressedDown) + { + switch (event.KeyInput.Key) + { + case KEY_HOME: + { + scene->setRotation(vector3df(0,0,0)); + fov = fov_home; + setProjection(); + break; + } + case KEY_SPACE: + { + u32 now_time = device->getTimer()->getTime() + 1000; + if (jump_time + 800 < now_time) + { + jump_time = now_time; + scene->jump(); + } + break; + } + case KEY_LEFT: + scene->rotate(E_SCENE_AXIS_Y, 15); + break; + case KEY_RIGHT: + scene->rotate(E_SCENE_AXIS_Y, -15); + break; + case KEY_UP: + scene->rotate(E_SCENE_AXIS_X, -15); + break; + case KEY_DOWN: + scene->rotate(E_SCENE_AXIS_X, 15); + break; + case KEY_KEY_Z: + scene->rotate(E_SCENE_AXIS_Z, -15); + break; + case KEY_KEY_X: + scene->rotate(E_SCENE_AXIS_Z, 15); + break; + case KEY_PLUS: + fov = M_ZOOM_IN(fov); + setProjection(); + break; + case KEY_MINUS: + fov = M_ZOOM_OUT(fov); + setProjection(); + break; + case KEY_F5: + scene->refresh(); + break; + default: + break; + } + } + else if (event.EventType == EET_MOUSE_INPUT_EVENT && !gui->getFocused()) + { + switch (event.MouseInput.Event) + { + case EMIE_LMOUSE_LEFT_UP: + trackball->release(); + break; + case EMIE_MOUSE_MOVED: + { + if (event.MouseInput.isLeftPressed() && !trackball->isClicked()) + trackball->click(); + + trackball->setDragPos(event.MouseInput.X, event.MouseInput.Y); + break; + + } + case EMIE_MOUSE_WHEEL: + { + if (event.MouseInput.Wheel < 0) + fov = M_ZOOM_OUT(fov); + else + fov = M_ZOOM_IN(fov); + setProjection(); + break; + } + default: + break; + } + } + return false; +} + +AnimState::AnimState(IGUIEnvironment *env) : + env(env), + frame(0), + max(0), + state(E_ANIM_STATE_PAUSED) +{} + +void AnimState::load(ISceneNode *node) +{ + IAnimatedMeshSceneNode *model = (IAnimatedMeshSceneNode*)node; + if (!model) + return; + + IGUIToolBar *toolbar = getToolBar(); + if (!toolbar) + return; + + max = model->getEndFrame(); + model->setFrameLoop(0, 0); + state = E_ANIM_STATE_PAUSED; + + setField(E_GUI_ID_ANIM_START, 0); + setField(E_GUI_ID_ANIM_END, max); + setField(E_GUI_ID_ANIM_FRAME, 0); + + bool enabled = (max > 0); + const list &children = toolbar->getChildren(); + list::ConstIterator iter = children.begin(); + while (iter != children.end()) + { + if ((*iter)->getID() == E_GUI_ID_ANIM_CTRL) + { + AnimCtrl *anim = (AnimCtrl*)(*iter); + anim->reset(enabled); + if (enabled) + { + IGUIButton *button = + (IGUIButton*)anim->getElementFromId(E_GUI_ID_PAUSE, true); + button->setPressed(true); + } + } + else if ((*iter)->getType() != EGUIET_STATIC_TEXT) + { + (*iter)->setEnabled(enabled); + } + ++iter; + } +} + +void AnimState::update(ISceneNode *node) +{ + IAnimatedMeshSceneNode *model = (IAnimatedMeshSceneNode*)node; + frame = model->getFrameNr(); + setField(E_GUI_ID_ANIM_FRAME, frame); +} + +IGUIToolBar *AnimState::getToolBar() +{ + IGUIElement *root = env->getRootGUIElement(); + return (IGUIToolBar*)root->getElementFromId(E_GUI_ID_TOOLBAR, true); +} + +u32 AnimState::getField(s32 id) +{ + IGUIToolBar *toolbar = getToolBar(); + if (!toolbar) + return 0; + + IGUISpinBox *spin; + spin = (IGUISpinBox*)toolbar->getElementFromId(id, true); + return spin->getValue(); +} + +void AnimState::setField(s32 id, const u32 &value) +{ + IGUIToolBar *toolbar = getToolBar(); + if (!toolbar) + return; + + IGUISpinBox *spin; + spin = (IGUISpinBox*)toolbar->getElementFromId(id, true); + spin->setRange(0, max); + spin->setValue(std::min(value, max)); +} diff --git a/src/viewer.h b/src/viewer.h new file mode 100644 index 0000000..b41da74 --- /dev/null +++ b/src/viewer.h @@ -0,0 +1,78 @@ +#ifndef D_VIEWER_H +#define D_VIEWER_H + +using namespace irr; +using namespace core; +using namespace scene; +using namespace gui; +using namespace video; + +class Config; +class Scene; +class Trackball; +class GUI; + +enum +{ + E_ANIM_STATE_PLAY_FWD, + E_ANIM_STATE_PLAY_REV, + E_ANIM_STATE_PAUSED +}; + +enum +{ + E_PROJECTION_PERSPECTIVE, + E_PROJECTION_ORTHOGRAPHIC +}; + +class AnimState +{ +public: + AnimState(IGUIEnvironment *env); + void load(ISceneNode *node); + void update(ISceneNode *node); + void setField(s32 id, const u32 &value); + void setState(s32 id) { state = id; } + u32 getField(s32 id); + u32 getFrame() { return frame; } + s32 getState() { return state; } + +private: + IGUIToolBar *getToolBar(); + + IGUIEnvironment *env; + u32 frame; + u32 max; + s32 state; +}; + +class Viewer : public IEventReceiver +{ +public: + Viewer(Config *conf); + ~Viewer(); + bool run(IrrlichtDevice *irr_device); + virtual bool OnEvent(const SEvent &event); + +private: + void resize(); + void setProjection(); + void setBackgroundColor(const u32 &color); + void setCaptionFileName(const io::path &filename); + + Config *conf; + IrrlichtDevice *device; + ICameraSceneNode *camera; + Scene *scene; + Trackball *trackball; + GUI *gui; + AnimState *animation; + matrix4 ortho; + f32 fov; + f32 fov_home; + dimension2du screen; + SColor bg_color; + u32 jump_time; +}; + +#endif // D_VIEWER_H