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 0000000..225d0c4 Binary files /dev/null and b/resources/terrain/world_map.png differ 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,