From 4b24d85123b2ef940e7cfe2e17cda29d313db250 Mon Sep 17 00:00:00 2001 From: Sergey Lapin Date: Mon, 19 Jan 2026 00:07:03 +0300 Subject: [PATCH] Action nodes support and lots of other updates --- CMakeLists.txt | 49 +- Game.cpp | 2 +- .../buildings/parts/export_furniture_parts.py | 90 + .../blender/buildings/parts/furniture.blend | 4 +- resources/CMakeLists.txt | 34 + brushes.kra => resources/brushes.kra | 0 resources/shaderlib/RTSLib_Colour.glsl | 16 + resources/terrain/world_map.png | Bin 0 -> 45182 bytes world_map.kra => resources/world_map.kra | 0 src/FastNoiseLite/FastNoiseLite.h | 2586 +++++++++ src/FastNoiseLite/LICENSE | 22 + src/FastNoiseLite/README.md | 23 + src/editor/main.cpp | 2 +- src/gamedata/CMakeLists.txt | 5 +- src/gamedata/EditorGUIModule.cpp | 1421 +++++ src/gamedata/EditorGUIModule.h | 9 + src/gamedata/EventModule.h | 2 +- src/gamedata/GUIModule.cpp | 1329 +---- src/gamedata/GUIModule.h | 33 +- src/gamedata/GUIModuleCommon.h | 36 + src/gamedata/GameData.cpp | 15 +- src/gamedata/GameData.h | 4 +- src/gamedata/LuaData.cpp | 4 +- src/gamedata/PlayerActionModule.cpp | 114 + src/gamedata/PlayerActionModule.h | 42 + src/gamedata/StaticGeometryModule.cpp | 232 +- src/gamedata/StaticGeometryModule.h | 19 +- src/gamedata/items/CMakeLists.txt | 4 +- src/gamedata/items/items.cpp | 53 +- src/gamedata/items/items.h | 2 + src/gamedata/items/town.cpp | 5096 ++++++++++------- src/gamedata/items/town.h | 12 +- src/physics/physics.cpp | 30 +- src/physics/physics.h | 8 +- 34 files changed, 7810 insertions(+), 3488 deletions(-) create mode 100644 resources/CMakeLists.txt rename brushes.kra => resources/brushes.kra (100%) create mode 100644 resources/shaderlib/RTSLib_Colour.glsl create mode 100644 resources/terrain/world_map.png rename world_map.kra => resources/world_map.kra (100%) create mode 100644 src/FastNoiseLite/FastNoiseLite.h create mode 100644 src/FastNoiseLite/LICENSE create mode 100644 src/FastNoiseLite/README.md create mode 100644 src/gamedata/EditorGUIModule.cpp create mode 100644 src/gamedata/EditorGUIModule.h create mode 100644 src/gamedata/GUIModuleCommon.h create mode 100644 src/gamedata/PlayerActionModule.cpp create mode 100644 src/gamedata/PlayerActionModule.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b7564a6..77558d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,6 +77,7 @@ add_subdirectory(src/tests) add_subdirectory(src/physics) add_subdirectory(src/editor) add_subdirectory(assets/blender/buildings/parts) +add_subdirectory(resources) add_executable(Game Game.cpp ${WATER_SRC}) target_include_directories(Game PRIVATE src/gamedata) @@ -203,34 +204,8 @@ add_custom_target(stage_stories ALL DEPENDS stage_lua_scripts ${CMAKE_BINARY_DIR add_custom_command( OUTPUT ${CMAKE_BINARY_DIR}/resources.cfg - COMMAND cp ${CMAKE_SOURCE_DIR}/resources.cfg ${CMAKE_BINARY_DIR}/resources.cfg - COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/resources/main - ${CMAKE_BINARY_DIR}/resources/main - COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/resources/shaderlib - ${CMAKE_BINARY_DIR}/resources/shaderlib - COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/resources/terrain - ${CMAKE_BINARY_DIR}/resources/terrain -# COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/lua-scripts -# ${CMAKE_BINARY_DIR}/lua-scripts -# COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/skybox -# ${CMAKE_BINARY_DIR}/skybox -# COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/resources/debug -# ${CMAKE_BINARY_DIR}/resources/debug + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/resources.cfg ${CMAKE_BINARY_DIR}/resources.cfg DEPENDS ${CMAKE_SOURCE_DIR}/resources.cfg) -add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/resources/terrain/world_map.png - COMMAND unzip -o ${CMAKE_SOURCE_DIR}/world_map.kra mergedimage.png -d ${CMAKE_BINARY_DIR}/world_map - COMMAND ${CMAKE_COMMAND} -E copy - ${CMAKE_BINARY_DIR}/world_map/mergedimage.png - ${CMAKE_BINARY_DIR}/resources/terrain/world_map.png - COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/world_map - DEPENDS ${CMAKE_SOURCE_DIR}/world_map.kra) -add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/resources/terrain/brushes.png - COMMAND unzip -o ${CMAKE_SOURCE_DIR}/brushes.kra mergedimage.png -d ${CMAKE_BINARY_DIR}/brushes - COMMAND ${CMAKE_COMMAND} -E copy - ${CMAKE_BINARY_DIR}/brushes/mergedimage.png - ${CMAKE_BINARY_DIR}/resources/terrain/brushes.png - COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/brushes - DEPENDS ${CMAKE_SOURCE_DIR}/brushes.kra) set(SKYBOX_SRC early_morning_bk.jpg @@ -356,29 +331,11 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E touch ${CHARACTER_GLBS} DEPENDS ${CMAKE_SOURCE_DIR}/assets/blender/scripts/export_models.py ${VRM_IMPORTED_BLENDS} ${EDITED_BLEND_TARGETS} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) -#add_custom_command( -# OUTPUT ${CMAKE_SOURCE_DIR}/characters/female/vroid-normal-female.scene -# ${CMAKE_SOURCE_DIR}/characters/male/vroid-normal-male.scene -# ${CMAKE_SOURCE_DIR}/assets/blender/vrm-vroid-normal-female.blend -# ${CMAKE_SOURCE_DIR}/assets/blender/vrm-vroid-normal-male.blend -# ${CMAKE_SOURCE_DIR}/assets/blender/shapes/male/vrm-vroid-normal-male-chibi.blend -# COMMAND mkdir -p ${CREATE_DIRECTORIES} -# COMMAND ${BLENDER} -b -Y -P ${CMAKE_SOURCE_DIR}/assets/blender/scripts/import_vrm.py -# COMMAND ${BLENDER} -b -Y -P ${CMAKE_SOURCE_DIR}/assets/blender/scripts/export_models.py -## COMMAND ${BLENDER} -b -Y -P ${CMAKE_SOURCE_DIR}/assets/blender/scripts/export_ogre_scene.py -# COMMAND echo rm -Rf ${CMAKE_SOURCE_DIR}/characters/male/*.material ${CMAKE_SOURCE_DIR}/characters/female/*.material -# COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/characters -# ${CMAKE_BINARY_DIR}/characters -# DEPENDS ${CMAKE_SOURCE_DIR}/assets/blender/scripts/export_models.py ${CMAKE_SOURCE_DIR}/assets/blender/scripts/import_vrm.py -# ${CMAKE_SOURCE_DIR}/assets/vroid/buch1.vrm -# ${CMAKE_SOURCE_DIR}/assets/vroid/buch1-chibi.vrm -# ${CMAKE_SOURCE_DIR}/assets/vroid/jane2-dress.vrm -# WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) add_custom_target(stage_files ALL DEPENDS ${CMAKE_BINARY_DIR}/resources.cfg ${MATERIALS_OUTPUT} ${CMAKE_BINARY_DIR}/resources/terrain/world_map.png ${CMAKE_BINARY_DIR}/resources/terrain/brushes.png - edited-blends) + edited-blends stage_resources) add_custom_target(remove_scenes COMMAND rm -f ${VRM_SOURCE} ${VRM_IMPORTED_BLENDS} ${CHARACTER_GLBS}) diff --git a/Game.cpp b/Game.cpp index 2c7821e..8165225 100644 --- a/Game.cpp +++ b/Game.cpp @@ -15,7 +15,7 @@ #include "Components.h" #include "CharacterModule.h" #include "TerrainModule.h" -#include "GUIModule.h" +#include "GUIModuleCommon.h" #include "AppModule.h" #include "sound.h" class App; diff --git a/assets/blender/buildings/parts/export_furniture_parts.py b/assets/blender/buildings/parts/export_furniture_parts.py index 65e4133..c6aa593 100644 --- a/assets/blender/buildings/parts/export_furniture_parts.py +++ b/assets/blender/buildings/parts/export_furniture_parts.py @@ -1,6 +1,7 @@ import bpy import os, sys, time import json +from mathutils import Matrix, Quaternion argv = sys.argv argv = argv[argv.index("--") + 1:] @@ -9,6 +10,13 @@ incpath = os.path.dirname(__file__) sys.path.insert(0, incpath) outdir = argv[0] +basis_change = Matrix(( + (1, 0, 0, 0), + (0, 0, 1, 0), + (0, -1, 0, 0), + (0, 0, 0, 1) +)) + def export_root_objects_to_gltf(output_dir): # Ensure the output directory exists if not os.path.exists(output_dir): @@ -40,6 +48,88 @@ def export_root_objects_to_gltf(output_dir): desc["tags"] = [mtags] else: desc["tags"] = [] + desc["sensors"] = [] + desc["positions"] = [] + for child in obj.children: + if child.name.startswith("sensor-"): + if not "action" in child: + continue + if not "height" in child: + continue + if not "radius" in child: + continue + local_matrix = child.matrix_local + ogre_local_matrix = basis_change @ local_matrix @ basis_change.inverted() + local_pos_d = ogre_local_matrix.to_translation() + local_rot_d = ogre_local_matrix.to_quaternion() + local_pos = [round(x, 4) for x in local_pos_d] + local_rot = [round(x, 4) for x in local_rot_d] + sensor = {} + sensor["position_x"] = local_pos[0] + sensor["position_y"] = local_pos[2] + sensor["position_z"] = local_pos[1] + sensor["rotation_w"] = local_rot[0] + sensor["rotation_x"] = local_rot[1] + sensor["rotation_y"] = local_rot[2] + sensor["rotation_z"] = local_rot[3] + sensor["height"] = child["height"] + sensor["radius"] = child["radius"] + sensor["action"] = child["action"] + if "action_text" in child: + sensor["action_text"] = child["action_text"] + else: + sensor["action_text"] = child["action"].capitalize() + if "name" in child: + sensor["name"] = child["name"] + else: + sensor["name"] = desc["name"] + "_" + str(len(desc["sensors"])) + sensor["furniture"] = {} + sensor["furniture"]["name"] = desc["name"] + sensor["furniture"]["tags"] = desc["tags"] + sensor["positions"] = [] + for schild in child.children: + if schild.name.startswith("position-"): + local_matrix = schild.matrix_local + ogre_local_matrix = basis_change @ local_matrix @ basis_change.inverted() + local_pos_d = ogre_local_matrix.to_translation() + local_rot_d = ogre_local_matrix.to_quaternion() + local_pos = [round(x, 4) for x in local_pos_d] + local_rot = [round(x, 4) for x in local_rot_d] + position = {} + if "name" in schild: + position["name"] = schild["name"] + if "type" in schild: + position["type"] = schild["type"] + position["position_x"] = local_pos[0] + position["position_y"] = local_pos[2] + position["position_z"] = local_pos[1] + position["rotation_w"] = local_rot[0] + position["rotation_x"] = local_rot[1] + position["rotation_y"] = local_rot[2] + position["rotation_z"] = local_rot[3] + sensor["positions"].append(position) + desc["sensors"].append(sensor) + if child.name.startswith("position-"): + local_matrix = child.matrix_local + ogre_local_matrix = basis_change @ local_matrix @ basis_change.inverted() + local_pos_d = ogre_local_matrix.to_translation() + local_rot_d = ogre_local_matrix.to_quaternion() + local_pos = [round(x, 4) for x in local_pos_d] + local_rot = [round(x, 4) for x in local_rot_d] + position = {} + if "name" in child: + position["name"] = child["name"] + if "type" in child: + position["type"] = child["type"] + position["position_x"] = local_pos[0] + position["position_y"] = local_pos[2] + position["position_z"] = local_pos[1] + position["rotation_w"] = local_rot[0] + position["rotation_x"] = local_rot[1] + position["rotation_y"] = local_rot[2] + position["rotation_z"] = local_rot[3] + desc["positions"].append(position) + obj.select_set(True) for child in obj.children_recursive: child.select_set(True) diff --git a/assets/blender/buildings/parts/furniture.blend b/assets/blender/buildings/parts/furniture.blend index 5b9e993..f76b4e5 100644 --- a/assets/blender/buildings/parts/furniture.blend +++ b/assets/blender/buildings/parts/furniture.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6316ed1835394a3346eb57be2e55520b8513473f348c90bf871c2375e62aae6a -size 1986948 +oid sha256:ab498c6ed1003b7339fc2463fae4204a0d71561179cd6db30d4263f781540b72 +size 2143185 diff --git a/resources/CMakeLists.txt b/resources/CMakeLists.txt new file mode 100644 index 0000000..afd5f21 --- /dev/null +++ b/resources/CMakeLists.txt @@ -0,0 +1,34 @@ +project(resources) +set(DIRECTORY_LIST + main + shaderlib + terrain +) +set(TARGET_PATHS) +foreach(DIR_NAME ${DIRECTORY_LIST}) + add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${DIR_NAME} + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_CURRENT_SOURCE_DIR}/${DIR_NAME} + ${CMAKE_CURRENT_BINARY_DIR}/${DIR_NAME} + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${DIR_NAME}) + list(APPEND TARGET_PATHS ${CMAKE_CURRENT_BINARY_DIR}/${DIR_NAME}) +endforeach() + +#add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/terrain/world_map.png +# COMMAND unzip -o ${CMAKE_CURRENT_SOURCE_DIR}/world_map.kra mergedimage.png -d ${CMAKE_CURRENT_BINARY_DIR}/world_map +# COMMAND ${CMAKE_COMMAND} -E copy +# ${CMAKE_BINARY_DIR}/world_map/mergedimage.png +# ${CMAKE_BINARY_DIR}/resources/terrain/world_map.png +# COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/world_map +# DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/world_map.kra) +#list(APPEND TARGET_PATHS ${CMAKE_CURRENT_BINARY_DIR}/terrain/world_map.png) +add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/terrain/brushes.png + COMMAND unzip -o ${CMAKE_CURRENT_SOURCE_DIR}/brushes.kra mergedimage.png -d ${CMAKE_CURRENT_BINARY_DIR}/brushes + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_BINARY_DIR}/brushes/mergedimage.png + ${CMAKE_CURRENT_BINARY_DIR}/terrain/brushes.png + COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_CURRENT_BINARY_DIR}/brushes + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/brushes.kra) +list(APPEND TARGET_PATHS ${CMAKE_CURRENT_BINARY_DIR}/terrain/brushes.png) + +add_custom_target(stage_resources ALL DEPENDS ${TARGET_PATHS}) diff --git a/brushes.kra b/resources/brushes.kra similarity index 100% rename from brushes.kra rename to resources/brushes.kra diff --git a/resources/shaderlib/RTSLib_Colour.glsl b/resources/shaderlib/RTSLib_Colour.glsl new file mode 100644 index 0000000..014e11e --- /dev/null +++ b/resources/shaderlib/RTSLib_Colour.glsl @@ -0,0 +1,16 @@ +// This file is part of the OGRE project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at https://www.ogre3d.org/licensing. +// SPDX-License-Identifier: MIT + +#ifdef USE_LINEAR_COLOURS +#define ENABLE_LINEAR_COLOUR(colour) colour.rgb = pow(colour.rgb, vec3_splat(2.2)) +#else +#define ENABLE_LINEAR_COLOUR(colour) +#endif + +#if defined(USE_LINEAR_COLOURS) && !defined(TARGET_CONSUMES_LINEAR) +#define COLOUR_TRANSFER(colour) colour.rgb = pow(colour.rgb, vec3_splat(1.0/2.2)) +#else +#define COLOUR_TRANSFER(colour) +#endif \ No newline at end of file diff --git a/resources/terrain/world_map.png b/resources/terrain/world_map.png new file mode 100644 index 0000000000000000000000000000000000000000..225d0c430cdebb71d7e92f05edfc6b482b6ab9ed GIT binary patch literal 45182 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 B@EiaD literal 0 HcmV?d00001 diff --git a/world_map.kra b/resources/world_map.kra similarity index 100% rename from world_map.kra rename to resources/world_map.kra diff --git a/src/FastNoiseLite/FastNoiseLite.h b/src/FastNoiseLite/FastNoiseLite.h new file mode 100644 index 0000000..c67f2e5 --- /dev/null +++ b/src/FastNoiseLite/FastNoiseLite.h @@ -0,0 +1,2586 @@ +// MIT License +// +// Copyright(c) 2023 Jordan Peck (jordan.me2@gmail.com) +// Copyright(c) 2023 Contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// .'',;:cldxkO00KKXXNNWWWNNXKOkxdollcc::::::;:::ccllloooolllllllllooollc:,'... ...........',;cldxkO000Okxdlc::;;;,,;;;::cclllllll +// ..',;:ldxO0KXXNNNNNNNNXXK0kxdolcc::::::;;;,,,,,,;;;;;;;;;;:::cclllllc:;'.... ...........',;:ldxO0KXXXK0Okxdolc::;;;;::cllodddddo +// ...',:loxO0KXNNNNNXXKK0Okxdolc::;::::::::;;;,,'''''.....''',;:clllllc:;,'............''''''''',;:loxO0KXNNNNNXK0Okxdollccccllodxxxxxxd +// ....';:ldkO0KXXXKK00Okxdolcc:;;;;;::cclllcc:;;,''..... ....',;clooddolcc:;;;;,,;;;;;::::;;;;;;:cloxk0KXNWWWWWWNXKK0Okxddoooddxxkkkkkxx +// .....';:ldxkOOOOOkxxdolcc:;;;,,,;;:cllooooolcc:;'... ..,:codxkkkxddooollloooooooollcc:::::clodkO0KXNWWWWWWNNXK00Okxxxxxxxxkkkkxxx +// . ....';:cloddddo___________,,,,;;:clooddddoolc:,... ..,:ldx__00OOOkkk___kkkkkkxxdollc::::cclodkO0KXXNNNNNNXXK0OOkxxxxxxxxxxxxddd +// .......',;:cccc:| |,,,;;:cclooddddoll:;'.. ..';cox| \KKK000| |KK00OOkxdocc___;::clldxxkO0KKKKK00Okkxdddddddddddddddoo +// .......'',,,,,''| ________|',,;;::cclloooooolc:;'......___:ldk| \KK000| |XKKK0Okxolc| |;;::cclodxxkkkkxxdoolllcclllooodddooooo +// ''......''''....| | ....'',,,,;;;::cclloooollc:;,''.'| |oxk| \OOO0| |KKK00Oxdoll|___|;;;;;::ccllllllcc::;;,,;;;:cclloooooooo +// ;;,''.......... | |_____',,;;;____:___cllo________.___| |___| \xkk| |KK_______ool___:::;________;;;_______...'',;;:ccclllloo +// c:;,''......... | |:::/ ' |lo/ | | \dx| |0/ \d| |cc/ |'/ \......',,;;:ccllo +// ol:;,'..........| _____|ll/ __ |o/ ______|____ ___| | \o| |/ ___ \| |o/ ______|/ ___ \ .......'',;:clo +// dlc;,...........| |::clooo| / | |x\___ \KXKKK0| |dol| |\ \| | | | | |d\___ \..| | / / ....',:cl +// xoc;'... .....'| |llodddd| \__| |_____\ \KKK0O| |lc:| |'\ | |___| | |_____\ \.| |_/___/... ...',;:c +// dlc;'... ....',;| |oddddddo\ | |Okkx| |::;| |..\ |\ /| | | \ |... ....',;:c +// ol:,'.......',:c|___|xxxddollc\_____,___|_________/ddoll|___|,,,|___|...\_____|:\ ______/l|___|_________/...\________|'........',;::cc +// c:;'.......';:codxxkkkkxxolc::;::clodxkOO0OOkkxdollc::;;,,''''',,,,''''''''''',,'''''',;:loxkkOOkxol:;,'''',,;:ccllcc:;,'''''',;::ccll +// ;,'.......',:codxkOO0OOkxdlc:;,,;;:cldxxkkxxdolc:;;,,''.....'',;;:::;;,,,'''''........,;cldkO0KK0Okdoc::;;::cloodddoolc:;;;;;::ccllooo +// .........',;:lodxOO0000Okdoc:,,',,;:clloddoolc:;,''.......'',;:clooollc:;;,,''.......',:ldkOKXNNXX0Oxdolllloddxxxxxxdolccccccllooodddd +// . .....';:cldxkO0000Okxol:;,''',,;::cccc:;,,'.......'',;:cldxxkkxxdolc:;;,'.......';coxOKXNWWWNXKOkxddddxxkkkkkkxdoollllooddxxxxkkk +// ....',;:codxkO000OOxdoc:;,''',,,;;;;,''.......',,;:clodkO00000Okxolc::;,,''..',;:ldxOKXNWWWNNK0OkkkkkkkkkkkxxddooooodxxkOOOOO000 +// ....',;;clodxkkOOOkkdolc:;,,,,,,,,'..........,;:clodxkO0KKXKK0Okxdolcc::;;,,,;;:codkO0XXNNNNXKK0OOOOOkkkkxxdoollloodxkO0KKKXXXXX +// +// VERSION: 1.1.1 +// https://github.com/Auburn/FastNoiseLite + +#ifndef FASTNOISELITE_H +#define FASTNOISELITE_H + +#include + +class FastNoiseLite +{ +public: + enum NoiseType + { + NoiseType_OpenSimplex2, + NoiseType_OpenSimplex2S, + NoiseType_Cellular, + NoiseType_Perlin, + NoiseType_ValueCubic, + NoiseType_Value + }; + + enum RotationType3D + { + RotationType3D_None, + RotationType3D_ImproveXYPlanes, + RotationType3D_ImproveXZPlanes + }; + + enum FractalType + { + FractalType_None, + FractalType_FBm, + FractalType_Ridged, + FractalType_PingPong, + FractalType_DomainWarpProgressive, + FractalType_DomainWarpIndependent + }; + + enum CellularDistanceFunction + { + CellularDistanceFunction_Euclidean, + CellularDistanceFunction_EuclideanSq, + CellularDistanceFunction_Manhattan, + CellularDistanceFunction_Hybrid + }; + + enum CellularReturnType + { + CellularReturnType_CellValue, + CellularReturnType_Distance, + CellularReturnType_Distance2, + CellularReturnType_Distance2Add, + CellularReturnType_Distance2Sub, + CellularReturnType_Distance2Mul, + CellularReturnType_Distance2Div + }; + + enum DomainWarpType + { + DomainWarpType_OpenSimplex2, + DomainWarpType_OpenSimplex2Reduced, + DomainWarpType_BasicGrid + }; + + /// + /// Create new FastNoise object with optional seed + /// + FastNoiseLite(int seed = 1337) + { + mSeed = seed; + mFrequency = 0.01f; + mNoiseType = NoiseType_OpenSimplex2; + mRotationType3D = RotationType3D_None; + mTransformType3D = TransformType3D_DefaultOpenSimplex2; + + mFractalType = FractalType_None; + mOctaves = 3; + mLacunarity = 2.0f; + mGain = 0.5f; + mWeightedStrength = 0.0f; + mPingPongStrength = 2.0f; + + mFractalBounding = 1 / 1.75f; + + mCellularDistanceFunction = CellularDistanceFunction_EuclideanSq; + mCellularReturnType = CellularReturnType_Distance; + mCellularJitterModifier = 1.0f; + + mDomainWarpType = DomainWarpType_OpenSimplex2; + mWarpTransformType3D = TransformType3D_DefaultOpenSimplex2; + mDomainWarpAmp = 1.0f; + } + + /// + /// Sets seed used for all noise types + /// + /// + /// Default: 1337 + /// + void SetSeed(int seed) { mSeed = seed; } + + /// + /// Sets frequency for all noise types + /// + /// + /// Default: 0.01 + /// + void SetFrequency(float frequency) { mFrequency = frequency; } + + /// + /// Sets noise algorithm used for GetNoise(...) + /// + /// + /// Default: OpenSimplex2 + /// + void SetNoiseType(NoiseType noiseType) + { + mNoiseType = noiseType; + UpdateTransformType3D(); + } + + /// + /// Sets domain rotation type for 3D Noise and 3D DomainWarp. + /// Can aid in reducing directional artifacts when sampling a 2D plane in 3D + /// + /// + /// Default: None + /// + void SetRotationType3D(RotationType3D rotationType3D) + { + mRotationType3D = rotationType3D; + UpdateTransformType3D(); + UpdateWarpTransformType3D(); + } + + /// + /// Sets method for combining octaves in all fractal noise types + /// + /// + /// Default: None + /// Note: FractalType_DomainWarp... only affects DomainWarp(...) + /// + void SetFractalType(FractalType fractalType) { mFractalType = fractalType; } + + /// + /// Sets octave count for all fractal noise types + /// + /// + /// Default: 3 + /// + void SetFractalOctaves(int octaves) + { + mOctaves = octaves; + CalculateFractalBounding(); + } + + /// + /// Sets octave lacunarity for all fractal noise types + /// + /// + /// Default: 2.0 + /// + void SetFractalLacunarity(float lacunarity) { mLacunarity = lacunarity; } + + /// + /// Sets octave gain for all fractal noise types + /// + /// + /// Default: 0.5 + /// + void SetFractalGain(float gain) + { + mGain = gain; + CalculateFractalBounding(); + } + + /// + /// Sets octave weighting for all none DomainWarp fratal types + /// + /// + /// Default: 0.0 + /// Note: Keep between 0...1 to maintain -1...1 output bounding + /// + void SetFractalWeightedStrength(float weightedStrength) { mWeightedStrength = weightedStrength; } + + /// + /// Sets strength of the fractal ping pong effect + /// + /// + /// Default: 2.0 + /// + void SetFractalPingPongStrength(float pingPongStrength) { mPingPongStrength = pingPongStrength; } + + + /// + /// Sets distance function used in cellular noise calculations + /// + /// + /// Default: Distance + /// + void SetCellularDistanceFunction(CellularDistanceFunction cellularDistanceFunction) { mCellularDistanceFunction = cellularDistanceFunction; } + + /// + /// Sets return type from cellular noise calculations + /// + /// + /// Default: EuclideanSq + /// + void SetCellularReturnType(CellularReturnType cellularReturnType) { mCellularReturnType = cellularReturnType; } + + /// + /// Sets the maximum distance a cellular point can move from it's grid position + /// + /// + /// Default: 1.0 + /// Note: Setting this higher than 1 will cause artifacts + /// + void SetCellularJitter(float cellularJitter) { mCellularJitterModifier = cellularJitter; } + + + /// + /// Sets the warp algorithm when using DomainWarp(...) + /// + /// + /// Default: OpenSimplex2 + /// + void SetDomainWarpType(DomainWarpType domainWarpType) + { + mDomainWarpType = domainWarpType; + UpdateWarpTransformType3D(); + } + + + /// + /// Sets the maximum warp distance from original position when using DomainWarp(...) + /// + /// + /// Default: 1.0 + /// + void SetDomainWarpAmp(float domainWarpAmp) { mDomainWarpAmp = domainWarpAmp; } + + + /// + /// 2D noise at given position using current settings + /// + /// + /// Noise output bounded between -1...1 + /// + template + float GetNoise(FNfloat x, FNfloat y) const + { + Arguments_must_be_floating_point_values(); + + TransformNoiseCoordinate(x, y); + + switch (mFractalType) + { + default: + return GenNoiseSingle(mSeed, x, y); + case FractalType_FBm: + return GenFractalFBm(x, y); + case FractalType_Ridged: + return GenFractalRidged(x, y); + case FractalType_PingPong: + return GenFractalPingPong(x, y); + } + } + + /// + /// 3D noise at given position using current settings + /// + /// + /// Noise output bounded between -1...1 + /// + template + float GetNoise(FNfloat x, FNfloat y, FNfloat z) const + { + Arguments_must_be_floating_point_values(); + + TransformNoiseCoordinate(x, y, z); + + switch (mFractalType) + { + default: + return GenNoiseSingle(mSeed, x, y, z); + case FractalType_FBm: + return GenFractalFBm(x, y, z); + case FractalType_Ridged: + return GenFractalRidged(x, y, z); + case FractalType_PingPong: + return GenFractalPingPong(x, y, z); + } + } + + + /// + /// 2D warps the input position using current domain warp settings + /// + /// + /// Example usage with GetNoise + /// DomainWarp(x, y) + /// noise = GetNoise(x, y) + /// + template + void DomainWarp(FNfloat& x, FNfloat& y) const + { + Arguments_must_be_floating_point_values(); + + switch (mFractalType) + { + default: + DomainWarpSingle(x, y); + break; + case FractalType_DomainWarpProgressive: + DomainWarpFractalProgressive(x, y); + break; + case FractalType_DomainWarpIndependent: + DomainWarpFractalIndependent(x, y); + break; + } + } + + /// + /// 3D warps the input position using current domain warp settings + /// + /// + /// Example usage with GetNoise + /// DomainWarp(x, y, z) + /// noise = GetNoise(x, y, z) + /// + template + void DomainWarp(FNfloat& x, FNfloat& y, FNfloat& z) const + { + Arguments_must_be_floating_point_values(); + + switch (mFractalType) + { + default: + DomainWarpSingle(x, y, z); + break; + case FractalType_DomainWarpProgressive: + DomainWarpFractalProgressive(x, y, z); + break; + case FractalType_DomainWarpIndependent: + DomainWarpFractalIndependent(x, y, z); + break; + } + } + +private: + template + struct Arguments_must_be_floating_point_values; + + enum TransformType3D + { + TransformType3D_None, + TransformType3D_ImproveXYPlanes, + TransformType3D_ImproveXZPlanes, + TransformType3D_DefaultOpenSimplex2 + }; + + int mSeed; + float mFrequency; + NoiseType mNoiseType; + RotationType3D mRotationType3D; + TransformType3D mTransformType3D; + + FractalType mFractalType; + int mOctaves; + float mLacunarity; + float mGain; + float mWeightedStrength; + float mPingPongStrength; + + float mFractalBounding; + + CellularDistanceFunction mCellularDistanceFunction; + CellularReturnType mCellularReturnType; + float mCellularJitterModifier; + + DomainWarpType mDomainWarpType; + TransformType3D mWarpTransformType3D; + float mDomainWarpAmp; + + + template + struct Lookup + { + static const T Gradients2D[]; + static const T Gradients3D[]; + static const T RandVecs2D[]; + static const T RandVecs3D[]; + }; + + static float FastMin(float a, float b) { return a < b ? a : b; } + + static float FastMax(float a, float b) { return a > b ? a : b; } + + static float FastAbs(float f) { return f < 0 ? -f : f; } + + static float FastSqrt(float f) { return sqrtf(f); } + + template + static int FastFloor(FNfloat f) { return f >= 0 ? (int)f : (int)f - 1; } + + template + static int FastRound(FNfloat f) { return f >= 0 ? (int)(f + 0.5f) : (int)(f - 0.5f); } + + static float Lerp(float a, float b, float t) { return a + t * (b - a); } + + static float InterpHermite(float t) { return t * t * (3 - 2 * t); } + + static float InterpQuintic(float t) { return t * t * t * (t * (t * 6 - 15) + 10); } + + static float CubicLerp(float a, float b, float c, float d, float t) + { + float p = (d - c) - (a - b); + return t * t * t * p + t * t * ((a - b) - p) + t * (c - a) + b; + } + + static float PingPong(float t) + { + t -= (int)(t * 0.5f) * 2; + return t < 1 ? t : 2 - t; + } + + void CalculateFractalBounding() + { + float gain = FastAbs(mGain); + float amp = gain; + float ampFractal = 1.0f; + for (int i = 1; i < mOctaves; i++) + { + ampFractal += amp; + amp *= gain; + } + mFractalBounding = 1 / ampFractal; + } + + // Hashing + static const int PrimeX = 501125321; + static const int PrimeY = 1136930381; + static const int PrimeZ = 1720413743; + + static int Hash(int seed, int xPrimed, int yPrimed) + { + int hash = seed ^ xPrimed ^ yPrimed; + + hash *= 0x27d4eb2d; + return hash; + } + + + static int Hash(int seed, int xPrimed, int yPrimed, int zPrimed) + { + int hash = seed ^ xPrimed ^ yPrimed ^ zPrimed; + + hash *= 0x27d4eb2d; + return hash; + } + + + static float ValCoord(int seed, int xPrimed, int yPrimed) + { + int hash = Hash(seed, xPrimed, yPrimed); + + hash *= hash; + hash ^= hash << 19; + return hash * (1 / 2147483648.0f); + } + + + static float ValCoord(int seed, int xPrimed, int yPrimed, int zPrimed) + { + int hash = Hash(seed, xPrimed, yPrimed, zPrimed); + + hash *= hash; + hash ^= hash << 19; + return hash * (1 / 2147483648.0f); + } + + + float GradCoord(int seed, int xPrimed, int yPrimed, float xd, float yd) const + { + int hash = Hash(seed, xPrimed, yPrimed); + hash ^= hash >> 15; + hash &= 127 << 1; + + float xg = Lookup::Gradients2D[hash]; + float yg = Lookup::Gradients2D[hash | 1]; + + return xd * xg + yd * yg; + } + + + float GradCoord(int seed, int xPrimed, int yPrimed, int zPrimed, float xd, float yd, float zd) const + { + int hash = Hash(seed, xPrimed, yPrimed, zPrimed); + hash ^= hash >> 15; + hash &= 63 << 2; + + float xg = Lookup::Gradients3D[hash]; + float yg = Lookup::Gradients3D[hash | 1]; + float zg = Lookup::Gradients3D[hash | 2]; + + return xd * xg + yd * yg + zd * zg; + } + + + void GradCoordOut(int seed, int xPrimed, int yPrimed, float& xo, float& yo) const + { + int hash = Hash(seed, xPrimed, yPrimed) & (255 << 1); + + xo = Lookup::RandVecs2D[hash]; + yo = Lookup::RandVecs2D[hash | 1]; + } + + + void GradCoordOut(int seed, int xPrimed, int yPrimed, int zPrimed, float& xo, float& yo, float& zo) const + { + int hash = Hash(seed, xPrimed, yPrimed, zPrimed) & (255 << 2); + + xo = Lookup::RandVecs3D[hash]; + yo = Lookup::RandVecs3D[hash | 1]; + zo = Lookup::RandVecs3D[hash | 2]; + } + + + void GradCoordDual(int seed, int xPrimed, int yPrimed, float xd, float yd, float& xo, float& yo) const + { + int hash = Hash(seed, xPrimed, yPrimed); + int index1 = hash & (127 << 1); + int index2 = (hash >> 7) & (255 << 1); + + float xg = Lookup::Gradients2D[index1]; + float yg = Lookup::Gradients2D[index1 | 1]; + float value = xd * xg + yd * yg; + + float xgo = Lookup::RandVecs2D[index2]; + float ygo = Lookup::RandVecs2D[index2 | 1]; + + xo = value * xgo; + yo = value * ygo; + } + + + void GradCoordDual(int seed, int xPrimed, int yPrimed, int zPrimed, float xd, float yd, float zd, float& xo, float& yo, float& zo) const + { + int hash = Hash(seed, xPrimed, yPrimed, zPrimed); + int index1 = hash & (63 << 2); + int index2 = (hash >> 6) & (255 << 2); + + float xg = Lookup::Gradients3D[index1]; + float yg = Lookup::Gradients3D[index1 | 1]; + float zg = Lookup::Gradients3D[index1 | 2]; + float value = xd * xg + yd * yg + zd * zg; + + float xgo = Lookup::RandVecs3D[index2]; + float ygo = Lookup::RandVecs3D[index2 | 1]; + float zgo = Lookup::RandVecs3D[index2 | 2]; + + xo = value * xgo; + yo = value * ygo; + zo = value * zgo; + } + + + // Generic noise gen + + template + float GenNoiseSingle(int seed, FNfloat x, FNfloat y) const + { + switch (mNoiseType) + { + case NoiseType_OpenSimplex2: + return SingleSimplex(seed, x, y); + case NoiseType_OpenSimplex2S: + return SingleOpenSimplex2S(seed, x, y); + case NoiseType_Cellular: + return SingleCellular(seed, x, y); + case NoiseType_Perlin: + return SinglePerlin(seed, x, y); + case NoiseType_ValueCubic: + return SingleValueCubic(seed, x, y); + case NoiseType_Value: + return SingleValue(seed, x, y); + default: + return 0; + } + } + + template + float GenNoiseSingle(int seed, FNfloat x, FNfloat y, FNfloat z) const + { + switch (mNoiseType) + { + case NoiseType_OpenSimplex2: + return SingleOpenSimplex2(seed, x, y, z); + case NoiseType_OpenSimplex2S: + return SingleOpenSimplex2S(seed, x, y, z); + case NoiseType_Cellular: + return SingleCellular(seed, x, y, z); + case NoiseType_Perlin: + return SinglePerlin(seed, x, y, z); + case NoiseType_ValueCubic: + return SingleValueCubic(seed, x, y, z); + case NoiseType_Value: + return SingleValue(seed, x, y, z); + default: + return 0; + } + } + + + // Noise Coordinate Transforms (frequency, and possible skew or rotation) + + template + void TransformNoiseCoordinate(FNfloat& x, FNfloat& y) const + { + x *= mFrequency; + y *= mFrequency; + + switch (mNoiseType) + { + case NoiseType_OpenSimplex2: + case NoiseType_OpenSimplex2S: + { + const FNfloat SQRT3 = (FNfloat)1.7320508075688772935274463415059; + const FNfloat F2 = 0.5f * (SQRT3 - 1); + FNfloat t = (x + y) * F2; + x += t; + y += t; + } + break; + default: + break; + } + } + + template + void TransformNoiseCoordinate(FNfloat& x, FNfloat& y, FNfloat& z) const + { + x *= mFrequency; + y *= mFrequency; + z *= mFrequency; + + switch (mTransformType3D) + { + case TransformType3D_ImproveXYPlanes: + { + FNfloat xy = x + y; + FNfloat s2 = xy * -(FNfloat)0.211324865405187; + z *= (FNfloat)0.577350269189626; + x += s2 - z; + y = y + s2 - z; + z += xy * (FNfloat)0.577350269189626; + } + break; + case TransformType3D_ImproveXZPlanes: + { + FNfloat xz = x + z; + FNfloat s2 = xz * -(FNfloat)0.211324865405187; + y *= (FNfloat)0.577350269189626; + x += s2 - y; + z += s2 - y; + y += xz * (FNfloat)0.577350269189626; + } + break; + case TransformType3D_DefaultOpenSimplex2: + { + const FNfloat R3 = (FNfloat)(2.0 / 3.0); + FNfloat r = (x + y + z) * R3; // Rotation, not skew + x = r - x; + y = r - y; + z = r - z; + } + break; + default: + break; + } + } + + void UpdateTransformType3D() + { + switch (mRotationType3D) + { + case RotationType3D_ImproveXYPlanes: + mTransformType3D = TransformType3D_ImproveXYPlanes; + break; + case RotationType3D_ImproveXZPlanes: + mTransformType3D = TransformType3D_ImproveXZPlanes; + break; + default: + switch (mNoiseType) + { + case NoiseType_OpenSimplex2: + case NoiseType_OpenSimplex2S: + mTransformType3D = TransformType3D_DefaultOpenSimplex2; + break; + default: + mTransformType3D = TransformType3D_None; + break; + } + break; + } + } + + + // Domain Warp Coordinate Transforms + + template + void TransformDomainWarpCoordinate(FNfloat& x, FNfloat& y) const + { + switch (mDomainWarpType) + { + case DomainWarpType_OpenSimplex2: + case DomainWarpType_OpenSimplex2Reduced: + { + const FNfloat SQRT3 = (FNfloat)1.7320508075688772935274463415059; + const FNfloat F2 = 0.5f * (SQRT3 - 1); + FNfloat t = (x + y) * F2; + x += t; + y += t; + } + break; + default: + break; + } + } + + template + void TransformDomainWarpCoordinate(FNfloat& x, FNfloat& y, FNfloat& z) const + { + switch (mWarpTransformType3D) + { + case TransformType3D_ImproveXYPlanes: + { + FNfloat xy = x + y; + FNfloat s2 = xy * -(FNfloat)0.211324865405187; + z *= (FNfloat)0.577350269189626; + x += s2 - z; + y = y + s2 - z; + z += xy * (FNfloat)0.577350269189626; + } + break; + case TransformType3D_ImproveXZPlanes: + { + FNfloat xz = x + z; + FNfloat s2 = xz * -(FNfloat)0.211324865405187; + y *= (FNfloat)0.577350269189626; + x += s2 - y; + z += s2 - y; + y += xz * (FNfloat)0.577350269189626; + } + break; + case TransformType3D_DefaultOpenSimplex2: + { + const FNfloat R3 = (FNfloat)(2.0 / 3.0); + FNfloat r = (x + y + z) * R3; // Rotation, not skew + x = r - x; + y = r - y; + z = r - z; + } + break; + default: + break; + } + } + + void UpdateWarpTransformType3D() + { + switch (mRotationType3D) + { + case RotationType3D_ImproveXYPlanes: + mWarpTransformType3D = TransformType3D_ImproveXYPlanes; + break; + case RotationType3D_ImproveXZPlanes: + mWarpTransformType3D = TransformType3D_ImproveXZPlanes; + break; + default: + switch (mDomainWarpType) + { + case DomainWarpType_OpenSimplex2: + case DomainWarpType_OpenSimplex2Reduced: + mWarpTransformType3D = TransformType3D_DefaultOpenSimplex2; + break; + default: + mWarpTransformType3D = TransformType3D_None; + break; + } + break; + } + } + + + // Fractal FBm + + template + float GenFractalFBm(FNfloat x, FNfloat y) const + { + int seed = mSeed; + float sum = 0; + float amp = mFractalBounding; + + for (int i = 0; i < mOctaves; i++) + { + float noise = GenNoiseSingle(seed++, x, y); + sum += noise * amp; + amp *= Lerp(1.0f, FastMin(noise + 1, 2) * 0.5f, mWeightedStrength); + + x *= mLacunarity; + y *= mLacunarity; + amp *= mGain; + } + + return sum; + } + + template + float GenFractalFBm(FNfloat x, FNfloat y, FNfloat z) const + { + int seed = mSeed; + float sum = 0; + float amp = mFractalBounding; + + for (int i = 0; i < mOctaves; i++) + { + float noise = GenNoiseSingle(seed++, x, y, z); + sum += noise * amp; + amp *= Lerp(1.0f, (noise + 1) * 0.5f, mWeightedStrength); + + x *= mLacunarity; + y *= mLacunarity; + z *= mLacunarity; + amp *= mGain; + } + + return sum; + } + + + // Fractal Ridged + + template + float GenFractalRidged(FNfloat x, FNfloat y) const + { + int seed = mSeed; + float sum = 0; + float amp = mFractalBounding; + + for (int i = 0; i < mOctaves; i++) + { + float noise = FastAbs(GenNoiseSingle(seed++, x, y)); + sum += (noise * -2 + 1) * amp; + amp *= Lerp(1.0f, 1 - noise, mWeightedStrength); + + x *= mLacunarity; + y *= mLacunarity; + amp *= mGain; + } + + return sum; + } + + template + float GenFractalRidged(FNfloat x, FNfloat y, FNfloat z) const + { + int seed = mSeed; + float sum = 0; + float amp = mFractalBounding; + + for (int i = 0; i < mOctaves; i++) + { + float noise = FastAbs(GenNoiseSingle(seed++, x, y, z)); + sum += (noise * -2 + 1) * amp; + amp *= Lerp(1.0f, 1 - noise, mWeightedStrength); + + x *= mLacunarity; + y *= mLacunarity; + z *= mLacunarity; + amp *= mGain; + } + + return sum; + } + + + // Fractal PingPong + + template + float GenFractalPingPong(FNfloat x, FNfloat y) const + { + int seed = mSeed; + float sum = 0; + float amp = mFractalBounding; + + for (int i = 0; i < mOctaves; i++) + { + float noise = PingPong((GenNoiseSingle(seed++, x, y) + 1) * mPingPongStrength); + sum += (noise - 0.5f) * 2 * amp; + amp *= Lerp(1.0f, noise, mWeightedStrength); + + x *= mLacunarity; + y *= mLacunarity; + amp *= mGain; + } + + return sum; + } + + template + float GenFractalPingPong(FNfloat x, FNfloat y, FNfloat z) const + { + int seed = mSeed; + float sum = 0; + float amp = mFractalBounding; + + for (int i = 0; i < mOctaves; i++) + { + float noise = PingPong((GenNoiseSingle(seed++, x, y, z) + 1) * mPingPongStrength); + sum += (noise - 0.5f) * 2 * amp; + amp *= Lerp(1.0f, noise, mWeightedStrength); + + x *= mLacunarity; + y *= mLacunarity; + z *= mLacunarity; + amp *= mGain; + } + + return sum; + } + + + // Simplex/OpenSimplex2 Noise + + template + float SingleSimplex(int seed, FNfloat x, FNfloat y) const + { + // 2D OpenSimplex2 case uses the same algorithm as ordinary Simplex. + + const float SQRT3 = 1.7320508075688772935274463415059f; + const float G2 = (3 - SQRT3) / 6; + + /* + * --- Skew moved to TransformNoiseCoordinate method --- + * const FNfloat F2 = 0.5f * (SQRT3 - 1); + * FNfloat s = (x + y) * F2; + * x += s; y += s; + */ + + int i = FastFloor(x); + int j = FastFloor(y); + float xi = (float)(x - i); + float yi = (float)(y - j); + + float t = (xi + yi) * G2; + float x0 = (float)(xi - t); + float y0 = (float)(yi - t); + + i *= PrimeX; + j *= PrimeY; + + float n0, n1, n2; + + float a = 0.5f - x0 * x0 - y0 * y0; + if (a <= 0) n0 = 0; + else + { + n0 = (a * a) * (a * a) * GradCoord(seed, i, j, x0, y0); + } + + float c = (float)(2 * (1 - 2 * G2) * (1 / G2 - 2)) * t + ((float)(-2 * (1 - 2 * G2) * (1 - 2 * G2)) + a); + if (c <= 0) n2 = 0; + else + { + float x2 = x0 + (2 * (float)G2 - 1); + float y2 = y0 + (2 * (float)G2 - 1); + n2 = (c * c) * (c * c) * GradCoord(seed, i + PrimeX, j + PrimeY, x2, y2); + } + + if (y0 > x0) + { + float x1 = x0 + (float)G2; + float y1 = y0 + ((float)G2 - 1); + float b = 0.5f - x1 * x1 - y1 * y1; + if (b <= 0) n1 = 0; + else + { + n1 = (b * b) * (b * b) * GradCoord(seed, i, j + PrimeY, x1, y1); + } + } + else + { + float x1 = x0 + ((float)G2 - 1); + float y1 = y0 + (float)G2; + float b = 0.5f - x1 * x1 - y1 * y1; + if (b <= 0) n1 = 0; + else + { + n1 = (b * b) * (b * b) * GradCoord(seed, i + PrimeX, j, x1, y1); + } + } + + return (n0 + n1 + n2) * 99.83685446303647f; + } + + template + float SingleOpenSimplex2(int seed, FNfloat x, FNfloat y, FNfloat z) const + { + // 3D OpenSimplex2 case uses two offset rotated cube grids. + + /* + * --- Rotation moved to TransformNoiseCoordinate method --- + * const FNfloat R3 = (FNfloat)(2.0 / 3.0); + * FNfloat r = (x + y + z) * R3; // Rotation, not skew + * x = r - x; y = r - y; z = r - z; + */ + + int i = FastRound(x); + int j = FastRound(y); + int k = FastRound(z); + float x0 = (float)(x - i); + float y0 = (float)(y - j); + float z0 = (float)(z - k); + + int xNSign = (int)(-1.0f - x0) | 1; + int yNSign = (int)(-1.0f - y0) | 1; + int zNSign = (int)(-1.0f - z0) | 1; + + float ax0 = xNSign * -x0; + float ay0 = yNSign * -y0; + float az0 = zNSign * -z0; + + i *= PrimeX; + j *= PrimeY; + k *= PrimeZ; + + float value = 0; + float a = (0.6f - x0 * x0) - (y0 * y0 + z0 * z0); + + for (int l = 0; ; l++) + { + if (a > 0) + { + value += (a * a) * (a * a) * GradCoord(seed, i, j, k, x0, y0, z0); + } + + float b = a + 1; + int i1 = i; + int j1 = j; + int k1 = k; + float x1 = x0; + float y1 = y0; + float z1 = z0; + + if (ax0 >= ay0 && ax0 >= az0) + { + x1 += xNSign; + b -= xNSign * 2 * x1; + i1 -= xNSign * PrimeX; + } + else if (ay0 > ax0 && ay0 >= az0) + { + y1 += yNSign; + b -= yNSign * 2 * y1; + j1 -= yNSign * PrimeY; + } + else + { + z1 += zNSign; + b -= zNSign * 2 * z1; + k1 -= zNSign * PrimeZ; + } + + if (b > 0) + { + value += (b * b) * (b * b) * GradCoord(seed, i1, j1, k1, x1, y1, z1); + } + + if (l == 1) break; + + ax0 = 0.5f - ax0; + ay0 = 0.5f - ay0; + az0 = 0.5f - az0; + + x0 = xNSign * ax0; + y0 = yNSign * ay0; + z0 = zNSign * az0; + + a += (0.75f - ax0) - (ay0 + az0); + + i += (xNSign >> 1) & PrimeX; + j += (yNSign >> 1) & PrimeY; + k += (zNSign >> 1) & PrimeZ; + + xNSign = -xNSign; + yNSign = -yNSign; + zNSign = -zNSign; + + seed = ~seed; + } + + return value * 32.69428253173828125f; + } + + + // OpenSimplex2S Noise + + template + float SingleOpenSimplex2S(int seed, FNfloat x, FNfloat y) const + { + // 2D OpenSimplex2S case is a modified 2D simplex noise. + + const FNfloat SQRT3 = (FNfloat)1.7320508075688772935274463415059; + const FNfloat G2 = (3 - SQRT3) / 6; + + /* + * --- Skew moved to TransformNoiseCoordinate method --- + * const FNfloat F2 = 0.5f * (SQRT3 - 1); + * FNfloat s = (x + y) * F2; + * x += s; y += s; + */ + + int i = FastFloor(x); + int j = FastFloor(y); + float xi = (float)(x - i); + float yi = (float)(y - j); + + i *= PrimeX; + j *= PrimeY; + int i1 = i + PrimeX; + int j1 = j + PrimeY; + + float t = (xi + yi) * (float)G2; + float x0 = xi - t; + float y0 = yi - t; + + float a0 = (2.0f / 3.0f) - x0 * x0 - y0 * y0; + float value = (a0 * a0) * (a0 * a0) * GradCoord(seed, i, j, x0, y0); + + float a1 = (float)(2 * (1 - 2 * G2) * (1 / G2 - 2)) * t + ((float)(-2 * (1 - 2 * G2) * (1 - 2 * G2)) + a0); + float x1 = x0 - (float)(1 - 2 * G2); + float y1 = y0 - (float)(1 - 2 * G2); + value += (a1 * a1) * (a1 * a1) * GradCoord(seed, i1, j1, x1, y1); + + // Nested conditionals were faster than compact bit logic/arithmetic. + float xmyi = xi - yi; + if (t > G2) + { + if (xi + xmyi > 1) + { + float x2 = x0 + (float)(3 * G2 - 2); + float y2 = y0 + (float)(3 * G2 - 1); + float a2 = (2.0f / 3.0f) - x2 * x2 - y2 * y2; + if (a2 > 0) + { + value += (a2 * a2) * (a2 * a2) * GradCoord(seed, i + (PrimeX << 1), j + PrimeY, x2, y2); + } + } + else + { + float x2 = x0 + (float)G2; + float y2 = y0 + (float)(G2 - 1); + float a2 = (2.0f / 3.0f) - x2 * x2 - y2 * y2; + if (a2 > 0) + { + value += (a2 * a2) * (a2 * a2) * GradCoord(seed, i, j + PrimeY, x2, y2); + } + } + + if (yi - xmyi > 1) + { + float x3 = x0 + (float)(3 * G2 - 1); + float y3 = y0 + (float)(3 * G2 - 2); + float a3 = (2.0f / 3.0f) - x3 * x3 - y3 * y3; + if (a3 > 0) + { + value += (a3 * a3) * (a3 * a3) * GradCoord(seed, i + PrimeX, j + (PrimeY << 1), x3, y3); + } + } + else + { + float x3 = x0 + (float)(G2 - 1); + float y3 = y0 + (float)G2; + float a3 = (2.0f / 3.0f) - x3 * x3 - y3 * y3; + if (a3 > 0) + { + value += (a3 * a3) * (a3 * a3) * GradCoord(seed, i + PrimeX, j, x3, y3); + } + } + } + else + { + if (xi + xmyi < 0) + { + float x2 = x0 + (float)(1 - G2); + float y2 = y0 - (float)G2; + float a2 = (2.0f / 3.0f) - x2 * x2 - y2 * y2; + if (a2 > 0) + { + value += (a2 * a2) * (a2 * a2) * GradCoord(seed, i - PrimeX, j, x2, y2); + } + } + else + { + float x2 = x0 + (float)(G2 - 1); + float y2 = y0 + (float)G2; + float a2 = (2.0f / 3.0f) - x2 * x2 - y2 * y2; + if (a2 > 0) + { + value += (a2 * a2) * (a2 * a2) * GradCoord(seed, i + PrimeX, j, x2, y2); + } + } + + if (yi < xmyi) + { + float x2 = x0 - (float)G2; + float y2 = y0 - (float)(G2 - 1); + float a2 = (2.0f / 3.0f) - x2 * x2 - y2 * y2; + if (a2 > 0) + { + value += (a2 * a2) * (a2 * a2) * GradCoord(seed, i, j - PrimeY, x2, y2); + } + } + else + { + float x2 = x0 + (float)G2; + float y2 = y0 + (float)(G2 - 1); + float a2 = (2.0f / 3.0f) - x2 * x2 - y2 * y2; + if (a2 > 0) + { + value += (a2 * a2) * (a2 * a2) * GradCoord(seed, i, j + PrimeY, x2, y2); + } + } + } + + return value * 18.24196194486065f; + } + + template + float SingleOpenSimplex2S(int seed, FNfloat x, FNfloat y, FNfloat z) const + { + // 3D OpenSimplex2S case uses two offset rotated cube grids. + + /* + * --- Rotation moved to TransformNoiseCoordinate method --- + * const FNfloat R3 = (FNfloat)(2.0 / 3.0); + * FNfloat r = (x + y + z) * R3; // Rotation, not skew + * x = r - x; y = r - y; z = r - z; + */ + + int i = FastFloor(x); + int j = FastFloor(y); + int k = FastFloor(z); + float xi = (float)(x - i); + float yi = (float)(y - j); + float zi = (float)(z - k); + + i *= PrimeX; + j *= PrimeY; + k *= PrimeZ; + int seed2 = seed + 1293373; + + int xNMask = (int)(-0.5f - xi); + int yNMask = (int)(-0.5f - yi); + int zNMask = (int)(-0.5f - zi); + + float x0 = xi + xNMask; + float y0 = yi + yNMask; + float z0 = zi + zNMask; + float a0 = 0.75f - x0 * x0 - y0 * y0 - z0 * z0; + float value = (a0 * a0) * (a0 * a0) * GradCoord(seed, + i + (xNMask & PrimeX), j + (yNMask & PrimeY), k + (zNMask & PrimeZ), x0, y0, z0); + + float x1 = xi - 0.5f; + float y1 = yi - 0.5f; + float z1 = zi - 0.5f; + float a1 = 0.75f - x1 * x1 - y1 * y1 - z1 * z1; + value += (a1 * a1) * (a1 * a1) * GradCoord(seed2, + i + PrimeX, j + PrimeY, k + PrimeZ, x1, y1, z1); + + float xAFlipMask0 = ((xNMask | 1) << 1) * x1; + float yAFlipMask0 = ((yNMask | 1) << 1) * y1; + float zAFlipMask0 = ((zNMask | 1) << 1) * z1; + float xAFlipMask1 = (-2 - (xNMask << 2)) * x1 - 1.0f; + float yAFlipMask1 = (-2 - (yNMask << 2)) * y1 - 1.0f; + float zAFlipMask1 = (-2 - (zNMask << 2)) * z1 - 1.0f; + + bool skip5 = false; + float a2 = xAFlipMask0 + a0; + if (a2 > 0) + { + float x2 = x0 - (xNMask | 1); + float y2 = y0; + float z2 = z0; + value += (a2 * a2) * (a2 * a2) * GradCoord(seed, + i + (~xNMask & PrimeX), j + (yNMask & PrimeY), k + (zNMask & PrimeZ), x2, y2, z2); + } + else + { + float a3 = yAFlipMask0 + zAFlipMask0 + a0; + if (a3 > 0) + { + float x3 = x0; + float y3 = y0 - (yNMask | 1); + float z3 = z0 - (zNMask | 1); + value += (a3 * a3) * (a3 * a3) * GradCoord(seed, + i + (xNMask & PrimeX), j + (~yNMask & PrimeY), k + (~zNMask & PrimeZ), x3, y3, z3); + } + + float a4 = xAFlipMask1 + a1; + if (a4 > 0) + { + float x4 = (xNMask | 1) + x1; + float y4 = y1; + float z4 = z1; + value += (a4 * a4) * (a4 * a4) * GradCoord(seed2, + i + (xNMask & (PrimeX * 2)), j + PrimeY, k + PrimeZ, x4, y4, z4); + skip5 = true; + } + } + + bool skip9 = false; + float a6 = yAFlipMask0 + a0; + if (a6 > 0) + { + float x6 = x0; + float y6 = y0 - (yNMask | 1); + float z6 = z0; + value += (a6 * a6) * (a6 * a6) * GradCoord(seed, + i + (xNMask & PrimeX), j + (~yNMask & PrimeY), k + (zNMask & PrimeZ), x6, y6, z6); + } + else + { + float a7 = xAFlipMask0 + zAFlipMask0 + a0; + if (a7 > 0) + { + float x7 = x0 - (xNMask | 1); + float y7 = y0; + float z7 = z0 - (zNMask | 1); + value += (a7 * a7) * (a7 * a7) * GradCoord(seed, + i + (~xNMask & PrimeX), j + (yNMask & PrimeY), k + (~zNMask & PrimeZ), x7, y7, z7); + } + + float a8 = yAFlipMask1 + a1; + if (a8 > 0) + { + float x8 = x1; + float y8 = (yNMask | 1) + y1; + float z8 = z1; + value += (a8 * a8) * (a8 * a8) * GradCoord(seed2, + i + PrimeX, j + (yNMask & (PrimeY << 1)), k + PrimeZ, x8, y8, z8); + skip9 = true; + } + } + + bool skipD = false; + float aA = zAFlipMask0 + a0; + if (aA > 0) + { + float xA = x0; + float yA = y0; + float zA = z0 - (zNMask | 1); + value += (aA * aA) * (aA * aA) * GradCoord(seed, + i + (xNMask & PrimeX), j + (yNMask & PrimeY), k + (~zNMask & PrimeZ), xA, yA, zA); + } + else + { + float aB = xAFlipMask0 + yAFlipMask0 + a0; + if (aB > 0) + { + float xB = x0 - (xNMask | 1); + float yB = y0 - (yNMask | 1); + float zB = z0; + value += (aB * aB) * (aB * aB) * GradCoord(seed, + i + (~xNMask & PrimeX), j + (~yNMask & PrimeY), k + (zNMask & PrimeZ), xB, yB, zB); + } + + float aC = zAFlipMask1 + a1; + if (aC > 0) + { + float xC = x1; + float yC = y1; + float zC = (zNMask | 1) + z1; + value += (aC * aC) * (aC * aC) * GradCoord(seed2, + i + PrimeX, j + PrimeY, k + (zNMask & (PrimeZ << 1)), xC, yC, zC); + skipD = true; + } + } + + if (!skip5) + { + float a5 = yAFlipMask1 + zAFlipMask1 + a1; + if (a5 > 0) + { + float x5 = x1; + float y5 = (yNMask | 1) + y1; + float z5 = (zNMask | 1) + z1; + value += (a5 * a5) * (a5 * a5) * GradCoord(seed2, + i + PrimeX, j + (yNMask & (PrimeY << 1)), k + (zNMask & (PrimeZ << 1)), x5, y5, z5); + } + } + + if (!skip9) + { + float a9 = xAFlipMask1 + zAFlipMask1 + a1; + if (a9 > 0) + { + float x9 = (xNMask | 1) + x1; + float y9 = y1; + float z9 = (zNMask | 1) + z1; + value += (a9 * a9) * (a9 * a9) * GradCoord(seed2, + i + (xNMask & (PrimeX * 2)), j + PrimeY, k + (zNMask & (PrimeZ << 1)), x9, y9, z9); + } + } + + if (!skipD) + { + float aD = xAFlipMask1 + yAFlipMask1 + a1; + if (aD > 0) + { + float xD = (xNMask | 1) + x1; + float yD = (yNMask | 1) + y1; + float zD = z1; + value += (aD * aD) * (aD * aD) * GradCoord(seed2, + i + (xNMask & (PrimeX << 1)), j + (yNMask & (PrimeY << 1)), k + PrimeZ, xD, yD, zD); + } + } + + return value * 9.046026385208288f; + } + + + // Cellular Noise + + template + float SingleCellular(int seed, FNfloat x, FNfloat y) const + { + int xr = FastRound(x); + int yr = FastRound(y); + + float distance0 = 1e10f; + float distance1 = 1e10f; + int closestHash = 0; + + float cellularJitter = 0.43701595f * mCellularJitterModifier; + + int xPrimed = (xr - 1) * PrimeX; + int yPrimedBase = (yr - 1) * PrimeY; + + switch (mCellularDistanceFunction) + { + default: + case CellularDistanceFunction_Euclidean: + case CellularDistanceFunction_EuclideanSq: + for (int xi = xr - 1; xi <= xr + 1; xi++) + { + int yPrimed = yPrimedBase; + + for (int yi = yr - 1; yi <= yr + 1; yi++) + { + int hash = Hash(seed, xPrimed, yPrimed); + int idx = hash & (255 << 1); + + float vecX = (float)(xi - x) + Lookup::RandVecs2D[idx] * cellularJitter; + float vecY = (float)(yi - y) + Lookup::RandVecs2D[idx | 1] * cellularJitter; + + float newDistance = vecX * vecX + vecY * vecY; + + distance1 = FastMax(FastMin(distance1, newDistance), distance0); + if (newDistance < distance0) + { + distance0 = newDistance; + closestHash = hash; + } + yPrimed += PrimeY; + } + xPrimed += PrimeX; + } + break; + case CellularDistanceFunction_Manhattan: + for (int xi = xr - 1; xi <= xr + 1; xi++) + { + int yPrimed = yPrimedBase; + + for (int yi = yr - 1; yi <= yr + 1; yi++) + { + int hash = Hash(seed, xPrimed, yPrimed); + int idx = hash & (255 << 1); + + float vecX = (float)(xi - x) + Lookup::RandVecs2D[idx] * cellularJitter; + float vecY = (float)(yi - y) + Lookup::RandVecs2D[idx | 1] * cellularJitter; + + float newDistance = FastAbs(vecX) + FastAbs(vecY); + + distance1 = FastMax(FastMin(distance1, newDistance), distance0); + if (newDistance < distance0) + { + distance0 = newDistance; + closestHash = hash; + } + yPrimed += PrimeY; + } + xPrimed += PrimeX; + } + break; + case CellularDistanceFunction_Hybrid: + for (int xi = xr - 1; xi <= xr + 1; xi++) + { + int yPrimed = yPrimedBase; + + for (int yi = yr - 1; yi <= yr + 1; yi++) + { + int hash = Hash(seed, xPrimed, yPrimed); + int idx = hash & (255 << 1); + + float vecX = (float)(xi - x) + Lookup::RandVecs2D[idx] * cellularJitter; + float vecY = (float)(yi - y) + Lookup::RandVecs2D[idx | 1] * cellularJitter; + + float newDistance = (FastAbs(vecX) + FastAbs(vecY)) + (vecX * vecX + vecY * vecY); + + distance1 = FastMax(FastMin(distance1, newDistance), distance0); + if (newDistance < distance0) + { + distance0 = newDistance; + closestHash = hash; + } + yPrimed += PrimeY; + } + xPrimed += PrimeX; + } + break; + } + + if (mCellularDistanceFunction == CellularDistanceFunction_Euclidean && mCellularReturnType >= CellularReturnType_Distance) + { + distance0 = FastSqrt(distance0); + + if (mCellularReturnType >= CellularReturnType_Distance2) + { + distance1 = FastSqrt(distance1); + } + } + + switch (mCellularReturnType) + { + case CellularReturnType_CellValue: + return closestHash * (1 / 2147483648.0f); + case CellularReturnType_Distance: + return distance0 - 1; + case CellularReturnType_Distance2: + return distance1 - 1; + case CellularReturnType_Distance2Add: + return (distance1 + distance0) * 0.5f - 1; + case CellularReturnType_Distance2Sub: + return distance1 - distance0 - 1; + case CellularReturnType_Distance2Mul: + return distance1 * distance0 * 0.5f - 1; + case CellularReturnType_Distance2Div: + return distance0 / distance1 - 1; + default: + return 0; + } + } + + template + float SingleCellular(int seed, FNfloat x, FNfloat y, FNfloat z) const + { + int xr = FastRound(x); + int yr = FastRound(y); + int zr = FastRound(z); + + float distance0 = 1e10f; + float distance1 = 1e10f; + int closestHash = 0; + + float cellularJitter = 0.39614353f * mCellularJitterModifier; + + int xPrimed = (xr - 1) * PrimeX; + int yPrimedBase = (yr - 1) * PrimeY; + int zPrimedBase = (zr - 1) * PrimeZ; + + switch (mCellularDistanceFunction) + { + case CellularDistanceFunction_Euclidean: + case CellularDistanceFunction_EuclideanSq: + for (int xi = xr - 1; xi <= xr + 1; xi++) + { + int yPrimed = yPrimedBase; + + for (int yi = yr - 1; yi <= yr + 1; yi++) + { + int zPrimed = zPrimedBase; + + for (int zi = zr - 1; zi <= zr + 1; zi++) + { + int hash = Hash(seed, xPrimed, yPrimed, zPrimed); + int idx = hash & (255 << 2); + + float vecX = (float)(xi - x) + Lookup::RandVecs3D[idx] * cellularJitter; + float vecY = (float)(yi - y) + Lookup::RandVecs3D[idx | 1] * cellularJitter; + float vecZ = (float)(zi - z) + Lookup::RandVecs3D[idx | 2] * cellularJitter; + + float newDistance = vecX * vecX + vecY * vecY + vecZ * vecZ; + + distance1 = FastMax(FastMin(distance1, newDistance), distance0); + if (newDistance < distance0) + { + distance0 = newDistance; + closestHash = hash; + } + zPrimed += PrimeZ; + } + yPrimed += PrimeY; + } + xPrimed += PrimeX; + } + break; + case CellularDistanceFunction_Manhattan: + for (int xi = xr - 1; xi <= xr + 1; xi++) + { + int yPrimed = yPrimedBase; + + for (int yi = yr - 1; yi <= yr + 1; yi++) + { + int zPrimed = zPrimedBase; + + for (int zi = zr - 1; zi <= zr + 1; zi++) + { + int hash = Hash(seed, xPrimed, yPrimed, zPrimed); + int idx = hash & (255 << 2); + + float vecX = (float)(xi - x) + Lookup::RandVecs3D[idx] * cellularJitter; + float vecY = (float)(yi - y) + Lookup::RandVecs3D[idx | 1] * cellularJitter; + float vecZ = (float)(zi - z) + Lookup::RandVecs3D[idx | 2] * cellularJitter; + + float newDistance = FastAbs(vecX) + FastAbs(vecY) + FastAbs(vecZ); + + distance1 = FastMax(FastMin(distance1, newDistance), distance0); + if (newDistance < distance0) + { + distance0 = newDistance; + closestHash = hash; + } + zPrimed += PrimeZ; + } + yPrimed += PrimeY; + } + xPrimed += PrimeX; + } + break; + case CellularDistanceFunction_Hybrid: + for (int xi = xr - 1; xi <= xr + 1; xi++) + { + int yPrimed = yPrimedBase; + + for (int yi = yr - 1; yi <= yr + 1; yi++) + { + int zPrimed = zPrimedBase; + + for (int zi = zr - 1; zi <= zr + 1; zi++) + { + int hash = Hash(seed, xPrimed, yPrimed, zPrimed); + int idx = hash & (255 << 2); + + float vecX = (float)(xi - x) + Lookup::RandVecs3D[idx] * cellularJitter; + float vecY = (float)(yi - y) + Lookup::RandVecs3D[idx | 1] * cellularJitter; + float vecZ = (float)(zi - z) + Lookup::RandVecs3D[idx | 2] * cellularJitter; + + float newDistance = (FastAbs(vecX) + FastAbs(vecY) + FastAbs(vecZ)) + (vecX * vecX + vecY * vecY + vecZ * vecZ); + + distance1 = FastMax(FastMin(distance1, newDistance), distance0); + if (newDistance < distance0) + { + distance0 = newDistance; + closestHash = hash; + } + zPrimed += PrimeZ; + } + yPrimed += PrimeY; + } + xPrimed += PrimeX; + } + break; + default: + break; + } + + if (mCellularDistanceFunction == CellularDistanceFunction_Euclidean && mCellularReturnType >= CellularReturnType_Distance) + { + distance0 = FastSqrt(distance0); + + if (mCellularReturnType >= CellularReturnType_Distance2) + { + distance1 = FastSqrt(distance1); + } + } + + switch (mCellularReturnType) + { + case CellularReturnType_CellValue: + return closestHash * (1 / 2147483648.0f); + case CellularReturnType_Distance: + return distance0 - 1; + case CellularReturnType_Distance2: + return distance1 - 1; + case CellularReturnType_Distance2Add: + return (distance1 + distance0) * 0.5f - 1; + case CellularReturnType_Distance2Sub: + return distance1 - distance0 - 1; + case CellularReturnType_Distance2Mul: + return distance1 * distance0 * 0.5f - 1; + case CellularReturnType_Distance2Div: + return distance0 / distance1 - 1; + default: + return 0; + } + } + + + // Perlin Noise + + template + float SinglePerlin(int seed, FNfloat x, FNfloat y) const + { + int x0 = FastFloor(x); + int y0 = FastFloor(y); + + float xd0 = (float)(x - x0); + float yd0 = (float)(y - y0); + float xd1 = xd0 - 1; + float yd1 = yd0 - 1; + + float xs = InterpQuintic(xd0); + float ys = InterpQuintic(yd0); + + x0 *= PrimeX; + y0 *= PrimeY; + int x1 = x0 + PrimeX; + int y1 = y0 + PrimeY; + + float xf0 = Lerp(GradCoord(seed, x0, y0, xd0, yd0), GradCoord(seed, x1, y0, xd1, yd0), xs); + float xf1 = Lerp(GradCoord(seed, x0, y1, xd0, yd1), GradCoord(seed, x1, y1, xd1, yd1), xs); + + return Lerp(xf0, xf1, ys) * 1.4247691104677813f; + } + + template + float SinglePerlin(int seed, FNfloat x, FNfloat y, FNfloat z) const + { + int x0 = FastFloor(x); + int y0 = FastFloor(y); + int z0 = FastFloor(z); + + float xd0 = (float)(x - x0); + float yd0 = (float)(y - y0); + float zd0 = (float)(z - z0); + float xd1 = xd0 - 1; + float yd1 = yd0 - 1; + float zd1 = zd0 - 1; + + float xs = InterpQuintic(xd0); + float ys = InterpQuintic(yd0); + float zs = InterpQuintic(zd0); + + x0 *= PrimeX; + y0 *= PrimeY; + z0 *= PrimeZ; + int x1 = x0 + PrimeX; + int y1 = y0 + PrimeY; + int z1 = z0 + PrimeZ; + + float xf00 = Lerp(GradCoord(seed, x0, y0, z0, xd0, yd0, zd0), GradCoord(seed, x1, y0, z0, xd1, yd0, zd0), xs); + float xf10 = Lerp(GradCoord(seed, x0, y1, z0, xd0, yd1, zd0), GradCoord(seed, x1, y1, z0, xd1, yd1, zd0), xs); + float xf01 = Lerp(GradCoord(seed, x0, y0, z1, xd0, yd0, zd1), GradCoord(seed, x1, y0, z1, xd1, yd0, zd1), xs); + float xf11 = Lerp(GradCoord(seed, x0, y1, z1, xd0, yd1, zd1), GradCoord(seed, x1, y1, z1, xd1, yd1, zd1), xs); + + float yf0 = Lerp(xf00, xf10, ys); + float yf1 = Lerp(xf01, xf11, ys); + + return Lerp(yf0, yf1, zs) * 0.964921414852142333984375f; + } + + + // Value Cubic Noise + + template + float SingleValueCubic(int seed, FNfloat x, FNfloat y) const + { + int x1 = FastFloor(x); + int y1 = FastFloor(y); + + float xs = (float)(x - x1); + float ys = (float)(y - y1); + + x1 *= PrimeX; + y1 *= PrimeY; + int x0 = x1 - PrimeX; + int y0 = y1 - PrimeY; + int x2 = x1 + PrimeX; + int y2 = y1 + PrimeY; + int x3 = x1 + (int)((long)PrimeX << 1); + int y3 = y1 + (int)((long)PrimeY << 1); + + return CubicLerp( + CubicLerp(ValCoord(seed, x0, y0), ValCoord(seed, x1, y0), ValCoord(seed, x2, y0), ValCoord(seed, x3, y0), + xs), + CubicLerp(ValCoord(seed, x0, y1), ValCoord(seed, x1, y1), ValCoord(seed, x2, y1), ValCoord(seed, x3, y1), + xs), + CubicLerp(ValCoord(seed, x0, y2), ValCoord(seed, x1, y2), ValCoord(seed, x2, y2), ValCoord(seed, x3, y2), + xs), + CubicLerp(ValCoord(seed, x0, y3), ValCoord(seed, x1, y3), ValCoord(seed, x2, y3), ValCoord(seed, x3, y3), + xs), + ys) * (1 / (1.5f * 1.5f)); + } + + template + float SingleValueCubic(int seed, FNfloat x, FNfloat y, FNfloat z) const + { + int x1 = FastFloor(x); + int y1 = FastFloor(y); + int z1 = FastFloor(z); + + float xs = (float)(x - x1); + float ys = (float)(y - y1); + float zs = (float)(z - z1); + + x1 *= PrimeX; + y1 *= PrimeY; + z1 *= PrimeZ; + + int x0 = x1 - PrimeX; + int y0 = y1 - PrimeY; + int z0 = z1 - PrimeZ; + int x2 = x1 + PrimeX; + int y2 = y1 + PrimeY; + int z2 = z1 + PrimeZ; + int x3 = x1 + (int)((long)PrimeX << 1); + int y3 = y1 + (int)((long)PrimeY << 1); + int z3 = z1 + (int)((long)PrimeZ << 1); + + + return CubicLerp( + CubicLerp( + CubicLerp(ValCoord(seed, x0, y0, z0), ValCoord(seed, x1, y0, z0), ValCoord(seed, x2, y0, z0), ValCoord(seed, x3, y0, z0), xs), + CubicLerp(ValCoord(seed, x0, y1, z0), ValCoord(seed, x1, y1, z0), ValCoord(seed, x2, y1, z0), ValCoord(seed, x3, y1, z0), xs), + CubicLerp(ValCoord(seed, x0, y2, z0), ValCoord(seed, x1, y2, z0), ValCoord(seed, x2, y2, z0), ValCoord(seed, x3, y2, z0), xs), + CubicLerp(ValCoord(seed, x0, y3, z0), ValCoord(seed, x1, y3, z0), ValCoord(seed, x2, y3, z0), ValCoord(seed, x3, y3, z0), xs), + ys), + CubicLerp( + CubicLerp(ValCoord(seed, x0, y0, z1), ValCoord(seed, x1, y0, z1), ValCoord(seed, x2, y0, z1), ValCoord(seed, x3, y0, z1), xs), + CubicLerp(ValCoord(seed, x0, y1, z1), ValCoord(seed, x1, y1, z1), ValCoord(seed, x2, y1, z1), ValCoord(seed, x3, y1, z1), xs), + CubicLerp(ValCoord(seed, x0, y2, z1), ValCoord(seed, x1, y2, z1), ValCoord(seed, x2, y2, z1), ValCoord(seed, x3, y2, z1), xs), + CubicLerp(ValCoord(seed, x0, y3, z1), ValCoord(seed, x1, y3, z1), ValCoord(seed, x2, y3, z1), ValCoord(seed, x3, y3, z1), xs), + ys), + CubicLerp( + CubicLerp(ValCoord(seed, x0, y0, z2), ValCoord(seed, x1, y0, z2), ValCoord(seed, x2, y0, z2), ValCoord(seed, x3, y0, z2), xs), + CubicLerp(ValCoord(seed, x0, y1, z2), ValCoord(seed, x1, y1, z2), ValCoord(seed, x2, y1, z2), ValCoord(seed, x3, y1, z2), xs), + CubicLerp(ValCoord(seed, x0, y2, z2), ValCoord(seed, x1, y2, z2), ValCoord(seed, x2, y2, z2), ValCoord(seed, x3, y2, z2), xs), + CubicLerp(ValCoord(seed, x0, y3, z2), ValCoord(seed, x1, y3, z2), ValCoord(seed, x2, y3, z2), ValCoord(seed, x3, y3, z2), xs), + ys), + CubicLerp( + CubicLerp(ValCoord(seed, x0, y0, z3), ValCoord(seed, x1, y0, z3), ValCoord(seed, x2, y0, z3), ValCoord(seed, x3, y0, z3), xs), + CubicLerp(ValCoord(seed, x0, y1, z3), ValCoord(seed, x1, y1, z3), ValCoord(seed, x2, y1, z3), ValCoord(seed, x3, y1, z3), xs), + CubicLerp(ValCoord(seed, x0, y2, z3), ValCoord(seed, x1, y2, z3), ValCoord(seed, x2, y2, z3), ValCoord(seed, x3, y2, z3), xs), + CubicLerp(ValCoord(seed, x0, y3, z3), ValCoord(seed, x1, y3, z3), ValCoord(seed, x2, y3, z3), ValCoord(seed, x3, y3, z3), xs), + ys), + zs) * (1 / (1.5f * 1.5f * 1.5f)); + } + + + // Value Noise + + template + float SingleValue(int seed, FNfloat x, FNfloat y) const + { + int x0 = FastFloor(x); + int y0 = FastFloor(y); + + float xs = InterpHermite((float)(x - x0)); + float ys = InterpHermite((float)(y - y0)); + + x0 *= PrimeX; + y0 *= PrimeY; + int x1 = x0 + PrimeX; + int y1 = y0 + PrimeY; + + float xf0 = Lerp(ValCoord(seed, x0, y0), ValCoord(seed, x1, y0), xs); + float xf1 = Lerp(ValCoord(seed, x0, y1), ValCoord(seed, x1, y1), xs); + + return Lerp(xf0, xf1, ys); + } + + template + float SingleValue(int seed, FNfloat x, FNfloat y, FNfloat z) const + { + int x0 = FastFloor(x); + int y0 = FastFloor(y); + int z0 = FastFloor(z); + + float xs = InterpHermite((float)(x - x0)); + float ys = InterpHermite((float)(y - y0)); + float zs = InterpHermite((float)(z - z0)); + + x0 *= PrimeX; + y0 *= PrimeY; + z0 *= PrimeZ; + int x1 = x0 + PrimeX; + int y1 = y0 + PrimeY; + int z1 = z0 + PrimeZ; + + float xf00 = Lerp(ValCoord(seed, x0, y0, z0), ValCoord(seed, x1, y0, z0), xs); + float xf10 = Lerp(ValCoord(seed, x0, y1, z0), ValCoord(seed, x1, y1, z0), xs); + float xf01 = Lerp(ValCoord(seed, x0, y0, z1), ValCoord(seed, x1, y0, z1), xs); + float xf11 = Lerp(ValCoord(seed, x0, y1, z1), ValCoord(seed, x1, y1, z1), xs); + + float yf0 = Lerp(xf00, xf10, ys); + float yf1 = Lerp(xf01, xf11, ys); + + return Lerp(yf0, yf1, zs); + } + + + // Domain Warp + + template + void DoSingleDomainWarp(int seed, float amp, float freq, FNfloat x, FNfloat y, FNfloat& xr, FNfloat& yr) const + { + switch (mDomainWarpType) + { + case DomainWarpType_OpenSimplex2: + SingleDomainWarpSimplexGradient(seed, amp * 38.283687591552734375f, freq, x, y, xr, yr, false); + break; + case DomainWarpType_OpenSimplex2Reduced: + SingleDomainWarpSimplexGradient(seed, amp * 16.0f, freq, x, y, xr, yr, true); + break; + case DomainWarpType_BasicGrid: + SingleDomainWarpBasicGrid(seed, amp, freq, x, y, xr, yr); + break; + } + } + + template + void DoSingleDomainWarp(int seed, float amp, float freq, FNfloat x, FNfloat y, FNfloat z, FNfloat& xr, FNfloat& yr, FNfloat& zr) const + { + switch (mDomainWarpType) + { + case DomainWarpType_OpenSimplex2: + SingleDomainWarpOpenSimplex2Gradient(seed, amp * 32.69428253173828125f, freq, x, y, z, xr, yr, zr, false); + break; + case DomainWarpType_OpenSimplex2Reduced: + SingleDomainWarpOpenSimplex2Gradient(seed, amp * 7.71604938271605f, freq, x, y, z, xr, yr, zr, true); + break; + case DomainWarpType_BasicGrid: + SingleDomainWarpBasicGrid(seed, amp, freq, x, y, z, xr, yr, zr); + break; + } + } + + + // Domain Warp Single Wrapper + + template + void DomainWarpSingle(FNfloat& x, FNfloat& y) const + { + int seed = mSeed; + float amp = mDomainWarpAmp * mFractalBounding; + float freq = mFrequency; + + FNfloat xs = x; + FNfloat ys = y; + TransformDomainWarpCoordinate(xs, ys); + + DoSingleDomainWarp(seed, amp, freq, xs, ys, x, y); + } + + template + void DomainWarpSingle(FNfloat& x, FNfloat& y, FNfloat& z) const + { + int seed = mSeed; + float amp = mDomainWarpAmp * mFractalBounding; + float freq = mFrequency; + + FNfloat xs = x; + FNfloat ys = y; + FNfloat zs = z; + TransformDomainWarpCoordinate(xs, ys, zs); + + DoSingleDomainWarp(seed, amp, freq, xs, ys, zs, x, y, z); + } + + + // Domain Warp Fractal Progressive + + template + void DomainWarpFractalProgressive(FNfloat& x, FNfloat& y) const + { + int seed = mSeed; + float amp = mDomainWarpAmp * mFractalBounding; + float freq = mFrequency; + + for (int i = 0; i < mOctaves; i++) + { + FNfloat xs = x; + FNfloat ys = y; + TransformDomainWarpCoordinate(xs, ys); + + DoSingleDomainWarp(seed, amp, freq, xs, ys, x, y); + + seed++; + amp *= mGain; + freq *= mLacunarity; + } + } + + template + void DomainWarpFractalProgressive(FNfloat& x, FNfloat& y, FNfloat& z) const + { + int seed = mSeed; + float amp = mDomainWarpAmp * mFractalBounding; + float freq = mFrequency; + + for (int i = 0; i < mOctaves; i++) + { + FNfloat xs = x; + FNfloat ys = y; + FNfloat zs = z; + TransformDomainWarpCoordinate(xs, ys, zs); + + DoSingleDomainWarp(seed, amp, freq, xs, ys, zs, x, y, z); + + seed++; + amp *= mGain; + freq *= mLacunarity; + } + } + + + // Domain Warp Fractal Independant + + template + void DomainWarpFractalIndependent(FNfloat& x, FNfloat& y) const + { + FNfloat xs = x; + FNfloat ys = y; + TransformDomainWarpCoordinate(xs, ys); + + int seed = mSeed; + float amp = mDomainWarpAmp * mFractalBounding; + float freq = mFrequency; + + for (int i = 0; i < mOctaves; i++) + { + DoSingleDomainWarp(seed, amp, freq, xs, ys, x, y); + + seed++; + amp *= mGain; + freq *= mLacunarity; + } + } + + template + void DomainWarpFractalIndependent(FNfloat& x, FNfloat& y, FNfloat& z) const + { + FNfloat xs = x; + FNfloat ys = y; + FNfloat zs = z; + TransformDomainWarpCoordinate(xs, ys, zs); + + int seed = mSeed; + float amp = mDomainWarpAmp * mFractalBounding; + float freq = mFrequency; + + for (int i = 0; i < mOctaves; i++) + { + DoSingleDomainWarp(seed, amp, freq, xs, ys, zs, x, y, z); + + seed++; + amp *= mGain; + freq *= mLacunarity; + } + } + + + // Domain Warp Basic Grid + + template + void SingleDomainWarpBasicGrid(int seed, float warpAmp, float frequency, FNfloat x, FNfloat y, FNfloat& xr, FNfloat& yr) const + { + FNfloat xf = x * frequency; + FNfloat yf = y * frequency; + + int x0 = FastFloor(xf); + int y0 = FastFloor(yf); + + float xs = InterpHermite((float)(xf - x0)); + float ys = InterpHermite((float)(yf - y0)); + + x0 *= PrimeX; + y0 *= PrimeY; + int x1 = x0 + PrimeX; + int y1 = y0 + PrimeY; + + int hash0 = Hash(seed, x0, y0) & (255 << 1); + int hash1 = Hash(seed, x1, y0) & (255 << 1); + + float lx0x = Lerp(Lookup::RandVecs2D[hash0], Lookup::RandVecs2D[hash1], xs); + float ly0x = Lerp(Lookup::RandVecs2D[hash0 | 1], Lookup::RandVecs2D[hash1 | 1], xs); + + hash0 = Hash(seed, x0, y1) & (255 << 1); + hash1 = Hash(seed, x1, y1) & (255 << 1); + + float lx1x = Lerp(Lookup::RandVecs2D[hash0], Lookup::RandVecs2D[hash1], xs); + float ly1x = Lerp(Lookup::RandVecs2D[hash0 | 1], Lookup::RandVecs2D[hash1 | 1], xs); + + xr += Lerp(lx0x, lx1x, ys) * warpAmp; + yr += Lerp(ly0x, ly1x, ys) * warpAmp; + } + + template + void SingleDomainWarpBasicGrid(int seed, float warpAmp, float frequency, FNfloat x, FNfloat y, FNfloat z, FNfloat& xr, FNfloat& yr, FNfloat& zr) const + { + FNfloat xf = x * frequency; + FNfloat yf = y * frequency; + FNfloat zf = z * frequency; + + int x0 = FastFloor(xf); + int y0 = FastFloor(yf); + int z0 = FastFloor(zf); + + float xs = InterpHermite((float)(xf - x0)); + float ys = InterpHermite((float)(yf - y0)); + float zs = InterpHermite((float)(zf - z0)); + + x0 *= PrimeX; + y0 *= PrimeY; + z0 *= PrimeZ; + int x1 = x0 + PrimeX; + int y1 = y0 + PrimeY; + int z1 = z0 + PrimeZ; + + int hash0 = Hash(seed, x0, y0, z0) & (255 << 2); + int hash1 = Hash(seed, x1, y0, z0) & (255 << 2); + + float lx0x = Lerp(Lookup::RandVecs3D[hash0], Lookup::RandVecs3D[hash1], xs); + float ly0x = Lerp(Lookup::RandVecs3D[hash0 | 1], Lookup::RandVecs3D[hash1 | 1], xs); + float lz0x = Lerp(Lookup::RandVecs3D[hash0 | 2], Lookup::RandVecs3D[hash1 | 2], xs); + + hash0 = Hash(seed, x0, y1, z0) & (255 << 2); + hash1 = Hash(seed, x1, y1, z0) & (255 << 2); + + float lx1x = Lerp(Lookup::RandVecs3D[hash0], Lookup::RandVecs3D[hash1], xs); + float ly1x = Lerp(Lookup::RandVecs3D[hash0 | 1], Lookup::RandVecs3D[hash1 | 1], xs); + float lz1x = Lerp(Lookup::RandVecs3D[hash0 | 2], Lookup::RandVecs3D[hash1 | 2], xs); + + float lx0y = Lerp(lx0x, lx1x, ys); + float ly0y = Lerp(ly0x, ly1x, ys); + float lz0y = Lerp(lz0x, lz1x, ys); + + hash0 = Hash(seed, x0, y0, z1) & (255 << 2); + hash1 = Hash(seed, x1, y0, z1) & (255 << 2); + + lx0x = Lerp(Lookup::RandVecs3D[hash0], Lookup::RandVecs3D[hash1], xs); + ly0x = Lerp(Lookup::RandVecs3D[hash0 | 1], Lookup::RandVecs3D[hash1 | 1], xs); + lz0x = Lerp(Lookup::RandVecs3D[hash0 | 2], Lookup::RandVecs3D[hash1 | 2], xs); + + hash0 = Hash(seed, x0, y1, z1) & (255 << 2); + hash1 = Hash(seed, x1, y1, z1) & (255 << 2); + + lx1x = Lerp(Lookup::RandVecs3D[hash0], Lookup::RandVecs3D[hash1], xs); + ly1x = Lerp(Lookup::RandVecs3D[hash0 | 1], Lookup::RandVecs3D[hash1 | 1], xs); + lz1x = Lerp(Lookup::RandVecs3D[hash0 | 2], Lookup::RandVecs3D[hash1 | 2], xs); + + xr += Lerp(lx0y, Lerp(lx0x, lx1x, ys), zs) * warpAmp; + yr += Lerp(ly0y, Lerp(ly0x, ly1x, ys), zs) * warpAmp; + zr += Lerp(lz0y, Lerp(lz0x, lz1x, ys), zs) * warpAmp; + } + + + // Domain Warp Simplex/OpenSimplex2 + + template + void SingleDomainWarpSimplexGradient(int seed, float warpAmp, float frequency, FNfloat x, FNfloat y, FNfloat& xr, FNfloat& yr, bool outGradOnly) const + { + const float SQRT3 = 1.7320508075688772935274463415059f; + const float G2 = (3 - SQRT3) / 6; + + x *= frequency; + y *= frequency; + + /* + * --- Skew moved to TransformNoiseCoordinate method --- + * const FNfloat F2 = 0.5f * (SQRT3 - 1); + * FNfloat s = (x + y) * F2; + * x += s; y += s; + */ + + int i = FastFloor(x); + int j = FastFloor(y); + float xi = (float)(x - i); + float yi = (float)(y - j); + + float t = (xi + yi) * G2; + float x0 = (float)(xi - t); + float y0 = (float)(yi - t); + + i *= PrimeX; + j *= PrimeY; + + float vx, vy; + vx = vy = 0; + + float a = 0.5f - x0 * x0 - y0 * y0; + if (a > 0) + { + float aaaa = (a * a) * (a * a); + float xo, yo; + if (outGradOnly) + GradCoordOut(seed, i, j, xo, yo); + else + GradCoordDual(seed, i, j, x0, y0, xo, yo); + vx += aaaa * xo; + vy += aaaa * yo; + } + + float c = (float)(2 * (1 - 2 * G2) * (1 / G2 - 2)) * t + ((float)(-2 * (1 - 2 * G2) * (1 - 2 * G2)) + a); + if (c > 0) + { + float x2 = x0 + (2 * (float)G2 - 1); + float y2 = y0 + (2 * (float)G2 - 1); + float cccc = (c * c) * (c * c); + float xo, yo; + if (outGradOnly) + GradCoordOut(seed, i + PrimeX, j + PrimeY, xo, yo); + else + GradCoordDual(seed, i + PrimeX, j + PrimeY, x2, y2, xo, yo); + vx += cccc * xo; + vy += cccc * yo; + } + + if (y0 > x0) + { + float x1 = x0 + (float)G2; + float y1 = y0 + ((float)G2 - 1); + float b = 0.5f - x1 * x1 - y1 * y1; + if (b > 0) + { + float bbbb = (b * b) * (b * b); + float xo, yo; + if (outGradOnly) + GradCoordOut(seed, i, j + PrimeY, xo, yo); + else + GradCoordDual(seed, i, j + PrimeY, x1, y1, xo, yo); + vx += bbbb * xo; + vy += bbbb * yo; + } + } + else + { + float x1 = x0 + ((float)G2 - 1); + float y1 = y0 + (float)G2; + float b = 0.5f - x1 * x1 - y1 * y1; + if (b > 0) + { + float bbbb = (b * b) * (b * b); + float xo, yo; + if (outGradOnly) + GradCoordOut(seed, i + PrimeX, j, xo, yo); + else + GradCoordDual(seed, i + PrimeX, j, x1, y1, xo, yo); + vx += bbbb * xo; + vy += bbbb * yo; + } + } + + xr += vx * warpAmp; + yr += vy * warpAmp; + } + + template + void SingleDomainWarpOpenSimplex2Gradient(int seed, float warpAmp, float frequency, FNfloat x, FNfloat y, FNfloat z, FNfloat& xr, FNfloat& yr, FNfloat& zr, bool outGradOnly) const + { + x *= frequency; + y *= frequency; + z *= frequency; + + /* + * --- Rotation moved to TransformDomainWarpCoordinate method --- + * const FNfloat R3 = (FNfloat)(2.0 / 3.0); + * FNfloat r = (x + y + z) * R3; // Rotation, not skew + * x = r - x; y = r - y; z = r - z; + */ + + int i = FastRound(x); + int j = FastRound(y); + int k = FastRound(z); + float x0 = (float)x - i; + float y0 = (float)y - j; + float z0 = (float)z - k; + + int xNSign = (int)(-x0 - 1.0f) | 1; + int yNSign = (int)(-y0 - 1.0f) | 1; + int zNSign = (int)(-z0 - 1.0f) | 1; + + float ax0 = xNSign * -x0; + float ay0 = yNSign * -y0; + float az0 = zNSign * -z0; + + i *= PrimeX; + j *= PrimeY; + k *= PrimeZ; + + float vx, vy, vz; + vx = vy = vz = 0; + + float a = (0.6f - x0 * x0) - (y0 * y0 + z0 * z0); + for (int l = 0; l < 2; l++) + { + if (a > 0) + { + float aaaa = (a * a) * (a * a); + float xo, yo, zo; + if (outGradOnly) + GradCoordOut(seed, i, j, k, xo, yo, zo); + else + GradCoordDual(seed, i, j, k, x0, y0, z0, xo, yo, zo); + vx += aaaa * xo; + vy += aaaa * yo; + vz += aaaa * zo; + } + + float b = a + 1; + int i1 = i; + int j1 = j; + int k1 = k; + float x1 = x0; + float y1 = y0; + float z1 = z0; + + if (ax0 >= ay0 && ax0 >= az0) + { + x1 += xNSign; + b -= xNSign * 2 * x1; + i1 -= xNSign * PrimeX; + } + else if (ay0 > ax0 && ay0 >= az0) + { + y1 += yNSign; + b -= yNSign * 2 * y1; + j1 -= yNSign * PrimeY; + } + else + { + z1 += zNSign; + b -= zNSign * 2 * z1; + k1 -= zNSign * PrimeZ; + } + + if (b > 0) + { + float bbbb = (b * b) * (b * b); + float xo, yo, zo; + if (outGradOnly) + GradCoordOut(seed, i1, j1, k1, xo, yo, zo); + else + GradCoordDual(seed, i1, j1, k1, x1, y1, z1, xo, yo, zo); + vx += bbbb * xo; + vy += bbbb * yo; + vz += bbbb * zo; + } + + if (l == 1) break; + + ax0 = 0.5f - ax0; + ay0 = 0.5f - ay0; + az0 = 0.5f - az0; + + x0 = xNSign * ax0; + y0 = yNSign * ay0; + z0 = zNSign * az0; + + a += (0.75f - ax0) - (ay0 + az0); + + i += (xNSign >> 1) & PrimeX; + j += (yNSign >> 1) & PrimeY; + k += (zNSign >> 1) & PrimeZ; + + xNSign = -xNSign; + yNSign = -yNSign; + zNSign = -zNSign; + + seed += 1293373; + } + + xr += vx * warpAmp; + yr += vy * warpAmp; + zr += vz * warpAmp; + } +}; + +template <> +struct FastNoiseLite::Arguments_must_be_floating_point_values {}; +template <> +struct FastNoiseLite::Arguments_must_be_floating_point_values {}; +template <> +struct FastNoiseLite::Arguments_must_be_floating_point_values {}; + +template +const T FastNoiseLite::Lookup::Gradients2D[] = +{ + 0.130526192220052f, 0.99144486137381f, 0.38268343236509f, 0.923879532511287f, 0.608761429008721f, 0.793353340291235f, 0.793353340291235f, 0.608761429008721f, + 0.923879532511287f, 0.38268343236509f, 0.99144486137381f, 0.130526192220051f, 0.99144486137381f, -0.130526192220051f, 0.923879532511287f, -0.38268343236509f, + 0.793353340291235f, -0.60876142900872f, 0.608761429008721f, -0.793353340291235f, 0.38268343236509f, -0.923879532511287f, 0.130526192220052f, -0.99144486137381f, + -0.130526192220052f, -0.99144486137381f, -0.38268343236509f, -0.923879532511287f, -0.608761429008721f, -0.793353340291235f, -0.793353340291235f, -0.608761429008721f, + -0.923879532511287f, -0.38268343236509f, -0.99144486137381f, -0.130526192220052f, -0.99144486137381f, 0.130526192220051f, -0.923879532511287f, 0.38268343236509f, + -0.793353340291235f, 0.608761429008721f, -0.608761429008721f, 0.793353340291235f, -0.38268343236509f, 0.923879532511287f, -0.130526192220052f, 0.99144486137381f, + 0.130526192220052f, 0.99144486137381f, 0.38268343236509f, 0.923879532511287f, 0.608761429008721f, 0.793353340291235f, 0.793353340291235f, 0.608761429008721f, + 0.923879532511287f, 0.38268343236509f, 0.99144486137381f, 0.130526192220051f, 0.99144486137381f, -0.130526192220051f, 0.923879532511287f, -0.38268343236509f, + 0.793353340291235f, -0.60876142900872f, 0.608761429008721f, -0.793353340291235f, 0.38268343236509f, -0.923879532511287f, 0.130526192220052f, -0.99144486137381f, + -0.130526192220052f, -0.99144486137381f, -0.38268343236509f, -0.923879532511287f, -0.608761429008721f, -0.793353340291235f, -0.793353340291235f, -0.608761429008721f, + -0.923879532511287f, -0.38268343236509f, -0.99144486137381f, -0.130526192220052f, -0.99144486137381f, 0.130526192220051f, -0.923879532511287f, 0.38268343236509f, + -0.793353340291235f, 0.608761429008721f, -0.608761429008721f, 0.793353340291235f, -0.38268343236509f, 0.923879532511287f, -0.130526192220052f, 0.99144486137381f, + 0.130526192220052f, 0.99144486137381f, 0.38268343236509f, 0.923879532511287f, 0.608761429008721f, 0.793353340291235f, 0.793353340291235f, 0.608761429008721f, + 0.923879532511287f, 0.38268343236509f, 0.99144486137381f, 0.130526192220051f, 0.99144486137381f, -0.130526192220051f, 0.923879532511287f, -0.38268343236509f, + 0.793353340291235f, -0.60876142900872f, 0.608761429008721f, -0.793353340291235f, 0.38268343236509f, -0.923879532511287f, 0.130526192220052f, -0.99144486137381f, + -0.130526192220052f, -0.99144486137381f, -0.38268343236509f, -0.923879532511287f, -0.608761429008721f, -0.793353340291235f, -0.793353340291235f, -0.608761429008721f, + -0.923879532511287f, -0.38268343236509f, -0.99144486137381f, -0.130526192220052f, -0.99144486137381f, 0.130526192220051f, -0.923879532511287f, 0.38268343236509f, + -0.793353340291235f, 0.608761429008721f, -0.608761429008721f, 0.793353340291235f, -0.38268343236509f, 0.923879532511287f, -0.130526192220052f, 0.99144486137381f, + 0.130526192220052f, 0.99144486137381f, 0.38268343236509f, 0.923879532511287f, 0.608761429008721f, 0.793353340291235f, 0.793353340291235f, 0.608761429008721f, + 0.923879532511287f, 0.38268343236509f, 0.99144486137381f, 0.130526192220051f, 0.99144486137381f, -0.130526192220051f, 0.923879532511287f, -0.38268343236509f, + 0.793353340291235f, -0.60876142900872f, 0.608761429008721f, -0.793353340291235f, 0.38268343236509f, -0.923879532511287f, 0.130526192220052f, -0.99144486137381f, + -0.130526192220052f, -0.99144486137381f, -0.38268343236509f, -0.923879532511287f, -0.608761429008721f, -0.793353340291235f, -0.793353340291235f, -0.608761429008721f, + -0.923879532511287f, -0.38268343236509f, -0.99144486137381f, -0.130526192220052f, -0.99144486137381f, 0.130526192220051f, -0.923879532511287f, 0.38268343236509f, + -0.793353340291235f, 0.608761429008721f, -0.608761429008721f, 0.793353340291235f, -0.38268343236509f, 0.923879532511287f, -0.130526192220052f, 0.99144486137381f, + 0.130526192220052f, 0.99144486137381f, 0.38268343236509f, 0.923879532511287f, 0.608761429008721f, 0.793353340291235f, 0.793353340291235f, 0.608761429008721f, + 0.923879532511287f, 0.38268343236509f, 0.99144486137381f, 0.130526192220051f, 0.99144486137381f, -0.130526192220051f, 0.923879532511287f, -0.38268343236509f, + 0.793353340291235f, -0.60876142900872f, 0.608761429008721f, -0.793353340291235f, 0.38268343236509f, -0.923879532511287f, 0.130526192220052f, -0.99144486137381f, + -0.130526192220052f, -0.99144486137381f, -0.38268343236509f, -0.923879532511287f, -0.608761429008721f, -0.793353340291235f, -0.793353340291235f, -0.608761429008721f, + -0.923879532511287f, -0.38268343236509f, -0.99144486137381f, -0.130526192220052f, -0.99144486137381f, 0.130526192220051f, -0.923879532511287f, 0.38268343236509f, + -0.793353340291235f, 0.608761429008721f, -0.608761429008721f, 0.793353340291235f, -0.38268343236509f, 0.923879532511287f, -0.130526192220052f, 0.99144486137381f, + 0.38268343236509f, 0.923879532511287f, 0.923879532511287f, 0.38268343236509f, 0.923879532511287f, -0.38268343236509f, 0.38268343236509f, -0.923879532511287f, + -0.38268343236509f, -0.923879532511287f, -0.923879532511287f, -0.38268343236509f, -0.923879532511287f, 0.38268343236509f, -0.38268343236509f, 0.923879532511287f, +}; + +template +const T FastNoiseLite::Lookup::RandVecs2D[] = +{ + -0.2700222198f, -0.9628540911f, 0.3863092627f, -0.9223693152f, 0.04444859006f, -0.999011673f, -0.5992523158f, -0.8005602176f, -0.7819280288f, 0.6233687174f, 0.9464672271f, 0.3227999196f, -0.6514146797f, -0.7587218957f, 0.9378472289f, 0.347048376f, + -0.8497875957f, -0.5271252623f, -0.879042592f, 0.4767432447f, -0.892300288f, -0.4514423508f, -0.379844434f, -0.9250503802f, -0.9951650832f, 0.0982163789f, 0.7724397808f, -0.6350880136f, 0.7573283322f, -0.6530343002f, -0.9928004525f, -0.119780055f, + -0.0532665713f, 0.9985803285f, 0.9754253726f, -0.2203300762f, -0.7665018163f, 0.6422421394f, 0.991636706f, 0.1290606184f, -0.994696838f, 0.1028503788f, -0.5379205513f, -0.84299554f, 0.5022815471f, -0.8647041387f, 0.4559821461f, -0.8899889226f, + -0.8659131224f, -0.5001944266f, 0.0879458407f, -0.9961252577f, -0.5051684983f, 0.8630207346f, 0.7753185226f, -0.6315704146f, -0.6921944612f, 0.7217110418f, -0.5191659449f, -0.8546734591f, 0.8978622882f, -0.4402764035f, -0.1706774107f, 0.9853269617f, + -0.9353430106f, -0.3537420705f, -0.9992404798f, 0.03896746794f, -0.2882064021f, -0.9575683108f, -0.9663811329f, 0.2571137995f, -0.8759714238f, -0.4823630009f, -0.8303123018f, -0.5572983775f, 0.05110133755f, -0.9986934731f, -0.8558373281f, -0.5172450752f, + 0.09887025282f, 0.9951003332f, 0.9189016087f, 0.3944867976f, -0.2439375892f, -0.9697909324f, -0.8121409387f, -0.5834613061f, -0.9910431363f, 0.1335421355f, 0.8492423985f, -0.5280031709f, -0.9717838994f, -0.2358729591f, 0.9949457207f, 0.1004142068f, + 0.6241065508f, -0.7813392434f, 0.662910307f, 0.7486988212f, -0.7197418176f, 0.6942418282f, -0.8143370775f, -0.5803922158f, 0.104521054f, -0.9945226741f, -0.1065926113f, -0.9943027784f, 0.445799684f, -0.8951327509f, 0.105547406f, 0.9944142724f, + -0.992790267f, 0.1198644477f, -0.8334366408f, 0.552615025f, 0.9115561563f, -0.4111755999f, 0.8285544909f, -0.5599084351f, 0.7217097654f, -0.6921957921f, 0.4940492677f, -0.8694339084f, -0.3652321272f, -0.9309164803f, -0.9696606758f, 0.2444548501f, + 0.08925509731f, -0.996008799f, 0.5354071276f, -0.8445941083f, -0.1053576186f, 0.9944343981f, -0.9890284586f, 0.1477251101f, 0.004856104961f, 0.9999882091f, 0.9885598478f, 0.1508291331f, 0.9286129562f, -0.3710498316f, -0.5832393863f, -0.8123003252f, + 0.3015207509f, 0.9534596146f, -0.9575110528f, 0.2883965738f, 0.9715802154f, -0.2367105511f, 0.229981792f, 0.9731949318f, 0.955763816f, -0.2941352207f, 0.740956116f, 0.6715534485f, -0.9971513787f, -0.07542630764f, 0.6905710663f, -0.7232645452f, + -0.290713703f, -0.9568100872f, 0.5912777791f, -0.8064679708f, -0.9454592212f, -0.325740481f, 0.6664455681f, 0.74555369f, 0.6236134912f, 0.7817328275f, 0.9126993851f, -0.4086316587f, -0.8191762011f, 0.5735419353f, -0.8812745759f, -0.4726046147f, + 0.9953313627f, 0.09651672651f, 0.9855650846f, -0.1692969699f, -0.8495980887f, 0.5274306472f, 0.6174853946f, -0.7865823463f, 0.8508156371f, 0.52546432f, 0.9985032451f, -0.05469249926f, 0.1971371563f, -0.9803759185f, 0.6607855748f, -0.7505747292f, + -0.03097494063f, 0.9995201614f, -0.6731660801f, 0.739491331f, -0.7195018362f, -0.6944905383f, 0.9727511689f, 0.2318515979f, 0.9997059088f, -0.0242506907f, 0.4421787429f, -0.8969269532f, 0.9981350961f, -0.061043673f, -0.9173660799f, -0.3980445648f, + -0.8150056635f, -0.5794529907f, -0.8789331304f, 0.4769450202f, 0.0158605829f, 0.999874213f, -0.8095464474f, 0.5870558317f, -0.9165898907f, -0.3998286786f, -0.8023542565f, 0.5968480938f, -0.5176737917f, 0.8555780767f, -0.8154407307f, -0.5788405779f, + 0.4022010347f, -0.9155513791f, -0.9052556868f, -0.4248672045f, 0.7317445619f, 0.6815789728f, -0.5647632201f, -0.8252529947f, -0.8403276335f, -0.5420788397f, -0.9314281527f, 0.363925262f, 0.5238198472f, 0.8518290719f, 0.7432803869f, -0.6689800195f, + -0.985371561f, -0.1704197369f, 0.4601468731f, 0.88784281f, 0.825855404f, 0.5638819483f, 0.6182366099f, 0.7859920446f, 0.8331502863f, -0.553046653f, 0.1500307506f, 0.9886813308f, -0.662330369f, -0.7492119075f, -0.668598664f, 0.743623444f, + 0.7025606278f, 0.7116238924f, -0.5419389763f, -0.8404178401f, -0.3388616456f, 0.9408362159f, 0.8331530315f, 0.5530425174f, -0.2989720662f, -0.9542618632f, 0.2638522993f, 0.9645630949f, 0.124108739f, -0.9922686234f, -0.7282649308f, -0.6852956957f, + 0.6962500149f, 0.7177993569f, -0.9183535368f, 0.3957610156f, -0.6326102274f, -0.7744703352f, -0.9331891859f, -0.359385508f, -0.1153779357f, -0.9933216659f, 0.9514974788f, -0.3076565421f, -0.08987977445f, -0.9959526224f, 0.6678496916f, 0.7442961705f, + 0.7952400393f, -0.6062947138f, -0.6462007402f, -0.7631674805f, -0.2733598753f, 0.9619118351f, 0.9669590226f, -0.254931851f, -0.9792894595f, 0.2024651934f, -0.5369502995f, -0.8436138784f, -0.270036471f, -0.9628500944f, -0.6400277131f, 0.7683518247f, + -0.7854537493f, -0.6189203566f, 0.06005905383f, -0.9981948257f, -0.02455770378f, 0.9996984141f, -0.65983623f, 0.751409442f, -0.6253894466f, -0.7803127835f, -0.6210408851f, -0.7837781695f, 0.8348888491f, 0.5504185768f, -0.1592275245f, 0.9872419133f, + 0.8367622488f, 0.5475663786f, -0.8675753916f, -0.4973056806f, -0.2022662628f, -0.9793305667f, 0.9399189937f, 0.3413975472f, 0.9877404807f, -0.1561049093f, -0.9034455656f, 0.4287028224f, 0.1269804218f, -0.9919052235f, -0.3819600854f, 0.924178821f, + 0.9754625894f, 0.2201652486f, -0.3204015856f, -0.9472818081f, -0.9874760884f, 0.1577687387f, 0.02535348474f, -0.9996785487f, 0.4835130794f, -0.8753371362f, -0.2850799925f, -0.9585037287f, -0.06805516006f, -0.99768156f, -0.7885244045f, -0.6150034663f, + 0.3185392127f, -0.9479096845f, 0.8880043089f, 0.4598351306f, 0.6476921488f, -0.7619021462f, 0.9820241299f, 0.1887554194f, 0.9357275128f, -0.3527237187f, -0.8894895414f, 0.4569555293f, 0.7922791302f, 0.6101588153f, 0.7483818261f, 0.6632681526f, + -0.7288929755f, -0.6846276581f, 0.8729032783f, -0.4878932944f, 0.8288345784f, 0.5594937369f, 0.08074567077f, 0.9967347374f, 0.9799148216f, -0.1994165048f, -0.580730673f, -0.8140957471f, -0.4700049791f, -0.8826637636f, 0.2409492979f, 0.9705377045f, + 0.9437816757f, -0.3305694308f, -0.8927998638f, -0.4504535528f, -0.8069622304f, 0.5906030467f, 0.06258973166f, 0.9980393407f, -0.9312597469f, 0.3643559849f, 0.5777449785f, 0.8162173362f, -0.3360095855f, -0.941858566f, 0.697932075f, -0.7161639607f, + -0.002008157227f, -0.9999979837f, -0.1827294312f, -0.9831632392f, -0.6523911722f, 0.7578824173f, -0.4302626911f, -0.9027037258f, -0.9985126289f, -0.05452091251f, -0.01028102172f, -0.9999471489f, -0.4946071129f, 0.8691166802f, -0.2999350194f, 0.9539596344f, + 0.8165471961f, 0.5772786819f, 0.2697460475f, 0.962931498f, -0.7306287391f, -0.6827749597f, -0.7590952064f, -0.6509796216f, -0.907053853f, 0.4210146171f, -0.5104861064f, -0.8598860013f, 0.8613350597f, 0.5080373165f, 0.5007881595f, -0.8655698812f, + -0.654158152f, 0.7563577938f, -0.8382755311f, -0.545246856f, 0.6940070834f, 0.7199681717f, 0.06950936031f, 0.9975812994f, 0.1702942185f, -0.9853932612f, 0.2695973274f, 0.9629731466f, 0.5519612192f, -0.8338697815f, 0.225657487f, -0.9742067022f, + 0.4215262855f, -0.9068161835f, 0.4881873305f, -0.8727388672f, -0.3683854996f, -0.9296731273f, -0.9825390578f, 0.1860564427f, 0.81256471f, 0.5828709909f, 0.3196460933f, -0.9475370046f, 0.9570913859f, 0.2897862643f, -0.6876655497f, -0.7260276109f, + -0.9988770922f, -0.047376731f, -0.1250179027f, 0.992154486f, -0.8280133617f, 0.560708367f, 0.9324863769f, -0.3612051451f, 0.6394653183f, 0.7688199442f, -0.01623847064f, -0.9998681473f, -0.9955014666f, -0.09474613458f, -0.81453315f, 0.580117012f, + 0.4037327978f, -0.9148769469f, 0.9944263371f, 0.1054336766f, -0.1624711654f, 0.9867132919f, -0.9949487814f, -0.100383875f, -0.6995302564f, 0.7146029809f, 0.5263414922f, -0.85027327f, -0.5395221479f, 0.841971408f, 0.6579370318f, 0.7530729462f, + 0.01426758847f, -0.9998982128f, -0.6734383991f, 0.7392433447f, 0.639412098f, -0.7688642071f, 0.9211571421f, 0.3891908523f, -0.146637214f, -0.9891903394f, -0.782318098f, 0.6228791163f, -0.5039610839f, -0.8637263605f, -0.7743120191f, -0.6328039957f, +}; + +template +const T FastNoiseLite::Lookup::Gradients3D[] = +{ + 0, 1, 1, 0, 0,-1, 1, 0, 0, 1,-1, 0, 0,-1,-1, 0, + 1, 0, 1, 0, -1, 0, 1, 0, 1, 0,-1, 0, -1, 0,-1, 0, + 1, 1, 0, 0, -1, 1, 0, 0, 1,-1, 0, 0, -1,-1, 0, 0, + 0, 1, 1, 0, 0,-1, 1, 0, 0, 1,-1, 0, 0,-1,-1, 0, + 1, 0, 1, 0, -1, 0, 1, 0, 1, 0,-1, 0, -1, 0,-1, 0, + 1, 1, 0, 0, -1, 1, 0, 0, 1,-1, 0, 0, -1,-1, 0, 0, + 0, 1, 1, 0, 0,-1, 1, 0, 0, 1,-1, 0, 0,-1,-1, 0, + 1, 0, 1, 0, -1, 0, 1, 0, 1, 0,-1, 0, -1, 0,-1, 0, + 1, 1, 0, 0, -1, 1, 0, 0, 1,-1, 0, 0, -1,-1, 0, 0, + 0, 1, 1, 0, 0,-1, 1, 0, 0, 1,-1, 0, 0,-1,-1, 0, + 1, 0, 1, 0, -1, 0, 1, 0, 1, 0,-1, 0, -1, 0,-1, 0, + 1, 1, 0, 0, -1, 1, 0, 0, 1,-1, 0, 0, -1,-1, 0, 0, + 0, 1, 1, 0, 0,-1, 1, 0, 0, 1,-1, 0, 0,-1,-1, 0, + 1, 0, 1, 0, -1, 0, 1, 0, 1, 0,-1, 0, -1, 0,-1, 0, + 1, 1, 0, 0, -1, 1, 0, 0, 1,-1, 0, 0, -1,-1, 0, 0, + 1, 1, 0, 0, 0,-1, 1, 0, -1, 1, 0, 0, 0,-1,-1, 0 +}; + +template +const T FastNoiseLite::Lookup::RandVecs3D[] = +{ + -0.7292736885f, -0.6618439697f, 0.1735581948f, 0, 0.790292081f, -0.5480887466f, -0.2739291014f, 0, 0.7217578935f, 0.6226212466f, -0.3023380997f, 0, 0.565683137f, -0.8208298145f, -0.0790000257f, 0, 0.760049034f, -0.5555979497f, -0.3370999617f, 0, 0.3713945616f, 0.5011264475f, 0.7816254623f, 0, -0.1277062463f, -0.4254438999f, -0.8959289049f, 0, -0.2881560924f, -0.5815838982f, 0.7607405838f, 0, + 0.5849561111f, -0.662820239f, -0.4674352136f, 0, 0.3307171178f, 0.0391653737f, 0.94291689f, 0, 0.8712121778f, -0.4113374369f, -0.2679381538f, 0, 0.580981015f, 0.7021915846f, 0.4115677815f, 0, 0.503756873f, 0.6330056931f, -0.5878203852f, 0, 0.4493712205f, 0.601390195f, 0.6606022552f, 0, -0.6878403724f, 0.09018890807f, -0.7202371714f, 0, -0.5958956522f, -0.6469350577f, 0.475797649f, 0, + -0.5127052122f, 0.1946921978f, -0.8361987284f, 0, -0.9911507142f, -0.05410276466f, -0.1212153153f, 0, -0.2149721042f, 0.9720882117f, -0.09397607749f, 0, -0.7518650936f, -0.5428057603f, 0.3742469607f, 0, 0.5237068895f, 0.8516377189f, -0.02107817834f, 0, 0.6333504779f, 0.1926167129f, -0.7495104896f, 0, -0.06788241606f, 0.3998305789f, 0.9140719259f, 0, -0.5538628599f, -0.4729896695f, -0.6852128902f, 0, + -0.7261455366f, -0.5911990757f, 0.3509933228f, 0, -0.9229274737f, -0.1782808786f, 0.3412049336f, 0, -0.6968815002f, 0.6511274338f, 0.3006480328f, 0, 0.9608044783f, -0.2098363234f, -0.1811724921f, 0, 0.06817146062f, -0.9743405129f, 0.2145069156f, 0, -0.3577285196f, -0.6697087264f, -0.6507845481f, 0, -0.1868621131f, 0.7648617052f, -0.6164974636f, 0, -0.6541697588f, 0.3967914832f, 0.6439087246f, 0, + 0.6993340405f, -0.6164538506f, 0.3618239211f, 0, -0.1546665739f, 0.6291283928f, 0.7617583057f, 0, -0.6841612949f, -0.2580482182f, -0.6821542638f, 0, 0.5383980957f, 0.4258654885f, 0.7271630328f, 0, -0.5026987823f, -0.7939832935f, -0.3418836993f, 0, 0.3202971715f, 0.2834415347f, 0.9039195862f, 0, 0.8683227101f, -0.0003762656404f, -0.4959995258f, 0, 0.791120031f, -0.08511045745f, 0.6057105799f, 0, + -0.04011016052f, -0.4397248749f, 0.8972364289f, 0, 0.9145119872f, 0.3579346169f, -0.1885487608f, 0, -0.9612039066f, -0.2756484276f, 0.01024666929f, 0, 0.6510361721f, -0.2877799159f, -0.7023778346f, 0, -0.2041786351f, 0.7365237271f, 0.644859585f, 0, -0.7718263711f, 0.3790626912f, 0.5104855816f, 0, -0.3060082741f, -0.7692987727f, 0.5608371729f, 0, 0.454007341f, -0.5024843065f, 0.7357899537f, 0, + 0.4816795475f, 0.6021208291f, -0.6367380315f, 0, 0.6961980369f, -0.3222197429f, 0.641469197f, 0, -0.6532160499f, -0.6781148932f, 0.3368515753f, 0, 0.5089301236f, -0.6154662304f, -0.6018234363f, 0, -0.1635919754f, -0.9133604627f, -0.372840892f, 0, 0.52408019f, -0.8437664109f, 0.1157505864f, 0, 0.5902587356f, 0.4983817807f, -0.6349883666f, 0, 0.5863227872f, 0.494764745f, 0.6414307729f, 0, + 0.6779335087f, 0.2341345225f, 0.6968408593f, 0, 0.7177054546f, -0.6858979348f, 0.120178631f, 0, -0.5328819713f, -0.5205125012f, 0.6671608058f, 0, -0.8654874251f, -0.0700727088f, -0.4960053754f, 0, -0.2861810166f, 0.7952089234f, 0.5345495242f, 0, -0.04849529634f, 0.9810836427f, -0.1874115585f, 0, -0.6358521667f, 0.6058348682f, 0.4781800233f, 0, 0.6254794696f, -0.2861619734f, 0.7258696564f, 0, + -0.2585259868f, 0.5061949264f, -0.8227581726f, 0, 0.02136306781f, 0.5064016808f, -0.8620330371f, 0, 0.200111773f, 0.8599263484f, 0.4695550591f, 0, 0.4743561372f, 0.6014985084f, -0.6427953014f, 0, 0.6622993731f, -0.5202474575f, -0.5391679918f, 0, 0.08084972818f, -0.6532720452f, 0.7527940996f, 0, -0.6893687501f, 0.0592860349f, 0.7219805347f, 0, -0.1121887082f, -0.9673185067f, 0.2273952515f, 0, + 0.7344116094f, 0.5979668656f, -0.3210532909f, 0, 0.5789393465f, -0.2488849713f, 0.7764570201f, 0, 0.6988182827f, 0.3557169806f, -0.6205791146f, 0, -0.8636845529f, -0.2748771249f, -0.4224826141f, 0, -0.4247027957f, -0.4640880967f, 0.777335046f, 0, 0.5257722489f, -0.8427017621f, 0.1158329937f, 0, 0.9343830603f, 0.316302472f, -0.1639543925f, 0, -0.1016836419f, -0.8057303073f, -0.5834887393f, 0, + -0.6529238969f, 0.50602126f, -0.5635892736f, 0, -0.2465286165f, -0.9668205684f, -0.06694497494f, 0, -0.9776897119f, -0.2099250524f, -0.007368825344f, 0, 0.7736893337f, 0.5734244712f, 0.2694238123f, 0, -0.6095087895f, 0.4995678998f, 0.6155736747f, 0, 0.5794535482f, 0.7434546771f, 0.3339292269f, 0, -0.8226211154f, 0.08142581855f, 0.5627293636f, 0, -0.510385483f, 0.4703667658f, 0.7199039967f, 0, + -0.5764971849f, -0.07231656274f, -0.8138926898f, 0, 0.7250628871f, 0.3949971505f, -0.5641463116f, 0, -0.1525424005f, 0.4860840828f, -0.8604958341f, 0, -0.5550976208f, -0.4957820792f, 0.667882296f, 0, -0.1883614327f, 0.9145869398f, 0.357841725f, 0, 0.7625556724f, -0.5414408243f, -0.3540489801f, 0, -0.5870231946f, -0.3226498013f, -0.7424963803f, 0, 0.3051124198f, 0.2262544068f, -0.9250488391f, 0, + 0.6379576059f, 0.577242424f, -0.5097070502f, 0, -0.5966775796f, 0.1454852398f, -0.7891830656f, 0, -0.658330573f, 0.6555487542f, -0.3699414651f, 0, 0.7434892426f, 0.2351084581f, 0.6260573129f, 0, 0.5562114096f, 0.8264360377f, -0.0873632843f, 0, -0.3028940016f, -0.8251527185f, 0.4768419182f, 0, 0.1129343818f, -0.985888439f, -0.1235710781f, 0, 0.5937652891f, -0.5896813806f, 0.5474656618f, 0, + 0.6757964092f, -0.5835758614f, -0.4502648413f, 0, 0.7242302609f, -0.1152719764f, 0.6798550586f, 0, -0.9511914166f, 0.0753623979f, -0.2992580792f, 0, 0.2539470961f, -0.1886339355f, 0.9486454084f, 0, 0.571433621f, -0.1679450851f, -0.8032795685f, 0, -0.06778234979f, 0.3978269256f, 0.9149531629f, 0, 0.6074972649f, 0.733060024f, -0.3058922593f, 0, -0.5435478392f, 0.1675822484f, 0.8224791405f, 0, + -0.5876678086f, -0.3380045064f, -0.7351186982f, 0, -0.7967562402f, 0.04097822706f, -0.6029098428f, 0, -0.1996350917f, 0.8706294745f, 0.4496111079f, 0, -0.02787660336f, -0.9106232682f, -0.4122962022f, 0, -0.7797625996f, -0.6257634692f, 0.01975775581f, 0, -0.5211232846f, 0.7401644346f, -0.4249554471f, 0, 0.8575424857f, 0.4053272873f, -0.3167501783f, 0, 0.1045223322f, 0.8390195772f, -0.5339674439f, 0, + 0.3501822831f, 0.9242524096f, -0.1520850155f, 0, 0.1987849858f, 0.07647613266f, 0.9770547224f, 0, 0.7845996363f, 0.6066256811f, -0.1280964233f, 0, 0.09006737436f, -0.9750989929f, -0.2026569073f, 0, -0.8274343547f, -0.542299559f, 0.1458203587f, 0, -0.3485797732f, -0.415802277f, 0.840000362f, 0, -0.2471778936f, -0.7304819962f, -0.6366310879f, 0, -0.3700154943f, 0.8577948156f, 0.3567584454f, 0, + 0.5913394901f, -0.548311967f, -0.5913303597f, 0, 0.1204873514f, -0.7626472379f, -0.6354935001f, 0, 0.616959265f, 0.03079647928f, 0.7863922953f, 0, 0.1258156836f, -0.6640829889f, -0.7369967419f, 0, -0.6477565124f, -0.1740147258f, -0.7417077429f, 0, 0.6217889313f, -0.7804430448f, -0.06547655076f, 0, 0.6589943422f, -0.6096987708f, 0.4404473475f, 0, -0.2689837504f, -0.6732403169f, -0.6887635427f, 0, + -0.3849775103f, 0.5676542638f, 0.7277093879f, 0, 0.5754444408f, 0.8110471154f, -0.1051963504f, 0, 0.9141593684f, 0.3832947817f, 0.131900567f, 0, -0.107925319f, 0.9245493968f, 0.3654593525f, 0, 0.377977089f, 0.3043148782f, 0.8743716458f, 0, -0.2142885215f, -0.8259286236f, 0.5214617324f, 0, 0.5802544474f, 0.4148098596f, -0.7008834116f, 0, -0.1982660881f, 0.8567161266f, -0.4761596756f, 0, + -0.03381553704f, 0.3773180787f, -0.9254661404f, 0, -0.6867922841f, -0.6656597827f, 0.2919133642f, 0, 0.7731742607f, -0.2875793547f, -0.5652430251f, 0, -0.09655941928f, 0.9193708367f, -0.3813575004f, 0, 0.2715702457f, -0.9577909544f, -0.09426605581f, 0, 0.2451015704f, -0.6917998565f, -0.6792188003f, 0, 0.977700782f, -0.1753855374f, 0.1155036542f, 0, -0.5224739938f, 0.8521606816f, 0.02903615945f, 0, + -0.7734880599f, -0.5261292347f, 0.3534179531f, 0, -0.7134492443f, -0.269547243f, 0.6467878011f, 0, 0.1644037271f, 0.5105846203f, -0.8439637196f, 0, 0.6494635788f, 0.05585611296f, 0.7583384168f, 0, -0.4711970882f, 0.5017280509f, -0.7254255765f, 0, -0.6335764307f, -0.2381686273f, -0.7361091029f, 0, -0.9021533097f, -0.270947803f, -0.3357181763f, 0, -0.3793711033f, 0.872258117f, 0.3086152025f, 0, + -0.6855598966f, -0.3250143309f, 0.6514394162f, 0, 0.2900942212f, -0.7799057743f, -0.5546100667f, 0, -0.2098319339f, 0.85037073f, 0.4825351604f, 0, -0.4592603758f, 0.6598504336f, -0.5947077538f, 0, 0.8715945488f, 0.09616365406f, -0.4807031248f, 0, -0.6776666319f, 0.7118504878f, -0.1844907016f, 0, 0.7044377633f, 0.312427597f, 0.637304036f, 0, -0.7052318886f, -0.2401093292f, -0.6670798253f, 0, + 0.081921007f, -0.7207336136f, -0.6883545647f, 0, -0.6993680906f, -0.5875763221f, -0.4069869034f, 0, -0.1281454481f, 0.6419895885f, 0.7559286424f, 0, -0.6337388239f, -0.6785471501f, -0.3714146849f, 0, 0.5565051903f, -0.2168887573f, -0.8020356851f, 0, -0.5791554484f, 0.7244372011f, -0.3738578718f, 0, 0.1175779076f, -0.7096451073f, 0.6946792478f, 0, -0.6134619607f, 0.1323631078f, 0.7785527795f, 0, + 0.6984635305f, -0.02980516237f, -0.715024719f, 0, 0.8318082963f, -0.3930171956f, 0.3919597455f, 0, 0.1469576422f, 0.05541651717f, -0.9875892167f, 0, 0.708868575f, -0.2690503865f, 0.6520101478f, 0, 0.2726053183f, 0.67369766f, -0.68688995f, 0, -0.6591295371f, 0.3035458599f, -0.6880466294f, 0, 0.4815131379f, -0.7528270071f, 0.4487723203f, 0, 0.9430009463f, 0.1675647412f, -0.2875261255f, 0, + 0.434802957f, 0.7695304522f, -0.4677277752f, 0, 0.3931996188f, 0.594473625f, 0.7014236729f, 0, 0.7254336655f, -0.603925654f, 0.3301814672f, 0, 0.7590235227f, -0.6506083235f, 0.02433313207f, 0, -0.8552768592f, -0.3430042733f, 0.3883935666f, 0, -0.6139746835f, 0.6981725247f, 0.3682257648f, 0, -0.7465905486f, -0.5752009504f, 0.3342849376f, 0, 0.5730065677f, 0.810555537f, -0.1210916791f, 0, + -0.9225877367f, -0.3475211012f, -0.167514036f, 0, -0.7105816789f, -0.4719692027f, -0.5218416899f, 0, -0.08564609717f, 0.3583001386f, 0.929669703f, 0, -0.8279697606f, -0.2043157126f, 0.5222271202f, 0, 0.427944023f, 0.278165994f, 0.8599346446f, 0, 0.5399079671f, -0.7857120652f, -0.3019204161f, 0, 0.5678404253f, -0.5495413974f, -0.6128307303f, 0, -0.9896071041f, 0.1365639107f, -0.04503418428f, 0, + -0.6154342638f, -0.6440875597f, 0.4543037336f, 0, 0.1074204368f, -0.7946340692f, 0.5975094525f, 0, -0.3595449969f, -0.8885529948f, 0.28495784f, 0, -0.2180405296f, 0.1529888965f, 0.9638738118f, 0, -0.7277432317f, -0.6164050508f, -0.3007234646f, 0, 0.7249729114f, -0.00669719484f, 0.6887448187f, 0, -0.5553659455f, -0.5336586252f, 0.6377908264f, 0, 0.5137558015f, 0.7976208196f, -0.3160000073f, 0, + -0.3794024848f, 0.9245608561f, -0.03522751494f, 0, 0.8229248658f, 0.2745365933f, -0.4974176556f, 0, -0.5404114394f, 0.6091141441f, 0.5804613989f, 0, 0.8036581901f, -0.2703029469f, 0.5301601931f, 0, 0.6044318879f, 0.6832968393f, 0.4095943388f, 0, 0.06389988817f, 0.9658208605f, -0.2512108074f, 0, 0.1087113286f, 0.7402471173f, -0.6634877936f, 0, -0.713427712f, -0.6926784018f, 0.1059128479f, 0, + 0.6458897819f, -0.5724548511f, -0.5050958653f, 0, -0.6553931414f, 0.7381471625f, 0.159995615f, 0, 0.3910961323f, 0.9188871375f, -0.05186755998f, 0, -0.4879022471f, -0.5904376907f, 0.6429111375f, 0, 0.6014790094f, 0.7707441366f, -0.2101820095f, 0, -0.5677173047f, 0.7511360995f, 0.3368851762f, 0, 0.7858573506f, 0.226674665f, 0.5753666838f, 0, -0.4520345543f, -0.604222686f, -0.6561857263f, 0, + 0.002272116345f, 0.4132844051f, -0.9105991643f, 0, -0.5815751419f, -0.5162925989f, 0.6286591339f, 0, -0.03703704785f, 0.8273785755f, 0.5604221175f, 0, -0.5119692504f, 0.7953543429f, -0.3244980058f, 0, -0.2682417366f, -0.9572290247f, -0.1084387619f, 0, -0.2322482736f, -0.9679131102f, -0.09594243324f, 0, 0.3554328906f, -0.8881505545f, 0.2913006227f, 0, 0.7346520519f, -0.4371373164f, 0.5188422971f, 0, + 0.9985120116f, 0.04659011161f, -0.02833944577f, 0, -0.3727687496f, -0.9082481361f, 0.1900757285f, 0, 0.91737377f, -0.3483642108f, 0.1925298489f, 0, 0.2714911074f, 0.4147529736f, -0.8684886582f, 0, 0.5131763485f, -0.7116334161f, 0.4798207128f, 0, -0.8737353606f, 0.18886992f, -0.4482350644f, 0, 0.8460043821f, -0.3725217914f, 0.3814499973f, 0, 0.8978727456f, -0.1780209141f, -0.4026575304f, 0, + 0.2178065647f, -0.9698322841f, -0.1094789531f, 0, -0.1518031304f, -0.7788918132f, -0.6085091231f, 0, -0.2600384876f, -0.4755398075f, -0.8403819825f, 0, 0.572313509f, -0.7474340931f, -0.3373418503f, 0, -0.7174141009f, 0.1699017182f, -0.6756111411f, 0, -0.684180784f, 0.02145707593f, -0.7289967412f, 0, -0.2007447902f, 0.06555605789f, -0.9774476623f, 0, -0.1148803697f, -0.8044887315f, 0.5827524187f, 0, + -0.7870349638f, 0.03447489231f, 0.6159443543f, 0, -0.2015596421f, 0.6859872284f, 0.6991389226f, 0, -0.08581082512f, -0.10920836f, -0.9903080513f, 0, 0.5532693395f, 0.7325250401f, -0.396610771f, 0, -0.1842489331f, -0.9777375055f, -0.1004076743f, 0, 0.0775473789f, -0.9111505856f, 0.4047110257f, 0, 0.1399838409f, 0.7601631212f, -0.6344734459f, 0, 0.4484419361f, -0.845289248f, 0.2904925424f, 0 +}; + +#endif diff --git a/src/FastNoiseLite/LICENSE b/src/FastNoiseLite/LICENSE new file mode 100644 index 0000000..dd6df2c --- /dev/null +++ b/src/FastNoiseLite/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright(c) 2020 Jordan Peck (jordan.me2@gmail.com) +Copyright(c) 2020 Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/src/FastNoiseLite/README.md b/src/FastNoiseLite/README.md new file mode 100644 index 0000000..e8514d9 --- /dev/null +++ b/src/FastNoiseLite/README.md @@ -0,0 +1,23 @@ +## Getting Started + +Here's an example for creating a 128x128 array of OpenSimplex2 noise + +```cpp +// Create and configure FastNoise object +FastNoiseLite noise; +noise.SetNoiseType(FastNoiseLite::NoiseType_OpenSimplex2); + +// Gather noise data +std::vector noiseData(128 * 128); +int index = 0; + +for (int y = 0; y < 128; y++) +{ + for (int x = 0; x < 128; x++) + { + noiseData[index++] = noise.GetNoise((float)x, (float)y); + } +} + +// Do something with this data... +``` diff --git a/src/editor/main.cpp b/src/editor/main.cpp index df2cb24..c9742c3 100644 --- a/src/editor/main.cpp +++ b/src/editor/main.cpp @@ -16,7 +16,7 @@ #include "Components.h" #include "CharacterModule.h" #include "TerrainModule.h" -#include "GUIModule.h" +#include "GUIModuleCommon.h" #include "AppModule.h" #include "EditorGizmoModule.h" #include "EditorInputModule.h" diff --git a/src/gamedata/CMakeLists.txt b/src/gamedata/CMakeLists.txt index 183da0c..ecf659c 100644 --- a/src/gamedata/CMakeLists.txt +++ b/src/gamedata/CMakeLists.txt @@ -6,9 +6,10 @@ find_package(nlohmann_json REQUIRED) find_package(OgreProcedural REQUIRED CONFIG) add_subdirectory(items) add_library(GameData STATIC GameData.cpp CharacterModule.cpp WaterModule.cpp SunModule.cpp TerrainModule.cpp - GUIModule.cpp LuaData.cpp WorldMapModule.cpp BoatModule.cpp EventTriggerModule.cpp + GUIModule.cpp EditorGUIModule.cpp LuaData.cpp WorldMapModule.cpp BoatModule.cpp EventTriggerModule.cpp CharacterAnimationModule.cpp PhysicsModule.cpp EventModule.cpp CharacterManagerModule.cpp - VehicleManagerModule.cpp AppModule.cpp StaticGeometryModule.cpp SmartObject.cpp SlotsModule.cpp goap.cpp) + VehicleManagerModule.cpp AppModule.cpp StaticGeometryModule.cpp SmartObject.cpp SlotsModule.cpp + PlayerActionModule.cpp goap.cpp) target_link_libraries(GameData PUBLIC lua flecs::flecs_static diff --git a/src/gamedata/EditorGUIModule.cpp b/src/gamedata/EditorGUIModule.cpp new file mode 100644 index 0000000..d67a3b5 --- /dev/null +++ b/src/gamedata/EditorGUIModule.cpp @@ -0,0 +1,1421 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "GameData.h" +#include "Components.h" +#include "LuaData.h" +#include "AppModule.h" +#include "TerrainModule.h" +#include "StaticGeometryModule.h" +#include "EditorGizmoModule.h" +#include "PhysicsModule.h" +#include "items.h" +#include "EditorGUIModule.h" +#include "GUIModuleCommon.h" + +namespace ECS +{ + +struct EditorGUIListener; +struct EditorGUIData { + Ogre::ImGuiOverlay *mGuiOverlay; + std::vector glb_names; + EditorGUIListener *mGUIListener; +}; +struct EditorGUIListener : public Ogre::RenderTargetListener { + float panel_width; + bool enableEditor; + bool enableMapEditor; + ImFont *smallFont, *midFont, *bigFont; + Ogre::FontPtr _smallFont, _midFont, _bigFont; + Ogre::TexturePtr worldMap; + Ogre::Image worldMapImage; + + EditorGUIListener(Ogre::ImGuiOverlay *overlay) + : Ogre::RenderTargetListener() + , enableEditor(false) + , enableMapEditor(false) + , keepCameraAbove(true) + , command(COMMAND_NONE) + { + _midFont = createFont("midFont", "General", + "Jupiteroid-Regular.ttf", 18.0f); + _smallFont = createFont("smallFont", "General", + "Jupiteroid-Regular.ttf", 13.0f); + _bigFont = createFont("bigFont", "General", "Kenney Bold.ttf", + 32.0f); + smallFont = overlay->addFont("smallFont", "General"); + OgreAssert(smallFont, "Could not load font"); + midFont = overlay->addFont("midFont", "General"); + OgreAssert(midFont, "Could not load font"); + bigFont = overlay->addFont("bigFont", "General"); + OgreAssert(bigFont, "Could not load font"); + worldMapImage.load( + "world_map.png", + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); +#if 0 + int x, y, i, j; + for (y = 0; y < worldMapImage.getWidth(); y++) + for (x = 0; x < worldMapImage.getWidth(); x++) { + float r = 0.0f; + for (i = -2; i < 3; i++) + for (j = -2; j < 3; j++) { + int xj = Ogre::Math::Clamp( + x + j, 0, + (int)worldMapImage + .getWidth() - + 1); + int yi = Ogre::Math::Clamp( + y + i, 0, + (int)worldMapImage + .getHeight() - + 1); + r += worldMapImage + .getColourAt(xj, + yi, 0) + .r; + } + r /= 25.0f; + Ogre::ColourValue cv = + worldMapImage.getColourAt(x, y, 0); + cv.r = r; + worldMapImage.setColourAt(cv, x, y, 0); + } +#endif + worldMap = Ogre::TextureManager::getSingleton().createManual( + "worldMap", + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + Ogre::TEX_TYPE_2D, worldMapImage.getWidth(), + worldMapImage.getHeight(), + worldMapImage.getNumMipmaps(), + worldMapImage.getFormat(), Ogre::TU_DYNAMIC_WRITE_ONLY); + worldMap->loadImage(worldMapImage); + OgreAssert(worldMap, "could not load world map"); + } + Ogre::FontPtr createFont(const Ogre::String &name, + const Ogre::String &group, + const Ogre::String &ttfname, float fontSize) + { + Ogre::FontPtr ret = + Ogre::FontManager::getSingleton().create(name, group); + ret->setType(Ogre::FontType::FT_TRUETYPE); + ret->setSource(ttfname); + ret->setTrueTypeSize(fontSize); + ret->setTrueTypeResolution(75); + ret->addCodePointRange(Ogre::Font::CodePointRange(30, 128)); + ret->load(); + return ret; + } + void + preViewportUpdate(const Ogre::RenderTargetViewportEvent &evt) override + { + preview(evt); + } + void + postViewportUpdate(const Ogre::RenderTargetViewportEvent &evt) override + { +#if 0 + if (switchWindow) { + ECS::get().set({ 1 }); + switchWindow = false; + } +#endif + } +#if 0 + bool switchWindow = false; +#endif + void buttons_panel() + { + bool enableDebugRender = ECS::get().enableDbgDraw; + + ImVec2 size = ImGui::GetMainViewport()->Size; + float window_width = size.x * 0.2f; + if (window_width > panel_width) + window_width = panel_width; + float window_height = size.y * 0.5f - 20; + ImGui::SetNextWindowPos(ImVec2(0, size.y * 0.5f + 20), + ImGuiCond_Always); + ImGui::SetNextWindowSize(ImVec2(window_width, window_height), + ImGuiCond_Always); + ImGui::Begin("Control"); + // if (ECS::get().get().enabled) + // ECS::get().get().app->setWindowGrab(true); + if (ImGui::Button("Quit")) + Ogre::Root::getSingleton().queueEndRendering(); + if (ImGui::Button("Return")) + ECS::get().get().finish(); + if (ImGui::Checkbox("Enable physics debug", + &enableDebugRender)) { + ECS::get_mut().enableDbgDraw = + enableDebugRender; + ECS::modified(); + PhysicsModule::setDebugDraw(enableDebugRender); + } +#if 0 + if (ImGui::Button("AltCam")) { + switchWindow = true; + } +#endif + if (ImGui::Button("Deploy config")) { + std::function m = [&](const Ogre::String &src, + const Ogre::String &dst) { + std::ifstream source(src, std::ios::binary); + std::ofstream destination(dst, + std::ios::binary); + + OgreAssert(source.is_open(), + "Failed to copy files " + src + + " -> " + dst); + OgreAssert(destination.is_open(), + "Failed to copy files " + src + + " -> " + dst); + OgreAssert(source.is_open() && + destination.is_open(), + "Failed to copy files " + src + + " -> " + dst); + destination << source.rdbuf(); + destination.close(); + source.close(); + }; + m("resources/buildings/items.list", + "../../resources/buildings/items.list"); + m("resources/terrain/world_map.png", + "../../resources/terrain/world_map.png"); + } + ImGui::Checkbox("Keep camera above 0", &keepCameraAbove); +#if 0 + if (ImGui::Button("Enable/Disable item editor")) { + enableEditor ^= true; + if (enableEditor) + enableMapEditor = false; + } + if (ImGui::Button("Enable/Disable map editor")) { + enableMapEditor ^= true; + if (enableMapEditor) + enableEditor = false; + } + ImGui::Text("Text message..."); +#endif + ImGui::Checkbox("Enable Map", &enableMapEditor); + ImGui::End(); + } + void create_entity_node(const Ogre::String &name, int key) + { + Ogre::Entity *ent = + ECS::get().get().mScnMgr->createEntity( + name); + Ogre::SceneNode *pnode = + ECS::get() + .get() + .mScnMgr->getRootSceneNode() + ->createChildSceneNode( + "ent:" + name + + Ogre::StringConverter::toString( + key), + ECS::get() + .get() + .mCameraPivot->getPosition(), + ECS::get() + .get() + .mCameraPivot->getOrientation()); + pnode->attachObject(ent); + Ogre::Quaternion q = pnode->getOrientation(); + Ogre::Radian yaw = q.getYaw(); + Ogre::Quaternion nq(yaw, Ogre::Vector3(0, 1, 0)); + pnode->setOrientation(nq); + + ECS::get().get().finish(); + } +#if 0 + void buildings_editor() + { + int i; + ImVec2 size = ImGui::GetMainViewport()->Size; + float window_width = size.x * 0.2f; + if (window_width > panel_width) + window_width = panel_width; + float window_height = size.y * 0.5 - 20; + ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always); + ImGui::SetNextWindowSize(ImVec2(window_width, window_height), + ImGuiCond_Always); + ImGui::Begin("Droppings..."); + for (i = 0; + i < ECS::get().get().glb_names.size(); + i++) { + Ogre::String id_button = + "Create entity: " + + ECS::get().get().glb_names[i] + + "##ent:" + + ECS::get().get().glb_names[i]; + if (ImGui::Button(id_button.c_str())) { + create_entity_node(ECS::get() + .get() + .glb_names[i], + i); + } + } + ImGui::End(); + } +#endif + void position_editor(Ogre::SceneNode *node) + { + Ogre::Vector3 position = node->getPosition(); + float v[3] = { position.x, position.y, position.z }; + ImGui::InputFloat3("position", v); + position.x = v[0]; + position.y = v[1]; + position.z = v[2]; + node->setPosition(position); + } + void orientation_editor(Ogre::SceneNode *node) + { + Ogre::Quaternion q = node->getOrientation(); + float yaw = Ogre::Radian(q.getYaw()).valueDegrees(); + float pitch = Ogre::Radian(q.getPitch()).valueDegrees(); + float roll = Ogre::Radian(q.getRoll()).valueDegrees(); + bool m1 = ImGui::InputFloat("yaw", &yaw); + bool m2 = ImGui::InputFloat("pitch", &pitch); + bool m3 = ImGui::InputFloat("roll", &roll); + if (m1 || m2 || m3) { + Ogre::Quaternion q1(Ogre::Radian(Ogre::Degree(yaw)), + Ogre::Vector3::UNIT_Y); + Ogre::Quaternion q2(Ogre::Degree(pitch), + Ogre::Vector3::UNIT_X); + Ogre::Quaternion q3(Ogre::Degree(roll), + Ogre::Vector3::UNIT_Z); + node->setOrientation(q1 * q2 * q3); + } + } + void attachments_editor(Ogre::SceneNode *node) + { + const Ogre::SceneNode::ObjectMap &pmap = + node->getAttachedObjects(); + int i; + for (i = 0; i < pmap.size(); i++) { + const Ogre::MovableObject *mobj = pmap[i]; + const Ogre::String &pname = mobj->getName(); + ImGui::Text("Name: %s", pname.c_str()); + } + } +#if 0 + void map_editor() + { + } +#endif + int pos_x = 0; + int pos_y = 0; + int top_x = 0; + int top_y = 0; + int selected_x = 0; + int selected_y = 0; + bool locationSelected = false; + float strength = 0.0f; + int size = 0; + long slot_x, slot_y; + float cursorAngle = 0; + void updateWorldTexture() + { + // Get the hardware pixel buffer + Ogre::HardwarePixelBufferSharedPtr pixelBuffer = + worldMap->getBuffer(); + + // Lock the buffer for writing, discarding previous contents for performance + pixelBuffer->lock(Ogre::HardwareBuffer::HBL_DISCARD); + + // Get information about the locked region (PixelBox) + const Ogre::PixelBox &pixelBox = pixelBuffer->getCurrentLock(); + + // Ensure the image format matches the texture format + OgreAssert(pixelBox.format == worldMapImage.getFormat(), + "bad format"); + + // Copy the image data to the pixel box's data pointer + memcpy(pixelBox.data, worldMapImage.getData(), + worldMapImage.getSize()); + + // Unlock the buffer to apply changes to the GPU + pixelBuffer->unlock(); + } + void updateHeightmap() + { + Ogre::Vector3 worldPos = + ECS::get().mCameraPivot->_getDerivedPosition(); + TerrainModule::update_heightmap(worldMapImage); + long x, y; + ECS::get() + .mTerrainGroup->convertWorldPositionToTerrainSlot( + worldPos, &x, &y); + for (auto &slot : + ECS::get().mTerrainGroup->getTerrainSlots()) { + Ogre::uint32 page = + ECS::get().mTerrainGroup->packIndex( + slot.second->x, slot.second->y); + Ogre::Terrain *terrain = + ECS::get().mTerrainGroup->getTerrain( + x, y); + if (terrain) + terrain->waitForDerivedProcesses(); + ECS::get() + .mTerrainPagedWorldSection->unloadPage(page, + false); + } + ECS::get().mTerrainGroup->update(false); + } + void setCursorPos(Ogre::Vector3 &cursorPosition, + Ogre::Quaternion &orientation) + { + Ogre::Vector3 worldPos = + ECS::get().sceneNode->_getDerivedPosition(); + worldPos.x = TerrainModule::get_world_x(selected_x); + worldPos.z = TerrainModule::get_world_y(selected_y); + worldPos.y = TerrainModule::get_height( + ECS::get().mTerrainGroup, worldPos); + ECS::get().sceneNode->_setDerivedPosition( + worldPos); + cursorPosition = worldPos; + orientation = ECS::get() + .sceneNode->_getDerivedOrientation(); + } + bool keepCameraAbove = true; + void setCameraPos() + { + Ogre::Vector3 cursorPos; + Ogre::Quaternion cursorOrientation; + setCursorPos(cursorPos, cursorOrientation); + Ogre::Vector3 cameraPos = + ECS::get().mCameraPivot->_getDerivedPosition(); + Ogre::Vector3 cameraOffset = + cursorOrientation * Ogre::Vector3::UNIT_Z * 30.0f; + cameraPos.x = cursorPos.x; + cameraPos.z = cursorPos.z; + cameraPos += cameraOffset; + cameraPos.y = + TerrainModule::get_height( + ECS::get().mTerrainGroup, cameraPos) + + 10.0f; + if (keepCameraAbove) { + if (cameraPos.y < 0.0f) + cameraPos.y = 10.0f; + } else { + Ogre::TerrainGroup *tg = + ECS::get().mTerrainGroup; + long x, y; + tg->convertWorldPositionToTerrainSlot(cameraPos, &x, + &y); + if (tg->getTerrain(x, y) && + tg->getTerrain(x, y)->isLoaded()) { + float height = + tg->getHeightAtWorldPosition(cameraPos); + cameraPos.y = height + 10.0f; + } + } + ECS::get().mCameraPivot->_setDerivedPosition(cameraPos); + ECS::get().mCameraPivot->_setDerivedOrientation( + cursorOrientation); + cameraPos = + ECS::get().mCameraGoal->_getDerivedPosition(); + ECS::get().mCameraNode->_setDerivedPosition(cameraPos); + ECS::get().mCameraNode->_setDerivedOrientation( + ECS::get() + .mCameraGoal->_getDerivedOrientation()); + updateHeightmap(); + } + void setCursorSelectedPos(flecs::entity e, Ogre::Vector3 &position, + Ogre::Quaternion &orientation) + { + StaticGeometryModule::getItemPositionAndRotation(e, position, + orientation); + selected_x = TerrainModule::get_img_x(position.x); + selected_y = TerrainModule::get_img_y(position.z); + ECS::get().sceneNode->_setDerivedPosition( + position); + ECS::get().sceneNode->_setDerivedOrientation( + orientation); + } + void setCameraSelectedPos(flecs::entity e) + { + Ogre::Vector3 cursorPos; + Ogre::Quaternion cursorOrientation; + setCursorSelectedPos(e, cursorPos, cursorOrientation); + Ogre::Vector3 cameraPos = + ECS::get().mCameraPivot->_getDerivedPosition(); + Ogre::Vector3 cameraOffset = + cursorOrientation * Ogre::Vector3::UNIT_Z * 30.0f; + cameraPos.x = cursorPos.x; + cameraPos.z = cursorPos.z; + cameraPos += cameraOffset; + cameraPos.y = + TerrainModule::get_height( + ECS::get().mTerrainGroup, cameraPos) + + 10.0f; + if (keepCameraAbove) { + if (cameraPos.y < 0.0f) + cameraPos.y = 10.0f; + } else { + Ogre::TerrainGroup *tg = + ECS::get().mTerrainGroup; + long x, y; + tg->convertWorldPositionToTerrainSlot(cameraPos, &x, + &y); + if (tg->getTerrain(x, y) && + tg->getTerrain(x, y)->isLoaded()) { + float height = + tg->getHeightAtWorldPosition(cameraPos); + cameraPos.y = height + 10.0f; + } + } + ECS::get().mCameraPivot->_setDerivedPosition(cameraPos); + ECS::get().mCameraPivot->_setDerivedOrientation( + cursorOrientation); + cameraPos = + ECS::get().mCameraGoal->_getDerivedPosition(); + ECS::get().mCameraNode->_setDerivedPosition(cameraPos); + ECS::get().mCameraNode->_setDerivedOrientation( + ECS::get() + .mCameraGoal->_getDerivedOrientation()); + updateHeightmap(); + } + void setSurfaceLevel(int actualSize, int center_x, int center_y, + float level) + { + int i, j; + float original = level; + if (actualSize == 1) + level += 0.4f; + else if (actualSize == 2) + level += 0.35f; + original = Ogre::Math::Clamp(original, 0.0f, 1.0f); + for (i = -actualSize; i < actualSize + 1; i++) + for (j = -actualSize; j < actualSize + 1; j++) { + if (i * i + j * j > actualSize * actualSize) + continue; + if (center_x + j < 0 || + center_x + j >= worldMap->getWidth()) + continue; + if (center_y + i < 0 || + center_y + i >= worldMap->getHeight()) + continue; + Ogre::ColourValue cv = + worldMapImage.getColourAt( + center_x + j, center_y + i, 0); + cv.r = original; + worldMapImage.setColourAt(cv, center_x + j, + center_y + i, 0); + } + updateWorldTexture(); + updateHeightmap(); + } + void setHarbourSurface() + { + int base_size = 3; + float base_height = 0.517f; + float base_step = 0.1f; + float deep = 0.25f; + float shallow = 0.35f; + float maxStep = 600.0f; + Ogre::Vector3 basePos = + ECS::get().sceneNode->_getDerivedPosition(); + Ogre::Quaternion baseRot = + ECS::get() + .sceneNode->_getDerivedOrientation(); + float baseOffset = 200.0f; + Ogre::Vector3 stepOffset = + baseRot * Ogre::Vector3::NEGATIVE_UNIT_Z * baseOffset; + std::vector heights = { deep, shallow, base_height, + base_height + base_step, + base_height + base_step * 2.0f }; + int step_count = 0; + for (float height : heights) { + float localStep = 0.0f; + Ogre::Vector3 currentPosition = + basePos + stepOffset * (float)step_count - + stepOffset * 0.5f; + float goTill = maxStep; + if (step_count < 2) + goTill += 420.0f; + while (localStep < + goTill - 150.0f * (float)step_count) { + localStep += baseOffset; + currentPosition += stepOffset; + int center_x = TerrainModule::get_img_x( + currentPosition.x); + int center_y = TerrainModule::get_img_y( + currentPosition.z); + int size = base_size + 10 - step_count * 2; + if (step_count < 2) + size = base_size + 14 - step_count * 2; + if (step_count == 0) + size += 1; + + setSurfaceLevel(size, center_x, center_y, + height); + } + step_count++; + } + } + static void to_json(nlohmann::json &j, const Ogre::Vector3 &position) + { + j["x"] = position.x; + j["y"] = position.y; + j["z"] = position.z; + } + float setLevelValue = 0.0f; + float riseLowerChange = 0.0f; + enum { + COMMAND_NONE, + COMMAND_RISELOWER, + COMMAND_RISELOWER2, + COMMAND_SMOOTH, + COMMAND_SETLEVEL + }; + int command; + void heightmapMenu() + { + command = COMMAND_NONE; + if (ImGui::Button("Elevate")) { + riseLowerChange = strength; + command = COMMAND_RISELOWER; + } + ImGui::SameLine(); + if (ImGui::Button("Elevate2")) { + riseLowerChange = strength; + command = COMMAND_RISELOWER2; + } + ImGui::SameLine(); + if (ImGui::Button("Lower")) { + riseLowerChange = -strength; + command = COMMAND_RISELOWER; + } + std::pair setLevelCommands[] = { + { "Deepest", 0.0f }, { "Deep", 0.25f }, + { "Shallow1", 0.35f }, { "Shallow2", 0.47f }, + { "Beach", 0.517f }, { "Shore1", 0.536f }, + { "Shore2", 0.556f }, { "Shore3", 0.586f }, + { "Shore4", 0.606f }, { "Shore5", 0.626f }, + { "Shore6", 0.646f }, { "Highest", 1.0f }, + }; + int buttonCounter = 0; + for (const auto &mb : setLevelCommands) { + if (ImGui::SmallButton(mb.first.c_str())) { + setLevelValue = mb.second; + command = COMMAND_SETLEVEL; + } + if ((buttonCounter & 3) != 0) + ImGui::SameLine(); + buttonCounter++; + } + ImGui::Spacing(); + if (ImGui::Button("Smooth")) { + command = COMMAND_SMOOTH; + } + ImGui::Separator(); + if (ImGui::MenuItem("Save heightmap")) { + updateWorldTexture(); + updateHeightmap(); + TerrainModule::save_heightmap(); + } + } + void executeCommands() + { + switch (command) { + case COMMAND_RISELOWER: { + int actualSize = 1 + size * 2; + int i, j; + for (i = -actualSize; i < actualSize + 1; i++) + for (j = -actualSize; j < actualSize + 1; j++) { + if (i * i + j * j > + actualSize * actualSize) + continue; + if (selected_x + j < 0 || + selected_x + j >= + worldMap->getWidth()) + continue; + if (selected_y + i < 0 || + selected_y + i >= + worldMap->getHeight()) + continue; + Ogre::ColourValue cv = + worldMapImage.getColourAt( + selected_x + j, + selected_y + i, 0); + float original = cv.r; + original += riseLowerChange; + original = Ogre::Math::Clamp( + original, 0.0f, 1.0f); + cv.r = original; + worldMapImage.setColourAt( + cv, selected_x + j, + selected_y + i, 0); + } + updateWorldTexture(); + updateHeightmap(); + + } break; + case COMMAND_RISELOWER2: { + int actualSize = 1 + size * 2; + int i, j; + Ogre::ColourValue maxcv = worldMapImage.getColourAt( + selected_x, selected_y, 0); + for (i = -actualSize; i < actualSize + 1; i++) + for (j = -actualSize; j < actualSize + 1; j++) { + if (i * i + j * j > + actualSize * actualSize) + continue; + if (selected_x + j < 0 || + selected_x + j >= + worldMap->getWidth()) + continue; + if (selected_y + i < 0 || + selected_y + i >= + worldMap->getHeight()) + continue; + float actualStrength = + riseLowerChange / + (1.0f + (float)(i * i + j * j)); + Ogre::ColourValue cv = + worldMapImage.getColourAt( + selected_x + j, + selected_y + i, 0); + float original = + maxcv.r + actualStrength; + original = Ogre::Math::Clamp( + original, 0.0f, 1.0f); + if (cv.r >= original) + continue; + cv.r = original; + worldMapImage.setColourAt( + cv, selected_x + j, + selected_y + i, 0); + } + updateWorldTexture(); + updateHeightmap(); + } break; + case COMMAND_SMOOTH: { + int actualSize = 1 + size * 2; + int i, j, k, l; + for (i = -actualSize; i < actualSize + 1; i++) + for (j = -actualSize; j < actualSize + 1; j++) { + if (i * i + j * j > + actualSize * actualSize) + continue; + if (selected_x + j < 0 || + selected_x + j >= + worldMap->getWidth()) + continue; + if (selected_y + i < 0 || + selected_y + i >= + worldMap->getHeight()) + continue; + int kernel = 3; + float original = 0.0f; + Ogre::ColourValue cv; + float count = 0.0f; + for (k = -kernel; k < kernel + 1; k++) { + if (selected_y + i + k < 0 || + selected_y + i + k >= + worldMap->getHeight()) + continue; + for (l = -kernel; + l < kernel + 1; l++) { + if (selected_x + j + l < + 0 || + selected_x + j + + l >= + worldMap->getWidth()) + continue; + cv = worldMapImage.getColourAt( + selected_x + j, + selected_y + i, + 0); + original += cv.r; + count += 1.0f; + } + } + original /= count; + cv = worldMapImage.getColourAt( + selected_x + j, selected_y + i, + 0); + original = Ogre::Math::Clamp( + original, 0.0f, 1.0f); + cv.r = original; + worldMapImage.setColourAt( + cv, selected_x + j, + selected_y + i, 0); + } + updateWorldTexture(); + updateHeightmap(); + } break; + case COMMAND_SETLEVEL: { + int actualSize = 1 + size * 2; + int i, j; + float original = setLevelValue; + original = Ogre::Math::Clamp(original, 0.0f, 1.0f); + for (i = -actualSize; i < actualSize + 1; i++) + for (j = -actualSize; j < actualSize + 1; j++) { + if (i * i + j * j > + actualSize * actualSize) + continue; + if (selected_x + j < 0 || + selected_x + j >= + worldMap->getWidth()) + continue; + if (selected_y + i < 0 || + selected_y + i >= + worldMap->getHeight()) + continue; + Ogre::ColourValue cv = + worldMapImage.getColourAt( + selected_x + j, + selected_y + i, 0); + cv.r = original; + worldMapImage.setColourAt( + cv, selected_x + j, + selected_y + i, 0); + } + updateWorldTexture(); + updateHeightmap(); + + } break; + } + } + void displayItems() + { + std::pair selected_item; + std::list > items; + StaticGeometryModule::getItemsProperties(&items); + bool item_is_selected = false; + for (const auto &item : items) { + nlohmann::json j = nlohmann::json::parse(item.second); + Ogre::String label = Ogre::StringConverter::toString( + item.first.id()); + label += ":" + j["type"].get(); + if (j.find("name") != j.end()) + label += " " + j["name"].get(); + + if (ImGui::SmallButton(label.c_str())) { /* select */ + selected_item = item; + item_is_selected = true; + } + Items::showItemButtons(item); + } + if (item_is_selected) + setCameraSelectedPos(selected_item.first); + for (const auto &item : items) + Items::showItemPopup(item); + } + void displayInfo() + { + ImGui::Text("Position: %d %d", pos_x, pos_y); + ImGui::Text("Selected Position: %d %d", selected_x, selected_y); + ImGui::Text("Height: %f", + worldMapImage.getColourAt(pos_x, pos_y, 0).r); + ImGui::Text( + "Selected height: %f", + worldMapImage.getColourAt(selected_x, selected_y, 0).r); + { + Ogre::Vector3 position = + ECS::get() + .sceneNode->_getDerivedPosition(); + ImGui::Text("Cursor position %f %f %f", position.x, + position.y, position.z); + } + } + std::vector split(const std::string &str) + { + std::vector lines; + std::istringstream iss( + str); // Create a stringstream from the input string. + std::string line; + + while (std::getline( + iss, line)) { // Read lines until the end of the stream. + // Optional: you can add logic here to handle different newline conventions + // (e.g., Windows uses \r\n, which getline handles by discarding \n, + // but the \r might remain if not explicitly handled/removed). + lines.push_back(line); + } + + return lines; + } + void cleanupTownTemplateData(nlohmann::json &jtown) + { + if (jtown.find("districts") != jtown.end()) { + nlohmann::json &districts = jtown["districts"]; + for (auto &jdistrict : districts) { + if (jdistrict.find("lots") != jdistrict.end()) { + nlohmann::json &lots = + jdistrict["lots"]; + for (auto &lot : lots) { + if (lot.find("cells") != + lot.end()) + lot.erase("cells"); + if (lot.find( + "furniture_cells") != + lot.end()) + lot.erase( + "furniture_cells"); + if (lot.find("roofs") != + lot.end()) + lot.erase("roofs"); + } + } + } + } + } + Ogre::String createTownScript(const Ogre::String &townData) + { + Ogre::String ret = "-- generated template\n"; + return ret; + } + void editTownTemplateScript(const Ogre::String &templateLabel) + { + } + void worldMapView() + { + bool update_cursor_position = false; + bool update_cursor_height = false; + bool update_cursor_angle = false; + OgreAssert(TerrainModule::get_img_x(0) == + worldMap->getWidth() / 2, + "get_img_x"); + OgreAssert(TerrainModule::get_img_y(0) == + worldMap->getHeight() / 2, + "get_img_x"); + OgreAssert(TerrainModule::get_world_x(worldMap->getWidth() / + 2) == 0.0f, + "get_world_x"); + OgreAssert(TerrainModule::get_world_y(worldMap->getHeight() / + 2) == 0.0f, + "get_world_y"); + if (ECS::get().sceneNode) { + Ogre::Vector3 worldPos = + ECS::get() + .sceneNode->_getDerivedPosition(); + selected_x = TerrainModule::get_img_x(worldPos.x); + selected_y = TerrainModule::get_img_y(worldPos.z); + locationSelected = true; + OgreAssert(selected_x >= 0 && + selected_x < worldMap->getWidth(), + "mix width"); + OgreAssert(selected_y >= 0 && + selected_y < worldMap->getHeight(), + "mix height"); + ECS::get() + .mTerrainGroup + ->convertWorldPositionToTerrainSlot( + worldPos, &slot_x, &slot_y); + } + ImGui::SetNextWindowSizeConstraints(ImVec2(512 + 20, 512 + 20), + ImVec2(768, 768)); + // ImGui::SetNextWindowScroll( + // ImVec2(worldMap->getWidth(), worldMap->getHeight())); + ImGui::Begin("WorldMap...", nullptr, ImGuiWindowFlags_MenuBar); + if (ImGui::BeginMenuBar()) { + if (ImGui::BeginMenu("Create")) { + Items::createItemsMenu(); + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("Heightmap")) { + if (ImGui::MenuItem("Update terrain")) + ECS::get() + .mTerrainGroup->update(false); + ImGui::Separator(); + ImGui::SliderFloat("Strength...", &strength, + 0.0f, 1.0f); + ImGui::SliderInt("Size", &size, 0, 32); + ImGui::Separator(); + heightmapMenu(); + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("Cursor")) { + if (ImGui::MenuItem("Update cursor position")) + update_cursor_height = true; + Ogre::Vector3 cursorPosition = + ECS::get() + .sceneNode + ->_getDerivedPosition(); + bool modified = false; + if (ImGui::SmallButton("X-1")) { + cursorPosition.x -= 1.0f; + modified = true; + } + ImGui::SameLine(); + if (ImGui::SmallButton("X+1")) { + cursorPosition.x += 1.0f; + modified = true; + } + ImGui::SameLine(); + if (ImGui::SmallButton("Y-1")) { + cursorPosition.y -= 1.0f; + modified = true; + } + ImGui::SameLine(); + if (ImGui::SmallButton("Y+1")) { + cursorPosition.y += 1.0f; + modified = true; + } + ImGui::SameLine(); + if (ImGui::SmallButton("Z-1")) { + cursorPosition.z -= 1.0f; + modified = true; + } + ImGui::SameLine(); + if (ImGui::SmallButton("Z+1")) { + cursorPosition.z += 1.0f; + modified = true; + } + ImGui::EndMenu(); + if (modified) { + ECS::get() + .sceneNode->_setDerivedPosition( + cursorPosition); + modified = false; + } + } + if (ImGui::BeginMenu("Furniture")) { + if (ImGui::BeginMenu("Create New Furniture")) { + static char nameBuffer[32]; + static int current_mesh = 0; + static std::vector + glb_names; + const std::vector &groups = + Ogre::ResourceGroupManager:: + getSingleton() + .getResourceGroups(); + if (glb_names.size() == 0) { + int i; + glb_names.push_back(""); + for (i = 0; i < groups.size(); + i++) { + std::vector names = + *Ogre::ResourceGroupManager::getSingleton() + .findResourceNames( + groups[i], + "furniture-*.glb"); + glb_names.insert( + glb_names.end(), + names.begin(), + names.end()); + } + } + ImGui::InputText( + "Furniture Name", nameBuffer, + IM_ARRAYSIZE(nameBuffer)); + if (glb_names.size() > 0) { + if (ImGui::BeginCombo( + "Furniture Mesh", + glb_names[current_mesh] + .c_str())) { + int i; + for (i = 0; + i < + glb_names.size(); + i++) { + bool isSelected = + i == + current_mesh; + if (ImGui::Selectable( + (glb_names[i] + + "##select" + + Ogre::StringConverter:: + toString( + i)) + .c_str(), + isSelected)) { + current_mesh = + i; + } + if (isSelected) + ImGui::SetItemDefaultFocus(); + } + ImGui::EndCombo(); + } + } else + ImGui::Text( + "No furniture meshes found"); + if (ImGui::MenuItem( + "Create Furniture")) { + } + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("Edit Furniture")) { + if (ImGui::MenuItem("Edit Furniture")) { + } + ImGui::EndMenu(); + } + ImGui::EndMenu(); + } + ImGui::EndMenuBar(); + } + ImGui::Spacing(); + ImGui::BeginChild("WorldMap...", ImVec2(480, 480), + ImGuiChildFlags_None, + ImGuiWindowFlags_HorizontalScrollbar); + ImGui::Spacing(); + Ogre::ResourceHandle hdl = worldMap->getHandle(); + int w = worldMap->getWidth(); + int h = worldMap->getHeight(); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, + ImVec2((float)5, (float)5)); + ImGui::ImageButton("WorldMapPress", (ImTextureID)hdl, + ImVec2(w, h)); + ImVec2 mouse_absolute_pos = ImGui::GetMousePos(); + ImVec2 item_absolute_pos = ImGui::GetItemRectMin(); + top_x = item_absolute_pos.x + 5; + top_y = item_absolute_pos.y + 5; + bool hovered = false; + if (ImGui::IsItemHovered()) { + ImVec2 mouse_absolute_pos = ImGui::GetMousePos(); + ImVec2 item_absolute_pos = ImGui::GetItemRectMin(); + pos_x = mouse_absolute_pos.x - item_absolute_pos.x - 5; + pos_y = mouse_absolute_pos.y - item_absolute_pos.y - 5; + hovered = true; + } + pos_x = Ogre::Math::Clamp(pos_x, 0, + (int)worldMap->getWidth() - 1); + pos_y = Ogre::Math::Clamp(pos_y, 0, + (int)worldMap->getHeight() - 1); + if (pos_x < 0) + pos_x = 0; + if (pos_x >= worldMap->getWidth()) + pos_x = worldMap->getWidth() - 1; + if (pos_y < 0) + pos_y = 0; + if (pos_y >= worldMap->getHeight()) + pos_y = worldMap->getHeight() - 1; + if (ImGui::IsItemActivated()) { + locationSelected = true; + selected_x = pos_x; + selected_y = pos_y; + OgreAssert(selected_x >= 0 && + selected_x < worldMap->getWidth(), + "mix width"); + OgreAssert(selected_y >= 0 && + selected_y < worldMap->getHeight(), + "mix height"); + setCameraPos(); + std::cout << "worldClickPos: " << pos_x << " " << pos_y + << std::endl; + } + ImDrawList *draw_list = ImGui::GetWindowDrawList(); + draw_list->AddCircleFilled(ImVec2(top_x + pos_x, top_y + pos_y), + 1.0f, IM_COL32(0, 255, 0, 255)); + { + std::list positions; + StaticGeometryModule::getItemPositions(&positions); + for (auto pos : positions) { + int item_x = TerrainModule::get_img_x(pos.x); + int item_y = TerrainModule::get_img_y(pos.z); + draw_list->AddCircleFilled( + ImVec2(top_x + item_x, top_y + item_y), + 3.0f, IM_COL32(255, 255, 0, 255)); + } + } + if (locationSelected) + draw_list->AddCircleFilled( + ImVec2(top_x + selected_x, top_y + selected_y), + 4.0f, IM_COL32(64, 255, 64, 255)); + { + Ogre::Vector3 cursorPos = + ECS::get() + .sceneNode->_getDerivedPosition(); + int cursor_x = TerrainModule::get_img_x(cursorPos.x); + int cursor_y = TerrainModule::get_img_y(cursorPos.z); + draw_list->AddCircleFilled( + ImVec2(top_x + cursor_x, top_y + cursor_y), + 4.0f, IM_COL32(255, 64, 64, 128)); + } + ImGui::PopStyleVar(); + ImGui::Spacing(); + ImGui::EndChild(); + ImGui::SameLine(); + ImGui::BeginChild("WorldMap...", ImVec2(64, 480), + ImGuiChildFlags_None, + ImGuiWindowFlags_HorizontalScrollbar); + ImGui::EndChild(); + ImGui::Spacing(); + ImGui::BeginChild("WorldMap Bottom...", ImVec2(0, 0)); + if (ImGui::CollapsingHeader("Display Info")) { + displayInfo(); + if (ImGui::SliderFloat("Cursor Angle...", &cursorAngle, + -180.0f, 180.0f)) + update_cursor_angle = true; + } + if (ImGui::SmallButton("Display Templates")) + ImGui::OpenPopup("display_templates_popup"); + if (ImGui::BeginPopup("display_templates_popup")) { + ImGui::Text("Town templates"); + ImGui::Separator(); + std::list > items; + StaticGeometryModule::getItemsProperties(&items); + nlohmann::json &templates = + StaticGeometryModule::getTemplates(); + bool changed = false; + for (auto p = templates.begin(); p != templates.end(); + p++) { + Ogre::String templateLabel = p.key(); + ImGui::Text("%s", templateLabel.c_str()); + ImGui::SameLine(); + if (ImGui::SmallButton( + ("Delete##" + templateLabel) + .c_str())) { + templates.erase(p.key()); + changed = true; + } + } + for (const auto &item : items) { + nlohmann::json j = + nlohmann::json::parse(item.second); + Ogre::String label = + Ogre::StringConverter::toString( + item.first.id()); + Ogre::String type = + j["type"].get(); + label += ":" + type; + if (type == "town") { + ImGui::Text("%llu", + (unsigned long long) + item.first.id()); + ImGui::SameLine(); + if (ImGui::SmallButton( + ("Create Template##" + label) + .c_str())) { /* select */ + cleanupTownTemplateData(j); + nlohmann::json t; + Ogre::String townScript = + createTownScript( + item.second); + t["townScript"] = townScript; + if (j.find("colorRects") != + j.end()) + t["colorRects"] = + j["colorRects"]; + if (j.find("districts") != + j.end()) + t["districts"] = + j["districts"]; + if (j.find("districtTemplates") != + j.end()) + t["districtTemplates"] = + j["districtTemplates"]; + if (j.find("lotTemplates") != + j.end()) + t["lotTemplates"] = + j["lotTemplates"]; + templates["Default_" + + Ogre::StringConverter::toString( + item.first + .id())] = + t; + changed = true; + } + } + } + ImGui::EndPopup(); + if (changed) + StaticGeometryModule::saveTemplates(); + } + if (ImGui::SmallButton("Run script for all towns...")) + Items::runScriptsForAllTowns(); + displayItems(); + ImGui::EndChild(); + ImGui::Spacing(); + ImGui::End(); + if (update_cursor_height || update_cursor_angle) { + if (update_cursor_height) { + Ogre::Vector3 position = + ECS::get() + .sceneNode + ->_getDerivedPosition(); + position.y = ECS::get() + .mTerrainGroup + ->getHeightAtWorldPosition( + position); + ECS::get() + .sceneNode->_setDerivedPosition( + position); + } + if (update_cursor_angle) + ECS::get() + .sceneNode->_setDerivedOrientation( + Ogre::Quaternion( + Ogre::Degree( + cursorAngle), + Ogre::Vector3::UNIT_Y)); + } + executeCommands(); + } + void panel() + { + ImVec2 size = ImGui::GetMainViewport()->Size; + float window_width = size.x * 0.2f; + if (window_width > panel_width) + window_width = panel_width; + float window_height = size.y * 0.5f - 20; + ImGui::SetNextWindowPos(ImVec2(size.x - window_width, 20), + ImGuiCond_Always); + ImGui::SetNextWindowSize(ImVec2(window_width, window_height), + ImGuiCond_Always); + // ImGui::Begin("Dumb and Stupid", &mKbd.gui_active); + ImGui::Begin("Panel..."); + std::deque tree_input_queue, + tree_output_queue; + std::vector tree_list; + tree_input_queue.push_back( + ECS::get() + .get() + .mScnMgr->getRootSceneNode()); + tree_input_queue.push_back(nullptr); + std::set visited; + while (true) { + int new_nodes_count = 0; + while (!tree_input_queue.empty()) { + int child; + Ogre::SceneNode *item = + tree_input_queue.front(); + tree_input_queue.pop_front(); + if (item && visited.find(item) == + visited.end()) { // new node + new_nodes_count++; + tree_output_queue.push_back(item); + visited.insert(item); + const Ogre::Node::ChildNodeMap + &children = item->getChildren(); + for (child = 0; child < children.size(); + child++) { + tree_output_queue.push_back( + static_cast( + children[child])); + tree_output_queue.push_back( + nullptr); + } + } else + tree_output_queue.push_back(item); + } + if (new_nodes_count == 0) + break; + tree_input_queue = tree_output_queue; + tree_output_queue.clear(); + } + tree_list.insert(tree_list.begin(), tree_output_queue.begin(), + tree_output_queue.end()); + int count = 0; + int depth = 0; + std::vector check_depth; + int max_depth = 0; + check_depth.push_back(0); + for (count = 0; count < tree_list.size(); count++) { + int t; + + Ogre::SceneNode *node = tree_list[count]; + if (node && max_depth >= depth) { + Ogre::String name = node->getName(); + if (name.length() == 0) { + name = "Node #" + + Ogre::StringConverter::toString( + count); + } + if (ImGui::TreeNode(name.c_str())) { + check_depth.push_back(max_depth); + max_depth++; + ImGui::Text( + "%s", + (name + "##caption").c_str()); + position_editor(node); + ImGui::Separator(); + orientation_editor(node); + ImGui::Separator(); + if (ImGui::Button("From Cursor")) + std::cout << name + << " From Cursor" + << std::endl; + if (ImGui::Button("To Cursor")) + std::cout << name + << " To Cursor" + << std::endl; + ImGui::Separator(); + ImGui::Text("Attachments"); + attachments_editor(node); + } + } else if (!node && max_depth >= depth) { + max_depth = check_depth.back(); + check_depth.pop_back(); + ImGui::TreePop(); + } + if (tree_list[count]) + depth++; + else + depth--; + } + ImGui::Spacing(); + ImGui::End(); + } + void preview(const Ogre::RenderTargetViewportEvent &evt) + { + int i; + Ogre::ImGuiOverlay::NewFrame(); + if (ECS::get().get().enabled) { + buttons_panel(); + panel(); + if (enableMapEditor) + worldMapView(); + } + } +}; +EditorGUIModule::EditorGUIModule(flecs::world &ecs) +{ + ecs.module(); + ecs.import (); + ecs.import (); + ecs.component() + .on_add([](GUI &gui) { + gui.enabled = true; + gui.grab = false; + gui.grabChanged = false; + }) + .add(flecs::Singleton); + ecs.component() + .on_add([](EditorGUIData &priv) { + priv.glb_names.clear(); + priv.mGUIListener = nullptr; + priv.mGuiOverlay = nullptr; + }) + .add(flecs::Singleton); + ecs.observer("SupportEditorGUI") + .event(flecs::OnSet) + .without() + .each([](const RenderWindow &window, const App &app, GUI &gui) { + float vpScale = window.dpi / 96 * + window.window->getWidth() / 1600.0f; + Ogre::OverlayManager::getSingleton().setPixelRatio( + vpScale); + std::cout << "Editor GUI configure\n"; + OgreAssert(app.mGuiOverlay, "No ImGUI overlay"); + Ogre::ImGuiOverlay *guiOverlay = app.mGuiOverlay; + EditorGUIListener *guiListener = + new EditorGUIListener(guiOverlay); + guiOverlay->setZOrder(300); + guiOverlay->show(); + guiListener->panel_width = 300.0f; + guiListener->enableEditor = false; + window.window->addListener(guiListener); + + ECS::get().set( + { app.mGuiOverlay, {}, guiListener }); + std::cout << "Editor GUI configure finished\n"; + }); +} +} diff --git a/src/gamedata/EditorGUIModule.h b/src/gamedata/EditorGUIModule.h new file mode 100644 index 0000000..9c08dd6 --- /dev/null +++ b/src/gamedata/EditorGUIModule.h @@ -0,0 +1,9 @@ +#ifndef __EDITORGUIMODULE_H__ +#define __EDITORGUIMODULE_H__ + +namespace ECS { +struct EditorGUIModule { + EditorGUIModule(flecs::world &ecs); +}; +} +#endif // EDITORGUIMODULE_H diff --git a/src/gamedata/EventModule.h b/src/gamedata/EventModule.h index 1a2447a..c8fb0b6 100644 --- a/src/gamedata/EventModule.h +++ b/src/gamedata/EventModule.h @@ -26,4 +26,4 @@ struct EventModule { flecs::entity to); }; } -#endif \ No newline at end of file +#endif diff --git a/src/gamedata/GUIModule.cpp b/src/gamedata/GUIModule.cpp index 91e9d89..45631bc 100644 --- a/src/gamedata/GUIModule.cpp +++ b/src/gamedata/GUIModule.cpp @@ -21,22 +21,18 @@ #include "StaticGeometryModule.h" #include "EditorGizmoModule.h" #include "PhysicsModule.h" +#include "PlayerActionModule.h" #include "items.h" #include "GUIModule.h" +#include "GUIModuleCommon.h" namespace ECS { struct GUIListener; -struct EditorGUIListener; struct GUIData { Ogre::ImGuiOverlay *mGuiOverlay; std::vector glb_names; GUIListener *mGUIListener; }; -struct EditorGUIData { - Ogre::ImGuiOverlay *mGuiOverlay; - std::vector glb_names; - EditorGUIListener *mGUIListener; -}; struct GUIListener : public Ogre::RenderTargetListener { float panel_width; bool enableEditor; @@ -216,7 +212,31 @@ struct GUIListener : public Ogre::RenderTargetListener { void map_editor() { } - void preview(const Ogre::RenderTargetViewportEvent &evt) + Ogre::Vector2 projectToScreen(const Ogre::Vector3 &worldPoint) + { + ImVec2 size = ImGui::GetMainViewport()->Size; + float width = size.x; + float height = size.y; + Ogre::Camera *camera = ECS::get().mCamera; + // 1. Convert to camera space + Ogre::Vector3 eyeSpacePoint = + camera->getViewMatrix() * worldPoint; + + // 2. Project to clip space + Ogre::Vector3 clipSpacePoint = + camera->getProjectionMatrix() * eyeSpacePoint; + if (clipSpacePoint.z < 0.0f) + return Ogre::Vector2(-1, -1); + + // 3. Convert from clip space (-1 to 1) to screen space (0 to 1) + // Note: Y is usually flipped in API screen coordinates compared to projection + float screenX = (clipSpacePoint.x / 2.0f) + 0.5f; + float screenY = 1.0f - ((clipSpacePoint.y / 2.0f) + 0.5f); + + // 4. Map to actual pixel dimensions + return Ogre::Vector2(screenX * width, screenY * height); + } + void preview(const Ogre::RenderTargetViewportEvent &evt) { int i; Ogre::ImGuiOverlay::NewFrame(); @@ -472,1222 +492,48 @@ struct GUIListener : public Ogre::RenderTargetListener { ImGui::Spacing(); ImGui::End(); } - } + } else { + ECS::ActionNodeList &list = + ECS::get_mut(); + if (list.nodes.size() > 0) { + Ogre::Vector3 queryPos = + ECS::get() + .mCameraNode + ->_getDerivedPosition(); + std::vector points; + list.query(queryPos, points); + for (auto &p : points) { + std::cout << p << std::endl + << list.nodes[p].props.dump() + << std::endl; + Ogre::Vector2 screenPos = + projectToScreen( + list.nodes[p].position); + if (screenPos.x < 0) + continue; + std::cout << list.nodes[p].position + << " " << screenPos + << std::endl; + ImGui::SetNextWindowPos(ImVec2( + screenPos.x, screenPos.y)); + ImGui::Begin( + ("SensorLabel##" + + Ogre::StringConverter::toString( + p)) + .c_str(), + nullptr, + ImGuiWindowFlags_NoBackground | + ImGuiWindowFlags_NoTitleBar | + ImGuiWindowFlags_NoInputs); + ImGui::TextColored(ImVec4(1, 0, 0, 1), + "SENSOR TRIGGERED"); + ImGui::End(); + } + } + } } }; -struct EditorGUIListener : public Ogre::RenderTargetListener { - float panel_width; - bool enableEditor; - bool enableMapEditor; - ImFont *smallFont, *midFont, *bigFont; - Ogre::FontPtr _smallFont, _midFont, _bigFont; - Ogre::TexturePtr worldMap; - Ogre::Image worldMapImage; - - EditorGUIListener(Ogre::ImGuiOverlay *overlay) - : Ogre::RenderTargetListener() - , enableEditor(false) - , enableMapEditor(false) - , keepCameraAbove(true) - , command(COMMAND_NONE) - { - _midFont = createFont("midFont", "General", - "Jupiteroid-Regular.ttf", 18.0f); - _smallFont = createFont("smallFont", "General", - "Jupiteroid-Regular.ttf", 13.0f); - _bigFont = createFont("bigFont", "General", "Kenney Bold.ttf", - 32.0f); - smallFont = overlay->addFont("smallFont", "General"); - OgreAssert(smallFont, "Could not load font"); - midFont = overlay->addFont("midFont", "General"); - OgreAssert(midFont, "Could not load font"); - bigFont = overlay->addFont("bigFont", "General"); - OgreAssert(bigFont, "Could not load font"); - worldMapImage.load( - "world_map.png", - Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); -#if 0 - int x, y, i, j; - for (y = 0; y < worldMapImage.getWidth(); y++) - for (x = 0; x < worldMapImage.getWidth(); x++) { - float r = 0.0f; - for (i = -2; i < 3; i++) - for (j = -2; j < 3; j++) { - int xj = Ogre::Math::Clamp( - x + j, 0, - (int)worldMapImage - .getWidth() - - 1); - int yi = Ogre::Math::Clamp( - y + i, 0, - (int)worldMapImage - .getHeight() - - 1); - r += worldMapImage - .getColourAt(xj, - yi, 0) - .r; - } - r /= 25.0f; - Ogre::ColourValue cv = - worldMapImage.getColourAt(x, y, 0); - cv.r = r; - worldMapImage.setColourAt(cv, x, y, 0); - } -#endif - worldMap = Ogre::TextureManager::getSingleton().createManual( - "worldMap", - Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - Ogre::TEX_TYPE_2D, worldMapImage.getWidth(), - worldMapImage.getHeight(), - worldMapImage.getNumMipmaps(), - worldMapImage.getFormat(), Ogre::TU_DYNAMIC_WRITE_ONLY); - worldMap->loadImage(worldMapImage); - OgreAssert(worldMap, "could not load world map"); - } - Ogre::FontPtr createFont(const Ogre::String &name, - const Ogre::String &group, - const Ogre::String &ttfname, float fontSize) - { - Ogre::FontPtr ret = - Ogre::FontManager::getSingleton().create(name, group); - ret->setType(Ogre::FontType::FT_TRUETYPE); - ret->setSource(ttfname); - ret->setTrueTypeSize(fontSize); - ret->setTrueTypeResolution(75); - ret->addCodePointRange(Ogre::Font::CodePointRange(30, 128)); - ret->load(); - return ret; - } - void - preViewportUpdate(const Ogre::RenderTargetViewportEvent &evt) override - { - preview(evt); - } - void - postViewportUpdate(const Ogre::RenderTargetViewportEvent &evt) override - { -#if 0 - if (switchWindow) { - ECS::get().set({ 1 }); - switchWindow = false; - } -#endif - } -#if 0 - bool switchWindow = false; -#endif - void buttons_panel() - { - bool enableDebugRender = ECS::get().enableDbgDraw; - - ImVec2 size = ImGui::GetMainViewport()->Size; - float window_width = size.x * 0.2f; - if (window_width > panel_width) - window_width = panel_width; - float window_height = size.y * 0.5f - 20; - ImGui::SetNextWindowPos(ImVec2(0, size.y * 0.5f + 20), - ImGuiCond_Always); - ImGui::SetNextWindowSize(ImVec2(window_width, window_height), - ImGuiCond_Always); - ImGui::Begin("Control"); - // if (ECS::get().get().enabled) - // ECS::get().get().app->setWindowGrab(true); - if (ImGui::Button("Quit")) - Ogre::Root::getSingleton().queueEndRendering(); - if (ImGui::Button("Return")) - ECS::get().get().finish(); - if (ImGui::Checkbox("Enable physics debug", - &enableDebugRender)) { - ECS::get_mut().enableDbgDraw = - enableDebugRender; - ECS::modified(); - PhysicsModule::setDebugDraw(enableDebugRender); - } -#if 0 - if (ImGui::Button("AltCam")) { - switchWindow = true; - } -#endif - if (ImGui::Button("Deploy config")) { - std::function m = [&](const Ogre::String &src, - const Ogre::String &dst) { - std::ifstream source(src, std::ios::binary); - std::ofstream destination(dst, - std::ios::binary); - - OgreAssert(source.is_open(), - "Failed to copy files " + src + - " -> " + dst); - OgreAssert(destination.is_open(), - "Failed to copy files " + src + - " -> " + dst); - OgreAssert(source.is_open() && - destination.is_open(), - "Failed to copy files " + src + - " -> " + dst); - destination << source.rdbuf(); - destination.close(); - source.close(); - }; - m("resources/buildings/items.list", - "../../resources/buildings/items.list"); - m("resources/terrain/world_map.png", - "../../resources/terrain/world_map.png"); - } - ImGui::Checkbox("Keep camera above 0", &keepCameraAbove); -#if 0 - if (ImGui::Button("Enable/Disable item editor")) { - enableEditor ^= true; - if (enableEditor) - enableMapEditor = false; - } - if (ImGui::Button("Enable/Disable map editor")) { - enableMapEditor ^= true; - if (enableMapEditor) - enableEditor = false; - } - ImGui::Text("Text message..."); -#endif - ImGui::Checkbox("Enable Map", &enableMapEditor); - ImGui::End(); - } - void create_entity_node(const Ogre::String &name, int key) - { - Ogre::Entity *ent = - ECS::get().get().mScnMgr->createEntity( - name); - Ogre::SceneNode *pnode = - ECS::get() - .get() - .mScnMgr->getRootSceneNode() - ->createChildSceneNode( - "ent:" + name + - Ogre::StringConverter::toString( - key), - ECS::get() - .get() - .mCameraPivot->getPosition(), - ECS::get() - .get() - .mCameraPivot->getOrientation()); - pnode->attachObject(ent); - Ogre::Quaternion q = pnode->getOrientation(); - Ogre::Radian yaw = q.getYaw(); - Ogre::Quaternion nq(yaw, Ogre::Vector3(0, 1, 0)); - pnode->setOrientation(nq); - - ECS::get().get().finish(); - } -#if 0 - void buildings_editor() - { - int i; - ImVec2 size = ImGui::GetMainViewport()->Size; - float window_width = size.x * 0.2f; - if (window_width > panel_width) - window_width = panel_width; - float window_height = size.y * 0.5 - 20; - ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always); - ImGui::SetNextWindowSize(ImVec2(window_width, window_height), - ImGuiCond_Always); - ImGui::Begin("Droppings..."); - for (i = 0; - i < ECS::get().get().glb_names.size(); - i++) { - Ogre::String id_button = - "Create entity: " + - ECS::get().get().glb_names[i] + - "##ent:" + - ECS::get().get().glb_names[i]; - if (ImGui::Button(id_button.c_str())) { - create_entity_node(ECS::get() - .get() - .glb_names[i], - i); - } - } - ImGui::End(); - } -#endif - void position_editor(Ogre::SceneNode *node) - { - Ogre::Vector3 position = node->getPosition(); - float v[3] = { position.x, position.y, position.z }; - ImGui::InputFloat3("position", v); - position.x = v[0]; - position.y = v[1]; - position.z = v[2]; - node->setPosition(position); - } - void orientation_editor(Ogre::SceneNode *node) - { - Ogre::Quaternion q = node->getOrientation(); - float yaw = Ogre::Radian(q.getYaw()).valueDegrees(); - float pitch = Ogre::Radian(q.getPitch()).valueDegrees(); - float roll = Ogre::Radian(q.getRoll()).valueDegrees(); - bool m1 = ImGui::InputFloat("yaw", &yaw); - bool m2 = ImGui::InputFloat("pitch", &pitch); - bool m3 = ImGui::InputFloat("roll", &roll); - if (m1 || m2 || m3) { - Ogre::Quaternion q1(Ogre::Radian(Ogre::Degree(yaw)), - Ogre::Vector3::UNIT_Y); - Ogre::Quaternion q2(Ogre::Degree(pitch), - Ogre::Vector3::UNIT_X); - Ogre::Quaternion q3(Ogre::Degree(roll), - Ogre::Vector3::UNIT_Z); - node->setOrientation(q1 * q2 * q3); - } - } - void attachments_editor(Ogre::SceneNode *node) - { - const Ogre::SceneNode::ObjectMap &pmap = - node->getAttachedObjects(); - int i; - for (i = 0; i < pmap.size(); i++) { - const Ogre::MovableObject *mobj = pmap[i]; - const Ogre::String &pname = mobj->getName(); - ImGui::Text("Name: %s", pname.c_str()); - } - } -#if 0 - void map_editor() - { - } -#endif - int pos_x = 0; - int pos_y = 0; - int top_x = 0; - int top_y = 0; - int selected_x = 0; - int selected_y = 0; - bool locationSelected = false; - float strength = 0.0f; - int size = 0; - long slot_x, slot_y; - float cursorAngle = 0; - void updateWorldTexture() - { - // Get the hardware pixel buffer - Ogre::HardwarePixelBufferSharedPtr pixelBuffer = - worldMap->getBuffer(); - - // Lock the buffer for writing, discarding previous contents for performance - pixelBuffer->lock(Ogre::HardwareBuffer::HBL_DISCARD); - - // Get information about the locked region (PixelBox) - const Ogre::PixelBox &pixelBox = pixelBuffer->getCurrentLock(); - - // Ensure the image format matches the texture format - OgreAssert(pixelBox.format == worldMapImage.getFormat(), - "bad format"); - - // Copy the image data to the pixel box's data pointer - memcpy(pixelBox.data, worldMapImage.getData(), - worldMapImage.getSize()); - - // Unlock the buffer to apply changes to the GPU - pixelBuffer->unlock(); - } - void updateHeightmap() - { - Ogre::Vector3 worldPos = - ECS::get().mCameraPivot->_getDerivedPosition(); - TerrainModule::update_heightmap(worldMapImage); - long x, y; - ECS::get() - .mTerrainGroup->convertWorldPositionToTerrainSlot( - worldPos, &x, &y); - for (auto &slot : - ECS::get().mTerrainGroup->getTerrainSlots()) { - Ogre::uint32 page = - ECS::get().mTerrainGroup->packIndex( - slot.second->x, slot.second->y); - Ogre::Terrain *terrain = - ECS::get().mTerrainGroup->getTerrain( - x, y); - if (terrain) - terrain->waitForDerivedProcesses(); - ECS::get() - .mTerrainPagedWorldSection->unloadPage(page, - false); - } - ECS::get().mTerrainGroup->update(false); - } - void setCursorPos(Ogre::Vector3 &cursorPosition, - Ogre::Quaternion &orientation) - { - Ogre::Vector3 worldPos = - ECS::get().sceneNode->_getDerivedPosition(); - worldPos.x = TerrainModule::get_world_x(selected_x); - worldPos.z = TerrainModule::get_world_y(selected_y); - worldPos.y = TerrainModule::get_height( - ECS::get().mTerrainGroup, worldPos); - ECS::get().sceneNode->_setDerivedPosition( - worldPos); - cursorPosition = worldPos; - orientation = ECS::get() - .sceneNode->_getDerivedOrientation(); - } - bool keepCameraAbove = true; - void setCameraPos() - { - Ogre::Vector3 cursorPos; - Ogre::Quaternion cursorOrientation; - setCursorPos(cursorPos, cursorOrientation); - Ogre::Vector3 cameraPos = - ECS::get().mCameraPivot->_getDerivedPosition(); - Ogre::Vector3 cameraOffset = - cursorOrientation * Ogre::Vector3::UNIT_Z * 30.0f; - cameraPos.x = cursorPos.x; - cameraPos.z = cursorPos.z; - cameraPos += cameraOffset; - cameraPos.y = - TerrainModule::get_height( - ECS::get().mTerrainGroup, cameraPos) + - 10.0f; - if (keepCameraAbove) { - if (cameraPos.y < 0.0f) - cameraPos.y = 10.0f; - } else { - Ogre::TerrainGroup *tg = - ECS::get().mTerrainGroup; - long x, y; - tg->convertWorldPositionToTerrainSlot(cameraPos, &x, - &y); - if (tg->getTerrain(x, y) && - tg->getTerrain(x, y)->isLoaded()) { - float height = - tg->getHeightAtWorldPosition(cameraPos); - cameraPos.y = height + 10.0f; - } - } - ECS::get().mCameraPivot->_setDerivedPosition(cameraPos); - ECS::get().mCameraPivot->_setDerivedOrientation( - cursorOrientation); - cameraPos = - ECS::get().mCameraGoal->_getDerivedPosition(); - ECS::get().mCameraNode->_setDerivedPosition(cameraPos); - ECS::get().mCameraNode->_setDerivedOrientation( - ECS::get() - .mCameraGoal->_getDerivedOrientation()); - updateHeightmap(); - } - void setCursorSelectedPos(flecs::entity e, Ogre::Vector3 &position, - Ogre::Quaternion &orientation) - { - StaticGeometryModule::getItemPositionAndRotation(e, position, - orientation); - selected_x = TerrainModule::get_img_x(position.x); - selected_y = TerrainModule::get_img_y(position.z); - ECS::get().sceneNode->_setDerivedPosition( - position); - ECS::get().sceneNode->_setDerivedOrientation( - orientation); - } - void setCameraSelectedPos(flecs::entity e) - { - Ogre::Vector3 cursorPos; - Ogre::Quaternion cursorOrientation; - setCursorSelectedPos(e, cursorPos, cursorOrientation); - Ogre::Vector3 cameraPos = - ECS::get().mCameraPivot->_getDerivedPosition(); - Ogre::Vector3 cameraOffset = - cursorOrientation * Ogre::Vector3::UNIT_Z * 30.0f; - cameraPos.x = cursorPos.x; - cameraPos.z = cursorPos.z; - cameraPos += cameraOffset; - cameraPos.y = - TerrainModule::get_height( - ECS::get().mTerrainGroup, cameraPos) + - 10.0f; - if (keepCameraAbove) { - if (cameraPos.y < 0.0f) - cameraPos.y = 10.0f; - } else { - Ogre::TerrainGroup *tg = - ECS::get().mTerrainGroup; - long x, y; - tg->convertWorldPositionToTerrainSlot(cameraPos, &x, - &y); - if (tg->getTerrain(x, y) && - tg->getTerrain(x, y)->isLoaded()) { - float height = - tg->getHeightAtWorldPosition(cameraPos); - cameraPos.y = height + 10.0f; - } - } - ECS::get().mCameraPivot->_setDerivedPosition(cameraPos); - ECS::get().mCameraPivot->_setDerivedOrientation( - cursorOrientation); - cameraPos = - ECS::get().mCameraGoal->_getDerivedPosition(); - ECS::get().mCameraNode->_setDerivedPosition(cameraPos); - ECS::get().mCameraNode->_setDerivedOrientation( - ECS::get() - .mCameraGoal->_getDerivedOrientation()); - updateHeightmap(); - } - void setSurfaceLevel(int actualSize, int center_x, int center_y, - float level) - { - int i, j; - float original = level; - if (actualSize == 1) - level += 0.4f; - else if (actualSize == 2) - level += 0.35f; - original = Ogre::Math::Clamp(original, 0.0f, 1.0f); - for (i = -actualSize; i < actualSize + 1; i++) - for (j = -actualSize; j < actualSize + 1; j++) { - if (i * i + j * j > actualSize * actualSize) - continue; - if (center_x + j < 0 || - center_x + j >= worldMap->getWidth()) - continue; - if (center_y + i < 0 || - center_y + i >= worldMap->getHeight()) - continue; - Ogre::ColourValue cv = - worldMapImage.getColourAt( - center_x + j, center_y + i, 0); - cv.r = original; - worldMapImage.setColourAt(cv, center_x + j, - center_y + i, 0); - } - updateWorldTexture(); - updateHeightmap(); - } - void setHarbourSurface() - { - int base_size = 3; - float base_height = 0.517f; - float base_step = 0.1f; - float deep = 0.25f; - float shallow = 0.35f; - float maxStep = 600.0f; - Ogre::Vector3 basePos = - ECS::get().sceneNode->_getDerivedPosition(); - Ogre::Quaternion baseRot = - ECS::get() - .sceneNode->_getDerivedOrientation(); - float baseOffset = 200.0f; - Ogre::Vector3 stepOffset = - baseRot * Ogre::Vector3::NEGATIVE_UNIT_Z * baseOffset; - std::vector heights = { deep, shallow, base_height, - base_height + base_step, - base_height + base_step * 2.0f }; - int step_count = 0; - for (float height : heights) { - float localStep = 0.0f; - Ogre::Vector3 currentPosition = - basePos + stepOffset * (float)step_count - - stepOffset * 0.5f; - float goTill = maxStep; - if (step_count < 2) - goTill += 420.0f; - while (localStep < - goTill - 150.0f * (float)step_count) { - localStep += baseOffset; - currentPosition += stepOffset; - int center_x = TerrainModule::get_img_x( - currentPosition.x); - int center_y = TerrainModule::get_img_y( - currentPosition.z); - int size = base_size + 10 - step_count * 2; - if (step_count < 2) - size = base_size + 14 - step_count * 2; - if (step_count == 0) - size += 1; - - setSurfaceLevel(size, center_x, center_y, - height); - } - step_count++; - } - } - static void to_json(nlohmann::json &j, const Ogre::Vector3 &position) - { - j["x"] = position.x; - j["y"] = position.y; - j["z"] = position.z; - } - float setLevelValue = 0.0f; - float riseLowerChange = 0.0f; - enum { - COMMAND_NONE, - COMMAND_RISELOWER, - COMMAND_RISELOWER2, - COMMAND_SMOOTH, - COMMAND_SETLEVEL - }; - int command; - void heightmapMenu() - { - command = COMMAND_NONE; - if (ImGui::Button("Elevate")) { - riseLowerChange = strength; - command = COMMAND_RISELOWER; - } - ImGui::SameLine(); - if (ImGui::Button("Elevate2")) { - riseLowerChange = strength; - command = COMMAND_RISELOWER2; - } - ImGui::SameLine(); - if (ImGui::Button("Lower")) { - riseLowerChange = -strength; - command = COMMAND_RISELOWER; - } - std::pair setLevelCommands[] = { - { "Deepest", 0.0f }, { "Deep", 0.25f }, - { "Shallow1", 0.35f }, { "Shallow2", 0.47f }, - { "Beach", 0.517f }, { "Shore1", 0.536f }, - { "Shore2", 0.556f }, { "Shore3", 0.586f }, - { "Shore4", 0.606f }, { "Shore5", 0.626f }, - { "Shore6", 0.646f }, { "Highest", 1.0f }, - }; - int buttonCounter = 0; - for (const auto &mb : setLevelCommands) { - if (ImGui::SmallButton(mb.first.c_str())) { - setLevelValue = mb.second; - command = COMMAND_SETLEVEL; - } - if ((buttonCounter & 3) != 0) - ImGui::SameLine(); - buttonCounter++; - } - ImGui::Spacing(); - if (ImGui::Button("Smooth")) { - command = COMMAND_SMOOTH; - } - ImGui::Separator(); - if (ImGui::MenuItem("Save heightmap")) { - updateWorldTexture(); - updateHeightmap(); - TerrainModule::save_heightmap(); - } - } - void executeCommands() - { - switch (command) { - case COMMAND_RISELOWER: { - int actualSize = 1 + size * 2; - int i, j; - for (i = -actualSize; i < actualSize + 1; i++) - for (j = -actualSize; j < actualSize + 1; j++) { - if (i * i + j * j > - actualSize * actualSize) - continue; - if (selected_x + j < 0 || - selected_x + j >= - worldMap->getWidth()) - continue; - if (selected_y + i < 0 || - selected_y + i >= - worldMap->getHeight()) - continue; - Ogre::ColourValue cv = - worldMapImage.getColourAt( - selected_x + j, - selected_y + i, 0); - float original = cv.r; - original += riseLowerChange; - original = Ogre::Math::Clamp( - original, 0.0f, 1.0f); - cv.r = original; - worldMapImage.setColourAt( - cv, selected_x + j, - selected_y + i, 0); - } - updateWorldTexture(); - updateHeightmap(); - - } break; - case COMMAND_RISELOWER2: { - int actualSize = 1 + size * 2; - int i, j; - Ogre::ColourValue maxcv = worldMapImage.getColourAt( - selected_x, selected_y, 0); - for (i = -actualSize; i < actualSize + 1; i++) - for (j = -actualSize; j < actualSize + 1; j++) { - if (i * i + j * j > - actualSize * actualSize) - continue; - if (selected_x + j < 0 || - selected_x + j >= - worldMap->getWidth()) - continue; - if (selected_y + i < 0 || - selected_y + i >= - worldMap->getHeight()) - continue; - float actualStrength = - riseLowerChange / - (1.0f + (float)(i * i + j * j)); - Ogre::ColourValue cv = - worldMapImage.getColourAt( - selected_x + j, - selected_y + i, 0); - float original = - maxcv.r + actualStrength; - original = Ogre::Math::Clamp( - original, 0.0f, 1.0f); - if (cv.r >= original) - continue; - cv.r = original; - worldMapImage.setColourAt( - cv, selected_x + j, - selected_y + i, 0); - } - updateWorldTexture(); - updateHeightmap(); - } break; - case COMMAND_SMOOTH: { - int actualSize = 1 + size * 2; - int i, j, k, l; - for (i = -actualSize; i < actualSize + 1; i++) - for (j = -actualSize; j < actualSize + 1; j++) { - if (i * i + j * j > - actualSize * actualSize) - continue; - if (selected_x + j < 0 || - selected_x + j >= - worldMap->getWidth()) - continue; - if (selected_y + i < 0 || - selected_y + i >= - worldMap->getHeight()) - continue; - int kernel = 3; - float original = 0.0f; - Ogre::ColourValue cv; - float count = 0.0f; - for (k = -kernel; k < kernel + 1; k++) { - if (selected_y + i + k < 0 || - selected_y + i + k >= - worldMap->getHeight()) - continue; - for (l = -kernel; - l < kernel + 1; l++) { - if (selected_x + j + l < - 0 || - selected_x + j + - l >= - worldMap->getWidth()) - continue; - cv = worldMapImage.getColourAt( - selected_x + j, - selected_y + i, - 0); - original += cv.r; - count += 1.0f; - } - } - original /= count; - cv = worldMapImage.getColourAt( - selected_x + j, selected_y + i, - 0); - original = Ogre::Math::Clamp( - original, 0.0f, 1.0f); - cv.r = original; - worldMapImage.setColourAt( - cv, selected_x + j, - selected_y + i, 0); - } - updateWorldTexture(); - updateHeightmap(); - } break; - case COMMAND_SETLEVEL: { - int actualSize = 1 + size * 2; - int i, j; - float original = setLevelValue; - original = Ogre::Math::Clamp(original, 0.0f, 1.0f); - for (i = -actualSize; i < actualSize + 1; i++) - for (j = -actualSize; j < actualSize + 1; j++) { - if (i * i + j * j > - actualSize * actualSize) - continue; - if (selected_x + j < 0 || - selected_x + j >= - worldMap->getWidth()) - continue; - if (selected_y + i < 0 || - selected_y + i >= - worldMap->getHeight()) - continue; - Ogre::ColourValue cv = - worldMapImage.getColourAt( - selected_x + j, - selected_y + i, 0); - cv.r = original; - worldMapImage.setColourAt( - cv, selected_x + j, - selected_y + i, 0); - } - updateWorldTexture(); - updateHeightmap(); - - } break; - } - } - void displayItems() - { - std::pair selected_item; - std::list > items; - StaticGeometryModule::getItemsProperties(&items); - bool item_is_selected = false; - for (const auto &item : items) { - nlohmann::json j = nlohmann::json::parse(item.second); - Ogre::String label = Ogre::StringConverter::toString( - item.first.id()); - label += ":" + j["type"].get(); - if (ImGui::SmallButton(label.c_str())) { /* select */ - selected_item = item; - item_is_selected = true; - } - Items::showItemButtons(item); - } - if (item_is_selected) - setCameraSelectedPos(selected_item.first); - for (const auto &item : items) - Items::showItemPopup(item); - } - void displayInfo() - { - ImGui::Text("Position: %d %d", pos_x, pos_y); - ImGui::Text("Selected Position: %d %d", selected_x, selected_y); - ImGui::Text("Height: %f", - worldMapImage.getColourAt(pos_x, pos_y, 0).r); - ImGui::Text( - "Selected height: %f", - worldMapImage.getColourAt(selected_x, selected_y, 0).r); - { - Ogre::Vector3 position = - ECS::get() - .sceneNode->_getDerivedPosition(); - ImGui::Text("Cursor position %f %f %f", position.x, - position.y, position.z); - } - } - void worldMapView() - { - bool update_cursor_position = false; - bool update_cursor_height = false; - bool update_cursor_angle = false; - OgreAssert(TerrainModule::get_img_x(0) == - worldMap->getWidth() / 2, - "get_img_x"); - OgreAssert(TerrainModule::get_img_y(0) == - worldMap->getHeight() / 2, - "get_img_x"); - OgreAssert(TerrainModule::get_world_x(worldMap->getWidth() / - 2) == 0.0f, - "get_world_x"); - OgreAssert(TerrainModule::get_world_y(worldMap->getHeight() / - 2) == 0.0f, - "get_world_y"); - if (ECS::get().sceneNode) { - Ogre::Vector3 worldPos = - ECS::get() - .sceneNode->_getDerivedPosition(); - selected_x = TerrainModule::get_img_x(worldPos.x); - selected_y = TerrainModule::get_img_y(worldPos.z); - locationSelected = true; - OgreAssert(selected_x >= 0 && - selected_x < worldMap->getWidth(), - "mix width"); - OgreAssert(selected_y >= 0 && - selected_y < worldMap->getHeight(), - "mix height"); - ECS::get() - .mTerrainGroup - ->convertWorldPositionToTerrainSlot( - worldPos, &slot_x, &slot_y); - } - ImGui::SetNextWindowSizeConstraints(ImVec2(512 + 20, 512 + 20), - ImVec2(768, 768)); - // ImGui::SetNextWindowScroll( - // ImVec2(worldMap->getWidth(), worldMap->getHeight())); - ImGui::Begin("WorldMap...", nullptr, ImGuiWindowFlags_MenuBar); - if (ImGui::BeginMenuBar()) { - if (ImGui::BeginMenu("Create")) { - Items::createItemsMenu(); - ImGui::EndMenu(); - } - if (ImGui::BeginMenu("Heightmap")) { - if (ImGui::MenuItem("Update terrain")) - ECS::get() - .mTerrainGroup->update(false); - ImGui::Separator(); - ImGui::SliderFloat("Strength...", &strength, - 0.0f, 1.0f); - ImGui::SliderInt("Size", &size, 0, 32); - ImGui::Separator(); - heightmapMenu(); - ImGui::EndMenu(); - } - if (ImGui::BeginMenu("Cursor")) { - if (ImGui::MenuItem("Update cursor position")) - update_cursor_height = true; - Ogre::Vector3 cursorPosition = - ECS::get() - .sceneNode - ->_getDerivedPosition(); - bool modified = false; - if (ImGui::SmallButton("X-1")) { - cursorPosition.x -= 1.0f; - modified = true; - } - ImGui::SameLine(); - if (ImGui::SmallButton("X+1")) { - cursorPosition.x += 1.0f; - modified = true; - } - ImGui::SameLine(); - if (ImGui::SmallButton("Y-1")) { - cursorPosition.y -= 1.0f; - modified = true; - } - ImGui::SameLine(); - if (ImGui::SmallButton("Y+1")) { - cursorPosition.y += 1.0f; - modified = true; - } - ImGui::SameLine(); - if (ImGui::SmallButton("Z-1")) { - cursorPosition.z -= 1.0f; - modified = true; - } - ImGui::SameLine(); - if (ImGui::SmallButton("Z+1")) { - cursorPosition.z += 1.0f; - modified = true; - } - ImGui::EndMenu(); - if (modified) { - ECS::get() - .sceneNode->_setDerivedPosition( - cursorPosition); - modified = false; - } - } - if (ImGui::BeginMenu("Furniture")) { - if (ImGui::BeginMenu("Create New Furniture")) { - static char nameBuffer[32]; - static int current_mesh = 0; - static std::vector - glb_names; - const std::vector &groups = - Ogre::ResourceGroupManager:: - getSingleton() - .getResourceGroups(); - if (glb_names.size() == 0) { - int i; - glb_names.push_back(""); - for (i = 0; i < groups.size(); - i++) { - std::vector names = - *Ogre::ResourceGroupManager::getSingleton() - .findResourceNames( - groups[i], - "furniture-*.glb"); - glb_names.insert( - glb_names.end(), - names.begin(), - names.end()); - } - } - ImGui::InputText( - "Furniture Name", nameBuffer, - IM_ARRAYSIZE(nameBuffer)); - if (glb_names.size() > 0) { - if (ImGui::BeginCombo( - "Furniture Mesh", - glb_names[current_mesh] - .c_str())) { - int i; - for (i = 0; - i < - glb_names.size(); - i++) { - bool isSelected = - i == - current_mesh; - if (ImGui::Selectable( - (glb_names[i] + - "##select" + - Ogre::StringConverter:: - toString( - i)) - .c_str(), - isSelected)) { - current_mesh = - i; - } - if (isSelected) - ImGui::SetItemDefaultFocus(); - } - ImGui::EndCombo(); - } - } else - ImGui::Text( - "No furniture meshes found"); - if (ImGui::MenuItem( - "Create Furniture")) { - } - ImGui::EndMenu(); - } - if (ImGui::BeginMenu("Edit Furniture")) { - if (ImGui::MenuItem("Edit Furniture")) { - } - ImGui::EndMenu(); - } - ImGui::EndMenu(); - } - ImGui::EndMenuBar(); - } - ImGui::Spacing(); - ImGui::BeginChild("WorldMap...", ImVec2(480, 480), - ImGuiChildFlags_None, - ImGuiWindowFlags_HorizontalScrollbar); - ImGui::Spacing(); - Ogre::ResourceHandle hdl = worldMap->getHandle(); - int w = worldMap->getWidth(); - int h = worldMap->getHeight(); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, - ImVec2((float)5, (float)5)); - ImGui::ImageButton("WorldMapPress", (ImTextureID)hdl, - ImVec2(w, h)); - ImVec2 mouse_absolute_pos = ImGui::GetMousePos(); - ImVec2 item_absolute_pos = ImGui::GetItemRectMin(); - top_x = item_absolute_pos.x + 5; - top_y = item_absolute_pos.y + 5; - bool hovered = false; - if (ImGui::IsItemHovered()) { - ImVec2 mouse_absolute_pos = ImGui::GetMousePos(); - ImVec2 item_absolute_pos = ImGui::GetItemRectMin(); - pos_x = mouse_absolute_pos.x - item_absolute_pos.x - 5; - pos_y = mouse_absolute_pos.y - item_absolute_pos.y - 5; - hovered = true; - } - pos_x = Ogre::Math::Clamp(pos_x, 0, - (int)worldMap->getWidth() - 1); - pos_y = Ogre::Math::Clamp(pos_y, 0, - (int)worldMap->getHeight() - 1); - if (pos_x < 0) - pos_x = 0; - if (pos_x >= worldMap->getWidth()) - pos_x = worldMap->getWidth() - 1; - if (pos_y < 0) - pos_y = 0; - if (pos_y >= worldMap->getHeight()) - pos_y = worldMap->getHeight() - 1; - if (ImGui::IsItemActivated()) { - locationSelected = true; - selected_x = pos_x; - selected_y = pos_y; - OgreAssert(selected_x >= 0 && - selected_x < worldMap->getWidth(), - "mix width"); - OgreAssert(selected_y >= 0 && - selected_y < worldMap->getHeight(), - "mix height"); - setCameraPos(); - std::cout << "worldClickPos: " << pos_x << " " << pos_y - << std::endl; - } - ImDrawList *draw_list = ImGui::GetWindowDrawList(); - draw_list->AddCircleFilled(ImVec2(top_x + pos_x, top_y + pos_y), - 1.0f, IM_COL32(0, 255, 0, 255)); - { - std::list positions; - StaticGeometryModule::getItemPositions(&positions); - for (auto pos : positions) { - int item_x = TerrainModule::get_img_x(pos.x); - int item_y = TerrainModule::get_img_y(pos.z); - draw_list->AddCircleFilled( - ImVec2(top_x + item_x, top_y + item_y), - 3.0f, IM_COL32(255, 255, 0, 255)); - } - } - if (locationSelected) - draw_list->AddCircleFilled( - ImVec2(top_x + selected_x, top_y + selected_y), - 4.0f, IM_COL32(64, 255, 64, 255)); - { - Ogre::Vector3 cursorPos = - ECS::get() - .sceneNode->_getDerivedPosition(); - int cursor_x = TerrainModule::get_img_x(cursorPos.x); - int cursor_y = TerrainModule::get_img_y(cursorPos.z); - draw_list->AddCircleFilled( - ImVec2(top_x + cursor_x, top_y + cursor_y), - 4.0f, IM_COL32(255, 64, 64, 128)); - } - ImGui::PopStyleVar(); - ImGui::Spacing(); - ImGui::EndChild(); - ImGui::SameLine(); - ImGui::BeginChild("WorldMap...", ImVec2(64, 480), - ImGuiChildFlags_None, - ImGuiWindowFlags_HorizontalScrollbar); - ImGui::EndChild(); - ImGui::Spacing(); - ImGui::BeginChild("WorldMap Bottom...", ImVec2(0, 0)); - if (ImGui::CollapsingHeader("Display Info")) { - displayInfo(); - if (ImGui::SliderFloat("Cursor Angle...", &cursorAngle, - -180.0f, 180.0f)) - update_cursor_angle = true; - } - displayItems(); - ImGui::EndChild(); - ImGui::Spacing(); - ImGui::End(); - if (update_cursor_height || update_cursor_angle) { - if (update_cursor_height) { - Ogre::Vector3 position = - ECS::get() - .sceneNode - ->_getDerivedPosition(); - position.y = ECS::get() - .mTerrainGroup - ->getHeightAtWorldPosition( - position); - ECS::get() - .sceneNode->_setDerivedPosition( - position); - } - if (update_cursor_angle) - ECS::get() - .sceneNode->_setDerivedOrientation( - Ogre::Quaternion( - Ogre::Degree( - cursorAngle), - Ogre::Vector3::UNIT_Y)); - } - executeCommands(); - } - void panel() - { - ImVec2 size = ImGui::GetMainViewport()->Size; - float window_width = size.x * 0.2f; - if (window_width > panel_width) - window_width = panel_width; - float window_height = size.y * 0.5f - 20; - ImGui::SetNextWindowPos(ImVec2(size.x - window_width, 20), - ImGuiCond_Always); - ImGui::SetNextWindowSize(ImVec2(window_width, window_height), - ImGuiCond_Always); - // ImGui::Begin("Dumb and Stupid", &mKbd.gui_active); - ImGui::Begin("Panel..."); - std::deque tree_input_queue, - tree_output_queue; - std::vector tree_list; - tree_input_queue.push_back( - ECS::get() - .get() - .mScnMgr->getRootSceneNode()); - tree_input_queue.push_back(nullptr); - std::set visited; - while (true) { - int new_nodes_count = 0; - while (!tree_input_queue.empty()) { - int child; - Ogre::SceneNode *item = - tree_input_queue.front(); - tree_input_queue.pop_front(); - if (item && visited.find(item) == - visited.end()) { // new node - new_nodes_count++; - tree_output_queue.push_back(item); - visited.insert(item); - const Ogre::Node::ChildNodeMap - &children = item->getChildren(); - for (child = 0; child < children.size(); - child++) { - tree_output_queue.push_back( - static_cast( - children[child])); - tree_output_queue.push_back( - nullptr); - } - } else - tree_output_queue.push_back(item); - } - if (new_nodes_count == 0) - break; - tree_input_queue = tree_output_queue; - tree_output_queue.clear(); - } - tree_list.insert(tree_list.begin(), tree_output_queue.begin(), - tree_output_queue.end()); - int count = 0; - int depth = 0; - std::vector check_depth; - int max_depth = 0; - check_depth.push_back(0); - for (count = 0; count < tree_list.size(); count++) { - int t; - - Ogre::SceneNode *node = tree_list[count]; - if (node && max_depth >= depth) { - Ogre::String name = node->getName(); - if (name.length() == 0) { - name = "Node #" + - Ogre::StringConverter::toString( - count); - } - if (ImGui::TreeNode(name.c_str())) { - check_depth.push_back(max_depth); - max_depth++; - ImGui::Text( - "%s", - (name + "##caption").c_str()); - position_editor(node); - ImGui::Separator(); - orientation_editor(node); - ImGui::Separator(); - if (ImGui::Button("From Cursor")) - std::cout << name - << " From Cursor" - << std::endl; - if (ImGui::Button("To Cursor")) - std::cout << name - << " To Cursor" - << std::endl; - ImGui::Separator(); - ImGui::Text("Attachments"); - attachments_editor(node); - } - } else if (!node && max_depth >= depth) { - max_depth = check_depth.back(); - check_depth.pop_back(); - ImGui::TreePop(); - } - if (tree_list[count]) - depth++; - else - depth--; - } - ImGui::Spacing(); - ImGui::End(); - } - void preview(const Ogre::RenderTargetViewportEvent &evt) - { - int i; - Ogre::ImGuiOverlay::NewFrame(); - if (ECS::get().get().enabled) { - buttons_panel(); - panel(); - if (enableMapEditor) - worldMapView(); - } - } -}; GUIModule::GUIModule(flecs::world &ecs) { ecs.module(); @@ -1753,47 +599,4 @@ GUIModule::GUIModule(flecs::world &ecs) } }); } -EditorGUIModule::EditorGUIModule(flecs::world &ecs) -{ - ecs.module(); - ecs.import (); - ecs.import (); - ecs.component() - .on_add([](GUI &gui) { - gui.enabled = true; - gui.grab = false; - gui.grabChanged = false; - }) - .add(flecs::Singleton); - ecs.component() - .on_add([](EditorGUIData &priv) { - priv.glb_names.clear(); - priv.mGUIListener = nullptr; - priv.mGuiOverlay = nullptr; - }) - .add(flecs::Singleton); - ecs.observer("SupportEditorGUI") - .event(flecs::OnSet) - .without() - .each([](const RenderWindow &window, const App &app, GUI &gui) { - float vpScale = window.dpi / 96 * - window.window->getWidth() / 1600.0f; - Ogre::OverlayManager::getSingleton().setPixelRatio( - vpScale); - std::cout << "Editor GUI configure\n"; - OgreAssert(app.mGuiOverlay, "No ImGUI overlay"); - Ogre::ImGuiOverlay *guiOverlay = app.mGuiOverlay; - EditorGUIListener *guiListener = - new EditorGUIListener(guiOverlay); - guiOverlay->setZOrder(300); - guiOverlay->show(); - guiListener->panel_width = 300.0f; - guiListener->enableEditor = false; - window.window->addListener(guiListener); - - ECS::get().set( - { app.mGuiOverlay, {}, guiListener }); - std::cout << "Editor GUI configure finished\n"; - }); -} } diff --git a/src/gamedata/GUIModule.h b/src/gamedata/GUIModule.h index 3043430..890ea7e 100644 --- a/src/gamedata/GUIModule.h +++ b/src/gamedata/GUIModule.h @@ -6,40 +6,9 @@ namespace OgreBites } namespace ECS { -struct GUI { - bool enabled; - bool grab; - bool grabChanged; - bool narrationBox; - bool mainMenu; - Ogre::String narrationText; - std::vector choices; - int narration_answer; - static void setWindowGrab(bool g = true) - { - ECS::GUI &gui = ECS::get().get_mut(); - if (gui.grab != g) { - gui.grab = g; - gui.grabChanged = true; - ECS::get().modified(); - } - } - static void finish() - { - ECS::GUI &gui = ECS::get().get_mut(); - gui.enabled = false; - gui.mainMenu = false; - gui.narrationBox = false; - ECS::get().modified(); - setWindowGrab(true); - } -}; struct GUIModule { flecs::entity ui_wait; GUIModule(flecs::world &ecs); }; -struct EditorGUIModule { - EditorGUIModule(flecs::world &ecs); -}; } -#endif \ No newline at end of file +#endif diff --git a/src/gamedata/GUIModuleCommon.h b/src/gamedata/GUIModuleCommon.h new file mode 100644 index 0000000..41aaf20 --- /dev/null +++ b/src/gamedata/GUIModuleCommon.h @@ -0,0 +1,36 @@ +#ifndef __GUIMODULECOMMON_H__ +#define __GUIMODULECOMMON_H__ +namespace ECS +{ + +struct GUI { + bool enabled; + bool grab; + bool grabChanged; + bool narrationBox; + bool mainMenu; + Ogre::String narrationText; + std::vector choices; + int narration_answer; + static void setWindowGrab(bool g = true) + { + ECS::GUI &gui = ECS::get().get_mut(); + if (gui.grab != g) { + gui.grab = g; + gui.grabChanged = true; + ECS::get().modified(); + } + } + static void finish() + { + ECS::GUI &gui = ECS::get().get_mut(); + gui.enabled = false; + gui.mainMenu = false; + gui.narrationBox = false; + ECS::get().modified(); + setWindowGrab(true); + } +}; +} + +#endif // GUIMODULECOMMON_H diff --git a/src/gamedata/GameData.cpp b/src/gamedata/GameData.cpp index 60e098a..661d22c 100644 --- a/src/gamedata/GameData.cpp +++ b/src/gamedata/GameData.cpp @@ -10,7 +10,9 @@ #include "WaterModule.h" #include "TerrainModule.h" #include "SunModule.h" +#include "GUIModuleCommon.h" #include "GUIModule.h" +#include "EditorGUIModule.h" #include "LuaData.h" #include "WorldMapModule.h" #include "BoatModule.h" @@ -20,6 +22,7 @@ #include "EventModule.h" #include "CharacterManagerModule.h" #include "VehicleManagerModule.h" +#include "PlayerActionModule.h" #include "AppModule.h" #include "world-build.h" @@ -42,7 +45,7 @@ void setup_minimal() ecs.component().add(flecs::Singleton); } void setupExteriorScene(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode, - Ogre::Camera *camera, Ogre::RenderWindow *window) + Ogre::Camera *camera, Ogre::RenderWindow *window) { std::cout << "Setup GameData\n"; setup_minimal(); @@ -53,11 +56,13 @@ void setupExteriorScene(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode, ecs.import (); ecs.import (); ecs.import (); - ecs.import (); + ecs.import (); ecs.import (); ecs.import (); ecs.import (); ecs.import (); + ecs.import (); + ecs.add(); ecs.system("UpdateDelta") .kind(flecs::OnUpdate) @@ -90,7 +95,7 @@ void setupExteriorScene(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode, #endif }); ecs.set({ scnMgr, 0.0f, 5.0f, (int)window->getWidth(), - (int)window->getHeight(), false }); + (int)window->getHeight(), false }); ecs.set({ cameraNode, camera, false }); ecs.add(); ecs.add(); @@ -168,7 +173,7 @@ void setupInventoryScene(Ogre::SceneManager *scnMgr, } void setupEditor(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode, - Ogre::Camera *camera, Ogre::RenderWindow *window) + Ogre::Camera *camera, Ogre::RenderWindow *window) { std::cout << "Setup Editor\n"; setup_minimal(); @@ -217,7 +222,7 @@ void setupEditor(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode, #endif }); ecs.set({ scnMgr, 0.0f, 5.0f, (int)window->getWidth(), - (int)window->getHeight(), false }); + (int)window->getHeight(), false }); ecs.set({ cameraNode, camera, false }); #if 0 ecs.set({ 0 }); diff --git a/src/gamedata/GameData.h b/src/gamedata/GameData.h index f4b54f6..f0d3298 100644 --- a/src/gamedata/GameData.h +++ b/src/gamedata/GameData.h @@ -6,13 +6,13 @@ namespace ECS extern flecs::entity player; void setup_minimal(); void setupExteriorScene(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode, - Ogre::Camera *camera, Ogre::RenderWindow *window); + Ogre::Camera *camera, Ogre::RenderWindow *window); void setupInteriorScene(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode, Ogre::Camera *camera, Ogre::RenderWindow *window); void setupInventoryScene(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode, Ogre::Camera *camera, Ogre::RenderWindow *window); void setupEditor(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode, - Ogre::Camera *camera, Ogre::RenderWindow *window); + Ogre::Camera *camera, Ogre::RenderWindow *window); void setupEditorAlt(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode, Ogre::Camera *camera, Ogre::RenderWindow *window); void update(float delta); diff --git a/src/gamedata/LuaData.cpp b/src/gamedata/LuaData.cpp index fe385e6..b0fc9ba 100644 --- a/src/gamedata/LuaData.cpp +++ b/src/gamedata/LuaData.cpp @@ -1,7 +1,7 @@ #include #include "GameData.h" #include "Components.h" -#include "GUIModule.h" +#include "GUIModuleCommon.h" #include "PhysicsModule.h" #include "CharacterModule.h" #include "CharacterAnimationModule.h" @@ -911,4 +911,4 @@ LuaModule::LuaModule(flecs::world &ecs) } }); } -} \ No newline at end of file +} diff --git a/src/gamedata/PlayerActionModule.cpp b/src/gamedata/PlayerActionModule.cpp new file mode 100644 index 0000000..4a9bdf3 --- /dev/null +++ b/src/gamedata/PlayerActionModule.cpp @@ -0,0 +1,114 @@ +#include +#include +#include +#include "Components.h" +#include "GameData.h" +#include "PlayerActionModule.h" + +namespace ECS +{ +struct OgreVector3Adaptor { + const std::vector &nodes; + + OgreVector3Adaptor(const std::vector &nodes) + : nodes(nodes) + { + } + + // Required by nanoflann: Number of data points + inline size_t kdtree_get_point_count() const + { + return nodes.size(); + } + + // Required by nanoflann: Returns the distance between the vector and a point + // Using squared distance is standard for performance + inline float kdtree_get_pt(const size_t idx, const size_t dim) const + { + return nodes[idx].position[dim]; + } + + // Optional: bounding box optimization (return false if not implemented) + template bool kdtree_get_bbox(BBOX & /*bb*/) const + { + return false; + } +}; + +typedef nanoflann::KDTreeSingleIndexAdaptor< + nanoflann::L2_Simple_Adaptor, + OgreVector3Adaptor, 3 /* dimensionality */ + > + OgreKDTree; +struct ActionNodeList::indexObject { + OgreVector3Adaptor adaptor; + OgreKDTree index; + indexObject(const std::vector &nodes) + : adaptor(nodes) + , index(3, adaptor, + nanoflann::KDTreeSingleIndexAdaptorParams(10)) + { + } +}; + +PlayerActionModule::PlayerActionModule(flecs::world &ecs) +{ + ecs.module(); + ecs.component() + .on_add([](flecs::entity e, ActionNodeList &alist) { + alist.dirty = true; + alist.nodes.reserve(1000); + }) + .add(flecs::Singleton); +#if 0 + ecs.system("testNodeList") + .kind(flecs::OnUpdate) + .each([](ActionNodeList &list) { + if (list.nodes.size() > 0) { + Ogre::Vector3 queryPos = + ECS::get() + .mCameraNode + ->_getDerivedPosition(); + std::vector points; + list.query(queryPos, points); + for (auto &p : points) + std::cout << p << std::endl + << list.nodes[p].props.dump() + << std::endl; + OgreAssert(points.size() == 0, "got result"); + } + }); +#endif +} + +void ActionNodeList::build() +{ + indexObj = std::make_shared(nodes); + indexObj->index.buildIndex(); + dirty = false; + std::cout << "index built" << std::endl; +} + +bool ActionNodeList::query(const Ogre::Vector3 &position, + std::vector &points) +{ + if (dirty) + build(); + std::vector tmppoints; + std::vector tmpdistances; + points.clear(); + points.reserve(4); + tmppoints.resize(4); + tmpdistances.resize(4); + nanoflann::KNNResultSet resultSet(4); + resultSet.init(tmppoints.data(), tmpdistances.data()); + bool ret = indexObj->index.findNeighbors(resultSet, &position.x, + nanoflann::SearchParameters()); + int i; + for (i = 0; i < resultSet.size(); i++) + if (tmpdistances[i] < 25.0f) + points.push_back(tmppoints[i]); + return ret; +} + +} diff --git a/src/gamedata/PlayerActionModule.h b/src/gamedata/PlayerActionModule.h new file mode 100644 index 0000000..8a611d0 --- /dev/null +++ b/src/gamedata/PlayerActionModule.h @@ -0,0 +1,42 @@ +#ifndef PLAYERACTIONMODULE_H +#define PLAYERACTIONMODULE_H +#include +#include +#include + +namespace ECS { + +struct ActionNodeList { + struct indexObject; + struct ActionNode { + Ogre::String action; + Ogre::String action_text; + Ogre::Vector3 position; + Ogre::Quaternion rotation; + nlohmann::json props; + }; + std::vector nodes; + std::shared_ptr indexObj; + bool dirty; + void build(); + bool query(const Ogre::Vector3 &position, std::vector &points); + int addNode(struct ActionNodeList::ActionNode &node) + { + int index = nodes.size(); + nodes.push_back(node); + dirty = true; + return index; + } + void removeNode(int index) + { + nodes.erase(nodes.begin() + index); + } +}; + +struct PlayerActionModule +{ + PlayerActionModule(flecs::world &ecs); +}; +} + +#endif // PLAYERACTIONMODULE_H diff --git a/src/gamedata/StaticGeometryModule.cpp b/src/gamedata/StaticGeometryModule.cpp index 5b8b6b5..518efea 100644 --- a/src/gamedata/StaticGeometryModule.cpp +++ b/src/gamedata/StaticGeometryModule.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include "Components.h" #include "GameData.h" @@ -20,6 +21,7 @@ namespace ECS static bool itemsLoaded = false; static bool furnitureLoaded = false; +static bool templatesLoaded = false; static std::list > addQueue; StaticGeometryModule::StaticGeometryModule(flecs::world &ecs) { @@ -27,6 +29,32 @@ StaticGeometryModule::StaticGeometryModule(flecs::world &ecs) ecs.component(); ecs.component(); ecs.component(); + ecs.component() + .on_remove([](flecs::entity e, FurnitureInstance &instance) { + if (instance.furniture) { + instance.furniture + ->destroyAllChildrenAndObjects(); + instance.furniture->getCreator() + ->destroySceneNode(instance.furniture); + instance.furniture = nullptr; + } + }) + .on_set([](flecs::entity e, FurnitureInstance &instance) { + if (instance.furniture != + e.get().furniture) { + FurnitureInstance &f = + e.get_mut(); + if (f.furniture) { + f.furniture + ->destroyAllChildrenAndObjects(); + f.furniture->getCreator() + ->destroySceneNode(f.furniture); + } + } + }) + .on_add([](flecs::entity e, FurnitureInstance &instance) { + instance.furniture = nullptr; + }); ecs.component().on_remove([](flecs::entity e, TerrainItemNode &item) { if (item.itemNode) { @@ -42,6 +70,7 @@ StaticGeometryModule::StaticGeometryModule(flecs::world &ecs) } }); ecs.component(); + ecs.component(); ecs.import (); ecs.observer("LoadTerrainItems") .event(flecs::OnSet) @@ -65,6 +94,10 @@ StaticGeometryModule::StaticGeometryModule(flecs::world &ecs) if (!furnitureLoaded) { loadFurniture(); furnitureLoaded = true; + } + if (!templatesLoaded) { + loadTemplates(); + templatesLoaded = true; } std::list > output; while (!addQueue.empty()) { @@ -163,8 +196,48 @@ void StaticGeometryModule::setItemProperties(flecs::entity id, const Ogre::String &StaticGeometryModule::getItemProperties(flecs::entity id) { OgreAssert(id.is_valid(), "bad id"); - return id.get().properties; + return id.get().properties; } + +nlohmann::json templates; +void StaticGeometryModule::loadTemplates() +{ + if (!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup( + "templates.list")) + return; + Ogre::String group = + Ogre::ResourceGroupManager::getSingleton() + .findGroupContainingResource("templates.list"); + Ogre::DataStreamPtr stream = + Ogre::ResourceGroupManager::getSingleton().openResource( + "templates.list", group); + Ogre::String json = stream->getAsString(); + nlohmann::json jtemplates = nlohmann::json::parse(json); + templates = jtemplates; +} + +void StaticGeometryModule::saveTemplates() +{ + Ogre::String path = "resources/buildings/templates.list"; + if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup( + "templates.list")) { + Ogre::String group = + Ogre::ResourceGroupManager::getSingleton() + .findGroupContainingResource("templates.list"); + Ogre::FileInfoListPtr fileInfoList( + Ogre::ResourceGroupManager::getSingleton() + .findResourceFileInfo(group, "templates.list")); + OgreAssert(fileInfoList->size() == 1, + "templates.list should be there and only once"); + path = fileInfoList->at(0).archive->getName() + "/" + + "templates.list"; + Ogre::FileSystemLayer::removeFile(path); + } + std::fstream fout(path.c_str(), std::ios::out); + fout << templates.dump(); + fout.close(); +} + void StaticGeometryModule::saveItems() { Ogre::String path = "resources/buildings/items.list"; @@ -378,6 +451,58 @@ void StaticGeometryModule::destroyItemGeometry(flecs::entity e) { Geometry::destroyItemGeometry(e); } + +nlohmann::json &StaticGeometryModule::getTemplates() +{ + return templates; +} + +void StaticGeometryModule::updateItemGeometry(flecs::entity e) +{ + if (e.has()) + return; + e.add(); + Ogre::Root::getSingleton().getWorkQueue()->addTask([e]() { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + Ogre::Root::getSingleton().getWorkQueue()->addMainThreadTask( + [e]() { + Geometry::updateItemGeometry(e); + e.remove(); + }); + }); +} + +void StaticGeometryModule::addTriangleBufferWork( + const Ogre::String &meshName, Ogre::StaticGeometry *geo, + const Ogre::Vector3 &position, const Ogre::Quaternion &rotation, + const Procedural::TriangleBuffer &tb) +{ + struct WorkData { + Ogre::String meshName; + Ogre::StaticGeometry *geo; + Ogre::Vector3 position; + Ogre::Quaternion rotation; + Procedural::TriangleBuffer tb; + }; + WorkData data = { meshName, geo, position, rotation, tb }; + + Ogre::Root::getSingleton().getWorkQueue()->addTask([captData = std::move( + data)]() { + Ogre::Root::getSingleton().getWorkQueue()->addMainThreadTask( + [captData]() { + Ogre::MeshPtr mesh = + captData.tb.transformToMesh( + captData.meshName); + Ogre::Entity *ent = + ECS::get() + .mScnMgr->createEntity(mesh); + captData.geo->addEntity(ent, captData.position, + captData.rotation); + ECS::get().mScnMgr->destroyEntity( + ent); + }); + }); +} struct TiledMeshes { struct Tile { Ogre::String materialName; @@ -412,13 +537,6 @@ struct TiledMeshes { if (tiles.find(name) == tiles.end()) return; tiles[name].positions.erase(packKey(position)); -#if 0 - auto pos = std::find(tiles[name].positions.begin(), - tiles[name].positions.end(), - packKey(position)); - if (pos != tiles[name].positions.end()) - tiles[name].positions.erase(pos); -#endif } void addTile(const Ogre::String &name, Ogre::MeshPtr mesh) { @@ -435,102 +553,6 @@ struct TiledMeshes { std::shared_ptr( submesh->indexData->clone()), {} }; -#if 0 - std::vector vertices; - std::vector indices; - int count = mesh->getNumSubMeshes(); - int i, j; - int indexCount = 0; - int vertexCount = 0; - int sharedVertexOffset = 0; - for (i = 0; i < count; i++) { - Ogre::SubMesh *submesh = mesh->getSubMesh(i); - indexCount += submesh->indexData->indexCount; - if (submesh->useSharedVertices) - vertexCount += - mesh->sharedVertexData->vertexCount; - else - vertexCount += submesh->vertexData->vertexCount; - } - indices.reserve(indexCount); - vertices.reserve(vertexCount); - size_t currentVertexOffset = 0; - bool added_shared = false; - for (i = 0; i < count; i++) { - Ogre::SubMesh *submesh = mesh->getSubMesh(i); - Ogre::VertexData *vertex_data = - submesh->useSharedVertices ? - mesh->sharedVertexData : - submesh->vertexData; - bool add_vertices = - (submesh->useSharedVertices && !added_shared) || - !submesh->useSharedVertices; - if (add_vertices) { - if (submesh->useSharedVertices) - sharedVertexOffset = vertices.size(); - const Ogre::VertexDeclaration *decl = - vertex_data->vertexDeclaration; - const Ogre::VertexBufferBinding *bind = - vertex_data->vertexBufferBinding; - const Ogre::VertexElement *position_element = - decl->findElementBySemantic( - Ogre::VES_POSITION); - if (!position_element) - continue; - Ogre::HardwareVertexBufferSharedPtr vbuf = - bind->getBuffer( - position_element->getSource()); - unsigned char *vertex_buffer = static_cast< - unsigned char *>(vbuf->lock( - Ogre::HardwareBuffer::HBL_READ_ONLY)); - int vertexSize = vbuf->getVertexSize(); - for (j = 0; j < vertex_data->vertexCount; j++) { - float *position_data; - position_element - ->baseVertexPointerToElement( - vertex_buffer, - &position_data); - vertices.push_back( - { position_data[0], - position_data[1], - position_data[2] }); - vertex_buffer += vertexSize; - } - if (submesh->useSharedVertices) - added_shared = true; - vbuf->unlock(); - } - Ogre::HardwareIndexBufferSharedPtr ibuf = - submesh->indexData->indexBuffer; - size_t numIndices = submesh->indexData->indexCount; - size_t vertexOffset = submesh->useSharedVertices ? - sharedVertexOffset : - currentVertexOffset; - if (ibuf->getType() == - Ogre::HardwareIndexBuffer::IT_32BIT) { - unsigned int *pIndices = static_cast< - unsigned int *>(ibuf->lock( - Ogre::HardwareBuffer::HBL_READ_ONLY)); - for (j = 0; j < numIndices; j++) { - indices.push_back( - (uint32_t)pIndices[j] + - vertexOffset); - } - ibuf->unlock(); - } else { - unsigned short *pIndices = static_cast< - unsigned short *>(ibuf->lock( - Ogre::HardwareBuffer::HBL_READ_ONLY)); - for (j = 0; j < numIndices; j++) { - indices.push_back( - (uint32_t)pIndices[j] + - vertexOffset); - } - ibuf->unlock(); - } - currentVertexOffset = vertices.size(); - } -#endif } struct buildSettings { Ogre::String meshName; @@ -766,10 +788,8 @@ out:; config.advanced.preventBreakingLines = true; config.createGeneratedLodLevel(2, 0.15f); config.createGeneratedLodLevel(20, 0.49f); -#if 0 config.createGeneratedLodLevel(15, 0.49f); config.createGeneratedLodLevel(150, 0.75f); -#endif config.advanced.useBackgroundQueue = false; Ogre::MeshLodGenerator::getSingleton().generateLodLevels( config); diff --git a/src/gamedata/StaticGeometryModule.h b/src/gamedata/StaticGeometryModule.h index f9c1c10..f996adb 100644 --- a/src/gamedata/StaticGeometryModule.h +++ b/src/gamedata/StaticGeometryModule.h @@ -3,6 +3,10 @@ #include #include #include +namespace Procedural +{ +class TriangleBuffer; +} namespace Ogre { struct LodConfig; @@ -29,6 +33,10 @@ struct FurnitureItem { Ogre::String properties; std::vector tags; }; +struct FurnitureInstance { + Ogre::SceneNode *furniture; +}; +struct GeometryUpdateItem {}; struct TownCollider {}; @@ -42,7 +50,9 @@ struct StaticGeometryModule { static void setItemProperties(flecs::entity id, Ogre::String properties); static const Ogre::String &getItemProperties(flecs::entity id); - static void saveItems(); + static void loadTemplates(); + static void saveTemplates(); + static void saveItems(); static void loadItems(); static void saveFurniture(); static void loadFurniture(); @@ -56,6 +66,13 @@ struct StaticGeometryModule { std::list > *items); static void createItemGeometry(flecs::entity e); static void destroyItemGeometry(flecs::entity e); + static nlohmann::json &getTemplates(); + static void updateItemGeometry(flecs::entity e); + static void addTriangleBufferWork(const Ogre::String &meshName, + Ogre::StaticGeometry *geo, + const Ogre::Vector3 &position, + const Ogre::Quaternion &rotation, + const Procedural::TriangleBuffer &tb); }; } #endif diff --git a/src/gamedata/items/CMakeLists.txt b/src/gamedata/items/CMakeLists.txt index 740dc0b..d765a64 100644 --- a/src/gamedata/items/CMakeLists.txt +++ b/src/gamedata/items/CMakeLists.txt @@ -4,7 +4,7 @@ find_package(Bullet REQUIRED) find_package(nlohmann_json REQUIRED) find_package(OgreProcedural REQUIRED CONFIG) add_library(items STATIC items.cpp harbour.cpp temple.cpp town.cpp) -target_include_directories(items PUBLIC .) +target_include_directories(items PUBLIC . ${CMAKE_SOURCE_DIR}/src/FastNoiseLite) target_link_libraries(items PRIVATE flecs::flecs_static nlohmann_json::nlohmann_json @@ -13,4 +13,4 @@ target_link_libraries(items PRIVATE OgreBites editor physics -) \ No newline at end of file +) diff --git a/src/gamedata/items/items.cpp b/src/gamedata/items/items.cpp index b5abe58..0945998 100644 --- a/src/gamedata/items/items.cpp +++ b/src/gamedata/items/items.cpp @@ -22,6 +22,22 @@ namespace ECS { namespace Items { +void runScriptsForAllTowns() +{ + std::pair selected_item; + std::list > items; + StaticGeometryModule::getItemsProperties(&items); + for (const auto &item : items) { + nlohmann::json j = nlohmann::json::parse(item.second); + Ogre::String itemType = j["type"].get(); + if (itemType == "town") { + Items::runAllScriptsForTown(item.first); + if (item.first.has()) + Geometry::updateItemGeometry(item.first); + } + } + StaticGeometryModule::saveItems(); +} void showItemPopup(const std::pair &item) { Ogre::String popupLabel = @@ -131,8 +147,7 @@ void showItemPopup(const std::pair &item) orientation; item.first.modified(); StaticGeometryModule::saveItems(); - StaticGeometryModule::destroyItemGeometry(item.first); - StaticGeometryModule::createItemGeometry(item.first); + StaticGeometryModule::updateItemGeometry(item.first); } if (itemType == "harbour") createHarbourPopup(item); @@ -194,6 +209,7 @@ namespace Geometry { void setupLods(Ogre::LodConfig &config) { + int count = 0; // config.advanced.useCompression = false; config.advanced.useVertexNormals = true; config.advanced.preventPunchingHoles = true; @@ -204,7 +220,28 @@ void setupLods(Ogre::LodConfig &config) // config.createGeneratedLodLevel(200, 0.50f); config.createGeneratedLodLevel(500, 0.85f); config.advanced.useBackgroundQueue = false; - Ogre::MeshLodGenerator::getSingleton().generateLodLevels(config); + std::cout << "mesh name: " << config.mesh->getName() << std::endl; + bool crash = false; + for (count = 0; count < config.mesh->getSubMeshes().size(); count++) { + Ogre::SubMesh *submesh = config.mesh->getSubMeshes()[count]; + std::cout << "unprocessed submesh: " << count << " " << submesh + << std::endl; + if (submesh) + submesh->parent = config.mesh.get(); + else + crash = true; + } + Ogre::MeshLodGenerator::getSingleton().generateLodLevels(config); + for (count = 0; count < config.mesh->getSubMeshes().size(); count++) { + Ogre::SubMesh *submesh = config.mesh->getSubMeshes()[count]; + std::cout << "submesh: " << count << " " << submesh + << std::endl; + if (submesh) + submesh->parent = config.mesh.get(); + else + crash = true; + } + OgreAssert(!crash, "no submesh"); } Ogre::StaticGeometry *createStaticGeometry(flecs::entity e) @@ -294,6 +331,14 @@ void destroyItemGeometry(flecs::entity e) #endif e.remove(); } +void updateItemGeometry(flecs::entity e) +{ + OgreAssert(e.has(), "not terrain item"); + if (e.has()) + destroyItemGeometry(e); + createItemGeometry(e); +} + flecs::entity createMeshGeometry(const Ogre::String &meshName, flecs::entity parente, Ogre::SceneNode *sceneNode, @@ -322,7 +367,7 @@ flecs::entity createMeshGeometry(const Ogre::String &meshName, .set({ sceneNode, geo }); JoltPhysicsWrapper::getSingleton().addBody(id, JPH::EActivation::Activate); - return e; + return e; } } diff --git a/src/gamedata/items/items.h b/src/gamedata/items/items.h index 48654e3..747bbce 100644 --- a/src/gamedata/items/items.h +++ b/src/gamedata/items/items.h @@ -9,6 +9,7 @@ namespace Items void showItemPopup(const std::pair &item); void showItemButtons(const std::pair &item); void createItemsMenu(); +void runScriptsForAllTowns(); } namespace Geometry { @@ -53,6 +54,7 @@ struct harbourMaker { }; void createItemGeometry(flecs::entity e); void destroyItemGeometry(flecs::entity e); +void updateItemGeometry(flecs::entity e); flecs::entity createMeshGeometry(const Ogre::String &meshName, flecs::entity parente, Ogre::SceneNode *sceneNode, diff --git a/src/gamedata/items/town.cpp b/src/gamedata/items/town.cpp index bf327cb..5251a97 100644 --- a/src/gamedata/items/town.cpp +++ b/src/gamedata/items/town.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "Components.h" #include "GameData.h" #include "EditorGizmoModule.h" @@ -16,6 +17,7 @@ #include "physics.h" #include "PhysicsModule.h" #include "LuaData.h" +#include "PlayerActionModule.h" #include "items.h" #include "town.h" @@ -175,6 +177,7 @@ struct CellsScript { nlohmann::json &lot; nlohmann::json &cells; nlohmann::json &fucells; + FastNoiseLite noise; CellsScript(const Ogre::String &cellScript, nlohmann::json &lot) : L(luaL_newstate()) , currentX(0) @@ -185,6 +188,27 @@ struct CellsScript { , cells(lot["cells"]) , fucells(lot["furniture_cells"]) { + noise.SetNoiseType(FastNoiseLite::NoiseType_OpenSimplex2); + noise.SetSeed(310); + noise.SetFrequency(0.01f); + noise.SetFractalType(FastNoiseLite::FractalType_FBm); + noise.SetFractalOctaves(4); + noise.SetFractalLacunarity(2.0f); + noise.SetFractalGain(0.5f); + luaL_requiref(L, "table", luaopen_table, 1); + lua_pop(L, 1); + lua_pushlightuserdata(L, this); + lua_pushcclosure( + L, + [](lua_State *L) -> int { + CellsScript *_this = static_cast( + lua_touserdata(L, lua_upvalueindex(1))); + int seed = lua_tointeger(L, 1); + _this->noise.SetSeed(seed); + return 0; + }, + 1); + lua_setglobal(L, "seed"); lua_pushlightuserdata(L, this); lua_pushcclosure( L, @@ -419,6 +443,42 @@ struct CellsScript { }, 1); lua_setglobal(L, "create_exterior"); + lua_pushlightuserdata(L, this); + lua_pushcclosure( + L, + [](lua_State *L) -> int { + CellsScript *_this = static_cast( + lua_touserdata(L, lua_upvalueindex(1))); + float angle = lua_tonumber(L, 1); + _this->lot["angle"] = angle; + return 0; + }, + 1); + lua_setglobal(L, "set_lot_angle_degrees"); + lua_pushlightuserdata(L, this); + lua_pushcclosure( + L, + [](lua_State *L) -> int { + CellsScript *_this = static_cast( + lua_touserdata(L, lua_upvalueindex(1))); + float width = lua_tonumber(L, 1); + _this->lot["width"] = width; + return 0; + }, + 1); + lua_setglobal(L, "set_lot_width"); + lua_pushlightuserdata(L, this); + lua_pushcclosure( + L, + [](lua_State *L) -> int { + CellsScript *_this = static_cast( + lua_touserdata(L, lua_upvalueindex(1))); + float depth = lua_tonumber(L, 1); + _this->lot["depth"] = depth; + return 0; + }, + 1); + lua_setglobal(L, "set_lot_depth"); } static std::string trim(const std::string &str) { @@ -713,12 +773,21 @@ struct CellsScript { } } } - nlohmann::json select_furniture(const std::vector &tags) + nlohmann::json select_furniture(const std::vector &tags, + const std::vector ¬ags) { nlohmann::json adata = nlohmann::json::array(); ECS::get().query_builder().build().each( [&](flecs::entity e, const FurnitureItem &item) { bool apply = true; + // if at least one tag not found, do not add + for (auto &t : item.tags) { + std::cout << "tag: " << t << std::endl; + for (auto ¬ag : notags) { + std::cout << "notag: " << notag + << std::endl; + } + } for (auto &t : tags) { if (std::find(item.tags.begin(), item.tags.end(), @@ -727,6 +796,17 @@ struct CellsScript { break; } } + if (apply) + // if at least one notag found, do not add + for (auto &t : item.tags) { + if (std::find(notags.begin(), + notags.end(), + t) != + notags.end()) { + apply = false; + break; + } + } if (apply) { nlohmann::json jdata = nlohmann::json::parse( @@ -734,11 +814,17 @@ struct CellsScript { adata.push_back(jdata); } }); - // FIXME: use noise function - return adata[0]; + if (adata.size() == 0) + return nlohmann::json(); + float nvalue = noise.GetNoise((float)currentX, (float)currentY, + (float)currentZ); + float selectionf = (nvalue + 1) / 2.0f; + int selection = Ogre::Math::Floor(selectionf * adata.size()); + return adata[selection]; } - void place_furniture(int room, Ogre::String tags) + bool place_furniture(int room, Ogre::String tags) { + bool ret = true; std::vector atags = split_and_trim(tags, ','); int i, j; int minX = rooms[room].minX; @@ -750,185 +836,189 @@ struct CellsScript { std::cout << "room: " << room << " " << minX << " " << minZ << " " << sizeX << " " << sizeZ << std::endl; clear_furniture_area(minX, minZ, sizeX, sizeZ); - currentX = midX; - currentZ = minZ; - bool placed = false; - if (isBit("iwallz-") && !isBit("idoorz+") && - !isBit("idoorz-") && !isBit("idoorx+") && - !isBit("idoorx-") && !placed) { - std::vector ptags = atags; - ptags.push_back("essential"); - nlohmann::json furniture = select_furniture(ptags); - std::cout << currentX << " " << currentY << " " - << currentZ << std::endl; - fcell(ptags, furniture, 2); - placed = true; - } - currentX = midX; - currentZ = minZ + sizeZ - 1; - if (isBit("iwallz+") && !isBit("idoorz+") && - !isBit("idoorz-") && !isBit("idoorx+") && - !isBit("idoorx-") && !placed) { - std::vector ptags = atags; - ptags.push_back("essential"); - nlohmann::json furniture = select_furniture(ptags); - std::cout << currentX << " " << currentY << " " - << currentZ << std::endl; - fcell(ptags, furniture, 0); - placed = true; - } - currentX = midX; - currentZ = midZ; - if (isBit("iwallx-") && !isBit("idoorz+") && - !isBit("idoorz-") && !isBit("idoorx+") && - !isBit("idoorx-") && !placed) { - std::vector ptags = atags; - ptags.push_back("essential"); - nlohmann::json furniture = select_furniture(ptags); - std::cout << currentX << " " << currentY << " " - << currentZ << std::endl; - fcell(ptags, furniture, 1); - placed = true; - } - currentX = midX + sizeX - 1; - currentZ = midZ; - if (isBit("iwallx+") && !isBit("idoorz+") && - !isBit("idoorz-") && !isBit("idoorx+") && - !isBit("idoorx-") && !placed) { - std::vector ptags = atags; - ptags.push_back("essential"); - nlohmann::json furniture = select_furniture(ptags); - std::cout << currentX << " " << currentY << " " - << currentZ << std::endl; - fcell(ptags, furniture, 3); - placed = true; - } - if (placed) - return; - if (isBit("windowz-") && !isBit("idoorz+") && - !isBit("idoorz-") && !isBit("idoorx+") && - !isBit("idoorx-") && !placed) { - std::vector ptags = atags; - ptags.push_back("essential"); - nlohmann::json furniture = select_furniture(ptags); - std::cout << currentX << " " << currentY << " " - << currentZ << std::endl; - fcell(ptags, furniture, 2); - placed = true; - } - currentX = midX; - currentZ = minZ + sizeZ - 1; - if (isBit("windowz+") && !isBit("idoorz+") && - !isBit("idoorz-") && !isBit("idoorx+") && - !isBit("idoorx-") && !placed) { - std::vector ptags = atags; - ptags.push_back("essential"); - nlohmann::json furniture = select_furniture(ptags); - std::cout << currentX << " " << currentY << " " - << currentZ << std::endl; - fcell(ptags, furniture, 0); - placed = true; - } - currentX = midX; - currentZ = midZ; - if (isBit("windowx-") && !isBit("idoorz+") && - !isBit("idoorz-") && !isBit("idoorx+") && - !isBit("idoorx-") && !placed) { - std::vector ptags = atags; - ptags.push_back("essential"); - nlohmann::json furniture = select_furniture(ptags); - std::cout << currentX << " " << currentY << " " - << currentZ << std::endl; - fcell(ptags, furniture, 1); - placed = true; - } - currentX = midX + sizeX - 1; - currentZ = midZ; - if (isBit("windowx+") && !isBit("idoorz+") && - !isBit("idoorz-") && !isBit("idoorx+") && - !isBit("idoorx-") && !placed) { - std::vector ptags = atags; - ptags.push_back("essential"); - nlohmann::json furniture = select_furniture(ptags); - std::cout << currentX << " " << currentY << " " - << currentZ << std::endl; - fcell(ptags, furniture, 3); - placed = true; - } - if (placed) - return; - for (i = 1; i < sizeX - 1; i++) { - currentX = minX + i; - currentZ = minZ; - if (isBit("iwallz-") && !isBit("idoorz+") && - !isBit("idoorz-") && !isBit("idoorx+") && - !isBit("idoorx-") && !placed) { - std::vector ptags = atags; - ptags.push_back("essential"); + auto nodoors = [&]() -> bool { + return !isBit("idoorz+") && !isBit("idoorz-") && + !isBit("idoorx+") && !isBit("idoorx-"); + }; + auto nowindows = [&]() -> bool { + return !isBit("windowz+") && !isBit("windowz-") && + !isBit("windowx+") && !isBit("windowx-"); + }; + auto placeOne = [&](const Ogre::String &bit, + const std::vector &tags, + const std::vector ¬ags, + int rotation) -> bool { + if (findFCell(currentX, currentY, currentZ) >= 0) + return false; + if (isBit(bit) && nodoors()) { nlohmann::json furniture = - select_furniture(ptags); + select_furniture(tags, notags); + if (furniture.empty()) // got nothing + return false; std::cout << currentX << " " << currentY << " " << currentZ << std::endl; - fcell(ptags, furniture, 2); - placed = true; - break; + fcell(tags, furniture, rotation); + return true; } - } - if (placed) - return; - for (i = 1; i < sizeX - 1; i++) { - currentX = minX + i; - currentZ = minZ + sizeZ - 1; - if (isBit("iwallz+") && !isBit("idoorz+") && - !isBit("idoorz-") && !isBit("idoorx+") && - !isBit("idoorx-") && !placed) { - std::vector ptags = atags; - ptags.push_back("essential"); - nlohmann::json furniture = - select_furniture(ptags); - std::cout << currentX << " " << currentY << " " - << currentZ << std::endl; - fcell(ptags, furniture, 0); - placed = true; - break; + return false; + }; + std::vector etags = atags; + std::vector itags = atags; + std::vector otags = atags; + std::vector ftags = atags; + struct midpoints { + int X, Z; + int rotation; + }; + struct ranges { + int startX, endX; + int startZ, endZ; + int rotation; + }; + struct tagdata { + std::vector tags, notagswalls, + notagswindows; + bool essential; + bool filler; + }; + etags.push_back("essential"); + itags.push_back("important"); + otags.push_back("optional"); + ftags.push_back("filler"); + struct tagdata taglist[] = { + { etags, + { "nowall", "nowindow" }, + { "nowindow" }, + true, + false }, + { itags, + { "nowall", "nowindow" }, + { "nowindow" }, + false, + false }, + { otags, + { "nowall", "nowindow" }, + { "nowindow" }, + false, + false }, + { ftags, + { "nowall", "nowindow" }, + { "nowindow" }, + false, + true }, + }; + Ogre::String bits_walls[] = { "iwallz+", "iwallx+", "iwallz-", + "iwallx-" }; + Ogre::String bits_windows[] = { "windowz+", "windowx+", + "windowz-", "windowx-" }; + struct midpoints mpoints[] = { + { midX, minZ, 2 }, { midX, minZ + sizeZ - 1, 0 }, + { minX, midZ, 3 }, { minX + sizeX - 1, midZ, 1 }, + { midX, minZ, 2 }, { midX, minZ + sizeZ - 1, 0 }, + { minX, midZ, 3 }, { minX + sizeX - 1, midZ, 1 } + }; + struct ranges range_points[] = { + { minX, sizeX - 1, minZ, minZ, 2 }, + { minX, sizeX - 1, minZ + sizeZ - 1, minZ + sizeZ - 1, + 0 }, + { minX, minX, minZ, minZ + sizeZ - 1, 3 }, + { minX + sizeX - 1, minX + sizeX - 1, minZ, + minZ + sizeZ - 1, 1 }, + }; + for (const auto &mtags : taglist) { + bool placed = false; + if (!placed) + for (auto &p : mpoints) { + currentX = p.X; + currentZ = p.Z; + if (!placed && nodoors()) { + const Ogre::String &bit = + bits_walls[p.rotation]; + placed = placeOne( + bit, mtags.tags, + mtags.notagswalls, + p.rotation); + if (placed && !mtags.filler) + break; + } + if (mtags.filler) + placed = false; + } + if (!placed) + for (auto &p : range_points) { + int i, j; + for (i = p.startZ; i <= p.endZ; i++) { + for (j = p.startX; j <= p.endX; + j++) { + currentX = j; + currentZ = i; + const Ogre::String &bit = bits_walls + [p.rotation]; + + placed = placeOne( + bit, mtags.tags, + mtags.notagswalls, + p.rotation); + if (placed && + !mtags.filler) + break; + } + if (placed && !mtags.filler) + break; + } + if (placed && !mtags.filler) + break; + } + if (mtags.filler) + placed = false; + if (!placed) + for (auto &p : mpoints) { + if (!placed && nodoors()) { + const Ogre::String &bit = + bits_windows[p.rotation]; + placed = placeOne( + bit, mtags.tags, + mtags.notagswindows, + p.rotation); + if (placed && !mtags.filler) + break; + } + } + if (mtags.filler) + placed = false; + if (!placed) + for (auto &p : range_points) { + int i, j; + for (i = p.startZ; i <= p.endZ; i++) { + for (j = p.startX; j <= p.endX; + j++) { + currentX = j; + currentZ = i; + const Ogre::String &bit = + bits_windows + [p.rotation]; + placed = placeOne( + bit, mtags.tags, + mtags.notagswindows, + p.rotation); + if (placed && + !mtags.filler) + break; + } + if (placed && !mtags.filler) + break; + } + if (placed && !mtags.filler) + break; + } + if (!placed && !mtags.filler) { + ret = false; + break; // do not place anything if essentials were not placed } - } - if (placed) - return; - for (i = 1; i < sizeZ - 1; i++) { - currentX = minX; - currentZ = minZ + i; - if (isBit("iwallx-") && !isBit("idoorz+") && - !isBit("idoorz-") && !isBit("idoorx+") && - !isBit("idoorx-") && !placed) { - std::vector ptags = atags; - ptags.push_back("essential"); - nlohmann::json furniture = - select_furniture(ptags); - std::cout << currentX << " " << currentY << " " - << currentZ << std::endl; - fcell(ptags, furniture, 1); - placed = true; + if (!placed && !mtags.filler) break; - } - } - for (i = 1; i < sizeZ - 1; i++) { - currentX = minX + sizeX - 1; - currentZ = minZ + i; - if (isBit("iwallx+") && !isBit("idoorz+") && - !isBit("idoorz-") && !isBit("idoorx+") && - !isBit("idoorx-") && !placed) { - std::vector ptags = atags; - ptags.push_back("essential"); - nlohmann::json furniture = - select_furniture(ptags); - std::cout << currentX << " " << currentY << " " - << currentZ << std::endl; - fcell(ptags, furniture, 3); - placed = true; - break; - } } + return ret; } std::vector > roomEdge(int room) @@ -1395,124 +1485,139 @@ bool editRoofs(const Ogre::String &lotLabel, nlohmann::json &lot) if (lot.find("roofs") != lot.end()) roofs = lot["roofs"]; Ogre::String addNewLabel = lotLabel + "_AddNewRoof"; - ImGui::Text("Roofs"); - ImGui::Separator(); - ImGui::SeparatorText("in cell sizes"); - int roofCount = 0; - for (auto &roof : roofs) { - Ogre::String roofLabel = - lotLabel + "_" + - Ogre::StringConverter::toString(roofCount); - ImGui::Text("roof - %d", roofCount); - int mroofType = roof["type"].get(); - int mroofPosition[3]; - int mroofSize[2]; - float mroofOffset[3]; - float mbaseHeight, mmaxHeight; - mroofPosition[0] = roof["position_x"].get(); - mroofPosition[1] = roof["position_y"].get(); - mroofPosition[2] = roof["position_z"].get(); - mroofSize[0] = roof["size_x"].get(); - mroofSize[1] = roof["size_z"].get(); - mroofOffset[0] = roof["offset_x"].get(); - mroofOffset[1] = roof["offset_y"].get(); - mroofOffset[2] = roof["offset_z"].get(); - mbaseHeight = roof["base_height"].get(); - mmaxHeight = roof["max_height"].get(); - if (ImGui::Combo(("Roof Type##" + lotLabel).c_str(), &mroofType, - items, IM_ARRAYSIZE(items))) - changed = true; - if (ImGui::SliderInt(("Roof Position X##" + lotLabel).c_str(), - &mroofPosition[0], -100, 100)) - changed = true; - if (ImGui::SliderInt(("Roof Position Y##" + lotLabel).c_str(), - &mroofPosition[1], -100, 100)) - changed = true; - if (ImGui::SliderInt(("Roof Position Z##" + lotLabel).c_str(), - &mroofPosition[2], -100, 100)) - changed = true; - if (ImGui::SliderInt(("Roof Size X##" + lotLabel).c_str(), - &mroofSize[0], 1, 50)) - changed = true; - if (ImGui::SliderInt(("Roof Size Z##" + lotLabel).c_str(), - &mroofSize[1], 1, 50)) - changed = true; - if (ImGui::SliderFloat(("Roof Offset X##" + lotLabel).c_str(), - &mroofOffset[0], -10.0f, 10.0f)) - changed = true; - if (ImGui::SliderFloat(("Roof Offset Y##" + lotLabel).c_str(), - &mroofOffset[1], -10.0f, 10.0f)) - changed = true; - if (ImGui::SliderFloat(("Roof Offset Z##" + lotLabel).c_str(), - &mroofOffset[2], -10.0f, 10.0f)) - changed = true; - if (ImGui::SliderFloat(("Roof Base Height##" + lotLabel).c_str(), - &mbaseHeight, 0.1f, 100.0f)) - changed = true; - mmaxHeight = Ogre::Math::Clamp(mmaxHeight, mbaseHeight, 100.0f); - if (ImGui::SliderFloat(("Roof Max Height##" + lotLabel).c_str(), - &mmaxHeight, 0.1f, 100.0f)) - changed = true; - if (changed) { - roof["type"] = mroofType; - roof["position_x"] = mroofPosition[0]; - roof["position_y"] = mroofPosition[1]; - roof["position_z"] = mroofPosition[2]; - roof["size_x"] = mroofSize[0]; - roof["size_z"] = mroofSize[1]; - roof["offset_x"] = mroofOffset[0]; - roof["offset_y"] = mroofOffset[1]; - roof["offset_z"] = mroofOffset[2]; - roof["base_height"] = mbaseHeight; - roof["max_height"] = mmaxHeight; - } - if (ImGui::SmallButton(("Delete##" + roofLabel).c_str())) { - roofs.erase(roofCount); - changed = true; - break; + if (ImGui::CollapsingHeader(("Roofs..." + lotLabel).c_str())) { + ImGui::Text("Roofs"); + ImGui::Separator(); + ImGui::SeparatorText("in cell sizes"); + int roofCount = 0; + for (auto &roof : roofs) { + Ogre::String roofLabel = + lotLabel + "_" + + Ogre::StringConverter::toString(roofCount); + ImGui::Text("roof - %d", roofCount); + int mroofType = roof["type"].get(); + int mroofPosition[3]; + int mroofSize[2]; + float mroofOffset[3]; + float mbaseHeight, mmaxHeight; + mroofPosition[0] = roof["position_x"].get(); + mroofPosition[1] = roof["position_y"].get(); + mroofPosition[2] = roof["position_z"].get(); + mroofSize[0] = roof["size_x"].get(); + mroofSize[1] = roof["size_z"].get(); + mroofOffset[0] = roof["offset_x"].get(); + mroofOffset[1] = roof["offset_y"].get(); + mroofOffset[2] = roof["offset_z"].get(); + mbaseHeight = roof["base_height"].get(); + mmaxHeight = roof["max_height"].get(); + if (ImGui::Combo(("Roof Type##" + lotLabel).c_str(), + &mroofType, items, + IM_ARRAYSIZE(items))) + changed = true; + if (ImGui::SliderInt( + ("Roof Position X##" + lotLabel).c_str(), + &mroofPosition[0], -100, 100)) + changed = true; + if (ImGui::SliderInt( + ("Roof Position Y##" + lotLabel).c_str(), + &mroofPosition[1], -100, 100)) + changed = true; + if (ImGui::SliderInt( + ("Roof Position Z##" + lotLabel).c_str(), + &mroofPosition[2], -100, 100)) + changed = true; + if (ImGui::SliderInt( + ("Roof Size X##" + lotLabel).c_str(), + &mroofSize[0], 1, 50)) + changed = true; + if (ImGui::SliderInt( + ("Roof Size Z##" + lotLabel).c_str(), + &mroofSize[1], 1, 50)) + changed = true; + if (ImGui::SliderFloat( + ("Roof Offset X##" + lotLabel).c_str(), + &mroofOffset[0], -10.0f, 10.0f)) + changed = true; + if (ImGui::SliderFloat( + ("Roof Offset Y##" + lotLabel).c_str(), + &mroofOffset[1], -10.0f, 10.0f)) + changed = true; + if (ImGui::SliderFloat( + ("Roof Offset Z##" + lotLabel).c_str(), + &mroofOffset[2], -10.0f, 10.0f)) + changed = true; + if (ImGui::SliderFloat( + ("Roof Base Height##" + lotLabel).c_str(), + &mbaseHeight, 0.1f, 100.0f)) + changed = true; + mmaxHeight = Ogre::Math::Clamp(mmaxHeight, mbaseHeight, + 100.0f); + if (ImGui::SliderFloat( + ("Roof Max Height##" + lotLabel).c_str(), + &mmaxHeight, 0.1f, 100.0f)) + changed = true; + if (changed) { + roof["type"] = mroofType; + roof["position_x"] = mroofPosition[0]; + roof["position_y"] = mroofPosition[1]; + roof["position_z"] = mroofPosition[2]; + roof["size_x"] = mroofSize[0]; + roof["size_z"] = mroofSize[1]; + roof["offset_x"] = mroofOffset[0]; + roof["offset_y"] = mroofOffset[1]; + roof["offset_z"] = mroofOffset[2]; + roof["base_height"] = mbaseHeight; + roof["max_height"] = mmaxHeight; + } + if (ImGui::SmallButton( + ("Delete##" + roofLabel).c_str())) { + roofs.erase(roofCount); + changed = true; + break; + } } roofCount++; + ImGui::Combo(("Roof Type##" + addNewLabel).c_str(), &roofType, + items, IM_ARRAYSIZE(items)); + ImGui::SliderInt(("Roof Position X##" + addNewLabel).c_str(), + &roofPosition[0], -100, 100); + ImGui::SliderInt(("Roof Position Y##" + addNewLabel).c_str(), + &roofPosition[1], 0, 100); + ImGui::SliderInt(("Roof Position Z##" + addNewLabel).c_str(), + &roofPosition[2], -100, 100); + ImGui::SliderInt(("Roof Size X##" + addNewLabel).c_str(), + &roofSize[0], 1, 50); + ImGui::SliderInt(("Roof Size Z##" + addNewLabel).c_str(), + &roofSize[1], 1, 50); + ImGui::SliderFloat(("Roof Offset X##" + addNewLabel).c_str(), + &roofOffset[0], -10.0f, 10.0f); + ImGui::SliderFloat(("Roof Offset Y##" + addNewLabel).c_str(), + &roofOffset[1], -10.0f, 10.0f); + ImGui::SliderFloat(("Roof Offset Z##" + addNewLabel).c_str(), + &roofOffset[2], -10.0f, 10.0f); + ImGui::SliderFloat(("Roof Base Height##" + addNewLabel).c_str(), + &baseHeight, 0.1f, 100.0f); + ImGui::SliderFloat(("Roof Max Height##" + addNewLabel).c_str(), + &maxHeight, 0.1f, 100.0f); + if (ImGui::SmallButton(("Add##" + addNewLabel).c_str())) { + nlohmann::json roof; + roof["type"] = roofType; + roof["position_x"] = roofPosition[0]; + roof["position_y"] = roofPosition[1]; + roof["position_z"] = roofPosition[2]; + roof["size_x"] = roofSize[0]; + roof["size_z"] = roofSize[1]; + roof["offset_x"] = roofOffset[0]; + roof["offset_y"] = roofOffset[1]; + roof["offset_z"] = roofOffset[2]; + roof["base_height"] = baseHeight; + roof["max_height"] = maxHeight; + roofs.push_back(roof); + changed = true; + } + if (changed) + lot["roofs"] = roofs; } - ImGui::Combo(("Roof Type##" + addNewLabel).c_str(), &roofType, items, - IM_ARRAYSIZE(items)); - ImGui::SliderInt(("Roof Position X##" + addNewLabel).c_str(), - &roofPosition[0], -100, 100); - ImGui::SliderInt(("Roof Position Y##" + addNewLabel).c_str(), - &roofPosition[1], 0, 100); - ImGui::SliderInt(("Roof Position Z##" + addNewLabel).c_str(), - &roofPosition[2], -100, 100); - ImGui::SliderInt(("Roof Size X##" + addNewLabel).c_str(), &roofSize[0], - 1, 50); - ImGui::SliderInt(("Roof Size Z##" + addNewLabel).c_str(), &roofSize[1], - 1, 50); - ImGui::SliderFloat(("Roof Offset X##" + addNewLabel).c_str(), - &roofOffset[0], -10.0f, 10.0f); - ImGui::SliderFloat(("Roof Offset Y##" + addNewLabel).c_str(), - &roofOffset[1], -10.0f, 10.0f); - ImGui::SliderFloat(("Roof Offset Z##" + addNewLabel).c_str(), - &roofOffset[2], -10.0f, 10.0f); - ImGui::SliderFloat(("Roof Base Height##" + addNewLabel).c_str(), - &baseHeight, 0.1f, 100.0f); - ImGui::SliderFloat(("Roof Max Height##" + addNewLabel).c_str(), - &maxHeight, 0.1f, 100.0f); - if (ImGui::SmallButton(("Add##" + addNewLabel).c_str())) { - nlohmann::json roof; - roof["type"] = roofType; - roof["position_x"] = roofPosition[0]; - roof["position_y"] = roofPosition[1]; - roof["position_z"] = roofPosition[2]; - roof["size_x"] = roofSize[0]; - roof["size_z"] = roofSize[1]; - roof["offset_x"] = roofOffset[0]; - roof["offset_y"] = roofOffset[1]; - roof["offset_z"] = roofOffset[2]; - roof["base_height"] = baseHeight; - roof["max_height"] = maxHeight; - roofs.push_back(roof); - changed = true; - } - if (changed) - lot["roofs"] = roofs; return changed; } bool editLot(const Ogre::String &lotLabel, nlohmann::json &lot) @@ -1684,7 +1789,86 @@ bool editLot(const Ogre::String &lotLabel, nlohmann::json &lot) changed = changed || editRoofs(lotLabel, lot); return changed; } -bool editDistrict(const Ogre::String &districtLabel, nlohmann::json &district) +void commandEraseLot(nlohmann::json &district, int lotIndex) +{ + nlohmann::json lots = nlohmann::json::array(); + for (const auto &lot : district["lots"]) + lots.push_back(lot); + lots.erase(lotIndex); + district["lots"] = lots; +} +void cleanupLot(nlohmann::json &lot) +{ + lot.erase("cells"); + lot.erase("furniture_cells"); + lot.erase("roofs"); +} +void addLotTemplate(nlohmann::json &district, nlohmann::json &lotTemplates, + int lotIndex) +{ + if (district.find("lots") != district.end()) { + nlohmann::json &lots = district["lots"]; + nlohmann::json lotTemplate = lots[lotIndex]; + cleanupLot(lotTemplate); + lotTemplates.push_back(lotTemplate); + } +} +void addLotFromTemplate(nlohmann::json &district, + const nlohmann::json &lotTemplates, + int lotTemplateIndex) +{ + nlohmann::json lots = nlohmann::json::array(); + OgreAssert(lotTemplateIndex < lotTemplates.size() && + lotTemplates.size() > 0, + "bad template"); + if (lotTemplates.size() == 0 || lotTemplateIndex >= lotTemplates.size()) + return; + for (const auto &lot : district["lots"]) + lots.push_back(lot); + nlohmann::json t = lotTemplates[lotTemplateIndex]; + float angle = 0.0f; + float nextAngle = angle; + float radius = district["radius"].get(); + if (lots.size() > 0) { + angle = lots[0]["angle"].get(); + nextAngle = angle; + for (auto &lot : lots) { + float width = lot["width"].get(); + if (angle < lot["angle"].get()) { + angle = lot["angle"].get(); + nextAngle = angle + + Ogre::Math::ASin(width / + (radius * 2.0f)) + .valueDegrees(); + } + } + } + t["angle"] = nextAngle; + CellsScript script(t["cellScript"].get(), t); + script.run(); + lots.push_back(t); + district["lots"] = lots; +} +void addLot(nlohmann::json &district, float angle, int width, int depth, + float elevation, const Ogre::String &script) +{ + nlohmann::json lots = nlohmann::json::array(); + for (const auto &lot : district["lots"]) + lots.push_back(lot); + nlohmann::json l; + l["angle"] = angle; + l["width"] = width; + l["depth"] = depth; + l["elevation"] = elevation; + l["cellScript"] = script; + l["objects"] = nlohmann::json::array(); + CellsScript cellScript(l["cellScript"].get(), l); + cellScript.run(); + lots.push_back(l); + district["lots"] = lots; +} +bool editDistrict(const Ogre::String &districtLabel, nlohmann::json &district, + nlohmann::json &lotTemplates) { bool changed = false; nlohmann::json lots = nlohmann::json::array(); @@ -1698,25 +1882,60 @@ bool editDistrict(const Ogre::String &districtLabel, nlohmann::json &district) ImGui::Text("%s", lotLabel.c_str()); if (ImGui::CollapsingHeader( ("Edit lot... ##" + lotLabel).c_str())) { - changed = changed || editLot(lotLabel, lot); + bool lotChanged = editLot(lotLabel, lot); + lot["_dirty"] = lotChanged; + changed = changed || lotChanged; ImGui::Separator(); + if (ImGui::SmallButton( + ("Add as template##" + lotLabel).c_str())) { + addLotTemplate(district, lotTemplates, + lotCount); + changed = true; + } if (ImGui::SmallButton( ("Lot Delete##" + lotLabel).c_str())) { - lots.erase(lotCount); + commandEraseLot(district, lotCount); + changed = true; break; } } lotCount++; } ImGui::Separator(); + int templateCount = 0; + for (auto &t : lotTemplates) { + Ogre::String templateLabel = + districtLabel + "_template" + + Ogre::StringConverter::toString(templateCount); + Ogre::String templateName; + if (t.find("templateName") != t.end()) + templateName = t["templateName"]; + char nameBuffer[64]; + strncpy(nameBuffer, templateName.c_str(), sizeof(nameBuffer)); + ImGui::Text("%d", templateCount); + if (ImGui::InputText(("Name##" + templateLabel).c_str(), + nameBuffer, sizeof(nameBuffer))) { + templateName = Ogre::String(nameBuffer); + t["templateName"] = templateName; + changed = true; + } + if (ImGui::SmallButton( + ("Add lot with template##" + districtLabel + + Ogre::StringConverter::toString(templateCount)) + .c_str())) { + int d = district["lots"].size(); + addLotFromTemplate(district, lotTemplates, + templateCount); + int d2 = district["lots"].size(); + OgreAssert(d2 > d, "failed to add lot"); + lots = district["lots"]; + changed = true; + } + templateCount++; + } + ImGui::Separator(); if (ImGui::SmallButton("Add lot")) { - nlohmann::json l; - l["angle"] = 0.0f; - l["width"] = 10; - l["depth"] = 10; - l["elevation"] = 0.0f; - l["objects"] = nlohmann::json::array(); - lots.push_back(l); + addLot(district, 0.0f, 10, 10, 0.0f, "-- cell creation script"); changed = true; } district["lots"] = lots; @@ -1848,72 +2067,114 @@ bool editColorRects(nlohmann::json &rects) } return changed; } +void runAllScriptsForTown(flecs::entity e) +{ + Ogre::String prop = StaticGeometryModule::getItemProperties(e); + nlohmann::json j = nlohmann::json::parse(prop); + auto &districts = j["districts"]; + for (auto &district : districts) { + if (district.find("lots") == district.end()) + continue; + auto &lots = district["lots"]; + for (auto &lot : lots) { + if (lot.find("cellScript") == lot.end()) + continue; + CellsScript script( + lot["cellScript"].get(), lot); + script.run(); + } + district["lots"] = lots; + } + j["districts"] = districts; + StaticGeometryModule::setItemProperties(e, j.dump()); +} void createTownPopup(const std::pair item) { - Ogre::String prop = StaticGeometryModule::getItemProperties(item.first); - Ogre::Vector3 townPosition; - Ogre::Quaternion townRotation; - StaticGeometryModule::getItemPositionAndRotation( - item.first, townPosition, townRotation); - nlohmann::json j = nlohmann::json::parse(prop); - bool changed = false; - int count = 0; - ImGui::Text("Town"); + Ogre::String prop = StaticGeometryModule::getItemProperties(item.first); + Ogre::Vector3 townPosition; + Ogre::Quaternion townRotation; + StaticGeometryModule::getItemPositionAndRotation( + item.first, townPosition, townRotation); + nlohmann::json j = nlohmann::json::parse(prop); + bool changed = false; + int count = 0; + ImGui::Text("Town"); + char townNameBuf[64]; + strcpy(townNameBuf, ""); + if (j.find("name") != j.end()) + strncpy(townNameBuf, j["name"].get().c_str(), + sizeof(townNameBuf)); + ImGui::InputText("Town Name", townNameBuf, sizeof(townNameBuf)); + if (ImGui::IsItemDeactivatedAfterEdit()) { + j["name"] = Ogre::String(townNameBuf); + changed = true; + } + if (ImGui::SmallButton("Run all town scripts")) { + runAllScriptsForTown(item.first); + StaticGeometryModule::saveItems(); + changed = true; + } nlohmann::json colorRects = nlohmann::json::object(); + nlohmann::json lotTemplates = nlohmann::json::array(); if (j.find("colorRects") != j.end()) colorRects = j["colorRects"]; + if (j.find("lotTemplates") != j.end()) + lotTemplates = j["lotTemplates"]; if (ImGui::CollapsingHeader("Edit Color Rects")) { changed = changed || editColorRects(colorRects); ImGui::Separator(); } - nlohmann::json districts = nlohmann::json::array(); - for (auto &district : j["districts"]) - districts.push_back(district); - count = 0; - for (auto &district : districts) { + nlohmann::json districts = nlohmann::json::array(); + for (auto &district : j["districts"]) + districts.push_back(district); + count = 0; + for (auto &district : districts) { ImGui::Separator(); Ogre::String districtLabel = "district" + Ogre::StringConverter::toString(count); ImGui::Text("%s", districtLabel.c_str()); - changed = changed || editDistrict(districtLabel, district); + bool districtChanged = + editDistrict(districtLabel, district, lotTemplates); + district["_dirty"] = districtChanged; + changed = changed || districtChanged; ImGui::Separator(); - if (ImGui::SmallButton("Delete")) { + if (ImGui::SmallButton(("Delete##" + districtLabel).c_str())) { districts.erase(count); changed = true; break; } count++; - } - ImGui::Separator(); - if (ImGui::SmallButton("Add district")) { - Ogre::Vector3 cursorPosition = - ECS::get().sceneNode->_getDerivedPosition(); - Ogre::Quaternion cursorOrientation = - ECS::get() - .sceneNode->_getDerivedOrientation(); - Ogre::Vector3 localPosition = cursorPosition - townPosition; - Ogre::Quaternion localRotation = - townRotation.Inverse() * cursorOrientation; - nlohmann::json d; - d["radius"] = 50.0f; - d["lots"] = nlohmann::json::array(); - to_json(d["position"], localPosition); - to_json(d["rotation"], localRotation); - d["elevation"] = 0.0f; - d["plazza"] = false; - districts.push_back(d); - changed = true; - } - ImGui::Separator(); - ImGui::Text("%s", j.dump(4).c_str()); - if (changed) { - j["districts"] = districts; + } + ImGui::Separator(); + if (ImGui::SmallButton("Add district")) { + Ogre::Vector3 cursorPosition = + ECS::get().sceneNode->_getDerivedPosition(); + Ogre::Quaternion cursorOrientation = + ECS::get() + .sceneNode->_getDerivedOrientation(); + Ogre::Vector3 localPosition = cursorPosition - townPosition; + Ogre::Quaternion localRotation = + townRotation.Inverse() * cursorOrientation; + nlohmann::json d; + d["radius"] = 50.0f; + d["lots"] = nlohmann::json::array(); + to_json(d["position"], localPosition); + to_json(d["rotation"], localRotation); + d["elevation"] = 0.0f; + d["plazza"] = false; + districts.push_back(d); + changed = true; + } + ImGui::Separator(); + ImGui::Text("%s", j.dump(4).c_str()); + if (changed) { + j["districts"] = districts; j["colorRects"] = colorRects; - StaticGeometryModule::setItemProperties(item.first, j.dump()); - StaticGeometryModule::saveItems(); - StaticGeometryModule::destroyItemGeometry(item.first); + j["lotTemplates"] = lotTemplates; + StaticGeometryModule::setItemProperties(item.first, j.dump()); + StaticGeometryModule::saveItems(); Geometry::createTownMaterial(item.first, true); - StaticGeometryModule::createItemGeometry(item.first); + StaticGeometryModule::updateItemGeometry(item.first); } } } @@ -2315,15 +2576,17 @@ struct ProcessCells { }; const float windowWidth = 1.6f; for (const struct BitSet &bits : windowbits) { + float windowBottomOffset = 0.8f; float sideWidth = (2.0f - windowWidth) / 2.0f; float moffset = (2.0f - sideWidth) / 2.0f; float midSize = 2.0f; - float bottomSize = 0.5f - solidExtOffset; + float bottomSize = windowBottomOffset - solidExtOffset; float topOffset = 3.0f; float topSize = solidExtHeight - midSize - bottomSize; float offsetY = solidExtOffset; if (bits.tb == &intwindowstb) { - bottomSize = 0.5f - solidIntOffset; + bottomSize = + windowBottomOffset - solidIntOffset; topSize = solidIntHeight - midSize - bottomSize; sideWidth = (1.8f - windowWidth) / 2.0f; moffset = (1.8f - sideWidth) / 2.0f; @@ -2747,18 +3010,45 @@ void createTownWindows(flecs::entity e) .setPosition(Ogre::Vector3(0, 2.0f, 0)) .setEnableNormals(true) .addToTriangleBuffer(frame1tb); + float l = 0.36f; Procedural::BoxGenerator() .setSizeX(1.6f) .setSizeY(0.15f) - .setSizeZ(0.45f) + .setSizeZ(l) .setNumSegY(2) .setNumSegX(2) .setNumSegZ(2) - .setPosition(Ogre::Vector3(0, 0.0f, -(0.45f - 0.34f))) + .setPosition(Ogre::Vector3(0, 0.0f, -(l - 0.34f))) .setEnableNormals(true) .addToTriangleBuffer(frame1tb); clampUV(e, frame1tb, "windowsFrameColor"); + for (const auto &v : frame1tb.getVertices()) + std::cout << v.mPosition << " " << v.mUV << std::endl; frame1mesh = frame1tb.transformToMesh("window-frame1"); + std::cout << "submeshes: " << frame1mesh->getSubMeshes().size() + << std::endl; + while (frame1mesh->getSubMeshes().size() > 1) { + std::cout << "destroying submesh: " + << frame1mesh->getSubMeshes().size() - 1 + << std::endl; + frame1mesh->destroySubMesh( + frame1mesh->getSubMeshes().size() - 1); + } + int count = 0; + bool crash; + for (count = 0; count < frame1mesh->getSubMeshes().size(); + count++) { + Ogre::SubMesh *submesh = + frame1mesh->getSubMeshes()[count]; + std::cout << frame1mesh->getName() + << ": submesh: " << count << " " << submesh + << std::endl; + if (submesh) + submesh->parent = frame1mesh.get(); + else + crash = true; + } + OgreAssert(!crash, "no submesh"); Ogre::LodConfig configFrame(frame1mesh); setupLods(configFrame); Ogre::MeshPtr meshGlass = @@ -2894,581 +3184,2454 @@ void createTownDoors(flecs::entity e) } } } -void createDecorateFurniture(flecs::entity e, const nlohmann::json &jdistrict, +struct TownTask { + virtual void operator()(flecs::entity e, + const nlohmann::json &jdistrict, int index, + Ogre::SceneNode *sceneNode, + Ogre::StaticGeometry *geo) = 0; + virtual void wait() = 0; + virtual ~TownTask(){}; + TownTask(){}; + static void addMeshToStatic(Ogre::StaticGeometry *geo, + Ogre::MeshPtr mesh, + Ogre::MaterialPtr material, + const Ogre::Vector3 &position, + const Ogre::Quaternion &orientation) + { + Ogre::Entity *ent = + ECS::get().mScnMgr->createEntity(mesh); + ent->setMaterial(material); + geo->addEntity(ent, position, orientation); + ECS::get().mScnMgr->destroyEntity(ent); + } + static void addStaticBodyMesh(flecs::entity e, Ogre::MeshPtr mesh, + const Ogre::Vector3 &position, + const Ogre::Quaternion &orientation) + { + JPH::ShapeRefC shape = + JoltPhysicsWrapper::getSingleton().createMeshShape( + mesh); + JPH::BodyID id = JoltPhysicsWrapper::getSingleton().createBody( + shape, 0, position, orientation, + JPH::EMotionType::Static, Layers::NON_MOVING); + JoltPhysicsWrapper::getSingleton().addBody( + id, JPH::EActivation::Activate); + flecs::entity ce = + ECS::get().entity().child_of(e).set(id); + } +}; + +struct TownPlazza : TownTask { + std::shared_future townPlazzaComplete; + void createTownPlazza(flecs::entity e, const nlohmann::json &jdistrict, + int index, Ogre::SceneNode *sceneNode, + Ogre::StaticGeometry *geo) + { + struct QueueData { + Procedural::TriangleBuffer tb; + Ogre::Vector3 position; + Ogre::Quaternion rotation; + nlohmann::json jp; + Procedural::Shape *shape; + Ogre::String meshName; + Ogre::MaterialPtr material; + Ogre::StaticGeometry *geo; + flecs::entity e; + }; + if (townPlazzaComplete.valid() && + townPlazzaComplete.wait_for(std::chrono::seconds(0)) != + std::future_status::ready) + townPlazzaComplete.wait(); + + auto promise = std::make_shared >(); + townPlazzaComplete = promise->get_future(); + Ogre::MaterialPtr townMaterial; + townMaterial = Ogre::MaterialManager::getSingleton().getByName( + "proceduralMaterialTown" + + Ogre::StringConverter::toString(e.id())); + Ogre::Vector3 worldPosition = sceneNode->_getDerivedPosition(); + Ogre::Quaternion worldOrientation = + sceneNode->_getDerivedOrientation(); + Procedural::TriangleBuffer tb; + Ogre::String meshName = + "plazzaMesh" + Ogre::StringConverter::toString(index) + + "_" + Ogre::StringConverter::toString(e.id()); + QueueData *queueData = + new QueueData({ tb, worldPosition, worldOrientation, + jdistrict, new Procedural::Shape, + meshName, townMaterial, geo, e }); + Ogre::Vector3 localPosition(0, 0, 0); + Ogre::Quaternion localRotation = Ogre::Quaternion::IDENTITY; + from_json(queueData->jp["position"], localPosition); + from_json(queueData->jp["rotation"], localRotation); + Ogre::MeshPtr mesh = + Ogre::MeshManager::getSingleton().getByName( + queueData->meshName); + if (mesh) + Ogre::MeshManager::getSingleton().remove(mesh); + Ogre::Root::getSingleton().getWorkQueue()->addTask([promise, + queueData, + localPosition, + localRotation]() { + const nlohmann::json &jp = queueData->jp; + float radius = 5.0f; + float height = 0.2f; + float elevation = 0.0f; + bool plazza = false; + if (jp.find("plazza") != jp.end()) + plazza = jp["plazza"].get(); + if (!plazza) + return; + if (jp.find("radius") != jp.end()) + radius = jp["radius"].get(); + if (jp.find("height") != jp.end()) + height = jp["height"].get(); + if (jp.find("elevation") != jp.end()) + elevation = jp["elevation"].get(); + if (height < 0.1f) + height = 0.1f; + if (radius < 5.0f) + radius = 5.0f; + float mh = 4.0f; + Procedural::Shape *plazzaShape = queueData->shape; + plazzaShape->addPoint(0, -height - mh - mh); + plazzaShape->addPoint(radius * 0.5f + mh, + -height - mh - mh); + plazzaShape->addPoint(radius + mh, -height - mh); + plazzaShape->addPoint(radius, -height); + plazzaShape->addPoint(radius, 0.0f); + plazzaShape->addPoint(radius - 0.1f, 0.1f); + plazzaShape->addPoint(radius - mh + 0.1f, height); + plazzaShape->addPoint(radius - mh, height + 0.1f); + plazzaShape->addPoint(radius * 0.5f + mh, height); + plazzaShape->addPoint(0, height); + Procedural::Lathe() + .setShapeToExtrude(plazzaShape) + .setEnableNormals(true) + .setPosition(Ogre::Vector3( + 0.0f, height / 2.0f + elevation, 0.0f)) + .setNumSeg(24) + .addToTriangleBuffer(queueData->tb); + for (auto &v : queueData->tb.getVertices()) { + v.mUV *= 0.08f; + v.mUV.x += 0.41f; + v.mUV.x = + Ogre::Math::Clamp(v.mUV.x, 0.4f, 0.5f); + v.mUV.y = + Ogre::Math::Clamp(v.mUV.y, 0.0f, 0.1f); + } + Ogre::Root::getSingleton() + .getWorkQueue() + ->addMainThreadTask([queueData, localPosition, + localRotation]() { + Ogre::Vector3 worldPlazzaCenter = + queueData->position + + localPosition; + Ogre::MeshPtr mesh = + queueData->tb.transformToMesh( + queueData->meshName); + Ogre::LodConfig config(mesh); + setupLods(config); + addMeshToStatic(queueData->geo, mesh, + queueData->material, + worldPlazzaCenter, + queueData->rotation * + localRotation); + addStaticBodyMesh( + queueData->e, mesh, + worldPlazzaCenter, + queueData->rotation * + localRotation); + delete queueData; + }); + promise->set_value(true); + }); + } + void operator()(flecs::entity e, const nlohmann::json &jdistrict, + int index, Ogre::SceneNode *sceneNode, + Ogre::StaticGeometry *geo) + { + createTownPlazza(e, jdistrict, index, sceneNode, geo); + } + void wait() + { + if (townPlazzaComplete.valid() && + townPlazzaComplete.wait_for(std::chrono::seconds(0)) != + std::future_status::ready) + townPlazzaComplete.wait(); + } +}; + +struct TownLots : TownTask { + std::shared_future townLotsComplete; + void createTownLots(flecs::entity e, const nlohmann::json &jdistrict, + int index, Ogre::SceneNode *sceneNode, + Ogre::StaticGeometry *geo) + { + struct QueueData { + Procedural::TriangleBuffer tb; + Ogre::Vector3 position; + Ogre::Quaternion rotation; + nlohmann::json jp; + Procedural::Shape *shape; + Ogre::String meshName; + Ogre::MaterialPtr material; + Ogre::StaticGeometry *geo; + flecs::entity e; + }; + struct LotData { + int width, depth; + float angle, elevation; + struct QueueData queueData; + }; + + if (townLotsComplete.valid() && + townLotsComplete.wait_for(std::chrono::seconds(0)) != + std::future_status::ready) + townLotsComplete.wait(); + + auto promise = std::make_shared >(); + townLotsComplete = promise->get_future(); + Ogre::MaterialPtr townMaterial; + townMaterial = Ogre::MaterialManager::getSingleton().getByName( + "proceduralMaterialTown" + + Ogre::StringConverter::toString(e.id())); + nlohmann::json jlots = nlohmann::json::array(); + if (jdistrict.find("lots") != jdistrict.end()) + jlots = jdistrict["lots"]; + Ogre::Vector3 centerPosition = sceneNode->_getDerivedPosition(); + Ogre::Quaternion centerOrientation = + sceneNode->_getDerivedOrientation(); + float baseHeight = 4.0f; + const nlohmann::json &jp = jdistrict; + Ogre::Vector3 localPosition(0, 0, 0); + Ogre::Quaternion localRotation = Ogre::Quaternion::IDENTITY; + float delevation = 0.0f; + float radius = 50.0f; + if (jp.find("elevation") != jp.end()) + delevation = jp["elevation"].get(); + if (jp.find("radius") != jp.end()) + radius = jp["radius"].get(); + from_json(jp["position"], localPosition); + from_json(jp["rotation"], localRotation); + centerPosition = centerPosition + localPosition + + Ogre::Vector3(0, delevation, 0); + centerOrientation = centerOrientation * localRotation; + static std::vector lots; + lots.resize(0); + lots.reserve(jlots.size()); + { + int count = 0; + for (const auto &jb : jlots) { + Procedural::TriangleBuffer tb; + Ogre::String meshName = + "lotbase" + + Ogre::StringConverter::toString(count) + + "_" + + Ogre::StringConverter::toString( + e.id()) + + "_" + + Ogre::StringConverter::toString(index); + Ogre::MeshPtr mesh = + Ogre::MeshManager::getSingleton() + .getByName(meshName); + if (mesh) + Ogre::MeshManager::getSingleton().remove( + mesh); + int depth = 10; + int width = 10; + float angle = 0.0f; + float elevation = 0.0f; + if (jb.find("depth") != jb.end()) + depth = jb["depth"].get(); + if (jb.find("width") != jb.end()) + width = jb["width"].get(); + if (jb.find("angle") != jb.end()) + angle = jb["angle"].get(); + if (jb.find("elevation") != jb.end()) + elevation = + jb["elevation"].get(); + OgreAssert(width > 1 && depth > 1 && + baseHeight > 1, + "Bad stuff happen"); + lots.push_back( + { width, + depth, + angle, + elevation, + { tb, centerPosition, + centerOrientation, jdistrict, + new Procedural::Shape, meshName, + townMaterial, geo, e } }); + count++; + } + } + OgreAssert(lots.size() > 0, "bad lots count"); + Ogre::Root::getSingleton().getWorkQueue()->addTask([radius, + baseHeight, + promise]() { + std::cout << "lots count: " << lots.size() << std::endl; + OgreAssert(lots.size() > 0, "bad lots count"); + for (auto &lot : lots) { + float distance = radius; + + Ogre::Quaternion rotation = Ogre::Quaternion( + Ogre::Degree(lot.angle), + Ogre::Vector3::UNIT_Y); + Ogre::Vector3 offset = + lot.queueData.rotation * rotation * + (Ogre::Vector3::UNIT_Z * distance); + Procedural::BoxGenerator() + .setSizeX(4.0f * (float)lot.width) + .setSizeY(baseHeight) + .setSizeZ(4.0f * (float)lot.depth) + .setNumSegY(8) + .setNumSegX(8) + .setNumSegZ(8) + .setEnableNormals(true) + .setPosition(Ogre::Vector3( + 0.0f, + -baseHeight / 2.0f + + lot.elevation - 0.01f, + 0.0f)) + .addToTriangleBuffer(lot.queueData.tb); + OgreAssert( + lot.queueData.tb.getVertices().size() > + 8, + "bad box"); + for (auto &v : lot.queueData.tb.getVertices()) { + v.mUV *= 0.08f; + v.mUV.x += 0.41f; + v.mUV.x = Ogre::Math::Clamp(v.mUV.x, + 0.4f, 0.5f); + v.mUV.y = Ogre::Math::Clamp(v.mUV.y, + 0.0f, 0.1f); + } + } + Ogre::Root::getSingleton() + .getWorkQueue() + ->addMainThreadTask([promise, radius]() { + float distance = radius; + for (auto &lot : lots) { + std::cout + << lot.queueData.meshName + << std::endl; + Ogre::MeshPtr mesh = + Ogre::MeshManager::getSingleton() + .getByName( + lot.queueData + .meshName); + if (mesh) + Ogre::MeshManager:: + getSingleton() + .remove(mesh); + mesh = lot.queueData.tb.transformToMesh( + lot.queueData.meshName); + Ogre::Quaternion rotation = + Ogre::Quaternion( + Ogre::Degree( + lot.angle), + Ogre::Vector3:: + UNIT_Y); + Ogre::Vector3 offset = + lot.queueData.rotation * + rotation * + (Ogre::Vector3::UNIT_Z * + distance); + Ogre::LodConfig config(mesh); + setupLods(config); + addMeshToStatic( + lot.queueData.geo, mesh, + lot.queueData.material, + lot.queueData.position + + offset, + lot.queueData.rotation * + rotation); + addStaticBodyMesh( + lot.queueData.e, mesh, + lot.queueData.position + + offset, + lot.queueData.rotation * + rotation); + } + //OgreAssert(false, "TownLots"); + }); + promise->set_value(true); + }); + } + void operator()(flecs::entity e, const nlohmann::json &jdistrict, + int index, Ogre::SceneNode *sceneNode, + Ogre::StaticGeometry *geo) + { + createTownLots(e, jdistrict, index, sceneNode, geo); + } + void wait() + { + if (townLotsComplete.valid() && + townLotsComplete.wait_for(std::chrono::seconds(0)) != + std::future_status::ready) + townLotsComplete.wait(); + } +}; +struct TownCells : TownTask { + std::shared_future townCellsComplete; + void createCells(flecs::entity e, const nlohmann::json &jdistrict, + int index, Ogre::SceneNode *sceneNode, + Ogre::StaticGeometry *geo) + { + struct QueueData { + Procedural::TriangleBuffer tb; + Ogre::Vector3 position; + Ogre::Quaternion rotation; + nlohmann::json jp; + Procedural::Shape *shape; + Ogre::String meshName; + Ogre::MaterialPtr material; + Ogre::StaticGeometry *geo; + flecs::entity e; + }; + struct LotData { + int width, depth; + float angle, elevation; + nlohmann::json jlot; + Procedural::TriangleBuffer cellTb; + Procedural::TriangleBuffer intwinTb; + Procedural::TriangleBuffer exteriorTb; + struct QueueData queueData; + }; + + if (townCellsComplete.valid() && + townCellsComplete.wait_for(std::chrono::seconds(0)) != + std::future_status::ready) + townCellsComplete.wait(); + + auto promise = std::make_shared >(); + townCellsComplete = promise->get_future(); + Ogre::MaterialPtr townMaterial; + townMaterial = Ogre::MaterialManager::getSingleton().getByName( + "proceduralMaterialTown" + + Ogre::StringConverter::toString(e.id())); + nlohmann::json jlots = nlohmann::json::array(); + if (jdistrict.find("lots") != jdistrict.end()) + jlots = jdistrict["lots"]; + Ogre::Vector3 centerPosition = sceneNode->_getDerivedPosition(); + Ogre::Quaternion centerOrientation = + sceneNode->_getDerivedOrientation(); + float baseHeight = 4.0f; + const nlohmann::json &jp = jdistrict; + Ogre::Vector3 localPosition(0, 0, 0); + Ogre::Quaternion localRotation = Ogre::Quaternion::IDENTITY; + float delevation = 0.0f; + float radius = 50.0f; + if (jp.find("elevation") != jp.end()) + delevation = jp["elevation"].get(); + if (jp.find("radius") != jp.end()) + radius = jp["radius"].get(); + from_json(jp["position"], localPosition); + from_json(jp["rotation"], localRotation); + centerPosition = centerPosition + localPosition + + Ogre::Vector3(0, delevation, 0); + centerOrientation = centerOrientation * localRotation; + static std::vector lots; + lots.resize(0); + lots.reserve(jlots.size()); + { + int count = 0; + for (const auto &jb : jlots) { + Procedural::TriangleBuffer tb; + Procedural::TriangleBuffer cellTb; + Procedural::TriangleBuffer intwinTb; + Procedural::TriangleBuffer exteriorTb; + Ogre::String meshName = + "lotcells" + + Ogre::StringConverter::toString(count) + + "_" + + Ogre::StringConverter::toString( + e.id()) + + "_" + + Ogre::StringConverter::toString(index); + Ogre::MeshPtr mesh = + Ogre::MeshManager::getSingleton() + .getByName(meshName); + if (mesh) + Ogre::MeshManager::getSingleton().remove( + mesh); + int depth = 10; + int width = 10; + float angle = 0.0f; + float elevation = 0.0f; + if (jb.find("depth") != jb.end()) + depth = jb["depth"].get(); + if (jb.find("width") != jb.end()) + width = jb["width"].get(); + if (jb.find("angle") != jb.end()) + angle = jb["angle"].get(); + if (jb.find("elevation") != jb.end()) + elevation = + jb["elevation"].get(); + OgreAssert(width > 1 && depth > 1 && + baseHeight > 1, + "Bad stuff happen"); + if (jb.find("cells") != jb.end()) + lots.push_back( + { width, + depth, + angle, + elevation, + jb, + cellTb, + intwinTb, + exteriorTb, + { tb, centerPosition, + centerOrientation, + jdistrict, + new Procedural::Shape, + meshName, townMaterial, geo, + e } }); + count++; + } + } + Ogre::Root::getSingleton().getWorkQueue()->addTask([radius, + baseHeight, + promise]() { + { + int count = 0; + for (auto &lot : lots) { + for (auto &jcell : lot.jlot["cells"]) { + struct ProcessCells pcells; + int x = jcell["x"].get(); + int y = jcell["y"].get(); + int z = jcell["z"].get(); + uint64_t flags = + jcell["flags"] + .get(); + Ogre::Vector3 cellOffset( + x * 2.0f, y * 4.0f, + z * 2.0f); + if (flags == 0) { + Procedural::BoxGenerator() + .setSizeX(2.0f + + 0.4f) + .setSizeY(0.2f) + .setSizeZ(2.0f + + 0.4f) + .setNumSegY(8) + .setNumSegX(8) + .setNumSegZ(8) + .setEnableNormals( + true) + .setPosition( + cellOffset) + .addToTriangleBuffer( + pcells.floortb); + lot.cellTb.append( + pcells.floortb); + + } else + pcells.process( + lot.queueData.e, + cellOffset, + flags, + lot.cellTb, + lot.intwinTb, + lot.exteriorTb); + } + } + } + Ogre::Root::getSingleton().getWorkQueue()->addMainThreadTask([radius, + baseHeight, + promise]() { + int count = 0; + for (auto &lot : lots) { + float distance = radius; + Ogre::Quaternion rotation = + Ogre::Quaternion( + Ogre::Degree(lot.angle), + Ogre::Vector3::UNIT_Y); + Ogre::Vector3 offset = + lot.queueData.rotation * + rotation * + (Ogre::Vector3::UNIT_Z * + distance); + Ogre::Vector3 worldCenterPosition = + lot.queueData.position + + offset + + Ogre::Vector3(0, lot.elevation, + 0); + Ogre::Quaternion worldCenterOrientation = + lot.queueData.rotation * + rotation; + if (lot.cellTb.getVertices().size() > + 0) { + Ogre::MeshPtr mesh = + Ogre::MeshManager::getSingleton() + .getByName( + lot.queueData + .meshName + + "_cells"); + if (mesh) + Ogre::MeshManager:: + getSingleton() + .remove(mesh); + std::cout + << "vertex count: " + << lot.cellTb + .getVertices() + .size() + << std::endl; + mesh = lot.cellTb.transformToMesh( + lot.queueData.meshName); + /* debug save mesh */ + // serializeMesh(mesh, meshName); + Ogre::LodConfig config(mesh); + setupLods(config); + addMeshToStatic( + lot.queueData.geo, mesh, + lot.queueData.material, + worldCenterPosition, + worldCenterOrientation); + addStaticBodyMesh( + lot.queueData.e, mesh, + worldCenterPosition, + worldCenterOrientation); + } + if (lot.intwinTb.getVertices().size() > + 0) { + Ogre::MeshPtr mesh = + Ogre::MeshManager::getSingleton() + .getByName( + lot.queueData + .meshName + + "_intwin"); + if (mesh) + Ogre::MeshManager:: + getSingleton() + .remove(mesh); + std::cout + << "vertex count: " + << lot.intwinTb + .getVertices() + .size() + << std::endl; + mesh = lot.intwinTb.transformToMesh( + lot.queueData.meshName + + "_intwin"); + OgreAssert(mesh, "bad mesh"); + /* debug save mesh */ + // serializeMesh(mesh, meshName); + Ogre::LodConfig config(mesh); + setupLods(config); + addMeshToStatic( + lot.queueData.geo, mesh, + lot.queueData.material, + worldCenterPosition, + worldCenterOrientation); + addStaticBodyMesh( + lot.queueData.e, mesh, + worldCenterPosition, + worldCenterOrientation); + } + if (lot.exteriorTb.getVertices().size() > + 0) { + Ogre::MeshPtr mesh = + Ogre::MeshManager::getSingleton() + .getByName( + lot.queueData + .meshName + + "_exterior"); + if (mesh) + Ogre::MeshManager:: + getSingleton() + .remove(mesh); + std::cout + << "vertex count: " + << lot.exteriorTb + .getVertices() + .size() + << std::endl; + mesh = lot.exteriorTb.transformToMesh( + lot.queueData.meshName + + "_exterior"); + /* debug save mesh */ + // serializeMesh(mesh, meshName); + Ogre::LodConfig config(mesh); + setupLods(config); + addMeshToStatic( + lot.queueData.geo, mesh, + lot.queueData.material, + worldCenterPosition, + worldCenterOrientation); + addStaticBodyMesh( + lot.queueData.e, mesh, + worldCenterPosition, + worldCenterOrientation); + } + count++; + } + }); + promise->set_value(true); + }); + } + void operator()(flecs::entity e, const nlohmann::json &jdistrict, + int index, Ogre::SceneNode *sceneNode, + Ogre::StaticGeometry *geo) + { + createCells(e, jdistrict, index, sceneNode, geo); + } + void wait() + { + if (townCellsComplete.valid() && + townCellsComplete.wait_for(std::chrono::seconds(0)) != + std::future_status::ready) + townCellsComplete.wait(); + } +}; + +struct TownRoofs : TownTask { + std::shared_future townRoofsComplete; + void createTownRoofs(flecs::entity e, const nlohmann::json &jdistrict, int index, Ogre::SceneNode *sceneNode, Ogre::StaticGeometry *geo) -{ - Ogre::MaterialPtr townMaterial = createTownMaterial(e); - const nlohmann::json &jp = jdistrict; - nlohmann::json jlots = nlohmann::json::array(); - float baseHeight = 4.0f; - Ogre::Vector3 localPosition(0, 0, 0); - Ogre::Quaternion localRotation = Ogre::Quaternion::IDENTITY; - Ogre::Vector3 centerPosition = sceneNode->_getDerivedPosition(); - Ogre::Quaternion centerOrientation = - sceneNode->_getDerivedOrientation(); - float delevation = 0.0f; - float radius = 50.0f; - if (jp.find("elevation") != jp.end()) - delevation = jp["elevation"].get(); - if (jp.find("radius") != jp.end()) - radius = jp["radius"].get(); - from_json(jp["position"], localPosition); - from_json(jp["rotation"], localRotation); - centerPosition = centerPosition + localPosition + - Ogre::Vector3(0, delevation, 0); - centerOrientation = centerOrientation * localRotation; - if (jdistrict.find("lots") != jdistrict.end()) - jlots = jdistrict["lots"]; - for (const auto &jb : jlots) { - float angle = 0.0f; - int depth = 10; - int width = 10; - float distance = radius; - float elevation = 0.0f; - std::cout << jb.dump() << std::endl; - if (jb.find("angle") != jb.end()) - angle = jb["angle"].get(); - if (jb.find("depth") != jb.end()) - depth = jb["depth"].get(); - if (jb.find("width") != jb.end()) - width = jb["width"].get(); - if (jb.find("elevation") != jb.end()) - elevation = jb["elevation"].get(); + { + struct QueueData { + Procedural::TriangleBuffer tb; + Ogre::Vector3 position; + Ogre::Quaternion rotation; + nlohmann::json jp; + Procedural::Shape *shape; + Ogre::String meshName; + Ogre::MaterialPtr material; + Ogre::StaticGeometry *geo; + flecs::entity e; + }; + struct LotData { + int width, depth; + float angle, elevation; + nlohmann::json jlot; + Procedural::TriangleBuffer tb; + Procedural::TriangleBuffer tbTop; + Procedural::TriangleBuffer tbSide; + struct QueueData queueData; + }; - OgreAssert(width > 1 && depth > 1 && baseHeight > 1, - "Bad stuff happen"); + if (townRoofsComplete.valid() && + townRoofsComplete.wait_for(std::chrono::seconds(0)) != + std::future_status::ready) + townRoofsComplete.wait(); - Ogre::Quaternion rotation = Ogre::Quaternion( - Ogre::Degree(angle), Ogre::Vector3::UNIT_Y); - Ogre::Vector3 offset = centerOrientation * rotation * - (Ogre::Vector3::UNIT_Z * distance); - Ogre::Vector3 worldCenterPosition = - centerPosition + offset + - Ogre::Vector3(0, elevation, 0); - Ogre::Quaternion worldCenterOrientation = - centerOrientation * rotation; - float outOffset = 1.05f; - if (jb.find("furniture_cells") != jb.end()) { - for (auto &jfcell : jb["furniture_cells"]) { - int x = jfcell["x"].get(); - int y = jfcell["y"].get(); - int z = jfcell["z"].get(); - nlohmann::json furniture = jfcell["furniture"]; - Ogre::Vector3 cellOffset(x * 2.0f, y * 4.0f, - z * 2.0f); - Ogre::Vector3 offsetX = worldCenterOrientation * - Ogre::Vector3::UNIT_X * - (float)x * 2.0f; - Ogre::Vector3 offsetZ = worldCenterOrientation * - Ogre::Vector3::UNIT_Z * - (float)z * 2.0f; - Ogre::Vector3 offsetY(0, y * 4.0f, 0); - if (furniture.find("mesh") != furniture.end()) { + auto promise = std::make_shared >(); + townRoofsComplete = promise->get_future(); + Ogre::MaterialPtr townMaterial; + townMaterial = Ogre::MaterialManager::getSingleton().getByName( + "proceduralMaterialTown" + + Ogre::StringConverter::toString(e.id())); + nlohmann::json jlots = nlohmann::json::array(); + if (jdistrict.find("lots") != jdistrict.end()) + jlots = jdistrict["lots"]; + Ogre::Vector3 centerPosition = sceneNode->_getDerivedPosition(); + Ogre::Quaternion centerOrientation = + sceneNode->_getDerivedOrientation(); + const nlohmann::json &jp = jdistrict; + Ogre::Vector3 localPosition(0, 0, 0); + Ogre::Quaternion localRotation = Ogre::Quaternion::IDENTITY; + float delevation = 0.0f; + float radius = 50.0f; + if (jp.find("elevation") != jp.end()) + delevation = jp["elevation"].get(); + if (jp.find("radius") != jp.end()) + radius = jp["radius"].get(); + from_json(jp["position"], localPosition); + from_json(jp["rotation"], localRotation); + centerPosition = centerPosition + localPosition + + Ogre::Vector3(0, delevation, 0); + centerOrientation = centerOrientation * localRotation; + static std::vector lots; + lots.resize(0); + lots.reserve(jlots.size()); + { + int count = 0; + for (const auto &jb : jlots) { + if (jb.find("roofs") != jb.end()) { + Procedural::TriangleBuffer tb; + Procedural::TriangleBuffer tbTop; + Procedural::TriangleBuffer tbSide; Ogre::String meshName = - furniture["mesh"] - .get(); + "roofbase" + + Ogre::StringConverter::toString( + count) + + "_" + + Ogre::StringConverter::toString( + e.id()) + + "_" + + Ogre::StringConverter::toString( + index); Ogre::MeshPtr mesh = Ogre::MeshManager::getSingleton() .getByName(meshName); - if (!mesh) { - mesh = Ogre::MeshManager::getSingleton() - .load(meshName, - "General"); - Ogre::LodConfig meshconf(mesh); - setupLods(meshconf); + if (mesh) + Ogre::MeshManager::getSingleton() + .remove(mesh); + int depth = 10; + int width = 10; + float angle = 0.0f; + float elevation = 0.0f; + if (jb.find("depth") != jb.end()) + depth = jb["depth"].get(); + if (jb.find("width") != jb.end()) + width = jb["width"].get(); + if (jb.find("angle") != jb.end()) + angle = jb["angle"].get(); + if (jb.find("elevation") != jb.end()) + elevation = + jb["elevation"] + .get(); + OgreAssert(width > 1 && depth > 1, + "Bad stuff happen"); + if (jb.find("cells") != jb.end()) + lots.push_back( + { width, + depth, + angle, + elevation, + jb, + tb, + tbTop, + tbSide, + { tb, centerPosition, + centerOrientation, + jdistrict, + new Procedural::Shape, + meshName, + townMaterial, geo, + e } }); + } + count++; + } + } + Ogre::Root::getSingleton().getWorkQueue()->addTask([radius, + promise]() { + for (auto &lot : lots) { + for (auto &jroof : lot.jlot["roofs"]) { + int roofType = jroof["type"].get(); + int position_x = + jroof["position_x"].get(); + int position_y = + jroof["position_y"].get(); + int position_z = + jroof["position_z"].get(); + int width = jroof["size_x"].get(); + int depth = jroof["size_z"].get(); + float baseHeight = + jroof["base_height"] + .get(); + float maxHeight = + jroof["max_height"].get(); + float extend = 0.24f; + if (roofType == 0) { + Procedural::TriangleBuffer tbTop; + Procedural::TriangleBuffer + tbSide; + Procedural::BoxGenerator() + .setSizeX(2.0f * + (float)width) + .setSizeY(baseHeight) + .setSizeZ(2.0f * + (float)depth) + .setNumSegY(1) + .setNumSegX(1) + .setNumSegZ(1) + .setEnableNormals(true) + .setPosition(Ogre::Vector3( + (float)position_x * + 2.0f - + 1.0f + + (float)width * + 2.0f / + 2.0f, + (float)position_y * + 4.0f + + baseHeight / + 2.0f + + lot.elevation, + (float)position_z * + 2.0f - + 1.0f + + (float)depth * + 2.0f / + 2.0f)) + .addToTriangleBuffer( + tbTop); + Procedural::BoxGenerator() + .setSizeX(2.0f * + (float)width) + .setSizeY(baseHeight + + extend * 2.0f) + .setSizeZ(extend) + .setNumSegY(1) + .setNumSegX(1) + .setNumSegZ(1) + .setEnableNormals(true) + .setPosition(Ogre::Vector3( + (float)position_x * + 2.0f - + 1.0f + + (float)width * + 2.0f / + 2.0f, + (float)position_y * + 4.0f + + baseHeight / + 2.0f + + lot.elevation + + extend, + (float)position_z * + 2.0f - + 1.0f + + (float)depth * + 2.0f / + 2.0f + + (float)depth * + 2.0f / + 2.0f + + extend / + 2.0f)) + .addToTriangleBuffer( + tbSide); + Procedural::BoxGenerator() + .setSizeX(2.0f * + (float)width) + .setSizeY(baseHeight + + extend * 2.0f) + .setSizeZ(extend) + .setNumSegY(1) + .setNumSegX(1) + .setNumSegZ(1) + .setEnableNormals(true) + .setPosition(Ogre::Vector3( + (float)position_x * + 2.0f - + 1.0f + + (float)width * + 2.0f / + 2.0f, + (float)position_y * + 4.0f + + baseHeight / + 2.0f + + lot.elevation + + extend, + (float)position_z * + 2.0f - + 1.0f + + (float)depth * + 2.0f / + 2.0f - + (float)depth * + 2.0f / + 2.0f - + extend / + 2.0f)) + .addToTriangleBuffer( + tbSide); + Procedural::BoxGenerator() + .setSizeX(extend) + .setSizeY(baseHeight + + extend * 2.0f) + .setSizeZ( + 2.0f * (float)depth + + extend * 2.0f) + .setNumSegY(1) + .setNumSegX(1) + .setNumSegZ(1) + .setEnableNormals(true) + .setPosition(Ogre::Vector3( + (float)position_x * + 2.0f - + 1.0f + + (float)width * + 2.0f / + 2.0f + + (float)width * + 2.0f / + 2.0f + + extend / + 2.0f, + (float)position_y * + 4.0f + + baseHeight / + 2.0f + + lot.elevation + + extend, + (float)position_z * + 2.0f - + 1.0f + + (float)depth * + 2.0f / + 2.0f)) + .addToTriangleBuffer( + tbSide); + Procedural::BoxGenerator() + .setSizeX(extend) + .setSizeY(baseHeight + + extend * 2.0f) + .setSizeZ( + 2.0f * (float)depth + + extend * 2.0f) + .setNumSegY(1) + .setNumSegX(1) + .setNumSegZ(1) + .setEnableNormals(true) + .setPosition(Ogre::Vector3( + (float)position_x * + 2.0f - + 1.0f + + (float)width * + 2.0f / + 2.0f - + (float)width * + 2.0f / + 2.0f - + extend / + 2.0f, + (float)position_y * + 4.0f + + baseHeight / + 2.0f + + lot.elevation + + extend, + (float)position_z * + 2.0f - + 1.0f + + (float)depth * + 2.0f / + 2.0f)) + .addToTriangleBuffer( + tbSide); + OgreAssert( + tbTop.getVertices() + .size() > + 8, + "bad box"); + clampUV(lot.queueData.e, tbTop, + "roofTop"); + clampUV(lot.queueData.e, tbSide, + "roofSide"); + lot.tbTop.append(tbTop); + lot.tbSide.append(tbSide); + } else if (roofType == 1) { + Procedural::TriangleBuffer tbTop; + Procedural::TriangleBuffer + tbSide; + float q = 2.0f * (float)width / + 2.0f; + float m = 1.0f; + float d = 1.4142f * (q + m); + Procedural::BoxGenerator() + .setSizeX(d) + .setSizeY(baseHeight / + 2.0f) + .setSizeZ(2.0f * + (float)depth) + .setNumSegY(1) + .setNumSegX(1) + .setNumSegZ(1) + .setEnableNormals(true) + .setOrientation(Ogre::Quaternion( + Ogre::Degree(45), + Ogre::Vector3:: + NEGATIVE_UNIT_Z)) + .setPosition(Ogre::Vector3( + (float)position_x * + 2.0f - + 1.0f + + (float)q + + q / 2.0f + + m / 2.0f, + (float)position_y * + 4.0f + + baseHeight / + 2.0f + + lot.elevation + + q / 2.0f - + m / 2.0f + + baseHeight / + 4.0f, + (float)position_z * + 2.0f - + 1.0f + + (float)depth * + 2.0f / + 2.0f)) + .addToTriangleBuffer( + tbTop); + Procedural::BoxGenerator() + .setSizeX(d) + .setSizeY(baseHeight / + 2.0f) + .setSizeZ(2.0f * + (float)depth) + .setNumSegY(1) + .setNumSegX(1) + .setNumSegZ(1) + .setEnableNormals(true) + .setOrientation(Ogre::Quaternion( + Ogre::Degree( + -45), + Ogre::Vector3:: + NEGATIVE_UNIT_Z)) + .setPosition(Ogre::Vector3( + (float)position_x * + 2.0f - + 1.0f + + q / 2.0f - + m / 2.0f, + (float)position_y * + 4.0f + + baseHeight / + 2.0f + + lot.elevation + + q / 2.0f - + m / 2.0f + + baseHeight / + 4.0f, + (float)position_z * + 2.0f - + 1.0f + + (float)depth * + 2.0f / + 2.0f)) + .addToTriangleBuffer( + tbTop); + Procedural::BoxGenerator() + .setSizeX( + 2.0f * (float)width + + extend * 2.0f) + .setSizeY(q + + baseHeight * + 2.5f) + .setSizeZ(extend) + .setNumSegX(3) + .setNumSegY(3) + .setNumSegZ(1) + .setEnableNormals(true) + .setPosition(Ogre::Vector3( + (float)position_x * + 2.0f - + 1.0f + + q, + (float)position_y * + 4.0f + + lot.elevation + + q / 2.0f, + (float)position_z * + 2.0f - + 1.0f + + (float)depth * + 2.0f / + 2.0f + + (float)depth * + 2.0f / + 2.0f + + extend / + 2.0f)) + .addToTriangleBuffer( + tbSide); + Procedural::BoxGenerator() + .setSizeX( + 2.0f * (float)width + + extend * 2.0f) + .setSizeY(q + + baseHeight * + 2.5f) + .setSizeZ(extend) + .setNumSegX(3) + .setNumSegY(3) + .setNumSegZ(1) + .setEnableNormals(true) + .setPosition(Ogre::Vector3( + (float)position_x * + 2.0f - + 1.0f + + q, + (float)position_y * + 4.0f + + lot.elevation + + q / 2.0f, + (float)position_z * + 2.0f - + 1.0f + + (float)depth * + 2.0f / + 2.0f - + (float)depth * + 2.0f / + 2.0f - + extend / + 2.0f)) + .addToTriangleBuffer( + tbSide); + float minY = 0.0f; + float maxY = 0.0f; + float maxX = 0.0f; + float minX = 0.0f; + int count = 0; + for (auto &v : + tbSide.getVertices()) { + if (count == 0) { + minY = v.mPosition + .y; + maxY = v.mPosition + .y; + minX = v.mPosition + .x; + maxX = v.mPosition + .x; + } else { + if (v.mPosition + .y > + maxY) + maxY = v.mPosition + .y; + if (v.mPosition + .y < + minY) + minY = v.mPosition + .y; + if (v.mPosition + .x > + maxX) + maxX = v.mPosition + .x; + if (v.mPosition + .x < + minX) + minX = v.mPosition + .x; + } + count++; + } + float midX = + minX + + (maxX - minX) / 2.0f; + for (auto &v : + tbSide.getVertices()) { + float md = + maxY - + v.mPosition.y; + float hm = + v.mPosition.x - + midX; + float he = + (hm / ((maxX - + minX) / + 2.0f)) * + md; + v.mPosition.x = + he + midX; + } + clampUV(lot.queueData.e, tbTop, + "roofTop"); + clampUV(lot.queueData.e, tbSide, + "roofSide"); + lot.tbTop.append(tbTop); + lot.tbTop.append(tbSide); + } else if (roofType == 2) { + Procedural::TriangleBuffer tbTop; + Procedural::TriangleBuffer + tbSide; + float q = 2.0f * (float)depth / + 2.0f; + float m = 1.0f; + float d = 1.4142f * (q + m); + Procedural::BoxGenerator() + .setSizeX(2.0f * + (float)width) + .setSizeY(baseHeight / + 2.0f) + .setSizeZ(d) + .setNumSegY(1) + .setNumSegX(1) + .setNumSegZ(1) + .setEnableNormals(true) + .setOrientation(Ogre::Quaternion( + Ogre::Degree(45), + Ogre::Vector3:: + NEGATIVE_UNIT_X)) + .setPosition(Ogre::Vector3( + (float)position_x * + 2.0f - + 1.0f + + (float)width * + 2.0f / + 2.0f, + (float)position_y * + 4.0f + + baseHeight / + 2.0f + + lot.elevation + + q / 2.0f - + m / 2.0f + + baseHeight / + 4.0f, + (float)position_z * + 2.0f - + 1.0f + + q / 2.0f - + m / 2.0f)) + .addToTriangleBuffer( + tbTop); + Procedural::BoxGenerator() + .setSizeX(2.0f * + (float)width) + .setSizeY(baseHeight / + 2.0f) + .setSizeZ(d) + .setNumSegX(1) + .setNumSegY(1) + .setNumSegZ(1) + .setEnableNormals(true) + .setOrientation(Ogre::Quaternion( + Ogre::Degree( + -45), + Ogre::Vector3:: + NEGATIVE_UNIT_X)) + .setPosition(Ogre::Vector3( + (float)position_x * + 2.0f - + 1.0f + + (float)width * + 2.0f / + 2.0f, + (float)position_y * + 4.0f + + baseHeight / + 2.0f + + lot.elevation + + q / 2.0f - + m / 2.0f + + baseHeight / + 4.0f, + (float)position_z * + 2.0f - + 1.0f + + q + + q / 2.0f + + m / 2.0f)) + .addToTriangleBuffer( + tbTop); + Procedural::BoxGenerator() + .setSizeX(extend) + .setSizeY(q + + baseHeight * + 2.5f) + .setSizeZ( + 2.0f * (float)depth + + 2.0f * baseHeight * + 3.0f) + .setNumSegX(2) + .setNumSegY(2) + .setNumSegZ(1) + .setEnableNormals(true) + .setPosition(Ogre::Vector3( + (float)position_x * + 2.0f - + 1.0f + + (float)width * + 2.0f / + 2.0f + + (float)width * + 2.0f / + 2.0f + + extend / + 2.0f, + (float)position_y * + 4.0f + + lot.elevation + + q / 2.0f, + (float)position_z * + 2.0f - + 1.0f + + (float)depth * + 2.0f / + 2.0f)) + .addToTriangleBuffer( + tbSide); + Procedural::BoxGenerator() + .setSizeX(extend) + .setSizeY(q + + baseHeight * + 2.5f) + .setSizeZ( + 2.0f * (float)depth + + 2.0f * baseHeight * + 3.0f) + .setNumSegX(2) + .setNumSegY(2) + .setNumSegZ(1) + .setEnableNormals(true) + .setPosition(Ogre::Vector3( + (float)position_x * + 2.0f - + 1.0f + + (float)width * + 2.0f / + 2.0f - + (float)width * + 2.0f / + 2.0f - + extend / + 2.0f, + (float)position_y * + 4.0f + + lot.elevation + + q / 2.0f, + (float)position_z * + 2.0f - + 1.0f + + (float)depth * + 2.0f / + 2.0f)) + .addToTriangleBuffer( + tbSide); + float minY = 0.0f; + float maxY = 0.0f; + float maxZ = 0.0f; + float minZ = 0.0f; + int count = 0; + for (auto &v : + tbSide.getVertices()) { + if (count == 0) { + minY = v.mPosition + .y; + maxY = v.mPosition + .y; + minZ = v.mPosition + .z; + maxZ = v.mPosition + .z; + } else { + if (v.mPosition + .y > + maxY) + maxY = v.mPosition + .y; + if (v.mPosition + .y < + minY) + minY = v.mPosition + .y; + if (v.mPosition + .z > + maxZ) + maxZ = v.mPosition + .z; + if (v.mPosition + .z < + minZ) + minZ = v.mPosition + .z; + } + count++; + } + float midZ = + minZ + + (maxZ - minZ) / 2.0f; + for (auto &v : + tbSide.getVertices()) { + float md = + maxY - + v.mPosition.y; + float hm = + v.mPosition.z - + midZ; + float he = + (hm / ((maxZ - + minZ) / + 2.0f)) * + md; + v.mPosition.z = + he + midZ; + } + clampUV(lot.queueData.e, tbTop, + "roofTop"); + clampUV(lot.queueData.e, tbSide, + "roofSide"); + lot.tbTop.append(tbTop); + lot.tbSide.append(tbSide); } - int rotation = 2; - if (jfcell.find("rotation") != - jfcell.end()) - rotation = jfcell["rotation"] - .get(); - - Ogre::Vector3 offset = - worldCenterOrientation * - Ogre::Vector3::UNIT_Z * 0.0f; - Ogre::Entity *ent = - ECS::get() - .mScnMgr->createEntity( - meshName); - geo->addEntity( - ent, - worldCenterPosition + offsetX + - offsetZ + offsetY + - offset, - worldCenterOrientation * + } + if (lot.tbTop.getVertices().size() > 0) + lot.tb.append(lot.tbTop); + if (lot.tbSide.getVertices().size() > 0) + lot.tb.append(lot.tbSide); + } + Ogre::Root::getSingleton().getWorkQueue()->addMainThreadTask([radius, + promise]() { + { + int count = 0; + for (auto lot : lots) { + std::cout + << count + << ": vertices: " + << lot.tb.getVertices() + .size() + << " " + << lot.tbSide + .getVertices() + .size() + << std::endl; + count++; + } + } + for (auto lot : lots) { + { + float distance = radius; + Ogre::Quaternion rotation = Ogre::Quaternion( Ogre::Degree( - 90.0f * - (float)rotation), + lot.angle), Ogre::Vector3:: - UNIT_Y)); - ECS::get() - .mScnMgr->destroyEntity(ent); + UNIT_Y); + Ogre::Vector3 offset = + lot.queueData.rotation * + rotation * + (Ogre::Vector3::UNIT_Z * + distance); + Ogre::MeshPtr mesh = + Ogre::MeshManager::getSingleton() + .getByName( + lot.queueData + .meshName); + if (mesh) + Ogre::MeshManager:: + getSingleton() + .remove(mesh); + if (lot.tb.getVertices().size() > + 0) { + mesh = lot.tb.transformToMesh( + lot.queueData + .meshName); + Ogre::LodConfig config( + mesh); + setupLods(config); + addMeshToStatic( + lot.queueData + .geo, + mesh, + lot.queueData + .material, + lot.queueData.position + + offset, + lot.queueData.rotation * + rotation); + addStaticBodyMesh( + lot.queueData.e, + mesh, + lot.queueData.position + + offset, + lot.queueData.rotation * + rotation); + } + } + } + }); + promise->set_value(true); + }); + } + void operator()(flecs::entity e, const nlohmann::json &jdistrict, + int index, Ogre::SceneNode *sceneNode, + Ogre::StaticGeometry *geo) + { + createTownRoofs(e, jdistrict, index, sceneNode, geo); + } + void wait() + { + if (townRoofsComplete.valid() && + townRoofsComplete.wait_for(std::chrono::seconds(0)) != + std::future_status::ready) + townRoofsComplete.wait(); + } +}; + +struct TownDecorateWindows : TownTask { + std::shared_future townDecorateWindowsComplete; + void createDecorateWindows(flecs::entity e, + const nlohmann::json &jdistrict, int index, + Ogre::SceneNode *sceneNode, + Ogre::StaticGeometry *geo) + { + // FIXME: make furniture placement a background task. + Ogre::MaterialPtr townMaterial = createTownMaterial(e); + const nlohmann::json &jp = jdistrict; + nlohmann::json jlots = nlohmann::json::array(); + float baseHeight = 4.0f; + Ogre::Vector3 localPosition(0, 0, 0); + Ogre::Quaternion localRotation = Ogre::Quaternion::IDENTITY; + Ogre::Vector3 centerPosition = sceneNode->_getDerivedPosition(); + Ogre::Quaternion centerOrientation = + sceneNode->_getDerivedOrientation(); + float delevation = 0.0f; + float radius = 50.0f; + if (jp.find("elevation") != jp.end()) + delevation = jp["elevation"].get(); + if (jp.find("radius") != jp.end()) + radius = jp["radius"].get(); + from_json(jp["position"], localPosition); + from_json(jp["rotation"], localRotation); + centerPosition = centerPosition + localPosition + + Ogre::Vector3(0, delevation, 0); + centerOrientation = centerOrientation * localRotation; + if (jdistrict.find("lots") != jdistrict.end()) + jlots = jdistrict["lots"]; + for (const auto &jb : jlots) { + float angle = 0.0f; + int depth = 10; + int width = 10; + float distance = radius; + float elevation = 0.0f; + std::cout << jb.dump() << std::endl; + if (jb.find("angle") != jb.end()) + angle = jb["angle"].get(); + if (jb.find("depth") != jb.end()) + depth = jb["depth"].get(); + if (jb.find("width") != jb.end()) + width = jb["width"].get(); + if (jb.find("elevation") != jb.end()) + elevation = jb["elevation"].get(); + + OgreAssert(width > 1 && depth > 1 && baseHeight > 1, + "Bad stuff happen"); + + Ogre::Quaternion rotation = Ogre::Quaternion( + Ogre::Degree(angle), Ogre::Vector3::UNIT_Y); + Ogre::Vector3 offset = + centerOrientation * rotation * + (Ogre::Vector3::UNIT_Z * distance); + Ogre::Vector3 worldCenterPosition = + centerPosition + offset + + Ogre::Vector3(0, elevation, 0); + Ogre::Quaternion worldCenterOrientation = + centerOrientation * rotation; + float outOffset = 1.05f; + float windowVerticalOffset = 0.8f; + if (jb.find("cells") != jb.end()) { + for (auto &jcell : jb["cells"]) { + int x = jcell["x"].get(); + int y = jcell["y"].get(); + int z = jcell["z"].get(); + uint64_t flags = + jcell["flags"].get(); + Ogre::Vector3 cellOffset( + x * 2.0f, y * 4.0f, z * 2.0f); + Ogre::Vector3 offsetX = + worldCenterOrientation * + Ogre::Vector3::UNIT_X * + (float)x * 2.0f; + Ogre::Vector3 offsetZ = + worldCenterOrientation * + Ogre::Vector3::UNIT_Z * + (float)z * 2.0f; + Ogre::Vector3 offsetY(0, y * 4.0f, 0); + if (Items::CellsScript::isBit( + jcell, "windowz+")) { + Ogre::Entity *ent = + ECS::get() + .mScnMgr + ->createEntity( + "window-cell", + "window-frame1"); + ent->setMaterial(townMaterial); + Ogre::Vector3 offset = + worldCenterOrientation * + Ogre::Vector3:: + UNIT_Z * + outOffset + + Ogre::Vector3::UNIT_Y * + windowVerticalOffset; + geo->addEntity( + ent, + worldCenterPosition + + offsetX + + offsetZ + + offsetY + + offset, + worldCenterOrientation); + ECS::get() + .mScnMgr->destroyEntity( + ent); + } + if (Items::CellsScript::isBit( + jcell, "windowz-")) { + Ogre::Entity *ent = + ECS::get() + .mScnMgr + ->createEntity( + "window-cell", + "window-frame1"); + ent->setMaterial(townMaterial); + Ogre::Vector3 offset = + worldCenterOrientation * + Ogre::Vector3:: + NEGATIVE_UNIT_Z * + outOffset + + Ogre::Vector3::UNIT_Y * + windowVerticalOffset; + Ogre::Quaternion rotation = + worldCenterOrientation * + Ogre::Quaternion( + Ogre::Degree( + 180), + Ogre::Vector3:: + UNIT_Y); + geo->addEntity( + ent, + worldCenterPosition + + offsetX + + offsetZ + + offsetY + + offset, + rotation); + ECS::get() + .mScnMgr->destroyEntity( + ent); + } + if (Items::CellsScript::isBit( + jcell, "windowx+")) { + Ogre::Entity *ent = + ECS::get() + .mScnMgr + ->createEntity( + "window-cell", + "window-frame1"); + ent->setMaterial(townMaterial); + Ogre::Vector3 offset = + worldCenterOrientation * + Ogre::Vector3:: + UNIT_X * + outOffset + + Ogre::Vector3::UNIT_Y * + windowVerticalOffset; + Ogre::Quaternion rotation = + worldCenterOrientation * + Ogre::Quaternion( + Ogre::Degree( + 90), + Ogre::Vector3:: + UNIT_Y); + geo->addEntity( + ent, + worldCenterPosition + + offsetX + + offsetZ + + offsetY + + offset, + rotation); + ECS::get() + .mScnMgr->destroyEntity( + ent); + } + if (Items::CellsScript::isBit( + jcell, "windowx-")) { + Ogre::Entity *ent = + ECS::get() + .mScnMgr + ->createEntity( + "window-cell", + "window-frame1"); + ent->setMaterial(townMaterial); + Ogre::Vector3 offset = + worldCenterOrientation * + Ogre::Vector3:: + NEGATIVE_UNIT_X * + outOffset + + Ogre::Vector3::UNIT_Y * + windowVerticalOffset; + Ogre::Quaternion rotation = + worldCenterOrientation * + Ogre::Quaternion( + Ogre::Degree( + -90), + Ogre::Vector3:: + UNIT_Y); + geo->addEntity( + ent, + worldCenterPosition + + offsetX + + offsetZ + + offsetY + + offset, + rotation); + ECS::get() + .mScnMgr->destroyEntity( + ent); + } } } } } -} - -void createDecorateDoors(flecs::entity e, const nlohmann::json &jdistrict, - int index, Ogre::SceneNode *sceneNode, - Ogre::StaticGeometry *geo) -{ - Ogre::MaterialPtr townMaterial = createTownMaterial(e); - const nlohmann::json &jp = jdistrict; - nlohmann::json jlots = nlohmann::json::array(); - float baseHeight = 4.0f; - Ogre::Vector3 localPosition(0, 0, 0); - Ogre::Quaternion localRotation = Ogre::Quaternion::IDENTITY; - Ogre::Vector3 centerPosition = sceneNode->_getDerivedPosition(); - Ogre::Quaternion centerOrientation = - sceneNode->_getDerivedOrientation(); - float delevation = 0.0f; - float radius = 50.0f; - if (jp.find("elevation") != jp.end()) - delevation = jp["elevation"].get(); - if (jp.find("radius") != jp.end()) - radius = jp["radius"].get(); - from_json(jp["position"], localPosition); - from_json(jp["rotation"], localRotation); - centerPosition = centerPosition + localPosition + - Ogre::Vector3(0, delevation, 0); - centerOrientation = centerOrientation * localRotation; - if (jdistrict.find("lots") != jdistrict.end()) - jlots = jdistrict["lots"]; - for (const auto &jb : jlots) { - float angle = 0.0f; - int depth = 10; - int width = 10; - float distance = radius; - float elevation = 0.0f; - std::cout << jb.dump() << std::endl; - if (jb.find("angle") != jb.end()) - angle = jb["angle"].get(); - if (jb.find("depth") != jb.end()) - depth = jb["depth"].get(); - if (jb.find("width") != jb.end()) - width = jb["width"].get(); - if (jb.find("elevation") != jb.end()) - elevation = jb["elevation"].get(); - - OgreAssert(width > 1 && depth > 1 && baseHeight > 1, - "Bad stuff happen"); - - Ogre::Quaternion rotation = Ogre::Quaternion( - Ogre::Degree(angle), Ogre::Vector3::UNIT_Y); - Ogre::Vector3 offset = centerOrientation * rotation * - (Ogre::Vector3::UNIT_Z * distance); - Ogre::Vector3 worldCenterPosition = - centerPosition + offset + - Ogre::Vector3(0, elevation, 0); - Ogre::Quaternion worldCenterOrientation = - centerOrientation * rotation; - float outOffset = 1.05f; - if (jb.find("cells") != jb.end()) { - for (auto &jcell : jb["cells"]) { - int x = jcell["x"].get(); - int y = jcell["y"].get(); - int z = jcell["z"].get(); - uint64_t flags = jcell["flags"].get(); - Ogre::Vector3 cellOffset(x * 2.0f, y * 4.0f, - z * 2.0f); - Ogre::Vector3 offsetX = worldCenterOrientation * - Ogre::Vector3::UNIT_X * - (float)x * 2.0f; - Ogre::Vector3 offsetZ = worldCenterOrientation * - Ogre::Vector3::UNIT_Z * - (float)z * 2.0f; - Ogre::Vector3 offsetY(0, y * 4.0f, 0); - if (Items::CellsScript::isBit(jcell, - "doorz+")) { - Ogre::Entity *ent = - ECS::get() - .mScnMgr->createEntity( - "door-cell", - "external-door-frame1"); - ent->setMaterial(townMaterial); - Ogre::Vector3 offset = - worldCenterOrientation * - Ogre::Vector3::UNIT_Z * - outOffset; - geo->addEntity(ent, - worldCenterPosition + - offsetX + - offsetZ + - offsetY + offset, - worldCenterOrientation); - ECS::get() - .mScnMgr->destroyEntity(ent); - } else if (Items::CellsScript::isBit( - jcell, "idoorz+")) { - Ogre::Entity *ent = - ECS::get() - .mScnMgr->createEntity( - "idoor-cell", - "internal-door-frame1"); - ent->setMaterial(townMaterial); - Ogre::Vector3 offset = - worldCenterOrientation * - Ogre::Vector3::UNIT_Z * - outOffset; - geo->addEntity(ent, - worldCenterPosition + - offsetX + - offsetZ + - offsetY + offset, - worldCenterOrientation); - ECS::get() - .mScnMgr->destroyEntity(ent); - } - if (Items::CellsScript::isBit(jcell, - "doorz-")) { - Ogre::Entity *ent = - ECS::get() - .mScnMgr->createEntity( - "door-cell", - "external-door-frame1"); - ent->setMaterial(townMaterial); - Ogre::Vector3 offset = - worldCenterOrientation * - Ogre::Vector3::NEGATIVE_UNIT_Z * - outOffset; - Ogre::Quaternion rotation = - worldCenterOrientation * - Ogre::Quaternion( - Ogre::Degree(180), - Ogre::Vector3::UNIT_Y); - geo->addEntity(ent, - worldCenterPosition + - offsetX + - offsetZ + - offsetY + offset, - rotation); - ECS::get() - .mScnMgr->destroyEntity(ent); - } else if (Items::CellsScript::isBit( - jcell, "idoorz-")) { - Ogre::Entity *ent = - ECS::get() - .mScnMgr->createEntity( - "idoor-cell", - "internal-door-frame1"); - ent->setMaterial(townMaterial); - Ogre::Vector3 offset = - worldCenterOrientation * - Ogre::Vector3::NEGATIVE_UNIT_Z * - outOffset; - Ogre::Quaternion rotation = - worldCenterOrientation * - Ogre::Quaternion( - Ogre::Degree(180), - Ogre::Vector3::UNIT_Y); - geo->addEntity(ent, - worldCenterPosition + - offsetX + - offsetZ + - offsetY + offset, - rotation); - ECS::get() - .mScnMgr->destroyEntity(ent); - } - if (Items::CellsScript::isBit(jcell, - "doorx+")) { - Ogre::Entity *ent = - ECS::get() - .mScnMgr->createEntity( - "door-cell", - "external-door-frame1"); - ent->setMaterial(townMaterial); - Ogre::Vector3 offset = - worldCenterOrientation * - Ogre::Vector3::UNIT_X * - outOffset; - Ogre::Quaternion rotation = - worldCenterOrientation * - Ogre::Quaternion( - Ogre::Degree(90), - Ogre::Vector3::UNIT_Y); - geo->addEntity(ent, - worldCenterPosition + - offsetX + - offsetZ + - offsetY + offset, - rotation); - ECS::get() - .mScnMgr->destroyEntity(ent); - } else if (Items::CellsScript::isBit( - jcell, "idoorx+")) { - Ogre::Entity *ent = - ECS::get() - .mScnMgr->createEntity( - "idoor-cell", - "internal-door-frame1"); - ent->setMaterial(townMaterial); - Ogre::Vector3 offset = - worldCenterOrientation * - Ogre::Vector3::UNIT_X * - outOffset; - Ogre::Quaternion rotation = - worldCenterOrientation * - Ogre::Quaternion( - Ogre::Degree(90), - Ogre::Vector3::UNIT_Y); - geo->addEntity(ent, - worldCenterPosition + - offsetX + - offsetZ + - offsetY + offset, - rotation); - ECS::get() - .mScnMgr->destroyEntity(ent); - } - if (Items::CellsScript::isBit(jcell, - "doorx-")) { - Ogre::Entity *ent = - ECS::get() - .mScnMgr->createEntity( - "door-cell", - "external-door-frame1"); - ent->setMaterial(townMaterial); - Ogre::Vector3 offset = - worldCenterOrientation * - Ogre::Vector3::NEGATIVE_UNIT_X * - outOffset; - Ogre::Quaternion rotation = - worldCenterOrientation * - Ogre::Quaternion( - Ogre::Degree(-90), - Ogre::Vector3::UNIT_Y); - geo->addEntity(ent, - worldCenterPosition + - offsetX + - offsetZ + - offsetY + offset, - rotation); - ECS::get() - .mScnMgr->destroyEntity(ent); - } else if (Items::CellsScript::isBit( - jcell, "idoorx-")) { - Ogre::Entity *ent = - ECS::get() - .mScnMgr->createEntity( - "idoor-cell", - "internal-door-frame1"); - ent->setMaterial(townMaterial); - Ogre::Vector3 offset = - worldCenterOrientation * - Ogre::Vector3::NEGATIVE_UNIT_X * - outOffset; - Ogre::Quaternion rotation = - worldCenterOrientation * - Ogre::Quaternion( - Ogre::Degree(-90), - Ogre::Vector3::UNIT_Y); - geo->addEntity(ent, - worldCenterPosition + - offsetX + - offsetZ + - offsetY + offset, - rotation); - ECS::get() - .mScnMgr->destroyEntity(ent); - } - } - } + void operator()(flecs::entity e, const nlohmann::json &jdistrict, + int index, Ogre::SceneNode *sceneNode, + Ogre::StaticGeometry *geo) + { + createDecorateWindows(e, jdistrict, index, sceneNode, geo); } -} + void wait() + { + if (townDecorateWindowsComplete.valid() && + townDecorateWindowsComplete.wait_for(std::chrono::seconds( + 0)) != std::future_status::ready) + townDecorateWindowsComplete.wait(); + } +}; +struct TownDecorateDoors : TownTask { + std::shared_future townDecorateDoorsComplete; + void createDecorateDoors(flecs::entity e, + const nlohmann::json &jdistrict, int index, + Ogre::SceneNode *sceneNode, + Ogre::StaticGeometry *geo) + { + // FIXME: make furniture placement a background task. + Ogre::MaterialPtr townMaterial = createTownMaterial(e); + const nlohmann::json &jp = jdistrict; + nlohmann::json jlots = nlohmann::json::array(); + float baseHeight = 4.0f; + Ogre::Vector3 localPosition(0, 0, 0); + Ogre::Quaternion localRotation = Ogre::Quaternion::IDENTITY; + Ogre::Vector3 centerPosition = sceneNode->_getDerivedPosition(); + Ogre::Quaternion centerOrientation = + sceneNode->_getDerivedOrientation(); + float delevation = 0.0f; + float radius = 50.0f; + if (jp.find("elevation") != jp.end()) + delevation = jp["elevation"].get(); + if (jp.find("radius") != jp.end()) + radius = jp["radius"].get(); + from_json(jp["position"], localPosition); + from_json(jp["rotation"], localRotation); + centerPosition = centerPosition + localPosition + + Ogre::Vector3(0, delevation, 0); + centerOrientation = centerOrientation * localRotation; + if (jdistrict.find("lots") != jdistrict.end()) + jlots = jdistrict["lots"]; + for (const auto &jb : jlots) { + float angle = 0.0f; + int depth = 10; + int width = 10; + float distance = radius; + float elevation = 0.0f; + std::cout << jb.dump() << std::endl; + if (jb.find("angle") != jb.end()) + angle = jb["angle"].get(); + if (jb.find("depth") != jb.end()) + depth = jb["depth"].get(); + if (jb.find("width") != jb.end()) + width = jb["width"].get(); + if (jb.find("elevation") != jb.end()) + elevation = jb["elevation"].get(); -void createDecorateWindows(flecs::entity e, const nlohmann::json &jdistrict, - int index, Ogre::SceneNode *sceneNode, - Ogre::StaticGeometry *geo) -{ - Ogre::MaterialPtr townMaterial = createTownMaterial(e); - const nlohmann::json &jp = jdistrict; - nlohmann::json jlots = nlohmann::json::array(); - float baseHeight = 4.0f; - Ogre::Vector3 localPosition(0, 0, 0); - Ogre::Quaternion localRotation = Ogre::Quaternion::IDENTITY; - Ogre::Vector3 centerPosition = sceneNode->_getDerivedPosition(); - Ogre::Quaternion centerOrientation = - sceneNode->_getDerivedOrientation(); - float delevation = 0.0f; - float radius = 50.0f; - if (jp.find("elevation") != jp.end()) - delevation = jp["elevation"].get(); - if (jp.find("radius") != jp.end()) - radius = jp["radius"].get(); - from_json(jp["position"], localPosition); - from_json(jp["rotation"], localRotation); - centerPosition = centerPosition + localPosition + - Ogre::Vector3(0, delevation, 0); - centerOrientation = centerOrientation * localRotation; - if (jdistrict.find("lots") != jdistrict.end()) - jlots = jdistrict["lots"]; - for (const auto &jb : jlots) { - float angle = 0.0f; - int depth = 10; - int width = 10; - float distance = radius; - float elevation = 0.0f; - std::cout << jb.dump() << std::endl; - if (jb.find("angle") != jb.end()) - angle = jb["angle"].get(); - if (jb.find("depth") != jb.end()) - depth = jb["depth"].get(); - if (jb.find("width") != jb.end()) - width = jb["width"].get(); - if (jb.find("elevation") != jb.end()) - elevation = jb["elevation"].get(); + OgreAssert(width > 1 && depth > 1 && baseHeight > 1, + "Bad stuff happen"); - OgreAssert(width > 1 && depth > 1 && baseHeight > 1, - "Bad stuff happen"); - - Ogre::Quaternion rotation = Ogre::Quaternion( - Ogre::Degree(angle), Ogre::Vector3::UNIT_Y); - Ogre::Vector3 offset = centerOrientation * rotation * - (Ogre::Vector3::UNIT_Z * distance); - Ogre::Vector3 worldCenterPosition = - centerPosition + offset + - Ogre::Vector3(0, elevation, 0); - Ogre::Quaternion worldCenterOrientation = - centerOrientation * rotation; - float outOffset = 1.05f; - if (jb.find("cells") != jb.end()) { - for (auto &jcell : jb["cells"]) { - int x = jcell["x"].get(); - int y = jcell["y"].get(); - int z = jcell["z"].get(); - uint64_t flags = jcell["flags"].get(); - Ogre::Vector3 cellOffset(x * 2.0f, y * 4.0f, - z * 2.0f); - Ogre::Vector3 offsetX = worldCenterOrientation * - Ogre::Vector3::UNIT_X * - (float)x * 2.0f; - Ogre::Vector3 offsetZ = worldCenterOrientation * - Ogre::Vector3::UNIT_Z * - (float)z * 2.0f; - Ogre::Vector3 offsetY(0, y * 4.0f, 0); - if (Items::CellsScript::isBit(jcell, - "windowz+")) { - Ogre::Entity *ent = - ECS::get() - .mScnMgr->createEntity( - "window-cell", - "window-frame1"); - ent->setMaterial(townMaterial); - Ogre::Vector3 offset = + Ogre::Quaternion rotation = Ogre::Quaternion( + Ogre::Degree(angle), Ogre::Vector3::UNIT_Y); + Ogre::Vector3 offset = + centerOrientation * rotation * + (Ogre::Vector3::UNIT_Z * distance); + Ogre::Vector3 worldCenterPosition = + centerPosition + offset + + Ogre::Vector3(0, elevation, 0); + Ogre::Quaternion worldCenterOrientation = + centerOrientation * rotation; + float outOffset = 1.05f; + if (jb.find("cells") != jb.end()) { + for (auto &jcell : jb["cells"]) { + int x = jcell["x"].get(); + int y = jcell["y"].get(); + int z = jcell["z"].get(); + uint64_t flags = + jcell["flags"].get(); + Ogre::Vector3 cellOffset( + x * 2.0f, y * 4.0f, z * 2.0f); + Ogre::Vector3 offsetX = worldCenterOrientation * - Ogre::Vector3::UNIT_Z * - outOffset + - Ogre::Vector3::UNIT_Y * 0.5f; - geo->addEntity(ent, - worldCenterPosition + - offsetX + - offsetZ + - offsetY + offset, - worldCenterOrientation); - ECS::get() - .mScnMgr->destroyEntity(ent); - } - if (Items::CellsScript::isBit(jcell, - "windowz-")) { - Ogre::Entity *ent = - ECS::get() - .mScnMgr->createEntity( - "window-cell", - "window-frame1"); - ent->setMaterial(townMaterial); - Ogre::Vector3 offset = + Ogre::Vector3::UNIT_X * + (float)x * 2.0f; + Ogre::Vector3 offsetZ = worldCenterOrientation * + Ogre::Vector3::UNIT_Z * + (float)z * 2.0f; + Ogre::Vector3 offsetY(0, y * 4.0f, 0); + if (Items::CellsScript::isBit( + jcell, "doorz+")) { + Ogre::Entity *ent = + ECS::get() + .mScnMgr + ->createEntity( + "door-cell", + "external-door-frame1"); + ent->setMaterial(townMaterial); + Ogre::Vector3 offset = + worldCenterOrientation * + Ogre::Vector3::UNIT_Z * + outOffset; + geo->addEntity( + ent, + worldCenterPosition + + offsetX + + offsetZ + + offsetY + + offset, + worldCenterOrientation); + ECS::get() + .mScnMgr->destroyEntity( + ent); + } else if (Items::CellsScript::isBit( + jcell, "idoorz+")) { + Ogre::Entity *ent = + ECS::get() + .mScnMgr + ->createEntity( + "idoor-cell", + "internal-door-frame1"); + ent->setMaterial(townMaterial); + Ogre::Vector3 offset = + worldCenterOrientation * + Ogre::Vector3::UNIT_Z * + outOffset; + geo->addEntity( + ent, + worldCenterPosition + + offsetX + + offsetZ + + offsetY + + offset, + worldCenterOrientation); + ECS::get() + .mScnMgr->destroyEntity( + ent); + } + if (Items::CellsScript::isBit( + jcell, "doorz-")) { + Ogre::Entity *ent = + ECS::get() + .mScnMgr + ->createEntity( + "door-cell", + "external-door-frame1"); + ent->setMaterial(townMaterial); + Ogre::Vector3 offset = + worldCenterOrientation * Ogre::Vector3:: NEGATIVE_UNIT_Z * - outOffset + - Ogre::Vector3::UNIT_Y * 0.5f; - Ogre::Quaternion rotation = - worldCenterOrientation * - Ogre::Quaternion( - Ogre::Degree(180), - Ogre::Vector3::UNIT_Y); - geo->addEntity(ent, - worldCenterPosition + - offsetX + - offsetZ + - offsetY + offset, - rotation); - ECS::get() - .mScnMgr->destroyEntity(ent); - } - if (Items::CellsScript::isBit(jcell, - "windowx+")) { - Ogre::Entity *ent = + outOffset; + Ogre::Quaternion rotation = + worldCenterOrientation * + Ogre::Quaternion( + Ogre::Degree( + 180), + Ogre::Vector3:: + UNIT_Y); + geo->addEntity( + ent, + worldCenterPosition + + offsetX + + offsetZ + + offsetY + + offset, + rotation); ECS::get() - .mScnMgr->createEntity( - "window-cell", - "window-frame1"); - ent->setMaterial(townMaterial); - Ogre::Vector3 offset = - worldCenterOrientation * + .mScnMgr->destroyEntity( + ent); + } else if (Items::CellsScript::isBit( + jcell, "idoorz-")) { + Ogre::Entity *ent = + ECS::get() + .mScnMgr + ->createEntity( + "idoor-cell", + "internal-door-frame1"); + ent->setMaterial(townMaterial); + Ogre::Vector3 offset = + worldCenterOrientation * + Ogre::Vector3:: + NEGATIVE_UNIT_Z * + outOffset; + Ogre::Quaternion rotation = + worldCenterOrientation * + Ogre::Quaternion( + Ogre::Degree( + 180), + Ogre::Vector3:: + UNIT_Y); + geo->addEntity( + ent, + worldCenterPosition + + offsetX + + offsetZ + + offsetY + + offset, + rotation); + ECS::get() + .mScnMgr->destroyEntity( + ent); + } + if (Items::CellsScript::isBit( + jcell, "doorx+")) { + Ogre::Vector3 offset = + worldCenterOrientation * Ogre::Vector3::UNIT_X * - outOffset + - Ogre::Vector3::UNIT_Y * 0.5f; - Ogre::Quaternion rotation = - worldCenterOrientation * - Ogre::Quaternion( - Ogre::Degree(90), - Ogre::Vector3::UNIT_Y); - geo->addEntity(ent, - worldCenterPosition + - offsetX + - offsetZ + - offsetY + offset, - rotation); - ECS::get() - .mScnMgr->destroyEntity(ent); - } - if (Items::CellsScript::isBit(jcell, - "windowx-")) { - Ogre::Entity *ent = - ECS::get() - .mScnMgr->createEntity( - "window-cell", - "window-frame1"); - ent->setMaterial(townMaterial); - Ogre::Vector3 offset = - worldCenterOrientation * + outOffset; + Ogre::Quaternion rotation = + worldCenterOrientation * + Ogre::Quaternion( + Ogre::Degree( + 90), + Ogre::Vector3:: + UNIT_Y); + addMeshToStatic( + geo, + Ogre::MeshManager::getSingleton() + .getByName( + "external-door-frame1"), + townMaterial, + worldCenterPosition + + offsetX + + offsetZ + + offsetY + + offset, + rotation); + } else if (Items::CellsScript::isBit( + jcell, "idoorx+")) { + Ogre::Vector3 offset = + worldCenterOrientation * + Ogre::Vector3::UNIT_X * + outOffset; + Ogre::Quaternion rotation = + worldCenterOrientation * + Ogre::Quaternion( + Ogre::Degree( + 90), + Ogre::Vector3:: + UNIT_Y); + addMeshToStatic( + geo, + Ogre::MeshManager::getSingleton() + .getByName( + "internal-door-frame1"), + townMaterial, + worldCenterPosition + + offsetX + + offsetZ + + offsetY + + offset, + rotation); + } + if (Items::CellsScript::isBit( + jcell, "doorx-")) { + Ogre::Vector3 offset = + worldCenterOrientation * Ogre::Vector3:: NEGATIVE_UNIT_X * - outOffset + - Ogre::Vector3::UNIT_Y * 0.5f; - Ogre::Quaternion rotation = - worldCenterOrientation * - Ogre::Quaternion( - Ogre::Degree(-90), - Ogre::Vector3::UNIT_Y); - geo->addEntity(ent, - worldCenterPosition + - offsetX + - offsetZ + - offsetY + offset, - rotation); - ECS::get() - .mScnMgr->destroyEntity(ent); + outOffset; + Ogre::Quaternion rotation = + worldCenterOrientation * + Ogre::Quaternion( + Ogre::Degree( + -90), + Ogre::Vector3:: + UNIT_Y); + addMeshToStatic( + geo, + Ogre::MeshManager::getSingleton() + .getByName( + "external-door-frame1"), + townMaterial, + worldCenterPosition + + offsetX + + offsetZ + + offsetY + + offset, + rotation); + } else if (Items::CellsScript::isBit( + jcell, "idoorx-")) { + Ogre::Vector3 offset = + worldCenterOrientation * + Ogre::Vector3:: + NEGATIVE_UNIT_X * + outOffset; + Ogre::Quaternion rotation = + worldCenterOrientation * + Ogre::Quaternion( + Ogre::Degree( + -90), + Ogre::Vector3:: + UNIT_Y); + addMeshToStatic( + geo, + Ogre::MeshManager::getSingleton() + .getByName( + "internal-door-frame1"), + townMaterial, + worldCenterPosition + + offsetX + + offsetZ + + offsetY + + offset, + rotation); + } } } } } -} + void operator()(flecs::entity e, const nlohmann::json &jdistrict, + int index, Ogre::SceneNode *sceneNode, + Ogre::StaticGeometry *geo) + { + createDecorateDoors(e, jdistrict, index, sceneNode, geo); + } + void wait() + { + if (townDecorateDoorsComplete.valid() && + townDecorateDoorsComplete.wait_for(std::chrono::seconds( + 0)) != std::future_status::ready) + townDecorateDoorsComplete.wait(); + } +}; +struct TownDecorateFurniture : TownTask { + std::shared_future townDecorateFurnitureComplete; + void createDecorateFurniture(flecs::entity e, + const nlohmann::json &jdistrict, int index, + Ogre::SceneNode *sceneNode, + Ogre::StaticGeometry *geo) + { + // FIXME: make furniture placement a background task. + Ogre::MaterialPtr townMaterial = createTownMaterial(e); + const nlohmann::json &jp = jdistrict; + nlohmann::json jlots = nlohmann::json::array(); + float baseHeight = 4.0f; + Ogre::Vector3 localPosition(0, 0, 0); + Ogre::Quaternion localRotation = Ogre::Quaternion::IDENTITY; + Ogre::Vector3 centerPosition = sceneNode->_getDerivedPosition(); + Ogre::Quaternion centerOrientation = + sceneNode->_getDerivedOrientation(); + float delevation = 0.0f; + float radius = 50.0f; + if (jp.find("elevation") != jp.end()) + delevation = jp["elevation"].get(); + if (jp.find("radius") != jp.end()) + radius = jp["radius"].get(); + from_json(jp["position"], localPosition); + from_json(jp["rotation"], localRotation); + centerPosition = centerPosition + localPosition + + Ogre::Vector3(0, delevation, 0); + centerOrientation = centerOrientation * localRotation; + if (jdistrict.find("lots") != jdistrict.end()) + jlots = jdistrict["lots"]; + for (const auto &jb : jlots) { + float angle = 0.0f; + int depth = 10; + int width = 10; + float distance = radius; + float elevation = 0.0f; + std::cout << jb.dump() << std::endl; + if (jb.find("angle") != jb.end()) + angle = jb["angle"].get(); + if (jb.find("depth") != jb.end()) + depth = jb["depth"].get(); + if (jb.find("width") != jb.end()) + width = jb["width"].get(); + if (jb.find("elevation") != jb.end()) + elevation = jb["elevation"].get(); + + OgreAssert(width > 1 && depth > 1 && baseHeight > 1, + "Bad stuff happen"); + + Ogre::Quaternion rotation = Ogre::Quaternion( + Ogre::Degree(angle), Ogre::Vector3::UNIT_Y); + Ogre::Vector3 offset = + centerOrientation * rotation * + (Ogre::Vector3::UNIT_Z * distance); + Ogre::Vector3 worldCenterPosition = + centerPosition + offset + + Ogre::Vector3(0, elevation, 0); + Ogre::Quaternion worldCenterOrientation = + centerOrientation * rotation; + float outOffset = 1.05f; + if (jb.find("furniture_cells") != jb.end()) { + for (auto &jfcell : jb["furniture_cells"]) { + int x = jfcell["x"].get(); + int y = jfcell["y"].get(); + int z = jfcell["z"].get(); + nlohmann::json furniture = + jfcell["furniture"]; + Ogre::Vector3 cellOffset( + x * 2.0f, y * 4.0f, z * 2.0f); + Ogre::Vector3 offsetX = + worldCenterOrientation * + Ogre::Vector3::UNIT_X * + (float)x * 2.0f; + Ogre::Vector3 offsetZ = + worldCenterOrientation * + Ogre::Vector3::UNIT_Z * + (float)z * 2.0f; + Ogre::Vector3 offsetY(0, y * 4.0f, 0); + if (furniture.find("mesh") != + furniture.end()) { + Ogre::String meshName = + furniture["mesh"] + .get(); + Ogre::MeshPtr mesh = + Ogre::MeshManager::getSingleton() + .getByName( + meshName); + if (!mesh) { + if (Ogre::ResourceGroupManager::getSingleton() + .resourceExists( + "General", + meshName)) { + mesh = Ogre::MeshManager::getSingleton() + .load(meshName, + "General"); + Ogre::LodConfig meshconf( + mesh); + setupLods( + meshconf); + } + } + if (mesh) { + int rotation = 2; + if (jfcell.find( + "rotation") != + jfcell.end()) + rotation = + jfcell["rotation"] + .get(); + + Ogre::Vector3 offset = + worldCenterOrientation * + Ogre::Vector3:: + UNIT_Z * + 0.0f; + Ogre::Entity *ent = + ECS::get< + EngineData>() + .mScnMgr + ->createEntity( + meshName); + Ogre::SceneNode *node = + ECS::get< + EngineData>() + .mScnMgr + ->getRootSceneNode() + ->createChildSceneNode(); + node->_setDerivedPosition( + worldCenterPosition + + offsetX + + offsetZ + + offsetY + + offset); + node->_setDerivedOrientation( + worldCenterOrientation * + Ogre::Quaternion( + Ogre::Degree( + 90.0f * + (float)rotation), + Ogre::Vector3:: + UNIT_Y)); + node->attachObject(ent); + addStaticBodyMesh( + e, mesh, + worldCenterPosition + + offsetX + + offsetZ + + offsetY + + offset, + worldCenterOrientation * + Ogre::Quaternion( + Ogre::Degree( + 90.0f * + (float)rotation), + Ogre::Vector3:: + UNIT_Y)); + ECS::get() + .entity() + .child_of(e) + .set( + { node }); + if (furniture.find( + "sensors") != + furniture.end()) { + for (const auto & + sensor : + furniture["sensors"]) { + std::cout + << "SENSOR: " + << sensor.dump() + << std::endl; + std::cout + << furniture + .dump() + << std::endl; + Ogre::Vector3 + sensorPosition; + sensorPosition + .x = + sensor["position_x"] + .get(); + sensorPosition + .y = + sensor["position_y"] + .get(); + sensorPosition + .z = + sensor["position_z"] + .get(); + Ogre::Quaternion worldSensorRotation = + worldCenterOrientation * + Ogre::Quaternion( + Ogre::Degree( + 90.0f * + (float)rotation), + Ogre::Vector3:: + UNIT_Y); + Ogre::Vector3 worldSensorPosition = + worldCenterPosition + + offsetX + + offsetZ + + offsetY + + offset + + worldSensorRotation * + sensorPosition; + float height = + sensor["height"] + .get(); + float radius = + sensor["radius"] + .get(); + std::cout + << "sensor: " + << height + << " " + << radius + << std::endl; + if (ECS::get() + .has()) { + ActionNodeList::ActionNode + anode; + anode.action = + sensor["action"] + .get(); + anode.action_text = + sensor["action_text"] + .get(); + anode.props = + sensor; + anode.position = + worldSensorPosition; + anode.rotation = + worldSensorRotation; + ECS::get_mut< + ActionNodeList>() + .addNode( + anode); + } + + JPH::ShapeRefC shape2 = + JoltPhysicsWrapper::getSingleton() + .createCylinderShape( + height / + 2.0f, + radius); + JPH::ShapeRefC shape = + JoltPhysicsWrapper::getSingleton() + .createRotatedTranslatedShape( + Ogre::Vector3( + 0, + height / + 2.0f, + 0), + Ogre::Quaternion:: + IDENTITY, + shape2); + + JPH::BodyID id = + JoltPhysicsWrapper::getSingleton() + .createSensor( + shape, + worldSensorPosition, + worldSensorRotation, + JPH::EMotionType:: + Static, + Layers::SENSORS); + JoltPhysicsWrapper::getSingleton() + .addBody( + id, + JPH::EActivation:: + Activate); + JoltPhysicsWrapper::getSingleton() + .addContactListener( + id, + [sensor]( + const JoltPhysics:: + ContactListener::ContactReport + &report) + -> void { + std::cout + << sensor + << std::endl; +#if 0 + OgreAssert( + false, + "contact!"); +#endif + }); + // body for sensor + ECS::get() + .entity() + .child_of( + e) + .set( + id); +#if 0 + OgreAssert( + false, + "sensor"); +#endif + } + } + } + } + } + } + } + } + void operator()(flecs::entity e, const nlohmann::json &jdistrict, + int index, Ogre::SceneNode *sceneNode, + Ogre::StaticGeometry *geo) + { + createDecorateFurniture(e, jdistrict, index, sceneNode, geo); + } + void wait() + { + if (townDecorateFurnitureComplete.valid() && + townDecorateFurnitureComplete.wait_for(std::chrono::seconds( + 0)) != std::future_status::ready) + townDecorateFurnitureComplete.wait(); + } +}; void createTown(flecs::entity e, Ogre::SceneNode *sceneNode, - Ogre::StaticGeometry *geo) + Ogre::StaticGeometry *geo) { - std::cout << "createTown " << e.id() << std::endl; + std::cout << "createTown " << e.id() << std::endl; Ogre::MaterialPtr townMaterial = createTownMaterial(e); + static std::vector townTasks; + townTasks.clear(); createTownWindows(e); createTownDoors(e); { std::list destroy; + // Remove physics entities ECS::get() .query_builder() .with(flecs::ChildOf, e) @@ -3476,1129 +5639,60 @@ void createTown(flecs::entity e, Ogre::SceneNode *sceneNode, .each([&](flecs::entity de, JPH::BodyID &id) { destroy.push_back(de); }); + // Remove furniture entities + ECS::get() + .query_builder() + .with(flecs::ChildOf, e) + .build() + .each([&](flecs::entity de, + FurnitureInstance &instance) { + destroy.push_back(de); + }); for (flecs::entity de : destroy) de.destruct(); destroy.clear(); - Ogre::String props = e.get().properties; - nlohmann::json jp = nlohmann::json::parse(props); - Ogre::Vector3 worldPosition = sceneNode->_getDerivedPosition(); - Ogre::Quaternion worldOrientation = - sceneNode->_getDerivedOrientation(); - int index = 0; - for (const auto &jdistrict : jp["districts"]) { - createTownPlazza(e, jdistrict, index, sceneNode, geo); - createTownLots(e, jdistrict, index, sceneNode, geo); - createCells(e, jdistrict, index, sceneNode, geo); - createTownRoofs(e, jdistrict, index, sceneNode, geo); - createDecorateWindows(e, jdistrict, index, sceneNode, - geo); - createDecorateDoors(e, jdistrict, index, sceneNode, - geo); - createDecorateFurniture(e, jdistrict, index, sceneNode, - geo); + Ogre::String props = e.get().properties; + nlohmann::json jp = nlohmann::json::parse(props); + Ogre::Vector3 worldPosition = sceneNode->_getDerivedPosition(); + Ogre::Quaternion worldOrientation = + sceneNode->_getDerivedOrientation(); + int index = 0; + for (const auto &jdistrict : jp["districts"]) { + TownPlazza *townPlazzaTask = new TownPlazza; + TownLots *townLotsTask = new TownLots; + TownCells *townCellsTask = new TownCells; + TownRoofs *townRoofsTask = new TownRoofs; + TownDecorateWindows *townDecorateWindowsTask = + new TownDecorateWindows; + TownDecorateDoors *townDecorateDoorsTask = + new TownDecorateDoors; + TownDecorateFurniture *townDecorateFurnitureTask = + new TownDecorateFurniture; + townTasks.push_back(townPlazzaTask); + townTasks.push_back(townLotsTask); + townTasks.push_back(townCellsTask); + townTasks.push_back(townRoofsTask); + townTasks.push_back(townDecorateWindowsTask); + townTasks.push_back(townDecorateDoorsTask); + townTasks.push_back(townDecorateFurnitureTask); + for (auto m : townTasks) + (*m)(e, jdistrict, index, sceneNode, geo); index++; } - } - geo->build(); -} -void createTownPlazza(flecs::entity e, const nlohmann::json &jdistrict, - int index, Ogre::SceneNode *sceneNode, - Ogre::StaticGeometry *geo) -{ - Ogre::MaterialPtr townMaterial; - townMaterial = Ogre::MaterialManager::getSingleton().getByName( - "proceduralMaterialTown" + - Ogre::StringConverter::toString(e.id())); - Ogre::Vector3 worldPosition = sceneNode->_getDerivedPosition(); - Ogre::Quaternion worldOrientation = sceneNode->_getDerivedOrientation(); - const nlohmann::json &jp = jdistrict; - Procedural::TriangleBuffer tb; - float radius = 5.0f; - float height = 0.2f; - float elevation = 0.0f; - bool plazza = false; - Ogre::Vector3 localPosition(0, 0, 0); - Ogre::Quaternion localRotation = Ogre::Quaternion::IDENTITY; - if (jp.find("plazza") != jp.end()) - plazza = jp["plazza"].get(); - if (!plazza) - return; - if (jp.find("radius") != jp.end()) - radius = jp["radius"].get(); - if (jp.find("height") != jp.end()) - height = jp["height"].get(); - if (jp.find("elevation") != jp.end()) - elevation = jp["elevation"].get(); - from_json(jp["position"], localPosition); - from_json(jp["rotation"], localRotation); - if (height < 0.1f) - height = 0.1f; - if (radius < 5.0f) - radius = 5.0f; - Ogre::Vector3 worldPlazzaCenter = worldPosition + localPosition; - float mh = 4.0f; - Procedural::Shape *plazzaShape = new Procedural::Shape(); - plazzaShape->addPoint(0, -height - mh - mh); - plazzaShape->addPoint(radius * 0.5f + mh, -height - mh - mh); - plazzaShape->addPoint(radius + mh, -height - mh); - plazzaShape->addPoint(radius, -height); - plazzaShape->addPoint(radius, 0.0f); - plazzaShape->addPoint(radius - 0.1f, 0.1f); - plazzaShape->addPoint(radius - mh + 0.1f, height); - plazzaShape->addPoint(radius - mh, height + 0.1f); - plazzaShape->addPoint(radius * 0.5f + mh, height); - plazzaShape->addPoint(0, height); - Procedural::Lathe() - .setShapeToExtrude(plazzaShape) - .setEnableNormals(true) - .setPosition( - Ogre::Vector3(0.0f, height / 2.0f + elevation, 0.0f)) - .setNumSeg(24) - .addToTriangleBuffer(tb); - for (auto &v : tb.getVertices()) { - v.mUV *= 0.08f; - v.mUV.x += 0.41f; - v.mUV.x = Ogre::Math::Clamp(v.mUV.x, 0.4f, 0.5f); - v.mUV.y = Ogre::Math::Clamp(v.mUV.y, 0.0f, 0.1f); - } - Ogre::String meshName = "plazzaMesh" + - Ogre::StringConverter::toString(index) + "_" + - Ogre::StringConverter::toString(e.id()); - Ogre::MeshPtr mesh = - Ogre::MeshManager::getSingleton().getByName(meshName); - if (mesh) - Ogre::MeshManager::getSingleton().remove(mesh); - mesh = tb.transformToMesh(meshName); - Ogre::LodConfig config(mesh); - setupLods(config); - Ogre::Entity *plazzaEnt = - ECS::get().mScnMgr->createEntity(mesh); - plazzaEnt->setMaterial(townMaterial); - geo->addEntity(plazzaEnt, worldPlazzaCenter, - worldOrientation * localRotation, - Ogre::Vector3(1, 1, 1)); - ECS::get().mScnMgr->destroyEntity(plazzaEnt); - JPH::ShapeRefC shape = - JoltPhysicsWrapper::getSingleton().createMeshShape(mesh); - JPH::BodyID id = JoltPhysicsWrapper::getSingleton().createBody( - shape, 0, worldPlazzaCenter, worldOrientation * localRotation, - JPH::EMotionType::Static, Layers::NON_MOVING); - JoltPhysicsWrapper::getSingleton().addBody(id, - JPH::EActivation::Activate); - flecs::entity ce = ECS::get().entity().child_of(e).set(id); -} -void createCells(flecs::entity e, const nlohmann::json &jdistrict, int index, - Ogre::SceneNode *sceneNode, Ogre::StaticGeometry *geo) -{ - Ogre::MaterialPtr townMaterial; - townMaterial = Ogre::MaterialManager::getSingleton().getByName( - "proceduralMaterialTown" + - Ogre::StringConverter::toString(e.id())); - nlohmann::json jlots = nlohmann::json::array(); - if (jdistrict.find("lots") != jdistrict.end()) - jlots = jdistrict["lots"]; - Ogre::Vector3 centerPosition = sceneNode->_getDerivedPosition(); - Ogre::Quaternion centerOrientation = - sceneNode->_getDerivedOrientation(); - int count = 0; - float baseHeight = 4.0f; - const nlohmann::json &jp = jdistrict; - Ogre::Vector3 localPosition(0, 0, 0); - Ogre::Quaternion localRotation = Ogre::Quaternion::IDENTITY; - float delevation = 0.0f; - float radius = 50.0f; - if (jp.find("elevation") != jp.end()) - delevation = jp["elevation"].get(); - if (jp.find("radius") != jp.end()) - radius = jp["radius"].get(); - from_json(jp["position"], localPosition); - from_json(jp["rotation"], localRotation); - centerPosition = centerPosition + localPosition + - Ogre::Vector3(0, delevation, 0); - centerOrientation = centerOrientation * localRotation; - for (const auto &jb : jlots) { - float angle = 0.0f; - int depth = 10; - int width = 10; - float distance = radius; - float elevation = 0.0f; - std::cout << jb.dump() << std::endl; - if (jb.find("angle") != jb.end()) - angle = jb["angle"].get(); - if (jb.find("depth") != jb.end()) - depth = jb["depth"].get(); - if (jb.find("width") != jb.end()) - width = jb["width"].get(); - if (jb.find("elevation") != jb.end()) - elevation = jb["elevation"].get(); - - OgreAssert(width > 1 && depth > 1 && baseHeight > 1, - "Bad stuff happen"); - - Ogre::Quaternion rotation = Ogre::Quaternion( - Ogre::Degree(angle), Ogre::Vector3::UNIT_Y); - Ogre::Vector3 offset = centerOrientation * rotation * - (Ogre::Vector3::UNIT_Z * distance); - Ogre::Vector3 worldCenterPosition = - centerPosition + offset + - Ogre::Vector3(0, elevation, 0); - Ogre::Quaternion worldCenterOrientation = - centerOrientation * rotation; - if (jb.find("cells") != jb.end()) { - Procedural::TriangleBuffer cellTb; - Procedural::TriangleBuffer intwinTb; - Procedural::TriangleBuffer exteriorTb; - for (auto &jcell : jb["cells"]) { - struct ProcessCells pcells; - int x = jcell["x"].get(); - int y = jcell["y"].get(); - int z = jcell["z"].get(); - uint64_t flags = jcell["flags"].get(); - Ogre::Vector3 cellOffset(x * 2.0f, y * 4.0f, - z * 2.0f); - if (flags == 0) { - Procedural::BoxGenerator() - .setSizeX(2.0f + 0.4f) - .setSizeY(0.2f) - .setSizeZ(2.0f + 0.4f) - .setNumSegY(8) - .setNumSegX(8) - .setNumSegZ(8) - .setEnableNormals(true) - .setPosition(cellOffset) - .addToTriangleBuffer( - pcells.floortb); - cellTb.append(pcells.floortb); - - } else - pcells.process(e, cellOffset, flags, - cellTb, intwinTb, - exteriorTb); - } - if (cellTb.getVertices().size() > 0) { - Ogre::String meshName = - "lotCells" + - Ogre::StringConverter::toString(count) + - "_" + - Ogre::StringConverter::toString( - e.id()) + - "_" + - Ogre::StringConverter::toString(index); - Ogre::MeshPtr mesh = - Ogre::MeshManager::getSingleton() - .getByName(meshName); - if (mesh) - Ogre::MeshManager::getSingleton().remove( - mesh); - std::cout << "vertex count: " - << cellTb.getVertices().size() - << std::endl; - mesh = cellTb.transformToMesh(meshName); - /* debug save mesh */ - // serializeMesh(mesh, meshName); - Ogre::LodConfig config(mesh); - setupLods(config); - Ogre::Entity *ent = - ECS::get() - .mScnMgr->createEntity( - "Ent" + meshName, mesh); - ent->setMaterial(townMaterial); - geo->addEntity(ent, worldCenterPosition, - worldCenterOrientation, - Ogre::Vector3::UNIT_SCALE); - JPH::ShapeRefC shape = - JoltPhysicsWrapper::getSingleton() - .createMeshShape(mesh); - JPH::BodyID id = - JoltPhysicsWrapper::getSingleton() - .createBody( - shape, 0, - worldCenterPosition, - worldCenterOrientation, - JPH::EMotionType::Static, - Layers::NON_MOVING); - JoltPhysicsWrapper::getSingleton().addBody( - id, JPH::EActivation::Activate); - flecs::entity ce = - ECS::get() - .entity() - .child_of(e) - .set(id); - ECS::get().mScnMgr->destroyEntity( - ent); - } - if (intwinTb.getVertices().size() > 0) { - Ogre::String meshName = - "lotCells" + - Ogre::StringConverter::toString(count) + - "_" + - Ogre::StringConverter::toString( - e.id()) + - "_" + - Ogre::StringConverter::toString(index) + - "_intwin"; - Ogre::MeshPtr mesh = - Ogre::MeshManager::getSingleton() - .getByName(meshName); - if (mesh) - Ogre::MeshManager::getSingleton().remove( - mesh); - std::cout << "vertex count: " - << intwinTb.getVertices().size() - << std::endl; - mesh = intwinTb.transformToMesh(meshName); - /* debug save mesh */ - // serializeMesh(mesh, meshName); - Ogre::LodConfig config(mesh); - setupLods(config); - Ogre::Entity *ent = - ECS::get() - .mScnMgr->createEntity( - "Ent" + meshName, mesh); - ent->setMaterial(townMaterial); - geo->addEntity(ent, worldCenterPosition, - worldCenterOrientation, - Ogre::Vector3::UNIT_SCALE); - ECS::get().mScnMgr->destroyEntity( - ent); - JPH::ShapeRefC shape = - JoltPhysicsWrapper::getSingleton() - .createMeshShape(mesh); - JPH::BodyID id = - JoltPhysicsWrapper::getSingleton() - .createBody( - shape, 0, - worldCenterPosition, - worldCenterOrientation, - JPH::EMotionType::Static, - Layers::NON_MOVING); - JoltPhysicsWrapper::getSingleton().addBody( - id, JPH::EActivation::Activate); - flecs::entity ce = - ECS::get() - .entity() - .child_of(e) - .set(id); - } - if (exteriorTb.getVertices().size() > 0) { - Ogre::String meshName = - "lotCells" + - Ogre::StringConverter::toString(count) + - "_" + - Ogre::StringConverter::toString( - e.id()) + - "_" + - Ogre::StringConverter::toString(index) + - "_exterior"; - Ogre::MeshPtr mesh = - Ogre::MeshManager::getSingleton() - .getByName(meshName); - if (mesh) - Ogre::MeshManager::getSingleton().remove( - mesh); - std::cout << "vertex count: " - << exteriorTb.getVertices().size() - << std::endl; - mesh = exteriorTb.transformToMesh(meshName); - /* debug save mesh */ - // serializeMesh(mesh, meshName); - Ogre::LodConfig config(mesh); - setupLods(config); - Ogre::Entity *ent = - ECS::get() - .mScnMgr->createEntity( - "Ent" + meshName, mesh); - ent->setMaterial(townMaterial); - geo->addEntity(ent, worldCenterPosition, - worldCenterOrientation, - Ogre::Vector3::UNIT_SCALE); - ECS::get().mScnMgr->destroyEntity( - ent); - JPH::ShapeRefC shape = - JoltPhysicsWrapper::getSingleton() - .createMeshShape(mesh); - JPH::BodyID id = - JoltPhysicsWrapper::getSingleton() - .createBody( - shape, 0, - worldCenterPosition, - worldCenterOrientation, - JPH::EMotionType::Static, - Layers::NON_MOVING); - JoltPhysicsWrapper::getSingleton().addBody( - id, JPH::EActivation::Activate); - flecs::entity ce = - ECS::get() - .entity() - .child_of(e) - .set(id); - } - count++; - } } -} -void createTownLots(flecs::entity e, const nlohmann::json &jdistrict, int index, - Ogre::SceneNode *sceneNode, Ogre::StaticGeometry *geo) -{ - Ogre::MaterialPtr townMaterial; - townMaterial = Ogre::MaterialManager::getSingleton().getByName( - "proceduralMaterialTown" + - Ogre::StringConverter::toString(e.id())); - nlohmann::json jlots = nlohmann::json::array(); - if (jdistrict.find("lots") != jdistrict.end()) - jlots = jdistrict["lots"]; - Ogre::Vector3 centerPosition = sceneNode->_getDerivedPosition(); - Ogre::Quaternion centerOrientation = - sceneNode->_getDerivedOrientation(); - int count = 0; - float baseHeight = 4.0f; - const nlohmann::json &jp = jdistrict; - Ogre::Vector3 localPosition(0, 0, 0); - Ogre::Quaternion localRotation = Ogre::Quaternion::IDENTITY; - float delevation = 0.0f; - float radius = 50.0f; - if (jp.find("elevation") != jp.end()) - delevation = jp["elevation"].get(); - if (jp.find("radius") != jp.end()) - radius = jp["radius"].get(); - from_json(jp["position"], localPosition); - from_json(jp["rotation"], localRotation); - centerPosition = centerPosition + localPosition + - Ogre::Vector3(0, delevation, 0); - centerOrientation = centerOrientation * localRotation; - for (const auto &jb : jlots) { - Procedural::TriangleBuffer tb; - float angle = 0.0f; - int depth = 10; - int width = 10; - float distance = radius; - float elevation = 0.0f; - std::cout << jb.dump() << std::endl; - if (jb.find("angle") != jb.end()) - angle = jb["angle"].get(); - if (jb.find("depth") != jb.end()) - depth = jb["depth"].get(); - if (jb.find("width") != jb.end()) - width = jb["width"].get(); - if (jb.find("elevation") != jb.end()) - elevation = jb["elevation"].get(); - - OgreAssert(width > 1 && depth > 1 && baseHeight > 1, - "Bad stuff happen"); - - Ogre::Quaternion rotation = Ogre::Quaternion( - Ogre::Degree(angle), Ogre::Vector3::UNIT_Y); - Ogre::Vector3 offset = centerOrientation * rotation * - (Ogre::Vector3::UNIT_Z * distance); - Procedural::BoxGenerator() - .setSizeX(4.0f * (float)width) - .setSizeY(baseHeight) - .setSizeZ(4.0f * (float)depth) - .setNumSegY(8) - .setNumSegX(8) - .setNumSegZ(8) - .setEnableNormals(true) - .setPosition(Ogre::Vector3( - 0.0f, -baseHeight / 2.0f + elevation - 0.01f, - 0.0f)) - .addToTriangleBuffer(tb); - OgreAssert(tb.getVertices().size() > 8, "bad box"); - for (auto &v : tb.getVertices()) { - v.mUV *= 0.08f; - v.mUV.x += 0.41f; - v.mUV.x = Ogre::Math::Clamp(v.mUV.x, 0.4f, 0.5f); - v.mUV.y = Ogre::Math::Clamp(v.mUV.y, 0.0f, 0.1f); - } - Ogre::String meshName = - "lotbase" + Ogre::StringConverter::toString(count) + - "_" + Ogre::StringConverter::toString(e.id()) + "_" + - Ogre::StringConverter::toString(index); - Ogre::MeshPtr mesh = - Ogre::MeshManager::getSingleton().getByName(meshName); - if (mesh) - Ogre::MeshManager::getSingleton().remove(mesh); - mesh = tb.transformToMesh(meshName); - Ogre::LodConfig config(mesh); - setupLods(config); - Ogre::Entity *ent = - ECS::get().mScnMgr->createEntity( - "Ent" + meshName, mesh); - ent->setMaterial(townMaterial); - geo->addEntity(ent, centerPosition + offset, - centerOrientation * rotation, - Ogre::Vector3::UNIT_SCALE); - ECS::get().mScnMgr->destroyEntity(ent); - JPH::ShapeRefC shape = - JoltPhysicsWrapper::getSingleton().createMeshShape( - mesh); - JPH::BodyID id = JoltPhysicsWrapper::getSingleton().createBody( - shape, 0, centerPosition + offset, - centerOrientation * rotation, JPH::EMotionType::Static, - Layers::NON_MOVING); - JoltPhysicsWrapper::getSingleton().addBody( - id, JPH::EActivation::Activate); - flecs::entity ce = - ECS::get().entity().child_of(e).set(id); - count++; - } -} -void createTownRoofs(flecs::entity e, const nlohmann::json &jdistrict, - int index, Ogre::SceneNode *sceneNode, - Ogre::StaticGeometry *geo) -{ - Ogre::MaterialPtr townMaterial; - townMaterial = Ogre::MaterialManager::getSingleton().getByName( - "proceduralMaterialTown" + - Ogre::StringConverter::toString(e.id())); - nlohmann::json jlots = nlohmann::json::array(); - if (jdistrict.find("lots") != jdistrict.end()) - jlots = jdistrict["lots"]; - Ogre::Vector3 centerPosition = sceneNode->_getDerivedPosition(); - Ogre::Quaternion centerOrientation = - sceneNode->_getDerivedOrientation(); - int count = 0; - const nlohmann::json &jp = jdistrict; - Ogre::Vector3 localPosition(0, 0, 0); - Ogre::Quaternion localRotation = Ogre::Quaternion::IDENTITY; - float delevation = 0.0f; - float radius = 50.0f; - if (jp.find("elevation") != jp.end()) - delevation = jp["elevation"].get(); - if (jp.find("radius") != jp.end()) - radius = jp["radius"].get(); - from_json(jp["position"], localPosition); - from_json(jp["rotation"], localRotation); - centerPosition = centerPosition + localPosition + - Ogre::Vector3(0, delevation, 0); - centerOrientation = centerOrientation * localRotation; - for (const auto &jb : jlots) { - Procedural::TriangleBuffer tb; - Procedural::TriangleBuffer tbTop; - Procedural::TriangleBuffer tbSide; - float angle = 0.0f; - int depth = 10; - int width = 10; - float distance = radius; - float elevation = 0.0f; - std::cout << jb.dump() << std::endl; - if (jb.find("angle") != jb.end()) - angle = jb["angle"].get(); - if (jb.find("depth") != jb.end()) - depth = jb["depth"].get(); - if (jb.find("width") != jb.end()) - width = jb["width"].get(); - if (jb.find("elevation") != jb.end()) - elevation = jb["elevation"].get(); - - Ogre::Quaternion rotation = Ogre::Quaternion( - Ogre::Degree(angle), Ogre::Vector3::UNIT_Y); - Ogre::Vector3 offset = centerOrientation * rotation * - (Ogre::Vector3::UNIT_Z * distance); - Ogre::Vector3 worldCenterPosition = - centerPosition + offset + - Ogre::Vector3(0, elevation, 0); - Ogre::Quaternion worldCenterOrientation = - centerOrientation * rotation; - if (jb.find("roofs") != jb.end()) { - for (auto &jroof : jb["roofs"]) { - int roofType = jroof["type"].get(); - int position_x = jroof["position_x"].get(); - int position_y = jroof["position_y"].get(); - int position_z = jroof["position_z"].get(); - int width = jroof["size_x"].get(); - int depth = jroof["size_z"].get(); - float baseHeight = - jroof["base_height"].get(); - float maxHeight = - jroof["max_height"].get(); - float extend = 0.24f; - if (roofType == 0) { - Procedural::BoxGenerator() - .setSizeX(2.0f * (float)width) - .setSizeY(baseHeight) - .setSizeZ(2.0f * (float)depth) - .setNumSegY(1) - .setNumSegX(1) - .setNumSegZ(1) - .setEnableNormals(true) - .setPosition(Ogre::Vector3( - (float)position_x * - 2.0f - - 1.0f + - (float)width * - 2.0f / - 2.0f, - (float)position_y * - 4.0f + - baseHeight / - 2.0f + - elevation, - (float)position_z * - 2.0f - - 1.0f + - (float)depth * - 2.0f / - 2.0f)) - .addToTriangleBuffer(tbTop); - Procedural::BoxGenerator() - .setSizeX(2.0f * (float)width) - .setSizeY(baseHeight + - extend * 2.0f) - .setSizeZ(extend) - .setNumSegY(1) - .setNumSegX(1) - .setNumSegZ(1) - .setEnableNormals(true) - .setPosition(Ogre::Vector3( - (float)position_x * - 2.0f - - 1.0f + - (float)width * - 2.0f / - 2.0f, - (float)position_y * - 4.0f + - baseHeight / - 2.0f + - elevation + - extend, - (float)position_z * - 2.0f - - 1.0f + - (float)depth * - 2.0f / - 2.0f + - (float)depth * - 2.0f / - 2.0f + - extend / 2.0f)) - .addToTriangleBuffer(tbSide); - Procedural::BoxGenerator() - .setSizeX(2.0f * (float)width) - .setSizeY(baseHeight + - extend * 2.0f) - .setSizeZ(extend) - .setNumSegY(1) - .setNumSegX(1) - .setNumSegZ(1) - .setEnableNormals(true) - .setPosition(Ogre::Vector3( - (float)position_x * - 2.0f - - 1.0f + - (float)width * - 2.0f / - 2.0f, - (float)position_y * - 4.0f + - baseHeight / - 2.0f + - elevation + - extend, - (float)position_z * - 2.0f - - 1.0f + - (float)depth * - 2.0f / - 2.0f - - (float)depth * - 2.0f / - 2.0f - - extend / 2.0f)) - .addToTriangleBuffer(tbSide); - Procedural::BoxGenerator() - .setSizeX(extend) - .setSizeY(baseHeight + - extend * 2.0f) - .setSizeZ(2.0f * (float)depth + - extend * 2.0f) - .setNumSegY(1) - .setNumSegX(1) - .setNumSegZ(1) - .setEnableNormals(true) - .setPosition(Ogre::Vector3( - (float)position_x * - 2.0f - - 1.0f + - (float)width * - 2.0f / - 2.0f + - (float)width * - 2.0f / - 2.0f + - extend / 2.0f, - (float)position_y * - 4.0f + - baseHeight / - 2.0f + - elevation + - extend, - (float)position_z * - 2.0f - - 1.0f + - (float)depth * - 2.0f / - 2.0f)) - .addToTriangleBuffer(tbSide); - Procedural::BoxGenerator() - .setSizeX(extend) - .setSizeY(baseHeight + - extend * 2.0f) - .setSizeZ(2.0f * (float)depth + - extend * 2.0f) - .setNumSegY(1) - .setNumSegX(1) - .setNumSegZ(1) - .setEnableNormals(true) - .setPosition(Ogre::Vector3( - (float)position_x * - 2.0f - - 1.0f + - (float)width * - 2.0f / - 2.0f - - (float)width * - 2.0f / - 2.0f - - extend / 2.0f, - (float)position_y * - 4.0f + - baseHeight / - 2.0f + - elevation + - extend, - (float)position_z * - 2.0f - - 1.0f + - (float)depth * - 2.0f / - 2.0f)) - .addToTriangleBuffer(tbSide); - OgreAssert(tbTop.getVertices().size() > - 8, - "bad box"); - clampUV(e, tbTop, "roofTop"); - clampUV(e, tbSide, "roofSide"); - } else if (roofType == 1) { - float q = 2.0f * (float)width / 2.0f; - float m = 1.0f; - float d = 1.4142f * (q + m); - Procedural::BoxGenerator() - .setSizeX(d) - .setSizeY(baseHeight / 2.0f) - .setSizeZ(2.0f * (float)depth) - .setNumSegY(1) - .setNumSegX(1) - .setNumSegZ(1) - .setEnableNormals(true) - .setOrientation(Ogre::Quaternion( - Ogre::Degree(45), - Ogre::Vector3:: - NEGATIVE_UNIT_Z)) - .setPosition(Ogre::Vector3( - (float)position_x * - 2.0f - - 1.0f + - (float)q + - q / 2.0f + - m / 2.0f, - (float)position_y * - 4.0f + - baseHeight / - 2.0f + - elevation + - q / 2.0f - - m / 2.0f + - baseHeight / - 4.0f, - (float)position_z * - 2.0f - - 1.0f + - (float)depth * - 2.0f / - 2.0f)) - .addToTriangleBuffer(tbTop); - Procedural::BoxGenerator() - .setSizeX(d) - .setSizeY(baseHeight / 2.0f) - .setSizeZ(2.0f * (float)depth) - .setNumSegY(1) - .setNumSegX(1) - .setNumSegZ(1) - .setEnableNormals(true) - .setOrientation(Ogre::Quaternion( - Ogre::Degree(-45), - Ogre::Vector3:: - NEGATIVE_UNIT_Z)) - .setPosition(Ogre::Vector3( - (float)position_x * - 2.0f - - 1.0f + - q / 2.0f - - m / 2.0f, - (float)position_y * - 4.0f + - baseHeight / - 2.0f + - elevation + - q / 2.0f - - m / 2.0f + - baseHeight / - 4.0f, - (float)position_z * - 2.0f - - 1.0f + - (float)depth * - 2.0f / - 2.0f)) - .addToTriangleBuffer(tbTop); - Procedural::BoxGenerator() - .setSizeX(2.0f * (float)width + - extend * 2.0f) - .setSizeY(q + baseHeight * 2.5f) - .setSizeZ(extend) - .setNumSegX(3) - .setNumSegY(3) - .setNumSegZ(1) - .setEnableNormals(true) - .setPosition(Ogre::Vector3( - (float)position_x * - 2.0f - - 1.0f + q, - (float)position_y * - 4.0f + - elevation + - q / 2.0f, - (float)position_z * - 2.0f - - 1.0f + - (float)depth * - 2.0f / - 2.0f + - (float)depth * - 2.0f / - 2.0f + - extend / 2.0f)) - .addToTriangleBuffer(tbSide); - Procedural::BoxGenerator() - .setSizeX(2.0f * (float)width + - extend * 2.0f) - .setSizeY(q + baseHeight * 2.5f) - .setSizeZ(extend) - .setNumSegX(3) - .setNumSegY(3) - .setNumSegZ(1) - .setEnableNormals(true) - .setPosition(Ogre::Vector3( - (float)position_x * - 2.0f - - 1.0f + q, - (float)position_y * - 4.0f + - elevation + - q / 2.0f, - (float)position_z * - 2.0f - - 1.0f + - (float)depth * - 2.0f / - 2.0f - - (float)depth * - 2.0f / - 2.0f - - extend / 2.0f)) - .addToTriangleBuffer(tbSide); - float minY = 0.0f; - float maxY = 0.0f; - float maxX = 0.0f; - float minX = 0.0f; - int count = 0; - for (auto &v : tbSide.getVertices()) { - if (count == 0) { - minY = v.mPosition.y; - maxY = v.mPosition.y; - minX = v.mPosition.x; - maxX = v.mPosition.x; - } else { - if (v.mPosition.y > - maxY) - maxY = v.mPosition - .y; - if (v.mPosition.y < - minY) - minY = v.mPosition - .y; - if (v.mPosition.x > - maxX) - maxX = v.mPosition - .x; - if (v.mPosition.x < - minX) - minX = v.mPosition - .x; - } - count++; - } - float midX = - minX + (maxX - minX) / 2.0f; - for (auto &v : tbSide.getVertices()) { - float md = maxY - v.mPosition.y; - float hm = v.mPosition.x - midX; - float he = - (hm / ((maxX - minX) / - 2.0f)) * - md; - v.mPosition.x = he + midX; - } - clampUV(e, tbTop, "roofTop"); - clampUV(e, tbSide, "roofSide"); - } else if (roofType == 2) { - float q = 2.0f * (float)depth / 2.0f; - float m = 1.0f; - float d = 1.4142f * (q + m); - Procedural::BoxGenerator() - .setSizeX(2.0f * (float)width) - .setSizeY(baseHeight / 2.0f) - .setSizeZ(d) - .setNumSegY(1) - .setNumSegX(1) - .setNumSegZ(1) - .setEnableNormals(true) - .setOrientation(Ogre::Quaternion( - Ogre::Degree(45), - Ogre::Vector3:: - NEGATIVE_UNIT_X)) - .setPosition(Ogre::Vector3( - (float)position_x * - 2.0f - - 1.0f + - (float)width * - 2.0f / - 2.0f, - (float)position_y * - 4.0f + - baseHeight / - 2.0f + - elevation + - q / 2.0f - - m / 2.0f + - baseHeight / - 4.0f, - (float)position_z * - 2.0f - - 1.0f + - q / 2.0f - - m / 2.0f)) - .addToTriangleBuffer(tbTop); - Procedural::BoxGenerator() - .setSizeX(2.0f * (float)width) - .setSizeY(baseHeight / 2.0f) - .setSizeZ(d) - .setNumSegX(1) - .setNumSegY(1) - .setNumSegZ(1) - .setEnableNormals(true) - .setOrientation(Ogre::Quaternion( - Ogre::Degree(-45), - Ogre::Vector3:: - NEGATIVE_UNIT_X)) - .setPosition(Ogre::Vector3( - (float)position_x * - 2.0f - - 1.0f + - (float)width * - 2.0f / - 2.0f, - (float)position_y * - 4.0f + - baseHeight / - 2.0f + - elevation + - q / 2.0f - - m / 2.0f + - baseHeight / - 4.0f, - (float)position_z * - 2.0f - - 1.0f + q + - q / 2.0f + - m / 2.0f)) - .addToTriangleBuffer(tbTop); - Procedural::BoxGenerator() - .setSizeX(extend) - .setSizeY(q + baseHeight * 2.5f) - .setSizeZ(2.0f * (float)depth + - 2.0f * baseHeight * - 3.0f) - .setNumSegX(2) - .setNumSegY(2) - .setNumSegZ(1) - .setEnableNormals(true) - .setPosition(Ogre::Vector3( - (float)position_x * - 2.0f - - 1.0f + - (float)width * - 2.0f / - 2.0f + - (float)width * - 2.0f / - 2.0f + - extend / 2.0f, - (float)position_y * - 4.0f + - elevation + - q / 2.0f, - (float)position_z * - 2.0f - - 1.0f + - (float)depth * - 2.0f / - 2.0f)) - .addToTriangleBuffer(tbSide); - Procedural::BoxGenerator() - .setSizeX(extend) - .setSizeY(q + baseHeight * 2.5f) - .setSizeZ(2.0f * (float)depth + - 2.0f * baseHeight * - 3.0f) - .setNumSegX(2) - .setNumSegY(2) - .setNumSegZ(1) - .setEnableNormals(true) - .setPosition(Ogre::Vector3( - (float)position_x * - 2.0f - - 1.0f + - (float)width * - 2.0f / - 2.0f - - (float)width * - 2.0f / - 2.0f - - extend / 2.0f, - (float)position_y * - 4.0f + - elevation + - q / 2.0f, - (float)position_z * - 2.0f - - 1.0f + - (float)depth * - 2.0f / - 2.0f)) - .addToTriangleBuffer(tbSide); - float minY = 0.0f; - float maxY = 0.0f; - float maxZ = 0.0f; - float minZ = 0.0f; - int count = 0; - for (auto &v : tbSide.getVertices()) { - if (count == 0) { - minY = v.mPosition.y; - maxY = v.mPosition.y; - minZ = v.mPosition.z; - maxZ = v.mPosition.z; - } else { - if (v.mPosition.y > - maxY) - maxY = v.mPosition - .y; - if (v.mPosition.y < - minY) - minY = v.mPosition - .y; - if (v.mPosition.z > - maxZ) - maxZ = v.mPosition - .z; - if (v.mPosition.z < - minZ) - minZ = v.mPosition - .z; - } - count++; - } - float midZ = - minZ + (maxZ - minZ) / 2.0f; - for (auto &v : tbSide.getVertices()) { - float md = maxY - v.mPosition.y; - float hm = v.mPosition.z - midZ; - float he = - (hm / ((maxZ - minZ) / - 2.0f)) * - md; - v.mPosition.z = he + midZ; - } - clampUV(e, tbTop, "roofTop"); - clampUV(e, tbSide, "roofSide"); + Ogre::Root::getSingleton().getWorkQueue()->addTask([geo]() { + for (auto m : townTasks) + m->wait(); + Ogre::Root::getSingleton().getWorkQueue()->addMainThreadTask( + [geo]() { + for (auto m : townTasks) { + delete m; } - } - { - Ogre::String meshName = - "roofbase" + - Ogre::StringConverter::toString(count) + - "_" + - Ogre::StringConverter::toString( - e.id()) + - "_" + - Ogre::StringConverter::toString(index); - Ogre::MeshPtr mesh = - Ogre::MeshManager::getSingleton() - .getByName(meshName); - if (mesh) - Ogre::MeshManager::getSingleton().remove( - mesh); - if (tbTop.getVertices().size() > 0) - tb.append(tbTop); - if (tbSide.getVertices().size() > 0) - tb.append(tbSide); - if (tb.getVertices().size() > 0) { - mesh = tb.transformToMesh(meshName); - Ogre::LodConfig config(mesh); - setupLods(config); - Ogre::Entity *ent = - ECS::get() - .mScnMgr->createEntity( - "Ent" + meshName, - mesh); - ent->setMaterial(townMaterial); - geo->addEntity( - ent, centerPosition + offset, - centerOrientation * rotation, - Ogre::Vector3::UNIT_SCALE); - ECS::get() - .mScnMgr->destroyEntity( + townTasks.clear(); - ent); - } - if (mesh) { - JPH::ShapeRefC shape = - JoltPhysicsWrapper::getSingleton() - .createMeshShape(mesh); - JPH::BodyID id = - JoltPhysicsWrapper::getSingleton() - .createBody( - shape, 0, - centerPosition + - offset, - centerOrientation * - rotation, - JPH::EMotionType:: - Static, - Layers::NON_MOVING); - JoltPhysicsWrapper::getSingleton().addBody( - id, JPH::EActivation::Activate); - flecs::entity ce = - ECS::get() - .entity() - .child_of(e) - .set(id); - } - } - count++; - } - } + geo->build(); + }); + }); } } } diff --git a/src/gamedata/items/town.h b/src/gamedata/items/town.h index 9813a97..431fa3f 100644 --- a/src/gamedata/items/town.h +++ b/src/gamedata/items/town.h @@ -12,25 +12,15 @@ namespace Items void createTownItem(); void createTownMenu(); void createTownPopup(const std::pair item); +void runAllScriptsForTown(flecs::entity e); } namespace Geometry { void clampUV(flecs::entity e, Procedural::TriangleBuffer &tb, const Ogre::String &rectKey); Ogre::MaterialPtr createTownMaterial(flecs::entity e, bool force = false); -void createCells(flecs::entity e, const nlohmann::json &jdistrict, int index, - Ogre::SceneNode *sceneNode, Ogre::StaticGeometry *geo); void createTown(flecs::entity e, Ogre::SceneNode *sceneNode, Ogre::StaticGeometry *geo); -void createTownPlazza(flecs::entity e, const nlohmann::json &jdistrict, - int index, Ogre::SceneNode *sceneNode, - Ogre::StaticGeometry *geo); -void createTownLots(flecs::entity e, const nlohmann::json &jdistrict, - int index, Ogre::SceneNode *sceneNode, - Ogre::StaticGeometry *geo); -void createTownRoofs(flecs::entity e, const nlohmann::json &jdistrict, - int index, Ogre::SceneNode *sceneNode, - Ogre::StaticGeometry *geo); } } #endif diff --git a/src/physics/physics.cpp b/src/physics/physics.cpp index 028aa20..ce55306 100644 --- a/src/physics/physics.cpp +++ b/src/physics/physics.cpp @@ -90,7 +90,10 @@ public: Layers::MOVING; // Non moving only collides with moving case Layers::MOVING: return true; // Moving collides with everything - default: + case Layers::SENSORS: + return inObject2 == + Layers::MOVING; // Non moving only collides with moving + default: JPH_ASSERT(false); return false; } @@ -107,7 +110,8 @@ public: mObjectToBroadPhase[Layers::NON_MOVING] = BroadPhaseLayers::NON_MOVING; mObjectToBroadPhase[Layers::MOVING] = BroadPhaseLayers::MOVING; - } + mObjectToBroadPhase[Layers::SENSORS] = BroadPhaseLayers::MOVING; + } virtual uint GetNumBroadPhaseLayers() const override { @@ -1330,7 +1334,18 @@ public: OgreAssert(result.Get(), "Can not create com offset shape"); return result.Get(); } - void applyBuoyancyImpulse(JPH::BodyID id, + JPH::ShapeRefC + createRotatedTranslatedShape(const Ogre::Vector3 &offset, + const Ogre::Quaternion rotation, + JPH::ShapeRefC shape) + { + return JPH::RotatedTranslatedShapeSettings( + JoltPhysics::convert(offset), + JoltPhysics::convert(rotation), shape) + .Create() + .Get(); + } + void applyBuoyancyImpulse(JPH::BodyID id, const Ogre::Vector3 &surfacePosition, const Ogre::Vector3 &surfaceNormal, float buoyancy, float linearDrag, @@ -1597,7 +1612,14 @@ JPH::ShapeRefC JoltPhysicsWrapper::createOffsetCenterOfMassShape(const Ogre::Vector3 &offset, JPH::ShapeRefC shape) { - return phys->createOffsetCenterOfMassShape(offset, shape); + return phys->createOffsetCenterOfMassShape(offset, shape); +} + +JPH::ShapeRefC JoltPhysicsWrapper::createRotatedTranslatedShape( + const Ogre::Vector3 &offset, const Ogre::Quaternion rotation, + JPH::ShapeRefC shape) +{ + return phys->createRotatedTranslatedShape(offset, rotation, shape); } JPH::BodyID diff --git a/src/physics/physics.h b/src/physics/physics.h index 6da51e5..0a2aa58 100644 --- a/src/physics/physics.h +++ b/src/physics/physics.h @@ -26,7 +26,8 @@ namespace Layers { static constexpr JPH::ObjectLayer NON_MOVING = 0; static constexpr JPH::ObjectLayer MOVING = 1; -static constexpr JPH::ObjectLayer NUM_LAYERS = 2; +static constexpr JPH::ObjectLayer SENSORS = 2; +static constexpr JPH::ObjectLayer NUM_LAYERS = 3; }; // Each broadphase layer results in a separate bounding volume tree in the broad phase. You at least want to have @@ -134,7 +135,10 @@ public: JPH::ShapeRefC createOffsetCenterOfMassShape(const Ogre::Vector3 &offset, JPH::ShapeRefC shape); - JPH::BodyID createBody(const JPH::BodyCreationSettings &settings); + JPH::ShapeRefC + createRotatedTranslatedShape(const Ogre::Vector3 &offset, const Ogre::Quaternion rotation, + JPH::ShapeRefC shape); + JPH::BodyID createBody(const JPH::BodyCreationSettings &settings); JPH::BodyID createBody(const JPH::Shape *shape, float mass, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation,