├── .gitattributes ├── .gitignore ├── .travis.yml ├── CMakeLists.txt ├── Irrlicht.ico ├── README.md ├── cmake ├── FindIrrlicht.cmake └── FindOpenGLES2.cmake ├── docs ├── developers.md ├── fileformat.md ├── make_install.md └── projectfileformat.txt ├── editor.conf.example ├── examples ├── default_dirt.png ├── default_stone.png ├── wool_blue.png ├── wool_green.png └── wool_red.png ├── media ├── coordinates.png ├── flip_x.png ├── flip_y.png ├── flip_z.png ├── fontlucida.png ├── gui_scale.png ├── icon.png ├── icon_mode_node.png ├── icon_mode_nodebox.png ├── icon_mode_texture.png ├── rotate_x.png ├── rotate_y.png ├── rotate_z.png ├── sky.jpg ├── texture_box.png └── texture_terrain.png ├── nodeboxeditor.desktop ├── src ├── Configuration.cpp ├── Configuration.hpp ├── Editor.cpp ├── Editor.hpp ├── EditorState.cpp ├── EditorState.hpp ├── FileFormat │ ├── CPP.cpp │ ├── CPP.hpp │ ├── FileFormat.cpp │ ├── FileFormat.hpp │ ├── Lua.cpp │ ├── Lua.hpp │ ├── NBE.cpp │ ├── NBE.hpp │ ├── helpers.cpp │ ├── helpers.hpp │ ├── obj.cpp │ └── obj.hpp ├── GUIHelpers.cpp ├── GUIHelpers.hpp ├── MenuState.cpp ├── MenuState.hpp ├── common.hpp ├── conf_cmake.hpp.in ├── dialogs │ ├── Dialog.hpp │ ├── FileDialog.cpp │ ├── FileDialog.hpp │ ├── ImageDialog.cpp │ ├── ImageDialog.hpp │ ├── TextureDialog.cpp │ └── TextureDialog.hpp ├── main.cpp ├── minetest.cpp ├── minetest.hpp ├── modes │ ├── NBEditor.cpp │ ├── NBEditor.hpp │ ├── NodeEditor.cpp │ ├── NodeEditor.hpp │ ├── TextureEditor.cpp │ └── TextureEditor.hpp ├── project │ ├── media.cpp │ ├── media.hpp │ ├── node.cpp │ ├── node.hpp │ ├── nodebox.cpp │ ├── nodebox.hpp │ ├── project.cpp │ └── project.hpp └── util │ ├── SimpleFileCombiner.cpp │ ├── SimpleFileCombiner.hpp │ ├── filesys.cpp │ ├── filesys.hpp │ ├── string.cpp │ ├── string.hpp │ ├── tinyfiledialogs.c │ └── tinyfiledialogs.h └── util ├── buildbot ├── buildwin32.sh ├── buildwin64.sh ├── toolchain_mingw.cmake └── toolchain_mingw64.cmake ├── install_nbe.sh └── travis ├── before_install.sh ├── script.sh └── toolchain_mingw.cmake.in /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build 2 | bin 3 | 4 | # Build system 5 | CMakeCache.txt 6 | CMakeFiles 7 | cmake* 8 | Makefile 9 | 10 | # Configuration/generated 11 | editor.conf 12 | debug.txt 13 | conf_cmake.hpp 14 | install_manifest.txt 15 | *.nbe 16 | *.lua 17 | *.obj 18 | 19 | ################# 20 | ## Eclipse 21 | ################# 22 | 23 | # Build directories 24 | build 25 | Build 26 | 27 | # Locally stored "Eclipse launch configurations" 28 | *.launch 29 | 30 | # CDT-specific 31 | .cproject 32 | 33 | # PDT-specific 34 | .buildpath 35 | 36 | 37 | ################# 38 | ## Visual Studio 39 | ################# 40 | 41 | ## Ignore Visual Studio temporary files, build results, and 42 | ## files generated by popular Visual Studio add-ons. 43 | 44 | # User-specific files 45 | *.suo 46 | *.user 47 | *.sln.docstates 48 | 49 | # Build results 50 | [Dd]ebug/ 51 | [Rr]elease/ 52 | *_i.c 53 | *_p.c 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.vspscc 68 | .builds 69 | *.dotCover 70 | 71 | ## TODO: If you have NuGet Package Restore enabled, uncomment this 72 | #packages/ 73 | 74 | # Visual C++ cache files 75 | ipch/ 76 | *.aps 77 | *.ncb 78 | *.opensdf 79 | *.sdf 80 | 81 | # Visual Studio profiler 82 | *.psess 83 | *.vsp 84 | 85 | # ReSharper is a .NET coding add-in 86 | _ReSharper* 87 | 88 | # Installshield output folder 89 | [Ee]xpress 90 | 91 | # DocProject is a documentation generator add-in 92 | DocProject/buildhelp/ 93 | DocProject/Help/*.HxT 94 | DocProject/Help/*.HxC 95 | DocProject/Help/*.hhc 96 | DocProject/Help/*.hhk 97 | DocProject/Help/*.hhp 98 | DocProject/Help/Html2 99 | DocProject/Help/html 100 | 101 | # Click-Once directory 102 | publish 103 | 104 | # Others 105 | [Bb]in 106 | [Oo]bj 107 | sql 108 | TestResults 109 | *.Cache 110 | ClientBin 111 | stylecop.* 112 | ~$* 113 | *.dbmdl 114 | Generated_Code #added for RIA/Silverlight projects 115 | 116 | # Backup & report files from converting an old project file to a newer 117 | # Visual Studio version. Backup files are not needed, because we have git ;-) 118 | _UpgradeReport_Files/ 119 | Backup*/ 120 | UpgradeLog*.XML 121 | 122 | 123 | 124 | ############ 125 | ## Windows 126 | ############ 127 | 128 | # Windows image file caches 129 | Thumbs.db 130 | 131 | # Folder config file 132 | Desktop.ini 133 | 134 | # Installer logs 135 | pip-log.txt 136 | 137 | # Unit test / coverage reports 138 | .coverage 139 | .tox 140 | 141 | #Translations 142 | *.mo 143 | 144 | #Mr Developer 145 | .mr.developer.cfg 146 | 147 | # Mac crap 148 | .DS_Store 149 | 150 | # Linux 151 | *~* 152 | .directory 153 | *kate-swp* 154 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | compiler: 3 | - gcc 4 | - clang 5 | env: 6 | # - PLATFORM=Win32 7 | # - PLATFORM=Win64 8 | - PLATFORM=Linux 9 | before_install: ./util/travis/before_install.sh 10 | script: ./util/travis/script.sh 11 | matrix: 12 | fast_finish: true 13 | exclude: 14 | - env: PLATFORM=Win32 15 | compiler: clang 16 | - env: PLATFORM=Win64 17 | compiler: clang 18 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Set up project 3 | # 4 | cmake_minimum_required(VERSION 2.6) 5 | project(nodeboxeditor) 6 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/") 7 | 8 | # 9 | # Configuration 10 | # 11 | set(NBE_MAJOR_VERSION 0) 12 | set(NBE_MINOR_VERSION 9) 13 | set(NBE_PATCH_VERSION 0) 14 | set(NBE_LABEL_VERSION "Obsidian Glass") 15 | option(DEBUG "Debug mode" 0) 16 | if(DEBUG) 17 | message("-- Is debug") 18 | set(IS_DEBUG true) 19 | endif(DEBUG) 20 | configure_file( 21 | "${PROJECT_SOURCE_DIR}/src/conf_cmake.hpp.in" 22 | "${PROJECT_BINARY_DIR}/conf_cmake.hpp" 23 | ) 24 | 25 | 26 | # 27 | # Source files 28 | # 29 | set(NBE_SRC 30 | src/main.cpp 31 | src/Configuration.cpp 32 | src/GUIHelpers.cpp 33 | src/EditorState.cpp 34 | src/MenuState.cpp 35 | src/Editor.cpp 36 | src/minetest.cpp 37 | 38 | src/project/project.cpp 39 | src/project/media.cpp 40 | src/project/node.cpp 41 | src/project/nodebox.cpp 42 | 43 | src/modes/NBEditor.cpp 44 | src/modes/NodeEditor.cpp 45 | src/modes/TextureEditor.cpp 46 | 47 | src/dialogs/FileDialog.cpp 48 | src/dialogs/TextureDialog.cpp 49 | src/dialogs/ImageDialog.cpp 50 | 51 | src/FileFormat/FileFormat.cpp 52 | src/FileFormat/helpers.cpp 53 | src/FileFormat/NBE.cpp 54 | src/FileFormat/Lua.cpp 55 | src/FileFormat/CPP.cpp 56 | src/FileFormat/obj.cpp 57 | 58 | src/util/string.cpp 59 | src/util/filesys.cpp 60 | src/util/SimpleFileCombiner.cpp 61 | src/util/tinyfiledialogs.c 62 | ) 63 | add_executable(${PROJECT_NAME} ${NBE_SRC}) 64 | 65 | # 66 | # Dependencies 67 | # 68 | find_package(Irrlicht REQUIRED) 69 | 70 | if(WIN32) 71 | # Windows 72 | if(MSVC) # MSVC Specifics 73 | set(PLATFORM_LIBS dbghelp.lib ${PLATFORM_LIBS}) 74 | # Surpress some useless warnings 75 | add_definitions ( /D "_CRT_SECURE_NO_DEPRECATE" /W1 ) 76 | else() # Probably MinGW = GCC 77 | set(PLATFORM_LIBS "") 78 | endif() 79 | set(PLATFORM_LIBS ws2_32.lib shlwapi.lib ${PLATFORM_LIBS}) 80 | 81 | # Zlib stuff 82 | set(ZLIB_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/../../zlib/zlib-1.2.5" 83 | CACHE PATH "Zlib include directory") 84 | set(ZLIB_LIBRARIES "${PROJECT_SOURCE_DIR}/../../zlib125dll/dll32/zlibwapi.lib" 85 | CACHE FILEPATH "Path to zlibwapi.lib") 86 | set(ZLIB_DLL "${PROJECT_SOURCE_DIR}/../../zlib125dll/dll32/zlibwapi.dll" 87 | CACHE FILEPATH "Path to zlibwapi.dll (for installation)") 88 | else(WIN32) 89 | find_package(ZLIB REQUIRED) 90 | endif(WIN32) 91 | 92 | 93 | find_package(X11 REQUIRED) 94 | find_package(OpenGL REQUIRED) 95 | find_package(JPEG REQUIRED) 96 | find_package(BZip2 REQUIRED) 97 | find_package(PNG REQUIRED) 98 | include_directories( 99 | ${PROJECT_BINARY_DIR} 100 | ${IRRLICHT_INCLUDE_DIR} 101 | ${ZLIB_INCLUDE_DIR} 102 | ${CMAKE_BUILD_TYPE} 103 | ${X11_INCLUDE_DIR} 104 | ${OPENGL_INCLUDE_DIR} 105 | ${PNG_INCLUDE_DIR} 106 | ) 107 | set(TLL 108 | ${IRRLICHT_LIBRARY} 109 | ${ZLIB_LIBRARIES} 110 | ${X11_LIBRARIES} 111 | ${OPENGL_LIBRARIES} 112 | ${JPEG_LIBRARIES} 113 | ${BZIP2_LIBRARIES} 114 | ${PNG_LIBRARIES} 115 | ) 116 | if(UNIX) 117 | find_library(XXF86VM_LIBRARY Xxf86vm) 118 | target_link_libraries( 119 | ${PROJECT_NAME} 120 | ${TLL} 121 | ${XXF86VM_LIBRARY} 122 | ) 123 | else() 124 | target_link_libraries( 125 | ${PROJECT_NAME} 126 | ${TLL} 127 | ) 128 | endif(UNIX) 129 | 130 | # 131 | # Executable 132 | # 133 | file(MAKE_DIRECTORY "bin") 134 | set(EXECUTABLE_OUTPUT_PATH "${CMAKE_SOURCE_DIR}/bin") 135 | set(CMAKE_CXX_FLAGS 136 | "${CMAKE_CXX_FLAGS} -pthread") 137 | 138 | # 139 | # Installation 140 | # 141 | install(FILES media/fontlucida.png DESTINATION share/nodeboxeditor/media) 142 | install(FILES media/gui_scale.png DESTINATION share/nodeboxeditor/media) 143 | install(FILES media/coordinates.png DESTINATION share/nodeboxeditor/media) 144 | install(FILES media/icon_mode_node.png DESTINATION share/nodeboxeditor/media) 145 | install(FILES media/icon_mode_nodebox.png DESTINATION share/nodeboxeditor/media) 146 | install(FILES media/icon_mode_texture.png DESTINATION share/nodeboxeditor/media) 147 | install(FILES media/sky.jpg DESTINATION share/nodeboxeditor/media) 148 | install(FILES media/texture_box.png DESTINATION share/nodeboxeditor/media) 149 | install(FILES media/texture_terrain.png DESTINATION share/nodeboxeditor/media) 150 | install(FILES media/icon.png DESTINATION share/nodeboxeditor/media) 151 | install(FILES media/rotate_x.png DESTINATION share/nodeboxeditor/media) 152 | install(FILES media/rotate_y.png DESTINATION share/nodeboxeditor/media) 153 | install(FILES media/rotate_z.png DESTINATION share/nodeboxeditor/media) 154 | install(FILES media/flip_x.png DESTINATION share/nodeboxeditor/media) 155 | install(FILES media/flip_y.png DESTINATION share/nodeboxeditor/media) 156 | install(FILES media/flip_z.png DESTINATION share/nodeboxeditor/media) 157 | install(FILES editor.conf.example DESTINATION share/nodeboxeditor) 158 | if(UNIX) 159 | install (FILES nodeboxeditor.desktop DESTINATION share/applications) 160 | endif() 161 | install(FILES README.md DESTINATION share/nodeboxeditor) 162 | install(TARGETS ${PROJECT_NAME} DESTINATION bin) 163 | if(WIN32) 164 | if(DEFINED IRRLICHT_DLL) 165 | install(FILES ${IRRLICHT_DLL} DESTINATION bin) 166 | endif() 167 | endif() 168 | -------------------------------------------------------------------------------- /Irrlicht.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubenwardy/NodeBoxEditor/a5ef5e4c0d07e44f32f088d9a10907fe8b008c73/Irrlicht.ico -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Node Box Editor 2 | =============== 3 | 4 | [![Build Status](https://travis-ci.org/rubenwardy/NodeBoxEditor.svg?branch=master)](https://travis-ci.org/rubenwardy/NodeBoxEditor) 5 | 6 | Use this editor to make nodeboxes for nodes in Minetest. 7 | 8 | Version: 0.9.0 9 | 10 | To do list and bug tracker is here: https://github.com/rubenwardy/NodeBoxEditor/issues?state=open 11 | 12 | License 13 | ------- 14 | 15 | Created by rubenwardy. 16 | 17 | GPL 3.0 or later. 18 | 19 | https://www.gnu.org/licenses/gpl-3.0.txt 20 | 21 | Contributors: 22 | 23 | * Kaeza: small Linux based fixes, and the start of the CMakeLists.txt (now modified) 24 | * Traxie21: small changed textures for node. 25 | * jmf: Fixed small compile bug fixes. 26 | * ShadowNinja: Unlimited node boxes, code style, refactoring and small fixes. 27 | * Krock/SmallJoker: Fixed warnings, fixed conf_cmake.hpp and Windows builds. 28 | * asl97: Fixed a memory leak. 29 | 30 | If I forgot anyone, please let me know. 31 | 32 | Building 33 | -------- 34 | 35 | For notes on compiling the editor, see docs/developers.md 36 | 37 | Using the editor 38 | ================ 39 | 40 | What actions you can take are limited by which mode / tool you are in. 41 | 42 | Editor 43 | ------ 44 | 45 | * Use WASD to move the perspective view. 46 | * Use the options under view to pick which viewport to show. 47 | * Use the mode icon on the top left of the screen to change modes / tools. 48 | * Click the icon to pop out a list of modes / tools. 49 | * Click an icon to select the corresponding mode. 50 | * Press N to enter the node tool. 51 | * Press B to enter the node box tool. 52 | * Press T to enter the texture tool. 53 | 54 | Menu 55 | ---- 56 | 57 | * File 58 | * Open Project - discard the current project and open a new one. 59 | * Save Project - save the current project. 60 | * Export - export the project to Lua, or other formats supported. 61 | * Exit - exit the editor. 62 | * Editor 63 | * Limiting - toggle whether node boxes can go outside of node boundaries. 64 | * Snapping - toggle whether node boxes will snap to a 16 pixel grid. 65 | * View - use these options to change the viewport (tiled, or make a viewport fullscreen). 66 | * Project - options unique to an tool. 67 | * Help 68 | * About - see program's version, and credits. 69 | 70 | Node Box Tool 71 | ------------- 72 | 73 | In this mode / tool, you can edit the contents of a node. 74 | Icon: A blue cube with a chunk taken out of it. 75 | 76 | * Click + on the sidebar to add a node box. (or press insert) 77 | * Click an item in the listbox to select a node box. (or press up/down) 78 | * Click - on the sidebar to remove the selected node box. (or press delete) 79 | * Size the node box using the handles in the orthographic viewports. 80 | * Scale the node box by pressing control, and using the handles in the orthographic viewports. 81 | * Position the node box by pressing shift, and using the handles in the orthographic viewports. 82 | * Enter properties for a node box in the text boxes on the side bar. 83 | * Click update to apply your changes. 84 | * Click revert to discard your changes, and get the current properties. 85 | 86 | Node Tool 87 | --------- 88 | 89 | In this mode / tool, you can create multiple nodes and manage them. 90 | Icon: A yellow cube. 91 | 92 | * Click + on the sidebar to add a node. (or press insert) 93 | * Click an item in the listbox to select a node. (or press up/down) 94 | * Click - on the sidebar to remove the selected node. (or press delete) 95 | 96 | Texture Tool 97 | ------------ 98 | 99 | In this mode / tool, you can edit the textures of a node. 100 | Icon: Red, green and blue cube. 101 | 102 | * Click a face in the side bar to open up the texture dialog. 103 | * Click import to import an texture. 104 | * Type the location relative to the NBE root if a portable build, or relative to the home directory if installed. 105 | * Click export to export the currently selected texture to the nbe root folder if portable, or the home directory if installed. 106 | * Click an texture in the list box to select it. 107 | * Click apply to apply the selected texture to the side of the node. 108 | 109 | Saving and Exporting 110 | -------------------- 111 | 112 | The editor can currently save and export to several formats. 113 | 114 | * Node Box Editor file (nbe) - The file unique to this editor. General save / open format. 115 | * Lua file (lua) - Exports code which could be installed as a mod. Use when you want to run in Minetest. 116 | * Minetest Classic (cpp) - Exports code to be used in Minetest Classic. Use when you want to run in Minetest Classic. 117 | -------------------------------------------------------------------------------- /cmake/FindIrrlicht.cmake: -------------------------------------------------------------------------------- 1 | #FindIrrlicht.cmake 2 | 3 | set(IRRLICHT_SOURCE_DIR "" CACHE PATH "Path to irrlicht source directory (optional)") 4 | 5 | if( UNIX ) 6 | # Unix 7 | else( UNIX ) 8 | # Windows 9 | endif( UNIX ) 10 | 11 | # Find include directory 12 | 13 | if(NOT IRRLICHT_SOURCE_DIR STREQUAL "") 14 | set(IRRLICHT_SOURCE_DIR_INCLUDE 15 | "${IRRLICHT_SOURCE_DIR}/include" 16 | ) 17 | 18 | set(IRRLICHT_LIBRARY_NAMES libIrrlicht.a Irrlicht Irrlicht.lib) 19 | 20 | if(WIN32) 21 | if(MSVC) 22 | set(IRRLICHT_SOURCE_DIR_LIBS "${IRRLICHT_SOURCE_DIR}/lib/Win32-visualstudio") 23 | set(IRRLICHT_LIBRARY_NAMES Irrlicht.lib) 24 | else() 25 | set(IRRLICHT_SOURCE_DIR_LIBS "${IRRLICHT_SOURCE_DIR}/lib/Win32-gcc") 26 | set(IRRLICHT_LIBRARY_NAMES libIrrlicht.a libIrrlicht.dll.a) 27 | endif() 28 | else() 29 | set(IRRLICHT_SOURCE_DIR_LIBS "${IRRLICHT_SOURCE_DIR}/lib/Linux") 30 | set(IRRLICHT_LIBRARY_NAMES libIrrlicht.a) 31 | endif() 32 | 33 | FIND_PATH(IRRLICHT_INCLUDE_DIR NAMES irrlicht.h 34 | PATHS 35 | ${IRRLICHT_SOURCE_DIR_INCLUDE} 36 | NO_DEFAULT_PATH 37 | ) 38 | 39 | FIND_LIBRARY(IRRLICHT_LIBRARY NAMES ${IRRLICHT_LIBRARY_NAMES} 40 | PATHS 41 | ${IRRLICHT_SOURCE_DIR_LIBS} 42 | NO_DEFAULT_PATH 43 | ) 44 | 45 | else() 46 | 47 | FIND_PATH(IRRLICHT_INCLUDE_DIR NAMES irrlicht.h 48 | PATHS 49 | /usr/local/include/irrlicht 50 | /usr/include/irrlicht 51 | ) 52 | 53 | FIND_LIBRARY(IRRLICHT_LIBRARY NAMES libIrrlicht.a Irrlicht 54 | PATHS 55 | /usr/local/lib 56 | /usr/lib 57 | ) 58 | endif() 59 | 60 | MESSAGE(STATUS "IRRLICHT_SOURCE_DIR = ${IRRLICHT_SOURCE_DIR}") 61 | MESSAGE(STATUS "IRRLICHT_INCLUDE_DIR = ${IRRLICHT_INCLUDE_DIR}") 62 | MESSAGE(STATUS "IRRLICHT_LIBRARY = ${IRRLICHT_LIBRARY}") 63 | 64 | # On windows, find the dll for installation 65 | if(WIN32) 66 | if(MSVC) 67 | FIND_FILE(IRRLICHT_DLL NAMES Irrlicht.dll 68 | PATHS 69 | "${IRRLICHT_SOURCE_DIR}/bin/Win32-VisualStudio" 70 | DOC "Path of the Irrlicht dll (for installation)" 71 | ) 72 | else() 73 | FIND_FILE(IRRLICHT_DLL NAMES Irrlicht.dll 74 | PATHS 75 | "${IRRLICHT_SOURCE_DIR}/bin/Win32-gcc" 76 | DOC "Path of the Irrlicht dll (for installation)" 77 | ) 78 | endif() 79 | MESSAGE(STATUS "IRRLICHT_DLL = ${IRRLICHT_DLL}") 80 | endif(WIN32) 81 | 82 | # handle the QUIETLY and REQUIRED arguments and set IRRLICHT_FOUND to TRUE if 83 | # all listed variables are TRUE 84 | INCLUDE(FindPackageHandleStandardArgs) 85 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(IRRLICHT DEFAULT_MSG IRRLICHT_LIBRARY IRRLICHT_INCLUDE_DIR) 86 | 87 | IF(IRRLICHT_FOUND) 88 | SET(IRRLICHT_LIBRARIES ${IRRLICHT_LIBRARY}) 89 | ELSE(IRRLICHT_FOUND) 90 | SET(IRRLICHT_LIBRARIES) 91 | ENDIF(IRRLICHT_FOUND) 92 | 93 | MARK_AS_ADVANCED(IRRLICHT_LIBRARY IRRLICHT_INCLUDE_DIR IRRLICHT_DLL) 94 | 95 | -------------------------------------------------------------------------------- /cmake/FindOpenGLES2.cmake: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------- 2 | # This file is stolen from part of the CMake build system for OGRE (Object-oriented Graphics Rendering Engine) http://www.ogre3d.org/ 3 | # 4 | # The contents of this file are placed in the public domain. Feel 5 | # free to make use of it in any way you like. 6 | #------------------------------------------------------------------- 7 | 8 | # - Try to find OpenGLES and EGL 9 | # Once done this will define 10 | # 11 | # OPENGLES2_FOUND - system has OpenGLES 12 | # OPENGLES2_INCLUDE_DIR - the GL include directory 13 | # OPENGLES2_LIBRARIES - Link these to use OpenGLES 14 | # 15 | # EGL_FOUND - system has EGL 16 | # EGL_INCLUDE_DIR - the EGL include directory 17 | # EGL_LIBRARIES - Link these to use EGL 18 | 19 | # win32, apple, android NOT TESED 20 | # linux tested and works 21 | 22 | IF (WIN32) 23 | IF (CYGWIN) 24 | 25 | FIND_PATH(OPENGLES2_INCLUDE_DIR GLES2/gl2.h ) 26 | 27 | FIND_LIBRARY(OPENGLES2_gl_LIBRARY libGLESv2 ) 28 | 29 | ELSE (CYGWIN) 30 | 31 | IF(BORLAND) 32 | SET (OPENGLES2_gl_LIBRARY import32 CACHE STRING "OpenGL ES 2.x library for win32") 33 | ELSE(BORLAND) 34 | # todo 35 | # SET (OPENGLES_gl_LIBRARY ${SOURCE_DIR}/Dependencies/lib/release/libGLESv2.lib CACHE STRING "OpenGL ES 2.x library for win32" 36 | ENDIF(BORLAND) 37 | 38 | ENDIF (CYGWIN) 39 | 40 | ELSE (WIN32) 41 | 42 | IF (APPLE) 43 | 44 | create_search_paths(/Developer/Platforms) 45 | findpkg_framework(OpenGLES2) 46 | set(OPENGLES2_gl_LIBRARY "-framework OpenGLES") 47 | 48 | ELSE(APPLE) 49 | 50 | FIND_PATH(OPENGLES2_INCLUDE_DIR GLES2/gl2.h 51 | /usr/openwin/share/include 52 | /opt/graphics/OpenGL/include /usr/X11R6/include 53 | /usr/include 54 | ) 55 | 56 | FIND_LIBRARY(OPENGLES2_gl_LIBRARY 57 | NAMES GLESv2 58 | PATHS /opt/graphics/OpenGL/lib 59 | /usr/openwin/lib 60 | /usr/shlib /usr/X11R6/lib 61 | /usr/lib 62 | ) 63 | 64 | IF (NOT BUILD_ANDROID) 65 | FIND_PATH(EGL_INCLUDE_DIR EGL/egl.h 66 | /usr/openwin/share/include 67 | /opt/graphics/OpenGL/include /usr/X11R6/include 68 | /usr/include 69 | ) 70 | 71 | FIND_LIBRARY(EGL_egl_LIBRARY 72 | NAMES EGL 73 | PATHS /opt/graphics/OpenGL/lib 74 | /usr/openwin/lib 75 | /usr/shlib /usr/X11R6/lib 76 | /usr/lib 77 | ) 78 | 79 | # On Unix OpenGL most certainly always requires X11. 80 | # Feel free to tighten up these conditions if you don't 81 | # think this is always true. 82 | # It's not true on OSX. 83 | 84 | IF (OPENGLES2_gl_LIBRARY) 85 | IF(NOT X11_FOUND) 86 | INCLUDE(FindX11) 87 | ENDIF(NOT X11_FOUND) 88 | IF (X11_FOUND) 89 | IF (NOT APPLE) 90 | SET (OPENGLES2_LIBRARIES ${X11_LIBRARIES}) 91 | ENDIF (NOT APPLE) 92 | ENDIF (X11_FOUND) 93 | ENDIF (OPENGLES2_gl_LIBRARY) 94 | ENDIF () 95 | 96 | ENDIF(APPLE) 97 | ENDIF (WIN32) 98 | 99 | #SET( OPENGLES2_LIBRARIES ${OPENGLES2_gl_LIBRARY} ${OPENGLES2_LIBRARIES}) 100 | 101 | IF (BUILD_ANDROID) 102 | IF(OPENGLES2_gl_LIBRARY) 103 | SET( OPENGLES2_LIBRARIES ${OPENGLES2_gl_LIBRARY} ${OPENGLES2_LIBRARIES}) 104 | SET( EGL_LIBRARIES) 105 | SET( OPENGLES2_FOUND "YES" ) 106 | ENDIF(OPENGLES2_gl_LIBRARY) 107 | ELSE () 108 | 109 | SET( OPENGLES2_LIBRARIES ${OPENGLES2_gl_LIBRARY} ${OPENGLES2_LIBRARIES}) 110 | 111 | IF(OPENGLES2_gl_LIBRARY AND EGL_egl_LIBRARY) 112 | SET( OPENGLES2_LIBRARIES ${OPENGLES2_gl_LIBRARY} ${OPENGLES2_LIBRARIES}) 113 | SET( EGL_LIBRARIES ${EGL_egl_LIBRARY} ${EGL_LIBRARIES}) 114 | SET( OPENGLES2_FOUND "YES" ) 115 | ENDIF(OPENGLES2_gl_LIBRARY AND EGL_egl_LIBRARY) 116 | 117 | ENDIF () 118 | 119 | MARK_AS_ADVANCED( 120 | OPENGLES2_INCLUDE_DIR 121 | OPENGLES2_gl_LIBRARY 122 | EGL_INCLUDE_DIR 123 | EGL_egl_LIBRARY 124 | ) 125 | 126 | IF(OPENGLES2_FOUND) 127 | MESSAGE(STATUS "Found system opengles2 library ${OPENGLES2_LIBRARIES}") 128 | ELSE () 129 | SET(OPENGLES2_LIBRARIES "") 130 | ENDIF () 131 | -------------------------------------------------------------------------------- /docs/developers.md: -------------------------------------------------------------------------------- 1 | Introduction 2 | ============ 3 | 4 | This guide contains technical information on installing, building, and maintaining the Node Box Editor 5 | 6 | Code Style 7 | ---------- 8 | 9 | The project tries to follow [Minetest's code style](http://dev.minetest.net/Code_style_guidelines) 10 | 11 | GitHub things 12 | ------------- 13 | 14 | Patches should be in their own branch when making a pull request. 15 | 16 | Do not pull request from upstream to your repo using GitHub's pull request and merge feature. 17 | 18 | Releasing a new version 19 | ----------------------- 20 | 21 | * Bump settings in CMAKE. 22 | * Update README.md 23 | * Create windows build. 24 | * Write changelog. 25 | * Commit and push code to upstream. 26 | * Post message on Minetest forums. 27 | * Tag and release version on GitHub. 28 | 29 | 30 | Building and Installation 31 | ========================= 32 | 33 | Linux 34 | ----- 35 | 36 | **Install Dependencies** 37 | 38 | # download source and go to the root folder 39 | $ sudo apt-get install build-essential libirrlicht-dev cmake libpng-dev libbz2-dev libjpeg8-dev libgl1-mesa-dev libxxf86vm-dev 40 | 41 | **Compiling** 42 | 43 | # Compile the editor 44 | $ cmake . 45 | $ make -j2 46 | 47 | **Running** 48 | 49 | $ ./bin/nodeboxeditor 50 | # You could also double click the executable file in bin 51 | 52 | **Installing** 53 | 54 | $ sudo make install 55 | $ nodeboxeditor 56 | # See make_install.md 57 | 58 | Microsoft Visual Studio 59 | ----------------------- 60 | 61 | Express edition makes no difference to the process. 62 | 63 | **Prerequisites** 64 | 65 | * Download Irrlicht. Some features in NBE require Irrlicht 1.8 or later in order to be enabled. 66 | * Download the source code for NBE 67 | * You will need to download Visual Studio, of course. 68 | 69 | **Step One: Set up a project** 70 | 71 | * Copy the conf_cmake.hpp.in to conf_cmake.hpp where you downloaded the source 72 | * Open up conf_cmake.hpp, and edit the defines where there is @NBE_A_SETTING@ 73 | * @NBE_DESCR_VERSION@: "0.7 - Iron" for example 74 | * Create a C++ project in Visual Studio 75 | * Add the source code using right click > Add > Existing file. Make sure to include src/FileFormat and src/util as well. 76 | 77 | **Step Two: Adding Irrlicht** 78 | 79 | See [Irrlicht's tutorial](http://irrlicht.sourceforge.net/docu/example001.html) on setting up Visual Studio if you have problems. 80 | * Right click on the name of the project on the Solution Explorer 81 | * Click properties 82 | * Go to the C++ Include Directories tab 83 | * Add the irrlicht include folder to include directories 84 | * Add the irrlicht lib/win32-visualstudio folder to lib directories 85 | * Copy irrlicht/bin/win32-visualstudio/irrlicht.dll to project/debug/irrlicht.dll 86 | 87 | The project should now build correctly. You will need to copy the media folder across to project/debug 88 | 89 | 90 | 91 | Code 92 | ==== 93 | 94 | This is outdated info, needs updating. 95 | 96 | * **common**.hpp - general compile settings, includes and defines. Used in every other file. 97 | * **conf_cmake**.hpp.in - defines from cmake. 98 | * **main**.cpp - contains the main() function, starts irrlicht and the editor. 99 | * **Editor**.cpp/hpp - contains the Editor class which contains the update loop. 100 | * **EditorState**.cpp/hpp - contains the EditorState class, which is used to share common variables. Also contains main class for mode FSM. 101 | * **MenuState**.cpp/hpp - contains the MenuState class which handles the user interface such as the menu bar and mode icons. 102 | * **NBEEditor**.cpp/hpp - contains the node box editor mode in the mode FSM. 103 | * **NodeEditor**.cpp/hpp - contains the node editor mode in the mode FSM. 104 | * **Project**.cpp/hpp - holds the project in a contained way. 105 | * **Node**.cpp/hpp - hold the data and node boxes for a single node. 106 | * **NodeBox**.cpp/hpp - a single node box. 107 | * **Configuration**.cpp/hpp - the settings manager for the editor. Reads from / writes to editor.conf 108 | * **GUIHelpers**.cpp/hpp - contains helpers such as creating x/y/z text boxes. 109 | * FileFormat 110 | * **FileFormat**.cpp/hpp - factory and base class for file formats. 111 | * **Lua**.cpp/hpp - the Lua file parser. 112 | * **NBE**.cpp/hpp - the NBE file parser. 113 | * **MTC**.cpp/hpp - the Minetest Classic (.cpp) file parser. 114 | * util 115 | * **string**.cpp/hpp - helper functions for strings 116 | 117 | -------------------------------------------------------------------------------- /docs/fileformat.md: -------------------------------------------------------------------------------- 1 | NBE File Format 2 | =============== 3 | 4 | The first five bytes tell the parser that it is an NBE file. 5 | "NBEFP" 6 | 7 | The next byte is a number between 1 and 255 which indicates how many files there are. 8 | 9 | Next up is the file table. It is in this format: 10 | 11 | | Length | Description | 12 | |----------|-----------------------------------------| 13 | | 50 bytes | Name of the file | 14 | | 4 bytes | Start position of file data, x bytes in | 15 | | 4 bytes | Size of the file data, x bytes | 16 | 17 | Then comes the file data, one after another. 18 | -------------------------------------------------------------------------------- /docs/make_install.md: -------------------------------------------------------------------------------- 1 | Make Install 2 | ============ 3 | 4 | This document will outline the process of installing the editor onto your system, 5 | and will also give important information in order to avoid common slip ups. 6 | 7 | This is only for Linux operating systems. 8 | 9 | Installing 10 | ---------- 11 | 12 | After you compile the editor (see readme, or developers.md), it is as simple as this: 13 | 14 | $ sudo make install 15 | 16 | If you receive any errors, please report them. 17 | 18 | Using Installed Builds 19 | ---------------------- 20 | 21 | To run, use $ nodeboxeditor 22 | 23 | * Project, imports and exports will be saved to and read from /home/. 24 | You can change this with the save_directory setting in editor.conf 25 | * Configuration will be read from /home/.config/nodeboxeditor.conf first, 26 | and then /usr/local/share/nodeboxeditor/editor.conf if that fails. 27 | * Resources are saved in /usr/local/share/nodeboxeditor 28 | * The executable is in /usr/local/bin 29 | * Textures will be imported and exported to /home/. 30 | 31 | -------------------------------------------------------------------------------- /docs/projectfileformat.txt: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------- # 2 | # FILE FORMAT FOR NBE PROJECT FILES USING PARSER VERSION 1 # 3 | # This is the file format for project.txt in .nbe files # 4 | # Comments ARE NOT allowed in real format # 5 | # Axis Y is height # 6 | # --------------------------------------------------------- # 7 | 8 | MINETEST NODEBOX EDITOR # Tells the parser that this is a .nbe file 9 | PARSER 2 # What version the parser uses. Versions may not be backwards compatible. 10 | NAME my_mod_namespace # The folder the mod should be in 11 | 12 | 13 | # Blank lines are okay 14 | 15 | 16 | # -------------------------------------------------- # 17 | # this is a node block which adds an individual node # 18 | # -------------------------------------------------- # 19 | NODE tree_block # Declares a node, and gives it its name. 20 | POSITION 0 0 0 # position in editor grid 21 | # 22 | NODEBOX name -0.5 -0.5 -0.5 0.5 0.5 0.5 # A single box called name. 23 | NODEBOX name -0.5 -0.5 -0.5 0.5 0.5 0.5 # a single box called name 24 | TEXTURE top mod_top_texture.png # uses textures/mod_top_texture.png for top face 25 | END NODE 26 | 27 | 28 | # Blank line 29 | 30 | 31 | # -------------------------------------------------- # 32 | # this is a node block which adds an individual node # 33 | # -------------------------------------------------- # 34 | NODE tree_block_2 # Declares a node, and gives it its name. 35 | POSITION 1 0 0 # position in editor grid. 36 | # 37 | NODEBOX name -0.5 -0.5 -0.5 0.5 0.5 0.5 # A single box called name. 38 | NODEBOX name -0.5 -0.5 -0.5 0.5 0.5 0.5 # a single box called name 39 | END NODE 40 | -------------------------------------------------------------------------------- /editor.conf.example: -------------------------------------------------------------------------------- 1 | # In node box editor mode, snapping makes node boxes 2 | # snap to pixels 3 | snapping = true 4 | 5 | # The resolution the nodebox snaps to (can be overriden per node) 6 | default_snap_res = 16 7 | 8 | # limiting stops node boxes going out of node bounds 9 | limiting = true 10 | 11 | # If true, positions in nodebox properties are shown as multiples of 1/16 12 | fractional_positions = false 13 | 14 | # Directory to save projects and export to. 15 | # Should be absolute path. 16 | save_directory = 17 | 18 | # The editor should attempt to find Minetest automatically 19 | # If it fails, enter an absolute path to the minetest root folder, eg: 20 | # c://Games/Minetest/ 21 | # /home/rubenwardy/Game/Minetest/ 22 | # Do not include bin/minetest or bin/minetest.exe in the root. 23 | minetest_root = 24 | 25 | # The view of each viewport 26 | # can be: pers, front, back, top, bottom, left, right 27 | viewport_top_left = pers 28 | viewport_top_right = top 29 | viewport_bottom_left = front 30 | viewport_bottom_right = right 31 | 32 | # Lighting 33 | # 0: no lighting / shadows 34 | # 1: Depth lighting (things further away from camera are darker) (TODO) 35 | # 2: Normal lighting (like in Minetest) 36 | lighting = 2 37 | 38 | # If true, nodes that are not selected will be hidden 39 | # when not in the node tool 40 | hide_other_nodes = true 41 | 42 | # Move all nodes up when a node is placed at negative y 43 | no_negative_node_y = true 44 | 45 | # Driver used to render 46 | driver = opengl 47 | 48 | # Hide the sidebar 49 | hide_sidebar = false 50 | 51 | # Whether the position draw handle should be shown 52 | always_show_position_handle = false 53 | 54 | # Vertical syncronisation 55 | dont_use_vsync = false 56 | 57 | # Enable this if vsync is not working for you 58 | use_sleep = false 59 | 60 | # Screen settings 61 | fullscreen = false 62 | width = 896 63 | height = 520 64 | -------------------------------------------------------------------------------- /examples/default_dirt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubenwardy/NodeBoxEditor/a5ef5e4c0d07e44f32f088d9a10907fe8b008c73/examples/default_dirt.png -------------------------------------------------------------------------------- /examples/default_stone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubenwardy/NodeBoxEditor/a5ef5e4c0d07e44f32f088d9a10907fe8b008c73/examples/default_stone.png -------------------------------------------------------------------------------- /examples/wool_blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubenwardy/NodeBoxEditor/a5ef5e4c0d07e44f32f088d9a10907fe8b008c73/examples/wool_blue.png -------------------------------------------------------------------------------- /examples/wool_green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubenwardy/NodeBoxEditor/a5ef5e4c0d07e44f32f088d9a10907fe8b008c73/examples/wool_green.png -------------------------------------------------------------------------------- /examples/wool_red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubenwardy/NodeBoxEditor/a5ef5e4c0d07e44f32f088d9a10907fe8b008c73/examples/wool_red.png -------------------------------------------------------------------------------- /media/coordinates.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubenwardy/NodeBoxEditor/a5ef5e4c0d07e44f32f088d9a10907fe8b008c73/media/coordinates.png -------------------------------------------------------------------------------- /media/flip_x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubenwardy/NodeBoxEditor/a5ef5e4c0d07e44f32f088d9a10907fe8b008c73/media/flip_x.png -------------------------------------------------------------------------------- /media/flip_y.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubenwardy/NodeBoxEditor/a5ef5e4c0d07e44f32f088d9a10907fe8b008c73/media/flip_y.png -------------------------------------------------------------------------------- /media/flip_z.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubenwardy/NodeBoxEditor/a5ef5e4c0d07e44f32f088d9a10907fe8b008c73/media/flip_z.png -------------------------------------------------------------------------------- /media/fontlucida.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubenwardy/NodeBoxEditor/a5ef5e4c0d07e44f32f088d9a10907fe8b008c73/media/fontlucida.png -------------------------------------------------------------------------------- /media/gui_scale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubenwardy/NodeBoxEditor/a5ef5e4c0d07e44f32f088d9a10907fe8b008c73/media/gui_scale.png -------------------------------------------------------------------------------- /media/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubenwardy/NodeBoxEditor/a5ef5e4c0d07e44f32f088d9a10907fe8b008c73/media/icon.png -------------------------------------------------------------------------------- /media/icon_mode_node.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubenwardy/NodeBoxEditor/a5ef5e4c0d07e44f32f088d9a10907fe8b008c73/media/icon_mode_node.png -------------------------------------------------------------------------------- /media/icon_mode_nodebox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubenwardy/NodeBoxEditor/a5ef5e4c0d07e44f32f088d9a10907fe8b008c73/media/icon_mode_nodebox.png -------------------------------------------------------------------------------- /media/icon_mode_texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubenwardy/NodeBoxEditor/a5ef5e4c0d07e44f32f088d9a10907fe8b008c73/media/icon_mode_texture.png -------------------------------------------------------------------------------- /media/rotate_x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubenwardy/NodeBoxEditor/a5ef5e4c0d07e44f32f088d9a10907fe8b008c73/media/rotate_x.png -------------------------------------------------------------------------------- /media/rotate_y.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubenwardy/NodeBoxEditor/a5ef5e4c0d07e44f32f088d9a10907fe8b008c73/media/rotate_y.png -------------------------------------------------------------------------------- /media/rotate_z.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubenwardy/NodeBoxEditor/a5ef5e4c0d07e44f32f088d9a10907fe8b008c73/media/rotate_z.png -------------------------------------------------------------------------------- /media/sky.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubenwardy/NodeBoxEditor/a5ef5e4c0d07e44f32f088d9a10907fe8b008c73/media/sky.jpg -------------------------------------------------------------------------------- /media/texture_box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubenwardy/NodeBoxEditor/a5ef5e4c0d07e44f32f088d9a10907fe8b008c73/media/texture_box.png -------------------------------------------------------------------------------- /media/texture_terrain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubenwardy/NodeBoxEditor/a5ef5e4c0d07e44f32f088d9a10907fe8b008c73/media/texture_terrain.png -------------------------------------------------------------------------------- /nodeboxeditor.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Node Box Editor 3 | GenericName=Minetest Node Box Editor 4 | Comment=Edit and create node boxes for Minetest 5 | GenericName[fr]=Éditeur de node boxes pour Minetest 6 | Comment[fr]=Créer et éditer des node boxes pour Minetest 7 | Exec=nodeboxeditor 8 | Icon=/usr/local/share/nodeboxeditor/media/icon.png 9 | Terminal=false 10 | Type=Application 11 | Categories=Development; 12 | StartupNotify=false 13 | -------------------------------------------------------------------------------- /src/Configuration.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "Configuration.hpp" 5 | #include "util/string.hpp" 6 | 7 | 8 | bool Configuration::load(const std::string & filename){ 9 | std::string line; 10 | std::ifstream file(filename.c_str()); 11 | if (!file) { 12 | return false; 13 | } 14 | while (std::getline(file, line)) { 15 | readLine(line); 16 | } 17 | file.close(); 18 | return true; 19 | } 20 | 21 | 22 | void Configuration::readLine(std::string & line){ 23 | // Skip comments 24 | if (line[0] == '#') 25 | return; 26 | line = trim(line); 27 | 28 | if (line.empty()) { 29 | return; 30 | } 31 | 32 | // Split string into hash and value per 33 | size_t eqPos = line.find('='); 34 | if (eqPos == std::string::npos) { 35 | return; 36 | } 37 | const std::string key = trim(line.substr(0, eqPos)); 38 | const std::string value = trim(line.substr(eqPos + 1)); 39 | 40 | // Create setting 41 | settings[key] = value; 42 | } 43 | 44 | 45 | bool Configuration::save(const std::string & filename){ 46 | std::ofstream file(filename.c_str()); 47 | if (!file) { 48 | return false; 49 | } 50 | for (std::map::const_iterator it = settings.begin(); 51 | it != settings.end(); 52 | ++it) { 53 | file << it->first << " = " << it->second << "\n"; 54 | } 55 | file.close(); 56 | return true; 57 | } 58 | 59 | 60 | const std::string & Configuration::get(const std::string & key) const 61 | { 62 | return settings.find(key)->second; 63 | } 64 | 65 | 66 | void Configuration::set(const std::string & key, const std::string & value) 67 | { 68 | settings[key] = value; 69 | } 70 | 71 | 72 | static inline bool to_bool(const std::string & str) 73 | { 74 | std::string s = str_to_lower(str); 75 | return s == "true" || s == "yes" || s == "1" || s == "on"; 76 | } 77 | 78 | 79 | bool Configuration::getBool(const std::string & key) const 80 | { 81 | return to_bool(settings.find(key)->second); 82 | } 83 | 84 | 85 | int Configuration::getInt(const std::string & key) const 86 | { 87 | return atoi(settings.find(key)->second.c_str()); 88 | } 89 | -------------------------------------------------------------------------------- /src/Configuration.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CONFIGURATION_HPP_INCLUDED 2 | #define CONFIGURATION_HPP_INCLUDED 3 | 4 | #include 5 | 6 | class Configuration 7 | { 8 | public: 9 | Configuration() {} 10 | bool load(const std::string & filename); 11 | bool save(const std::string & filename); 12 | 13 | // Getters 14 | const std::string & get(const std::string & key) const; 15 | bool getBool(const std::string & key) const; 16 | int getInt(const std::string & key) const; 17 | 18 | // Setters 19 | void set(const std::string & key, const std::string & value); 20 | protected: 21 | void readLine(std::string & line); 22 | 23 | std::map settings; 24 | }; 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /src/Editor.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDITOR_HPP_INCLUDED 2 | #define EDITOR_HPP_INCLUDED 3 | 4 | #include "common.hpp" 5 | #include "EditorState.hpp" 6 | #include "project/project.hpp" 7 | 8 | class Editor : public IEventReceiver 9 | { 10 | public: 11 | Editor(); 12 | bool run(IrrlichtDevice *irr_device, Configuration *conf, bool editor_is_installed); 13 | virtual bool OnEvent(const SEvent &event); 14 | private: 15 | void recreateCameras(); 16 | void applyCameraOffsets(EViewport i); 17 | void LoadScene(); 18 | void viewportTick(EViewport vp, rect rect, bool mousehit, bool middlehit); 19 | EViewport getViewportAt(vector2di pos); 20 | 21 | 22 | EditorState *state; 23 | irr::IrrlichtDevice *device; 24 | 25 | // Helper functions 26 | int currentWindow; 27 | ISceneNode *target; 28 | ISceneNode *pivot; 29 | ICameraSceneNode *camera[4]; 30 | vector3df viewport_offset[4]; 31 | EViewport viewport_drag; 32 | vector2di viewport_drag_last; 33 | IMeshSceneNode *plane; 34 | EViewport viewport_contextmenu; 35 | bool click_handled; 36 | bool middle_click_handled; 37 | }; 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /src/EditorState.cpp: -------------------------------------------------------------------------------- 1 | #include "EditorState.hpp" 2 | 3 | EditorState::EditorState(irr::IrrlichtDevice* dev, Project* proj, Configuration* settings) : 4 | device(dev), 5 | project(proj), 6 | currentmode(0), 7 | plane_tri(NULL), 8 | mousedown(false), 9 | settings(settings), 10 | close_requested(false), 11 | modeCount(0), 12 | menu(NULL) 13 | { 14 | for (int i = 0; i < 5; i++) { 15 | modes[i] = NULL; 16 | } 17 | 18 | for (int i = 0; i < NUMBER_OF_KEYS; i++) { 19 | keys[i] = EKS_UP; 20 | } 21 | } 22 | 23 | void EditorState::AddMode(EditorMode* value) 24 | { 25 | modes[modeCount] = value; 26 | value->id = modeCount; 27 | modeCount++; 28 | } 29 | 30 | void EditorState::SelectMode(int id) 31 | { 32 | if (menu->dialog) 33 | if (!menu->dialog->canClose() || !menu->dialog->close()) 34 | return; 35 | 36 | if (!modes[id]) { 37 | return; 38 | } 39 | 40 | if (Mode()) { 41 | Mode()->unload(); 42 | } 43 | 44 | currentmode = id; 45 | 46 | menu->init(); 47 | 48 | if (Mode()) { 49 | Mode()->load(); 50 | } 51 | } 52 | 53 | EViewportType stringToType(std::string input, EViewportType def) 54 | { 55 | if (input == "pers") 56 | return VIEWT_PERS; 57 | else if (input == "front") 58 | return VIEWT_FRONT; 59 | else if (input == "back") 60 | return VIEWT_BACK; 61 | else if (input == "left") 62 | return VIEWT_LEFT; 63 | else if (input == "right") 64 | return VIEWT_RIGHT; 65 | else if (input == "top") 66 | return VIEWT_TOP; 67 | else if (input == "bottom") 68 | return VIEWT_BOTTOM; 69 | else 70 | return def; 71 | } 72 | 73 | EViewportType EditorState::getEViewportType(EViewport id) 74 | { 75 | switch (id) { 76 | case VIEW_TL: 77 | return stringToType(settings->get("viewport_top_left"), VIEWT_PERS); 78 | case VIEW_TR: 79 | return stringToType(settings->get("viewport_top_right"), VIEWT_TOP); 80 | case VIEW_BL: 81 | return stringToType(settings->get("viewport_bottom_left"), VIEWT_FRONT); 82 | case VIEW_BR: 83 | return stringToType(settings->get("viewport_bottom_right"), VIEWT_PERS); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/EditorState.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDITORSTATE_HPP_INCLUDED 2 | #define EDITORSTATE_HPP_INCLUDED 3 | #include "common.hpp" 4 | #include "Configuration.hpp" 5 | #include "project/project.hpp" 6 | #include "MenuState.hpp" 7 | 8 | #define NUMBER_OF_KEYS 252 9 | enum EKeyState 10 | { 11 | EKS_UP = false, 12 | EKS_DOWN = true 13 | }; 14 | 15 | class Project; 16 | class EditorMode; 17 | class MenuState; 18 | class EditorState 19 | { 20 | public: 21 | EditorState(irr::IrrlichtDevice* dev, Project* proj, Configuration* settings); 22 | 23 | // Irrlicht 24 | ITriangleSelector* plane_tri; 25 | IrrlichtDevice* device; 26 | 27 | // Project 28 | Project* project; 29 | 30 | // Editor 31 | EditorMode* Mode(int id) const 32 | { 33 | if (id < 0 || id >= 5) { 34 | return NULL; 35 | } 36 | return modes[id]; 37 | } 38 | EditorMode* Mode() const 39 | { 40 | if (!Mode(currentmode)) 41 | std::cerr << "Warning! Null mode returned..." << std::endl; 42 | return Mode(currentmode); 43 | } 44 | 45 | void SelectMode(int id); 46 | 47 | void AddMode(EditorMode *value); 48 | void CloseEditor() { close_requested = true; } 49 | bool NeedsClose() const { return close_requested; } 50 | 51 | // Input 52 | bool mousedown; 53 | vector2di mouse_position; 54 | EKeyState keys[NUMBER_OF_KEYS]; 55 | 56 | Configuration *settings; 57 | MenuState *menu; 58 | 59 | EViewportType getEViewportType(EViewport id); 60 | 61 | bool isInstalled; 62 | private: 63 | int currentmode; 64 | EditorMode *modes[5]; 65 | int modeCount; 66 | bool close_requested; 67 | }; 68 | 69 | class EditorMode : public irr::IEventReceiver 70 | { 71 | public: 72 | EditorMode(EditorState* st) : state(st) {} 73 | virtual void load() = 0; 74 | virtual void unload() = 0; 75 | virtual void update(double dtime) = 0; 76 | virtual void draw(irr::video::IVideoDriver* driver) {} 77 | virtual void drawViewport(irr::video::IVideoDriver* driver, EViewport viewport, rect area) {} 78 | virtual void viewportTick(EViewport window, irr::video::IVideoDriver* driver, rect offset) = 0; 79 | virtual bool OnEvent(const irr::SEvent &event) = 0; 80 | virtual irr::video::ITexture* icon() = 0; 81 | 82 | int id; 83 | EditorState* state; 84 | }; 85 | 86 | #endif 87 | -------------------------------------------------------------------------------- /src/FileFormat/CPP.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "../common.hpp" 7 | #include "../project/project.hpp" 8 | #include "../project/node.hpp" 9 | #include "CPP.hpp" 10 | 11 | bool CPPFileFormat::write(Project * project, const std::string & filename){ 12 | std::ofstream file(filename.c_str()); 13 | if (!file) { 14 | error_code = EFFE_IO_ERROR; 15 | return false; 16 | } 17 | file << "// GENERATED CODE\n"; 18 | file << "// Node Box Editor, version " << EDITOR_TEXT_VERSION << '\n'; 19 | file << "// Project name: " << project->name << "\n"; 20 | file << "// Copy and paste the following code snippets\n"; 21 | file << "// into the correct place in src/content_nodebox.cpp\n\n"; 22 | 23 | std::list & nodes = project->nodes; 24 | unsigned int i = 0; 25 | for (std::list::const_iterator it = nodes.begin(); 26 | it != nodes.end(); 27 | ++it, ++i) { 28 | Node* node = *it; 29 | file << "void content_nodebox_"; 30 | if (node->name == "") { 31 | file << "node_" << 1; 32 | } else { 33 | file << node->name; 34 | } 35 | file << "(ContentFeatures *f)\n"; 36 | file << "{\n"; 37 | bool is_first = true; 38 | std::vector & boxes = node->boxes; 39 | for (std::vector::const_iterator it = boxes.begin(); 40 | it != boxes.end(); 41 | ++it) { 42 | 43 | file << "\t// " << (*it)->name << "\n"; 44 | if (is_first) { 45 | file << "\tf->setNodeBox(core::aabbox3d(\n"; 46 | } else { 47 | file << "\tf->addNodeBox(core::aabbox3d(\n"; 48 | } 49 | file << "\t\t" << (*it)->one.X << "*BS,\n\t\t" << (*it)->one.Y << "*BS,\n\t\t" << (*it)->one.Z << "*BS,\n"; 50 | file << "\t\t" << (*it)->two.X << "*BS,\n\t\t" << (*it)->two.Y << "*BS,\n\t\t" << (*it)->two.Z << "*BS\n"; 51 | file << "\t));\n\n"; 52 | is_first = false; 53 | } 54 | file << "}\n\n"; 55 | } 56 | 57 | file.close(); 58 | 59 | return true; 60 | } 61 | 62 | 63 | Project * CPPFileFormat::read(const std::string & file, Project *project) 64 | { 65 | throw std::runtime_error("CPPFileFormat::read() is not implemented!"); 66 | return NULL; 67 | } 68 | -------------------------------------------------------------------------------- /src/FileFormat/CPP.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CPPFILEFORMAT_HPP_INCLUDED 2 | #define CPPFILEFORMAT_HPP_INCLUDED 3 | 4 | #include "FileFormat.hpp" 5 | 6 | class FileFormat; 7 | class CPPFileFormat : public FileFormat 8 | { 9 | public: 10 | CPPFileFormat(EditorState* st) : state(st) {} 11 | virtual bool write(Project* project, const std::string & filename); 12 | virtual Project * read(const std::string & file, Project *project=NULL); 13 | 14 | virtual const char * getExtension() const { 15 | return "cpp"; 16 | } 17 | private: 18 | EditorState* state; 19 | }; 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /src/FileFormat/FileFormat.cpp: -------------------------------------------------------------------------------- 1 | #include "FileFormat.hpp" 2 | #include "NBE.hpp" 3 | #include "Lua.hpp" 4 | #include "CPP.hpp" 5 | #include "../util/filesys.hpp" 6 | 7 | FileFormat *getFromType(FileFormatType id, EditorState *st) 8 | { 9 | switch (id) { 10 | case FILE_FORMAT_NBE: 11 | return new NBEFileFormat(st); 12 | case FILE_FORMAT_LUA: 13 | return new LuaFileFormat(st); 14 | case FILE_FORMAT_CPP: 15 | return new CPPFileFormat(st); 16 | } 17 | return NULL; 18 | } 19 | 20 | 21 | FileFormat *getFromExt(std::string path, EditorState *st) 22 | { 23 | std::string ext = extFromFilename(path); 24 | 25 | std::cerr << "Ext is " << ext.c_str() << std::endl; 26 | 27 | if (ext == "nbe") 28 | return new NBEFileFormat(st); 29 | else if (ext == "lua") 30 | return new LuaFileFormat(st); 31 | else if (ext == "cpp") 32 | return new CPPFileFormat(st); 33 | else 34 | return NULL; 35 | } 36 | -------------------------------------------------------------------------------- /src/FileFormat/FileFormat.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FILEFORMAT_HPP_INCLUDED 2 | #define FILEFORMAT_HPP_INCLUDED 3 | #include 4 | #include "../project/project.hpp" 5 | #include "../EditorState.hpp" 6 | 7 | enum FileFormatError 8 | { 9 | EFFE_NONE, // No error reported 10 | EFFE_IO_ERROR, // Problem opening file 11 | EFFE_READ_OLD_VERSION, // File is too old for this version of NBE 12 | EFFE_READ_NEW_VERSION, // File is newer than this version of NBE 13 | EFFE_READ_PARSE_ERROR, // Corruption or issue in reading file 14 | EFFE_READ_WRONG_TYPE // Wrong type of file 15 | }; 16 | 17 | enum FileFormatType 18 | { 19 | FILE_FORMAT_NBE, 20 | FILE_FORMAT_LUA, 21 | FILE_FORMAT_CPP 22 | }; 23 | 24 | class Project; 25 | class EditorState; 26 | class FileFormat 27 | { 28 | public: 29 | FileFormat(): 30 | error_code(EFFE_NONE) 31 | {} 32 | 33 | virtual Project * read(const std::string &filename, Project *project=NULL) = 0; // Open from file 34 | virtual bool write(Project *project, const std::string & filename) = 0; // Save to file 35 | virtual std::string getAsString(Project *project) { return ""; } 36 | virtual const char * getExtension() const = 0; // Get the main file extension 37 | FileFormatError error_code; 38 | }; 39 | 40 | // FileFormat factory 41 | extern FileFormat *getFromType(FileFormatType id, EditorState *st); 42 | extern FileFormat *getFromExt(std::string path, EditorState *st); 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /src/FileFormat/Lua.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "../common.hpp" 7 | #include "../project/project.hpp" 8 | #include "../project/node.hpp" 9 | #include "Lua.hpp" 10 | #include 11 | #include "../util/filesys.hpp" 12 | 13 | bool LuaFileFormat::write(Project * project, const std::string & filename){ 14 | std::ofstream file(filename.c_str()); 15 | if (!file) { 16 | error_code = EFFE_IO_ERROR; 17 | return false; 18 | } 19 | file << getAsString(project); 20 | file.close(); 21 | return true; 22 | } 23 | 24 | std::string doTileImage(Node *node, ECUBE_SIDE face) 25 | { 26 | Media::Image *image = node->getTexture(face); 27 | if (!image) 28 | return "default_wood.png"; 29 | 30 | return image->name; 31 | } 32 | 33 | std::string LuaFileFormat::getAsString(Project *project) 34 | { 35 | std::ostringstream file; 36 | file << "-- GENERATED CODE\n"; 37 | file << "-- Node Box Editor, version " << EDITOR_TEXT_VERSION << '\n'; 38 | file << "-- Namespace: " << project->name << "\n\n"; 39 | 40 | std::list & nodes = project->nodes; 41 | unsigned int i = 0; 42 | for (std::list::const_iterator it = nodes.begin(); 43 | it != nodes.end(); 44 | ++it, ++i) { 45 | Node* node = *it; 46 | file << "minetest.register_node(\""; 47 | if (node->name == "") { 48 | file << project->name << ":node_" << 1; 49 | } else { 50 | file << project->name << ":" << node->name; 51 | } 52 | file << "\", {\n"; 53 | file << "\ttiles = {\n"; 54 | file << "\t\t\"" << doTileImage(node, ECS_TOP).c_str() << "\",\n"; 55 | file << "\t\t\"" << doTileImage(node, ECS_BOTTOM).c_str() << "\",\n"; 56 | file << "\t\t\"" << doTileImage(node, ECS_RIGHT).c_str() << "\",\n"; 57 | file << "\t\t\"" << doTileImage(node, ECS_LEFT).c_str() << "\",\n"; 58 | file << "\t\t\"" << doTileImage(node, ECS_BACK).c_str() << "\",\n"; 59 | file << "\t\t\"" << doTileImage(node, ECS_FRONT).c_str() << "\"\n"; 60 | file << "\t},\n"; 61 | file << "\tdrawtype = \"nodebox\",\n" 62 | "\tparamtype = \"light\",\n" 63 | "\tnode_box = {\n" 64 | "\t\ttype = \"fixed\",\n" 65 | "\t\tfixed = {\n"; 66 | 67 | std::vector & boxes = node->boxes; 68 | for (std::vector::const_iterator it = boxes.begin(); 69 | it != boxes.end(); 70 | ++it) { 71 | file << "\t\t\t{"; 72 | file << (*it)->one.X << ", " << (*it)->one.Y << ", " << (*it)->one.Z << ", "; 73 | file << (*it)->two.X << ", " << (*it)->two.Y << ", " << (*it)->two.Z << "},"; 74 | file << " -- " << (*it)->name << "\n"; 75 | } 76 | 77 | file << "\t\t}\n" 78 | "\t}\n" 79 | "})\n\n"; 80 | } 81 | 82 | return file.str(); 83 | } 84 | 85 | 86 | Project * LuaFileFormat::read(const std::string & file, Project *project) 87 | { 88 | throw std::runtime_error("LuaFileFormat::read() is not implemented!"); 89 | return NULL; 90 | } 91 | -------------------------------------------------------------------------------- /src/FileFormat/Lua.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LUAFILEFORMAT_HPP_INCLUDED 2 | #define LUAFILEFORMAT_HPP_INCLUDED 3 | 4 | #include "FileFormat.hpp" 5 | 6 | class LuaFileFormat : public FileFormat 7 | { 8 | public: 9 | LuaFileFormat(EditorState* st) : state(st) {} 10 | virtual bool write(Project* project, const std::string & filename); 11 | virtual std::string getAsString(Project *project); 12 | virtual Project * read(const std::string & file, Project *project=NULL); 13 | 14 | const char * getExtension() const { 15 | return "lua"; 16 | } 17 | private: 18 | EditorState* state; 19 | }; 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /src/FileFormat/NBE.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "NBE.hpp" 4 | #include "../util/string.hpp" 5 | #include "../util/filesys.hpp" 6 | #include "../util/SimpleFileCombiner.hpp" 7 | 8 | Project *NBEFileFormat::read(const std::string &filename, Project *project) 9 | { 10 | std::string tmpdir = getTmpDirectory(state->isInstalled); 11 | if (!CreateDir(tmpdir)) { 12 | error_code = EFFE_IO_ERROR; 13 | return NULL; 14 | } 15 | if (project) { 16 | merging = true; 17 | } else { 18 | merging = false; 19 | project = new Project(); 20 | project->file = std::string(filename); 21 | } 22 | SimpleFileCombiner fc; 23 | std::list files = fc.read(filename.c_str(), tmpdir); 24 | if (files.size() == 0) { 25 | if (fc.errcode == SimpleFileCombiner::EERR_WRONG_FILE) { 26 | if (!readProjectFile(project, filename)) { 27 | delete project; 28 | return NULL; 29 | } 30 | return project; 31 | } else { 32 | if (fc.errcode == SimpleFileCombiner::EERR_IO) 33 | error_code = EFFE_IO_ERROR; 34 | delete project; 35 | return NULL; 36 | } 37 | } 38 | 39 | for (std::list::const_iterator it = files.begin(); 40 | it != files.end(); 41 | ++it) { 42 | std::string name = *it; 43 | if (name != "project.txt") { 44 | project->media.add(name.c_str(), name.c_str(), state->device->getVideoDriver()->createImageFromFile((tmpdir + name).c_str())); 45 | } 46 | } 47 | if (!readProjectFile(project, tmpdir + "project.txt")) { 48 | delete project; 49 | return NULL; 50 | } 51 | return project; 52 | } 53 | 54 | bool NBEFileFormat::write(Project *project, const std::string &filename) 55 | { 56 | std::string tmpdir = getTmpDirectory(state->isInstalled); 57 | if (!CreateDir(tmpdir)) { 58 | error_code = EFFE_IO_ERROR; 59 | return false; 60 | } 61 | if (writeProjectFile(project, tmpdir + "project.txt")) { 62 | SimpleFileCombiner fc; 63 | fc.add((tmpdir + "project.txt").c_str(), "project.txt"); 64 | Media *media = &project->media; 65 | std::map& images = media->getList(); 66 | for (std::map::const_iterator it = images.begin(); 67 | it != images.end(); 68 | ++it) { 69 | Media::Image *image = it->second; 70 | if (!image->get()) { 71 | std::cerr << "Image->get() is NULL!" << std::endl; 72 | continue; 73 | } 74 | state->device->getVideoDriver()->writeImageToFile(image->get(), (tmpdir + image->name).c_str()); 75 | fc.add(trim(tmpdir + image->name).c_str(), image->name); 76 | } 77 | if (fc.write(filename)) { 78 | return true; 79 | } else { 80 | if (fc.errcode == SimpleFileCombiner::EERR_IO) 81 | error_code = EFFE_IO_ERROR; 82 | return false; 83 | } 84 | } 85 | return true; 86 | } 87 | 88 | bool NBEFileFormat::readProjectFile(Project *project, const std::string & filename) 89 | { 90 | // Open file 91 | std::string line; 92 | std::ifstream file(filename.c_str()); 93 | if (!file) { 94 | error_code = EFFE_IO_ERROR; 95 | return false; 96 | } 97 | 98 | // Read parser header 99 | std::getline(file, line); 100 | if (line != "MINETEST NODEBOX EDITOR") { 101 | error_code = EFFE_READ_WRONG_TYPE; 102 | return false; 103 | } 104 | std::getline(file, line); 105 | if (line != "PARSER 1" && line != "PARSER 2") { 106 | error_code = EFFE_READ_NEW_VERSION; 107 | return false; 108 | } 109 | 110 | // Parse file 111 | stage = READ_STAGE_ROOT; 112 | while (std::getline(file, line)) { 113 | parseLine(project, line); 114 | } 115 | file.close(); 116 | 117 | if (node) { 118 | std::cerr << "Unexpected EOF, expecting END NODE." << std::endl; 119 | error_code = EFFE_READ_PARSE_ERROR; 120 | return false; 121 | } 122 | 123 | return true; 124 | } 125 | 126 | const char* getLabelForECUBE_SIDE(ECUBE_SIDE face) 127 | { 128 | switch(face) { 129 | case (ECS_TOP): 130 | return "top"; 131 | case (ECS_BOTTOM): 132 | return "bottom"; 133 | case (ECS_LEFT): 134 | return "left"; 135 | case (ECS_RIGHT): 136 | return "right"; 137 | case (ECS_FRONT): 138 | return "front"; 139 | case (ECS_BACK): 140 | return "back"; 141 | } 142 | } 143 | 144 | ECUBE_SIDE cubeSideFromString(std::string input) 145 | { 146 | if (input == "left") { 147 | return ECS_LEFT; 148 | } else if (input == "right") { 149 | return ECS_RIGHT; 150 | } else if (input == "top") { 151 | return ECS_TOP; 152 | } else if (input == "bottom") { 153 | return ECS_BOTTOM; 154 | } else if (input == "front") { 155 | return ECS_FRONT; 156 | } else if (input == "back") { 157 | return ECS_BACK; 158 | } 159 | } 160 | 161 | bool NBEFileFormat::writeProjectFile(Project *project, const std::string &filename) 162 | { 163 | std::ofstream file(filename.c_str()); 164 | if (!file) { 165 | return false; 166 | } 167 | file << "MINETEST NODEBOX EDITOR\n"; 168 | file << "PARSER 2\n"; 169 | file << "NAME " << project->name << "\n\n"; 170 | 171 | std::list & nodes = project->nodes; 172 | unsigned int i = 0; 173 | for (std::list::const_iterator it = nodes.begin(); 174 | it != nodes.end(); 175 | ++it, ++i) { 176 | Node* node = *it; 177 | file << "NODE "; 178 | if (node->name == "") { 179 | file << "Node" << i; 180 | } else { 181 | file << node->name; 182 | } 183 | file << "\n"; 184 | vector3di pos = node->position; 185 | file << "POSITION " << pos.X << ' ' << pos.Y << ' ' << pos.Z << '\n'; 186 | 187 | for (int i = 0; i < 6; i++) { 188 | Media::Image* image = node->getTexture((ECUBE_SIDE)i); 189 | if (image) { 190 | file << "TEXTURE " << getLabelForECUBE_SIDE((ECUBE_SIDE)i) << " " << image->name.c_str() << "\n"; 191 | } 192 | } 193 | 194 | for (std::vector::const_iterator it = node->boxes.begin(); 195 | it != node->boxes.end(); 196 | ++it) { 197 | NodeBox* box = *it; 198 | file << "NODEBOX " << box->name << ' '; 199 | file << box->one.X << ' ' << box->one.Y << ' ' << box->one.Z << ' '; 200 | file << box->two.X << ' ' << box->two.Y << ' ' << box->two.Z << '\n'; 201 | } 202 | 203 | file << "END NODE\n\n"; 204 | } 205 | 206 | file.close(); 207 | 208 | return true; 209 | } 210 | 211 | void NBEFileFormat::parseLine(Project * project, std::string & line) 212 | { 213 | line = trim(line); 214 | 215 | if (line.empty()) { 216 | return; 217 | } 218 | 219 | std::string lower = str_to_lower(line); 220 | 221 | if (stage == READ_STAGE_ROOT) { 222 | if (lower.find("name ") == 0 && !merging) { 223 | project->name = trim(line.substr(4)); 224 | } else if (lower.find("node ") == 0) { 225 | stage = READ_STAGE_NODE; 226 | node = new Node(state->device, state, project->GetNodeCount()); 227 | node->name = trim(line.substr(4)); 228 | std::list & nodes = project->nodes; 229 | for (std::list::const_iterator it = nodes.begin(); 230 | it != nodes.end(); 231 | ++it) { 232 | Node* xnode = *it; 233 | if (xnode->name == node->name) { 234 | node->name = ""; 235 | return; 236 | } 237 | } 238 | } 239 | } else if (stage == READ_STAGE_NODE) { 240 | if (lower.find("position ") == 0) { 241 | std::string n = trim(line.substr(8)); 242 | std::string s[3]; 243 | for (unsigned int i = 0; n != ""; i++){ 244 | size_t nid = n.find(" "); 245 | 246 | if (nid == std::string::npos){ 247 | nid = n.size(); 248 | } 249 | if (i >= 3) { 250 | std::cerr << "Too many arguments in position tag" << std::endl; 251 | break; 252 | } 253 | s[i] = trim(n.substr(0, nid)); 254 | n = trim(n.substr(nid)); 255 | } 256 | vector3di newpos((int)atof(s[0].c_str()), 257 | (int)atof(s[1].c_str()), 258 | (int)atof(s[2].c_str())); 259 | if (merging) { 260 | std::list & nodes = project->nodes; 261 | for (std::list::const_iterator it = nodes.begin(); 262 | it != nodes.end(); 263 | ++it) { 264 | Node* node = *it; 265 | if (node->position == newpos) 266 | return; 267 | } 268 | } 269 | node->position = newpos; 270 | } else if (lower.find("texture ") == 0){ 271 | std::string n = trim(line.substr(7)); 272 | size_t nid = n.find(" "); 273 | if (nid == std::string::npos || nid < 0){ 274 | nid = n.size(); 275 | } 276 | std::string one = trim(n.substr(0, nid)); 277 | std::string two = trim(n.substr(nid)); 278 | node->setTexture(cubeSideFromString(one), project->media.get(two.c_str())); 279 | } else if (lower.find("nodebox ") == 0) { 280 | std::string n = trim(line.substr(7)); 281 | std::string s[7]; 282 | for (unsigned int i = 0; n != ""; i++){ 283 | size_t nid = n.find(" "); 284 | 285 | if (nid == std::string::npos){ 286 | nid = n.size(); 287 | } 288 | if (i >= 7) { 289 | std::cerr << "Too many arguments in nodebox tag" << std::endl; 290 | break; 291 | } 292 | s[i] = trim(n.substr(0, nid)); 293 | n = trim(n.substr(nid)); 294 | } 295 | NodeBox *box = node->addNodeBox( 296 | vector3df( 297 | (f32)atof(s[1].c_str()), 298 | (f32)atof(s[2].c_str()), 299 | (f32)atof(s[3].c_str()) 300 | ), 301 | vector3df( 302 | (f32)atof(s[4].c_str()), 303 | (f32)atof(s[5].c_str()), 304 | (f32)atof(s[6].c_str()) 305 | )); 306 | box->name = s[0]; 307 | box->rebuild_needed = true; 308 | node->remesh(box); 309 | } else if (lower.find("end node") == 0){ 310 | project->AddNode(node); 311 | node = NULL; 312 | stage = READ_STAGE_ROOT; 313 | } 314 | } 315 | } 316 | -------------------------------------------------------------------------------- /src/FileFormat/NBE.hpp: -------------------------------------------------------------------------------- 1 | #ifndef NBEFILEFORMAT_HPP_INCLUDED 2 | #define NBEFILEFORMAT_HPP_INCLUDED 3 | 4 | #include "FileFormat.hpp" 5 | #include "../project/node.hpp" 6 | 7 | class NBEFileFormat : public FileFormat 8 | { 9 | public: 10 | NBEFileFormat(EditorState *st) : 11 | state(st), 12 | node(NULL), 13 | stage(READ_STAGE_ROOT) 14 | {} 15 | virtual Project *read(const std::string &filename, Project *project=NULL); 16 | virtual bool write(Project *project, const std::string &filename); 17 | virtual const char *getExtension() const { 18 | return "nbe"; 19 | } 20 | enum readstage 21 | { 22 | READ_STAGE_ROOT, 23 | READ_STAGE_NODE 24 | }; 25 | private: 26 | readstage stage; 27 | Node *node; 28 | EditorState *state; 29 | bool merging; 30 | bool readProjectFile(Project *project, const std::string &filename); 31 | bool writeProjectFile(Project *project, const std::string &filename); 32 | void parseLine(Project *project, std::string &line); 33 | }; 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /src/FileFormat/helpers.cpp: -------------------------------------------------------------------------------- 1 | #include "helpers.hpp" 2 | #include "../util/string.hpp" 3 | #include "../util/filesys.hpp" 4 | 5 | void save_file(FileFormat *writer, EditorState *state, std::string file, bool check_ext) 6 | { 7 | if (!writer || !state) 8 | return; 9 | 10 | std::string after(file); 11 | 12 | if (check_ext && after.find('.') == std::string::npos) { 13 | after += '.'; 14 | after += writer->getExtension(); 15 | } 16 | 17 | std::cerr << "Saving to " << after << std::endl; 18 | 19 | if (!writer->write(state->project, after)) { 20 | if (writer->error_code == EFFE_IO_ERROR) { 21 | state->device->getGUIEnvironment()->addMessageBox(L"Unable to Save", 22 | L"Unable to open file to save to"); 23 | } else { 24 | state->device->getGUIEnvironment()->addMessageBox(L"Unable to Save", 25 | L"Unknown reason"); 26 | } 27 | } 28 | 29 | delete writer; 30 | } 31 | 32 | void export_textures(std::string dir, EditorState *state) 33 | { 34 | if (dir == "") 35 | return; 36 | 37 | std::cerr << "Exporting Images to " << dir.c_str() << std::endl; 38 | CreateDir(dir.c_str()); 39 | Media *media = &state->project->media; 40 | std::map& images = media->getList(); 41 | for (std::map::const_iterator it = images.begin(); 42 | it != images.end(); 43 | ++it) { 44 | Media::Image *image = it->second; 45 | if (!image->get()) { 46 | std::cerr << "Image->get() is NULL!" << std::endl; 47 | continue; 48 | } 49 | state->device->getVideoDriver()->writeImageToFile(image->get(), (dir + image->name).c_str()); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/FileFormat/helpers.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FILEFORMAT_HELPERS_HPP_INCLUDED 2 | #define FILEFORMAT_HELPERS_HPP_INCLUDED 3 | #include "../common.hpp" 4 | #include "FileFormat.hpp" 5 | 6 | void save_file(FileFormat *writer, EditorState *state, std::string file, bool check_ext=true); 7 | 8 | void export_textures(std::string dir, EditorState *state); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /src/FileFormat/obj.cpp: -------------------------------------------------------------------------------- 1 | #include "obj.hpp" 2 | #include 3 | #include 4 | #include "../util/string.hpp" 5 | #include 6 | 7 | void nodeBoxObj(int order, NodeBox *box, std::string &vertices, std::string &faces) 8 | { 9 | std::ostringstream vert; 10 | vert << "v " << -box->one.X << " " << box->one.Y << " " << box->one.Z << "\n"; 11 | vert << "v " << -box->one.X << " " << box->one.Y << " " << box->two.Z << "\n"; 12 | vert << "v " << -box->one.X << " " << box->two.Y << " " << box->two.Z << "\n"; 13 | vert << "v " << -box->one.X << " " << box->two.Y << " " << box->one.Z << "\n"; 14 | 15 | vert << "v " << -box->two.X << " " << box->one.Y << " " << box->one.Z << "\n"; 16 | vert << "v " << -box->two.X << " " << box->one.Y << " " << box->two.Z << "\n"; 17 | vert << "v " << -box->two.X << " " << box->two.Y << " " << box->two.Z << "\n"; 18 | vert << "v " << -box->two.X << " " << box->two.Y << " " << box->one.Z << "\n"; 19 | vertices = vert.str(); 20 | 21 | static const int faces_val[] = { 22 | 1, 2, 3, 4, 23 | 5, 6, 7, 8, 24 | 1, 4, 8, 5, 25 | 2, 3, 7, 6, 26 | 1, 2, 6, 5, 27 | 4, 3, 7, 8 28 | }; 29 | 30 | std::ostringstream fac; 31 | std::string name(box->name); 32 | std::transform(name.begin(), name.end(), name.begin(), ::tolower); 33 | str_replace(std::string(name.c_str(), name.size()), ' ', '_'); 34 | fac << "g " << name << "\n"; 35 | for (int i = 0; i < 6; i++) { 36 | fac << "f " 37 | << faces_val[i*4] + order*8 << " " 38 | << faces_val[i*4 + 1] + order*8 << " " 39 | << faces_val[i*4 + 2] + order*8 << " " 40 | << faces_val[i*4 + 3] + order*8 << "\n"; 41 | } 42 | faces = fac.str(); 43 | } 44 | 45 | std::string nodeToObj(Node *node, std::string filenameNoExt) 46 | { 47 | std::string vertices = ""; 48 | std::string faces = ""; 49 | int count = 0; 50 | for (std::vector::const_iterator it = node->boxes.begin(); 51 | it != node->boxes.end(); 52 | ++it) { 53 | NodeBox* box = *it; 54 | std::string vert = ""; 55 | std::string fac = ""; 56 | nodeBoxObj(count, box, vert, fac); 57 | vertices += vert; 58 | faces += fac; 59 | count++; 60 | } 61 | 62 | std::ostringstream res; 63 | res << "mtllib " << filenameNoExt.c_str() << ".mtl" << std::endl; 64 | res << "o converted_out" << std::endl; 65 | res << vertices.c_str(); 66 | res << "usemtl none" << std::endl; 67 | res << "s off" << std::endl; 68 | res << faces.c_str(); 69 | return res.str(); 70 | } 71 | -------------------------------------------------------------------------------- /src/FileFormat/obj.hpp: -------------------------------------------------------------------------------- 1 | #ifndef OBJ_HPP_INCLUDED 2 | #define OBJ_HPP_INCLUDED 3 | 4 | #include "../project/node.hpp" 5 | 6 | std::string nodeToObj(Node *node, std::string filenameNoExt = "out"); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /src/GUIHelpers.cpp: -------------------------------------------------------------------------------- 1 | #include "GUIHelpers.hpp" 2 | 3 | void fillTB(IGUIElement* sidebar, int parentId, int id, int value){ 4 | IGUIElement* element = sidebar->getElementFromId(parentId)->getElementFromId(id); 5 | 6 | if (element) { 7 | IGUIEditBox* editbox = static_cast(element); 8 | 9 | if (!editbox) { 10 | return; 11 | } 12 | 13 | editbox->setText(stringw(value).c_str()); 14 | } 15 | } 16 | 17 | void fillTB(IGUIElement* sidebar, int parentId, int id, float value){ 18 | IGUIElement* element = sidebar->getElementFromId(parentId)->getElementFromId(id); 19 | 20 | if (element) { 21 | IGUIEditBox* editbox = static_cast(element); 22 | 23 | if (!editbox) { 24 | return; 25 | } 26 | 27 | editbox->setText(stringw(value).c_str()); 28 | } 29 | } 30 | 31 | 32 | void addBox(IGUIElement* parent, IGUIEnvironment* guienv, vector2di pos, int index, const wchar_t* label){ 33 | guienv->addStaticText(label, 34 | rect(pos.X, pos.Y, pos.X + 20, pos.Y + 20), 35 | false, true, parent)->setNotClipped(true); 36 | guienv->addEditBox(L"", 37 | rect(pos.X + 15, pos.Y, pos.X + 200, pos.Y + 20), 38 | true, parent, index)->setNotClipped(true); 39 | } 40 | 41 | 42 | void addXYZ(IGUIElement* parent,IGUIEnvironment* guienv, vector2di pos, int startIndex){ 43 | addBox(parent, guienv, vector2di(pos.X, pos.Y), startIndex, L"X"); // 0,0 44 | addBox(parent, guienv, vector2di(pos.X, pos.Y + 25), startIndex + 1, L"Y"); // 80, 0 45 | addBox(parent, guienv, vector2di(pos.X, pos.Y + 50), startIndex + 2, L"Z"); // 160, 0 46 | } 47 | -------------------------------------------------------------------------------- /src/GUIHelpers.hpp: -------------------------------------------------------------------------------- 1 | #ifndef GUIHELPERS_HPP_INCLUDED 2 | #define GUIHELPERS_HPP_INCLUDED 3 | 4 | #include "common.hpp" 5 | 6 | extern void fillTB(IGUIElement* sidebar, int parentId, int id, int value); 7 | extern void fillTB(IGUIElement* sidebar, int parentId, int id, float value); 8 | extern void addBox(IGUIElement* parent, IGUIEnvironment* guienv, vector2di pos, int index, const wchar_t* label); 9 | extern void addXYZ(IGUIElement* parent, IGUIEnvironment* guienv, vector2di pos, int startIndex); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /src/MenuState.cpp: -------------------------------------------------------------------------------- 1 | #include "util/string.hpp" 2 | #include "util/filesys.hpp" 3 | #include "MenuState.hpp" 4 | #include "FileFormat/FileFormat.hpp" 5 | #include "FileFormat/NBE.hpp" 6 | #include "FileFormat/helpers.hpp" 7 | #include "dialogs/FileDialog.hpp" 8 | #include "dialogs/ImageDialog.hpp" 9 | #include "minetest.hpp" 10 | #include 11 | 12 | #if _WIN32 13 | #define OPEN_URL(url) system((std::string("start \"\" \"") + std::string(url) + "\"").c_str()) 14 | #else 15 | #define OPEN_URL(url) system((std::string("xdg-open \"") + std::string(url) + "\"").c_str()) 16 | #endif 17 | 18 | MenuState::MenuState(EditorState* state) : 19 | state(state), 20 | projectMenubar(NULL), 21 | menubar(NULL), 22 | mode_icons_open(false), 23 | dialog(NULL) 24 | {} 25 | 26 | 27 | void MenuState::init() 28 | { 29 | IGUIEnvironment *guienv = state->device->getGUIEnvironment(); 30 | guienv->clear(); 31 | guienv->getSkin()->setFont(guienv->getFont("media/fontlucida.png")); 32 | 33 | // Main menu bar 34 | menubar = guienv->addMenu(); 35 | menubar->addItem(L"File", -1, true, true); 36 | menubar->addItem(L"Edit", -1, true, true); 37 | menubar->addItem(L"View", -1, true, true); 38 | menubar->addItem(L"Project", -1, true, true); 39 | menubar->addItem(L"Help", -1, true, true); 40 | gui::IGUIContextMenu *submenu; 41 | 42 | // File 43 | submenu = menubar->getSubMenu(0); 44 | //submenu->addItem(L"New", GUI_FILE_NEW_PROJECT); 45 | submenu->addItem(L"Open Project", GUI_FILE_OPEN_PROJECT); 46 | submenu->addItem(L"Save Project", GUI_FILE_SAVE_PROJECT); 47 | submenu->addItem(L"Save Project As", GUI_FILE_SAVE_PROJECT_AS); 48 | submenu->addSeparator(); 49 | submenu->addItem(L"Run in Minetest", GUI_FILE_RUN_IN_MINETEST); 50 | submenu->addItem(L"Export", -1, true, true); 51 | submenu->addItem(L"Import Nodes", GUI_FILE_IMPORT); 52 | submenu->addSeparator(); 53 | submenu->addItem(L"Exit", GUI_FILE_EXIT); 54 | 55 | // File > Export 56 | submenu = submenu->getSubMenu(5); 57 | submenu->addItem(L"Standalone Lua File (.lua)", GUI_FILE_EXPORT_LUA); 58 | submenu->addItem(L"Voxelands (.cpp)", GUI_FILE_EXPORT_CPP); 59 | submenu->addItem(L"Minetest Mod", GUI_FILE_EXPORT_MOD); 60 | submenu->addItem(L"Current node to mesh (.obj)", GUI_FILE_EXPORT_OBJ); 61 | submenu->addItem(L"Textures to Folder", GUI_FILE_EXPORT_TEX); 62 | 63 | // Edit 64 | submenu = menubar->getSubMenu(1); 65 | submenu->addItem( 66 | L"Snapping", GUI_EDIT_SNAP, true, false, 67 | state->settings->getBool("snapping"), 68 | true 69 | ); 70 | submenu->addItem( 71 | L"Limiting", GUI_EDIT_LIMIT, true, false, 72 | state->settings->getBool("limiting"), 73 | true 74 | ); 75 | 76 | // View 77 | submenu = menubar->getSubMenu(2); 78 | submenu->addItem(L"Tiled View", GUI_VIEW_SP_ALL); 79 | submenu->addSeparator(); 80 | submenu->addItem(L"Top Left", GUI_VIEW_SP_PER); 81 | submenu->addItem(L"Top Right", GUI_VIEW_SP_TOP); 82 | submenu->addItem(L"Bottom Left", GUI_VIEW_SP_FRT); 83 | submenu->addItem(L"Bottom Right", GUI_VIEW_SP_RHT); 84 | 85 | // Project 86 | projectMenubar = menubar->getSubMenu(3); 87 | projectMenubar->addItem(L"Import Image", GUI_PROJ_IMAGE_IM); 88 | 89 | 90 | // Help 91 | submenu = menubar->getSubMenu(4); 92 | submenu->addItem(L"Help...", GUI_HELP_HELP); 93 | submenu->addItem(L"Forum Topic...", GUI_HELP_FORUM); 94 | submenu->addItem(L"Report bugs...", GUI_HELP_REPORT); 95 | submenu->addSeparator(); 96 | submenu->addItem(L"About", GUI_HELP_ABOUT); 97 | 98 | // Sidebar root 99 | u32 top = menubar->getAbsoluteClippingRect().LowerRightCorner.Y; 100 | sidebar = guienv->addStaticText(L"Loading...", 101 | rect( 102 | state->device->getVideoDriver()->getScreenSize().Width - 246, 103 | top + 10, 104 | state->device->getVideoDriver()->getScreenSize().Width, 105 | state->device->getVideoDriver()->getScreenSize().Height 106 | ), false, true, 0, GUI_SIDEBAR 107 | ); 108 | sidebar->setAlignment(EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_UPPERLEFT); 109 | sidebar->setNotClipped(true); 110 | } 111 | 112 | bool MenuState::OnEvent(const SEvent& event) 113 | { 114 | if (dialog) 115 | return dialog->OnEvent(event); 116 | 117 | if (event.EventType == irr::EET_MOUSE_INPUT_EVENT && 118 | event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) { 119 | if (rect(10, 32, 42, 64).isPointInside(state->mouse_position)){ 120 | mode_icons_open = !mode_icons_open; 121 | return true; 122 | } 123 | if (mode_icons_open) { 124 | if (!(rect(10, 32, 210, 64).isPointInside(state->mouse_position))) { 125 | mode_icons_open = false; 126 | return true; 127 | } 128 | EditorMode *curs = state->Mode(); 129 | int x = 0; 130 | for (int i = 0; i < 5; i++){ 131 | EditorMode *m = state->Mode(i); 132 | 133 | if (m && m != curs){ 134 | if (rect(47 + 37 * x, 32, 79 + 37 * x, 64) 135 | .isPointInside(state->mouse_position)) { 136 | state->SelectMode(i); 137 | mode_icons_open = false; 138 | return true; 139 | } 140 | x++; 141 | } 142 | } 143 | } 144 | } 145 | if (event.EventType == EET_GUI_EVENT) { 146 | if (event.GUIEvent.EventType == EGET_MENU_ITEM_SELECTED) { 147 | IGUIContextMenu *menu = (IGUIContextMenu *)event.GUIEvent.Caller; 148 | switch (menu->getItemCommandId(menu->getSelectedItem())){ 149 | case GUI_FILE_OPEN_PROJECT: 150 | FileDialog_open_project(state); 151 | return true; 152 | case GUI_FILE_SAVE_PROJECT: 153 | if (!state->project) { 154 | state->device->getGUIEnvironment()->addMessageBox( 155 | L"Unable to save", 156 | L"You have not yet opened a project."); 157 | return true; 158 | } 159 | if (state->project->file != "") { 160 | save_file(getFromType(FILE_FORMAT_NBE, state), state, state->project->file, false); 161 | } else { 162 | FileDialog_save_project(state); 163 | } 164 | return true; 165 | case GUI_FILE_SAVE_PROJECT_AS: 166 | if (!state->project) { 167 | state->device->getGUIEnvironment()->addMessageBox( 168 | L"Unable to save", 169 | L"You have not yet opened a project."); 170 | return true; 171 | } 172 | FileDialog_save_project(state); 173 | return true; 174 | case GUI_FILE_RUN_IN_MINETEST: { 175 | Minetest mt(state->settings); 176 | if (!mt.findMinetest(state->isInstalled)) { 177 | state->device->getGUIEnvironment()->addMessageBox(L"Unable to find Minetest", 178 | L"Minetest could not be found by NBE.\n\t(try setting 'minetest_root' in editor.conf)"); 179 | return true; 180 | } 181 | mt.runMod(state); 182 | return true; 183 | } 184 | case GUI_FILE_EXPORT_LUA: 185 | FileDialog_export(state, FILE_FORMAT_LUA); 186 | return true; 187 | case GUI_FILE_EXPORT_CPP: 188 | FileDialog_export(state, FILE_FORMAT_CPP); 189 | return true; 190 | case GUI_FILE_EXPORT_MOD: 191 | FileDialog_export_mod(state); 192 | return true; 193 | case GUI_FILE_EXPORT_OBJ: 194 | FileDialog_export_obj(state, state->project->GetCurrentNode()); 195 | return true; 196 | case GUI_FILE_EXPORT_TEX: 197 | FileDialog_export_textures(state); 198 | return true; 199 | case GUI_FILE_IMPORT: 200 | FileDialog_import(state); 201 | return true; 202 | case GUI_FILE_EXIT: { 203 | IGUIEnvironment *guienv = state->device->getGUIEnvironment(); 204 | IGUIWindow *win = guienv->addWindow(rect(100, 100, 356, 215), 205 | true, L"Are you sure?"); 206 | guienv->addButton(rect(128 - 40, 80, 128 + 40, 105), 207 | win, GUI_FILE_EXIT, L"Close", L"Close the editor"); 208 | return true; 209 | } 210 | case GUI_EDIT_SNAP: 211 | if (menu->isItemChecked(menu->getSelectedItem())) { 212 | state->settings->set("snapping", "true"); 213 | } else { 214 | state->settings->set("snapping", "false"); 215 | } 216 | 217 | menu->setItemChecked(menu->getSelectedItem(), 218 | state->settings->getBool("snapping")); 219 | return true; 220 | case GUI_EDIT_LIMIT: 221 | if (menu->isItemChecked(menu->getSelectedItem())) { 222 | state->settings->set("limiting", "true"); 223 | } else { 224 | state->settings->set("limiting", "false"); 225 | } 226 | 227 | menu->setItemChecked(menu->getSelectedItem(), 228 | state->settings->getBool("limiting")); 229 | return true; 230 | case GUI_PROJ_IMAGE_IM: 231 | ImageDialog::show(state, NULL, ECS_TOP); 232 | return true; 233 | case GUI_HELP_HELP: 234 | OPEN_URL("http://rubenwardy.com/NodeBoxEditor/?v="); 235 | break; 236 | case GUI_HELP_FORUM: 237 | OPEN_URL("https://forum.minetest.net/viewtopic.php?f=14&t=2840"); 238 | break; 239 | case GUI_HELP_REPORT: 240 | OPEN_URL("https://github.com/rubenwardy/NodeBoxEditor/issues"); 241 | break; 242 | case GUI_HELP_ABOUT: { 243 | core::stringw msg = L"NodeBoxEditor (NBE)\n" 244 | L"Version: "; 245 | 246 | msg += EDITOR_TEXT_VERSION_LABEL; 247 | msg += L"\n\n" 248 | L"A free and open source nodebox editor " 249 | L"created by rubenwardy, using C++ and Irrlicht." 250 | L"You can download newer versions from the Minetest forum."; 251 | 252 | state->device->getGUIEnvironment()->addMessageBox(L"About", msg.c_str()); 253 | return true; 254 | } 255 | } 256 | } else if (event.GUIEvent.EventType == EGET_BUTTON_CLICKED) { 257 | switch (event.GUIEvent.Caller->getID()) { 258 | case GUI_FILE_EXIT: 259 | if (state->project) { 260 | NBEFileFormat writer(state); 261 | 262 | // Get directory to save to 263 | std::string dir = getSaveLoadDirectory(state->settings->get("save_directory"), state->isInstalled); 264 | 265 | std::cerr << "Saving to " << dir + "exit." << std::endl; 266 | if (!writer.write(state->project, dir + "exit.nbe")) 267 | std::cerr << "Failed to save file for unknown reason." << std::endl; 268 | } 269 | state->CloseEditor(); 270 | return true; 271 | } 272 | } 273 | } else if (event.EventType == EET_KEY_INPUT_EVENT){ 274 | if (event.KeyInput.Control && event.KeyInput.Key == KEY_KEY_S && 275 | !event.KeyInput.PressedDown) { 276 | if (!state->project) { 277 | state->device->getGUIEnvironment()->addMessageBox(L"Unable to save", 278 | L"You have not yet opened a project."); 279 | return true; 280 | } 281 | if (state->project->file != "") { 282 | save_file(getFromType(FILE_FORMAT_NBE, state), state, state->project->file, false); 283 | } else { 284 | FileDialog_save_project(state); 285 | } 286 | return true; 287 | } 288 | } 289 | return false; 290 | } 291 | 292 | void MenuState::draw(IVideoDriver *driver){ 293 | EditorMode* curs = state->Mode(); 294 | 295 | if (state->settings->getBool("hide_sidebar")) { 296 | sidebar->setVisible(false); 297 | } else { 298 | sidebar->setVisible(true); 299 | u32 top = menubar->getAbsoluteClippingRect().LowerRightCorner.Y; 300 | state->device->getGUIEnvironment()->getSkin() 301 | ->draw3DWindowBackground(NULL, false, 0, 302 | rect((driver->getScreenSize().Width - 256), 303 | top, 304 | driver->getScreenSize().Width, 305 | driver->getScreenSize().Height 306 | ) 307 | ); 308 | } 309 | 310 | if (dialog) 311 | return; 312 | 313 | if (curs) { 314 | driver->draw2DImage(curs->icon(), 315 | rect(10, 32, 42, 64), 316 | rect( 0, 0, 32, 32), 317 | 0, 0, true); 318 | } 319 | 320 | if (mode_icons_open) { 321 | int x = 0; 322 | for (int i = 0; i < 5; i++) { 323 | EditorMode* m = state->Mode(i); 324 | 325 | if (m && m != curs) { 326 | driver->draw2DImage(m->icon(), 327 | rect(47 + 37 * x, 32, 79 + 37 * x, 64), 328 | rect(0, 0, 32, 32), 329 | 0, 0, true); 330 | x++; 331 | } 332 | } 333 | } 334 | } 335 | -------------------------------------------------------------------------------- /src/MenuState.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MENUSTATE_HPP_INCLUDED 2 | #define MENUSTATE_HPP_INCLUDED 3 | #include "common.hpp" 4 | #include "EditorState.hpp" 5 | #include "dialogs/Dialog.hpp" 6 | 7 | #define SIDEBAR_MAX_IDS 20 8 | enum GUI_ID 9 | { 10 | // File 11 | GUI_FILE_NEW_PROJECT = 201, 12 | GUI_FILE_NEW_ITEM, 13 | GUI_FILE_OPEN_PROJECT, 14 | GUI_FILE_SAVE_PROJECT, 15 | GUI_FILE_SAVE_PROJECT_AS, 16 | GUI_FILE_RUN_IN_MINETEST, 17 | GUI_FILE_EXPORT_LUA, 18 | GUI_FILE_EXPORT_CPP, 19 | GUI_FILE_EXPORT_MOD, 20 | GUI_FILE_EXPORT_OBJ, 21 | GUI_FILE_EXPORT_TEX, 22 | GUI_FILE_IMPORT, 23 | GUI_FILE_EXIT, 24 | 25 | // Edit 26 | GUI_EDIT_UNDO, 27 | GUI_EDIT_REDO, 28 | GUI_EDIT_SNAP, 29 | GUI_EDIT_LIMIT, 30 | 31 | // View 32 | GUI_VIEW_SP_ALL, 33 | GUI_VIEW_SP_PER, 34 | GUI_VIEW_SP_TOP, 35 | GUI_VIEW_SP_FRT, 36 | GUI_VIEW_SP_RHT, 37 | 38 | // Tools 39 | GUI_PROJ_NEW_BOX, 40 | GUI_PROJ_DELETE_BOX, 41 | GUI_PROJ_CLONE, 42 | GUI_PROJ_IMAGE_IM, 43 | 44 | // Help 45 | GUI_HELP_HELP, 46 | GUI_HELP_FORUM, 47 | GUI_HELP_REPORT, 48 | GUI_HELP_ABOUT, 49 | 50 | // Sidebar 51 | GUI_SIDEBAR, 52 | 53 | // File Dialog 54 | GUI_FILEDIALOG_PATH = GUI_SIDEBAR + SIDEBAR_MAX_IDS, 55 | GUI_DIALOG_SUBMIT, 56 | GUI_DIALOG 57 | }; 58 | 59 | class EditorState; 60 | class Dialog; 61 | class MenuState{ 62 | public: 63 | MenuState(EditorState* state); 64 | void init(); 65 | void draw(IVideoDriver* driver); 66 | bool OnEvent(const SEvent& event); 67 | EditorState *state; 68 | IGUIStaticText *sidebar; 69 | Dialog *dialog; 70 | private: 71 | IGUIContextMenu *projectMenubar; 72 | IGUIContextMenu* menubar; 73 | bool mode_icons_open; 74 | }; 75 | 76 | #endif 77 | -------------------------------------------------------------------------------- /src/common.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_HPP_INCLUDED 2 | #define COMMON_HPP_INCLUDED 3 | #include 4 | #include 5 | using namespace irr; 6 | using namespace core; 7 | using namespace scene; 8 | using namespace gui; 9 | using namespace video; 10 | 11 | // Enums 12 | enum ECUBE_SIDE 13 | { 14 | ECS_TOP = 0, 15 | ECS_BOTTOM, 16 | ECS_RIGHT, 17 | ECS_LEFT, 18 | ECS_BACK, 19 | ECS_FRONT 20 | }; 21 | 22 | enum EAxis 23 | { 24 | EAX_X, 25 | EAX_Y, 26 | EAX_Z 27 | }; 28 | 29 | enum EViewport 30 | { 31 | VIEW_NONE = -1, 32 | VIEW_TL, 33 | VIEW_TR, 34 | VIEW_BL, 35 | VIEW_BR 36 | }; 37 | 38 | enum EViewportType 39 | { 40 | VIEWT_PERS = 0, 41 | VIEWT_FRONT, 42 | VIEWT_LEFT, 43 | VIEWT_TOP, 44 | VIEWT_BACK, 45 | VIEWT_RIGHT, 46 | VIEWT_BOTTOM 47 | }; 48 | 49 | enum ECDR 50 | { 51 | CDR_L = 0, 52 | CDR_R, 53 | CDR_U, 54 | CDR_D, 55 | CDR_M 56 | }; 57 | 58 | enum ECDR_DIR 59 | { 60 | CDR_NONE = 0, 61 | CDR_X_P, 62 | CDR_X_N, 63 | CDR_Y_P, 64 | CDR_Y_N, 65 | CDR_Z_P, 66 | CDR_Z_N, 67 | CDR_XZ, 68 | CDR_XY, 69 | CDR_ZY 70 | }; 71 | 72 | // Defines 73 | #include "conf_cmake.hpp" 74 | #define EDITOR_VERSION 1 75 | #define EDITOR_PARSER 1 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /src/conf_cmake.hpp.in: -------------------------------------------------------------------------------- 1 | #ifndef CONF_CMAKE_HPP_INCLUDED 2 | #define CONF_CMAKE_HPP_INCLUDED 3 | 4 | #define EDITOR_TEXT_VERSION "@NBE_MAJOR_VERSION@.@NBE_MINOR_VERSION@.@NBE_PATCH_VERSION@" 5 | #define EDITOR_TEXT_VERSION_LABEL EDITOR_TEXT_VERSION " - @NBE_LABEL_VERSION@" 6 | 7 | #if @DEBUG@ 8 | #define _DEBUG 9 | #endif 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /src/dialogs/Dialog.hpp: -------------------------------------------------------------------------------- 1 | #ifndef DIALOG_HPP_INCLUDED 2 | #define DIALOG_HPP_INCLUDED 3 | #include "../common.hpp" 4 | #include "../EditorState.hpp" 5 | 6 | class EditorState; 7 | class Dialog : public IEventReceiver 8 | { 9 | public: 10 | Dialog(EditorState *mstate): 11 | state(mstate) 12 | {} 13 | 14 | virtual bool canClose() { return true; } 15 | virtual bool close() = 0; 16 | virtual bool OnEvent(const SEvent &event) { return false; } 17 | virtual void draw(IVideoDriver *driver) {} 18 | protected: 19 | EditorState *state; 20 | }; 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /src/dialogs/FileDialog.cpp: -------------------------------------------------------------------------------- 1 | #include "FileDialog.hpp" 2 | #include "../util/string.hpp" 3 | #include "../util/filesys.hpp" 4 | #include "../FileFormat/FileFormat.hpp" 5 | #include "../util/tinyfiledialogs.h" 6 | #include "../FileFormat/helpers.hpp" 7 | 8 | void FileDialog_open_project(EditorState *state) 9 | { 10 | std::string path = getSaveLoadDirectory(state->settings->get("save_directory"), 11 | state->isInstalled); 12 | 13 | const char* filters[] = {"*.nbe"}; 14 | const char *cfile = tinyfd_openFileDialog("Open Project", 15 | path.c_str(), 1, filters, 0); 16 | 17 | if (!cfile) 18 | return; 19 | 20 | std::string file = cfile; 21 | 22 | if (file == "") 23 | return; 24 | 25 | 26 | std::cerr << file.c_str() << std::endl; 27 | 28 | // Get file parser 29 | FileFormat *parser = getFromType(FILE_FORMAT_NBE, state); 30 | if (!parser) { 31 | state->device->getGUIEnvironment()->addMessageBox(L"Unable to open", 32 | L"File format does not exist. (It is based on file extensions)"); 33 | return; 34 | } 35 | 36 | // Get directory, and load# 37 | std::cerr << "Reading from " << file << std::endl; 38 | Project *tmp = parser->read(file); 39 | if (tmp) { 40 | if (state->project) 41 | delete state->project; 42 | state->project = tmp; 43 | state->project->SelectNode(0); 44 | state->Mode()->unload(); 45 | state->menu->init(); 46 | state->Mode()->load(); 47 | delete parser; 48 | parser = NULL; 49 | return; 50 | } else { 51 | switch(parser->error_code) { 52 | case EFFE_IO_ERROR: 53 | state->device->getGUIEnvironment()->addMessageBox(L"Unable to open", 54 | L"Failed to open the file\n\t(Does it not exist, or is it readonly?)"); 55 | break; 56 | case EFFE_READ_OLD_VERSION: 57 | state->device->getGUIEnvironment()->addMessageBox(L"Unable to open", 58 | L"This file is outdated and is not supported"); 59 | break; 60 | case EFFE_READ_NEW_VERSION: 61 | state->device->getGUIEnvironment()->addMessageBox(L"Unable to open", 62 | L"This file was created with a new version of NBE\n\t(Update your copy)"); 63 | break; 64 | case EFFE_READ_PARSE_ERROR: 65 | state->device->getGUIEnvironment()->addMessageBox(L"Unable to open", 66 | L"An error occurred while reading the file - it may be corrupted\n\t(This should never happen)"); 67 | break; 68 | case EFFE_READ_WRONG_TYPE: 69 | state->device->getGUIEnvironment()->addMessageBox(L"Unable to open", 70 | L"The file is not in the correct format\n\t(Are you opening the wrong type of file?)"); 71 | break; 72 | default: 73 | state->device->getGUIEnvironment()->addMessageBox(L"Unable to open", 74 | L"Unknown error"); 75 | break; 76 | } 77 | } 78 | } 79 | 80 | void FileDialog_import(EditorState *state) 81 | { 82 | std::string path = getSaveLoadDirectory(state->settings->get("save_directory"), 83 | state->isInstalled); 84 | 85 | const char* filters[] = {"*.nbe"}; 86 | const char *cfile = tinyfd_openFileDialog("Import Nodes", 87 | path.c_str(), 1, filters, 0); 88 | 89 | if (!cfile) 90 | return; 91 | 92 | std::string file = cfile; 93 | 94 | if (file == "") 95 | return; 96 | 97 | 98 | std::cerr << file.c_str() << std::endl; 99 | 100 | // Get file parser 101 | FileFormat *parser = getFromType(FILE_FORMAT_NBE, state); 102 | if (!parser) { 103 | state->device->getGUIEnvironment()->addMessageBox(L"Unable to open", 104 | L"File format does not exist."); 105 | return; 106 | } 107 | 108 | // Get directory, and load 109 | std::cerr << "Reading from " << file << std::endl; 110 | Project *tmp = parser->read(file, state->project); 111 | if (tmp) { 112 | state->project->remesh(); 113 | } else { 114 | switch(parser->error_code) { 115 | case EFFE_IO_ERROR: 116 | state->device->getGUIEnvironment()->addMessageBox(L"Unable to open", 117 | L"Failed to open the file\n\t(Does it not exist, or is it readonly?)"); 118 | break; 119 | case EFFE_READ_OLD_VERSION: 120 | state->device->getGUIEnvironment()->addMessageBox(L"Unable to open", 121 | L"This file is outdated and is not supported"); 122 | break; 123 | case EFFE_READ_NEW_VERSION: 124 | state->device->getGUIEnvironment()->addMessageBox(L"Unable to open", 125 | L"This file was created with a new version of NBE\n\t(Update your copy)"); 126 | break; 127 | case EFFE_READ_PARSE_ERROR: 128 | state->device->getGUIEnvironment()->addMessageBox(L"Unable to open", 129 | L"An error occurred while reading the file - it may be corrupted\n\t(This should never happen)"); 130 | break; 131 | case EFFE_READ_WRONG_TYPE: 132 | state->device->getGUIEnvironment()->addMessageBox(L"Unable to open", 133 | L"The file is not in the correct format\n\t(Are you opening the wrong type of file?)"); 134 | break; 135 | default: 136 | state->device->getGUIEnvironment()->addMessageBox(L"Unable to open", 137 | L"Unknown error"); 138 | break; 139 | } 140 | delete parser; 141 | parser = NULL; 142 | } 143 | } 144 | 145 | void FileDialog_save_project(EditorState *state) 146 | { 147 | // Get path 148 | std::string path = getSaveLoadDirectory(state->settings->get("save_directory"), 149 | state->isInstalled); 150 | 151 | const char* filters[] = {"*.nbe"}; 152 | const char *cfile = tinyfd_saveFileDialog("Save Project", path.c_str(), 153 | 1, filters); 154 | 155 | if (!cfile) 156 | return; 157 | 158 | std::string file = cfile; 159 | 160 | if (file == "") 161 | return; 162 | 163 | std::cerr << file << std::endl; 164 | 165 | FileFormat *writer = getFromType(FILE_FORMAT_NBE, state); 166 | save_file(writer, state, file); 167 | } 168 | 169 | void FileDialog_export(EditorState *state, int parser) 170 | { 171 | // Get path 172 | std::string path = getSaveLoadDirectory(state->settings->get("save_directory"), 173 | state->isInstalled); 174 | 175 | const char* filters[] = {""}; 176 | 177 | if (parser == (int)FILE_FORMAT_LUA) 178 | filters[0] = "*.lua"; 179 | else if (parser == (int)FILE_FORMAT_CPP) 180 | filters[0] = "*.cpp"; 181 | 182 | const char *cfile = tinyfd_saveFileDialog("Save Project", path.c_str(), 183 | 1, filters); 184 | 185 | if (!cfile) 186 | return; 187 | 188 | std::string file = cfile; 189 | 190 | if (file == "") 191 | return; 192 | 193 | std::cerr << file << std::endl; 194 | 195 | FileFormat *writer = getFromType((FileFormatType)parser, state); 196 | save_file(writer, state, file); 197 | } 198 | 199 | #include "../FileFormat/obj.hpp" 200 | #include 201 | 202 | void FileDialog_export_obj(EditorState *state, Node *node) 203 | { 204 | // Get path 205 | std::string path = getSaveLoadDirectory(state->settings->get("save_directory"), 206 | state->isInstalled); 207 | 208 | const char* filters[] = {"*.obj"}; 209 | 210 | const char *cfile = tinyfd_saveFileDialog("Export Node to Mesh", path.c_str(), 211 | 1, filters); 212 | 213 | if (!cfile) 214 | return; 215 | 216 | std::string filename = cfile; 217 | 218 | if (filename == "") 219 | return; 220 | 221 | std::string res = nodeToObj(node, filenameWithoutExt(filename)); 222 | std::ofstream file(filename.c_str()); 223 | if (!file) 224 | return; 225 | file << res.c_str(); 226 | file.close(); 227 | } 228 | 229 | void FileDialog_export_mod(EditorState *state) 230 | { 231 | std::string path = getSaveLoadDirectory(state->settings->get("save_directory"), 232 | state->isInstalled); 233 | 234 | const char *cdir = tinyfd_selectFolderDialog ("Select Folder", path.c_str()); 235 | 236 | if (!cdir) 237 | return; 238 | 239 | std::string dir = trim(cdir); 240 | 241 | if (dir == "") 242 | return; 243 | 244 | dir = cleanDirectoryPath(dir); 245 | export_textures(dir + "textures/", state); 246 | 247 | FileFormat *writer = getFromType(FILE_FORMAT_LUA, state); 248 | save_file(writer, state, dir + "init.lua"); 249 | } 250 | 251 | void FileDialog_export_textures(EditorState *state) 252 | { 253 | std::string path = getSaveLoadDirectory(state->settings->get("save_directory"), 254 | state->isInstalled); 255 | 256 | const char *cdir = tinyfd_selectFolderDialog ("Select Folder", path.c_str()); 257 | 258 | if (!cdir) 259 | return; 260 | 261 | std::string dir = trim(cdir); 262 | 263 | if (dir == "") 264 | return; 265 | 266 | dir = cleanDirectoryPath(dir); 267 | export_textures(dir, state); 268 | } 269 | -------------------------------------------------------------------------------- /src/dialogs/FileDialog.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FILEDIALOG_HPP_INCLUDED 2 | #define FILEDIALOG_HPP_INCLUDED 3 | #include "../common.hpp" 4 | #include "../EditorState.hpp" 5 | 6 | extern void FileDialog_open_project(EditorState *state); 7 | extern void FileDialog_save_project(EditorState *state); 8 | extern void FileDialog_import(EditorState *state); 9 | extern void FileDialog_export(EditorState *state, int parser); 10 | extern void FileDialog_export_obj(EditorState *state, Node *node); 11 | extern void FileDialog_export_mod(EditorState *state); 12 | extern void FileDialog_export_textures(EditorState *state); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /src/dialogs/ImageDialog.cpp: -------------------------------------------------------------------------------- 1 | #include "ImageDialog.hpp" 2 | #include "../util/string.hpp" 3 | #include "../util/filesys.hpp" 4 | #include "TextureDialog.hpp" 5 | #include "../util/tinyfiledialogs.h" 6 | 7 | enum IMAGE_DIALOG_GUI_IDS 8 | { 9 | EID_GUI_ID_BROWSE = GUI_DIALOG, 10 | EID_GUI_ID_NAME_AS, 11 | EID_GUI_ID_OVERWRITE 12 | }; 13 | 14 | ImageDialog::ImageDialog(EditorState *mstate, Node *tnode, ECUBE_SIDE tface): 15 | Dialog(mstate), 16 | node(tnode), 17 | face(tface) 18 | { 19 | IGUIEnvironment *guienv = state->device->getGUIEnvironment(); 20 | win = guienv->addWindow(rect(340, 50, 669, 50+180), true, L"Import Image"); 21 | guienv->addEditBox(L"", rect(10, 30, 240, 60), 22 | true, win, GUI_FILEDIALOG_PATH); 23 | guienv->addEditBox(L"", rect(10, 70, 240, 100), 24 | true, win, EID_GUI_ID_NAME_AS); 25 | guienv->addButton(rect(250, 30, 320, 60), win, EID_GUI_ID_BROWSE, L"Browse"); 26 | guienv->addCheckBox(false, rect(30, 110, 270, 130), win, 27 | EID_GUI_ID_OVERWRITE, L"Overwrite Existing"); 28 | guienv->addButton(rect(130, 140, 200, 170), win, GUI_DIALOG_SUBMIT, L"Import"); 29 | } 30 | 31 | 32 | bool ImageDialog::show(EditorState *mstate, Node *node, ECUBE_SIDE face) 33 | { 34 | // Check if last can close. 35 | if (mstate->menu->dialog != NULL) { 36 | if (mstate->menu->dialog->canClose()) { 37 | mstate->menu->dialog->close(); 38 | } else { 39 | return false; 40 | } 41 | } 42 | 43 | // Create new 44 | ImageDialog *dialog = new ImageDialog(mstate, node, face); 45 | if (!dialog) { 46 | std::cerr << "Failed to create dialog!" << std::endl; 47 | return false; 48 | } 49 | mstate->menu->dialog = dialog; 50 | return true; 51 | } 52 | 53 | bool ImageDialog::canClose() 54 | { 55 | return true; 56 | } 57 | 58 | bool ImageDialog::close() 59 | { 60 | if (win) { 61 | win->remove(); 62 | win = NULL; 63 | } 64 | state->menu->dialog = NULL; 65 | delete this; 66 | return true; 67 | } 68 | 69 | bool ImageDialog::OnEvent(const SEvent &event) 70 | { 71 | if (event.EventType != EET_GUI_EVENT) 72 | return false; 73 | 74 | if (event.GUIEvent.EventType == EGET_ELEMENT_CLOSED && event.GUIEvent.Caller == win) { 75 | if (canClose()) 76 | close(); 77 | return true; 78 | } 79 | 80 | // 81 | // On 'import' clicked 82 | // 83 | if (event.GUIEvent.EventType == EGET_BUTTON_CLICKED) { 84 | if (event.GUIEvent.Caller->getID() == EID_GUI_ID_BROWSE) { 85 | std::string path = getSaveLoadDirectory(state->settings->get("save_directory"), 86 | state->isInstalled); 87 | const char* filters[] = {"*.png", "*.jpg", "*.gif", "*.jpeg"}; 88 | const char *cfile = tinyfd_openFileDialog("Select Image", 89 | path.c_str(), 1, filters, 0); 90 | 91 | if (!cfile) 92 | return false; 93 | 94 | std::string file = cfile; 95 | 96 | if (file == "") 97 | return false; 98 | 99 | 100 | IGUIEditBox *box = static_cast(win->getElementFromId(GUI_FILEDIALOG_PATH)); 101 | IGUIEditBox *nameas = static_cast(win->getElementFromId(EID_GUI_ID_NAME_AS)); 102 | 103 | box->setText(narrow_to_wide(file).c_str()); 104 | nameas->setText(narrow_to_wide(filenameWithExt(file)).c_str()); 105 | } else if (event.GUIEvent.Caller->getID() == GUI_DIALOG_SUBMIT) { 106 | IGUIEditBox *box = static_cast(win->getElementFromId(GUI_FILEDIALOG_PATH)); 107 | IGUIEditBox *nameas = static_cast(win->getElementFromId(EID_GUI_ID_NAME_AS)); 108 | IGUICheckBox *cb = static_cast(win->getElementFromId(EID_GUI_ID_OVERWRITE)); 109 | irr::core::stringc t = box->getText(); 110 | irr::core::stringc t2 = nameas->getText(); 111 | 112 | // Get path and filename 113 | std::string path(t.c_str(), t.size()); 114 | std::string shortname(t2.c_str(), t2.size()); 115 | path = trim(path); 116 | std::cerr << path.c_str() << std::endl; 117 | 118 | // Check for existance in file manager 119 | bool already_exists = (state->project->media.get(shortname.c_str()) != NULL); 120 | 121 | // Do import or show warning 122 | if (!already_exists || cb->isChecked()) { 123 | IImage *image = state->device->getVideoDriver()->createImageFromFile(path.c_str()); 124 | if (!image) { 125 | state->device->getGUIEnvironment()->addMessageBox(L"Unable to import", 126 | L"Failed to open the image\n\t(Does it not exist, or is it readonly?)"); 127 | return true; 128 | } else if (!state->project->media.add(path.c_str(), shortname, image, cb->isChecked())) { 129 | state->device->getGUIEnvironment()->addMessageBox(L"Unable to import", 130 | L"Failed to add the image\n\t(Unknown reason)"); 131 | return true; 132 | } 133 | state->project->remesh(); 134 | if (node) 135 | TextureDialog::show(state, node, face); 136 | else 137 | close(); 138 | return true; 139 | } else { 140 | state->device->getGUIEnvironment()->addMessageBox(L"Unable to import", 141 | L"An image with the same filename has already been imported.\n\t(Change nameas or click overwrite.)"); 142 | } 143 | } 144 | } 145 | return false; 146 | } 147 | -------------------------------------------------------------------------------- /src/dialogs/ImageDialog.hpp: -------------------------------------------------------------------------------- 1 | #ifndef IMAGEDIALOG_HPP_INCLUDED 2 | #define IMAGEDIALOG_HPP_INCLUDED 3 | #include "Dialog.hpp" 4 | 5 | class ImageDialog : public Dialog 6 | { 7 | public: 8 | ImageDialog(EditorState *mstate, Node *tnode, ECUBE_SIDE tface); 9 | static bool show(EditorState *mstate, Node *node, ECUBE_SIDE face); 10 | void doSave(const SEvent &event); 11 | void doOpen(const SEvent &event); 12 | virtual bool canClose(); 13 | virtual bool close(); 14 | virtual bool OnEvent(const SEvent &event); 15 | private: 16 | IGUIWindow *win; 17 | Node *node; 18 | ECUBE_SIDE face; 19 | }; 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /src/dialogs/TextureDialog.cpp: -------------------------------------------------------------------------------- 1 | #include "TextureDialog.hpp" 2 | #include "../util/string.hpp" 3 | #include "ImageDialog.hpp" 4 | #include "../util/filesys.hpp" 5 | #include "../util/tinyfiledialogs.h" 6 | 7 | enum TEXTURE_DIALOG_GUI_IDS 8 | { 9 | ETD_GUI_ID_APPLY = GUI_DIALOG, 10 | ETD_GUI_ID_IMPORT, 11 | ETD_GUI_ID_ACTIONS, 12 | ETD_GUI_ID_ACTIONS_CM, 13 | ETD_GUI_ID_EXPORT 14 | }; 15 | 16 | const char* getECUBE_SIDEName(ECUBE_SIDE face) 17 | { 18 | switch (face) { 19 | case ECS_TOP: 20 | return "top"; 21 | case ECS_BOTTOM: 22 | return "bottom"; 23 | case ECS_RIGHT: 24 | return "right"; 25 | case ECS_LEFT: 26 | return "left"; 27 | case ECS_FRONT: 28 | return "front"; 29 | case ECS_BACK: 30 | return "back"; 31 | default: 32 | std::cerr << "Error in getECUBE_SIDEName() - Unknown cubeside given" << std::endl; 33 | return ":/"; 34 | } 35 | } 36 | 37 | TextureDialog::TextureDialog(EditorState *pstate, Node *pnode, ECUBE_SIDE pface): 38 | Dialog(pstate), 39 | node(pnode), 40 | face(pface), 41 | lb(NULL), 42 | the_image(NULL), 43 | context(NULL) 44 | { 45 | IVideoDriver *driver = state->device->getVideoDriver(); 46 | IGUIEnvironment *guienv = state->device->getGUIEnvironment(); 47 | 48 | // Window and basic items 49 | win = guienv->addWindow(rect(340, 50, 340 + 74 * 3 + 10, 50 + 74 * 3 + 10), true, 50 | narrow_to_wide(std::string(getECUBE_SIDEName(face)) + " texture").c_str()); 51 | guienv->addButton(rect(155, 30, 74*3, 55), win, ETD_GUI_ID_APPLY, L"Apply", L"Apply this texture selection to the node face"); 52 | guienv->addButton(rect(155, 60, 74*3, 85), win, ETD_GUI_ID_IMPORT, L"Import", L"Import images from files"); 53 | guienv->addButton(rect(84, 60, 150, 85), win, ETD_GUI_ID_ACTIONS, L"Actions"); 54 | 55 | // Fill out listbox 56 | lb = guienv->addListBox(rect(10, 104, 74 * 3, 74 * 3), win, 502); 57 | Media *media = &state->project->media; 58 | std::map& images = media->getList(); 59 | int count = 1; 60 | lb->addItem(L""); 61 | lb->setSelected(0); 62 | for (std::map::const_iterator it = images.begin(); 63 | it != images.end(); 64 | ++it) { 65 | if (!it->second) { 66 | continue; 67 | } 68 | if (it->second->name == "default") { 69 | lb->addItem(L""); 70 | } else { 71 | lb->addItem(narrow_to_wide(it->second->name + " [used " + 72 | num_to_str(it->second->getHolders()) + " times]").c_str()); 73 | } 74 | if (it->second == node->getTexture(face)) 75 | lb->setSelected(count); 76 | count++; 77 | } 78 | 79 | Media::Image *image = node->getTexture(face); 80 | if (image) { 81 | if (the_image) 82 | driver->removeTexture(the_image); 83 | the_image = driver->addTexture("tmpicon.png", image->get()); 84 | } 85 | 86 | // Context menu 87 | context = guienv->addContextMenu(rect(84, 85, 150, 180), win, ETD_GUI_ID_ACTIONS_CM); 88 | context->addItem(L"Export", ETD_GUI_ID_EXPORT); 89 | context->setCloseHandling(ECMC_HIDE); 90 | context->setVisible(false); 91 | context->setEventParent(win); 92 | } 93 | 94 | void TextureDialog::draw(IVideoDriver *driver) 95 | { 96 | int x = win->getAbsolutePosition().UpperLeftCorner.X + 10; 97 | int y = win->getAbsolutePosition().UpperLeftCorner.Y + 30; 98 | if (!the_image) { 99 | driver->draw2DRectangle(SColor(100, 0, 0, 0), rect(x, y, x + 64, y + 64)); 100 | } else { 101 | driver->draw2DImage(the_image, rect(x, y, x + 64, y + 64), 102 | rect(0, 0, the_image->getSize().Width, the_image->getSize().Height)); 103 | } 104 | } 105 | 106 | 107 | bool TextureDialog::show(EditorState *mstate, Node *pnode, ECUBE_SIDE pface) 108 | { 109 | // Check if last can close. 110 | if (mstate->menu->dialog != NULL) { 111 | if (mstate->menu->dialog->canClose()) { 112 | mstate->menu->dialog->close(); 113 | } else { 114 | return false; 115 | } 116 | } 117 | 118 | // Create new 119 | TextureDialog *dialog = new TextureDialog(mstate, pnode, pface); 120 | if (!dialog) { 121 | std::cerr << "Failed to create dialog!" << std::endl; 122 | return false; 123 | } 124 | mstate->menu->dialog = dialog; 125 | return true; 126 | } 127 | 128 | bool TextureDialog::canClose() 129 | { 130 | return true; 131 | } 132 | 133 | bool TextureDialog::close() 134 | { 135 | IVideoDriver *driver = state->device->getVideoDriver(); 136 | if (the_image) 137 | driver->removeTexture(the_image); 138 | win->remove(); 139 | state->menu->dialog = NULL; 140 | delete this; 141 | return true; 142 | } 143 | 144 | bool TextureDialog::OnEvent(const SEvent &event) 145 | { 146 | if (event.EventType != EET_GUI_EVENT) 147 | return false; 148 | 149 | IVideoDriver *driver = state->device->getVideoDriver(); 150 | 151 | if (event.GUIEvent.EventType == EGET_BUTTON_CLICKED) { 152 | switch (event.GUIEvent.Caller->getID()) { 153 | case ETD_GUI_ID_APPLY: { 154 | if (lb->getSelected() == 0) { 155 | node->setTexture(face, NULL); 156 | node->remesh(true); 157 | return true; 158 | } 159 | 160 | int count = 0; 161 | Media *media = &state->project->media; 162 | std::map& images = media->getList(); 163 | for (std::map::const_iterator it = images.begin(); 164 | it != images.end(); 165 | ++it) { 166 | if (count == lb->getSelected()-1) { 167 | node->setTexture(face, it->second); 168 | node->remesh(true); 169 | break; 170 | } 171 | count++; 172 | } 173 | 174 | 175 | close(); 176 | return true; 177 | } 178 | case ETD_GUI_ID_IMPORT: { 179 | ImageDialog::show(state, node, face); 180 | return false; 181 | } 182 | case ETD_GUI_ID_ACTIONS: 183 | context->setVisible(true); 184 | state->device->getGUIEnvironment()->setFocus(context); 185 | return false; 186 | } 187 | } else if (event.GUIEvent.EventType == EGET_MENU_ITEM_SELECTED) { 188 | IGUIContextMenu *menu = (IGUIContextMenu *)event.GUIEvent.Caller; 189 | switch (menu->getItemCommandId(menu->getSelectedItem())) { 190 | case ETD_GUI_ID_EXPORT: { 191 | if (lb->getSelected() == 0) 192 | return true; 193 | 194 | int count = 0; 195 | Media *media = &state->project->media; 196 | Media::Image *image = NULL; 197 | std::map& images = media->getList(); 198 | for (std::map::const_iterator it = images.begin(); 199 | it != images.end(); 200 | ++it) { 201 | if (count == lb->getSelected() - 1) { 202 | image = it->second; 203 | break; 204 | } 205 | count++; 206 | } 207 | 208 | if (!image) 209 | return true; 210 | 211 | std::string path = getSaveLoadDirectory(state->settings->get("save_directory"), 212 | state->isInstalled); 213 | path += image->name; 214 | 215 | const char *filters[] = {"*.png"}; 216 | const char *cfilename = tinyfd_saveFileDialog("Save Image", path.c_str(), 217 | 1, filters); 218 | if (!cfilename) 219 | return true; 220 | 221 | std::string filename = cfilename; 222 | 223 | if (filename == "") 224 | return true; 225 | 226 | state->device->getVideoDriver()->writeImageToFile(image->get(), 227 | filename.c_str()); 228 | 229 | return true; 230 | }} // end of switch 231 | } else if (event.GUIEvent.EventType == EGET_LISTBOX_CHANGED && event.GUIEvent.Caller == lb) { 232 | if (lb->getSelected() == 0) { 233 | if (the_image) 234 | driver->removeTexture(the_image); 235 | the_image = NULL; 236 | return true; 237 | } 238 | 239 | int count = 0; 240 | Media *media = &state->project->media; 241 | std::map& images = media->getList(); 242 | for (std::map::const_iterator it = images.begin(); 243 | it != images.end(); 244 | ++it) { 245 | if (count == lb->getSelected()-1) { 246 | if (the_image) 247 | driver->removeTexture(the_image); 248 | the_image = state->device->getVideoDriver()->addTexture("tmpicon.png", it->second->get()); 249 | break; 250 | } 251 | count++; 252 | } 253 | return true; 254 | } else if (event.GUIEvent.EventType == EGET_ELEMENT_CLOSED && event.GUIEvent.Caller == win) { 255 | if (canClose()) 256 | close(); 257 | return true; 258 | } 259 | return false; 260 | } 261 | -------------------------------------------------------------------------------- /src/dialogs/TextureDialog.hpp: -------------------------------------------------------------------------------- 1 | #ifndef TEXTUREDIALOG_HPP_INCLUDED 2 | #define TEXTUREDIALOG_HPP_INCLUDED 3 | #include "Dialog.hpp" 4 | 5 | class TextureDialog : public Dialog 6 | { 7 | public: 8 | TextureDialog(EditorState *pstate, Node *pnode, ECUBE_SIDE pface); 9 | static bool show(EditorState *pstate, Node *pnode, ECUBE_SIDE pface); 10 | virtual bool canClose(); 11 | virtual bool close(); 12 | virtual bool OnEvent(const SEvent &event); 13 | virtual void draw(IVideoDriver *driver); 14 | private: 15 | Node *node; 16 | ECUBE_SIDE face; 17 | IGUIWindow *win; 18 | IGUIListBox *lb; 19 | ITexture *the_image; 20 | IGUIContextMenu *context; 21 | }; 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "util/string.hpp" 5 | #include "util/filesys.hpp" 6 | #include "common.hpp" 7 | #include "Editor.hpp" 8 | 9 | #ifdef _MSC_VER 10 | #pragma comment(lib, "Irrlicht.lib") 11 | #ifndef _DEBUG 12 | #pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup") 13 | #endif 14 | #endif 15 | 16 | #ifndef _WIN32 17 | #include 18 | #include 19 | 20 | void findWorkingDirectory(bool &editor_is_installed) 21 | { 22 | std::cerr << "Looking for the working directory..." << std::endl; 23 | if (FileExists("media/sky.jpg")) 24 | return; 25 | 26 | chdir("../"); 27 | if (FileExists("media/sky.jpg")) 28 | return; 29 | 30 | chdir("share/nodeboxeditor"); 31 | if (FileExists("media/sky.jpg")) { 32 | std::cerr << "Is installed!"; 33 | editor_is_installed = true; 34 | return; 35 | } 36 | 37 | chdir("/usr/share/nodeboxeditor"); 38 | if (FileExists("media/sky.jpg")) { 39 | std::cerr << "Is installed!"; 40 | editor_is_installed = true; 41 | return; 42 | } 43 | 44 | chdir("/usr/local/share/nodeboxeditor"); 45 | if (FileExists("media/sky.jpg")) { 46 | std::cerr << "Is installed!"; 47 | editor_is_installed = true; 48 | return; 49 | } 50 | 51 | char buff[PATH_MAX]; 52 | ssize_t len = ::readlink("/proc/self/exe", buff, sizeof(buff) - 1); 53 | if (len != -1) { 54 | buff[len] = '\0'; 55 | std::string path(buff); 56 | path = pathWithoutFilename(path); 57 | std::cerr << path << std::endl; 58 | chdir(path.c_str()); 59 | if (FileExists("media/sky.jpg")) 60 | return; 61 | 62 | chdir("../"); 63 | if (FileExists("media/sky.jpg")) 64 | return; 65 | } 66 | 67 | std::cerr << "Can't find the working directory!" << std::endl; 68 | } 69 | #endif // ifndef _WIN32 70 | 71 | int main(int argc, char *argv[]) { 72 | std::cerr << 73 | " _ _ _ ____ _____ _ _ _ \n" 74 | "| \\ | | ___ __| | ___ | __ ) _____ __ | ____|__| (_) |_ ___ _ __ \n" 75 | "| \\| |/ _ \\ / _` |/ _ \\ | _ \\ / _ \\ \\/ / | _| / _` | | __/ _ \\| '__|\n" 76 | "| |\\ | (_) | (_| | __/ | |_) | (_) > < | |__| (_| | | || (_) | | \n" 77 | "|_| \\_|\\___/ \\__,_|\\___| |____/ \\___/_/\\_\\ |_____\\__,_|_|\\__\\___/|_| \n\n" 78 | << std::endl; 79 | 80 | #ifdef _DEBUG 81 | std::cerr << "Debug mode enabled!" << std::endl; 82 | #endif 83 | 84 | 85 | // Find the working directory 86 | bool editor_is_installed = false; 87 | #ifndef _WIN32 88 | findWorkingDirectory(editor_is_installed); 89 | #endif 90 | 91 | // Settings 92 | Configuration* conf = new Configuration(); 93 | if (conf == NULL) { 94 | return EXIT_FAILURE; 95 | } 96 | conf->set("snapping", "true"); 97 | conf->set("default_snap_res", "16"); 98 | conf->set("limiting", "true"); 99 | conf->set("fractional_positions", "false"); 100 | conf->set("driver", "opengl"); 101 | conf->set("hide_sidebar", "false"); 102 | conf->set("save_directory", ""); 103 | conf->set("minetest_root", ""); 104 | conf->set("always_show_position_handle", "false"); 105 | #ifdef _WIN32 106 | conf->set("vsync", "false"); 107 | conf->set("use_sleep", "true"); 108 | #else 109 | conf->set("vsync", "true"); 110 | conf->set("use_sleep", "false"); 111 | #endif 112 | conf->set("viewport_top_left", "pers"); 113 | conf->set("viewport_top_right", "top"); 114 | conf->set("viewport_bottom_left", "front"); 115 | conf->set("viewport_bottom_right", "right"); 116 | conf->set("lighting", "2"); 117 | conf->set("hide_other_nodes", "true"); 118 | conf->set("no_negative_node_y", "true"); 119 | conf->set("fullscreen", "false"); 120 | conf->set("width", "896"); 121 | conf->set("height", "520"); 122 | if (!editor_is_installed) 123 | conf->load("editor.conf"); 124 | else 125 | if (!conf->load(std::string(getSaveLoadDirectory("", true)) + ".config/nodeboxeditor.conf")) 126 | conf->load("editor.conf"); 127 | 128 | // Set up irrlicht device 129 | E_DRIVER_TYPE driv = irr::video::EDT_OPENGL; 130 | 131 | const std::string confDriver = str_to_lower(conf->get("driver")); 132 | #ifdef _IRR_COMPILE_WITH_DIRECT3D_8_ 133 | if (confDriver == "directx8") { 134 | driv = EDT_DIRECT3D8; 135 | } else 136 | #endif 137 | #ifdef _IRR_COMPILE_WITH_DIRECT3D_9_ 138 | if (confDriver == "directx9") { 139 | driv = EDT_DIRECT3D9; 140 | } else 141 | #endif 142 | if (confDriver == "software") { 143 | driv = EDT_SOFTWARE; 144 | } 145 | 146 | // Start Irrlicht 147 | int w = conf->getInt("width"); 148 | int h = conf->getInt("height"); 149 | if (w < 1) w = 896; 150 | if (h < 1) h = 520; 151 | 152 | if (!conf->getBool("vsync")) { 153 | std::cerr << "[WARNING] You have disabled vsync. Expect major CPU usage!" << std::endl; 154 | } 155 | irr::IrrlichtDevice* device = irr::createDevice( 156 | driv, 157 | irr::core::dimension2d(w,h), 158 | 16U, 159 | conf->getBool("fullscreen"), 160 | false, 161 | conf->getBool("vsync") 162 | ); 163 | if (device == NULL) { 164 | return EXIT_FAILURE; // could not create selected driver. 165 | } 166 | 167 | #if IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8 168 | std::cerr << "Warning! Your Irrlicht version is outdated, so some NBE features will not be available." << std::endl; 169 | #endif 170 | 171 | // Editor 172 | Editor* editor = new Editor(); 173 | editor->run(device, conf, editor_is_installed); 174 | 175 | if (!editor_is_installed) 176 | conf->save("editor.conf"); 177 | else 178 | if (!conf->save(std::string(getSaveLoadDirectory("", true)) + ".config/nodeboxeditor.conf")) 179 | conf->save("editor.conf"); 180 | 181 | return 1; 182 | } 183 | 184 | #ifndef _WIN32 185 | // Fix for Inconsistency detected by ld.so 186 | #include 187 | void junk() { 188 | int i; 189 | i = pthread_getconcurrency(); 190 | }; 191 | #endif 192 | -------------------------------------------------------------------------------- /src/minetest.cpp: -------------------------------------------------------------------------------- 1 | #include "minetest.hpp" 2 | #include "util/string.hpp" 3 | #include "util/filesys.hpp" 4 | #include "FileFormat/FileFormat.hpp" 5 | #include "FileFormat/helpers.hpp" 6 | #include 7 | #include 8 | 9 | #ifdef _WIN32 10 | #include 11 | #define getcwd _getcwd 12 | #else 13 | #include 14 | #endif 15 | 16 | Minetest::Minetest(Configuration *conf): 17 | _conf(conf), minetest_dir(""), minetest_exe("") 18 | {} 19 | 20 | bool Minetest::findMinetestDir(std::string path) 21 | { 22 | if (DirExists(path.c_str())) { 23 | std::cerr << "Minetest found at " << path.c_str() << std::endl; 24 | minetest_dir = path; 25 | 26 | if (FileExists((path + "bin" + DIR_DELIM + "minetest" 27 | #if _WIN32 28 | ".exe" 29 | #endif 30 | ).c_str())) { 31 | minetest_exe = path + "bin" + DIR_DELIM + "minetest" 32 | #if _WIN32 33 | ".exe" 34 | #endif 35 | ; 36 | } else 37 | std::cerr << "...but no executable!" << std::endl; 38 | return true; 39 | } 40 | return false; 41 | } 42 | 43 | bool Minetest::findMinetest(bool editor_is_installed) 44 | { 45 | std::cerr << "Searching for Minetest using minetest_root setting.." << std::endl; 46 | std::string path = _conf->get("minetest_root"); 47 | path = cleanDirectoryPath(path); 48 | if (path != "") { 49 | if (findMinetestDir(path) && minetest_exe != "") 50 | return true; 51 | 52 | std::cerr << "Minetest not found in the path given, not trying anywhere else." << std::endl; 53 | return false; 54 | } 55 | 56 | #ifndef _WIN32 57 | std::cerr << "Searching for Minetest in system-wide..." << std::endl; 58 | path = getenv("HOME"); 59 | path += "/.minetest/"; 60 | if (findMinetestDir(path)) { 61 | if (minetest_exe == "") { 62 | if (FileExists("/usr/local/bin/minetest")) { 63 | minetest_exe = "/usr/local/bin/minetest"; 64 | return true; 65 | } else if (FileExists("/usr/bin/minetest")) { 66 | minetest_exe = "/usr/bin/minetest"; 67 | return true; 68 | } else 69 | std::cerr << "~/.minetest/ exists, but no exe in /usr/bin/ or /usr/local/bin/" << std::endl; 70 | } 71 | } 72 | #endif 73 | 74 | std::cerr << "Searching for Minetest relative to NBE save directory..." << std::endl; 75 | path = getSaveLoadDirectory(_conf->get("save_directory"), editor_is_installed); 76 | path = cleanDirectoryPath(path); 77 | 78 | // minetest/ 79 | if (findMinetestDir(path + "minetest" + DIR_DELIM) && minetest_exe != "") 80 | return true; 81 | 82 | // ../minetest 83 | if (findMinetestDir(path + ".." + DIR_DELIM + "minetest" + DIR_DELIM) && minetest_exe != "") 84 | return true; 85 | 86 | // Minetest/ 87 | if (findMinetestDir(path + "Minetest" + DIR_DELIM) && minetest_exe != "") 88 | return true; 89 | 90 | // ../Minetest/ 91 | if (findMinetestDir(path + ".." + DIR_DELIM + "Minetest" + DIR_DELIM) && minetest_exe != "") 92 | return true; 93 | 94 | // games/minetest/ 95 | if (findMinetestDir(path + "games" + DIR_DELIM + "minetest" + DIR_DELIM) && minetest_exe != "") 96 | return true; 97 | 98 | // ../games/minetest/ 99 | if (findMinetestDir(path + ".." + DIR_DELIM + "games" + DIR_DELIM + "minetest" + DIR_DELIM) && minetest_exe != "") 100 | return true; 101 | 102 | // apps/minetest/ 103 | if (findMinetestDir(path + "apps" + DIR_DELIM + "minetest" + DIR_DELIM) && minetest_exe != "") 104 | return true; 105 | 106 | // ../apps/minetest/ 107 | if (findMinetestDir(path + ".." + DIR_DELIM + "apps" + DIR_DELIM + "minetest" + DIR_DELIM) && minetest_exe != "") 108 | return true; 109 | 110 | std::cerr << "Minetest not found!" << std::endl; 111 | return false; 112 | } 113 | 114 | bool Minetest::runMod(EditorState *state, const std::string &world) 115 | { 116 | std::string worlddir = minetest_dir + "worlds" + DIR_DELIM + world + DIR_DELIM; 117 | std::string modname = state->project->name; 118 | if (DirExists(worlddir.c_str())) { 119 | // Open file 120 | const char *filename = (worlddir + "world.mt").c_str(); 121 | std::ifstream file(filename); 122 | if (file) { 123 | std::string line; 124 | std::string search_for = std::string("load_mod_") + modname.c_str(); 125 | bool found = false; 126 | while (std::getline(file, line)) { 127 | if (trim(line) == search_for) { 128 | found = true; 129 | break; 130 | } 131 | } 132 | file.close(); 133 | 134 | if (!found) { 135 | std::ofstream outfile; 136 | outfile.open(filename, std::ios_base::app); 137 | outfile << "\n" << search_for << "\n"; 138 | outfile.close(); 139 | } 140 | } else { 141 | std::ofstream outfile(filename); 142 | outfile << "gameid = minetest\n"; 143 | outfile << "backend = sqlite3\n"; 144 | outfile << "load_mod_" << modname.c_str() << " = true\n"; 145 | outfile.close(); 146 | } 147 | } else { 148 | // Create new world folder 149 | CreateDir(worlddir); 150 | std::ofstream file((worlddir + "world.mt").c_str()); 151 | file << "gameid = minetest\n"; 152 | file << "backend = sqlite3\n"; 153 | file << "load_mod_" << modname.c_str() << " = true\n"; 154 | file.close(); 155 | } 156 | 157 | // Create mod folder in world 158 | std::string cmd = worlddir + "worldmods"; 159 | CreateDir(cmd); 160 | 161 | // Export Mod 162 | std::string mod_to = worlddir + "worldmods" + DIR_DELIM + modname; 163 | CreateDir(mod_to); 164 | mod_to = cleanDirectoryPath(mod_to); 165 | export_textures(mod_to + "textures" + DIR_DELIM, state); 166 | FileFormat *writer = getFromType(FILE_FORMAT_LUA, state); 167 | save_file(writer, state, mod_to + "init.lua"); 168 | 169 | // Change working directory 170 | char buffer[300]; 171 | char *prev_cwd = getcwd(buffer, sizeof(buffer)); 172 | if (!prev_cwd) { 173 | std::cerr << "Failed to get CWD" << std::endl; 174 | return false; 175 | } 176 | 177 | // Run minetest 178 | chdir(minetest_dir.c_str()); 179 | std::string args = "--worldname " + world + " --name tester --address '' --go"; 180 | std::cerr << "Starting Minetest with " << args.c_str() << std::endl; 181 | system((minetest_exe + " " + args).c_str()); 182 | 183 | // Change directory back 184 | chdir(prev_cwd); 185 | 186 | return true; 187 | } 188 | -------------------------------------------------------------------------------- /src/minetest.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MINETEST_HPP_INCLUDED 2 | #define MINETEST_HPP_INCLUDED 3 | 4 | #include "common.hpp" 5 | #include "EditorState.hpp" 6 | #include "Configuration.hpp" 7 | 8 | class Minetest 9 | { 10 | public: 11 | Minetest(Configuration *conf); 12 | bool findMinetest(bool editor_is_installed); 13 | bool runMod(EditorState *state, const std::string &world = "nbe_test"); 14 | private: 15 | bool findMinetestDir(std::string path); 16 | 17 | Configuration *_conf; 18 | std::string minetest_dir; 19 | std::string minetest_exe; 20 | }; 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /src/modes/NBEditor.cpp: -------------------------------------------------------------------------------- 1 | #include "NBEditor.hpp" 2 | #include 3 | #include "../GUIHelpers.hpp" 4 | #include "../util/string.hpp" 5 | #include "../project/node.hpp" 6 | #include "../project/nodebox.hpp" 7 | 8 | // The gui id numbers for this mode 9 | // NOTE: the maximum that can be here is 20 10 | // see in MenuState.hpp to raise the limit 11 | enum NODEBOX_EDITOR_GUI_IDS { 12 | ENB_GUI_MAIN_LISTBOX = GUI_SIDEBAR + 1, 13 | ENB_GUI_MAIN_MSG, 14 | ENB_GUI_PROP, 15 | ENB_GUI_PROP_X1, 16 | ENB_GUI_PROP_Y1, 17 | ENB_GUI_PROP_Z1, 18 | ENB_GUI_PROP_X2, 19 | ENB_GUI_PROP_Y2, 20 | ENB_GUI_PROP_Z2, 21 | ENB_GUI_PROP_NAME, 22 | ENB_GUI_PROP_DECIMALS, 23 | ENB_GUI_PROP_UPDATE, 24 | ENB_GUI_PROP_REVERT, 25 | ENB_GUI_FLP_X, 26 | ENB_GUI_FLP_Y, 27 | ENB_GUI_FLP_Z, 28 | ENB_GUI_ROT_X, 29 | ENB_GUI_ROT_Y, 30 | ENB_GUI_ROT_Z 31 | }; 32 | 33 | NBEditor::NBEditor(EditorState* st) : 34 | EditorMode(st), 35 | current(-1), 36 | prop_needs_update(false) 37 | { 38 | for (int i = 0; i < 4; i++) { 39 | cdrs[i*5] = CDR((EViewport)i, CDR_L); 40 | cdrs[i*5 + 1] = CDR((EViewport)i, CDR_R); 41 | cdrs[i*5 + 2] = CDR((EViewport)i, CDR_U); 42 | cdrs[i*5 + 3] = CDR((EViewport)i, CDR_D); 43 | cdrs[i*5 + 4] = CDR((EViewport)i, CDR_M); 44 | } 45 | } 46 | 47 | 48 | void NBEditor::load() 49 | { 50 | IGUIStaticText* sidebar = state->menu->sidebar; 51 | IGUIEnvironment* guienv = state->device->getGUIEnvironment(); 52 | 53 | if (state->settings->getBool("hide_other_nodes")) 54 | state->project->hideAllButCurrentNode(); 55 | else 56 | state->project->remesh(); 57 | 58 | if (sidebar) { 59 | sidebar->setText(L"Node Box Tool"); 60 | IGUIStaticText* t = guienv->addStaticText(L"No node selected", 61 | rect(20, 30, 140, 100), 62 | false, true, sidebar, ENB_GUI_MAIN_MSG); 63 | 64 | 65 | IGUIListBox *lb = guienv->addListBox(rect(10, 35+20, 227, 133+20), 66 | sidebar, ENB_GUI_MAIN_LISTBOX, true); 67 | 68 | if (lb) { 69 | lb->setVisible(false); 70 | IGUIButton* b1 = guienv->addButton(rect(0, 100, 50, 125), 71 | lb, GUI_PROJ_NEW_BOX, L"+", L"Add a node box"); 72 | IGUIButton* b2 = guienv->addButton(rect(60, 100, 110, 125), 73 | lb, GUI_PROJ_DELETE_BOX, L"-", L"Delete node box"); 74 | IGUIButton* b3 = guienv->addButton(rect(120, 100, 170, 125), 75 | lb, GUI_PROJ_CLONE, L"Clone", L"Duplicate node box"); 76 | b1->setNotClipped(true); 77 | b2->setNotClipped(true); 78 | b3->setNotClipped(true); 79 | } 80 | 81 | 82 | // Rotate X 83 | static ITexture *flp_x = state->device->getVideoDriver()->getTexture("media/flip_x.png"); 84 | IGUIButton *btn = guienv->addButton(rect(10, 20, 120+32 - (32+5)*3, 52), sidebar, ENB_GUI_FLP_X, L"", L"Rotate node through X axis"); 85 | btn->setImage(flp_x); 86 | btn->setUseAlphaChannel(true); 87 | 88 | // Rotate Y 89 | static ITexture *flp_y = state->device->getVideoDriver()->getTexture("media/flip_y.png"); 90 | btn = guienv->addButton(rect(121 - (32+5)*2, 20, 120+32 - (32+5)*2, 32+20), sidebar, ENB_GUI_FLP_Y, L"", L"Rotate node through Y axis"); 91 | btn->setImage(flp_y); 92 | btn->setUseAlphaChannel(true); 93 | 94 | // Rotate Z 95 | static ITexture *flp_z = state->device->getVideoDriver()->getTexture("media/flip_z.png"); 96 | btn = guienv->addButton(rect(121 - (32+5), 20, 120+32 - (32+5), 32+20), sidebar, ENB_GUI_FLP_Z, L"", L"Rotate node through Z axis"); 97 | btn->setImage(flp_z); 98 | btn->setUseAlphaChannel(true); 99 | 100 | // Rotate X 101 | static ITexture *rot_x = state->device->getVideoDriver()->getTexture("media/rotate_x.png"); 102 | btn = guienv->addButton(rect(121, 20, 120+32, 32+20), sidebar, ENB_GUI_ROT_X, L"", L"Rotate node through X axis"); 103 | btn->setImage(rot_x); 104 | btn->setUseAlphaChannel(true); 105 | 106 | // Rotate Y 107 | static ITexture *rot_y = state->device->getVideoDriver()->getTexture("media/rotate_y.png"); 108 | btn = guienv->addButton(rect(121 + (32+5)*1, 20, 120+32 + (32+5)*1, 32+20), sidebar, ENB_GUI_ROT_Y, L"", L"Rotate node through Y axis"); 109 | btn->setImage(rot_y); 110 | btn->setUseAlphaChannel(true); 111 | 112 | // Rotate Z 113 | static ITexture *rot_z = state->device->getVideoDriver()->getTexture("media/rotate_z.png"); 114 | btn = guienv->addButton(rect(121 + (32+5)*2, 20, 120+32 + (32+5)*2, 32+20), sidebar, ENB_GUI_ROT_Z, L"", L"Rotate node through Z axis"); 115 | btn->setImage(rot_z); 116 | btn->setUseAlphaChannel(true); 117 | 118 | // Create nodebox properties 119 | t = guienv->addStaticText(L"Properties", 120 | rect(0, 170+20, 120, 190+20), 121 | false, true, sidebar, ENB_GUI_PROP); 122 | t->setVisible(false); 123 | 124 | // Add name properties box 125 | guienv->addStaticText(L"Name:", rect(10, 30, 50, 50), false, 126 | true, t)->setNotClipped(true); 127 | guienv->addEditBox(L"", rect(60, 30, 210, 50), true, 128 | t, ENB_GUI_PROP_NAME)->setNotClipped(true); 129 | 130 | // Add positioning 131 | addXYZ(t, guienv, vector2di(10, 60), ENB_GUI_PROP_X1); 132 | addXYZ(t, guienv, vector2di(10, 140), ENB_GUI_PROP_X2); 133 | 134 | // Add show decimals checkbox 135 | bool fp = state->settings->getBool("fractional_positions"); 136 | guienv->addCheckBox(fp, rect(30, 215, 200, 245), t, 137 | ENB_GUI_PROP_DECIMALS, L"As multiples of 1/16")->setNotClipped(true); 138 | 139 | // Add buttons 140 | guienv->addButton(rect(30, 250, 100, 250+30), t, ENB_GUI_PROP_UPDATE, 141 | L"Update", L"")->setNotClipped(true); 142 | guienv->addButton(rect(110, 250, 180, 250+30), t, ENB_GUI_PROP_REVERT, 143 | L"Revert", L"")->setNotClipped(true); 144 | 145 | } 146 | load_ui(); 147 | } 148 | 149 | void NBEditor::load_ui() 150 | { 151 | IGUIStaticText *sidebar = state->menu->sidebar; 152 | 153 | if (!sidebar) { 154 | return; 155 | } 156 | 157 | IGUIEnvironment *guienv = state->device->getGUIEnvironment(); 158 | Node *node = state->project->GetCurrentNode(); 159 | if (!node) { 160 | sidebar->getElementFromId(ENB_GUI_MAIN_MSG)->setVisible(true); 161 | sidebar->getElementFromId(ENB_GUI_MAIN_LISTBOX)->setVisible(false); 162 | sidebar->getElementFromId(ENB_GUI_PROP)->setVisible(false); 163 | } else { 164 | IGUIListBox *lb = (IGUIListBox *) sidebar->getElementFromId(ENB_GUI_MAIN_LISTBOX); 165 | sidebar->getElementFromId(ENB_GUI_MAIN_MSG)->setVisible(false); 166 | sidebar->getElementFromId(ENB_GUI_PROP)->setVisible(false); 167 | 168 | if (lb) { 169 | lb->clear(); 170 | lb->setVisible(true); 171 | std::vector & boxes = node->boxes; 172 | for (std::vector::const_iterator it = boxes.begin(); 173 | it != boxes.end(); 174 | ++it) { 175 | lb->addItem(narrow_to_wide((*it)->name).c_str()); 176 | } 177 | lb->setSelected(lb->getListItem(node->GetId())); 178 | } 179 | 180 | fillProperties(); 181 | } 182 | } 183 | 184 | void NBEditor::fillProperties() 185 | { 186 | IGUIStaticText *sidebar = state->menu->sidebar; 187 | Node *node = state->project->GetCurrentNode(); 188 | 189 | if (!sidebar || !node) 190 | return; 191 | 192 | NodeBox *nb = node->GetCurrentNodeBox(); 193 | 194 | if (!nb) 195 | return; 196 | 197 | sidebar->getElementFromId(ENB_GUI_PROP)->setVisible(true); 198 | 199 | vector3df one = nb->one; 200 | vector3df two = nb->two; 201 | 202 | if (state->settings->getBool("fractional_positions")) { 203 | one *= 16; 204 | two *= 16; 205 | } 206 | 207 | fillTB(sidebar, ENB_GUI_PROP, ENB_GUI_PROP_X1, one.X); 208 | fillTB(sidebar, ENB_GUI_PROP, ENB_GUI_PROP_Y1, one.Y); 209 | fillTB(sidebar, ENB_GUI_PROP, ENB_GUI_PROP_Z1, one.Z); 210 | fillTB(sidebar, ENB_GUI_PROP, ENB_GUI_PROP_X2, two.X); 211 | fillTB(sidebar, ENB_GUI_PROP, ENB_GUI_PROP_Y2, two.Y); 212 | fillTB(sidebar, ENB_GUI_PROP, ENB_GUI_PROP_Z2, two.Z); 213 | 214 | IGUIElement* element = sidebar->getElementFromId(ENB_GUI_PROP) 215 | ->getElementFromId(ENB_GUI_PROP_NAME); 216 | 217 | if (element) { 218 | IGUIEditBox* editbox = static_cast(element); 219 | 220 | std::wstring wide = narrow_to_wide(nb->name); 221 | editbox->setText(wide.c_str()); 222 | } 223 | } 224 | 225 | 226 | void NBEditor::unload() 227 | {} 228 | 229 | 230 | void NBEditor::update(double dtime) 231 | { 232 | if (state->menu->dialog) 233 | current = -1; 234 | 235 | wasmd = state->mousedown; 236 | } 237 | 238 | 239 | void NBEditor::drawViewport(irr::video::IVideoDriver *driver, EViewport viewport, 240 | rect area) 241 | { 242 | if (wasmd && !state->mousedown) 243 | current = -1; 244 | 245 | static irr::video::ITexture *scale = driver->getTexture("media/gui_scale.png"); 246 | 247 | for (int i = 0; i < 20; i++) { 248 | if (cdrs[i].visible && cdrs[i].window == viewport) { 249 | if (area.isPointInside(cdrs[i].position)) { 250 | rect drawarea = rect( 251 | cdrs[i].position.X - 5, 252 | cdrs[i].position.Y - 5, 253 | cdrs[i].position.X + 5, 254 | cdrs[i].position.Y + 5 255 | ); 256 | 257 | if (!wasmd && state->mousedown && 258 | drawarea.isPointInside(state->mouse_position)) { 259 | current = i; 260 | } 261 | 262 | SColor color = SColor(255, 255, 255, 255); 263 | 264 | if (current == i) { 265 | color = SColor(255, 0, 0, 255); 266 | } else if (state->keys[KEY_LCONTROL] == EKS_DOWN) { 267 | color = SColor(255, 255, 255, 0); 268 | } 269 | 270 | driver->draw2DImage( 271 | scale, 272 | drawarea.UpperLeftCorner, 273 | rect(0, 0, 10, 10), 274 | NULL, color, true 275 | ); 276 | 277 | cdrs[i].visible = false; 278 | } // end if point is inside 279 | } // end if cdr is to be drawn 280 | } // end for 281 | } 282 | 283 | 284 | void NBEditor::viewportTick(EViewport window, irr::video::IVideoDriver* driver, rect offset) 285 | { 286 | for (int i = 0; i < 20; i++) { 287 | if (cdrs[i].window == window) { 288 | cdrs[i].update(this, (current == i), offset); 289 | } 290 | } 291 | } 292 | 293 | ECDR_DIR CDR::getActualType(EditorState* state) 294 | { 295 | EViewportType vpt = state->getEViewportType(window); 296 | 297 | static ECDR_DIR crosstable[15] = { 298 | CDR_X_N, CDR_X_P, CDR_Y_P, CDR_Y_N, CDR_XY, // front / back 299 | CDR_Z_N, CDR_Z_P, CDR_Y_P, CDR_Y_N, CDR_ZY, // left / right 300 | CDR_X_N, CDR_X_P, CDR_Z_P, CDR_Z_N, CDR_XZ // top / bottom 301 | }; 302 | 303 | if (vpt == VIEWT_PERS) 304 | return CDR_NONE; 305 | else 306 | return crosstable[(ECDR_DIR)( ( ((int)vpt - 1) % 3) * 5 + type)]; 307 | } 308 | 309 | void CDR::update(NBEditor* editor, bool drag, rect offset) 310 | { 311 | if (!editor->state->project) { 312 | return; 313 | } 314 | 315 | ECDR_DIR actualType = getActualType(editor->state); 316 | if (actualType == CDR_NONE) 317 | return; 318 | 319 | if (!editor->state->settings->getBool("always_show_position_handle") && 320 | (editor->state->keys[KEY_LSHIFT] == EKS_UP) == 321 | (actualType == CDR_XY || actualType == CDR_XZ || actualType == CDR_ZY)) { 322 | return; 323 | } 324 | 325 | Node* node = editor->state->project->GetCurrentNode(); 326 | 327 | if (!node) { 328 | return; 329 | } 330 | 331 | NodeBox *box = node->GetCurrentNodeBox(); 332 | 333 | if (!box) { 334 | return; 335 | } 336 | 337 | ISceneManager * smgr = editor->state->device->getSceneManager(); 338 | 339 | if (drag) { 340 | // get mouse position 341 | position2di target = editor->state->mouse_position; 342 | target -= offset.UpperLeftCorner; 343 | 344 | // Get the ray 345 | line3d ray = smgr->getSceneCollisionManager() 346 | ->getRayFromScreenCoordinates(target, smgr->getActiveCamera()); 347 | 348 | // Contains the output values 349 | vector3df wpos = vector3df(0, 0, 0); // The collision position 350 | 351 | // Not used, but required for function 352 | #if IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8 353 | const ISceneNode* tmpNode; 354 | #else 355 | ISceneNode* tmpNode; 356 | #endif 357 | triangle3df tmpTri; 358 | 359 | // Execute ray 360 | smgr->getSceneCollisionManager()->getCollisionPoint(ray, 361 | editor->state->plane_tri, wpos, tmpTri, tmpNode); 362 | 363 | // Snapping 364 | wpos -= vector3df( 365 | (f32)node->position.X, 366 | (f32)node->position.Y, 367 | (f32)node->position.Z 368 | ); 369 | 370 | // Do node limiting 371 | if (editor->state->settings->getBool("limiting")) { 372 | // X Axis 373 | if (wpos.X < -0.5) { 374 | wpos.X = -0.5; 375 | } else if (wpos.X > 0.5) { 376 | wpos.X = 0.5; 377 | } 378 | 379 | // Y Axis 380 | if (wpos.Y < -0.5) { 381 | wpos.Y = -0.5; 382 | } else if (wpos.Y > 0.5) { 383 | wpos.Y = 0.5; 384 | } 385 | 386 | // Z Axis 387 | if (wpos.Z < -0.5) { 388 | wpos.Z = -0.5; 389 | } else if (wpos.Z > 0.5) { 390 | wpos.Z = 0.5; 391 | } 392 | } 393 | 394 | // Call required function 395 | editor->fillProperties(); 396 | 397 | int snap_res = node->snap_res; 398 | if (snap_res == -1) { 399 | static const int set_snap_res = 400 | editor->state->settings->getInt("default_snap_res"); 401 | snap_res = set_snap_res; 402 | } 403 | 404 | if (actualType < CDR_XZ) { 405 | if (editor->state->settings->getBool("snapping")) { 406 | wpos.X = (f32)floor((wpos.X + 0.5) * snap_res + 0.5) / snap_res - 0.5; 407 | wpos.Y = (f32)floor((wpos.Y + 0.5) * snap_res + 0.5) / snap_res - 0.5; 408 | wpos.Z = (f32)floor((wpos.Z + 0.5) * snap_res + 0.5) / snap_res - 0.5; 409 | 410 | // (X + 0.5) round relative to (-0.5, -0.5, -0.5) 411 | // ie: (-0.5, -0.5, -0.5) goes to (0, 0, 0) 412 | // * 16 round to the nearest 16th 413 | // + 0.5 do rounding, rather than flooring 414 | // / 16 - 0.5 go back to normal coordinates 415 | } 416 | 417 | box->moveFace(editor->state, actualType, wpos, 418 | editor->state->keys[KEY_LCONTROL] == EKS_DOWN); 419 | } else { 420 | box->move(editor->state, actualType, wpos, 421 | (editor->state->settings->getBool("snapping")?snap_res:0)); 422 | } 423 | node->remesh(box); 424 | 425 | editor->triggerCDRmoved(); 426 | } 427 | 428 | vector3df pos = box->GetCenter(); 429 | 430 | switch (actualType) { 431 | case CDR_X_P: 432 | pos.X = box->two.X; 433 | break; 434 | case CDR_X_N: 435 | pos.X = box->one.X; 436 | break; 437 | case CDR_Y_P: 438 | pos.Y = box->two.Y; 439 | break; 440 | case CDR_Y_N: 441 | pos.Y = box->one.Y; 442 | break; 443 | case CDR_Z_P: 444 | pos.Z = box->two.Z; 445 | break; 446 | case CDR_Z_N: 447 | pos.Z = box->one.Z; 448 | break; 449 | } 450 | 451 | pos.X += node->position.X; 452 | pos.Y += node->position.Y; 453 | pos.Z += node->position.Z; 454 | 455 | vector2d cpos = smgr->getSceneCollisionManager() 456 | ->getScreenCoordinatesFrom3DPosition( 457 | pos, 458 | smgr->getActiveCamera() 459 | #if !(IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8) 460 | , true 461 | #endif 462 | ); 463 | 464 | position = cpos + offset.UpperLeftCorner; 465 | visible = true; 466 | } 467 | 468 | bool NBEditor::OnEvent(const irr::SEvent &event) { 469 | if (event.EventType == EET_GUI_EVENT) { 470 | if (event.GUIEvent.EventType == EGET_CHECKBOX_CHANGED) { 471 | switch (event.GUIEvent.Caller->getID()) { 472 | case ENB_GUI_PROP_DECIMALS: { 473 | bool fp = state->settings->getBool("fractional_positions"); 474 | state->settings->set("fractional_positions", 475 | fp?"false":"true"); 476 | fillProperties(); 477 | return true; 478 | }} 479 | } else if (event.GUIEvent.EventType == EGET_BUTTON_CLICKED) { 480 | switch (event.GUIEvent.Caller->getID()) { 481 | case GUI_PROJ_NEW_BOX: { 482 | Node* node = state->project->GetCurrentNode(); 483 | if (node) { 484 | node->addNodeBox(); 485 | load_ui(); 486 | } 487 | break; 488 | } 489 | case GUI_PROJ_DELETE_BOX: { 490 | Node* node = state->project->GetCurrentNode(); 491 | IGUIListBox* lb = (IGUIListBox*) state->menu->sidebar->getElementFromId(ENB_GUI_MAIN_LISTBOX); 492 | if (node && node->GetNodeBox(lb->getSelected())){ 493 | node->deleteNodebox(lb->getSelected()); 494 | load_ui(); 495 | } 496 | break; 497 | } 498 | case GUI_PROJ_CLONE: { 499 | Node* node = state->project->GetCurrentNode(); 500 | IGUIListBox* lb = (IGUIListBox*) state->menu->sidebar->getElementFromId(ENB_GUI_MAIN_LISTBOX); 501 | if (node && node->GetNodeBox(lb->getSelected())){ 502 | node->cloneNodebox(lb->getSelected()); 503 | load_ui(); 504 | } 505 | break; 506 | } 507 | case ENB_GUI_PROP_REVERT: 508 | fillProperties(); 509 | break; 510 | case ENB_GUI_PROP_UPDATE: 511 | updateProperties(); 512 | break; 513 | case ENB_GUI_ROT_X: { 514 | Node* node = state->project->GetCurrentNode(); 515 | if (node) 516 | node->rotate(EAX_X); 517 | break; 518 | } 519 | case ENB_GUI_ROT_Y: { 520 | Node* node = state->project->GetCurrentNode(); 521 | if (node) 522 | node->rotate(EAX_Y); 523 | break; 524 | } 525 | case ENB_GUI_ROT_Z: { 526 | Node* node = state->project->GetCurrentNode(); 527 | if (node) 528 | node->rotate(EAX_Z); 529 | break; 530 | } 531 | case ENB_GUI_FLP_X: { 532 | Node* node = state->project->GetCurrentNode(); 533 | if (node) 534 | node->flip(EAX_X); 535 | break; 536 | } 537 | case ENB_GUI_FLP_Y: { 538 | Node* node = state->project->GetCurrentNode(); 539 | if (node) 540 | node->flip(EAX_Y); 541 | break; 542 | } 543 | case ENB_GUI_FLP_Z: { 544 | Node* node = state->project->GetCurrentNode(); 545 | if (node) 546 | node->flip(EAX_Z); 547 | break; 548 | }} 549 | } else if (event.GUIEvent.EventType == EGET_LISTBOX_CHANGED) { 550 | Node* node = state->project->GetCurrentNode(); 551 | IGUIListBox* lb = (IGUIListBox*) state->menu->sidebar->getElementFromId(ENB_GUI_MAIN_LISTBOX); 552 | if (node && lb && node->GetNodeBox(lb->getSelected())){ 553 | node->select(lb->getSelected()); 554 | } 555 | load_ui(); 556 | } 557 | } else if (event.EventType == EET_KEY_INPUT_EVENT && !event.KeyInput.PressedDown) { 558 | if (event.KeyInput.Key == KEY_RETURN) { 559 | updateProperties(); 560 | return false; 561 | } 562 | 563 | if ( 564 | state->device->getGUIEnvironment()->getFocus() && 565 | state->device->getGUIEnvironment()->getFocus()->getType() == EGUIET_EDIT_BOX) { 566 | return false; 567 | } 568 | 569 | if (event.KeyInput.Key == KEY_INSERT) { 570 | Node *node = state->project->GetCurrentNode(); 571 | if (node) { 572 | node->addNodeBox(); 573 | load_ui(); 574 | } 575 | } else if (event.KeyInput.Key == KEY_DELETE) { 576 | Node* node = state->project->GetCurrentNode(); 577 | IGUIListBox* lb = (IGUIListBox*) state->menu->sidebar->getElementFromId(ENB_GUI_MAIN_LISTBOX); 578 | if (node && node->GetNodeBox(lb->getSelected())) { 579 | node->deleteNodebox(lb->getSelected()); 580 | } 581 | load_ui(); 582 | } else if (event.KeyInput.Key == KEY_DOWN) { 583 | IGUIListBox* lb = (IGUIListBox*) state->menu->sidebar->getElementFromId(ENB_GUI_MAIN_LISTBOX); 584 | Node* node = state->project->GetCurrentNode(); 585 | if (node) { 586 | int idx = node->GetId(); 587 | if (lb && idx < (int)node->boxes.size() - 1){ 588 | node->select(idx + 1); 589 | } 590 | } 591 | load_ui(); 592 | } else if (event.KeyInput.Key == KEY_UP) { 593 | IGUIListBox* lb = (IGUIListBox*) state->menu->sidebar->getElementFromId(ENB_GUI_MAIN_LISTBOX); 594 | Node* node = state->project->GetCurrentNode(); 595 | if (node) { 596 | int idx = node->GetId(); 597 | if (lb && idx > 0) { 598 | node->select(idx - 1); 599 | } 600 | } 601 | load_ui(); 602 | } 603 | } 604 | return false; 605 | } 606 | 607 | void NBEditor::updateProperties() 608 | { 609 | IGUIElement* prop = state->menu->sidebar->getElementFromId(ENB_GUI_PROP); 610 | Node* node = state->project->GetCurrentNode(); 611 | if (!prop || !node) { 612 | return; 613 | } 614 | 615 | NodeBox* nb = node->GetCurrentNodeBox(); 616 | if (!nb) { 617 | return; 618 | } 619 | 620 | try { 621 | irr::core::stringc name = prop->getElementFromId(ENB_GUI_PROP_NAME)->getText(); 622 | nb->name = str_replace(std::string(name.c_str(), name.size()), ' ', '_'); 623 | vector3df one( 624 | (f32)wcstod(prop->getElementFromId(ENB_GUI_PROP_X1)->getText(), NULL), 625 | (f32)wcstod(prop->getElementFromId(ENB_GUI_PROP_Y1)->getText(), NULL), 626 | (f32)wcstod(prop->getElementFromId(ENB_GUI_PROP_Z1)->getText(), NULL) 627 | ); 628 | vector3df two( 629 | (f32)wcstod(prop->getElementFromId(ENB_GUI_PROP_X2)->getText(), NULL), 630 | (f32)wcstod(prop->getElementFromId(ENB_GUI_PROP_Y2)->getText(), NULL), 631 | (f32)wcstod(prop->getElementFromId(ENB_GUI_PROP_Z2)->getText(), NULL) 632 | ); 633 | 634 | if (state->settings->getBool("fractional_positions")) { 635 | one /= 16; 636 | two /= 16; 637 | } 638 | 639 | if (one != nb->one || two != nb->two) { 640 | nb->one = one; 641 | nb->two = two; 642 | nb->rebuild_needed = true; 643 | } 644 | node->remesh(); 645 | load_ui(); 646 | } catch(void* e) { 647 | state->device->getGUIEnvironment()->addMessageBox(L"Update failed", 648 | L"Please check that the properties contain only numbers."); 649 | } 650 | } 651 | 652 | irr::video::ITexture* NBEditor::icon() 653 | { 654 | static irr::video::ITexture* icon = state->device->getVideoDriver() 655 | ->getTexture("media/icon_mode_nodebox.png"); 656 | return icon; 657 | } 658 | -------------------------------------------------------------------------------- /src/modes/NBEditor.hpp: -------------------------------------------------------------------------------- 1 | #ifndef NBEDITOR_HPP_INCLUDED 2 | #define NBEDITOR_HPP_INCLUDED 3 | #include "../common.hpp" 4 | #include "../EditorState.hpp" 5 | 6 | class EditorState; 7 | class NBEditor; 8 | class CDR 9 | { 10 | public: 11 | CDR() : visible(false) {} 12 | CDR(EViewport win, ECDR typ) : window(win), type(typ), visible(false) {} 13 | 14 | void update(NBEditor* editor, bool drag, rect offset); 15 | ECDR_DIR getActualType(EditorState* state); 16 | 17 | EViewport window; 18 | ECDR type; 19 | vector2d position; 20 | bool visible; 21 | }; 22 | 23 | class EditorMode; 24 | class NBEditor :public EditorMode 25 | { 26 | friend class CDR; 27 | public: 28 | NBEditor(EditorState* st); 29 | 30 | // Interface 31 | virtual void load(); 32 | virtual void unload(); 33 | virtual void update(double dtime); 34 | virtual void drawViewport(irr::video::IVideoDriver* driver, EViewport viewport, rect area); 35 | virtual void viewportTick(EViewport window, irr::video::IVideoDriver* driver, rect offset); 36 | virtual bool OnEvent(const irr::SEvent &event); 37 | virtual irr::video::ITexture* icon(); 38 | void triggerCDRmoved() { prop_needs_update = true; } 39 | 40 | private: 41 | bool wasmd; 42 | int current; 43 | CDR cdrs[20]; 44 | void load_ui(); 45 | void fillProperties(); 46 | void updateProperties(); 47 | bool prop_needs_update; 48 | }; 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /src/modes/NodeEditor.cpp: -------------------------------------------------------------------------------- 1 | #include "NodeEditor.hpp" 2 | #include 3 | #include "../project/node.hpp" 4 | #include "../GUIHelpers.hpp" 5 | #include "../util/string.hpp" 6 | 7 | NodeEditor::NodeEditor(EditorState* st) : 8 | EditorMode(st) 9 | { 10 | } 11 | 12 | void NodeEditor::load() 13 | { 14 | state->project->remesh(); 15 | IGUIStaticText* sidebar = state->menu->sidebar; 16 | IGUIEnvironment* guienv = state->device->getGUIEnvironment(); 17 | 18 | if (!sidebar || !guienv) 19 | return; 20 | 21 | sidebar->setText(L"Node Tool"); 22 | IGUIListBox* lb = guienv->addListBox(rect(20, 30, 230, 128), sidebar, ENG_GUI_MAIN_LISTBOX, true); 23 | 24 | if (lb) { 25 | //lb->setVisible(false); 26 | IGUIButton* b1 = guienv->addButton(rect(0, 100, 50, 125), 27 | lb, ENG_GUI_MAIN_ADD, L"+", L"Add a node"); 28 | IGUIButton* b2 = guienv->addButton(rect(60, 100, 110, 125), 29 | lb, ENG_GUI_MAIN_DEL, L"-", L"Delete node"); 30 | IGUIButton* b3 = guienv->addButton(rect(120, 100, 170, 125), 31 | lb, ENG_GUI_MAIN_EDIT, L"Edit", L"Edit node boxes"); 32 | b1->setNotClipped(true); 33 | b2->setNotClipped(true); 34 | b3->setNotClipped(true); 35 | } 36 | 37 | // Create node properties 38 | { 39 | // Create properties 40 | IGUIStaticText* t = guienv->addStaticText(L"Properties", 41 | rect(0, 170, 120, 190), 42 | false, true, sidebar, ENG_GUI_PROP); 43 | t->setVisible(false); 44 | 45 | // Add name properties box 46 | guienv->addStaticText(L"Name:", rect(10, 30, 50, 50), 47 | false, true, t)->setNotClipped(true); 48 | guienv->addEditBox(L"", rect(60, 30, 210, 50), true, 49 | t, ENG_GUI_PROP_NAME)->setNotClipped(true); 50 | 51 | // Add positioning 52 | addXYZ(t, guienv, vector2di(10, 60), ENG_GUI_PROP_X); 53 | 54 | // Add snap res 55 | guienv->addStaticText(L"Snap res:", rect(10, 140, 70, 160), 56 | false, true, t)->setNotClipped(true); 57 | guienv->addEditBox(L"", rect(70, 140, 210, 160), true, 58 | t, ENG_GUI_PROP_SNAP_RES)->setNotClipped(true); 59 | guienv->addStaticText(L"(set to -1 to use project default)", 60 | rect(10, 165, 240, 185), false, true, t)->setNotClipped(true); 61 | 62 | // Add buttons 63 | guienv->addButton(rect(30, 250, 100, 280), t, 64 | ENG_GUI_PROP_UPDATE, L"Update", L"") 65 | ->setNotClipped(true); 66 | guienv->addButton(rect(110, 250, 180, 280), t, 67 | ENG_GUI_PROP_REVERT, L"Revert", L"") 68 | ->setNotClipped(true); 69 | } 70 | load_ui(); 71 | } 72 | 73 | void NodeEditor::load_ui(){ 74 | IGUIStaticText* sidebar = state->menu->sidebar; 75 | IGUIEnvironment* guienv = state->device->getGUIEnvironment(); 76 | 77 | if (!sidebar || !guienv) { 78 | return; 79 | } 80 | 81 | IGUIListBox* lb = (IGUIListBox*) sidebar->getElementFromId(ENG_GUI_MAIN_LISTBOX); 82 | 83 | if (lb) { 84 | lb->clear(); 85 | sidebar->getElementFromId(ENG_GUI_PROP)->setVisible(false); 86 | 87 | std::list & nodes = state->project->nodes; 88 | for (std::list::const_iterator it = nodes.begin(); 89 | it != nodes.end(); 90 | ++it) { 91 | std::wstring wide = narrow_to_wide((*it)->name); 92 | lb->addItem(wide.c_str()); 93 | } 94 | lb->setSelected(lb->getListItem(state->project->GetSelectedNodeId())); 95 | 96 | fillProperties(); 97 | } 98 | } 99 | 100 | void NodeEditor::fillProperties(){ 101 | IGUIStaticText* sidebar = state->menu->sidebar; 102 | 103 | if (!sidebar) { 104 | return; 105 | } 106 | 107 | Node* node = state->project->GetCurrentNode(); 108 | 109 | if (!node) { 110 | return; 111 | } 112 | 113 | sidebar->getElementFromId(ENG_GUI_PROP)->setVisible(true); 114 | fillTB(sidebar, ENG_GUI_PROP, ENG_GUI_PROP_X, node->position.X); 115 | fillTB(sidebar, ENG_GUI_PROP, ENG_GUI_PROP_Y, node->position.Y); 116 | fillTB(sidebar, ENG_GUI_PROP, ENG_GUI_PROP_Z, node->position.Z); 117 | 118 | 119 | fillTB(sidebar, ENG_GUI_PROP, ENG_GUI_PROP_SNAP_RES, node->snap_res); 120 | 121 | IGUIElement* element = sidebar->getElementFromId(ENG_GUI_PROP) 122 | ->getElementFromId(ENG_GUI_PROP_NAME); 123 | 124 | if (element) { 125 | IGUIEditBox* editbox = static_cast(element); 126 | 127 | std::wstring wide = narrow_to_wide(node->name); 128 | editbox->setText(wide.c_str()); 129 | } 130 | } 131 | 132 | 133 | void NodeEditor::unload() 134 | {} 135 | 136 | 137 | void NodeEditor::update(double dtime) 138 | {} 139 | 140 | 141 | void NodeEditor::draw(irr::video::IVideoDriver* driver) 142 | {} 143 | 144 | 145 | void NodeEditor::viewportTick(EViewport window, irr::video::IVideoDriver* driver, rect offset) 146 | {} 147 | 148 | 149 | bool NodeEditor::OnEvent(const irr::SEvent &event) 150 | { 151 | if (event.EventType == EET_GUI_EVENT){ 152 | if (event.GUIEvent.EventType == EGET_BUTTON_CLICKED){ 153 | switch (event.GUIEvent.Caller->getID()){ 154 | case ENG_GUI_MAIN_EDIT: 155 | if (state->project->GetCurrentNode()){ 156 | // TODO: This line is terrible 157 | state->SelectMode(0); 158 | return true; 159 | } 160 | break; 161 | case ENG_GUI_MAIN_ADD: 162 | state->project->AddNode(state); 163 | load_ui(); 164 | return true; 165 | break; 166 | case ENG_GUI_MAIN_DEL: 167 | if (state->project->GetCurrentNode()){ 168 | state->project->DeleteNode(state->project->GetSelectedNodeId()); 169 | load_ui(); 170 | return true; 171 | } 172 | break; 173 | case ENG_GUI_PROP_REVERT: 174 | fillProperties(); 175 | break; 176 | case ENG_GUI_PROP_UPDATE: 177 | updateProperties(); 178 | break; 179 | } 180 | } else if (event.GUIEvent.EventType == EGET_LISTBOX_CHANGED) { 181 | IGUIListBox* lb = (IGUIListBox*) state->menu->sidebar->getElementFromId(ENG_GUI_MAIN_LISTBOX); 182 | if (lb && state->project->GetNode(lb->getSelected())){ 183 | state->project->SelectNode(lb->getSelected()); 184 | load_ui(); 185 | } 186 | } 187 | } else if (event.EventType == EET_KEY_INPUT_EVENT && !event.KeyInput.PressedDown) { 188 | if (event.KeyInput.Key == KEY_RETURN){ 189 | updateProperties(); 190 | return false; 191 | } 192 | 193 | irr::gui::IGUIEnvironment *guienv = state->device->getGUIEnvironment(); 194 | if (guienv->getFocus() && guienv->getFocus()->getType() == EGUIET_EDIT_BOX) { 195 | return false; 196 | } 197 | 198 | if (event.KeyInput.Key == KEY_INSERT){ 199 | state->project->AddNode(state); 200 | load_ui(); 201 | } else if (event.KeyInput.Key == KEY_DELETE){ 202 | if (state->project->GetCurrentNode()){ 203 | state->project->DeleteNode(state->project->GetSelectedNodeId()); 204 | load_ui(); 205 | return true; 206 | } 207 | } else if (event.KeyInput.Key == KEY_DOWN){ 208 | IGUIListBox* lb = (IGUIListBox*) state->menu->sidebar->getElementFromId(ENG_GUI_MAIN_LISTBOX); 209 | int idx = state->project->GetSelectedNodeId(); 210 | if (lb && idx < (int)state->project->GetNodeCount() - 1){ 211 | state->project->SelectNode(idx + 1); 212 | load_ui(); 213 | } 214 | } else if (event.KeyInput.Key == KEY_UP){ 215 | IGUIListBox* lb = (IGUIListBox*) state->menu->sidebar->getElementFromId(ENG_GUI_MAIN_LISTBOX); 216 | int idx = state->project->GetSelectedNodeId(); 217 | if (lb && idx > 0){ 218 | state->project->SelectNode(idx - 1); 219 | load_ui(); 220 | } 221 | } 222 | } 223 | return false; 224 | } 225 | 226 | void NodeEditor::updateProperties() { 227 | IGUIElement* prop = state->menu->sidebar->getElementFromId(ENG_GUI_PROP); 228 | Node* node = state->project->GetCurrentNode(); 229 | if (!prop || !node) { 230 | return; 231 | } 232 | 233 | try { 234 | irr::core::stringc name = prop->getElementFromId(ENG_GUI_PROP_NAME)->getText(); 235 | node->name = str_replace(std::string(name.c_str(), name.size()), ' ', '_'); 236 | int y = (int)wcstod(prop->getElementFromId(ENG_GUI_PROP_Y)->getText(), NULL); 237 | if (state->settings->getBool("no_negative_node_y") && y < 0) { 238 | std::list & nodes = state->project->nodes; 239 | for (std::list::const_iterator it = nodes.begin(); 240 | it != nodes.end(); 241 | ++it) { 242 | (*it)->position.Y -= y; // Remember, y is negative 243 | } 244 | state->project->remesh(); 245 | y = 0; 246 | } 247 | 248 | node->position = vector3di( 249 | wcstod(prop->getElementFromId(ENG_GUI_PROP_X)->getText(), NULL), 250 | y, 251 | wcstod(prop->getElementFromId(ENG_GUI_PROP_Z)->getText(), NULL) 252 | ); 253 | node->snap_res = wcstod(prop->getElementFromId(ENG_GUI_PROP_SNAP_RES)->getText(), NULL); 254 | if (node->snap_res < 0) 255 | node->snap_res = -1; 256 | node->remesh(); 257 | load_ui(); 258 | } catch(void* e) { 259 | state->device->getGUIEnvironment()->addMessageBox(L"Update failed", 260 | L"Please check that the properties contain only numbers."); 261 | } 262 | } 263 | 264 | irr::video::ITexture* NodeEditor::icon() 265 | { 266 | static irr::video::ITexture* icon = state->device-> 267 | getVideoDriver()->getTexture("media/icon_mode_node.png"); 268 | return icon; 269 | } 270 | -------------------------------------------------------------------------------- /src/modes/NodeEditor.hpp: -------------------------------------------------------------------------------- 1 | #ifndef NODEEDITOR_HPP_INCLUDED 2 | #define NODEEDITOR_HPP_INCLUDED 3 | #include "../common.hpp" 4 | #include "../EditorState.hpp" 5 | 6 | class EditorMode; 7 | class NodeEditor :public EditorMode 8 | { 9 | public: 10 | NodeEditor(EditorState* st); 11 | 12 | // Interface 13 | virtual void load(); 14 | virtual void unload(); 15 | virtual void update(double dtime); 16 | virtual void draw(irr::video::IVideoDriver* driver); 17 | virtual void viewportTick(EViewport window, irr::video::IVideoDriver* driver, rect offset); 18 | virtual bool OnEvent(const irr::SEvent &event); 19 | virtual irr::video::ITexture* icon(); 20 | 21 | // The GUI ID numbers for this mode 22 | // NOTE: the maximum that can be here is 20 23 | // see in MenuState.h to raise limit 24 | enum NODE_EDITOR_GUI_ID { 25 | ENG_GUI_MAIN_LISTBOX = GUI_SIDEBAR + 1, 26 | ENG_GUI_MAIN_ADD, 27 | ENG_GUI_MAIN_DEL, 28 | ENG_GUI_MAIN_EDIT, 29 | ENG_GUI_PROP, 30 | ENG_GUI_PROP_NAME, 31 | ENG_GUI_PROP_X, 32 | ENG_GUI_PROP_Y, 33 | ENG_GUI_PROP_Z, 34 | ENG_GUI_PROP_SNAP_RES, 35 | ENG_GUI_PROP_UPDATE, 36 | ENG_GUI_PROP_REVERT 37 | }; 38 | private: 39 | void load_ui(); 40 | void fillProperties(); 41 | void updateProperties(); 42 | }; 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /src/modes/TextureEditor.cpp: -------------------------------------------------------------------------------- 1 | #include "TextureEditor.hpp" 2 | #include 3 | #include "../util/string.hpp" 4 | #include "../dialogs/TextureDialog.hpp" 5 | 6 | TextureEditor::TextureEditor(EditorState* st) : 7 | EditorMode(st) 8 | { 9 | } 10 | 11 | void TextureEditor::load() 12 | { 13 | if (state->settings->getBool("hide_other_nodes")) 14 | state->project->hideAllButCurrentNode(); 15 | else 16 | state->project->remesh(); 17 | 18 | IGUIStaticText* sidebar = state->menu->sidebar; 19 | 20 | if (!sidebar) 21 | return; 22 | 23 | sidebar->setText(L"Texture Tool"); 24 | } 25 | 26 | void TextureEditor::unload() 27 | {} 28 | 29 | 30 | void TextureEditor::update(double dtime) 31 | {} 32 | 33 | void drawIconAt(const wchar_t* label, int x, int y, Media::Image *image, IVideoDriver *driver, IGUIFont *font) 34 | { 35 | if (!image || image->name == "default") { 36 | driver->draw2DRectangle(SColor(100, 0, 0, 0), rect(x, y, x + 64, y + 64)); 37 | } else { 38 | ITexture *texture = driver->addTexture("tmpicon.png", image->get()); 39 | driver->draw2DImage(texture, rect(x, y, x + 64, y + 64), 40 | rect(0, 0, texture->getSize().Width, texture->getSize().Height)); 41 | driver->removeTexture(texture); 42 | } 43 | font->draw(label, rect(x, y + 68, 64, 25), SColor(255, 255, 255, 255)); 44 | } 45 | 46 | void TextureEditor::draw(irr::video::IVideoDriver* driver) 47 | { 48 | if (!state || !state->project || state->settings->getBool("hide_sidebar")) 49 | return; 50 | 51 | Node *node = state->project->GetCurrentNode(); 52 | 53 | if (!node) 54 | return; 55 | 56 | unsigned int start_x = driver->getScreenSize().Width - 258; 57 | drawIconAt(L"Left (X-)", start_x + 96, 70, node->getTexture(ECS_LEFT), 58 | driver, state->device->getGUIEnvironment()->getSkin()->getFont()); 59 | drawIconAt(L"Top (Y+)", start_x + 16, 170, node->getTexture(ECS_TOP), 60 | driver, state->device->getGUIEnvironment()->getSkin()->getFont()); 61 | drawIconAt(L"Front (Z-)", start_x + 96, 170, node->getTexture(ECS_FRONT), 62 | driver, state->device->getGUIEnvironment()->getSkin()->getFont()); 63 | drawIconAt(L"Bottom (Y-)", start_x + 180, 170, node->getTexture(ECS_BOTTOM), 64 | driver, state->device->getGUIEnvironment()->getSkin()->getFont()); 65 | drawIconAt(L"Right (X+)", start_x + 96, 270, node->getTexture(ECS_RIGHT), 66 | driver, state->device->getGUIEnvironment()->getSkin()->getFont()); 67 | drawIconAt(L"Back (Z+)", start_x + 96, 370, node->getTexture(ECS_BACK), 68 | driver, state->device->getGUIEnvironment()->getSkin()->getFont()); 69 | } 70 | 71 | 72 | void TextureEditor::viewportTick(EViewport window, irr::video::IVideoDriver* driver, rect offset) 73 | {} 74 | 75 | bool iconClicked(int x, int y, vector2di mouse) 76 | { 77 | return (rect(x, y, x + 64, y + 64).isPointInside(mouse)); 78 | } 79 | 80 | 81 | bool TextureEditor::OnEvent(const irr::SEvent &event) 82 | { 83 | if (!state || !state->project || state->settings->getBool("hide_sidebar")) 84 | return false; 85 | 86 | Node *node = state->project->GetCurrentNode(); 87 | 88 | if (!node) 89 | return false; 90 | 91 | if (event.EventType == EET_MOUSE_INPUT_EVENT && 92 | event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) { 93 | unsigned int start_x = state->device->getVideoDriver()->getScreenSize().Width - 256; 94 | 95 | if (iconClicked(start_x + 96, 70, state->mouse_position)) { 96 | TextureDialog::show(state, node, ECS_LEFT); 97 | } else if (iconClicked(start_x + 16, 170, state->mouse_position)) { 98 | TextureDialog::show(state, node, ECS_TOP); 99 | } else if (iconClicked(start_x + 96, 170, state->mouse_position)) { 100 | TextureDialog::show(state, node, ECS_FRONT); 101 | } else if (iconClicked(start_x + 180, 170, state->mouse_position)) { 102 | TextureDialog::show(state, node, ECS_BOTTOM); 103 | } else if (iconClicked(start_x + 96, 270, state->mouse_position)) { 104 | TextureDialog::show(state, node, ECS_RIGHT); 105 | } else if (iconClicked(start_x + 96, 370, state->mouse_position)) { 106 | TextureDialog::show(state, node, ECS_BACK); 107 | } 108 | } 109 | 110 | return false; 111 | } 112 | 113 | irr::video::ITexture* TextureEditor::icon() 114 | { 115 | static irr::video::ITexture* icon = state->device-> 116 | getVideoDriver()->getTexture("media/icon_mode_texture.png"); 117 | return icon; 118 | } 119 | -------------------------------------------------------------------------------- /src/modes/TextureEditor.hpp: -------------------------------------------------------------------------------- 1 | #ifndef TEXTUREEDITOR_HPP_INCLUDED 2 | #define TEXTUREEDITOR_HPP_INCLUDED 3 | #include "../common.hpp" 4 | #include "../EditorState.hpp" 5 | 6 | class EditorMode; 7 | class TextureEditor :public EditorMode 8 | { 9 | public: 10 | TextureEditor(EditorState* st); 11 | 12 | // Interface 13 | virtual void load(); 14 | virtual void unload(); 15 | virtual void update(double dtime); 16 | virtual void draw(irr::video::IVideoDriver* driver); 17 | virtual void viewportTick(EViewport window, irr::video::IVideoDriver* driver, rect offset); 18 | virtual bool OnEvent(const irr::SEvent &event); 19 | virtual irr::video::ITexture* icon(); 20 | }; 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /src/project/media.cpp: -------------------------------------------------------------------------------- 1 | #include "media.hpp" 2 | #include "../util/filesys.hpp" 3 | 4 | Media::~Media() 5 | { 6 | for (std::map::const_iterator it = images.begin(); 7 | it != images.end(); 8 | ++it) { 9 | delete it->second; 10 | } 11 | } 12 | 13 | bool Media::import(std::string filepath, std::string filename, IVideoDriver* driver) 14 | { 15 | return add(filepath, filename, driver->createImageFromFile(filepath.c_str())); 16 | } 17 | 18 | bool Media::add(std::string filepath, std::string filename, IImage *image, bool overwrite) 19 | { 20 | if (!image) 21 | return false; 22 | 23 | filename = trim(filename); 24 | const char *shortpath = filename.c_str(); 25 | if (images[shortpath] != NULL) { 26 | if (overwrite) { 27 | std::cerr << "Overwriting '" << shortpath << "'" << std::endl; 28 | images[shortpath]->update(image); 29 | images[shortpath]->origpath = filepath; 30 | } else { 31 | std::cerr << "Failed to add image '" << shortpath 32 | << "', it already exists (and overwrite was not authorised)" 33 | << std::endl; 34 | return false; 35 | } 36 | } else { 37 | std::cerr << "Adding '" << shortpath << "'" << std::endl; 38 | images[shortpath] = new Media::Image(shortpath, image); 39 | images[shortpath]->origpath = filepath; 40 | } 41 | return true; 42 | } 43 | 44 | Media::Image *Media::get(const char* name) 45 | { 46 | if (!images[name]) { 47 | return NULL; 48 | } 49 | return images[name]; 50 | } 51 | 52 | void Media::debug() 53 | { 54 | std::cerr << "Media Manager:" << std::endl; 55 | for (std::map::const_iterator it = images.begin(); 56 | it != images.end(); 57 | ++it) { 58 | std::cerr << it->second->name.c_str() << " (" << it->second->getHolders() << ")" << std::endl; 59 | } 60 | } 61 | 62 | void Media::clearGrabs() 63 | { 64 | for (std::map::const_iterator it = images.begin(); 65 | it != images.end(); 66 | ++it) { 67 | it->second->dropAll(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/project/media.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MEDIAMANAGER_HPP_INCLUDED 2 | #define MEDIAMANAGER_HPP_INCLUDED 3 | #include "../common.hpp" 4 | #include 5 | #include 6 | 7 | class Media 8 | { 9 | public: 10 | class Image 11 | { 12 | public: 13 | Image(const char *the_name, IImage *the_data): 14 | name(the_name), 15 | data(the_data), 16 | holders(0), 17 | origpath("") 18 | {} 19 | 20 | Image(): 21 | data(NULL) 22 | {} 23 | 24 | std::string name; 25 | std::string origpath; 26 | 27 | IImage *get() const { return data; } 28 | void grab() { holders++; } 29 | void drop() { assert(holders > 0); holders--; } 30 | void dropAll() { holders = 0; } 31 | void deleteImage() { data->drop(); } 32 | unsigned int getHolders() const { return holders; } 33 | void update(IImage *ndata) { data->drop(); data = ndata; } 34 | private: 35 | IImage *data; 36 | unsigned int holders; 37 | }; 38 | 39 | Media() { std::cerr << "Media Manager created!" << std::endl; } 40 | Media(const Media &old) { std::cerr << "Media Manager copied! (This shouldn't happen)" << std::endl; } 41 | ~Media(); 42 | bool import(std::string filepath, std::string filename, IVideoDriver* driver); 43 | bool add(std::string filepath, std::string filename, IImage *image, bool overwrite = false); 44 | Media::Image *get(const char* name); 45 | void clearGrabs(); 46 | void debug(); 47 | std::map& getList() const {return (std::map&)images;}; 48 | private: 49 | std::map images; 50 | }; 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /src/project/node.cpp: -------------------------------------------------------------------------------- 1 | #include "../util/string.hpp" 2 | #include "node.hpp" 3 | 4 | Node::Node(IrrlichtDevice* device, EditorState* state, unsigned int id) : 5 | device(device), 6 | state(state), 7 | _selected(-1), 8 | _nid(id), 9 | _box_count(0), 10 | snap_res(-1) 11 | { 12 | for (int i = 0; i < 6; i++) { 13 | images[i] = NULL; 14 | } 15 | } 16 | 17 | 18 | void Node::setAllTextures(Media::Image *def) 19 | { 20 | if (!def) { 21 | std::cerr << "[Node::setAllTextures] def is NULL" << std::endl; 22 | return; 23 | } 24 | for (int i = 0; i < 6; i++) { 25 | if (images[i]) 26 | images[i]->drop(); 27 | def->grab(); 28 | images[i] = def; 29 | } 30 | } 31 | 32 | Node::~Node() 33 | { 34 | for (int i = 0; i < 6; i++) { 35 | if (images[i]) 36 | images[i]->drop(); 37 | images[i] = NULL; 38 | } 39 | for (std::vector::iterator it = boxes.begin(); 40 | it != boxes.end(); 41 | ++it) { 42 | (*it)->removeMesh(state->device->getVideoDriver()); 43 | delete *it; 44 | } 45 | boxes.clear(); 46 | } 47 | 48 | NodeBox* Node::GetCurrentNodeBox() 49 | { 50 | return GetNodeBox(GetId()); 51 | } 52 | 53 | NodeBox* Node::GetNodeBox(int id) 54 | { 55 | if (id < 0 || id > (int)boxes.size()) { 56 | return NULL; 57 | } 58 | 59 | return boxes[id]; 60 | } 61 | 62 | // Operation functions 63 | NodeBox* Node::addNodeBox(vector3df one, vector3df two) 64 | { 65 | _box_count++; 66 | 67 | // Set up structure 68 | std::string name = "NodeBox" + num_to_str(_box_count); 69 | NodeBox *tmp = new NodeBox(name, one, two); 70 | boxes.push_back(tmp); 71 | 72 | // Select 73 | select(boxes.size() - 1); 74 | 75 | tmp->buildMesh(state, position, device, images); 76 | 77 | return tmp; 78 | } 79 | 80 | void Node::deleteNodebox(int id) 81 | { 82 | if (!GetNodeBox(id)) { 83 | return; 84 | } 85 | 86 | boxes[id]->removeMesh(state->device->getVideoDriver()); 87 | delete boxes[id]; 88 | boxes.erase(boxes.begin() + id); 89 | if (GetId() >= (int)boxes.size()) 90 | _selected = boxes.size() - 1; 91 | } 92 | 93 | void Node::cloneNodebox(int id) 94 | { 95 | NodeBox *nb = GetNodeBox(id); 96 | if (!nb) 97 | return; 98 | 99 | NodeBox *new_nb = addNodeBox(); 100 | new_nb->one = nb->one; 101 | new_nb->two = nb->two; 102 | new_nb->buildMesh(state, position, device, images); 103 | } 104 | 105 | void Node::setTexture(ECUBE_SIDE face, Media::Image *image) 106 | { 107 | if (image) { 108 | if (images[face]) 109 | images[face]->drop(); 110 | image->grab(); 111 | images[face] = image; 112 | } 113 | } 114 | 115 | // Build node models 116 | void Node::remesh(bool force) 117 | { 118 | for (std::vector::iterator it = boxes.begin(); 119 | it != boxes.end(); 120 | ++it) { 121 | (*it)->buildMesh(state, position, device, images, force); 122 | } 123 | } 124 | 125 | void Node::remesh(NodeBox *box) 126 | { 127 | box->buildMesh(state, position, device, images); 128 | } 129 | 130 | void Node::hide() 131 | { 132 | for (std::vector::iterator it = boxes.begin(); 133 | it != boxes.end(); 134 | ++it) { 135 | NodeBox *box = *it; 136 | if (box->model) { 137 | box->model->remove(); 138 | box->model = NULL; 139 | } 140 | } 141 | } 142 | 143 | void Node::rotate(EAxis axis) 144 | { 145 | for (std::vector::iterator it = boxes.begin(); 146 | it != boxes.end(); 147 | ++it) { 148 | (*it)->rotate(axis); 149 | } 150 | remesh(); 151 | } 152 | 153 | void Node::flip(EAxis axis) 154 | { 155 | for (std::vector::iterator it = boxes.begin(); 156 | it != boxes.end(); 157 | ++it) { 158 | (*it)->flip(axis); 159 | } 160 | remesh(); 161 | } 162 | -------------------------------------------------------------------------------- /src/project/node.hpp: -------------------------------------------------------------------------------- 1 | #ifndef NODE_HPP_INCLUDED 2 | #define NODE_HPP_INCLUDED 3 | 4 | #include 5 | #include "../common.hpp" 6 | #include "../EditorState.hpp" 7 | #include "nodebox.hpp" 8 | #include "media.hpp" 9 | 10 | class EditorState; 11 | class NodeBox; 12 | class Node 13 | { 14 | public: 15 | // Construction / Destruction 16 | Node(IrrlichtDevice* device, EditorState* state, unsigned int id); 17 | ~Node(); 18 | 19 | // Node box manager 20 | int GetId() const { return _selected; } 21 | unsigned int NodeId() const { return _nid; } 22 | NodeBox* GetCurrentNodeBox(); 23 | NodeBox* GetNodeBox(int id); 24 | NodeBox* addNodeBox(vector3df one = vector3df(-0.5, -0.5, -0.5), 25 | vector3df two = vector3df(0.5, 0.5, 0.5)); 26 | void deleteNodebox(int id); 27 | void cloneNodebox(int id); 28 | void select(int id) { _selected = id; } 29 | 30 | // Node bulk updaters 31 | void remesh(bool force = false); // creates the node mesh 32 | void remesh(NodeBox *box); 33 | void setAllTextures(Media::Image *def); 34 | void rotate(EAxis axis); 35 | void flip(EAxis axis); 36 | void hide(); 37 | 38 | void setTexture(ECUBE_SIDE face, Media::Image *image); 39 | Media::Image *getTexture(ECUBE_SIDE face) { return images[face]; } 40 | 41 | vector3di position; 42 | std::string name; 43 | std::vector boxes; 44 | int snap_res; 45 | private: 46 | // Data 47 | int _selected; 48 | unsigned int _nid; // the node's id. 49 | int _box_count; 50 | 51 | // Irrlicht 52 | IrrlichtDevice* device; 53 | EditorState* state; 54 | Media::Image *images[6]; 55 | }; 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /src/project/nodebox.cpp: -------------------------------------------------------------------------------- 1 | #include "nodebox.hpp" 2 | 3 | void NodeBox::moveFace(EditorState* editor, ECDR_DIR type, 4 | vector3df position, bool both) 5 | { 6 | vector3df before_one = one; 7 | vector3df before_two = two; 8 | switch(type) { 9 | case CDR_X_P: 10 | if (both) { 11 | f32 new_opp = one.X - (position.X - two.X); 12 | 13 | if (editor->settings->getBool("limiting")==true){ 14 | if (new_opp > 0.5 || new_opp < -0.5) 15 | return; 16 | } 17 | 18 | one.X = new_opp; 19 | } 20 | two.X = position.X; 21 | break; 22 | case CDR_X_N: 23 | if (both) { 24 | f32 new_opp = two.X - (position.X - one.X); 25 | 26 | if (editor->settings->getBool("limiting")==true){ 27 | if (new_opp > 0.5 || new_opp < -0.5) 28 | return; 29 | } 30 | 31 | two.X = new_opp; 32 | } 33 | one.X = position.X; 34 | break; 35 | case CDR_Y_P: 36 | if (both) { 37 | f32 new_opp = one.Y - (position.Y - two.Y); 38 | 39 | if (editor->settings->getBool("limiting")==true){ 40 | if (new_opp > 0.5 || new_opp < -0.5) 41 | return; 42 | } 43 | 44 | one.Y = new_opp; 45 | } 46 | two.Y = position.Y; 47 | break; 48 | case CDR_Y_N: 49 | if (both) { 50 | f32 new_opp = two.Y - (position.Y - one.Y); 51 | 52 | if (editor->settings->getBool("limiting")==true){ 53 | if (new_opp > 0.5 || new_opp < -0.5) 54 | return; 55 | } 56 | 57 | two.Y = new_opp; 58 | } 59 | one.Y = position.Y; 60 | break; 61 | case CDR_Z_P: 62 | if (both) { 63 | f32 new_opp = one.Z - (position.Z - two.Z); 64 | 65 | if (editor->settings->getBool("limiting")==true){ 66 | if (new_opp > 0.5 || new_opp < -0.5) 67 | return; 68 | } 69 | 70 | one.Z = new_opp; 71 | } 72 | two.Z = position.Z; 73 | break; 74 | case CDR_Z_N: 75 | if (both) { 76 | f32 new_opp = two.Z - (position.Z - one.Z); 77 | 78 | if (editor->settings->getBool("limiting")==true){ 79 | if (new_opp > 0.5 || new_opp < -0.5) 80 | return; 81 | } 82 | 83 | two.Z = new_opp; 84 | } 85 | one.Z = position.Z; 86 | break; 87 | } 88 | 89 | if (before_one != one || before_two != two) 90 | rebuild_needed = true; 91 | } 92 | 93 | void NodeBox::move(EditorState* editor, ECDR_DIR type, vector3df position, 94 | const int snap_res) 95 | { 96 | vector3df new_one = one; 97 | vector3df new_two = two; 98 | vector3df move_dist = vector3df(0,0,0); 99 | 100 | switch(type) { 101 | case CDR_XZ: 102 | move_dist = vector3df( 103 | position.X - GetCenter().X, 104 | 0, 105 | position.Z - GetCenter().Z 106 | ); 107 | break; 108 | case CDR_XY: 109 | move_dist = vector3df( 110 | position.X - GetCenter().X, 111 | position.Y - GetCenter().Y, 112 | 0 113 | ); 114 | break; 115 | case CDR_ZY: 116 | move_dist = vector3df( 117 | 0, 118 | position.Y - GetCenter().Y, 119 | position.Z - GetCenter().Z 120 | ); 121 | break; 122 | } 123 | 124 | if (move_dist.X != 0){ 125 | if ( 126 | (new_one.X + move_dist.X <= 0.5 && 127 | new_one.X + move_dist.X >= -0.5 && 128 | new_two.X + move_dist.X <= 0.5 && 129 | new_two.X + move_dist.X >= -0.5) || 130 | !editor->settings->getBool("limiting")) { 131 | new_one.X += move_dist.X; 132 | new_two.X += move_dist.X; 133 | } 134 | } 135 | 136 | if (move_dist.Y != 0) { 137 | if ( 138 | (new_one.Y + move_dist.Y <= 0.5 && 139 | new_one.Y + move_dist.Y >= -0.5 && 140 | new_two.Y + move_dist.Y <= 0.5 && 141 | new_two.Y + move_dist.Y >= -0.5) || 142 | !editor->settings->getBool("limiting")) { 143 | new_one.Y += move_dist.Y; 144 | new_two.Y += move_dist.Y; 145 | } 146 | } 147 | 148 | if (move_dist.Z != 0) { 149 | if ( 150 | (new_one.Z + move_dist.Z <= 0.5 && 151 | new_one.Z + move_dist.Z >= -0.5 && 152 | new_two.Z + move_dist.Z <= 0.5 && 153 | new_two.Z + move_dist.Z >= -0.5) || 154 | !editor->settings->getBool("limiting")) { 155 | new_one.Z += move_dist.Z; 156 | new_two.Z += move_dist.Z; 157 | } 158 | } 159 | 160 | if (snap_res > 0) { 161 | vector3df snapped_one( 162 | (f32)floor((new_one.X + 0.5) * snap_res + 0.5) / snap_res - 0.5, 163 | (f32)floor((new_one.Y + 0.5) * snap_res + 0.5) / snap_res - 0.5, 164 | (f32)floor((new_one.Z + 0.5) * snap_res + 0.5) / snap_res - 0.5); 165 | new_two += snapped_one - new_one; 166 | new_one = snapped_one; 167 | } 168 | 169 | one = new_one; 170 | two = new_two; 171 | 172 | if (move_dist != vector3df(0, 0, 0)) 173 | rebuild_needed = true; 174 | } 175 | 176 | ITexture* darken(IVideoDriver* driver, IImage* image, f32 amt, const char *name) 177 | { 178 | if (image == NULL) 179 | return NULL; 180 | 181 | core::dimension2d dim = image->getDimension(); 182 | IImage* image2 = driver->createImage(image->getColorFormat(), dim); 183 | image->copyTo(image2); 184 | 185 | for(u32 y=0; ygetPixel(x,y); 188 | c.setRed((u32)(amt * c.getRed() + 0.5f)); 189 | c.setGreen((u32)(amt * c.getGreen() + 0.5f)); 190 | c.setBlue((u32)(amt * c.getBlue() + 0.5f)); 191 | image2->setPixel(x, y, c); 192 | } 193 | } 194 | 195 | ITexture *retval = driver->addTexture(name, image2); 196 | image2->drop(); 197 | return retval; 198 | } 199 | 200 | void NodeBox::removeMesh(IVideoDriver *driver) 201 | { 202 | if (model) { 203 | for (int i = 0; i < model->getMaterialCount(); i++) 204 | driver->removeTexture(model->getMaterial(i).getTexture(0)); 205 | 206 | model->remove(); 207 | model = NULL; 208 | } 209 | } 210 | 211 | void NodeBox::buildMesh(EditorState* editor, vector3di nd_position, 212 | IrrlichtDevice* device, Media::Image* images[6], bool force) 213 | { 214 | if (!rebuild_needed && !force) 215 | return; 216 | 217 | rebuild_needed = false; 218 | 219 | video::IVideoDriver* driver = device->getVideoDriver(); 220 | static Media::Image *def = new Media::Image("default", driver->createImageFromFile("media/texture_box.png")); 221 | 222 | Media::Image *copied[6]; 223 | for (int i = 0; i < 6; i++) { 224 | copied[i] = images[i]; 225 | if (!copied[i]) 226 | copied[i] = def; 227 | } 228 | ISceneManager* smgr = device->getSceneManager(); 229 | 230 | removeMesh(driver); 231 | 232 | vector3df position = vector3df( 233 | nd_position.X + one.X + ((two.X - one.X) / 2), 234 | nd_position.Y + one.Y + ((two.Y - one.Y) / 2), 235 | nd_position.Z + one.Z + ((two.Z - one.Z) / 2) 236 | ); 237 | vector3df size = vector3df( 238 | two.X - one.X, 239 | two.Y - one.Y, 240 | two.Z - one.Z 241 | ); 242 | 243 | // init variables 244 | f32 cubeSize = 1.f; 245 | video::SColor cubeColour(255, 255, 255, 255); 246 | 247 | // Initialise buffers 248 | SMeshBuffer *buffer = new SMeshBuffer(); 249 | SMeshBuffer *buffer2 = new SMeshBuffer(); 250 | SMeshBuffer *buffer3 = new SMeshBuffer(); 251 | SMeshBuffer *buffer4 = new SMeshBuffer(); 252 | SMeshBuffer *buffer5 = new SMeshBuffer(); 253 | SMeshBuffer *buffer6 = new SMeshBuffer(); 254 | buffer->Indices.set_used(6); 255 | buffer2->Indices.set_used(6); 256 | buffer3->Indices.set_used(6); 257 | buffer4->Indices.set_used(6); 258 | buffer5->Indices.set_used(6); 259 | buffer6->Indices.set_used(6); 260 | u16 u[6] = {0,2,1, 0,3,2}; 261 | for (s32 i=0; i<6; ++i) { 262 | buffer->Indices[i] = u[i]; 263 | buffer2->Indices[i] = u[i]; 264 | buffer3->Indices[i] = u[i]; 265 | buffer4->Indices[i] = u[i]; 266 | buffer5->Indices[i] = u[i]; 267 | buffer6->Indices[i] = u[i]; 268 | } 269 | 270 | // Init mesh 271 | SMesh * cubeMesh = new SMesh(); 272 | 273 | #define x0 -0.5f 274 | #define x1 0.5f 275 | 276 | std::string lighting = editor->settings->get("lighting"); 277 | 278 | // Front face 279 | vector2df topl((one.X + 0.5f), (-two.Y + 0.5f)); 280 | vector2df btmr((two.X + 0.5f), (-one.Y + 0.5f)); 281 | buffer->Vertices.set_used(4); 282 | buffer->Vertices[0] = video::S3DVertex(x0,x0,x0, -1,-1,-1, cubeColour, topl.X, btmr.Y); 283 | buffer->Vertices[1] = video::S3DVertex(x1,x0,x0, 1,-1,-1, cubeColour, btmr.X, btmr.Y); 284 | buffer->Vertices[2] = video::S3DVertex(x1,x1,x0, 1, 1,-1, cubeColour, btmr.X, topl.Y); 285 | buffer->Vertices[3] = video::S3DVertex(x0,x1,x0, -1, 1,-1, cubeColour, topl.X, topl.Y); 286 | buffer->BoundingBox.reset(0,0,0); 287 | ITexture *texture = NULL; 288 | if (lighting == "1" || lighting == "2") 289 | texture = darken(driver, copied[ECS_FRONT]->get(), 0.5f, copied[ECS_RIGHT]->name.c_str()); 290 | else 291 | texture = driver->addTexture(copied[ECS_FRONT]->name.c_str(), copied[ECS_FRONT]->get()); 292 | SMaterial mat = SMaterial(); 293 | mat.setTexture(0, texture); 294 | buffer->Material = mat; 295 | cubeMesh->addMeshBuffer(buffer); 296 | buffer->drop(); 297 | 298 | 299 | // Back face 300 | topl = vector2df((-two.X + 0.5f), (-two.Y + 0.5f)); 301 | btmr = vector2df((-one.X + 0.5f), (-one.Y + 0.5f)); 302 | buffer2->Vertices.set_used(4); 303 | buffer2->Vertices[0] = video::S3DVertex(x1,x0,x1, 1, -1, 1, cubeColour, topl.X, btmr.Y); 304 | buffer2->Vertices[1] = video::S3DVertex(x0,x0,x1, -1,-1, 1, cubeColour, btmr.X, btmr.Y); 305 | buffer2->Vertices[2] = video::S3DVertex(x0,x1,x1, -1, 1, 1, cubeColour, btmr.X, topl.Y); 306 | buffer2->Vertices[3] = video::S3DVertex(x1,x1,x1, 1, 1, 1, cubeColour, topl.X, topl.Y); 307 | buffer2->BoundingBox.reset(0,0,0); 308 | texture = NULL; 309 | if (lighting == "1" || lighting == "2") 310 | texture = darken(driver, copied[ECS_BACK]->get(), 0.5f, copied[ECS_BACK]->name.c_str()); 311 | else 312 | texture = driver->addTexture(copied[ECS_BACK]->name.c_str(), copied[ECS_BACK]->get()); 313 | mat = SMaterial(); 314 | mat.setTexture(0, texture); 315 | buffer2->Material = mat; 316 | cubeMesh->addMeshBuffer(buffer2); 317 | buffer2->drop(); 318 | 319 | 320 | // Left face 321 | topl = vector2df((-two.Z + 0.5f), (-two.Y + 0.5f)); 322 | btmr = vector2df((-one.Z + 0.5f), (-one.Y + 0.5f)); 323 | buffer3->Vertices.set_used(4); 324 | buffer3->Vertices[0] = video::S3DVertex(x0,x0,x1, -1,-1, 1, cubeColour, topl.X, btmr.Y); 325 | buffer3->Vertices[1] = video::S3DVertex(x0,x0,x0, -1,-1,-1, cubeColour, btmr.X, btmr.Y); 326 | buffer3->Vertices[2] = video::S3DVertex(x0,x1,x0, -1, 1,-1, cubeColour, btmr.X, topl.Y); 327 | buffer3->Vertices[3] = video::S3DVertex(x0,x1,x1, -1, 1, 1, cubeColour, topl.X, topl.Y); 328 | buffer3->BoundingBox.reset(0,0,0); 329 | texture = NULL; 330 | if (lighting == "1" || lighting == "2") 331 | texture = darken(driver, copied[ECS_LEFT]->get(), 0.7f, copied[ECS_LEFT]->name.c_str()); 332 | else 333 | texture = driver->addTexture(copied[ECS_LEFT]->name.c_str(), copied[ECS_LEFT]->get()); 334 | mat = SMaterial(); 335 | mat.setTexture(0, texture); 336 | buffer3->Material = mat; 337 | cubeMesh->addMeshBuffer(buffer3); 338 | buffer3->drop(); 339 | 340 | 341 | // Right face 342 | topl = vector2df((one.Z + 0.5f), (-two.Y + 0.5f)); 343 | btmr = vector2df((two.Z + 0.5f), (-one.Y + 0.5f)); 344 | buffer4->Vertices.set_used(4); 345 | buffer4->Vertices[0] = video::S3DVertex(x1,x0,x0, 1,-1,-1, cubeColour, topl.X, btmr.Y); 346 | buffer4->Vertices[1] = video::S3DVertex(x1,x0,x1, 1,-1, 1, cubeColour, btmr.X, btmr.Y); 347 | buffer4->Vertices[2] = video::S3DVertex(x1,x1,x1, 1, 1, 1, cubeColour, btmr.X, topl.Y); 348 | buffer4->Vertices[3] = video::S3DVertex(x1,x1,x0, 1, 1,-1, cubeColour, topl.X, topl.Y); 349 | buffer4->BoundingBox.reset(0,0,0); 350 | texture = NULL; 351 | if (lighting == "1" || lighting == "2") 352 | texture = darken(driver, copied[ECS_RIGHT]->get(), 0.7f, copied[ECS_RIGHT]->name.c_str()); 353 | else 354 | texture = driver->addTexture(copied[ECS_RIGHT]->name.c_str(), copied[ECS_RIGHT]->get()); 355 | mat = SMaterial(); 356 | mat.setTexture(0, texture); 357 | buffer4->Material = mat; 358 | cubeMesh->addMeshBuffer(buffer4); 359 | buffer4->drop(); 360 | 361 | 362 | // Top face 363 | topl = vector2df((one.X + 0.5f), (-two.Z + 0.5f)); 364 | btmr = vector2df((two.X + 0.5f), (-one.Z + 0.5f)); 365 | buffer5->Vertices.set_used(4); 366 | buffer5->Vertices[0] = video::S3DVertex(x0,x1,x0, -1, 1,-1, cubeColour, topl.X, btmr.Y); 367 | buffer5->Vertices[1] = video::S3DVertex(x1,x1,x0, 1, 1,-1, cubeColour, btmr.X, btmr.Y); 368 | buffer5->Vertices[2] = video::S3DVertex(x1,x1,x1, 1, 1, 1, cubeColour, btmr.X, topl.Y); 369 | buffer5->Vertices[3] = video::S3DVertex(x0,x1,x1, -1, 1, 1, cubeColour, topl.X, topl.Y); 370 | buffer5->BoundingBox.reset(0,0,0); 371 | texture = NULL; 372 | if (lighting == "1") 373 | texture = darken(driver, copied[ECS_TOP]->get(), 0.7f, copied[ECS_TOP]->name.c_str()); 374 | else 375 | texture = driver->addTexture(copied[ECS_TOP]->name.c_str(), copied[ECS_TOP]->get()); 376 | mat = SMaterial(); 377 | mat.setTexture(0, texture); 378 | buffer5->Material = mat; 379 | cubeMesh->addMeshBuffer(buffer5); 380 | buffer5->drop(); 381 | 382 | 383 | // Bottom face 384 | topl = vector2df((-one.X + 0.5f), (-one.Z + 0.5f)); 385 | btmr = vector2df((-two.X + 0.5f), (-two.Z + 0.5f)); 386 | buffer6->Vertices.set_used(4); 387 | buffer6->Vertices[0] = video::S3DVertex(x0,x0,x1, -1,-1, 1, cubeColour, topl.X, btmr.Y); 388 | buffer6->Vertices[1] = video::S3DVertex(x1,x0,x1, 1,-1, 1, cubeColour, btmr.X, btmr.Y); 389 | buffer6->Vertices[2] = video::S3DVertex(x1,x0,x0, 1,-1,-1, cubeColour, btmr.X, topl.Y); 390 | buffer6->Vertices[3] = video::S3DVertex(x0,x0,x0, -1,-1,-1, cubeColour, topl.X, topl.Y); 391 | buffer6->BoundingBox.reset(0,0,0); 392 | if (lighting == "1" || lighting == "2") 393 | texture = darken(driver, copied[ECS_BOTTOM]->get(), 0.4f, copied[ECS_BOTTOM]->name.c_str()); 394 | else 395 | texture = driver->addTexture(copied[ECS_BOTTOM]->name.c_str(), copied[ECS_BOTTOM]->get()); 396 | mat = SMaterial(); 397 | mat.setTexture(0, texture); 398 | buffer6->Material = mat; 399 | cubeMesh->addMeshBuffer(buffer6); 400 | buffer6->drop(); 401 | 402 | 403 | // Create scene node from mesh 404 | model = smgr->addMeshSceneNode(cubeMesh); 405 | cubeMesh->drop(); 406 | model->setPosition(position); 407 | model->setScale(size); 408 | model->setMaterialFlag(EMF_BILINEAR_FILTER, false); 409 | model->setMaterialFlag(EMF_LIGHTING, false); 410 | } 411 | 412 | void NodeBox::rotate(EAxis axis) 413 | { 414 | switch (axis) { 415 | case EAX_X: { 416 | f32 tmp = one.X; 417 | one.X = one.Y; 418 | one.Y = -tmp; 419 | tmp = two.X; 420 | two.X = two.Y; 421 | two.Y = -tmp; 422 | break; 423 | } 424 | case EAX_Y: { 425 | f32 tmp = one.X; 426 | one.X = one.Z; 427 | one.Z = -tmp; 428 | tmp = two.X; 429 | two.X = two.Z; 430 | two.Z = -tmp; 431 | break; 432 | } 433 | case EAX_Z: { 434 | f32 tmp = one.Z; 435 | one.Z = one.Y; 436 | one.Y = -tmp; 437 | tmp = two.Z; 438 | two.Z = two.Y; 439 | two.Y = -tmp; 440 | break; 441 | }}; 442 | 443 | // Check relative sizes 444 | if (one.X > two.X) { 445 | f32 tmp = one.X; 446 | one.X = two.X; 447 | two.X = tmp; 448 | } 449 | if (one.Y > two.Y) { 450 | f32 tmp = one.Y; 451 | one.Y = two.Y; 452 | two.Y = tmp; 453 | } 454 | if (one.Z > two.Z) { 455 | f32 tmp = one.Z; 456 | one.Z = two.Z; 457 | two.Z = tmp; 458 | } 459 | rebuild_needed = true; 460 | } 461 | 462 | void NodeBox::flip(EAxis axis) 463 | { 464 | switch (axis) { 465 | case EAX_X: { 466 | f32 tmp = one.X; 467 | one.X = -two.X; 468 | two.X = -tmp; 469 | break; 470 | } 471 | case EAX_Y: { 472 | f32 tmp = one.Y; 473 | one.Y = -two.Y; 474 | two.Y = -tmp; 475 | break; 476 | } 477 | case EAX_Z: { 478 | f32 tmp = one.Z; 479 | one.Z = -two.Z; 480 | two.Z = -tmp; 481 | break; 482 | }}; 483 | 484 | // Check relative sizes 485 | if (one.X > two.X) { 486 | std::cerr << "This shouldn't happen! (X)" << std::endl; 487 | f32 tmp = one.X; 488 | one.X = two.X; 489 | two.X = tmp; 490 | } 491 | if (one.Y > two.Y) { 492 | std::cerr << "This shouldn't happen! (Y)" << std::endl; 493 | f32 tmp = one.Y; 494 | one.Y = two.Y; 495 | two.Y = tmp; 496 | } 497 | if (one.Z > two.Z) { 498 | std::cerr << "This shouldn't happen! (Z)" << std::endl; 499 | f32 tmp = one.Z; 500 | one.Z = two.Z; 501 | two.Z = tmp; 502 | } 503 | rebuild_needed = true; 504 | } 505 | -------------------------------------------------------------------------------- /src/project/nodebox.hpp: -------------------------------------------------------------------------------- 1 | #ifndef NODEBOX_HPP_INCLUDED 2 | #define NODEBOX_HPP_INCLUDED 3 | 4 | #include 5 | #include "../common.hpp" 6 | #include "../EditorState.hpp" 7 | #include "media.hpp" 8 | 9 | class EditorState; 10 | class NodeBox 11 | { 12 | public: 13 | NodeBox() {}; 14 | 15 | NodeBox(const std::string & name, const vector3df & one, const vector3df & two) : 16 | name(name), one(one), two(two), model(NULL), rebuild_needed(true) 17 | {} 18 | 19 | void removeMesh(IVideoDriver *driver); 20 | 21 | irr::core::vector3df one; 22 | irr::core::vector3df two; 23 | bool rebuild_needed; 24 | std::string name; 25 | irr::scene::IMeshSceneNode* model; 26 | 27 | irr::core::vector3df GetCenter() 28 | { 29 | return vector3df( 30 | one.X + ((two.X - one.X) / 2), 31 | one.Y + ((two.Y - one.Y) / 2), 32 | one.Z + ((two.Z - one.Z) / 2) 33 | ); 34 | } 35 | 36 | irr::core::vector3df GetScale() 37 | { 38 | return vector3df( 39 | (two.X - one.X) / 2, 40 | (two.Y - one.Y) / 2, 41 | (two.Z - one.Z) / 2 42 | ); 43 | } 44 | 45 | // Transformations 46 | void moveFace(EditorState* editor, ECDR_DIR type, 47 | vector3df position, bool both); 48 | void move(EditorState* editor, ECDR_DIR type, vector3df position, 49 | const int snap_res=0); 50 | void rotate(EAxis axis); 51 | void flip(EAxis axis); 52 | 53 | // Create the mesh for the nodebox, store is in this->model. 54 | // 55 | // Only runs if rebuild_needed is true. 56 | void buildMesh(EditorState* editor, vector3di nd_position, 57 | IrrlichtDevice* device, Media::Image* images[6], bool force = false); 58 | }; 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /src/project/project.cpp: -------------------------------------------------------------------------------- 1 | #include "project.hpp" 2 | #include "node.hpp" 3 | #include "../util/string.hpp" 4 | 5 | Project::Project() : 6 | name("test"), 7 | snode(-1), 8 | _node_count(0) 9 | { 10 | } 11 | 12 | Project::~Project() 13 | { 14 | for (std::list::const_iterator it = nodes.begin(); 15 | it != nodes.end(); 16 | ++it) { 17 | if (*it) { 18 | delete *it; 19 | } 20 | } 21 | } 22 | 23 | Node* Project::GetNode(int id) const 24 | { 25 | int curid = 0; 26 | for (std::list::const_iterator it = nodes.begin(); 27 | it != nodes.end(); 28 | ++it, ++curid) { 29 | if (curid == id) { 30 | return *it; 31 | } 32 | } 33 | return NULL; 34 | } 35 | 36 | 37 | void Project::hideAllButCurrentNode() 38 | { 39 | int curid = 0; 40 | for (std::list::const_iterator it = nodes.begin(); 41 | it != nodes.end(); 42 | ++it, ++curid) { 43 | if (snode == curid) { 44 | (*it)->remesh(); 45 | } else { 46 | (*it)->hide(); 47 | } 48 | } 49 | } 50 | 51 | Node* Project::GetNode(vector3di pos) const 52 | { 53 | for (std::list::const_iterator it = nodes.begin(); 54 | it != nodes.end(); 55 | ++it) { 56 | if (*it && (*it)->position == pos) { 57 | return *it; 58 | } 59 | } 60 | return NULL; 61 | } 62 | 63 | void Project::remesh() 64 | { 65 | for (std::list::const_iterator it = nodes.begin(); 66 | it != nodes.end(); 67 | ++it) { 68 | if (*it) { 69 | (*it)->remesh(); 70 | } 71 | } 72 | } 73 | 74 | void Project::AddNode(EditorState* state, bool select, bool add_initial_box) 75 | { 76 | Node* node = new Node(state->device, state, _node_count); 77 | if (add_initial_box) 78 | node->addNodeBox(); 79 | AddNode(node, select); 80 | } 81 | 82 | void Project::AddNode(Node* node, bool select) 83 | { 84 | _node_count++; 85 | if (node->name == "") { 86 | node->name = "node_" + num_to_str(_node_count); 87 | } 88 | if (node->position == vector3di(0, 0, 0)) 89 | node->position = vector3di((_node_count - 1), 0, 0); 90 | node->remesh(); 91 | nodes.push_back(node); 92 | if (select) { 93 | snode = _node_count - 1; 94 | } 95 | } 96 | 97 | void Project::DeleteNode(int id) 98 | { 99 | if (snode == id) { 100 | snode = -1; 101 | } 102 | 103 | int curid = 0; 104 | for (std::list::iterator it = nodes.begin(); 105 | it != nodes.end(); 106 | ++it, ++curid) { 107 | if (*it && curid == id){ 108 | delete *it; 109 | it = nodes.erase(it); 110 | return; 111 | } 112 | } 113 | } 114 | 115 | Node* Project::GetCurrentNode() const 116 | { 117 | if (snode >= 0) { 118 | return GetNode(snode); 119 | } else { 120 | return NULL; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/project/project.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PROJECT_HPP_INCLUDED 2 | #define PROJECT_HPP_INCLUDED 3 | 4 | #include 5 | #include 6 | #include "../common.hpp" 7 | #include "../EditorState.hpp" 8 | #include "media.hpp" 9 | #include "node.hpp" 10 | 11 | class Node; 12 | class EditorState; 13 | 14 | class Project 15 | { 16 | public: 17 | Project(); 18 | ~Project(); 19 | 20 | // Properties 21 | std::string name; 22 | std::string file; 23 | 24 | // Media 25 | Media media; 26 | 27 | // Nodes 28 | void AddNode(EditorState* state, bool select = true, bool add_initial_box = true); 29 | void AddNode(Node* node, bool select = true); 30 | void DeleteNode(int id); 31 | void SelectNode(int id) { snode = id; } 32 | void hideAllButCurrentNode(); 33 | void remesh(); 34 | Node* GetNode(int id) const; 35 | Node* GetNode(vector3di pos) const; 36 | Node* GetCurrentNode() const; 37 | int GetSelectedNodeId() const { return snode; } 38 | unsigned int GetNodeCount() const { return _node_count; } 39 | 40 | std::list nodes; 41 | private: 42 | int snode; 43 | unsigned int _node_count; 44 | }; 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /src/util/SimpleFileCombiner.cpp: -------------------------------------------------------------------------------- 1 | #include "SimpleFileCombiner.hpp" 2 | #include 3 | #include 4 | #include 5 | 6 | std::vector ReadAllBytes(char const* filename) 7 | { 8 | std::ifstream ifs(filename, std::ios::binary|std::ios::ate); 9 | 10 | if (!ifs) { 11 | std::cerr << "Error! Unable to open file '" << filename << "' in SimpleFileCombiner/ReadAllBytes" << std::endl; 12 | return std::vector(0); 13 | } 14 | 15 | std::ifstream::pos_type pos = ifs.tellg(); 16 | 17 | std::vector result(pos); 18 | 19 | ifs.seekg(0, std::ios::beg); 20 | ifs.read(&result[0], pos); 21 | 22 | return result; 23 | } 24 | 25 | bool SimpleFileCombiner::write(std::string filename) { 26 | std::ofstream output(filename.c_str(), std::ios::binary|std::ios::out); 27 | if (!output) { 28 | errcode = EERR_IO; 29 | return false; 30 | } 31 | output.write("NBEFP", 5); 32 | output << (char)files.size(); 33 | unsigned int start = files.size() * sizeofdef + 6; 34 | for (std::list::const_iterator it = files.begin(); 35 | it != files.end(); 36 | ++it) { 37 | SimpleFileCombiner::File file = *it; 38 | std::string name = file.name; 39 | unsigned int size = file.bytes.size(); 40 | std::cerr << "(SFC) Writing " << name.c_str() << ": " << start << " (" << size << ")" << std::endl; 41 | while (name.size() < 50) { 42 | name += " "; 43 | } 44 | output << name.c_str(); 45 | output.write(static_cast(static_cast(&start)), sizeof(unsigned int)); 46 | output.write(static_cast(static_cast(&size)), sizeof(unsigned int)); 47 | start += size; 48 | 49 | } 50 | for (std::list::const_iterator it = files.begin(); 51 | it != files.end(); 52 | ++it) { 53 | SimpleFileCombiner::File file = *it; 54 | output.write(&file.bytes[0], file.bytes.size()); 55 | } 56 | output.close(); 57 | return true; 58 | } 59 | bool SimpleFileCombiner::add(const char* readfrom, std::string file) 60 | { 61 | files.push_back(File(file, ReadAllBytes(readfrom))); 62 | return true; 63 | } 64 | std::list SimpleFileCombiner::read(const char* file, std::string dir) 65 | { 66 | // Start reading 67 | std::ifstream ifs(file, std::ios::binary|std::ios::ate); 68 | if (!ifs) { 69 | errcode = EERR_IO; 70 | return std::list(); 71 | } 72 | 73 | std::string start(5, '\0'); 74 | ifs.seekg(0, std::ios::beg); 75 | ifs.read(static_cast(static_cast(&start[0])), 5); 76 | if (start != "NBEFP") { 77 | errcode = EERR_WRONG_FILE; 78 | return std::list(); 79 | } 80 | 81 | // Read header 82 | char amount = 0; 83 | ifs.seekg(5, std::ios::beg); 84 | ifs.read(&amount, 1); 85 | std::list result; 86 | 87 | // Loop through files 88 | for (int f = 0; f < (int)amount; f++) { 89 | std::string name(50, '\0'); 90 | ifs.seekg(f * sizeofdef + 6, std::ios::beg); 91 | ifs.read(static_cast(static_cast(&name[0])), 50); 92 | name = trim(name); 93 | result.push_back(name); 94 | 95 | // Get start location 96 | unsigned int start = 0; 97 | ifs.seekg(f * sizeofdef + 56, std::ios::beg); 98 | ifs.read(static_cast(static_cast(&start)), sizeof(unsigned int)); 99 | 100 | // Get size 101 | unsigned int size = 0; 102 | ifs.seekg(f * sizeofdef + 60, std::ios::beg); 103 | ifs.read(static_cast(static_cast(&size)), sizeof(unsigned int)); 104 | std::cerr << "(SFC) Reading " << name.c_str() << ": " << start << " (" << size << ")" << std::endl; 105 | 106 | // Read and save data 107 | std::string data(size, '\0'); 108 | ifs.seekg(start, std::ios::beg); 109 | ifs.read(static_cast(static_cast(&data[0])), size); 110 | std::ofstream output((dir + "/" + name).c_str(), std::ios::binary|std::ios::out); 111 | output.write(static_cast(static_cast(&data[0])), size); 112 | output.close(); 113 | } 114 | return result; 115 | } 116 | -------------------------------------------------------------------------------- /src/util/SimpleFileCombiner.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SIMPLEFILECOMBINER_HPP_INCLUDED 2 | #define SIMPLEFILECOMBINER_HPP_INCLUDED 3 | 4 | #include "string.hpp" 5 | #include 6 | #include 7 | 8 | 9 | 10 | class SimpleFileCombiner 11 | { 12 | public: 13 | enum Errors 14 | { 15 | EERR_NONE = 0, 16 | EERR_IO, 17 | EERR_WRONG_FILE 18 | }; 19 | 20 | SimpleFileCombiner(): 21 | errcode(EERR_NONE) 22 | {} 23 | 24 | static const unsigned int sizeofdef = 50 + 2 * sizeof(unsigned int); 25 | class File 26 | { 27 | public: 28 | File(std::string tname, std::vector tbytes): 29 | name(tname), 30 | bytes(tbytes){} 31 | std::string name; 32 | std::vector bytes; 33 | }; 34 | std::list files; 35 | bool write(std::string filename); 36 | bool add(const char* readfrom, std::string file); 37 | std::list read(const char* file, std::string dir); 38 | SimpleFileCombiner::Errors errcode; 39 | }; 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /src/util/filesys.cpp: -------------------------------------------------------------------------------- 1 | #include "filesys.hpp" 2 | #include 3 | #include "string.hpp" 4 | #include 5 | 6 | std::string cleanDirectoryPath(std::string &path) 7 | { 8 | if (path == "") 9 | return ""; 10 | 11 | std::string r(path); 12 | if (*r.rbegin() != '/') 13 | r += "/"; 14 | return r; 15 | } 16 | 17 | std::string getSaveLoadDirectory(std::string save_dir_setting, bool editor_is_installed) 18 | { 19 | std::string dir = save_dir_setting; 20 | 21 | #ifndef _WIN32 22 | if (dir == "" && editor_is_installed) { 23 | dir = getenv("HOME"); 24 | } 25 | #endif 26 | 27 | return cleanDirectoryPath(dir); 28 | } 29 | 30 | std::string getTmpDirectory(bool editor_is_installed) 31 | { 32 | #ifndef _WIN32 33 | if (editor_is_installed) { 34 | std::string res = std::string(getenv("HOME")) + "/.nbetmp/"; 35 | std::cerr << "Tmpdir requested. Gave " << res.c_str() << std::endl; 36 | return res; 37 | } 38 | #endif 39 | std::cerr << "Tmpdir requested. Gave .tmp/" << std::endl; 40 | return ".tmp/"; 41 | } 42 | 43 | // This code was nicked from Minetest, subject to LGPLv2 44 | // See http://minetest.net 45 | #ifdef _WIN32 46 | #include 47 | 48 | bool FileExists(const char* path) 49 | { 50 | DWORD dwAttrib = GetFileAttributesA(path); 51 | 52 | return (dwAttrib != INVALID_FILE_ATTRIBUTES && 53 | !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); 54 | } 55 | 56 | bool DirExists(const char* path) 57 | { 58 | DWORD ftyp = GetFileAttributesA(path); 59 | if (ftyp == INVALID_FILE_ATTRIBUTES) 60 | return false; 61 | 62 | if (ftyp & FILE_ATTRIBUTE_DIRECTORY) 63 | return true; 64 | 65 | return false; 66 | } 67 | 68 | bool CreateDir(std::string path) 69 | { 70 | bool r = CreateDirectory(path.c_str(), NULL); 71 | if(r == true) 72 | return true; 73 | if(GetLastError() == ERROR_ALREADY_EXISTS) 74 | return true; 75 | return false; 76 | } 77 | 78 | std::vector filesInDirectory(std::string path) 79 | { 80 | std::vector res; 81 | res.push_back("No file browser for Windows yet :/"); 82 | return res; 83 | } 84 | 85 | #else 86 | #include 87 | #include 88 | #include 89 | #include 90 | 91 | bool FileExists(const char* path) 92 | { 93 | struct stat st; 94 | if (stat(path, &st) == -1) 95 | return false; 96 | return S_ISREG(st.st_mode); 97 | } 98 | 99 | bool DirExists(const char* path) 100 | { 101 | struct stat st; 102 | if (stat(path, &st) == -1) 103 | return false; 104 | return S_ISDIR(st.st_mode); 105 | } 106 | 107 | bool CreateDir(std::string path) 108 | { 109 | int r = mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); 110 | if(r == 0) { 111 | return true; 112 | } else { 113 | // If already exists, return true 114 | if(errno == EEXIST) 115 | return true; 116 | return false; 117 | } 118 | } 119 | 120 | std::vector filesInDirectory(std::string path) 121 | { 122 | std::vector res; 123 | if (path == "") 124 | path = "."; 125 | DIR *dirp = opendir(path.c_str()); 126 | if (!dirp) { 127 | std::cerr << "Failed to open directory" << std::endl; 128 | return std::vector(); 129 | } 130 | while (dirent *dp = readdir(dirp)) { 131 | res.push_back(std::string(dp->d_name)); 132 | } 133 | (void)closedir(dirp); 134 | return res; 135 | } 136 | 137 | #endif 138 | 139 | 140 | std::string filenameWithExt(std::string path) 141 | { 142 | size_t pos = str_replace(path, '\\', '/').find_last_of("/"); 143 | if (pos >= path.size() || pos < 0) 144 | return path; 145 | 146 | return path.substr(pos + 1); 147 | } 148 | 149 | std::string filenameWithoutExt(std::string path) 150 | { 151 | std::string res = filenameWithExt(path); 152 | size_t pos = res.find_last_of("."); 153 | if (pos > res.size() || pos < 0) 154 | return res; 155 | return res.substr(0, pos); 156 | } 157 | 158 | 159 | std::string extFromFilename(std::string path) 160 | { 161 | std::string res = filenameWithExt(path); 162 | size_t pos = res.find_last_of("."); 163 | if (pos > res.size() || pos < 0) 164 | return ""; 165 | return res.substr(pos + 1, res.size()); 166 | } 167 | 168 | std::string pathWithoutFilename(std::string path) 169 | { 170 | size_t pos = str_replace(path, '\\', '/').find_last_of("/"); 171 | if (pos >= path.size() || pos < 0) 172 | return ""; 173 | 174 | return str_replace(str_replace(path.substr(0, pos), '\\', DIR_DELIM), '/', DIR_DELIM); 175 | } 176 | -------------------------------------------------------------------------------- /src/util/filesys.hpp: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_FILESYS_HPP_INCLUDED 2 | #define UTIL_FILESYS_HPP_INCLUDED 3 | #include "string.hpp" 4 | #include 5 | 6 | #ifdef _WIN32 7 | #define DIR_DELIM '\\' 8 | #else 9 | #define DIR_DELIM '/' 10 | #endif 11 | 12 | std::string getSaveLoadDirectory(std::string save_dir_setting, bool editor_is_installed); 13 | 14 | std::string cleanDirectoryPath(std::string &path); 15 | 16 | std::string getTmpDirectory(bool editor_is_installed); 17 | 18 | bool FileExists(const char* path); 19 | bool DirExists(const char* path); 20 | 21 | bool CreateDir(std::string path); 22 | 23 | std::vector filesInDirectory(std::string path); 24 | 25 | std::string filenameWithExt(std::string path); 26 | std::string extFromFilename(std::string path); 27 | std::string filenameWithoutExt(std::string path); 28 | std::string pathWithoutFilename(std::string path); 29 | 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /src/util/string.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "string.hpp" 4 | 5 | std::string trim(const std::string & str) 6 | { 7 | std::string s(str); 8 | s.erase(s.find_last_not_of(" \t\r\n") + 1); 9 | s.erase(0, s.find_first_not_of(" \t\r\n")); 10 | return s; 11 | } 12 | 13 | 14 | std::string str_to_lower(const std::string & str) 15 | { 16 | std::string s(str); 17 | std::transform(s.begin(), s.end(), s.begin(), tolower); 18 | return s; 19 | } 20 | 21 | 22 | std::wstring narrow_to_wide(const std::string & input) 23 | { 24 | wchar_t * wide = new wchar_t[input.size() * 2]; 25 | size_t newlen = mbstowcs(wide, input.data(), input.size()); 26 | return std::wstring(wide, newlen); 27 | } 28 | 29 | 30 | std::string str_replace(const std::string & str, char f, char r) 31 | { 32 | std::string s(str); 33 | std::replace(s.begin(), s.end(), f, r); 34 | return s; 35 | } 36 | -------------------------------------------------------------------------------- /src/util/string.hpp: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_STRING_HPP_INCLUDED 2 | #define UTIL_STRING_HPP_INCLUDED 3 | 4 | #include 5 | #include 6 | 7 | extern std::string trim(const std::string & str); 8 | extern std::string str_to_lower(const std::string & str); 9 | extern std::wstring narrow_to_wide(const std::string & input); 10 | extern std::string str_replace(const std::string & str, char f, char r); 11 | 12 | 13 | template 14 | const std::string num_to_str(T num) 15 | { 16 | std::ostringstream os; 17 | os << num; 18 | return os.str(); 19 | } 20 | 21 | #endif // UTIL_STRING_HPP_INCLUDED 22 | -------------------------------------------------------------------------------- /src/util/tinyfiledialogs.h: -------------------------------------------------------------------------------- 1 | /* 2 | tinyfiledialogs.h 3 | optional unique header file of the tiny file dialogs library - tinyfd 4 | created [November 9, 2014] 5 | Copyright (c) 2014 - 2015 Guillaume Vareille http://ysengrin.com 6 | http://tinyfiledialogs.sourceforge.net 7 | 8 | tiny file dialogs - tinyfd - version 1.7.0 [January 30, 2015] zlib licence. 9 | Cross-platform dialogs in C/C++ WINDOWS OSX GNOME KDE SOLARIS CONSOLE 10 | Tested with C & C++ compilers 11 | on Visual Studio 2013 OSX Linux Freebsd Illumos Solaris. 12 | 13 | A single C file (add it to your project) with 6 modal function calls: 14 | - open file dialog (& multiple files) 15 | - save file dialog 16 | - select folder dialog 17 | - message box (& question) 18 | - input box 19 | - color picker. 20 | 21 | Conceived as a fully independent complement to GLUT, GLUI, SDL, UNITY3D 22 | or any GUI-less program, there is NO MAIN LOOP nor init. 23 | It also provides CONSOLE dialogs on unix. 24 | 25 | On Windows native code creates the dialogs (mostly). 26 | On UNIX it tries successive command line calls: 27 | - zenity 28 | - kdialog 29 | - applescript 30 | - python 2 / tkinter 31 | - dialog. 32 | The same executable can run across desktops and distributions. 33 | 34 | - License - 35 | 36 | This software is provided 'as-is', without any express or implied 37 | warranty. In no event will the authors be held liable for any damages 38 | arising from the use of this software. 39 | 40 | Permission is granted to anyone to use this software for any purpose, 41 | including commercial applications, and to alter it and redistribute it 42 | freely, subject to the following restrictions: 43 | 44 | 1. The origin of this software must not be misrepresented; you must not 45 | claim that you wrote the original software. If you use this software 46 | in a product, an acknowledgment in the product documentation would be 47 | appreciated but is not required. 48 | 2. Altered source versions must be plainly marked as such, and must not be 49 | misrepresented as being the original software. 50 | 3. This notice may not be removed or altered from any source distribution. 51 | */ 52 | 53 | #ifndef TINYFILEDIALOGS_H 54 | #define TINYFILEDIALOGS_H 55 | 56 | /* 57 | if tinydialogs.c is compiled with a C++ compiler 58 | rather than with a C compiler, you need to comment out: 59 | extern "C" { 60 | and the corresponding closing bracket: 61 | } 62 | */ 63 | 64 | #ifdef __cplusplus 65 | extern "C" { 66 | #endif /* __cplusplus */ 67 | 68 | extern int tinyfd_forceConsole ; /* for UNIX only: 0 (default) or 1 */ 69 | /* 1 forces all dialogs into console mode even when the X server is present */ 70 | /* can be modified at run time */ 71 | 72 | int tinyfd_messageBox ( 73 | char const * const aTitle , /* "" */ 74 | char const * const aMessage , /* "" may contain \n and \t */ 75 | char const * const aDialogType , /* "ok" "okcancel" "yesno" */ 76 | char const * const aIconType , /* "info" "warning" "error" "question" */ 77 | int const aDefaultButton ) ; /* 0 for cancel/no , 1 for ok/yes */ 78 | /* returns 0 for cancel/no , 1 for ok/yes */ 79 | 80 | char const * tinyfd_saveFileDialog ( 81 | char const * const aTitle , /* "" */ 82 | char const * const aDefaultPathAndFile , /* "" */ 83 | int const aNumOfFileFilters , /* 0 */ 84 | char const * const * const aFileFilters ) ; /* NULL or {"*.txt"} */ 85 | 86 | char const * tinyfd_openFileDialog ( 87 | char const * const aTitle , /* "" */ 88 | char const * const aDefaultPathAndFile , /* "" */ 89 | int const aNumOfFileFilters , /* 0 */ 90 | char const * const * const aFileFilters , /* NULL or {"*.jpg","*.png"} */ 91 | int aAllowMultipleSelects ) ; /* 0 or 1 */ 92 | /* in case of multiple files, the separator is | */ 93 | 94 | char const * tinyfd_selectFolderDialog ( 95 | char const * const aTitle , /* "" */ 96 | char const * const aDefaultPath ) ; /* "" */ 97 | 98 | char const * tinyfd_inputBox( 99 | char const * const aTitle , /* "" */ 100 | char const * const aMessage , /* "" may NOT contain \n nor \t */ 101 | char const * const aDefaultInput ) ; /* "" */ 102 | /* on cancel it returns aDefaultInput */ 103 | 104 | char const * tinyfd_colorChooser( 105 | char const * const aTitle , /* "" */ 106 | char const * const aDefaultHexRGB , /* NULL or "#FF0000" */ 107 | unsigned char aDefaultRGB[3] , /* { 0 , 255 , 255 } */ 108 | unsigned char aoResultRGB[3] ) ; /* { 0 , 0 , 0 } */ 109 | /* returns the hexcolor as a string "#FF0000" */ 110 | /* aoResultRGB also contains the result */ 111 | /* aDefaultRGB is used only if aDefaultHexRGB is NULL */ 112 | /* aDefaultRGB and aoResultRGB can be the same array */ 113 | 114 | #ifdef __cplusplus 115 | } 116 | #endif /* __cplusplus */ 117 | 118 | #endif /* TINYFILEDIALOGS_H */ 119 | 120 | 121 | /* 122 | - On linux: link against Comdlg32.lib User32.lib and Shell32.lib 123 | - On unix: it tries command line calls, so no such need. 124 | - Use linux separator on linux and unix separator on unix. 125 | - char const * fileFilters[3] = { "*.obj" , "*.stl" , "*.dxf" } ; 126 | - String memory is preallocated statically for all the returned values. 127 | - On unix you need zenity or kdialog or python2/tkinter or dialog installed. 128 | Don't worry, it's already included on most (if not all) desktops. 129 | - If you pass only a path instead of path + filename, 130 | make sure it ends with a separator. 131 | - tinyfd_forceConsole=1; forces all dialogs into console mode (unix only). 132 | */ 133 | -------------------------------------------------------------------------------- /util/buildbot/buildwin32.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Taken from https://github.com/minetest/minetest LGPL 2.1 or later 3 | 4 | set -e 5 | 6 | dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 7 | if [ $# -ne 1 ]; then 8 | echo "Usage: $0 " 9 | exit 1 10 | fi 11 | builddir=$1 12 | mkdir -p $builddir 13 | mkdir -p $builddir/bin 14 | builddir="$( cd "$builddir" && pwd )" 15 | packagedir=$builddir/packages 16 | libdir=$builddir/libs 17 | 18 | toolchain_file=$dir/toolchain_mingw.cmake 19 | irrlicht_version=1.8.1 20 | zlib_version=1.2.8 21 | 22 | mkdir -p $packagedir 23 | mkdir -p $libdir 24 | 25 | cd $builddir 26 | 27 | # Get stuff 28 | [ -e $packagedir/irrlicht-$irrlicht_version.zip ] || wget http://sfan5.pf-control.de/irrlicht-$irrlicht_version-win32.zip \ 29 | -c -O $packagedir/irrlicht-$irrlicht_version.zip 30 | [ -e $packagedir/zlib-$zlib_version.zip ] || wget http://sfan5.pf-control.de/zlib-$zlib_version-win32.zip \ 31 | -c -O $packagedir/zlib-$zlib_version.zip 32 | 33 | 34 | # Extract stuff 35 | cd $libdir 36 | [ -d irrlicht-$irrlicht_version ] || unzip -o $packagedir/irrlicht-$irrlicht_version.zip 37 | [ -d zlib ] || unzip -o $packagedir/zlib-$zlib_version.zip -d zlib 38 | 39 | 40 | # Get nodeboxeditor 41 | cd $builddir 42 | if [ ! "x$EXISTING_nodeboxeditor_DIR" = "x" ]; then 43 | ln -s $EXISTING_nodeboxeditor_DIR nodeboxeditor 44 | else 45 | [ -d nodeboxeditor ] && (cd nodeboxeditor && git pull) || (git clone https://github.com/rubenwardy/nodeboxeditor) 46 | fi 47 | cd nodeboxeditor 48 | git_hash=`git show | head -c14 | tail -c7` 49 | 50 | # Build the thing 51 | [ -d _build ] && rm -Rf _build/ 52 | mkdir _build 53 | cd _build 54 | cmake .. \ 55 | -DCMAKE_INSTALL_PREFIX=/tmp \ 56 | -DCMAKE_TOOLCHAIN_FILE=$toolchain_file \ 57 | \ 58 | -DIRRLICHT_INCLUDE_DIR=$libdir/irrlicht-$irrlicht_version/include \ 59 | -DIRRLICHT_LIBRARY=$libdir/irrlicht-$irrlicht_version/lib/Win32-gcc/libIrrlicht.dll.a \ 60 | -DIRRLICHT_DLL=$libdir/irrlicht-$irrlicht_version/bin/Win32-gcc/Irrlicht.dll \ 61 | \ 62 | -DZLIB_INCLUDE_DIR=$libdir/zlib/include \ 63 | -DZLIB_LIBRARIES=$libdir/zlib/lib/zlibwapi.dll.a \ 64 | -DZLIB_DLL=$libdir/zlib/bin/zlib1.dll \ 65 | -DZLIBWAPI_DLL=$libdir/zlib/bin/zlibwapi.dll 66 | 67 | make package -j2 68 | 69 | # EOF 70 | -------------------------------------------------------------------------------- /util/buildbot/buildwin64.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Taken from https://github.com/minetest/minetest LGPL 2.1 or later 3 | 4 | set -e 5 | 6 | dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 7 | if [ $# -ne 1 ]; then 8 | echo "Usage: $0 " 9 | exit 1 10 | fi 11 | builddir=$1 12 | mkdir -p $builddir 13 | mkdir -p $builddir/bin 14 | builddir="$( cd "$builddir" && pwd )" 15 | packagedir=$builddir/packages 16 | libdir=$builddir/libs 17 | 18 | toolchain_file=$dir/toolchain_mingw64.cmake 19 | irrlicht_version=1.8.1 20 | zlib_version=1.2.8 21 | 22 | mkdir -p $packagedir 23 | mkdir -p $libdir 24 | 25 | cd $builddir 26 | 27 | # Get stuff 28 | [ -e $packagedir/irrlicht-$irrlicht_version.zip ] || wget http://sfan5.pf-control.de/irrlicht-$irrlicht_version-win64.zip \ 29 | -c -O $packagedir/irrlicht-$irrlicht_version.zip 30 | [ -e $packagedir/zlib-$zlib_version.zip ] || wget http://sfan5.pf-control.de/zlib-$zlib_version-win64.zip \ 31 | -c -O $packagedir/zlib-$zlib_version.zip 32 | 33 | 34 | # Extract stuff 35 | cd $libdir 36 | [ -d irrlicht-$irrlicht_version ] || unzip -o $packagedir/irrlicht-$irrlicht_version.zip 37 | [ -d zlib ] || unzip -o $packagedir/zlib-$zlib_version.zip -d zlib 38 | 39 | # Get nodeboxeditor 40 | cd $builddir 41 | if [ ! "x$EXISTING_nodeboxeditor_DIR" = "x" ]; then 42 | ln -s $EXISTING_nodeboxeditor_DIR nodeboxeditor 43 | else 44 | [ -d nodeboxeditor ] && (cd nodeboxeditor && git pull) || (git clone https://github.com/rubenwardy/nodeboxeditor) 45 | fi 46 | cd nodeboxeditor 47 | git_hash=`git show | head -c14 | tail -c7` 48 | 49 | # Build the thing 50 | [ -d _build ] && rm -Rf _build/ 51 | mkdir _build 52 | cd _build 53 | cmake .. \ 54 | -DCMAKE_TOOLCHAIN_FILE=$toolchain_file \ 55 | -DCMAKE_INSTALL_PREFIX=/tmp \ 56 | \ 57 | -DIRRLICHT_INCLUDE_DIR=$libdir/irrlicht-$irrlicht_version/include \ 58 | -DIRRLICHT_LIBRARY=$libdir/irrlicht-$irrlicht_version/lib/Win64-gcc/libIrrlicht.dll.a \ 59 | -DIRRLICHT_DLL=$libdir/irrlicht-$irrlicht_version/bin/Win64-gcc/Irrlicht.dll \ 60 | \ 61 | -DZLIB_INCLUDE_DIR=$libdir/zlib/include \ 62 | -DZLIB_LIBRARIES=$libdir/zlib/lib/libz.dll.a \ 63 | -DZLIB_DLL=$libdir/zlib/bin/zlib1.dll 64 | 65 | make package -j2 66 | 67 | # EOF 68 | -------------------------------------------------------------------------------- /util/buildbot/toolchain_mingw.cmake: -------------------------------------------------------------------------------- 1 | # Target operating system name 2 | set(CMAKE_SYSTEM_NAME Windows) 3 | 4 | # Compilers to use 5 | set(CMAKE_C_COMPILER i686-w64-mingw32-gcc) 6 | set(CMAKE_CXX_COMPILER i686-w64-mingw32-g++) 7 | set(CMAKE_RC_COMPILER i686-w64-mingw32-windres) 8 | 9 | # Location of the target environment 10 | set(CMAKE_FIND_ROOT_PATH /usr/i686-w64-mingw32) 11 | 12 | # Adjust the default behaviour of the FIND_XXX() commands: 13 | # search for headers and libraries in the target environment, 14 | # search for programs in the host environment 15 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 16 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 17 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 18 | 19 | -------------------------------------------------------------------------------- /util/buildbot/toolchain_mingw64.cmake: -------------------------------------------------------------------------------- 1 | # name of the target operating system 2 | SET(CMAKE_SYSTEM_NAME Windows) 3 | 4 | # which compilers to use for C and C++ 5 | SET(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc) 6 | SET(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++) 7 | SET(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres) 8 | 9 | # here is the target environment located 10 | SET(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32) 11 | 12 | # adjust the default behaviour of the FIND_XXX() commands: 13 | # search headers and libraries in the target environment, search 14 | # programs in the host environment 15 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 16 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 17 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 18 | -------------------------------------------------------------------------------- /util/install_nbe.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | cd ../ 4 | cmake . 5 | make -j3 6 | sudo make install 7 | 8 | -------------------------------------------------------------------------------- /util/travis/before_install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | # Taken from https://github.com/minetest/minetest LGPL 2.1 or later 3 | 4 | if [[ $CC == "clang" ]]; then 5 | export PATH="/usr/bin/:$PATH" 6 | sudo sh -c 'echo "deb http://ppa.launchpad.net/eudoxos/llvm-3.1/ubuntu precise main" >> /etc/apt/sources.list' 7 | sudo apt-key adv --keyserver pool.sks-keyservers.net --recv-keys 92DE8183 8 | sudo apt-get update 9 | sudo apt-get install llvm-3.1 10 | sudo apt-get install clang 11 | fi 12 | sudo apt-get install p7zip-full 13 | if [[ $PLATFORM == "Linux" ]]; then 14 | sudo apt-get install libirrlicht-dev cmake libbz2-dev libpng-dev \ 15 | libjpeg-dev libxxf86vm-dev libgl1-mesa-dev 16 | elif [[ $PLATFORM == "Win32" ]]; then 17 | wget http://sfan5.pf-control.de/mingw_w64_i686_ubuntu12.04_4.9.1.7z -O mingw.7z 18 | sed -e "s|%PREFIX%|i686-w64-mingw32|" \ 19 | -e "s|%ROOTPATH%|/usr/i686-w64-mingw32|" \ 20 | < util/travis/toolchain_mingw.cmake.in > util/buildbot/toolchain_mingw.cmake 21 | sudo 7z x -y -o/usr mingw.7z 22 | elif [[ $PLATFORM == "Win64" ]]; then 23 | wget http://sfan5.pf-control.de/mingw_w64_x86_64_ubuntu12.04_4.9.1.7z -O mingw.7z 24 | sed -e "s|%PREFIX%|x86_64-w64-mingw32|" \ 25 | -e "s|%ROOTPATH%|/usr/x86_64-w64-mingw32|" \ 26 | < util/travis/toolchain_mingw.cmake.in > util/buildbot/toolchain_mingw64.cmake 27 | sudo 7z x -y -o/usr mingw.7z 28 | fi 29 | -------------------------------------------------------------------------------- /util/travis/script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | # Taken from https://github.com/minetest/minetest LGPL 2.1 or later 3 | 4 | if [[ $PLATFORM == "Linux" ]]; then 5 | mkdir -p travisbuild 6 | cd travisbuild 7 | CMAKE_FLAGS='-DCMAKE_BUILD_TYPE=Debug' 8 | cmake $CMAKE_FLAGS .. 9 | make -j2 && exit 0 10 | elif [[ $PLATFORM == Win* ]]; then 11 | [[ $CC == "clang" ]] && exit 1 # Not supposed to happen 12 | # We need to have our build directory outside of the minetest directory because 13 | # CMake will otherwise get very very confused with symlinks and complain that 14 | # something is not a subdirectory of something even if it actually is. 15 | # e.g.: 16 | # /home/travis/minetest/minetest/travisbuild/minetest 17 | # \/ \/ \/ 18 | # /home/travis/minetest/minetest/travisbuild/minetest/travisbuild/minetest 19 | # \/ \/ \/ 20 | # /home/travis/minetest/minetest/travisbuild/minetest/travisbuild/minetest/travisbuild/minetest 21 | # You get the idea. 22 | OLDDIR=$(pwd) 23 | cd .. 24 | export EXISTING_nodeboxeditor_DIR=$OLDDIR 25 | if [[ $PLATFORM == "Win32" ]]; then 26 | $OLDDIR/util/buildbot/buildwin32.sh travisbuild && exit 0 27 | elif [[ $PLATFORM == "Win64" ]]; then 28 | $OLDDIR/util/buildbot/buildwin64.sh travisbuild && exit 0 29 | fi 30 | else 31 | echo "Unknown platform \"${PLATFORM}\"." 32 | exit 1 33 | fi 34 | -------------------------------------------------------------------------------- /util/travis/toolchain_mingw.cmake.in: -------------------------------------------------------------------------------- 1 | # Target operating system name 2 | set(CMAKE_SYSTEM_NAME Windows) 3 | 4 | # Compilers to use 5 | set(CMAKE_C_COMPILER %PREFIX%-gcc) 6 | set(CMAKE_CXX_COMPILER %PREFIX%-g++) 7 | set(CMAKE_RC_COMPILER %PREFIX%-windres) 8 | 9 | # Location of the target environment 10 | set(CMAKE_FIND_ROOT_PATH %ROOTPATH%) 11 | 12 | # Adjust the default behaviour of the FIND_XXX() commands: 13 | # search for headers and libraries in the target environment, 14 | # search for programs in the host environment 15 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 16 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 17 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 18 | 19 | --------------------------------------------------------------------------------