From 0d471ec57866157d4e9c4cf0874fe203126df967 Mon Sep 17 00:00:00 2001 From: Auburn Date: Wed, 11 Oct 2023 00:21:44 +0100 Subject: [PATCH] WebPreviewTool: Feature parity with old preview tool excluding domain warp visualise --- .clang-format | 1 + WebPreviewApp/CMakeLists.txt | 20 +- WebPreviewApp/main.cpp | 473 ++++++++++++++++++++++++++++------- 3 files changed, 407 insertions(+), 87 deletions(-) diff --git a/.clang-format b/.clang-format index 59dd36e..7f03e16 100644 --- a/.clang-format +++ b/.clang-format @@ -5,6 +5,7 @@ AccessModifierOffset: -4 AlignOperands: false AlignTrailingComments: false AlwaysBreakTemplateDeclarations: Yes +PointerAlignment: Left BraceWrapping: AfterCaseLabel: true AfterClass: true diff --git a/WebPreviewApp/CMakeLists.txt b/WebPreviewApp/CMakeLists.txt index e8f5e1e..01d6527 100644 --- a/WebPreviewApp/CMakeLists.txt +++ b/WebPreviewApp/CMakeLists.txt @@ -1,7 +1,7 @@ -cmake_minimum_required(VERSION 3.7.1) +cmake_minimum_required(VERSION 3.12) -project(FastNoiseLitePreview VERSION 1.0) -SET(CMAKE_CXX_STANDARD 11) +project(FastNoiseLitePreview VERSION 1.1.0) +SET(CMAKE_CXX_STANDARD 17) include(FetchContent) @@ -12,6 +12,12 @@ FetchContent_Declare( ) FetchContent_MakeAvailable(imgui_application) +FetchContent_Declare( + emscripten-browser-file + GIT_REPOSITORY https://github.com/Armchair-Software/emscripten-browser-file + GIT_TAG 2ff8f8f791c0e7177b40e621ee3f758e03b31bf5 +) +FetchContent_MakeAvailable(emscripten-browser-file) # ---------------------------------------------------------- add_executable(${CMAKE_PROJECT_NAME} @@ -20,9 +26,14 @@ add_executable(${CMAKE_PROJECT_NAME} target_include_directories(${CMAKE_PROJECT_NAME} PUBLIC ${IMGUI_APPLICATION_INCLUDE_DIRECTORIES} + ${emscripten-browser-file_SOURCE_DIR} ../Cpp ) +target_compile_definitions(${CMAKE_PROJECT_NAME} PUBLIC + FNL_VERSION="v${CMAKE_PROJECT_VERSION}" +) + if(EMSCRIPTEN) # IMPORTANT NOTE: due to `target_link_options` deduplication, @@ -37,6 +48,9 @@ if(EMSCRIPTEN) -sUSE_WEBGL2=1 #-sMIN_WEBGL_VERSION=2 -sMAX_WEBGL_VERSION=2 # Only target for WebGL2 (drop support for WebGL1 to save code size) + -sEXPORTED_RUNTIME_METHODS=[ccall] + -sEXPORTED_FUNCTIONS=[_main,_malloc,_free] + -sALLOW_MEMORY_GROWTH=1 -sDISABLE_EXCEPTION_CATCHING=1 -sNO_EXIT_RUNTIME=0 -sASSERTIONS=1 diff --git a/WebPreviewApp/main.cpp b/WebPreviewApp/main.cpp index 1984e1b..2b76534 100644 --- a/WebPreviewApp/main.cpp +++ b/WebPreviewApp/main.cpp @@ -1,14 +1,19 @@ #include +#include #include +#include #include #include -#include #include +#include + +#include +#include -#include "FastNoiseLite.h" #include "../Cpp/FastNoiseLite.h" +#include "FastNoiseLite.h" class FastNoiseLitePreviewApp : public ImGui_Application { @@ -16,17 +21,31 @@ class FastNoiseLitePreviewApp : public ImGui_Application bool init() override { bool success = ImGui_Application::init(); - ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_DockingEnable; + set_window_title("FastNoise Lite GUI"); - UpdateTexture(); + ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_DockingEnable; return success; } uint32_t noiseTex = 0; - int noiseTexSize = 1024; + ImVec2 noiseTexSize; + ImVec2 noiseTexSizeDesired = { 768, 768 }; + bool preview3d = false; + float previewScroll = 0; + double previewPosZ = 0; + bool previewInvert = false; + bool previewDomainWarp = false; + + float previewGenTime = 0; + float previewMin = 0; + float previewMax = 0; + float previewMean = 0; + bool previewTriggerSave = false; FastNoiseLite fnl; + FastNoiseLite fnlWarp; + int fnlNoiseType = 0; int fnlRotationType = 0; int fnlSeed = 1337; @@ -38,102 +57,277 @@ class FastNoiseLitePreviewApp : public ImGui_Application float fnlFractalGain = 0.5f; float fnlFractalWeightedStrength = 0.0f; float fnlFractalPingPongStrength = 2.0f; - - int fnlCellularType = 0; - int fnlCellularReturnType = 0; + + int fnlCellularType = 1; + int fnlCellularReturnType = 1; float fnlCellularJitter = 1.0f; int fnlDomainWarpType = 0; int fnlDomainWarpRotationType = 0; - float fnlDomainWarpAmplitude = 30.0f; - float fnlDomainWarpFrequency = 0.005f; + float fnlDomainWarpAmplitude = 1.0f; + float fnlDomainWarpFrequency = 0.01f; int fnlDomainWarpFractalType = 0; - int fnlDomainWarpFractalOctaves = 5; + int fnlDomainWarpFractalOctaves = 3; float fnlDomainWarpFractalLacunarity = 2.0f; float fnlDomainWarpFractalGain = 0.5f; - void draw_ui() override - { - SetupDocking(); + void draw_ui() override + { + static const char* enumNoiseType[] = { "OpenSimplex2", "OpenSimplex2S", "Cellular", "Perlin", "Value Cubic", "Value" }; + static const char* enumRotationType[] = { "None", "Improve XY Planes", "Improve XZ Planes" }; + static const char* enumFractalType[] = { "None", "FBm", "Ridged", "Ping Pong" }; + static const char* enumCellularType[] = { "Euclidean", "Euclidean Sq", "Manhattan", "Hybrid" }; + static const char* enumCellularReturnType[] = { "Cell Value", "Distance", "Distance 2", "Distance 2 Add", "Distance 2 Sub", "Distance 2 Mul", "Distance 2 Div" }; + static const char* enumDomainWarpType[] = { "None", "OpenSimplex2", "OpenSimplex2 Reduced", "Basic Grid" }; + static const char* enumDomainWarpFractalType[] = { "None", "Progressive", "Independent" }; - //ImGui::ShowDemoWindow(); + SetupDocking(); - ImGui::Begin("FastNoise Lite"); + // ImGui::ShowDemoWindow(); bool texUpdate = false; - /// General - ImGui::TextUnformatted("General"); + ImGui::Begin("Settings"); + ImGui::PushItemWidth(120); - if( ImGui::Combo("Noise Type", &fnlNoiseType,"OpenSimplex2\0OpenSimplex2S\0Cellular\0Perlin\0ValueCubic\0Value\0") ) - { - fnl.SetNoiseType( (FastNoiseLite::NoiseType)fnlNoiseType ); - texUpdate = true; - } - if( ImGui::Combo("Rotation Type 3D", &fnlRotationType,"None\0ImproveXYPlanes\0ImproveXZPlanes\0") ) + ImGui::BeginTabBar("Tabs"); + if (ImGui::BeginTabItem("FastNoise Lite")) { - fnl.SetRotationType3D( (FastNoiseLite::RotationType3D)fnlRotationType ); - texUpdate = true; - } - if( ImGui::DragInt("Seed", &fnlSeed) ) - { - fnl.SetSeed( fnlSeed ); - texUpdate = true; - } - if( ImGui::DragFloat("Frequency", &fnlFrequency, 0.001f) ) - { - fnl.SetFrequency( fnlFrequency ); - texUpdate = true; - } + /// General + ImGui::TextUnformatted("General"); + + if (ImGui::Combo("Noise Type", &fnlNoiseType, enumNoiseType, IM_ARRAYSIZE(enumNoiseType))) + { + fnl.SetNoiseType((FastNoiseLite::NoiseType)fnlNoiseType); + texUpdate = true; + } + ImGui::BeginDisabled(!preview3d); + if (ImGui::Combo("Rotation Type 3D", &fnlRotationType, enumRotationType, IM_ARRAYSIZE(enumRotationType))) + { + fnl.SetRotationType3D((FastNoiseLite::RotationType3D)fnlRotationType); + texUpdate = true; + } + ImGui::EndDisabled(); + if (ImGui::DragInt("Seed", &fnlSeed)) + { + fnl.SetSeed(fnlSeed); + texUpdate = true; + } + if (ImGui::DragFloat("Frequency", &fnlFrequency, 0.001f)) + { + fnl.SetFrequency(fnlFrequency); + texUpdate = true; + } - /// Fractal - ImGui::TextUnformatted("Fractal"); + /// Fractal + ImGui::TextUnformatted("Fractal"); - if( ImGui::Combo("Type", &fnlFractalType,"None\0FBm\0Ridged\0PingPong\0") ) - { - fnl.SetFractalType( (FastNoiseLite::FractalType)fnlFractalType ); - texUpdate = true; + if (ImGui::Combo("Type", &fnlFractalType, enumFractalType, IM_ARRAYSIZE(enumFractalType))) + { + fnl.SetFractalType((FastNoiseLite::FractalType)fnlFractalType); + texUpdate = true; + } + ImGui::BeginDisabled(fnlFractalType == 0); + if (ImGui::DragInt("Octaves", &fnlFractalOctaves, 0.1f)) + { + fnl.SetFractalOctaves(fnlFractalOctaves); + texUpdate = true; + } + if (ImGui::DragFloat("Lacunarity", &fnlFractalLacunarity, 0.01f)) + { + fnl.SetFractalLacunarity(fnlFractalLacunarity); + texUpdate = true; + } + if (ImGui::DragFloat("Gain", &fnlFractalGain, 0.01f)) + { + fnl.SetFractalGain(fnlFractalGain); + texUpdate = true; + } + if (ImGui::DragFloat("Weighted Strength", &fnlFractalWeightedStrength, 0.01f)) + { + fnl.SetFractalWeightedStrength(fnlFractalWeightedStrength); + texUpdate = true; + } + ImGui::BeginDisabled(fnlFractalType != (int)FastNoiseLite::FractalType_PingPong); + if (ImGui::DragFloat("Ping Pong Strength", &fnlFractalPingPongStrength, 0.01f)) + { + fnl.SetFractalPingPongStrength(fnlFractalPingPongStrength); + texUpdate = true; + } + ImGui::EndDisabled(); + ImGui::EndDisabled(); + + /// Cellular + ImGui::TextUnformatted("Cellular"); + + ImGui::BeginDisabled(fnlNoiseType != (int)FastNoiseLite::NoiseType_Cellular); + if (ImGui::Combo("Distance Function", &fnlCellularType, enumCellularType, IM_ARRAYSIZE(enumCellularType))) + { + fnl.SetCellularDistanceFunction((FastNoiseLite::CellularDistanceFunction)fnlCellularType); + texUpdate = true; + } + if (ImGui::Combo("Return Type", &fnlCellularReturnType, enumCellularReturnType, IM_ARRAYSIZE(enumCellularReturnType))) + { + fnl.SetCellularReturnType((FastNoiseLite::CellularReturnType)fnlCellularReturnType); + texUpdate = true; + } + if (ImGui::DragFloat("Jitter", &fnlCellularJitter, 0.01f)) + { + fnl.SetCellularJitter(fnlCellularJitter); + texUpdate = true; + } + ImGui::EndDisabled(); + + /// Domain Warp + ImGui::PushID("Domain Warp"); + ImGui::TextUnformatted("Domain Warp"); + + if (ImGui::Combo("Type", &fnlDomainWarpType, enumDomainWarpType, IM_ARRAYSIZE(enumDomainWarpType))) + { + fnlWarp.SetDomainWarpType((FastNoiseLite::DomainWarpType)(fnlDomainWarpType - 1)); + texUpdate = true; + } + ImGui::BeginDisabled(fnlDomainWarpType == 0); + ImGui::BeginDisabled(!preview3d); + if (ImGui::Combo("Rotation Type 3D", &fnlDomainWarpRotationType, enumRotationType, IM_ARRAYSIZE(enumRotationType))) + { + fnlWarp.SetRotationType3D((FastNoiseLite::RotationType3D)fnlDomainWarpRotationType); + texUpdate = true; + } + ImGui::EndDisabled(); + if (ImGui::DragFloat("Amplitude", &fnlDomainWarpAmplitude, 0.5f)) + { + fnlWarp.SetDomainWarpAmp(fnlDomainWarpAmplitude); + texUpdate = true; + } + if (ImGui::DragFloat("Frequency", &fnlDomainWarpFrequency, 0.001f)) + { + fnlWarp.SetFrequency(fnlDomainWarpFrequency); + texUpdate = true; + } + + /// Domain Warp Fractal + ImGui::PushID("Domain Warp Fractal"); + ImGui::TextUnformatted("Domain Warp Fractal"); + + if (ImGui::Combo("Type", &fnlDomainWarpFractalType, enumDomainWarpFractalType, IM_ARRAYSIZE(enumDomainWarpFractalType))) + { + fnlWarp.SetFractalType((FastNoiseLite::FractalType)(fnlDomainWarpFractalType ? fnlDomainWarpFractalType + 3 : 0)); + texUpdate = true; + } + ImGui::BeginDisabled(fnlDomainWarpFractalType == 0); + if (ImGui::DragInt("Octaves", &fnlDomainWarpFractalOctaves, 0.1f)) + { + fnlWarp.SetFractalOctaves(fnlDomainWarpFractalOctaves); + texUpdate = true; + } + if (ImGui::DragFloat("Lacunarity", &fnlDomainWarpFractalLacunarity, 0.01f)) + { + fnlWarp.SetFractalLacunarity(fnlDomainWarpFractalLacunarity); + texUpdate = true; + } + if (ImGui::DragFloat("Gain", &fnlFractalGain, 0.01f)) + { + fnlWarp.SetFractalGain(fnlDomainWarpFractalGain); + texUpdate = true; + } + ImGui::EndDisabled(); + ImGui::EndDisabled(); + ImGui::PopID(); + ImGui::PopID(); + + ImGui::NewLine(); + ImGui::TextUnformatted(FNL_VERSION); + + ImGui::EndTabItem(); } - if( ImGui::DragInt("Octaves", &fnlFractalOctaves, 0.1f) ) + if (ImGui::BeginTabItem("Preview Settings")) { - fnl.SetFractalOctaves( fnlFractalOctaves ); - texUpdate = true; + int sizeXY[] = { (int)noiseTexSize.x, (int)noiseTexSize.y }; + + if (ImGui::DragInt2("Size", sizeXY, 1, 32, 4096)) + { + noiseTexSizeDesired.x = sizeXY[0]; + noiseTexSizeDesired.y = sizeXY[1]; + } + if (ImGui::Checkbox("Invert", &previewInvert)) + { + texUpdate = true; + } + if (ImGui::Checkbox("3D", &preview3d)) + { + texUpdate = true; + } + if (preview3d) + { + ImGui::Indent(); + ImGui::DragFloat("Scroll Speed", &previewScroll, 0.02f); + + ImGui::BeginDisabled(previewScroll != 0); + float floatPosZ = (float)previewPosZ; + if (ImGui::DragFloat("Z Position", &floatPosZ)) + { + previewPosZ = floatPosZ; + texUpdate = true; + } + ImGui::EndDisabled(); + + ImGui::Unindent(); + texUpdate = true; + } + + ImGui::NewLine(); + if (ImGui::Button("Save Preview")) + { + previewTriggerSave = true; + texUpdate = true; + } + + ImGui::EndTabItem(); } - if( ImGui::DragFloat("Lacunarity", &fnlFractalLacunarity, 0.01f) ) + ImGui::EndTabBar(); + ImGui::PopItemWidth(); + ImGui::End(); + + ImGui::Begin("Noise Texture"); + + // noiseTexSizeDesired = ImGui::GetContentRegionAvail(); + if (noiseTexSizeDesired.x != noiseTexSize.x || noiseTexSizeDesired.y != noiseTexSize.y) { - fnl.SetFractalLacunarity( fnlFractalLacunarity ); + noiseTexSize = noiseTexSizeDesired; texUpdate = true; } - if( ImGui::DragFloat("Gain", &fnlFractalGain, 0.01f) ) + if (previewScroll != 0) { - fnl.SetFractalGain( fnlFractalGain ); + previewPosZ += previewScroll; texUpdate = true; } - if( ImGui::DragFloat("Weighted Strength", &fnlFractalWeightedStrength, 0.01f) ) + if (texUpdate) { - fnl.SetFractalWeightedStrength( fnlFractalWeightedStrength ); - texUpdate = true; - } - ImGui::BeginDisabled( fnlFractalType != (int)FastNoiseLite::FractalType_PingPong ); - if( ImGui::DragFloat("Ping Pong Strength", &fnlFractalPingPongStrength, 0.01f) ) - { - fnl.SetFractalPingPongStrength( fnlFractalPingPongStrength ); - texUpdate = true; + UpdateTexture(); } - ImGui::EndDisabled(); - - + ImGui::Image((void*)(intptr_t)noiseTex, noiseTexSize); ImGui::End(); - - if(texUpdate) - { - UpdateTexture(); - } - ImGui::Begin("Noise Texture"); - ImGui::Image((void*)(intptr_t)noiseTex, ImVec2(noiseTexSize, noiseTexSize)); + ImGui::BeginViewportSideBar("status", ImGui::GetMainViewport(), ImGuiDir_Down, 32, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse); + float textOffset = 0; + ImGui::Text("Preview Stats: %0.02fms", previewGenTime); + ImGui::SameLine(textOffset += 200); + ImGui::Text("Min: %0.04f", previewMin); + ImGui::SameLine(textOffset += 100); + ImGui::Text("Max: %0.04f", previewMax); + ImGui::SameLine(textOffset += 100); + ImGui::Text("Mean: %0.04f", previewMean); + + ImGui::SameLine(ImGui::GetWindowWidth() - ImGui::CalcTextSize("GitHub").x - 15); + ImGui::SetCursorPosY(ImGui::GetCursorPosY() - 2); + if (ImGui::Button("GitHub")) + { + emscripten_run_script("window.open('https://github.com/Auburn/FastNoiseLite', '_blank').focus();"); + } + ImGui::PopStyleVar(); ImGui::End(); } @@ -143,7 +337,7 @@ class FastNoiseLitePreviewApp : public ImGui_Application // We are using the ImGuiWindowFlags_NoDocking flag to make the parent window not dockable into, // because it would be confusing to have two docking targets within each others. - ImGuiWindowFlags window_flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking; + ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDocking; ImGuiViewport* viewport = ImGui::GetMainViewport(); ImGui::SetNextWindowPos(viewport->Pos); @@ -156,12 +350,12 @@ class FastNoiseLitePreviewApp : public ImGui_Application // When using ImGuiDockNodeFlags_PassthruCentralNode, DockSpace() will render our background and handle the pass-thru hole, so we ask Begin() to not render a background. if (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode) - window_flags |= ImGuiWindowFlags_NoBackground; + window_flags |= ImGuiWindowFlags_NoBackground; // Important: note that we proceed even if Begin() returns false (aka window is collapsed). - // This is because we want to keep our DockSpace() active. If a DockSpace() is inactive, + // This is because we want to keep our DockSpace() active. If a DockSpace() is inactive, // all active windows docked into it will lose their parent and become undocked. - // We cannot preserve the docking relationship between an active window and an inactive docking, otherwise + // We cannot preserve the docking relationship between an active window and an inactive docking, otherwise // any change of dockspace/settings would lead to windows being stuck in limbo and never being visible. ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); ImGui::Begin("DockSpace", nullptr, window_flags); @@ -184,14 +378,18 @@ class FastNoiseLitePreviewApp : public ImGui_Application ImGui::DockBuilderAddNode(dockspace_id, dockspace_flags | ImGuiDockNodeFlags_DockSpace); ImGui::DockBuilderSetNodeSize(dockspace_id, viewport->Size); + float split = 280 / viewport->Size.x; + // split the dockspace into 2 nodes -- DockBuilderSplitNode takes in the following args in the following order // window ID to split, direction, fraction (between 0 and 1), the final two setting let's us choose which id we want (which ever one we DON'T set as NULL, will be returned by the function) // out_id_at_dir is the id of the node in the direction we specified earlier, out_id_at_opposite_dir is in the opposite direction - auto dock_id_left = ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Left, 0.3f, nullptr, &dockspace_id); + auto dock_id_left = ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Left, split, nullptr, &dockspace_id); // we now dock our windows into the docking node we made above ImGui::DockBuilderDockWindow("Noise Texture", dockspace_id); - ImGui::DockBuilderDockWindow("FastNoise Lite", dock_id_left); + ImGui::DockBuilderDockWindow("Settings", dock_id_left); + ImGui::DockBuilderGetNode(dockspace_id)->SetLocalFlags(ImGuiDockNodeFlags_NoTabBar); + ImGui::DockBuilderGetNode(dock_id_left)->SetLocalFlags(ImGuiDockNodeFlags_NoTabBar); ImGui::DockBuilderFinish(dockspace_id); } } @@ -199,29 +397,136 @@ class FastNoiseLitePreviewApp : public ImGui_Application ImGui::End(); } + std::stringstream EncodeBMP(size_t width, size_t height, unsigned char* data) + { + std::stringstream file; + + struct BmpHeader + { + // File header (14) + // char b = 'B'; + // char m = 'M'; + uint32_t fileSize; + uint32_t reserved = 0; + uint32_t dataOffset = 14u + 12u + (256u * 3u); + // Bmp Info Header (12) + uint32_t headerSize = 12u; + uint16_t sizeX; + uint16_t sizeY; + uint16_t colorPlanes = 1u; + uint16_t bitDepth = 8u; + }; + + int paddedSizeX = (int)width; + int padding = paddedSizeX % 4; + if (padding) + { + padding = 4 - padding; + paddedSizeX += padding; + } + + BmpHeader header; + header.fileSize = header.dataOffset + (uint32_t)(paddedSizeX * height); + header.sizeX = (uint16_t)width; + header.sizeY = (uint16_t)height; + + file << 'B' << 'M'; + file.write(reinterpret_cast(&header), sizeof(BmpHeader)); + + // Colour map + for (int i = 0; i < 256; i++) + { + char b3[] = { (char)i, (char)i, (char)i }; + file.write(b3, 3); + } + + int idx = 0; + + for (size_t y = 0; y < height; y++) + { + for (size_t x = 0; x < height; x++) + { + file.write(reinterpret_cast(data + idx), 1); + idx += 4; + } + + if (padding) + { + int zero = 0; + file.write(reinterpret_cast(&zero), padding); + } + } + + return file; + } + void UpdateTexture() { - if(noiseTex != 0) + if (noiseTex != 0) { glDeleteTextures(1, &noiseTex); } - unsigned char *pixelArray = new unsigned char[noiseTexSize * noiseTexSize * 4]; + previewMin = INFINITY; + previewMax = -INFINITY; + previewMean = 0; + + unsigned char* pixelArray = new unsigned char[(int)noiseTexSize.x * (int)noiseTexSize.y * 4]; int index = 0; + int sizeX = (int)noiseTexSize.x; + int sizeY = (int)noiseTexSize.y; + float invert = 1 - ((int)previewInvert * 2); - for(int y = 0; y < noiseTexSize; y++) - { - for(int x = 0; x < noiseTexSize; x++) + auto timer = std::chrono::high_resolution_clock::now(); + + for (int y = 0; y < sizeY; y++) + { + for (int x = 0; x < sizeX; x++) { - float noise = fnl.GetNoise((double)x, (double)y); - unsigned char cNoise = (unsigned char)std::max(0.0f, std::min(255.0f, noise * 127.5f + 127.5f)); + float noise; + double posX = (double)(x - sizeX / 2); + double posY = (double)(y - sizeY / 2); + + if (preview3d) + { + double posZ = previewPosZ; + if (fnlDomainWarpType > 0) + { + fnlWarp.DomainWarp(posX, posY, posZ); + } + noise = fnl.GetNoise(posX, posY, posZ); + } + else + { + if (fnlDomainWarpType > 0) + { + fnlWarp.DomainWarp(posX, posY); + } + noise = fnl.GetNoise(posX, posY); + } + + unsigned char cNoise = (unsigned char)std::max(0.0f, std::min(255.0f, noise * invert * 127.5f + 127.5f)); pixelArray[index++] = cNoise; pixelArray[index++] = cNoise; pixelArray[index++] = cNoise; pixelArray[index++] = 255; + + previewMin = std::min(previewMin, noise); + previewMax = std::max(previewMax, noise); + previewMean += noise; } } + previewGenTime = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - timer).count() / 1000000.f; + previewMean /= noiseTexSize.x * noiseTexSize.y; + + if (previewTriggerSave) + { + previewTriggerSave = false; + std::string bmpFile = EncodeBMP(sizeX, sizeY, pixelArray).str(); + emscripten_browser_file::download("FastNoiseLite.bmp", "image/bmp", bmpFile); + } + // Create a OpenGL texture identifier glGenTextures(1, &noiseTex); glBindTexture(GL_TEXTURE_2D, noiseTex); @@ -233,7 +538,7 @@ class FastNoiseLitePreviewApp : public ImGui_Application glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // Same // Upload pixels into texture - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, noiseTexSize, noiseTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixelArray); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (int)noiseTexSize.x, (int)noiseTexSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixelArray); delete[] pixelArray; } }; @@ -241,7 +546,7 @@ class FastNoiseLitePreviewApp : public ImGui_Application int main() { FastNoiseLitePreviewApp app; - + if (!app.init()) return 1;